diff --git a/Makefile.am b/Makefile.am index 35fdbe6cc..f30e3fafa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1375,7 +1375,6 @@ OUTPUT_API_SRC = \ src/output/OutputAPI.hxx \ src/output/Interface.hxx \ src/output/Filtered.cxx src/output/Filtered.hxx \ - src/output/Wrapper.hxx \ src/output/Registry.cxx src/output/Registry.hxx \ src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx \ src/output/SharedPipeConsumer.cxx src/output/SharedPipeConsumer.hxx \ diff --git a/src/output/Filtered.cxx b/src/output/Filtered.cxx index 723f55655..45d2d92be 100644 --- a/src/output/Filtered.cxx +++ b/src/output/Filtered.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "Filtered.hxx" #include "Interface.hxx" -#include "OutputPlugin.hxx" #include "Domain.hxx" #include "Log.hxx" #include "mixer/MixerInternal.hxx" @@ -32,22 +31,20 @@ bool FilteredAudioOutput::SupportsEnableDisable() const noexcept { - assert((output->GetPlugin().enable == nullptr) == (output->GetPlugin().disable == nullptr)); - - return output->GetPlugin().enable != nullptr; + return output->SupportsEnableDisable(); } bool FilteredAudioOutput::SupportsPause() const noexcept { - return output->GetPlugin().pause != nullptr; + return output->SupportsPause(); } void FilteredAudioOutput::Enable() { try { - ao_plugin_enable(*output); + output->Enable(); } catch (const std::runtime_error &e) { std::throw_with_nested(FormatRuntimeError("Failed to enable output %s", GetLogName())); @@ -57,7 +54,7 @@ FilteredAudioOutput::Enable() void FilteredAudioOutput::Disable() noexcept { - ao_plugin_disable(*output); + output->Disable(); } void @@ -77,7 +74,7 @@ FilteredAudioOutput::OpenOutputAndConvert(AudioFormat desired_audio_format) out_audio_format = desired_audio_format; try { - ao_plugin_open(*output, out_audio_format); + output->Open(out_audio_format); } catch (const std::runtime_error &e) { std::throw_with_nested(FormatRuntimeError("Failed to open %s", GetLogName())); @@ -91,7 +88,7 @@ FilteredAudioOutput::OpenOutputAndConvert(AudioFormat desired_audio_format) try { ConfigureConvertFilter(); } catch (const std::runtime_error &e) { - ao_plugin_close(*output); + output->Close(); if (out_audio_format.format == SampleFormat::DSD) { /* if the audio output supports DSD, but not @@ -120,7 +117,7 @@ FilteredAudioOutput::CloseOutput(bool drain) noexcept else Cancel(); - ao_plugin_close(*output); + output->Close(); } void @@ -149,31 +146,31 @@ FilteredAudioOutput::Close(bool drain) noexcept std::chrono::steady_clock::duration FilteredAudioOutput::Delay() noexcept { - return ao_plugin_delay(*output); + return output->Delay(); } void FilteredAudioOutput::SendTag(const Tag &tag) { - ao_plugin_send_tag(*output, tag); + output->SendTag(tag); } size_t FilteredAudioOutput::Play(const void *data, size_t size) { - return ao_plugin_play(*output, data, size); + return output->Play(data, size); } void FilteredAudioOutput::Drain() { - ao_plugin_drain(*output); + output->Drain(); } void FilteredAudioOutput::Cancel() noexcept { - ao_plugin_cancel(*output); + output->Cancel(); } void @@ -186,7 +183,7 @@ bool FilteredAudioOutput::IteratePause() noexcept { try { - return ao_plugin_pause(*output); + return output->Pause(); } catch (const std::runtime_error &e) { FormatError(e, "Failed to pause %s", GetLogName()); diff --git a/src/output/Filtered.hxx b/src/output/Filtered.hxx index 9717c484c..4f0a8713d 100644 --- a/src/output/Filtered.hxx +++ b/src/output/Filtered.hxx @@ -23,6 +23,7 @@ #include "AudioFormat.hxx" #include "filter/Observer.hxx" +#include #include #include @@ -31,6 +32,7 @@ class MusicPipe; class EventLoop; class Mixer; class MixerListener; +struct MixerPlugin; struct MusicChunk; struct ConfigBlock; class AudioOutput; @@ -38,6 +40,8 @@ struct ReplayGainConfig; struct Tag; struct FilteredAudioOutput { + const char *const plugin_name; + /** * The device's configured display name. */ @@ -54,7 +58,7 @@ public: /** * The plugin which implements this output device. */ - AudioOutput *const output; + std::unique_ptr output; /** * The #mixer object associated with this audio output device. @@ -120,7 +124,8 @@ public: /** * Throws #std::runtime_error on error. */ - FilteredAudioOutput(AudioOutput &_output, + FilteredAudioOutput(const char *_plugin_name, + std::unique_ptr &&_output, const ConfigBlock &block); ~FilteredAudioOutput(); @@ -131,6 +136,7 @@ private: public: void Setup(EventLoop &event_loop, const ReplayGainConfig &replay_gain_config, + const MixerPlugin *mixer_plugin, MixerListener &mixer_listener, const ConfigBlock &block); diff --git a/src/output/Finish.cxx b/src/output/Finish.cxx index 9db4d50fe..9e1c4eb46 100644 --- a/src/output/Finish.cxx +++ b/src/output/Finish.cxx @@ -19,7 +19,7 @@ #include "config.h" #include "Filtered.hxx" -#include "OutputPlugin.hxx" +#include "Interface.hxx" #include "mixer/MixerControl.hxx" #include "filter/FilterInternal.hxx" @@ -31,8 +31,6 @@ FilteredAudioOutput::~FilteredAudioOutput() delete prepared_replay_gain_filter; delete prepared_other_replay_gain_filter; delete prepared_filter; - - ao_plugin_finish(output); } void @@ -45,5 +43,5 @@ FilteredAudioOutput::BeginDestroy() noexcept void FilteredAudioOutput::FinishDestroy() noexcept { - ao_plugin_finish(output); + output.reset(); } diff --git a/src/output/Init.cxx b/src/output/Init.cxx index fb66b4a5d..c8756bb18 100644 --- a/src/output/Init.cxx +++ b/src/output/Init.cxx @@ -50,18 +50,11 @@ #define AUDIO_OUTPUT_FORMAT "format" #define AUDIO_FILTERS "filters" -FilteredAudioOutput::FilteredAudioOutput(AudioOutput &_output, +FilteredAudioOutput::FilteredAudioOutput(const char *_plugin_name, + std::unique_ptr &&_output, const ConfigBlock &block) - :output(&_output) + :plugin_name(_plugin_name), output(std::move(_output)) { -#ifndef NDEBUG - const auto &plugin = output->GetPlugin(); - assert(plugin.finish != nullptr); - assert(plugin.open != nullptr); - assert(plugin.close != nullptr); - assert(plugin.play != nullptr); -#endif - Configure(block); } @@ -172,7 +165,7 @@ FilteredAudioOutput::Configure(const ConfigBlock &block) { char buffer[64]; snprintf(buffer, sizeof(buffer), "\"%s\" (%s)", - name, output->GetPlugin().name); + name, plugin_name); log_name = buffer; } @@ -204,6 +197,7 @@ FilteredAudioOutput::Configure(const ConfigBlock &block) inline void FilteredAudioOutput::Setup(EventLoop &event_loop, const ReplayGainConfig &replay_gain_config, + const MixerPlugin *mixer_plugin, MixerListener &mixer_listener, const ConfigBlock &block) { @@ -233,7 +227,7 @@ FilteredAudioOutput::Setup(EventLoop &event_loop, try { mixer = audio_output_load_mixer(event_loop, *this, block, - output->GetPlugin().mixer_plugin, + mixer_plugin, *prepared_filter, mixer_listener); } catch (const std::runtime_error &e) { @@ -291,20 +285,15 @@ audio_output_new(EventLoop &event_loop, plugin->name); } - auto *ao = ao_plugin_init(event_loop, *plugin, block); + std::unique_ptr ao(ao_plugin_init(event_loop, *plugin, + block)); assert(ao != nullptr); - FilteredAudioOutput *f; - - try { - f = new FilteredAudioOutput(*ao, block); - } catch (...) { - ao_plugin_finish(ao); - throw; - } + auto *f = new FilteredAudioOutput(plugin->name, std::move(ao), block); try { f->Setup(event_loop, replay_gain_config, + plugin->mixer_plugin, mixer_listener, block); } catch (...) { delete f; diff --git a/src/output/Interface.hxx b/src/output/Interface.hxx index 645a94b2e..83737df5b 100644 --- a/src/output/Interface.hxx +++ b/src/output/Interface.hxx @@ -20,22 +20,33 @@ #ifndef MPD_AUDIO_OUTPUT_INTERFACE_HXX #define MPD_AUDIO_OUTPUT_INTERFACE_HXX -struct AudioOutputPlugin; +#include + +struct AudioFormat; +struct Tag; class AudioOutput { - /** - * The plugin which implements this output device. - */ - const AudioOutputPlugin &plugin; + const unsigned flags; bool need_fully_defined_audio_format = false; -public: - AudioOutput(const AudioOutputPlugin &_plugin) - :plugin(_plugin) {} +protected: + static constexpr unsigned FLAG_ENABLE_DISABLE = 0x1; + static constexpr unsigned FLAG_PAUSE = 0x2; - const AudioOutputPlugin &GetPlugin() const { - return plugin; +public: + explicit AudioOutput(unsigned _flags):flags(_flags) {} + virtual ~AudioOutput() = default; + + AudioOutput(const AudioOutput &) = delete; + AudioOutput &operator=(const AudioOutput &) = delete; + + bool SupportsEnableDisable() const { + return flags & FLAG_ENABLE_DISABLE; + } + + bool SupportsPause() const { + return flags & FLAG_PAUSE; } bool GetNeedFullyDefinedAudioFormat() const { @@ -50,6 +61,87 @@ public: void NeedFullyDefinedAudioFormat() { need_fully_defined_audio_format = true; } + + /** + * Enable the device. This may allocate resources, preparing + * for the device to be opened. + * + * Throws #std::runtime_error on error. + */ + virtual void Enable() {} + + /** + * Disables the device. It is closed before this method is + * called. + */ + virtual void Disable() noexcept {} + + /** + * Really open the device. + * + * Throws #std::runtime_error on error. + * + * @param audio_format the audio format in which data is going + * to be delivered; may be modified by the plugin + */ + virtual void Open(AudioFormat &audio_format) = 0; + + /** + * Close the device. + */ + virtual void Close() noexcept = 0; + + /** + * Returns a positive number if the output thread shall further + * delay the next call to Play() or Pause(), which will happen + * until this function returns 0. This should be implemented + * instead of doing a sleep inside the plugin, because this + * allows MPD to listen to commands meanwhile. + * + * @return the duration to wait + */ + virtual std::chrono::steady_clock::duration Delay() const noexcept { + return std::chrono::steady_clock::duration::zero(); + } + + /** + * Display metadata for the next chunk. Optional method, + * because not all devices can display metadata. + */ + virtual void SendTag(const Tag &) {} + + /** + * Play a chunk of audio data. + * + * Throws #std::runtime_error on error. + * + * @return the number of bytes played + */ + virtual size_t Play(const void *chunk, size_t size) = 0; + + /** + * Wait until the device has finished playing. + */ + virtual void Drain() {} + + /** + * Try to cancel data which may still be in the device's + * buffers. + */ + virtual void Cancel() noexcept {} + + /** + * Pause the device. If supported, it may perform a special + * action, which keeps the device open, but does not play + * anything. Output plugins like "shout" might want to play + * silence during pause, so their clients won't be + * disconnected. Plugins which do not support pausing will + * simply be closed, and have to be reopened when unpaused. + * + * @return false on error (output will be closed by caller), + * true for continue to pause + */ + virtual bool Pause() noexcept { return true; } }; #endif diff --git a/src/output/OutputPlugin.cxx b/src/output/OutputPlugin.cxx index e0a42e7e5..40ca82be0 100644 --- a/src/output/OutputPlugin.cxx +++ b/src/output/OutputPlugin.cxx @@ -19,7 +19,6 @@ #include "config.h" #include "OutputPlugin.hxx" -#include "Interface.hxx" #include @@ -32,76 +31,3 @@ ao_plugin_init(EventLoop &event_loop, return plugin.init(event_loop, block); } - -void -ao_plugin_finish(AudioOutput *ao) noexcept -{ - ao->GetPlugin().finish(ao); -} - -void -ao_plugin_enable(AudioOutput &ao) -{ - if (ao.GetPlugin().enable != nullptr) - ao.GetPlugin().enable(&ao); -} - -void -ao_plugin_disable(AudioOutput &ao) noexcept -{ - if (ao.GetPlugin().disable != nullptr) - ao.GetPlugin().disable(&ao); -} - -void -ao_plugin_open(AudioOutput &ao, AudioFormat &audio_format) -{ - ao.GetPlugin().open(&ao, audio_format); -} - -void -ao_plugin_close(AudioOutput &ao) noexcept -{ - ao.GetPlugin().close(&ao); -} - -std::chrono::steady_clock::duration -ao_plugin_delay(AudioOutput &ao) noexcept -{ - return ao.GetPlugin().delay != nullptr - ? ao.GetPlugin().delay(&ao) - : std::chrono::steady_clock::duration::zero(); -} - -void -ao_plugin_send_tag(AudioOutput &ao, const Tag &tag) -{ - if (ao.GetPlugin().send_tag != nullptr) - ao.GetPlugin().send_tag(&ao, tag); -} - -size_t -ao_plugin_play(AudioOutput &ao, const void *chunk, size_t size) -{ - return ao.GetPlugin().play(&ao, chunk, size); -} - -void -ao_plugin_drain(AudioOutput &ao) -{ - if (ao.GetPlugin().drain != nullptr) - ao.GetPlugin().drain(&ao); -} - -void -ao_plugin_cancel(AudioOutput &ao) noexcept -{ - if (ao.GetPlugin().cancel != nullptr) - ao.GetPlugin().cancel(&ao); -} - -bool -ao_plugin_pause(AudioOutput &ao) -{ - return ao.GetPlugin().pause != nullptr && ao.GetPlugin().pause(&ao); -} diff --git a/src/output/OutputPlugin.hxx b/src/output/OutputPlugin.hxx index 254f783fe..8738131d0 100644 --- a/src/output/OutputPlugin.hxx +++ b/src/output/OutputPlugin.hxx @@ -59,91 +59,6 @@ struct AudioOutputPlugin { */ AudioOutput *(*init)(EventLoop &event_loop, const ConfigBlock &block); - /** - * Free resources allocated by this device. - */ - void (*finish)(AudioOutput *data); - - /** - * Enable the device. This may allocate resources, preparing - * for the device to be opened. - * - * Throws #std::runtime_error on error. - */ - void (*enable)(AudioOutput *data); - - /** - * Disables the device. It is closed before this method is - * called. - */ - void (*disable)(AudioOutput *data); - - /** - * Really open the device. - * - * Throws #std::runtime_error on error. - * - * @param audio_format the audio format in which data is going - * to be delivered; may be modified by the plugin - */ - void (*open)(AudioOutput *data, AudioFormat &audio_format); - - /** - * Close the device. - */ - void (*close)(AudioOutput *data); - - /** - * Returns a positive number if the output thread shall further - * delay the next call to play() or pause(), which will happen - * until this function returns 0. This should be implemented - * instead of doing a sleep inside the plugin, because this - * allows MPD to listen to commands meanwhile. - * - * @return the duration to wait - */ - std::chrono::steady_clock::duration (*delay)(AudioOutput *data) noexcept; - - /** - * Display metadata for the next chunk. Optional method, - * because not all devices can display metadata. - */ - void (*send_tag)(AudioOutput *data, const Tag &tag); - - /** - * Play a chunk of audio data. - * - * Throws #std::runtime_error on error. - * - * @return the number of bytes played - */ - size_t (*play)(AudioOutput *data, - const void *chunk, size_t size); - - /** - * Wait until the device has finished playing. - */ - void (*drain)(AudioOutput *data); - - /** - * Try to cancel data which may still be in the device's - * buffers. - */ - void (*cancel)(AudioOutput *data); - - /** - * Pause the device. If supported, it may perform a special - * action, which keeps the device open, but does not play - * anything. Output plugins like "shout" might want to play - * silence during pause, so their clients won't be - * disconnected. Plugins which do not support pausing will - * simply be closed, and have to be reopened when unpaused. - * - * @return false on error (output will be closed by caller), - * true for continue to pause - */ - bool (*pause)(AudioOutput *data); - /** * The mixer plugin associated with this output plugin. This * may be nullptr if no mixer plugin is implemented. When @@ -167,38 +82,4 @@ ao_plugin_init(EventLoop &event_loop, const AudioOutputPlugin &plugin, const ConfigBlock &block); -void -ao_plugin_finish(AudioOutput *ao) noexcept; - -void -ao_plugin_enable(AudioOutput &ao); - -void -ao_plugin_disable(AudioOutput &ao) noexcept; - -void -ao_plugin_open(AudioOutput &ao, AudioFormat &audio_format); - -void -ao_plugin_close(AudioOutput &ao) noexcept; - -gcc_pure -std::chrono::steady_clock::duration -ao_plugin_delay(AudioOutput &ao) noexcept; - -void -ao_plugin_send_tag(AudioOutput &ao, const Tag &tag); - -size_t -ao_plugin_play(AudioOutput &ao, const void *chunk, size_t size); - -void -ao_plugin_drain(AudioOutput &ao); - -void -ao_plugin_cancel(AudioOutput &ao) noexcept; - -bool -ao_plugin_pause(AudioOutput &ao); - #endif diff --git a/src/output/Wrapper.hxx b/src/output/Wrapper.hxx deleted file mode 100644 index ac3e9f168..000000000 --- a/src/output/Wrapper.hxx +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2003-2017 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_WRAPPER_HXX -#define MPD_OUTPUT_WRAPPER_HXX - -#include "Interface.hxx" -#include "util/Cast.hxx" - -#include - -struct ConfigBlock; -struct AudioFormat; -struct Tag; - -template -struct AudioOutputWrapper { - static T &Cast(AudioOutput &ao) { - return ContainerCast(ao, &T::base); - } - - static AudioOutput *Init(EventLoop &event_loop, - const ConfigBlock &block) { - T *t = T::Create(event_loop, block); - return &t->base; - } - - static void Finish(AudioOutput *ao) { - T *t = &Cast(*ao); - delete t; - } - - static void Enable(AudioOutput *ao) { - T &t = Cast(*ao); - t.Enable(); - } - - static void Disable(AudioOutput *ao) { - T &t = Cast(*ao); - t.Disable(); - } - - static void Open(AudioOutput *ao, AudioFormat &audio_format) { - T &t = Cast(*ao); - t.Open(audio_format); - } - - static void Close(AudioOutput *ao) { - T &t = Cast(*ao); - t.Close(); - } - - gcc_pure - static std::chrono::steady_clock::duration Delay(AudioOutput *ao) noexcept { - T &t = Cast(*ao); - return t.Delay(); - } - - static void SendTag(AudioOutput *ao, const Tag &tag) { - T &t = Cast(*ao); - t.SendTag(tag); - } - - static size_t Play(AudioOutput *ao, const void *chunk, size_t size) { - T &t = Cast(*ao); - return t.Play(chunk, size); - } - - static void Drain(AudioOutput *ao) { - T &t = Cast(*ao); - t.Drain(); - } - - static void Cancel(AudioOutput *ao) { - T &t = Cast(*ao); - t.Cancel(); - } - - static bool Pause(AudioOutput *ao) { - T &t = Cast(*ao); - return t.Pause(); - } -}; - -#endif diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index b1b44b8bc..60c738466 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -22,7 +22,6 @@ #include "lib/alsa/NonBlock.hxx" #include "lib/alsa/Version.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "pcm/PcmExport.hxx" #include "system/ByteOrder.hxx" @@ -60,11 +59,7 @@ static constexpr unsigned MPD_ALSA_BUFFER_TIME_US = 500000; static constexpr unsigned MPD_ALSA_RETRY_NR = 5; class AlsaOutput final - : MultiSocketMonitor, DeferredMonitor { - - friend struct AudioOutputWrapper; - - AudioOutput base; + : AudioOutput, MultiSocketMonitor, DeferredMonitor { Manual pcm_export; @@ -290,20 +285,22 @@ public: return device.empty() ? default_device : device.c_str(); } - static AlsaOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); - - void Enable(); - void Disable(); - - void Open(AudioFormat &audio_format); - void Close(); - - size_t Play(const void *chunk, size_t size); - void Drain(); - void Cancel(); + static AudioOutput *Create(EventLoop &event_loop, + const ConfigBlock &block) { + return new AlsaOutput(event_loop, block); + } private: + void Enable() override; + void Disable() noexcept override; + + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; + + size_t Play(const void *chunk, size_t size) override; + void Drain() override; + void Cancel() noexcept override; + /** * Set up the snd_pcm_t object which was opened by the caller. * Set up the configured settings and the audio format. @@ -413,8 +410,8 @@ private: static constexpr Domain alsa_output_domain("alsa_output"); AlsaOutput::AlsaOutput(EventLoop &loop, const ConfigBlock &block) - :MultiSocketMonitor(loop), DeferredMonitor(loop), - base(alsa_output_plugin), + :AudioOutput(FLAG_ENABLE_DISABLE), + MultiSocketMonitor(loop), DeferredMonitor(loop), device(block.GetBlockValue("device", "")), #ifdef ENABLE_DSD dop(block.GetBlockValue("dop", false) || @@ -441,20 +438,14 @@ AlsaOutput::AlsaOutput(EventLoop &loop, const ConfigBlock &block) #endif } -inline AlsaOutput * -AlsaOutput::Create(EventLoop &event_loop, const ConfigBlock &block) -{ - return new AlsaOutput(event_loop, block); -} - -inline void +void AlsaOutput::Enable() { pcm_export.Construct(); } -inline void -AlsaOutput::Disable() +void +AlsaOutput::Disable() noexcept { pcm_export.Destruct(); } @@ -1015,7 +1006,7 @@ MaybeDmix(snd_pcm_t *pcm) noexcept return MaybeDmix(snd_pcm_type(pcm)); } -inline void +void AlsaOutput::Open(AudioFormat &audio_format) { int err = snd_pcm_open(&pcm, GetDevice(), @@ -1158,7 +1149,7 @@ AlsaOutput::DrainInternal() return snd_pcm_drain(pcm) != -EAGAIN; } -inline void +void AlsaOutput::Drain() { const std::lock_guard lock(mutex); @@ -1183,8 +1174,8 @@ AlsaOutput::CancelInternal() ClearRingBuffer(); } -inline void -AlsaOutput::Cancel() +void +AlsaOutput::Cancel() noexcept { if (!active) { /* early cancel, quick code path without thread @@ -1202,8 +1193,8 @@ AlsaOutput::Cancel() }); } -inline void -AlsaOutput::Close() +void +AlsaOutput::Close() noexcept { /* make sure the I/O thread isn't inside DispatchSockets() */ BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){ @@ -1217,7 +1208,7 @@ AlsaOutput::Close() delete[] silence; } -inline size_t +size_t AlsaOutput::Play(const void *chunk, size_t size) { assert(size > 0); @@ -1331,23 +1322,9 @@ try { cond.signal(); } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin alsa_output_plugin = { "alsa", alsa_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, - &Wrapper::Enable, - &Wrapper::Disable, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - nullptr, - &Wrapper::Play, - &Wrapper::Drain, - &Wrapper::Cancel, - nullptr, - + &AlsaOutput::Create, &alsa_mixer_plugin, }; diff --git a/src/output/plugins/AoOutputPlugin.cxx b/src/output/plugins/AoOutputPlugin.cxx index ed8b359aa..30de2ead6 100644 --- a/src/output/plugins/AoOutputPlugin.cxx +++ b/src/output/plugins/AoOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "AoOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "system/Error.hxx" #include "util/DivideString.hxx" #include "util/SplitString.hxx" @@ -37,11 +36,7 @@ static ao_sample_format OUR_AO_FORMAT_INITIALIZER; static unsigned ao_output_ref; -class AoOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class AoOutput final : AudioOutput { const size_t write_size; int driver; ao_option *options = nullptr; @@ -51,14 +46,14 @@ class AoOutput { ~AoOutput(); public: - static AoOutput *Create(EventLoop &, const ConfigBlock &block) { + static AudioOutput *Create(EventLoop &, const ConfigBlock &block) { return new AoOutput(block); } - void Open(AudioFormat &audio_format); - void Close(); + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - size_t Play(const void *chunk, size_t size); + size_t Play(const void *chunk, size_t size) override; }; static constexpr Domain ao_output_domain("ao_output"); @@ -95,7 +90,7 @@ MakeAoError() } AoOutput::AoOutput(const ConfigBlock &block) - :base(ao_output_plugin), + :AudioOutput(0), write_size(block.GetBlockValue("write_size", 1024u)) { if (ao_output_ref == 0) { @@ -177,7 +172,7 @@ AoOutput::Open(AudioFormat &audio_format) } void -AoOutput::Close() +AoOutput::Close() noexcept { ao_close(device); } @@ -199,22 +194,9 @@ AoOutput::Play(const void *chunk, size_t size) return size; } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin ao_output_plugin = { "ao", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - nullptr, - &Wrapper::Play, - nullptr, - nullptr, - nullptr, + &AoOutput::Create, nullptr, }; diff --git a/src/output/plugins/FifoOutputPlugin.cxx b/src/output/plugins/FifoOutputPlugin.cxx index 788ad85e6..da7b6de67 100644 --- a/src/output/plugins/FifoOutputPlugin.cxx +++ b/src/output/plugins/FifoOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "FifoOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "../Timer.hxx" #include "fs/AllocatedPath.hxx" #include "fs/FileSystem.hxx" @@ -34,11 +33,7 @@ #include #include -class FifoOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class FifoOutput final : AudioOutput { const AllocatedPath path; std::string path_utf8; @@ -54,9 +49,12 @@ public: CloseFifo(); } - static FifoOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); + static AudioOutput *Create(EventLoop &, + const ConfigBlock &block) { + return new FifoOutput(block); + } +private: void Create(); void Check(); void Delete(); @@ -64,18 +62,18 @@ public: void OpenFifo(); void CloseFifo(); - void Open(AudioFormat &audio_format); - void Close(); + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - std::chrono::steady_clock::duration Delay() const noexcept; - size_t Play(const void *chunk, size_t size); - void Cancel(); + std::chrono::steady_clock::duration Delay() const noexcept override; + size_t Play(const void *chunk, size_t size) override; + void Cancel() noexcept override; }; static constexpr Domain fifo_output_domain("fifo_output"); FifoOutput::FifoOutput(const ConfigBlock &block) - :base(fifo_output_plugin), + :AudioOutput(0), path(block.GetPath("path")) { if (path.IsNull()) @@ -169,12 +167,6 @@ try { throw; } -inline FifoOutput * -FifoOutput::Create(EventLoop &, const ConfigBlock &block) -{ - return new FifoOutput(block); -} - void FifoOutput::Open(AudioFormat &audio_format) { @@ -182,13 +174,13 @@ FifoOutput::Open(AudioFormat &audio_format) } void -FifoOutput::Close() +FifoOutput::Close() noexcept { delete timer; } -inline void -FifoOutput::Cancel() +void +FifoOutput::Cancel() noexcept { timer->Reset(); @@ -205,7 +197,7 @@ FifoOutput::Cancel() } } -inline std::chrono::steady_clock::duration +std::chrono::steady_clock::duration FifoOutput::Delay() const noexcept { return timer->IsStarted() @@ -213,7 +205,7 @@ FifoOutput::Delay() const noexcept : std::chrono::steady_clock::duration::zero(); } -inline size_t +size_t FifoOutput::Play(const void *chunk, size_t size) { if (!timer->IsStarted()) @@ -241,22 +233,9 @@ FifoOutput::Play(const void *chunk, size_t size) } } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin fifo_output_plugin = { "fifo", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - nullptr, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - nullptr, + &FifoOutput::Create, nullptr, }; diff --git a/src/output/plugins/HaikuOutputPlugin.cxx b/src/output/plugins/HaikuOutputPlugin.cxx index bb8d9a378..ff9c23f94 100644 --- a/src/output/plugins/HaikuOutputPlugin.cxx +++ b/src/output/plugins/HaikuOutputPlugin.cxx @@ -21,7 +21,6 @@ #include "config.h" #include "HaikuOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "util/Domain.hxx" #include "system/Error.hxx" @@ -43,13 +42,10 @@ #define UTF8_PLAY "\xE2\x96\xB6" -class HaikuOutput { - friend struct AudioOutputWrapper; +class HaikuOutput final: AudioOutput { friend int haiku_output_get_volume(HaikuOutput &haiku); friend bool haiku_output_set_volume(HaikuOutput &haiku, unsigned volume); - AudioOutput base; - size_t write_size; media_raw_audio_format format; @@ -66,27 +62,28 @@ class HaikuOutput { public: HaikuOutput(const ConfigBlock &block) - :base(haiku_output_plugin), + :AudioOutput(0), /* XXX: by default we should let the MediaKit propose the buffer size */ write_size(block.GetBlockValue("write_size", 4096u)) {} ~HaikuOutput(); - static HaikuOutput *Create(EventLoop &event_loop, + static AudioOutput *Create(EventLoop &event_loop, const ConfigBlock &block); - void Open(AudioFormat &audio_format); - void Close(); +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - size_t Play(const void *chunk, size_t size); - void Cancel(); + size_t Play(const void *chunk, size_t size) override; + void Cancel() noexcept override; - std::chrono::steady_clock::duration Delay() noexcept; + std::chrono::steady_clock::duration Delay() const noexcept override; void FillBuffer(void* _buffer, size_t size, gcc_unused const media_raw_audio_format& _format); - void SendTag(const Tag &tag); + void SendTag(const Tag &tag) override; }; static constexpr Domain haiku_output_domain("haiku_output"); @@ -120,7 +117,7 @@ haiku_test_default_device(void) } -inline HaikuOutput * +inline AudioOutput * HaikuOutput::Create(EventLoop &, const ConfigBlock &block) { initialize_application(); @@ -129,7 +126,7 @@ HaikuOutput::Create(EventLoop &, const ConfigBlock &block) } void -HaikuOutput::Close() +HaikuOutput::Close() noexcept { sound_player->SetHasData(false); delete_sem(new_buffer); @@ -139,8 +136,6 @@ HaikuOutput::Close() sound_player = nullptr; } - - HaikuOutput::~HaikuOutput() { delete_sem(new_buffer); @@ -186,7 +181,7 @@ HaikuOutput::FillBuffer(void* _buffer, size_t size, } } -inline void +void HaikuOutput::Open(AudioFormat &audio_format) { status_t err; @@ -265,7 +260,7 @@ HaikuOutput::Open(AudioFormat &audio_format) sound_player->SetHasData(false); } -inline size_t +size_t HaikuOutput::Play(const void *chunk, size_t size) { BSoundPlayer* const soundPlayer = sound_player; @@ -311,7 +306,7 @@ HaikuOutput::Play(const void *chunk, size_t size) } inline std::chrono::steady_clock::duration -HaikuOutput::Delay() noexcept +HaikuOutput::Delay() const noexcept { unsigned delay = buffer_filled ? 0 : buffer_delay; @@ -324,7 +319,7 @@ HaikuOutput::Delay() noexcept return std::chrono::steady_clock::duration::zero(); } -inline void +void HaikuOutput::SendTag(const Tag &tag) { status_t err; @@ -468,18 +463,6 @@ typedef AudioOutputWrapper Wrapper; const struct AudioOutputPlugin haiku_output_plugin = { "haiku", haiku_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - &Wrapper::SendTag, - &Wrapper::Play, - nullptr, - nullptr, - nullptr, - + &HaikuOutput::Create, &haiku_mixer_plugin, }; diff --git a/src/output/plugins/JackOutputPlugin.cxx b/src/output/plugins/JackOutputPlugin.cxx index e1884a95c..d0b62240f 100644 --- a/src/output/plugins/JackOutputPlugin.cxx +++ b/src/output/plugins/JackOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "JackOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "config/ConfigError.hxx" #include "util/ScopeExit.hxx" #include "util/ConstBuffer.hxx" @@ -42,9 +41,7 @@ static constexpr unsigned MAX_PORTS = 16; static constexpr size_t jack_sample_size = sizeof(jack_default_audio_sample_t); -struct JackOutput { - AudioOutput base; - +struct JackOutput final : AudioOutput { /** * libjack options passed to jack_client_open(). */ @@ -99,12 +96,12 @@ struct JackOutput { shutdown = true; } - void Enable(); - void Disable(); + void Enable() override; + void Disable() noexcept override; - void Open(AudioFormat &new_audio_format); + void Open(AudioFormat &new_audio_format) override; - void Close() { + void Close() noexcept override { Stop(); } @@ -128,15 +125,15 @@ struct JackOutput { */ size_t WriteSamples(const float *src, size_t n_frames); - std::chrono::steady_clock::duration Delay() const noexcept { + std::chrono::steady_clock::duration Delay() const noexcept override { return pause && !shutdown ? std::chrono::seconds(1) : std::chrono::steady_clock::duration::zero(); } - size_t Play(const void *chunk, size_t size); + size_t Play(const void *chunk, size_t size) override; - bool Pause(); + bool Pause() noexcept override; }; static constexpr Domain jack_output_domain("jack_output"); @@ -162,7 +159,7 @@ parse_port_list(const char *source, std::string dest[]) } JackOutput::JackOutput(const ConfigBlock &block) - :base(jack_output_plugin), + :AudioOutput(FLAG_ENABLE_DISABLE|FLAG_PAUSE), name(block.GetBlockValue("client_name", nullptr)), server_name(block.GetBlockValue("server_name", nullptr)) { @@ -430,7 +427,7 @@ JackOutput::Enable() } inline void -JackOutput::Disable() +JackOutput::Disable() noexcept { if (client != nullptr) Disconnect(); @@ -452,8 +449,7 @@ mpd_jack_init(EventLoop &, const ConfigBlock &block) jack_set_info_function(mpd_jack_info); #endif - auto *jd = new JackOutput(block); - return &jd->base; + return new JackOutput(block); } /** @@ -663,7 +659,7 @@ JackOutput::Play(const void *chunk, size_t size) } inline bool -JackOutput::Pause() +JackOutput::Pause() noexcept { if (shutdown) return false; @@ -673,22 +669,9 @@ JackOutput::Pause() return true; } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin jack_output_plugin = { "jack", mpd_jack_test_default_device, mpd_jack_init, - &Wrapper::Finish, - &Wrapper::Enable, - &Wrapper::Disable, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - nullptr, - &Wrapper::Play, - nullptr, - nullptr, - &Wrapper::Pause, nullptr, }; diff --git a/src/output/plugins/NullOutputPlugin.cxx b/src/output/plugins/NullOutputPlugin.cxx index 4b28751d5..bc87c690d 100644 --- a/src/output/plugins/NullOutputPlugin.cxx +++ b/src/output/plugins/NullOutputPlugin.cxx @@ -20,43 +20,41 @@ #include "config.h" #include "NullOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "../Timer.hxx" -class NullOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class NullOutput final : AudioOutput { const bool sync; Timer *timer; public: NullOutput(const ConfigBlock &block) - :base(null_output_plugin), + :AudioOutput(0), sync(block.GetBlockValue("sync", true)) {} - static NullOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); + static AudioOutput *Create(EventLoop &, + const ConfigBlock &block) { + return new NullOutput(block); + } - void Open(AudioFormat &audio_format) { +private: + void Open(AudioFormat &audio_format) override { if (sync) timer = new Timer(audio_format); } - void Close() { + void Close() noexcept override { if (sync) delete timer; } - std::chrono::steady_clock::duration Delay() const noexcept { + std::chrono::steady_clock::duration Delay() const noexcept override { return sync && timer->IsStarted() ? timer->GetDelay() : std::chrono::steady_clock::duration::zero(); } - size_t Play(gcc_unused const void *chunk, size_t size) { + size_t Play(gcc_unused const void *chunk, size_t size) override { if (sync) { if (!timer->IsStarted()) timer->Start(); @@ -66,34 +64,15 @@ public: return size; } - void Cancel() { + void Cancel() noexcept override { if (sync) timer->Reset(); } }; -inline NullOutput * -NullOutput::Create(EventLoop &, const ConfigBlock &block) -{ - return new NullOutput(block); -} - -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin null_output_plugin = { "null", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - nullptr, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - nullptr, + &NullOutput::Create, nullptr, }; diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 03c1bc6f1..eed0552da 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -35,9 +35,7 @@ #include -struct OSXOutput { - AudioOutput base; - +struct OSXOutput final : AudioOutput { /* configuration settings */ OSType component_subtype; /* only applicable with kAudioUnitSubType_HALOutput */ @@ -53,6 +51,18 @@ struct OSXOutput { boost::lockfree::spsc_queue *ring_buffer; OSXOutput(const ConfigBlock &block); + + static AudioOutput *Create(EventLoop &, const ConfigBlock &block); + +private: + void Enable() override; + void Disable() noexcept override; + + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; + + std::chrono::steady_clock::duration Delay() const noexcept override; + size_t Play(const void *chunk, size_t size) override; }; static constexpr Domain osx_output_domain("osx_output"); @@ -80,7 +90,7 @@ osx_output_test_default_device(void) } OSXOutput::OSXOutput(const ConfigBlock &block) - :base(osx_output_plugin) + :AudioOutput(FLAG_ENABLE_DISABLE) { const char *device = block.GetBlockValue("device"); @@ -103,8 +113,8 @@ OSXOutput::OSXOutput(const ConfigBlock &block) sync_sample_rate = block.GetBlockValue("sync_sample_rate", false); } -static AudioOutput * -osx_output_init(EventLoop &, const ConfigBlock &block) +AudioOutput * +OSXOutput::Create(EventLoop &, const ConfigBlock &block) { OSXOutput *oo = new OSXOutput(block); @@ -124,15 +134,7 @@ osx_output_init(EventLoop &, const ConfigBlock &block) &dev_id); oo->dev_id = dev_id; - return &oo->base; -} - -static void -osx_output_finish(AudioOutput *ao) -{ - OSXOutput *oo = (OSXOutput *)ao; - - delete oo; + return oo; } static void @@ -513,15 +515,14 @@ osx_render(void *vdata, return noErr; } -static void -osx_output_enable(AudioOutput *ao) +void +OSXOutput::Enable() { char errormsg[1024]; - OSXOutput *oo = (OSXOutput *)ao; AudioComponentDescription desc; desc.componentType = kAudioUnitType_Output; - desc.componentSubType = oo->component_subtype; + desc.componentSubType = component_subtype; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; @@ -530,7 +531,7 @@ osx_output_enable(AudioOutput *ao) if (comp == 0) throw std::runtime_error("Error finding OS X component"); - OSStatus status = AudioComponentInstanceNew(comp, &oo->au); + OSStatus status = AudioComponentInstanceNew(comp, &au); if (status != noErr) { osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); throw FormatRuntimeError("Unable to open OS X component: %s", @@ -538,105 +539,97 @@ osx_output_enable(AudioOutput *ao) } try { - osx_output_set_device(oo); + osx_output_set_device(this); } catch (...) { - AudioComponentInstanceDispose(oo->au); + AudioComponentInstanceDispose(au); throw; } - if (oo->hog_device) { - osx_output_hog_device(oo->dev_id, true); - } + if (hog_device) + osx_output_hog_device(dev_id, true); } -static void -osx_output_disable(AudioOutput *ao) +void +OSXOutput::Disable() noexcept { - OSXOutput *oo = (OSXOutput *)ao; + AudioComponentInstanceDispose(au); - AudioComponentInstanceDispose(oo->au); - - if (oo->hog_device) { - osx_output_hog_device(oo->dev_id, false); - } + if (hog_device) + osx_output_hog_device(dev_id, false); } -static void -osx_output_close(AudioOutput *ao) +void +OSXOutput::Close() noexcept { - OSXOutput *od = (OSXOutput *)ao; + AudioOutputUnitStop(au); + AudioUnitUninitialize(au); - AudioOutputUnitStop(od->au); - AudioUnitUninitialize(od->au); - - delete od->ring_buffer; + delete ring_buffer; } -static void -osx_output_open(AudioOutput *ao, AudioFormat &audio_format) +void +OSXOutput::Open(AudioFormat &audio_format) { char errormsg[1024]; - OSXOutput *od = (OSXOutput *)ao; - memset(&od->asbd, 0, sizeof(od->asbd)); - od->asbd.mSampleRate = audio_format.sample_rate; - od->asbd.mFormatID = kAudioFormatLinearPCM; - od->asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + memset(&asbd, 0, sizeof(asbd)); + asbd.mSampleRate = audio_format.sample_rate; + asbd.mFormatID = kAudioFormatLinearPCM; + asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; switch (audio_format.format) { case SampleFormat::S8: - od->asbd.mBitsPerChannel = 8; + asbd.mBitsPerChannel = 8; break; case SampleFormat::S16: - od->asbd.mBitsPerChannel = 16; + asbd.mBitsPerChannel = 16; break; case SampleFormat::S32: - od->asbd.mBitsPerChannel = 32; + asbd.mBitsPerChannel = 32; break; default: audio_format.format = SampleFormat::S32; - od->asbd.mBitsPerChannel = 32; + asbd.mBitsPerChannel = 32; break; } if (IsBigEndian()) - od->asbd.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; + asbd.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; - od->asbd.mBytesPerPacket = audio_format.GetFrameSize(); - od->asbd.mFramesPerPacket = 1; - od->asbd.mBytesPerFrame = od->asbd.mBytesPerPacket; - od->asbd.mChannelsPerFrame = audio_format.channels; + asbd.mBytesPerPacket = audio_format.GetFrameSize(); + asbd.mFramesPerPacket = 1; + asbd.mBytesPerFrame = asbd.mBytesPerPacket; + asbd.mChannelsPerFrame = audio_format.channels; - if (od->sync_sample_rate) { - osx_output_sync_device_sample_rate(od->dev_id, od->asbd); - } + if (sync_sample_rate) + osx_output_sync_device_sample_rate(dev_id, asbd); OSStatus status = - AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, + AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, - &od->asbd, - sizeof(od->asbd)); + &asbd, + sizeof(asbd)); if (status != noErr) throw std::runtime_error("Unable to set format on OS X device"); AURenderCallbackStruct callback; callback.inputProc = osx_render; - callback.inputProcRefCon = od; + callback.inputProcRefCon = this; status = - AudioUnitSetProperty(od->au, + AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)); if (status != noErr) { - AudioComponentInstanceDispose(od->au); + AudioComponentInstanceDispose(au); throw std::runtime_error("unable to set callback for OS X audio unit"); } - status = AudioUnitInitialize(od->au); + status = AudioUnitInitialize(au); if (status != noErr) { osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); throw FormatRuntimeError("Unable to initialize OS X audio unit: %s", @@ -644,36 +637,34 @@ osx_output_open(AudioOutput *ao, AudioFormat &audio_format) } UInt32 buffer_frame_size = 1; - status = osx_output_set_buffer_size(od->au, od->asbd, &buffer_frame_size); + status = osx_output_set_buffer_size(au, asbd, &buffer_frame_size); if (status != noErr) { osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); throw FormatRuntimeError("Unable to set frame size: %s", errormsg); } - od->ring_buffer = new boost::lockfree::spsc_queue(buffer_frame_size); + ring_buffer = new boost::lockfree::spsc_queue(buffer_frame_size); - status = AudioOutputUnitStart(od->au); + status = AudioOutputUnitStart(au); if (status != 0) { - AudioUnitUninitialize(od->au); + AudioUnitUninitialize(au); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); throw FormatRuntimeError("unable to start audio output: %s", errormsg); } } -static size_t -osx_output_play(AudioOutput *ao, const void *chunk, size_t size) +size_t +OSXOutput::Play(const void *chunk, size_t size) { - OSXOutput *od = (OSXOutput *)ao; - return od->ring_buffer->push((uint8_t *)chunk, size); + return ring_buffer->push((uint8_t *)chunk, size); } -static std::chrono::steady_clock::duration -osx_output_delay(AudioOutput *ao) noexcept +std::chrono::steady_clock::duration +OSXOutput::Delay() const noexcept { - OSXOutput *od = (OSXOutput *)ao; - return od->ring_buffer->write_available() + return ring_buffer->write_available() ? std::chrono::steady_clock::duration::zero() : std::chrono::milliseconds(25); } @@ -681,17 +672,6 @@ osx_output_delay(AudioOutput *ao) noexcept const struct AudioOutputPlugin osx_output_plugin = { "osx", osx_output_test_default_device, - osx_output_init, - osx_output_finish, - osx_output_enable, - osx_output_disable, - osx_output_open, - osx_output_close, - osx_output_delay, - nullptr, - osx_output_play, - nullptr, - nullptr, - nullptr, + &OSXOutput::Create, nullptr, }; diff --git a/src/output/plugins/OpenALOutputPlugin.cxx b/src/output/plugins/OpenALOutputPlugin.cxx index 0bdd98038..c96f14ede 100644 --- a/src/output/plugins/OpenALOutputPlugin.cxx +++ b/src/output/plugins/OpenALOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "OpenALOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "util/RuntimeError.hxx" #include @@ -33,14 +32,10 @@ #include #endif -class OpenALOutput { - friend struct AudioOutputWrapper; - +class OpenALOutput final : AudioOutput { /* should be enough for buffer size = 2048 */ static constexpr unsigned NUM_BUFFERS = 16; - AudioOutput base; - const char *device_name; ALCdevice *device; ALCcontext *context; @@ -52,14 +47,18 @@ class OpenALOutput { OpenALOutput(const ConfigBlock &block); - static OpenALOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); +public: + static AudioOutput *Create(EventLoop &, + const ConfigBlock &block) { + return new OpenALOutput(block); + } - void Open(AudioFormat &audio_format); - void Close(); +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; gcc_pure - std::chrono::steady_clock::duration Delay() const noexcept { + std::chrono::steady_clock::duration Delay() const noexcept override { return filled < NUM_BUFFERS || HasProcessed() ? std::chrono::steady_clock::duration::zero() /* we don't know exactly how long we must wait @@ -68,9 +67,9 @@ class OpenALOutput { : std::chrono::milliseconds(50); } - size_t Play(const void *chunk, size_t size); + size_t Play(const void *chunk, size_t size) override; - void Cancel(); + void Cancel() noexcept override; private: gcc_pure @@ -138,7 +137,7 @@ OpenALOutput::SetupContext() } OpenALOutput::OpenALOutput(const ConfigBlock &block) - :base(openal_output_plugin), + :AudioOutput(0), device_name(block.GetBlockValue("device")) { if (device_name == nullptr) @@ -146,13 +145,7 @@ OpenALOutput::OpenALOutput(const ConfigBlock &block) ALC_DEFAULT_DEVICE_SPECIFIER); } -inline OpenALOutput * -OpenALOutput::Create(EventLoop &, const ConfigBlock &block) -{ - return new OpenALOutput(block); -} - -inline void +void OpenALOutput::Open(AudioFormat &audio_format) { format = openal_audio_format(audio_format); @@ -176,8 +169,8 @@ OpenALOutput::Open(AudioFormat &audio_format) frequency = audio_format.sample_rate; } -inline void -OpenALOutput::Close() +void +OpenALOutput::Close() noexcept { alcMakeContextCurrent(context); alDeleteSources(1, &source); @@ -186,7 +179,7 @@ OpenALOutput::Close() alcCloseDevice(device); } -inline size_t +size_t OpenALOutput::Play(const void *chunk, size_t size) { if (alcGetCurrentContext() != context) @@ -214,8 +207,8 @@ OpenALOutput::Play(const void *chunk, size_t size) return size; } -inline void -OpenALOutput::Cancel() +void +OpenALOutput::Cancel() noexcept { filled = 0; alcMakeContextCurrent(context); @@ -226,22 +219,9 @@ OpenALOutput::Cancel() filled = 0; } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin openal_output_plugin = { "openal", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - nullptr, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - nullptr, + OpenALOutput::Create, nullptr, }; diff --git a/src/output/plugins/OssOutputPlugin.cxx b/src/output/plugins/OssOutputPlugin.cxx index 6581237fa..512ec67bc 100644 --- a/src/output/plugins/OssOutputPlugin.cxx +++ b/src/output/plugins/OssOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "OssOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "system/fd_util.h" #include "system/Error.hxx" @@ -60,11 +59,7 @@ #include "util/Manual.hxx" #endif -class OssOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class OssOutput final : AudioOutput { #ifdef AFMT_S24_PACKED Manual pcm_export; #endif @@ -84,32 +79,38 @@ class OssOutput { */ int oss_format; +#ifdef AFMT_S24_PACKED + static constexpr unsigned oss_flags = FLAG_ENABLE_DISABLE; +#else + static constexpr unsigned oss_flags = 0; +#endif + public: explicit OssOutput(const char *_device=nullptr) - :base(oss_output_plugin), + :AudioOutput(oss_flags), fd(-1), device(_device) {} - static OssOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); + static AudioOutput *Create(EventLoop &event_loop, + const ConfigBlock &block); #ifdef AFMT_S24_PACKED - void Enable() { + void Enable() override { pcm_export.Construct(); } - void Disable() { + void Disable() noexcept override { pcm_export.Destruct(); } #endif - void Open(AudioFormat &audio_format); + void Open(AudioFormat &audio_format) override; - void Close() { + void Close() noexcept override { DoClose(); } - size_t Play(const void *chunk, size_t size); - void Cancel(); + size_t Play(const void *chunk, size_t size) override; + void Cancel() noexcept override; private: /** @@ -225,7 +226,7 @@ oss_open_default() throw std::runtime_error("error trying to open default OSS device"); } -inline OssOutput * +AudioOutput * OssOutput::Create(EventLoop &, const ConfigBlock &block) { const char *device = block.GetBlockValue("device"); @@ -637,7 +638,7 @@ try { throw; } -inline void +void OssOutput::Open(AudioFormat &_audio_format) try { fd = open_cloexec(device, O_WRONLY, 0); @@ -652,8 +653,8 @@ try { throw; } -inline void -OssOutput::Cancel() +void +OssOutput::Cancel() noexcept { if (fd >= 0) { ioctl(fd, SNDCTL_DSP_RESET, 0); @@ -665,7 +666,7 @@ OssOutput::Cancel() #endif } -inline size_t +size_t OssOutput::Play(const void *chunk, size_t size) { ssize_t ret; @@ -698,28 +699,9 @@ OssOutput::Play(const void *chunk, size_t size) } } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin oss_output_plugin = { "oss", oss_output_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, -#ifdef AFMT_S24_PACKED - &Wrapper::Enable, - &Wrapper::Disable, -#else - nullptr, - nullptr, -#endif - &Wrapper::Open, - &Wrapper::Close, - nullptr, - nullptr, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - nullptr, - + OssOutput::Create, &oss_mixer_plugin, }; diff --git a/src/output/plugins/PipeOutputPlugin.cxx b/src/output/plugins/PipeOutputPlugin.cxx index b0f7f6492..623b88a9a 100644 --- a/src/output/plugins/PipeOutputPlugin.cxx +++ b/src/output/plugins/PipeOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "PipeOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "system/Error.hxx" #include @@ -28,43 +27,36 @@ #include -class PipeOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class PipeOutput final : AudioOutput { const std::string cmd; FILE *fh; PipeOutput(const ConfigBlock &block); public: - static PipeOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); + static AudioOutput *Create(EventLoop &, + const ConfigBlock &block) { + return new PipeOutput(block); + } - void Open(AudioFormat &audio_format); +private: + void Open(AudioFormat &audio_format) override; - void Close() { + void Close() noexcept override { pclose(fh); } - size_t Play(const void *chunk, size_t size); + size_t Play(const void *chunk, size_t size) override; }; PipeOutput::PipeOutput(const ConfigBlock &block) - :base(pipe_output_plugin), + :AudioOutput(0), cmd(block.GetBlockValue("command", "")) { if (cmd.empty()) throw std::runtime_error("No \"command\" parameter specified"); } -inline PipeOutput * -PipeOutput::Create(EventLoop &, const ConfigBlock &block) -{ - return new PipeOutput(block); -} - inline void PipeOutput::Open(gcc_unused AudioFormat &audio_format) { @@ -83,22 +75,9 @@ PipeOutput::Play(const void *chunk, size_t size) return nbytes; } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin pipe_output_plugin = { "pipe", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - nullptr, - &Wrapper::Play, - nullptr, - nullptr, - nullptr, + &PipeOutput::Create, nullptr, }; diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index da18bfc4b..fa603d5f7 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -24,7 +24,6 @@ #include "lib/pulse/LogError.hxx" #include "lib/pulse/LockGuard.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "mixer/plugins/PulseMixerPlugin.hxx" #include "Log.hxx" @@ -44,11 +43,7 @@ #define MPD_PULSE_NAME "Music Player Daemon" -class PulseOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class PulseOutput final : AudioOutput { const char *name; const char *server; const char *sink; @@ -94,19 +89,21 @@ public: static bool TestDefaultDevice(); - static PulseOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); + static AudioOutput *Create(EventLoop &, + const ConfigBlock &block) { + return new PulseOutput(block); + } - void Enable(); - void Disable(); + void Enable() override; + void Disable() noexcept override; - void Open(AudioFormat &audio_format); - void Close(); + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - std::chrono::steady_clock::duration Delay() noexcept; - size_t Play(const void *chunk, size_t size); - void Cancel(); - bool Pause(); + std::chrono::steady_clock::duration Delay() const noexcept override; + size_t Play(const void *chunk, size_t size) override; + void Cancel() noexcept override; + bool Pause() noexcept override; private: /** @@ -179,7 +176,7 @@ private: }; PulseOutput::PulseOutput(const ConfigBlock &block) - :base(pulse_output_plugin), + :AudioOutput(FLAG_ENABLE_DISABLE|FLAG_PAUSE), name(block.GetBlockValue("name", "mpd_pulse")), server(block.GetBlockValue("server")), sink(block.GetBlockValue("sink")) @@ -416,13 +413,7 @@ PulseOutput::SetupContext() } } -PulseOutput * -PulseOutput::Create(EventLoop &, const ConfigBlock &block) -{ - return new PulseOutput(block); -} - -inline void +void PulseOutput::Enable() { assert(mainloop == nullptr); @@ -458,8 +449,8 @@ PulseOutput::Enable() pa_threaded_mainloop_unlock(mainloop); } -inline void -PulseOutput::Disable() +void +PulseOutput::Disable() noexcept { assert(mainloop != nullptr); @@ -607,7 +598,7 @@ PulseOutput::SetupStream(const pa_sample_spec &ss) pulse_output_stream_write_cb, this); } -inline void +void PulseOutput::Open(AudioFormat &audio_format) { assert(mainloop != nullptr); @@ -680,8 +671,8 @@ PulseOutput::Open(AudioFormat &audio_format) pause = false; } -inline void -PulseOutput::Close() +void +PulseOutput::Close() noexcept { assert(mainloop != nullptr); @@ -744,8 +735,8 @@ PulseOutput::StreamPause(bool _pause) "pa_stream_cork() has failed"); } -inline std::chrono::steady_clock::duration -PulseOutput::Delay() noexcept +std::chrono::steady_clock::duration +PulseOutput::Delay() const noexcept { Pulse::LockGuard lock(mainloop); @@ -758,7 +749,7 @@ PulseOutput::Delay() noexcept return result; } -inline size_t +size_t PulseOutput::Play(const void *chunk, size_t size) { assert(mainloop != nullptr); @@ -807,8 +798,8 @@ PulseOutput::Play(const void *chunk, size_t size) return size; } -inline void -PulseOutput::Cancel() +void +PulseOutput::Cancel() noexcept { assert(mainloop != nullptr); assert(stream != nullptr); @@ -834,8 +825,8 @@ PulseOutput::Cancel() pulse_wait_for_operation(mainloop, o); } -inline bool -PulseOutput::Pause() +bool +PulseOutput::Pause() noexcept { assert(mainloop != nullptr); assert(stream != nullptr); @@ -875,23 +866,9 @@ pulse_output_test_default_device(void) return PulseOutput::TestDefaultDevice(); } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin pulse_output_plugin = { "pulse", pulse_output_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, - &Wrapper::Enable, - &Wrapper::Disable, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - nullptr, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - &Wrapper::Pause, - + PulseOutput::Create, &pulse_mixer_plugin, }; diff --git a/src/output/plugins/RecorderOutputPlugin.cxx b/src/output/plugins/RecorderOutputPlugin.cxx index a57a54773..91a88c7a2 100644 --- a/src/output/plugins/RecorderOutputPlugin.cxx +++ b/src/output/plugins/RecorderOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "RecorderOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "tag/Format.hxx" #include "encoder/ToOutputStream.hxx" #include "encoder/EncoderInterface.hxx" @@ -42,11 +41,7 @@ static constexpr Domain recorder_domain("recorder"); -class RecorderOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class RecorderOutput final : AudioOutput { /** * The configured encoder plugin. */ @@ -81,20 +76,23 @@ class RecorderOutput { delete prepared_encoder; } - static RecorderOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); +public: + static AudioOutput *Create(EventLoop &, const ConfigBlock &block) { + return new RecorderOutput(block); + } - void Open(AudioFormat &audio_format); - void Close(); +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; /** * Writes pending data from the encoder to the output file. */ void EncoderToFile(); - void SendTag(const Tag &tag); + void SendTag(const Tag &tag) override; - size_t Play(const void *chunk, size_t size); + size_t Play(const void *chunk, size_t size) override; private: gcc_pure @@ -114,7 +112,7 @@ private: }; RecorderOutput::RecorderOutput(const ConfigBlock &block) - :base(recorder_output_plugin) + :AudioOutput(0) { /* read configuration */ @@ -141,12 +139,6 @@ RecorderOutput::RecorderOutput(const ConfigBlock &block) prepared_encoder = encoder_init(*encoder_plugin, block); } -RecorderOutput * -RecorderOutput::Create(EventLoop &, const ConfigBlock &block) -{ - return new RecorderOutput(block); -} - inline void RecorderOutput::EncoderToFile() { @@ -155,7 +147,7 @@ RecorderOutput::EncoderToFile() EncoderToOutputStream(*file, *encoder); } -inline void +void RecorderOutput::Open(AudioFormat &audio_format) { /* create the output file */ @@ -227,8 +219,8 @@ RecorderOutput::Commit() delete file; } -inline void -RecorderOutput::Close() +void +RecorderOutput::Close() noexcept { if (file == nullptr) { /* not currently encoding to a file; nothing needs to @@ -305,7 +297,7 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path) path.ToUTF8().c_str()); } -inline void +void RecorderOutput::SendTag(const Tag &tag) { if (HasDynamicPath()) { @@ -347,7 +339,7 @@ RecorderOutput::SendTag(const Tag &tag) encoder->SendTag(tag); } -inline size_t +size_t RecorderOutput::Play(const void *chunk, size_t size) { if (file == nullptr) { @@ -365,22 +357,9 @@ RecorderOutput::Play(const void *chunk, size_t size) return size; } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin recorder_output_plugin = { "recorder", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - &Wrapper::SendTag, - &Wrapper::Play, - nullptr, - nullptr, - nullptr, + &RecorderOutput::Create, nullptr, }; diff --git a/src/output/plugins/RoarOutputPlugin.cxx b/src/output/plugins/RoarOutputPlugin.cxx index 6f0a31d22..6be6f2471 100644 --- a/src/output/plugins/RoarOutputPlugin.cxx +++ b/src/output/plugins/RoarOutputPlugin.cxx @@ -21,7 +21,6 @@ #include "config.h" #include "RoarOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "thread/Mutex.hxx" #include "util/Domain.hxx" @@ -36,11 +35,7 @@ #include #undef new -class RoarOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class RoarOutput final : AudioOutput { const std::string host, name; roar_vs_t * vss; @@ -54,23 +49,20 @@ class RoarOutput { public: RoarOutput(const ConfigBlock &block); - operator AudioOutput *() { - return &base; - } - - static RoarOutput *Create(EventLoop &, const ConfigBlock &block) { + static AudioOutput *Create(EventLoop &, const ConfigBlock &block) { return new RoarOutput(block); } - void Open(AudioFormat &audio_format); - void Close(); - - void SendTag(const Tag &tag); - size_t Play(const void *chunk, size_t size); - void Cancel(); - int GetVolume() const; void SetVolume(unsigned volume); + +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; + + void SendTag(const Tag &tag) override; + size_t Play(const void *chunk, size_t size) override; + void Cancel() noexcept override; }; static constexpr Domain roar_output_domain("roar_output"); @@ -86,7 +78,7 @@ GetConfiguredRole(const ConfigBlock &block) noexcept } RoarOutput::RoarOutput(const ConfigBlock &block) - :base(roar_output_plugin), + :AudioOutput(0), host(block.GetBlockValue("server", "")), name(block.GetBlockValue("name", "MPD")), role(GetConfiguredRole(block)) @@ -172,7 +164,7 @@ roar_use_audio_format(struct roar_audio_info *info, } } -inline void +void RoarOutput::Open(AudioFormat &audio_format) { const std::lock_guard protect(mutex); @@ -196,8 +188,8 @@ RoarOutput::Open(AudioFormat &audio_format) alive = true; } -inline void -RoarOutput::Close() +void +RoarOutput::Close() noexcept { const std::lock_guard protect(mutex); @@ -209,8 +201,8 @@ RoarOutput::Close() roar_disconnect(&con); } -inline void -RoarOutput::Cancel() +void +RoarOutput::Cancel() noexcept { const std::lock_guard protect(mutex); @@ -237,7 +229,7 @@ RoarOutput::Cancel() alive = true; } -inline size_t +size_t RoarOutput::Play(const void *chunk, size_t size) { if (vss == nullptr) @@ -296,7 +288,7 @@ roar_tag_convert(TagType type, bool *is_uuid) } } -inline void +void RoarOutput::SendTag(const Tag &tag) { if (vss == nullptr) @@ -344,22 +336,9 @@ RoarOutput::SendTag(const Tag &tag) roar_vs_meta(vss, vals, cnt, &(err)); } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin roar_output_plugin = { "roar", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - &Wrapper::SendTag, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - nullptr, + &RoarOutput::Create, &roar_mixer_plugin, }; diff --git a/src/output/plugins/ShoutOutputPlugin.cxx b/src/output/plugins/ShoutOutputPlugin.cxx index ecde49c81..b2aa904b4 100644 --- a/src/output/plugins/ShoutOutputPlugin.cxx +++ b/src/output/plugins/ShoutOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "ShoutOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "encoder/EncoderInterface.hxx" #include "encoder/EncoderPlugin.hxx" #include "encoder/EncoderList.hxx" @@ -39,9 +38,7 @@ static constexpr unsigned DEFAULT_CONN_TIMEOUT = 2; -struct ShoutOutput final { - AudioOutput base; - +struct ShoutOutput final : AudioOutput { shout_t *shout_conn; shout_metadata_t *shout_meta; @@ -58,17 +55,17 @@ struct ShoutOutput final { explicit ShoutOutput(const ConfigBlock &block); ~ShoutOutput(); - static ShoutOutput *Create(EventLoop &event_loop, + static AudioOutput *Create(EventLoop &event_loop, const ConfigBlock &block); - void Open(AudioFormat &audio_format); - void Close(); + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - std::chrono::steady_clock::duration Delay() const noexcept; - void SendTag(const Tag &tag); - size_t Play(const void *chunk, size_t size); - void Cancel(); - bool Pause(); + std::chrono::steady_clock::duration Delay() const noexcept override; + void SendTag(const Tag &tag) override; + size_t Play(const void *chunk, size_t size) override; + void Cancel() noexcept override; + bool Pause() noexcept override; private: void WritePage(); @@ -113,11 +110,11 @@ ShoutSetAudioInfo(shout_t *shout_conn, const AudioFormat &audio_format) } ShoutOutput::ShoutOutput(const ConfigBlock &block) - :base(shout_output_plugin), + :AudioOutput(FLAG_PAUSE), shout_conn(shout_new()), shout_meta(shout_metadata_new()) { - base.NeedFullyDefinedAudioFormat(); + NeedFullyDefinedAudioFormat(); const char *host = require_block_string(block, "host"); const char *mount = require_block_string(block, "mount"); @@ -250,7 +247,7 @@ ShoutOutput::~ShoutOutput() delete prepared_encoder; } -ShoutOutput * +AudioOutput * ShoutOutput::Create(EventLoop &, const ConfigBlock &block) { if (shout_init_count == 0) @@ -306,7 +303,7 @@ ShoutOutput::WritePage() } void -ShoutOutput::Close() +ShoutOutput::Close() noexcept { try { encoder->End(); @@ -326,7 +323,7 @@ ShoutOutput::Close() } void -ShoutOutput::Cancel() +ShoutOutput::Cancel() noexcept { /* needs to be implemented for shout */ } @@ -381,7 +378,7 @@ ShoutOutput::Play(const void *chunk, size_t size) } bool -ShoutOutput::Pause() +ShoutOutput::Pause() noexcept { static char silence[1020]; @@ -446,22 +443,9 @@ ShoutOutput::SendTag(const Tag &tag) WritePage(); } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin shout_output_plugin = { "shout", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - &Wrapper::SendTag, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - &Wrapper::Pause, + &ShoutOutput::Create, nullptr, }; diff --git a/src/output/plugins/SndioOutputPlugin.cxx b/src/output/plugins/SndioOutputPlugin.cxx index 7fa48fc1a..5f3ab23c5 100644 --- a/src/output/plugins/SndioOutputPlugin.cxx +++ b/src/output/plugins/SndioOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "SndioOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -45,9 +44,7 @@ static constexpr unsigned MPD_SNDIO_BUFFER_TIME_MS = 250; static constexpr Domain sndio_output_domain("sndio_output"); -class SndioOutput { - friend struct AudioOutputWrapper; - AudioOutput base; +class SndioOutput final : AudioOutput { const char *const device; const unsigned buffer_time; /* in ms */ struct sio_hdl *sio_hdl; @@ -55,29 +52,25 @@ class SndioOutput { public: SndioOutput(const ConfigBlock &block); - static SndioOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); + static AudioOutput *Create(EventLoop &, + const ConfigBlock &block) { + return new SndioOutput(block); + } - void Open(AudioFormat &audio_format); - void Close(); - size_t Play(const void *chunk, size_t size); - void Cancel(); +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; + size_t Play(const void *chunk, size_t size) override; }; SndioOutput::SndioOutput(const ConfigBlock &block) - :base(sndio_output_plugin), + :AudioOutput(0), device(block.GetBlockValue("device", SIO_DEVANY)), buffer_time(block.GetBlockValue("buffer_time", MPD_SNDIO_BUFFER_TIME_MS)) { } -SndioOutput * -SndioOutput::Create(EventLoop &, const ConfigBlock &block) -{ - return new SndioOutput(block); -} - static bool sndio_test_default_device() { @@ -154,7 +147,7 @@ SndioOutput::Open(AudioFormat &audio_format) } void -SndioOutput::Close() +SndioOutput::Close() noexcept { sio_close(sio_hdl); } @@ -170,22 +163,9 @@ SndioOutput::Play(const void *chunk, size_t size) return n; } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin sndio_output_plugin = { "sndio", sndio_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - nullptr, - &Wrapper::Play, - nullptr, - nullptr, - nullptr, + &SndioOutput::Create, nullptr, }; diff --git a/src/output/plugins/SolarisOutputPlugin.cxx b/src/output/plugins/SolarisOutputPlugin.cxx index 33309dec7..7ac2b07b6 100644 --- a/src/output/plugins/SolarisOutputPlugin.cxx +++ b/src/output/plugins/SolarisOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "SolarisOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "system/FileDescriptor.hxx" #include "system/Error.hxx" @@ -50,30 +49,27 @@ struct audio_info { #endif -class SolarisOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class SolarisOutput final : AudioOutput { /* configuration */ const char *const device; FileDescriptor fd; explicit SolarisOutput(const ConfigBlock &block) - :base(solaris_output_plugin), + :AudioOutput(0), device(block.GetBlockValue("device", "/dev/audio")) {} public: - static SolarisOutput *Create(EventLoop &, const ConfigBlock &block) { + static AudioOutput *Create(EventLoop &, const ConfigBlock &block) { return new SolarisOutput(block); } - void Open(AudioFormat &audio_format); - void Close(); +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - size_t Play(const void *chunk, size_t size); - void Cancel(); + size_t Play(const void *chunk, size_t size) override; + void Cancel() noexcept override; }; static bool @@ -128,7 +124,7 @@ SolarisOutput::Open(AudioFormat &audio_format) } void -SolarisOutput::Close() +SolarisOutput::Close() noexcept { fd.Close(); } @@ -144,27 +140,14 @@ SolarisOutput::Play(const void *chunk, size_t size) } void -SolarisOutput::Cancel() +SolarisOutput::Cancel() noexcept { ioctl(fd.Get(), I_FLUSH); } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin solaris_output_plugin = { "solaris", solaris_output_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - nullptr, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - nullptr, + &SolarisOutput::Create, nullptr, }; diff --git a/src/output/plugins/WinmmOutputPlugin.cxx b/src/output/plugins/WinmmOutputPlugin.cxx index 25372edf7..b3897b53b 100644 --- a/src/output/plugins/WinmmOutputPlugin.cxx +++ b/src/output/plugins/WinmmOutputPlugin.cxx @@ -20,7 +20,6 @@ #include "config.h" #include "WinmmOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "../Wrapper.hxx" #include "pcm/PcmBuffer.hxx" #include "mixer/MixerList.hxx" #include "fs/AllocatedPath.hxx" @@ -39,11 +38,7 @@ struct WinmmBuffer { WAVEHDR hdr; }; -class WinmmOutput { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class WinmmOutput final : AudioOutput { const UINT device_id; HWAVEOUT handle; @@ -63,16 +58,17 @@ public: return handle; } - static WinmmOutput *Create(EventLoop &, const ConfigBlock &block) { + static AudioOutput *Create(EventLoop &, const ConfigBlock &block) { return new WinmmOutput(block); } - void Open(AudioFormat &audio_format); - void Close(); +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - size_t Play(const void *chunk, size_t size); - void Drain(); - void Cancel(); + size_t Play(const void *chunk, size_t size) override; + void Drain() override; + void Cancel() noexcept override; private: /** @@ -82,7 +78,7 @@ private: void DrainAllBuffers(); - void Stop(); + void Stop() noexcept; }; @@ -148,7 +144,7 @@ get_device_id(const char *device_name) } WinmmOutput::WinmmOutput(const ConfigBlock &block) - :base(winmm_output_plugin), + :AudioOutput(0), device_id(get_device_id(block.GetBlockValue("device"))) { } @@ -202,7 +198,7 @@ WinmmOutput::Open(AudioFormat &audio_format) } void -WinmmOutput::Close() +WinmmOutput::Close() noexcept { for (auto &i : buffers) i.buffer.Clear(); @@ -291,7 +287,7 @@ WinmmOutput::DrainAllBuffers() } void -WinmmOutput::Stop() +WinmmOutput::Stop() noexcept { waveOutReset(handle); @@ -311,27 +307,14 @@ WinmmOutput::Drain() } void -WinmmOutput::Cancel() +WinmmOutput::Cancel() noexcept { Stop(); } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin winmm_output_plugin = { "winmm", winmm_output_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - nullptr, - nullptr, - &Wrapper::Play, - &Wrapper::Drain, - &Wrapper::Cancel, - nullptr, + WinmmOutput::Create, &winmm_mixer_plugin, }; diff --git a/src/output/plugins/httpd/HttpdInternal.hxx b/src/output/plugins/httpd/HttpdInternal.hxx index 45b6edde6..8ee7759fa 100644 --- a/src/output/plugins/httpd/HttpdInternal.hxx +++ b/src/output/plugins/httpd/HttpdInternal.hxx @@ -26,8 +26,7 @@ #define MPD_OUTPUT_HTTPD_INTERNAL_H #include "HttpdClient.hxx" -#include "output/Wrapper.hxx" -#include "output/Filtered.hxx" +#include "output/Interface.hxx" #include "output/Timer.hxx" #include "thread/Mutex.hxx" #include "thread/Cond.hxx" @@ -49,11 +48,7 @@ class PreparedEncoder; class Encoder; struct Tag; -class HttpdOutput final : ServerSocket, DeferredMonitor { - friend struct AudioOutputWrapper; - - AudioOutput base; - +class HttpdOutput final : AudioOutput, ServerSocket, DeferredMonitor { /** * True if the audio output is open and accepts client * connections. @@ -157,11 +152,9 @@ public: HttpdOutput(EventLoop &_loop, const ConfigBlock &block); ~HttpdOutput(); - static HttpdOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); - - static constexpr HttpdOutput *Cast(AudioOutput *ao) { - return &ContainerCast(*ao, &HttpdOutput::base); + static AudioOutput *Create(EventLoop &event_loop, + const ConfigBlock &block) { + return new HttpdOutput(event_loop, block); } using DeferredMonitor::GetEventLoop; @@ -169,11 +162,11 @@ public: void Bind(); void Unbind(); - void Enable() { + void Enable() override { Bind(); } - void Disable() { + void Disable() noexcept override { Unbind(); } @@ -187,12 +180,12 @@ public: /** * Caller must lock the mutex. */ - void Open(AudioFormat &audio_format); + void Open(AudioFormat &audio_format) override; /** * Caller must lock the mutex. */ - void Close(); + void Close() noexcept override; /** * Check whether there is at least one client. @@ -227,7 +220,7 @@ public: void SendHeader(HttpdClient &client) const; gcc_pure - std::chrono::steady_clock::duration Delay() const noexcept; + std::chrono::steady_clock::duration Delay() const noexcept override; /** * Reads data from the encoder (as much as available) and @@ -252,14 +245,14 @@ public: */ void EncodeAndPlay(const void *chunk, size_t size); - void SendTag(const Tag &tag); + void SendTag(const Tag &tag) override; - size_t Play(const void *chunk, size_t size); + size_t Play(const void *chunk, size_t size) override; void CancelAllClients(); - void Cancel(); - bool Pause(); + void Cancel() noexcept override; + bool Pause() noexcept override; private: virtual void RunDeferred() override; diff --git a/src/output/plugins/httpd/HttpdOutputPlugin.cxx b/src/output/plugins/httpd/HttpdOutputPlugin.cxx index e65a6982d..fa5d38ef0 100644 --- a/src/output/plugins/httpd/HttpdOutputPlugin.cxx +++ b/src/output/plugins/httpd/HttpdOutputPlugin.cxx @@ -50,8 +50,8 @@ const Domain httpd_output_domain("httpd_output"); inline HttpdOutput::HttpdOutput(EventLoop &_loop, const ConfigBlock &block) - :ServerSocket(_loop), DeferredMonitor(_loop), - base(httpd_output_plugin), + :AudioOutput(FLAG_ENABLE_DISABLE|FLAG_PAUSE), + ServerSocket(_loop), DeferredMonitor(_loop), encoder(nullptr), unflushed_input(0), metadata(nullptr) { @@ -113,12 +113,6 @@ HttpdOutput::Unbind() }); } -HttpdOutput * -HttpdOutput::Create(EventLoop &event_loop, const ConfigBlock &block) -{ - return new HttpdOutput(event_loop, block); -} - /** * Creates a new #HttpdClient object and adds it into the * HttpdOutput.clients linked list. @@ -246,7 +240,7 @@ HttpdOutput::OpenEncoder(AudioFormat &audio_format) unflushed_input = 0; } -inline void +void HttpdOutput::Open(AudioFormat &audio_format) { assert(!open); @@ -264,8 +258,8 @@ HttpdOutput::Open(AudioFormat &audio_format) pause = false; } -inline void -HttpdOutput::Close() +void +HttpdOutput::Close() noexcept { assert(open); @@ -300,7 +294,7 @@ HttpdOutput::SendHeader(HttpdClient &client) const client.PushPage(header); } -inline std::chrono::steady_clock::duration +std::chrono::steady_clock::duration HttpdOutput::Delay() const noexcept { if (!LockHasClients() && pause) { @@ -367,7 +361,7 @@ HttpdOutput::EncodeAndPlay(const void *chunk, size_t size) BroadcastFromEncoder(); } -inline size_t +size_t HttpdOutput::Play(const void *chunk, size_t size) { pause = false; @@ -383,7 +377,7 @@ HttpdOutput::Play(const void *chunk, size_t size) } bool -HttpdOutput::Pause() +HttpdOutput::Pause() noexcept { pause = true; @@ -395,7 +389,7 @@ HttpdOutput::Pause() return true; } -inline void +void HttpdOutput::SendTag(const Tag &tag) { if (encoder->ImplementsTag()) { @@ -463,29 +457,16 @@ HttpdOutput::CancelAllClients() } void -HttpdOutput::Cancel() +HttpdOutput::Cancel() noexcept { BlockingCall(GetEventLoop(), [this](){ CancelAllClients(); }); } -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin httpd_output_plugin = { "httpd", nullptr, - &Wrapper::Init, - &Wrapper::Finish, - &Wrapper::Enable, - &Wrapper::Disable, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - &Wrapper::SendTag, - &Wrapper::Play, - nullptr, - &Wrapper::Cancel, - &Wrapper::Pause, + &HttpdOutput::Create, nullptr, }; diff --git a/src/output/plugins/sles/SlesOutputPlugin.cxx b/src/output/plugins/sles/SlesOutputPlugin.cxx index 279d29c4d..d1ba818f7 100644 --- a/src/output/plugins/sles/SlesOutputPlugin.cxx +++ b/src/output/plugins/sles/SlesOutputPlugin.cxx @@ -24,7 +24,6 @@ #include "Play.hxx" #include "AndroidSimpleBufferQueue.hxx" #include "../../OutputAPI.hxx" -#include "../../Wrapper.hxx" #include "thread/Mutex.hxx" #include "thread/Cond.hxx" #include "util/Macros.hxx" @@ -37,14 +36,10 @@ #include -class SlesOutput { - friend struct AudioOutputWrapper; - +class SlesOutput final : AudioOutput { static constexpr unsigned N_BUFFERS = 3; static constexpr size_t BUFFER_SIZE = 65536; - AudioOutput base; - SLES::Object engine_object, mix_object, play_object; SLES::Play play; SLES::AndroidSimpleBufferQueue queue; @@ -86,30 +81,28 @@ class SlesOutput { */ uint8_t buffers[N_BUFFERS][BUFFER_SIZE]; -public: - SlesOutput(); + SlesOutput():AudioOutput(FLAG_PAUSE) {} - operator AudioOutput *() { - return &base; +public: + static AudioOutput *Create(EventLoop &, const ConfigBlock &) { + return new SlesOutput(); } - static SlesOutput *Create(EventLoop &event_loop, - const ConfigBlock &block); +private: + void Open(AudioFormat &audio_format) override; + void Close() noexcept override; - void Open(AudioFormat &audio_format); - void Close(); - - std::chrono::steady_clock::duration Delay() noexcept { + std::chrono::steady_clock::duration Delay() const noexcept override { return pause && !cancel ? std::chrono::milliseconds(100) : std::chrono::steady_clock::duration::zero(); } - size_t Play(const void *chunk, size_t size); + size_t Play(const void *chunk, size_t size) override; - void Drain(); - void Cancel(); - bool Pause(); + void Drain() override; + void Cancel() noexcept override; + bool Pause() noexcept override; private: void PlayedCallback(); @@ -129,12 +122,7 @@ private: static constexpr Domain sles_domain("sles"); -SlesOutput::SlesOutput() - :base(sles_output_plugin) -{ -} - -inline void +void SlesOutput::Open(AudioFormat &audio_format) { SLresult result; @@ -300,8 +288,8 @@ SlesOutput::Open(AudioFormat &audio_format) audio_format.format = SampleFormat::S16; } -inline void -SlesOutput::Close() +void +SlesOutput::Close() noexcept { play.SetPlayState(SL_PLAYSTATE_STOPPED); play_object.Destroy(); @@ -309,7 +297,7 @@ SlesOutput::Close() engine_object.Destroy(); } -inline size_t +size_t SlesOutput::Play(const void *chunk, size_t size) { cancel = false; @@ -348,7 +336,7 @@ SlesOutput::Play(const void *chunk, size_t size) return nbytes; } -inline void +void SlesOutput::Drain() { const std::lock_guard protect(mutex); @@ -359,8 +347,8 @@ SlesOutput::Drain() cond.wait(mutex); } -inline void -SlesOutput::Cancel() +void +SlesOutput::Cancel() noexcept { pause = true; cancel = true; @@ -379,8 +367,8 @@ SlesOutput::Cancel() filled = 0; } -inline bool -SlesOutput::Pause() +bool +SlesOutput::Pause() noexcept { cancel = false; @@ -415,28 +403,9 @@ sles_test_default_device() return true; } -inline SlesOutput * -SlesOutput::Create(EventLoop &, const ConfigBlock &) -{ - return new SlesOutput(); -} - -typedef AudioOutputWrapper Wrapper; - const struct AudioOutputPlugin sles_output_plugin = { "sles", sles_test_default_device, - &Wrapper::Init, - &Wrapper::Finish, - nullptr, - nullptr, - &Wrapper::Open, - &Wrapper::Close, - &Wrapper::Delay, - nullptr, - &Wrapper::Play, - &Wrapper::Drain, - &Wrapper::Cancel, - &Wrapper::Pause, + SlesOutput::Create, nullptr, }; diff --git a/test/run_output.cxx b/test/run_output.cxx index 2f7f75774..74bfc7aab 100644 --- a/test/run_output.cxx +++ b/test/run_output.cxx @@ -34,13 +34,15 @@ #include "util/ScopeExit.hxx" #include "Log.hxx" +#include + #include #include #include #include #include -static AudioOutput * +static std::unique_ptr load_audio_output(EventLoop &event_loop, const char *name) { const auto *block = config_find_block(ConfigBlockOption::AUDIO_OUTPUT, @@ -58,7 +60,8 @@ load_audio_output(EventLoop &event_loop, const char *name) throw FormatRuntimeError("No such audio output plugin: %s", plugin_name); - return ao_plugin_init(event_loop, *plugin, *block); + return std::unique_ptr(ao_plugin_init(event_loop, *plugin, + *block)); } static void @@ -66,11 +69,11 @@ run_output(AudioOutput &ao, AudioFormat audio_format) { /* open the audio output */ - ao_plugin_enable(ao); - AtScopeExit(&ao) { ao_plugin_disable(ao); }; + ao.Enable(); + AtScopeExit(&ao) { ao.Disable(); }; - ao_plugin_open(ao, audio_format); - AtScopeExit(&ao) { ao_plugin_close(ao); }; + ao.Open(audio_format); + AtScopeExit(&ao) { ao.Close(); }; fprintf(stderr, "audio_format=%s\n", ToString(audio_format).c_str()); @@ -93,8 +96,7 @@ run_output(AudioOutput &ao, AudioFormat audio_format) size_t play_length = (length / frame_size) * frame_size; if (play_length > 0) { - size_t consumed = ao_plugin_play(ao, - buffer, play_length); + size_t consumed = ao.Play(buffer, play_length); assert(consumed <= length); assert(consumed % frame_size == 0); @@ -126,7 +128,7 @@ try { /* initialize the audio output */ - auto *ao = load_audio_output(io_thread.GetEventLoop(), argv[2]); + auto ao = load_audio_output(io_thread.GetEventLoop(), argv[2]); /* parse the audio format */ @@ -139,7 +141,7 @@ try { /* cleanup and exit */ - ao_plugin_finish(ao); + ao.reset(); config_global_finish();