diff options
-rw-r--r-- | src/mixer/MixerAll.cxx | 28 | ||||
-rw-r--r-- | src/output/Control.cxx | 160 | ||||
-rw-r--r-- | src/output/Control.hxx | 106 | ||||
-rw-r--r-- | src/output/MultipleOutputs.cxx | 46 | ||||
-rw-r--r-- | src/output/MultipleOutputs.hxx | 9 | ||||
-rw-r--r-- | src/output/OutputCommand.cxx | 26 | ||||
-rw-r--r-- | src/output/Print.cxx | 2 | ||||
-rw-r--r-- | src/output/State.cxx | 6 | ||||
-rw-r--r-- | src/output/Thread.cxx | 1 |
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" |