diff --git a/src/output/Client.hxx b/src/output/Client.hxx new file mode 100644 index 000000000..facace4be --- /dev/null +++ b/src/output/Client.hxx @@ -0,0 +1,47 @@ +/* + * Copyright 2003-2016 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_OUTPUT_CLIENT_HXX +#define MPD_OUTPUT_CLIENT_HXX + +#include "check.h" + +/** + * An interface between the #AudioOutput and the #Player. + */ +class AudioOutputClient { +public: + /** + * Notify the client that we have consumed a few chunks. This + * is called from within the output thread. The client may + * perform actions to refill the #MusicPipe. + */ + virtual void ChunksConsumed() = 0; + + /** + * The #AudioOutput has modified the "enabled" flag, and the + * client shall make the #AudioOutput apply this new setting. + * This is called from any thread, one which can't send an + * AudioOutput::Command to the output thread; only the client + * can do that safely. + */ + virtual void ApplyEnabled() = 0; +}; + +#endif diff --git a/src/output/Init.cxx b/src/output/Init.cxx index 5efebb492..65234e9bd 100644 --- a/src/output/Init.cxx +++ b/src/output/Init.cxx @@ -275,7 +275,7 @@ audio_output_new(EventLoop &event_loop, const ReplayGainConfig &replay_gain_config, const ConfigBlock &block, MixerListener &mixer_listener, - PlayerControl &pc) + AudioOutputClient &client) { const AudioOutputPlugin *plugin; @@ -311,6 +311,6 @@ audio_output_new(EventLoop &event_loop, throw; } - ao->player_control = &pc; + ao->client = &client; return ao; } diff --git a/src/output/Internal.hxx b/src/output/Internal.hxx index bb8511f10..4f84fe0f5 100644 --- a/src/output/Internal.hxx +++ b/src/output/Internal.hxx @@ -37,9 +37,9 @@ class MusicPipe; class EventLoop; class Mixer; class MixerListener; +class AudioOutputClient; struct MusicChunk; struct ConfigBlock; -struct PlayerControl; struct AudioOutputPlugin; struct ReplayGainConfig; @@ -261,7 +261,7 @@ struct AudioOutput { * The PlayerControl object which "owns" this output. This * object is needed to signal command completion. */ - PlayerControl *player_control; + AudioOutputClient *client; /** * A reference to the #MusicPipe and the current position. @@ -510,7 +510,7 @@ audio_output_new(EventLoop &event_loop, const ReplayGainConfig &replay_gain_config, const ConfigBlock &block, MixerListener &mixer_listener, - PlayerControl &pc); + AudioOutputClient &client); void audio_output_free(AudioOutput *ao); diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx index 6341554bf..856678778 100644 --- a/src/output/MultipleOutputs.cxx +++ b/src/output/MultipleOutputs.cxx @@ -53,11 +53,11 @@ static AudioOutput * LoadOutput(EventLoop &event_loop, const ReplayGainConfig &replay_gain_config, MixerListener &mixer_listener, - PlayerControl &pc, const ConfigBlock &block) + AudioOutputClient &client, const ConfigBlock &block) try { return audio_output_new(event_loop, replay_gain_config, block, mixer_listener, - pc); + client); } catch (const std::runtime_error &e) { if (block.line > 0) std::throw_with_nested(FormatRuntimeError("Failed to configure output in line %i", @@ -69,13 +69,13 @@ try { void MultipleOutputs::Configure(EventLoop &event_loop, const ReplayGainConfig &replay_gain_config, - PlayerControl &pc) + AudioOutputClient &client) { for (const auto *param = config_get_block(ConfigBlockOption::AUDIO_OUTPUT); param != nullptr; param = param->next) { auto output = LoadOutput(event_loop, replay_gain_config, mixer_listener, - pc, *param); + client, *param); if (FindByName(output->name) != nullptr) throw FormatRuntimeError("output devices with identical " "names: %s", output->name); @@ -88,7 +88,7 @@ MultipleOutputs::Configure(EventLoop &event_loop, const ConfigBlock empty; auto output = LoadOutput(event_loop, replay_gain_config, mixer_listener, - pc, empty); + client, empty); outputs.push_back(output); } } diff --git a/src/output/MultipleOutputs.hxx b/src/output/MultipleOutputs.hxx index 3352ad7dc..2814d3ecd 100644 --- a/src/output/MultipleOutputs.hxx +++ b/src/output/MultipleOutputs.hxx @@ -39,8 +39,8 @@ class MusicBuffer; class MusicPipe; class EventLoop; class MixerListener; +class AudioOutputClient; struct MusicChunk; -struct PlayerControl; struct AudioOutput; struct ReplayGainConfig; @@ -78,7 +78,7 @@ public: void Configure(EventLoop &event_loop, const ReplayGainConfig &replay_gain_config, - PlayerControl &pc); + AudioOutputClient &client); /** * Returns the total number of audio output devices, including diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx index a4e797ab4..2e915c2bc 100644 --- a/src/output/OutputCommand.cxx +++ b/src/output/OutputCommand.cxx @@ -28,6 +28,7 @@ #include "OutputCommand.hxx" #include "MultipleOutputs.hxx" #include "Internal.hxx" +#include "Client.hxx" #include "player/Control.hxx" #include "mixer/MixerControl.hxx" #include "mixer/Volume.hxx" @@ -53,7 +54,7 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx) idle_add(IDLE_MIXER); } - ao.player_control->LockUpdateAudio(); + ao.client->ApplyEnabled(); ++audio_output_state_version; @@ -80,7 +81,7 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx) idle_add(IDLE_MIXER); } - ao.player_control->LockUpdateAudio(); + ao.client->ApplyEnabled(); ++audio_output_state_version; @@ -106,7 +107,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx) } } - ao.player_control->LockUpdateAudio(); + ao.client->ApplyEnabled(); ++audio_output_state_version; diff --git a/src/output/OutputThread.cxx b/src/output/OutputThread.cxx index 2318f43b3..6129ddcbf 100644 --- a/src/output/OutputThread.cxx +++ b/src/output/OutputThread.cxx @@ -19,6 +19,7 @@ #include "config.h" #include "Internal.hxx" +#include "Client.hxx" #include "OutputAPI.hxx" #include "Domain.hxx" #include "pcm/PcmMix.hxx" @@ -28,7 +29,6 @@ #include "filter/plugins/ReplayGainFilterPlugin.hxx" #include "mixer/MixerInternal.hxx" #include "mixer/plugins/SoftwareMixerPlugin.hxx" -#include "player/Control.hxx" #include "MusicPipe.hxx" #include "MusicChunk.hxx" #include "thread/Util.hxx" @@ -504,7 +504,7 @@ AudioOutput::Play() give it a chance to refill the pipe before it runs empty */ const ScopeUnlock unlock(mutex); - player_control->LockSignal(); + client->ChunksConsumed(); n = 0; } @@ -516,7 +516,7 @@ AudioOutput::Play() } while (chunk != nullptr); const ScopeUnlock unlock(mutex); - player_control->LockSignal(); + client->ChunksConsumed(); return true; } diff --git a/src/player/Control.hxx b/src/player/Control.hxx index 175677e4a..6d3e70647 100644 --- a/src/player/Control.hxx +++ b/src/player/Control.hxx @@ -20,6 +20,7 @@ #ifndef MPD_PLAYER_CONTROL_HXX #define MPD_PLAYER_CONTROL_HXX +#include "output/Client.hxx" #include "AudioFormat.hxx" #include "thread/Mutex.hxx" #include "thread/Cond.hxx" @@ -96,7 +97,7 @@ struct player_status { SongTime elapsed_time; }; -struct PlayerControl { +struct PlayerControl final : AudioOutputClient { PlayerListener &listener; MultipleOutputs &outputs; @@ -527,6 +528,15 @@ public: double GetTotalPlayTime() const { return total_play_time; } + + /* virtual methods from AudioOutputClient */ + void ChunksConsumed() override { + LockSignal(); + } + + void ApplyEnabled() override { + LockUpdateAudio(); + } }; #endif diff --git a/test/run_output.cxx b/test/run_output.cxx index cd19f7bf3..1df9a8660 100644 --- a/test/run_output.cxx +++ b/test/run_output.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "output/Internal.hxx" #include "output/OutputPlugin.hxx" +#include "output/Client.hxx" #include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" @@ -32,7 +33,6 @@ #include "ReplayGainConfig.hxx" #include "pcm/PcmConvert.hxx" #include "filter/FilterRegistry.hxx" -#include "player/Control.hxx" #include "util/RuntimeError.hxx" #include "util/ScopeExit.hxx" #include "Log.hxx" @@ -43,6 +43,16 @@ #include #include +class DummyAudioOutputClient final : public AudioOutputClient { +public: + /* virtual methods from AudioOutputClient */ + void ChunksConsumed() override { + } + + void ApplyEnabled() override { + } +}; + const FilterPlugin * filter_plugin_by_name(gcc_unused const char *name) { @@ -50,21 +60,9 @@ filter_plugin_by_name(gcc_unused const char *name) return NULL; } -PlayerControl::PlayerControl(PlayerListener &_listener, - MultipleOutputs &_outputs, - unsigned _buffer_chunks, - unsigned _buffered_before_play, - AudioFormat _configured_audio_format, - const ReplayGainConfig &_replay_gain_config) - :listener(_listener), outputs(_outputs), - buffer_chunks(_buffer_chunks), - buffered_before_play(_buffered_before_play), - configured_audio_format(_configured_audio_format), - replay_gain_config(_replay_gain_config) {} -PlayerControl::~PlayerControl() {} - static AudioOutput * -load_audio_output(EventLoop &event_loop, const char *name) +load_audio_output(EventLoop &event_loop, AudioOutputClient &client, + const char *name) { const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT, "name", name); @@ -72,15 +70,9 @@ load_audio_output(EventLoop &event_loop, const char *name) throw FormatRuntimeError("No such configured audio output: %s\n", name); - static struct PlayerControl dummy_player_control(*(PlayerListener *)nullptr, - *(MultipleOutputs *)nullptr, - 32, 4, - AudioFormat::Undefined(), - ReplayGainConfig()); - return audio_output_new(event_loop, ReplayGainConfig(), *param, *(MixerListener *)nullptr, - dummy_player_control); + client); } static void @@ -150,7 +142,8 @@ try { /* initialize the audio output */ - AudioOutput *ao = load_audio_output(event_loop, argv[2]); + DummyAudioOutputClient client; + AudioOutput *ao = load_audio_output(event_loop, client, argv[2]); /* parse the audio format */