summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mixer/MixerAll.cxx28
-rw-r--r--src/output/Control.cxx160
-rw-r--r--src/output/Control.hxx106
-rw-r--r--src/output/MultipleOutputs.cxx46
-rw-r--r--src/output/MultipleOutputs.hxx9
-rw-r--r--src/output/OutputCommand.cxx26
-rw-r--r--src/output/Print.cxx2
-rw-r--r--src/output/State.cxx6
-rw-r--r--src/output/Thread.cxx1
9 files changed, 325 insertions, 59 deletions
diff --git a/src/mixer/MixerAll.cxx b/src/mixer/MixerAll.cxx
index e9bcde93a..cb50c3418 100644
--- a/src/mixer/MixerAll.cxx
+++ b/src/mixer/MixerAll.cxx
@@ -31,12 +31,12 @@
#include <assert.h>
static int
-output_mixer_get_volume(const AudioOutput &ao)
+output_mixer_get_volume(const AudioOutputControl &ao)
{
- if (!ao.enabled)
+ if (!ao.IsEnabled())
return -1;
- Mixer *mixer = ao.mixer;
+ auto *mixer = ao.GetMixer();
if (mixer == nullptr)
return -1;
@@ -56,7 +56,7 @@ MultipleOutputs::GetVolume() const
unsigned ok = 0;
int total = 0;
- for (auto ao : outputs) {
+ for (auto *ao : outputs) {
int volume = output_mixer_get_volume(*ao);
if (volume >= 0) {
total += volume;
@@ -71,14 +71,14 @@ MultipleOutputs::GetVolume() const
}
static bool
-output_mixer_set_volume(AudioOutput &ao, unsigned volume)
+output_mixer_set_volume(AudioOutputControl &ao, unsigned volume)
{
assert(volume <= 100);
- if (!ao.enabled)
+ if (!ao.IsEnabled())
return false;
- Mixer *mixer = ao.mixer;
+ auto *mixer = ao.GetMixer();
if (mixer == nullptr)
return false;
@@ -99,7 +99,7 @@ MultipleOutputs::SetVolume(unsigned volume)
assert(volume <= 100);
bool success = false;
- for (auto ao : outputs)
+ for (auto *ao : outputs)
success = output_mixer_set_volume(*ao, volume)
|| success;
@@ -107,12 +107,12 @@ MultipleOutputs::SetVolume(unsigned volume)
}
static int
-output_mixer_get_software_volume(const AudioOutput &ao)
+output_mixer_get_software_volume(const AudioOutputControl &ao)
{
- if (!ao.enabled)
+ if (!ao.IsEnabled())
return -1;
- Mixer *mixer = ao.mixer;
+ auto *mixer = ao.GetMixer();
if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin))
return -1;
@@ -125,7 +125,7 @@ MultipleOutputs::GetSoftwareVolume() const
unsigned ok = 0;
int total = 0;
- for (auto ao : outputs) {
+ for (auto *ao : outputs) {
int volume = output_mixer_get_software_volume(*ao);
if (volume >= 0) {
total += volume;
@@ -144,8 +144,8 @@ MultipleOutputs::SetSoftwareVolume(unsigned volume)
{
assert(volume <= PCM_VOLUME_1);
- for (auto ao : outputs) {
- const auto mixer = ao->mixer;
+ for (auto *ao : outputs) {
+ auto *mixer = ao->GetMixer();
if (mixer != nullptr &&
(&mixer->plugin == &software_mixer_plugin ||
diff --git a/src/output/Control.cxx b/src/output/Control.cxx
index 55076a5d4..4ae8f5e08 100644
--- a/src/output/Control.cxx
+++ b/src/output/Control.cxx
@@ -18,6 +18,7 @@
*/
#include "config.h"
+#include "Control.hxx"
#include "Internal.hxx"
#include "OutputPlugin.hxx"
#include "Domain.hxx"
@@ -36,6 +37,72 @@ static constexpr PeriodClock::Duration REOPEN_AFTER = std::chrono::seconds(10);
struct notify audio_output_client_notify;
+AudioOutputControl::AudioOutputControl(AudioOutput *_output)
+ :output(_output), mutex(output->mutex)
+{
+}
+
+const char *
+AudioOutputControl::GetName() const
+{
+ return output->GetName();
+}
+
+AudioOutputClient &
+AudioOutputControl::GetClient()
+{
+ return *output->client;
+}
+
+Mixer *
+AudioOutputControl::GetMixer() const
+{
+ return output->mixer;
+}
+
+bool
+AudioOutputControl::IsEnabled() const
+{
+ return output->IsEnabled();
+}
+
+bool
+AudioOutputControl::LockSetEnabled(bool new_value)
+{
+ const std::lock_guard<Mutex> protect(mutex);
+
+ if (new_value == output->enabled)
+ return false;
+
+ output->enabled = new_value;
+ return true;
+}
+
+bool
+AudioOutputControl::LockToggleEnabled()
+{
+ const std::lock_guard<Mutex> protect(mutex);
+ return output->enabled = !output->enabled;
+}
+
+bool
+AudioOutputControl::IsOpen() const
+{
+ return output->IsOpen();
+}
+
+bool
+AudioOutputControl::IsBusy() const
+{
+ return output->IsBusy();
+}
+
+const std::exception_ptr &
+AudioOutputControl::GetLastError() const
+{
+ return output->GetLastError();
+}
+
void
AudioOutput::WaitForCommand()
{
@@ -47,6 +114,12 @@ AudioOutput::WaitForCommand()
}
void
+AudioOutputControl::WaitForCommand()
+{
+ output->WaitForCommand();
+}
+
+void
AudioOutput::CommandAsync(Command cmd)
{
assert(IsCommandFinished());
@@ -104,6 +177,18 @@ AudioOutput::DisableAsync()
CommandAsync(Command::DISABLE);
}
+void
+AudioOutputControl::EnableDisableAsync()
+{
+ output->EnableDisableAsync();
+}
+
+void
+AudioOutputControl::LockPauseAsync()
+{
+ output->LockPauseAsync();
+}
+
inline bool
AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
{
@@ -176,6 +261,38 @@ AudioOutput::LockUpdate(const AudioFormat audio_format,
}
void
+AudioOutputControl::LockRelease()
+{
+ output->LockRelease();
+}
+
+void
+AudioOutputControl::LockCloseWait()
+{
+ output->LockCloseWait();
+}
+
+bool
+AudioOutputControl::LockUpdate(const AudioFormat audio_format,
+ const MusicPipe &mp,
+ bool force)
+{
+ return output->LockUpdate(audio_format, mp, force);
+}
+
+bool
+AudioOutputControl::LockIsChunkConsumed(const MusicChunk &chunk) const
+{
+ return output->LockIsChunkConsumed(chunk);
+}
+
+void
+AudioOutputControl::ClearTailChunk(const MusicChunk &chunk)
+{
+ output->ClearTailChunk(chunk);
+}
+
+void
AudioOutput::LockPlay()
{
const std::lock_guard<Mutex> protect(mutex);
@@ -254,6 +371,12 @@ AudioOutput::LockCloseWait()
}
void
+AudioOutputControl::SetReplayGainMode(ReplayGainMode _mode)
+{
+ return output->SetReplayGainMode(_mode);
+}
+
+void
AudioOutput::StopThread()
{
assert(thread.IsDefined());
@@ -276,6 +399,12 @@ AudioOutput::BeginDestroy()
}
void
+AudioOutputControl::BeginDestroy()
+{
+ output->BeginDestroy();
+}
+
+void
AudioOutput::FinishDestroy()
{
if (thread.IsDefined())
@@ -283,3 +412,34 @@ AudioOutput::FinishDestroy()
audio_output_free(this);
}
+
+void
+AudioOutputControl::FinishDestroy()
+{
+ output->FinishDestroy();
+ output = nullptr;
+}
+
+void
+AudioOutputControl::LockPlay()
+{
+ output->LockPlay();
+}
+
+void
+AudioOutputControl::LockDrainAsync()
+{
+ output->LockDrainAsync();
+}
+
+void
+AudioOutputControl::LockCancelAsync()
+{
+ output->LockCancelAsync();
+}
+
+void
+AudioOutputControl::LockAllowPlay()
+{
+ output->LockAllowPlay();
+}
diff --git a/src/output/Control.hxx b/src/output/Control.hxx
index 778b6245e..8aa2161a1 100644
--- a/src/output/Control.hxx
+++ b/src/output/Control.hxx
@@ -20,6 +20,112 @@
#ifndef MPD_OUTPUT_CONTROL_HXX
#define MPD_OUTPUT_CONTROL_HXX
+#include "Compiler.h"
+
+#include <utility>
+#include <exception>
+
+#ifndef NDEBUG
+#include <assert.h>
+#endif
+
+#include <stdint.h>
+
+enum class ReplayGainMode : uint8_t;
+struct AudioFormat;
struct AudioOutput;
+struct MusicChunk;
+class MusicPipe;
+class Mutex;
+class Mixer;
+class AudioOutputClient;
+
+/**
+ * Controller for an #AudioOutput and its output thread.
+ */
+class AudioOutputControl {
+ AudioOutput *output;
+
+public:
+ Mutex &mutex;
+
+ explicit AudioOutputControl(AudioOutput *_output);
+
+#ifndef NDEBUG
+ ~AudioOutputControl() {
+ assert(output == nullptr);
+ }
+#endif
+
+ AudioOutputControl(const AudioOutputControl &) = delete;
+ AudioOutputControl &operator=(const AudioOutputControl &) = delete;
+
+ gcc_pure
+ const char *GetName() const;
+
+ AudioOutputClient &GetClient();
+
+ gcc_pure
+ Mixer *GetMixer() const;
+
+ gcc_pure
+ bool IsEnabled() const;
+
+ /**
+ * @return true if the value has been modified
+ */
+ bool LockSetEnabled(bool new_value);
+
+ /**
+ * @return the new "enabled" value
+ */
+ bool LockToggleEnabled();
+
+ gcc_pure
+ bool IsOpen() const;
+
+ gcc_pure
+ bool IsBusy() const;
+
+ /**
+ * Caller must lock the mutex.
+ */
+ gcc_const
+ const std::exception_ptr &GetLastError() const;
+
+ void WaitForCommand();
+
+ void BeginDestroy();
+ void FinishDestroy();
+
+ void EnableDisableAsync();
+ void LockPauseAsync();
+
+ void LockCloseWait();
+ void LockRelease();
+
+ void SetReplayGainMode(ReplayGainMode _mode);
+
+ /**
+ * Opens or closes the device, depending on the "enabled"
+ * flag.
+ *
+ * @param force true to ignore the #fail_timer
+ * @return true if the device is open
+ */
+ bool LockUpdate(const AudioFormat audio_format,
+ const MusicPipe &mp,
+ bool force);
+
+ gcc_pure
+ bool LockIsChunkConsumed(const MusicChunk &chunk) const;
+
+ void ClearTailChunk(const MusicChunk &chunk);
+
+ void LockPlay();
+ void LockDrainAsync();
+ void LockCancelAsync();
+ void LockAllowPlay();
+};
#endif
diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx
index c3d584652..3781d96dc 100644
--- a/src/output/MultipleOutputs.cxx
+++ b/src/output/MultipleOutputs.cxx
@@ -43,9 +43,9 @@ MultipleOutputs::MultipleOutputs(MixerListener &_mixer_listener)
MultipleOutputs::~MultipleOutputs()
{
/* parallel destruction */
- for (auto i : outputs)
+ for (auto *i : outputs)
i->BeginDestroy();
- for (auto i : outputs)
+ for (auto *i : outputs)
i->FinishDestroy();
}
@@ -80,7 +80,7 @@ MultipleOutputs::Configure(EventLoop &event_loop,
throw FormatRuntimeError("output devices with identical "
"names: %s", output->GetName());
- outputs.push_back(output);
+ outputs.push_back(new AudioOutputControl(output));
}
if (outputs.empty()) {
@@ -89,7 +89,7 @@ MultipleOutputs::Configure(EventLoop &event_loop,
auto output = LoadOutput(event_loop, replay_gain_config,
mixer_listener,
client, empty);
- outputs.push_back(output);
+ outputs.push_back(new AudioOutputControl(output));
}
}
@@ -104,13 +104,13 @@ MultipleOutputs::AddNullOutput(EventLoop &event_loop,
auto output = LoadOutput(event_loop, replay_gain_config,
mixer_listener,
client, block);
- outputs.push_back(output);
+ outputs.push_back(new AudioOutputControl(output));
}
-AudioOutput *
-MultipleOutputs::FindByName(const char *name) const
+AudioOutputControl *
+MultipleOutputs::FindByName(const char *name)
{
- for (auto i : outputs)
+ for (auto *i : outputs)
if (strcmp(i->GetName(), name) == 0)
return i;
@@ -122,12 +122,12 @@ MultipleOutputs::EnableDisable()
{
/* parallel execution */
- for (auto ao : outputs) {
+ for (auto *ao : outputs) {
const std::lock_guard<Mutex> lock(ao->mutex);
ao->EnableDisableAsync();
}
- for (auto ao : outputs) {
+ for (auto *ao : outputs) {
const std::lock_guard<Mutex> lock(ao->mutex);
ao->WaitForCommand();
}
@@ -136,7 +136,7 @@ MultipleOutputs::EnableDisable()
bool
MultipleOutputs::AllFinished() const
{
- for (auto ao : outputs) {
+ for (auto *ao : outputs) {
const std::lock_guard<Mutex> protect(ao->mutex);
if (ao->IsBusy())
return false;
@@ -155,7 +155,7 @@ MultipleOutputs::WaitAll()
void
MultipleOutputs::AllowPlay()
{
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->LockAllowPlay();
}
@@ -167,7 +167,7 @@ MultipleOutputs::Update(bool force)
if (!input_audio_format.IsDefined())
return false;
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ret = ao->LockUpdate(input_audio_format, *pipe, force)
|| ret;
@@ -177,7 +177,7 @@ MultipleOutputs::Update(bool force)
void
MultipleOutputs::SetReplayGainMode(ReplayGainMode mode)
{
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->SetReplayGainMode(mode);
}
@@ -195,7 +195,7 @@ MultipleOutputs::Play(MusicChunk *chunk)
pipe->Push(chunk);
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->LockPlay();
}
@@ -228,7 +228,7 @@ MultipleOutputs::Open(const AudioFormat audio_format,
std::exception_ptr first_error;
- for (auto ao : outputs) {
+ for (auto *ao : outputs) {
const std::lock_guard<Mutex> lock(ao->mutex);
if (ao->IsEnabled())
@@ -259,7 +259,7 @@ MultipleOutputs::Open(const AudioFormat audio_format,
bool
MultipleOutputs::IsChunkConsumed(const MusicChunk *chunk) const
{
- for (auto ao : outputs)
+ for (auto *ao : outputs)
if (!ao->LockIsChunkConsumed(*chunk))
return false;
@@ -274,7 +274,7 @@ MultipleOutputs::ClearTailChunk(const MusicChunk *chunk,
assert(pipe->Contains(chunk));
for (unsigned i = 0, n = outputs.size(); i != n; ++i) {
- AudioOutput *ao = outputs[i];
+ auto *ao = outputs[i];
/* this mutex will be unlocked by the caller when it's
ready */
@@ -343,7 +343,7 @@ MultipleOutputs::Pause()
{
Update(false);
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->LockPauseAsync();
WaitAll();
@@ -352,7 +352,7 @@ MultipleOutputs::Pause()
void
MultipleOutputs::Drain()
{
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->LockDrainAsync();
WaitAll();
@@ -363,7 +363,7 @@ MultipleOutputs::Cancel()
{
/* send the cancel() command to all audio outputs */
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->LockCancelAsync();
WaitAll();
@@ -386,7 +386,7 @@ MultipleOutputs::Cancel()
void
MultipleOutputs::Close()
{
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->LockCloseWait();
if (pipe != nullptr) {
@@ -407,7 +407,7 @@ MultipleOutputs::Close()
void
MultipleOutputs::Release()
{
- for (auto ao : outputs)
+ for (auto *ao : outputs)
ao->LockRelease();
if (pipe != nullptr) {
diff --git a/src/output/MultipleOutputs.hxx b/src/output/MultipleOutputs.hxx
index b9e12c07e..ccadaccbd 100644
--- a/src/output/MultipleOutputs.hxx
+++ b/src/output/MultipleOutputs.hxx
@@ -26,6 +26,7 @@
#ifndef OUTPUT_ALL_H
#define OUTPUT_ALL_H
+#include "Control.hxx"
#include "AudioFormat.hxx"
#include "ReplayGainMode.hxx"
#include "Chrono.hxx"
@@ -47,7 +48,7 @@ struct ReplayGainConfig;
class MultipleOutputs {
MixerListener &mixer_listener;
- std::vector<AudioOutput *> outputs;
+ std::vector<AudioOutputControl *> outputs;
AudioFormat input_audio_format = AudioFormat::Undefined();
@@ -96,13 +97,13 @@ public:
/**
* Returns the "i"th audio output device.
*/
- const AudioOutput &Get(unsigned i) const {
+ const AudioOutputControl &Get(unsigned i) const {
assert(i < Size());
return *outputs[i];
}
- AudioOutput &Get(unsigned i) {
+ AudioOutputControl &Get(unsigned i) {
assert(i < Size());
return *outputs[i];
@@ -113,7 +114,7 @@ public:
* Returns nullptr if the name does not exist.
*/
gcc_pure
- AudioOutput *FindByName(const char *name) const;
+ AudioOutputControl *FindByName(const char *name);
/**
* Checks the "enabled" flag of all audio outputs, and if one has
diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx
index 510211a0c..1da8e5fdf 100644
--- a/src/output/OutputCommand.cxx
+++ b/src/output/OutputCommand.cxx
@@ -42,19 +42,18 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx)
if (idx >= outputs.Size())
return false;
- AudioOutput &ao = outputs.Get(idx);
- if (ao.enabled)
+ auto &ao = outputs.Get(idx);
+ if (!ao.LockSetEnabled(true))
return true;
- ao.enabled = true;
idle_add(IDLE_OUTPUT);
- if (ao.mixer != nullptr) {
+ if (ao.GetMixer() != nullptr) {
InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
- ao.client->ApplyEnabled();
+ ao.GetClient().ApplyEnabled();
++audio_output_state_version;
@@ -67,21 +66,20 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx)
if (idx >= outputs.Size())
return false;
- AudioOutput &ao = outputs.Get(idx);
- if (!ao.enabled)
+ auto &ao = outputs.Get(idx);
+ if (!ao.LockSetEnabled(false))
return true;
- ao.enabled = false;
idle_add(IDLE_OUTPUT);
- Mixer *mixer = ao.mixer;
+ auto *mixer = ao.GetMixer();
if (mixer != nullptr) {
mixer_close(mixer);
InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
- ao.client->ApplyEnabled();
+ ao.GetClient().ApplyEnabled();
++audio_output_state_version;
@@ -94,12 +92,12 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
if (idx >= outputs.Size())
return false;
- AudioOutput &ao = outputs.Get(idx);
- const bool enabled = ao.enabled = !ao.enabled;
+ auto &ao = outputs.Get(idx);
+ const bool enabled = ao.LockToggleEnabled();
idle_add(IDLE_OUTPUT);
if (!enabled) {
- Mixer *mixer = ao.mixer;
+ auto *mixer = ao.GetMixer();
if (mixer != nullptr) {
mixer_close(mixer);
InvalidateHardwareVolume();
@@ -107,7 +105,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
}
}
- ao.client->ApplyEnabled();
+ ao.GetClient().ApplyEnabled();
++audio_output_state_version;
diff --git a/src/output/Print.cxx b/src/output/Print.cxx
index f24d09eda..01aa6a37a 100644
--- a/src/output/Print.cxx
+++ b/src/output/Print.cxx
@@ -32,7 +32,7 @@ void
printAudioDevices(Response &r, const MultipleOutputs &outputs)
{
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
- const AudioOutput &ao = outputs.Get(i);
+ const auto &ao = outputs.Get(i);
r.Format("outputid: %i\n"
"outputname: %s\n"
diff --git a/src/output/State.cxx b/src/output/State.cxx
index b57f8ce4d..dd84901d7 100644
--- a/src/output/State.cxx
+++ b/src/output/State.cxx
@@ -42,7 +42,7 @@ audio_output_state_save(BufferedOutputStream &os,
const MultipleOutputs &outputs)
{
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
- const AudioOutput &ao = outputs.Get(i);
+ const auto &ao = outputs.Get(i);
const std::lock_guard<Mutex> lock(ao.mutex);
os.Format(AUDIO_DEVICE_STATE "%d:%s\n",
@@ -70,14 +70,14 @@ audio_output_state_read(const char *line, MultipleOutputs &outputs)
return true;
name = endptr + 1;
- AudioOutput *ao = outputs.FindByName(name);
+ auto *ao = outputs.FindByName(name);
if (ao == NULL) {
FormatDebug(output_domain,
"Ignoring device state for '%s'", name);
return true;
}
- ao->enabled = false;
+ ao->LockSetEnabled(false);
return true;
}
diff --git a/src/output/Thread.cxx b/src/output/Thread.cxx
index 7b8aa435e..785ea947a 100644
--- a/src/output/Thread.cxx
+++ b/src/output/Thread.cxx
@@ -18,6 +18,7 @@
*/
#include "config.h"
+#include "Control.hxx"
#include "Internal.hxx"
#include "Client.hxx"
#include "OutputAPI.hxx"