output/Interface: convert to abstract class

Yet another C-style vtable replaced with C++.
This commit is contained in:
Max Kellermann 2017-08-09 16:58:44 +02:00
parent 1cf7f3d87c
commit 31bad5f7af
30 changed files with 531 additions and 1130 deletions

View File

@ -1375,7 +1375,6 @@ OUTPUT_API_SRC = \
src/output/OutputAPI.hxx \ src/output/OutputAPI.hxx \
src/output/Interface.hxx \ src/output/Interface.hxx \
src/output/Filtered.cxx src/output/Filtered.hxx \ src/output/Filtered.cxx src/output/Filtered.hxx \
src/output/Wrapper.hxx \
src/output/Registry.cxx src/output/Registry.hxx \ src/output/Registry.cxx src/output/Registry.hxx \
src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx \ src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx \
src/output/SharedPipeConsumer.cxx src/output/SharedPipeConsumer.hxx \ src/output/SharedPipeConsumer.cxx src/output/SharedPipeConsumer.hxx \

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "Filtered.hxx" #include "Filtered.hxx"
#include "Interface.hxx" #include "Interface.hxx"
#include "OutputPlugin.hxx"
#include "Domain.hxx" #include "Domain.hxx"
#include "Log.hxx" #include "Log.hxx"
#include "mixer/MixerInternal.hxx" #include "mixer/MixerInternal.hxx"
@ -32,22 +31,20 @@
bool bool
FilteredAudioOutput::SupportsEnableDisable() const noexcept FilteredAudioOutput::SupportsEnableDisable() const noexcept
{ {
assert((output->GetPlugin().enable == nullptr) == (output->GetPlugin().disable == nullptr)); return output->SupportsEnableDisable();
return output->GetPlugin().enable != nullptr;
} }
bool bool
FilteredAudioOutput::SupportsPause() const noexcept FilteredAudioOutput::SupportsPause() const noexcept
{ {
return output->GetPlugin().pause != nullptr; return output->SupportsPause();
} }
void void
FilteredAudioOutput::Enable() FilteredAudioOutput::Enable()
{ {
try { try {
ao_plugin_enable(*output); output->Enable();
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
std::throw_with_nested(FormatRuntimeError("Failed to enable output %s", std::throw_with_nested(FormatRuntimeError("Failed to enable output %s",
GetLogName())); GetLogName()));
@ -57,7 +54,7 @@ FilteredAudioOutput::Enable()
void void
FilteredAudioOutput::Disable() noexcept FilteredAudioOutput::Disable() noexcept
{ {
ao_plugin_disable(*output); output->Disable();
} }
void void
@ -77,7 +74,7 @@ FilteredAudioOutput::OpenOutputAndConvert(AudioFormat desired_audio_format)
out_audio_format = desired_audio_format; out_audio_format = desired_audio_format;
try { try {
ao_plugin_open(*output, out_audio_format); output->Open(out_audio_format);
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
std::throw_with_nested(FormatRuntimeError("Failed to open %s", std::throw_with_nested(FormatRuntimeError("Failed to open %s",
GetLogName())); GetLogName()));
@ -91,7 +88,7 @@ FilteredAudioOutput::OpenOutputAndConvert(AudioFormat desired_audio_format)
try { try {
ConfigureConvertFilter(); ConfigureConvertFilter();
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
ao_plugin_close(*output); output->Close();
if (out_audio_format.format == SampleFormat::DSD) { if (out_audio_format.format == SampleFormat::DSD) {
/* if the audio output supports DSD, but not /* if the audio output supports DSD, but not
@ -120,7 +117,7 @@ FilteredAudioOutput::CloseOutput(bool drain) noexcept
else else
Cancel(); Cancel();
ao_plugin_close(*output); output->Close();
} }
void void
@ -149,31 +146,31 @@ FilteredAudioOutput::Close(bool drain) noexcept
std::chrono::steady_clock::duration std::chrono::steady_clock::duration
FilteredAudioOutput::Delay() noexcept FilteredAudioOutput::Delay() noexcept
{ {
return ao_plugin_delay(*output); return output->Delay();
} }
void void
FilteredAudioOutput::SendTag(const Tag &tag) FilteredAudioOutput::SendTag(const Tag &tag)
{ {
ao_plugin_send_tag(*output, tag); output->SendTag(tag);
} }
size_t size_t
FilteredAudioOutput::Play(const void *data, size_t size) FilteredAudioOutput::Play(const void *data, size_t size)
{ {
return ao_plugin_play(*output, data, size); return output->Play(data, size);
} }
void void
FilteredAudioOutput::Drain() FilteredAudioOutput::Drain()
{ {
ao_plugin_drain(*output); output->Drain();
} }
void void
FilteredAudioOutput::Cancel() noexcept FilteredAudioOutput::Cancel() noexcept
{ {
ao_plugin_cancel(*output); output->Cancel();
} }
void void
@ -186,7 +183,7 @@ bool
FilteredAudioOutput::IteratePause() noexcept FilteredAudioOutput::IteratePause() noexcept
{ {
try { try {
return ao_plugin_pause(*output); return output->Pause();
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
FormatError(e, "Failed to pause %s", FormatError(e, "Failed to pause %s",
GetLogName()); GetLogName());

View File

@ -23,6 +23,7 @@
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "filter/Observer.hxx" #include "filter/Observer.hxx"
#include <memory>
#include <string> #include <string>
#include <chrono> #include <chrono>
@ -31,6 +32,7 @@ class MusicPipe;
class EventLoop; class EventLoop;
class Mixer; class Mixer;
class MixerListener; class MixerListener;
struct MixerPlugin;
struct MusicChunk; struct MusicChunk;
struct ConfigBlock; struct ConfigBlock;
class AudioOutput; class AudioOutput;
@ -38,6 +40,8 @@ struct ReplayGainConfig;
struct Tag; struct Tag;
struct FilteredAudioOutput { struct FilteredAudioOutput {
const char *const plugin_name;
/** /**
* The device's configured display name. * The device's configured display name.
*/ */
@ -54,7 +58,7 @@ public:
/** /**
* The plugin which implements this output device. * The plugin which implements this output device.
*/ */
AudioOutput *const output; std::unique_ptr<AudioOutput> output;
/** /**
* The #mixer object associated with this audio output device. * The #mixer object associated with this audio output device.
@ -120,7 +124,8 @@ public:
/** /**
* Throws #std::runtime_error on error. * Throws #std::runtime_error on error.
*/ */
FilteredAudioOutput(AudioOutput &_output, FilteredAudioOutput(const char *_plugin_name,
std::unique_ptr<AudioOutput> &&_output,
const ConfigBlock &block); const ConfigBlock &block);
~FilteredAudioOutput(); ~FilteredAudioOutput();
@ -131,6 +136,7 @@ private:
public: public:
void Setup(EventLoop &event_loop, void Setup(EventLoop &event_loop,
const ReplayGainConfig &replay_gain_config, const ReplayGainConfig &replay_gain_config,
const MixerPlugin *mixer_plugin,
MixerListener &mixer_listener, MixerListener &mixer_listener,
const ConfigBlock &block); const ConfigBlock &block);

View File

@ -19,7 +19,7 @@
#include "config.h" #include "config.h"
#include "Filtered.hxx" #include "Filtered.hxx"
#include "OutputPlugin.hxx" #include "Interface.hxx"
#include "mixer/MixerControl.hxx" #include "mixer/MixerControl.hxx"
#include "filter/FilterInternal.hxx" #include "filter/FilterInternal.hxx"
@ -31,8 +31,6 @@ FilteredAudioOutput::~FilteredAudioOutput()
delete prepared_replay_gain_filter; delete prepared_replay_gain_filter;
delete prepared_other_replay_gain_filter; delete prepared_other_replay_gain_filter;
delete prepared_filter; delete prepared_filter;
ao_plugin_finish(output);
} }
void void
@ -45,5 +43,5 @@ FilteredAudioOutput::BeginDestroy() noexcept
void void
FilteredAudioOutput::FinishDestroy() noexcept FilteredAudioOutput::FinishDestroy() noexcept
{ {
ao_plugin_finish(output); output.reset();
} }

View File

@ -50,18 +50,11 @@
#define AUDIO_OUTPUT_FORMAT "format" #define AUDIO_OUTPUT_FORMAT "format"
#define AUDIO_FILTERS "filters" #define AUDIO_FILTERS "filters"
FilteredAudioOutput::FilteredAudioOutput(AudioOutput &_output, FilteredAudioOutput::FilteredAudioOutput(const char *_plugin_name,
std::unique_ptr<AudioOutput> &&_output,
const ConfigBlock &block) 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); Configure(block);
} }
@ -172,7 +165,7 @@ FilteredAudioOutput::Configure(const ConfigBlock &block)
{ {
char buffer[64]; char buffer[64];
snprintf(buffer, sizeof(buffer), "\"%s\" (%s)", snprintf(buffer, sizeof(buffer), "\"%s\" (%s)",
name, output->GetPlugin().name); name, plugin_name);
log_name = buffer; log_name = buffer;
} }
@ -204,6 +197,7 @@ FilteredAudioOutput::Configure(const ConfigBlock &block)
inline void inline void
FilteredAudioOutput::Setup(EventLoop &event_loop, FilteredAudioOutput::Setup(EventLoop &event_loop,
const ReplayGainConfig &replay_gain_config, const ReplayGainConfig &replay_gain_config,
const MixerPlugin *mixer_plugin,
MixerListener &mixer_listener, MixerListener &mixer_listener,
const ConfigBlock &block) const ConfigBlock &block)
{ {
@ -233,7 +227,7 @@ FilteredAudioOutput::Setup(EventLoop &event_loop,
try { try {
mixer = audio_output_load_mixer(event_loop, *this, block, mixer = audio_output_load_mixer(event_loop, *this, block,
output->GetPlugin().mixer_plugin, mixer_plugin,
*prepared_filter, *prepared_filter,
mixer_listener); mixer_listener);
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
@ -291,20 +285,15 @@ audio_output_new(EventLoop &event_loop,
plugin->name); plugin->name);
} }
auto *ao = ao_plugin_init(event_loop, *plugin, block); std::unique_ptr<AudioOutput> ao(ao_plugin_init(event_loop, *plugin,
block));
assert(ao != nullptr); assert(ao != nullptr);
FilteredAudioOutput *f; auto *f = new FilteredAudioOutput(plugin->name, std::move(ao), block);
try {
f = new FilteredAudioOutput(*ao, block);
} catch (...) {
ao_plugin_finish(ao);
throw;
}
try { try {
f->Setup(event_loop, replay_gain_config, f->Setup(event_loop, replay_gain_config,
plugin->mixer_plugin,
mixer_listener, block); mixer_listener, block);
} catch (...) { } catch (...) {
delete f; delete f;

View File

@ -20,22 +20,33 @@
#ifndef MPD_AUDIO_OUTPUT_INTERFACE_HXX #ifndef MPD_AUDIO_OUTPUT_INTERFACE_HXX
#define MPD_AUDIO_OUTPUT_INTERFACE_HXX #define MPD_AUDIO_OUTPUT_INTERFACE_HXX
struct AudioOutputPlugin; #include <chrono>
struct AudioFormat;
struct Tag;
class AudioOutput { class AudioOutput {
/** const unsigned flags;
* The plugin which implements this output device.
*/
const AudioOutputPlugin &plugin;
bool need_fully_defined_audio_format = false; bool need_fully_defined_audio_format = false;
public: protected:
AudioOutput(const AudioOutputPlugin &_plugin) static constexpr unsigned FLAG_ENABLE_DISABLE = 0x1;
:plugin(_plugin) {} static constexpr unsigned FLAG_PAUSE = 0x2;
const AudioOutputPlugin &GetPlugin() const { public:
return plugin; 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 { bool GetNeedFullyDefinedAudioFormat() const {
@ -50,6 +61,87 @@ public:
void NeedFullyDefinedAudioFormat() { void NeedFullyDefinedAudioFormat() {
need_fully_defined_audio_format = true; 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 #endif

View File

@ -19,7 +19,6 @@
#include "config.h" #include "config.h"
#include "OutputPlugin.hxx" #include "OutputPlugin.hxx"
#include "Interface.hxx"
#include <assert.h> #include <assert.h>
@ -32,76 +31,3 @@ ao_plugin_init(EventLoop &event_loop,
return plugin.init(event_loop, block); 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);
}

View File

@ -59,91 +59,6 @@ struct AudioOutputPlugin {
*/ */
AudioOutput *(*init)(EventLoop &event_loop, const ConfigBlock &block); 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 * The mixer plugin associated with this output plugin. This
* may be nullptr if no mixer plugin is implemented. When * may be nullptr if no mixer plugin is implemented. When
@ -167,38 +82,4 @@ ao_plugin_init(EventLoop &event_loop,
const AudioOutputPlugin &plugin, const AudioOutputPlugin &plugin,
const ConfigBlock &block); 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 #endif

View File

@ -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 <chrono>
struct ConfigBlock;
struct AudioFormat;
struct Tag;
template<class T>
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

View File

@ -22,7 +22,6 @@
#include "lib/alsa/NonBlock.hxx" #include "lib/alsa/NonBlock.hxx"
#include "lib/alsa/Version.hxx" #include "lib/alsa/Version.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "pcm/PcmExport.hxx" #include "pcm/PcmExport.hxx"
#include "system/ByteOrder.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; static constexpr unsigned MPD_ALSA_RETRY_NR = 5;
class AlsaOutput final class AlsaOutput final
: MultiSocketMonitor, DeferredMonitor { : AudioOutput, MultiSocketMonitor, DeferredMonitor {
friend struct AudioOutputWrapper<AlsaOutput>;
AudioOutput base;
Manual<PcmExport> pcm_export; Manual<PcmExport> pcm_export;
@ -290,20 +285,22 @@ public:
return device.empty() ? default_device : device.c_str(); return device.empty() ? default_device : device.c_str();
} }
static AlsaOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &event_loop,
const ConfigBlock &block); const ConfigBlock &block) {
return new AlsaOutput(event_loop, 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();
private: 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 snd_pcm_t object which was opened by the caller.
* Set up the configured settings and the audio format. * Set up the configured settings and the audio format.
@ -413,8 +410,8 @@ private:
static constexpr Domain alsa_output_domain("alsa_output"); static constexpr Domain alsa_output_domain("alsa_output");
AlsaOutput::AlsaOutput(EventLoop &loop, const ConfigBlock &block) AlsaOutput::AlsaOutput(EventLoop &loop, const ConfigBlock &block)
:MultiSocketMonitor(loop), DeferredMonitor(loop), :AudioOutput(FLAG_ENABLE_DISABLE),
base(alsa_output_plugin), MultiSocketMonitor(loop), DeferredMonitor(loop),
device(block.GetBlockValue("device", "")), device(block.GetBlockValue("device", "")),
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
dop(block.GetBlockValue("dop", false) || dop(block.GetBlockValue("dop", false) ||
@ -441,20 +438,14 @@ AlsaOutput::AlsaOutput(EventLoop &loop, const ConfigBlock &block)
#endif #endif
} }
inline AlsaOutput * void
AlsaOutput::Create(EventLoop &event_loop, const ConfigBlock &block)
{
return new AlsaOutput(event_loop, block);
}
inline void
AlsaOutput::Enable() AlsaOutput::Enable()
{ {
pcm_export.Construct(); pcm_export.Construct();
} }
inline void void
AlsaOutput::Disable() AlsaOutput::Disable() noexcept
{ {
pcm_export.Destruct(); pcm_export.Destruct();
} }
@ -1015,7 +1006,7 @@ MaybeDmix(snd_pcm_t *pcm) noexcept
return MaybeDmix(snd_pcm_type(pcm)); return MaybeDmix(snd_pcm_type(pcm));
} }
inline void void
AlsaOutput::Open(AudioFormat &audio_format) AlsaOutput::Open(AudioFormat &audio_format)
{ {
int err = snd_pcm_open(&pcm, GetDevice(), int err = snd_pcm_open(&pcm, GetDevice(),
@ -1158,7 +1149,7 @@ AlsaOutput::DrainInternal()
return snd_pcm_drain(pcm) != -EAGAIN; return snd_pcm_drain(pcm) != -EAGAIN;
} }
inline void void
AlsaOutput::Drain() AlsaOutput::Drain()
{ {
const std::lock_guard<Mutex> lock(mutex); const std::lock_guard<Mutex> lock(mutex);
@ -1183,8 +1174,8 @@ AlsaOutput::CancelInternal()
ClearRingBuffer(); ClearRingBuffer();
} }
inline void void
AlsaOutput::Cancel() AlsaOutput::Cancel() noexcept
{ {
if (!active) { if (!active) {
/* early cancel, quick code path without thread /* early cancel, quick code path without thread
@ -1202,8 +1193,8 @@ AlsaOutput::Cancel()
}); });
} }
inline void void
AlsaOutput::Close() AlsaOutput::Close() noexcept
{ {
/* make sure the I/O thread isn't inside DispatchSockets() */ /* make sure the I/O thread isn't inside DispatchSockets() */
BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){ BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
@ -1217,7 +1208,7 @@ AlsaOutput::Close()
delete[] silence; delete[] silence;
} }
inline size_t size_t
AlsaOutput::Play(const void *chunk, size_t size) AlsaOutput::Play(const void *chunk, size_t size)
{ {
assert(size > 0); assert(size > 0);
@ -1331,23 +1322,9 @@ try {
cond.signal(); cond.signal();
} }
typedef AudioOutputWrapper<AlsaOutput> Wrapper;
const struct AudioOutputPlugin alsa_output_plugin = { const struct AudioOutputPlugin alsa_output_plugin = {
"alsa", "alsa",
alsa_test_default_device, alsa_test_default_device,
&Wrapper::Init, &AlsaOutput::Create,
&Wrapper::Finish,
&Wrapper::Enable,
&Wrapper::Disable,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
nullptr,
&Wrapper::Play,
&Wrapper::Drain,
&Wrapper::Cancel,
nullptr,
&alsa_mixer_plugin, &alsa_mixer_plugin,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "AoOutputPlugin.hxx" #include "AoOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "system/Error.hxx" #include "system/Error.hxx"
#include "util/DivideString.hxx" #include "util/DivideString.hxx"
#include "util/SplitString.hxx" #include "util/SplitString.hxx"
@ -37,11 +36,7 @@ static ao_sample_format OUR_AO_FORMAT_INITIALIZER;
static unsigned ao_output_ref; static unsigned ao_output_ref;
class AoOutput { class AoOutput final : AudioOutput {
friend struct AudioOutputWrapper<AoOutput>;
AudioOutput base;
const size_t write_size; const size_t write_size;
int driver; int driver;
ao_option *options = nullptr; ao_option *options = nullptr;
@ -51,14 +46,14 @@ class AoOutput {
~AoOutput(); ~AoOutput();
public: public:
static AoOutput *Create(EventLoop &, const ConfigBlock &block) { static AudioOutput *Create(EventLoop &, const ConfigBlock &block) {
return new AoOutput(block); return new AoOutput(block);
} }
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format) override;
void Close(); 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"); static constexpr Domain ao_output_domain("ao_output");
@ -95,7 +90,7 @@ MakeAoError()
} }
AoOutput::AoOutput(const ConfigBlock &block) AoOutput::AoOutput(const ConfigBlock &block)
:base(ao_output_plugin), :AudioOutput(0),
write_size(block.GetBlockValue("write_size", 1024u)) write_size(block.GetBlockValue("write_size", 1024u))
{ {
if (ao_output_ref == 0) { if (ao_output_ref == 0) {
@ -177,7 +172,7 @@ AoOutput::Open(AudioFormat &audio_format)
} }
void void
AoOutput::Close() AoOutput::Close() noexcept
{ {
ao_close(device); ao_close(device);
} }
@ -199,22 +194,9 @@ AoOutput::Play(const void *chunk, size_t size)
return size; return size;
} }
typedef AudioOutputWrapper<AoOutput> Wrapper;
const struct AudioOutputPlugin ao_output_plugin = { const struct AudioOutputPlugin ao_output_plugin = {
"ao", "ao",
nullptr, nullptr,
&Wrapper::Init, &AoOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
nullptr,
&Wrapper::Play,
nullptr,
nullptr,
nullptr,
nullptr, nullptr,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "FifoOutputPlugin.hxx" #include "FifoOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "../Timer.hxx" #include "../Timer.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx" #include "fs/FileSystem.hxx"
@ -34,11 +33,7 @@
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
class FifoOutput { class FifoOutput final : AudioOutput {
friend struct AudioOutputWrapper<FifoOutput>;
AudioOutput base;
const AllocatedPath path; const AllocatedPath path;
std::string path_utf8; std::string path_utf8;
@ -54,9 +49,12 @@ public:
CloseFifo(); CloseFifo();
} }
static FifoOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &,
const ConfigBlock &block); const ConfigBlock &block) {
return new FifoOutput(block);
}
private:
void Create(); void Create();
void Check(); void Check();
void Delete(); void Delete();
@ -64,18 +62,18 @@ public:
void OpenFifo(); void OpenFifo();
void CloseFifo(); void CloseFifo();
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format) override;
void Close(); void Close() noexcept override;
std::chrono::steady_clock::duration Delay() const noexcept; std::chrono::steady_clock::duration Delay() const noexcept override;
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;
}; };
static constexpr Domain fifo_output_domain("fifo_output"); static constexpr Domain fifo_output_domain("fifo_output");
FifoOutput::FifoOutput(const ConfigBlock &block) FifoOutput::FifoOutput(const ConfigBlock &block)
:base(fifo_output_plugin), :AudioOutput(0),
path(block.GetPath("path")) path(block.GetPath("path"))
{ {
if (path.IsNull()) if (path.IsNull())
@ -169,12 +167,6 @@ try {
throw; throw;
} }
inline FifoOutput *
FifoOutput::Create(EventLoop &, const ConfigBlock &block)
{
return new FifoOutput(block);
}
void void
FifoOutput::Open(AudioFormat &audio_format) FifoOutput::Open(AudioFormat &audio_format)
{ {
@ -182,13 +174,13 @@ FifoOutput::Open(AudioFormat &audio_format)
} }
void void
FifoOutput::Close() FifoOutput::Close() noexcept
{ {
delete timer; delete timer;
} }
inline void void
FifoOutput::Cancel() FifoOutput::Cancel() noexcept
{ {
timer->Reset(); timer->Reset();
@ -205,7 +197,7 @@ FifoOutput::Cancel()
} }
} }
inline std::chrono::steady_clock::duration std::chrono::steady_clock::duration
FifoOutput::Delay() const noexcept FifoOutput::Delay() const noexcept
{ {
return timer->IsStarted() return timer->IsStarted()
@ -213,7 +205,7 @@ FifoOutput::Delay() const noexcept
: std::chrono::steady_clock::duration::zero(); : std::chrono::steady_clock::duration::zero();
} }
inline size_t size_t
FifoOutput::Play(const void *chunk, size_t size) FifoOutput::Play(const void *chunk, size_t size)
{ {
if (!timer->IsStarted()) if (!timer->IsStarted())
@ -241,22 +233,9 @@ FifoOutput::Play(const void *chunk, size_t size)
} }
} }
typedef AudioOutputWrapper<FifoOutput> Wrapper;
const struct AudioOutputPlugin fifo_output_plugin = { const struct AudioOutputPlugin fifo_output_plugin = {
"fifo", "fifo",
nullptr, nullptr,
&Wrapper::Init, &FifoOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
nullptr,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
nullptr,
nullptr, nullptr,
}; };

View File

@ -21,7 +21,6 @@
#include "config.h" #include "config.h"
#include "HaikuOutputPlugin.hxx" #include "HaikuOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "system/Error.hxx" #include "system/Error.hxx"
@ -43,13 +42,10 @@
#define UTF8_PLAY "\xE2\x96\xB6" #define UTF8_PLAY "\xE2\x96\xB6"
class HaikuOutput { class HaikuOutput final: AudioOutput {
friend struct AudioOutputWrapper<HaikuOutput>;
friend int haiku_output_get_volume(HaikuOutput &haiku); friend int haiku_output_get_volume(HaikuOutput &haiku);
friend bool haiku_output_set_volume(HaikuOutput &haiku, unsigned volume); friend bool haiku_output_set_volume(HaikuOutput &haiku, unsigned volume);
AudioOutput base;
size_t write_size; size_t write_size;
media_raw_audio_format format; media_raw_audio_format format;
@ -66,27 +62,28 @@ class HaikuOutput {
public: public:
HaikuOutput(const ConfigBlock &block) HaikuOutput(const ConfigBlock &block)
:base(haiku_output_plugin), :AudioOutput(0),
/* XXX: by default we should let the MediaKit propose the buffer size */ /* XXX: by default we should let the MediaKit propose the buffer size */
write_size(block.GetBlockValue("write_size", 4096u)) {} write_size(block.GetBlockValue("write_size", 4096u)) {}
~HaikuOutput(); ~HaikuOutput();
static HaikuOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &event_loop,
const ConfigBlock &block); const ConfigBlock &block);
void Open(AudioFormat &audio_format); private:
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;
void Cancel(); 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, void FillBuffer(void* _buffer, size_t size,
gcc_unused const media_raw_audio_format& _format); 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"); 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) HaikuOutput::Create(EventLoop &, const ConfigBlock &block)
{ {
initialize_application(); initialize_application();
@ -129,7 +126,7 @@ HaikuOutput::Create(EventLoop &, const ConfigBlock &block)
} }
void void
HaikuOutput::Close() HaikuOutput::Close() noexcept
{ {
sound_player->SetHasData(false); sound_player->SetHasData(false);
delete_sem(new_buffer); delete_sem(new_buffer);
@ -139,8 +136,6 @@ HaikuOutput::Close()
sound_player = nullptr; sound_player = nullptr;
} }
HaikuOutput::~HaikuOutput() HaikuOutput::~HaikuOutput()
{ {
delete_sem(new_buffer); delete_sem(new_buffer);
@ -186,7 +181,7 @@ HaikuOutput::FillBuffer(void* _buffer, size_t size,
} }
} }
inline void void
HaikuOutput::Open(AudioFormat &audio_format) HaikuOutput::Open(AudioFormat &audio_format)
{ {
status_t err; status_t err;
@ -265,7 +260,7 @@ HaikuOutput::Open(AudioFormat &audio_format)
sound_player->SetHasData(false); sound_player->SetHasData(false);
} }
inline size_t size_t
HaikuOutput::Play(const void *chunk, size_t size) HaikuOutput::Play(const void *chunk, size_t size)
{ {
BSoundPlayer* const soundPlayer = sound_player; BSoundPlayer* const soundPlayer = sound_player;
@ -311,7 +306,7 @@ HaikuOutput::Play(const void *chunk, size_t size)
} }
inline std::chrono::steady_clock::duration inline std::chrono::steady_clock::duration
HaikuOutput::Delay() noexcept HaikuOutput::Delay() const noexcept
{ {
unsigned delay = buffer_filled ? 0 : buffer_delay; unsigned delay = buffer_filled ? 0 : buffer_delay;
@ -324,7 +319,7 @@ HaikuOutput::Delay() noexcept
return std::chrono::steady_clock::duration::zero(); return std::chrono::steady_clock::duration::zero();
} }
inline void void
HaikuOutput::SendTag(const Tag &tag) HaikuOutput::SendTag(const Tag &tag)
{ {
status_t err; status_t err;
@ -468,18 +463,6 @@ typedef AudioOutputWrapper<HaikuOutput> Wrapper;
const struct AudioOutputPlugin haiku_output_plugin = { const struct AudioOutputPlugin haiku_output_plugin = {
"haiku", "haiku",
haiku_test_default_device, haiku_test_default_device,
&Wrapper::Init, &HaikuOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
&Wrapper::SendTag,
&Wrapper::Play,
nullptr,
nullptr,
nullptr,
&haiku_mixer_plugin, &haiku_mixer_plugin,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "JackOutputPlugin.hxx" #include "JackOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/ConstBuffer.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); static constexpr size_t jack_sample_size = sizeof(jack_default_audio_sample_t);
struct JackOutput { struct JackOutput final : AudioOutput {
AudioOutput base;
/** /**
* libjack options passed to jack_client_open(). * libjack options passed to jack_client_open().
*/ */
@ -99,12 +96,12 @@ struct JackOutput {
shutdown = true; shutdown = true;
} }
void Enable(); void Enable() override;
void Disable(); void Disable() noexcept override;
void Open(AudioFormat &new_audio_format); void Open(AudioFormat &new_audio_format) override;
void Close() { void Close() noexcept override {
Stop(); Stop();
} }
@ -128,15 +125,15 @@ struct JackOutput {
*/ */
size_t WriteSamples(const float *src, size_t n_frames); 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 return pause && !shutdown
? std::chrono::seconds(1) ? std::chrono::seconds(1)
: std::chrono::steady_clock::duration::zero(); : 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"); 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) JackOutput::JackOutput(const ConfigBlock &block)
:base(jack_output_plugin), :AudioOutput(FLAG_ENABLE_DISABLE|FLAG_PAUSE),
name(block.GetBlockValue("client_name", nullptr)), name(block.GetBlockValue("client_name", nullptr)),
server_name(block.GetBlockValue("server_name", nullptr)) server_name(block.GetBlockValue("server_name", nullptr))
{ {
@ -430,7 +427,7 @@ JackOutput::Enable()
} }
inline void inline void
JackOutput::Disable() JackOutput::Disable() noexcept
{ {
if (client != nullptr) if (client != nullptr)
Disconnect(); Disconnect();
@ -452,8 +449,7 @@ mpd_jack_init(EventLoop &, const ConfigBlock &block)
jack_set_info_function(mpd_jack_info); jack_set_info_function(mpd_jack_info);
#endif #endif
auto *jd = new JackOutput(block); return new JackOutput(block);
return &jd->base;
} }
/** /**
@ -663,7 +659,7 @@ JackOutput::Play(const void *chunk, size_t size)
} }
inline bool inline bool
JackOutput::Pause() JackOutput::Pause() noexcept
{ {
if (shutdown) if (shutdown)
return false; return false;
@ -673,22 +669,9 @@ JackOutput::Pause()
return true; return true;
} }
typedef AudioOutputWrapper<JackOutput> Wrapper;
const struct AudioOutputPlugin jack_output_plugin = { const struct AudioOutputPlugin jack_output_plugin = {
"jack", "jack",
mpd_jack_test_default_device, mpd_jack_test_default_device,
mpd_jack_init, mpd_jack_init,
&Wrapper::Finish,
&Wrapper::Enable,
&Wrapper::Disable,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
nullptr,
&Wrapper::Play,
nullptr,
nullptr,
&Wrapper::Pause,
nullptr, nullptr,
}; };

View File

@ -20,43 +20,41 @@
#include "config.h" #include "config.h"
#include "NullOutputPlugin.hxx" #include "NullOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "../Timer.hxx" #include "../Timer.hxx"
class NullOutput { class NullOutput final : AudioOutput {
friend struct AudioOutputWrapper<NullOutput>;
AudioOutput base;
const bool sync; const bool sync;
Timer *timer; Timer *timer;
public: public:
NullOutput(const ConfigBlock &block) NullOutput(const ConfigBlock &block)
:base(null_output_plugin), :AudioOutput(0),
sync(block.GetBlockValue("sync", true)) {} sync(block.GetBlockValue("sync", true)) {}
static NullOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &,
const ConfigBlock &block); const ConfigBlock &block) {
return new NullOutput(block);
}
void Open(AudioFormat &audio_format) { private:
void Open(AudioFormat &audio_format) override {
if (sync) if (sync)
timer = new Timer(audio_format); timer = new Timer(audio_format);
} }
void Close() { void Close() noexcept override {
if (sync) if (sync)
delete timer; delete timer;
} }
std::chrono::steady_clock::duration Delay() const noexcept { std::chrono::steady_clock::duration Delay() const noexcept override {
return sync && timer->IsStarted() return sync && timer->IsStarted()
? timer->GetDelay() ? timer->GetDelay()
: std::chrono::steady_clock::duration::zero(); : 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 (sync) {
if (!timer->IsStarted()) if (!timer->IsStarted())
timer->Start(); timer->Start();
@ -66,34 +64,15 @@ public:
return size; return size;
} }
void Cancel() { void Cancel() noexcept override {
if (sync) if (sync)
timer->Reset(); timer->Reset();
} }
}; };
inline NullOutput *
NullOutput::Create(EventLoop &, const ConfigBlock &block)
{
return new NullOutput(block);
}
typedef AudioOutputWrapper<NullOutput> Wrapper;
const struct AudioOutputPlugin null_output_plugin = { const struct AudioOutputPlugin null_output_plugin = {
"null", "null",
nullptr, nullptr,
&Wrapper::Init, &NullOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
nullptr,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
nullptr,
nullptr, nullptr,
}; };

View File

@ -35,9 +35,7 @@
#include <memory> #include <memory>
struct OSXOutput { struct OSXOutput final : AudioOutput {
AudioOutput base;
/* configuration settings */ /* configuration settings */
OSType component_subtype; OSType component_subtype;
/* only applicable with kAudioUnitSubType_HALOutput */ /* only applicable with kAudioUnitSubType_HALOutput */
@ -53,6 +51,18 @@ struct OSXOutput {
boost::lockfree::spsc_queue<uint8_t> *ring_buffer; boost::lockfree::spsc_queue<uint8_t> *ring_buffer;
OSXOutput(const ConfigBlock &block); 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"); static constexpr Domain osx_output_domain("osx_output");
@ -80,7 +90,7 @@ osx_output_test_default_device(void)
} }
OSXOutput::OSXOutput(const ConfigBlock &block) OSXOutput::OSXOutput(const ConfigBlock &block)
:base(osx_output_plugin) :AudioOutput(FLAG_ENABLE_DISABLE)
{ {
const char *device = block.GetBlockValue("device"); const char *device = block.GetBlockValue("device");
@ -103,8 +113,8 @@ OSXOutput::OSXOutput(const ConfigBlock &block)
sync_sample_rate = block.GetBlockValue("sync_sample_rate", false); sync_sample_rate = block.GetBlockValue("sync_sample_rate", false);
} }
static AudioOutput * AudioOutput *
osx_output_init(EventLoop &, const ConfigBlock &block) OSXOutput::Create(EventLoop &, const ConfigBlock &block)
{ {
OSXOutput *oo = new OSXOutput(block); OSXOutput *oo = new OSXOutput(block);
@ -124,15 +134,7 @@ osx_output_init(EventLoop &, const ConfigBlock &block)
&dev_id); &dev_id);
oo->dev_id = dev_id; oo->dev_id = dev_id;
return &oo->base; return oo;
}
static void
osx_output_finish(AudioOutput *ao)
{
OSXOutput *oo = (OSXOutput *)ao;
delete oo;
} }
static void static void
@ -513,15 +515,14 @@ osx_render(void *vdata,
return noErr; return noErr;
} }
static void void
osx_output_enable(AudioOutput *ao) OSXOutput::Enable()
{ {
char errormsg[1024]; char errormsg[1024];
OSXOutput *oo = (OSXOutput *)ao;
AudioComponentDescription desc; AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output; desc.componentType = kAudioUnitType_Output;
desc.componentSubType = oo->component_subtype; desc.componentSubType = component_subtype;
desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0; desc.componentFlags = 0;
desc.componentFlagsMask = 0; desc.componentFlagsMask = 0;
@ -530,7 +531,7 @@ osx_output_enable(AudioOutput *ao)
if (comp == 0) if (comp == 0)
throw std::runtime_error("Error finding OS X component"); throw std::runtime_error("Error finding OS X component");
OSStatus status = AudioComponentInstanceNew(comp, &oo->au); OSStatus status = AudioComponentInstanceNew(comp, &au);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError("Unable to open OS X component: %s", throw FormatRuntimeError("Unable to open OS X component: %s",
@ -538,105 +539,97 @@ osx_output_enable(AudioOutput *ao)
} }
try { try {
osx_output_set_device(oo); osx_output_set_device(this);
} catch (...) { } catch (...) {
AudioComponentInstanceDispose(oo->au); AudioComponentInstanceDispose(au);
throw; throw;
} }
if (oo->hog_device) { if (hog_device)
osx_output_hog_device(oo->dev_id, true); osx_output_hog_device(dev_id, true);
}
} }
static void void
osx_output_disable(AudioOutput *ao) OSXOutput::Disable() noexcept
{ {
OSXOutput *oo = (OSXOutput *)ao; AudioComponentInstanceDispose(au);
AudioComponentInstanceDispose(oo->au); if (hog_device)
osx_output_hog_device(dev_id, false);
if (oo->hog_device) {
osx_output_hog_device(oo->dev_id, false);
}
} }
static void void
osx_output_close(AudioOutput *ao) OSXOutput::Close() noexcept
{ {
OSXOutput *od = (OSXOutput *)ao; AudioOutputUnitStop(au);
AudioUnitUninitialize(au);
AudioOutputUnitStop(od->au); delete ring_buffer;
AudioUnitUninitialize(od->au);
delete od->ring_buffer;
} }
static void void
osx_output_open(AudioOutput *ao, AudioFormat &audio_format) OSXOutput::Open(AudioFormat &audio_format)
{ {
char errormsg[1024]; char errormsg[1024];
OSXOutput *od = (OSXOutput *)ao;
memset(&od->asbd, 0, sizeof(od->asbd)); memset(&asbd, 0, sizeof(asbd));
od->asbd.mSampleRate = audio_format.sample_rate; asbd.mSampleRate = audio_format.sample_rate;
od->asbd.mFormatID = kAudioFormatLinearPCM; asbd.mFormatID = kAudioFormatLinearPCM;
od->asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
switch (audio_format.format) { switch (audio_format.format) {
case SampleFormat::S8: case SampleFormat::S8:
od->asbd.mBitsPerChannel = 8; asbd.mBitsPerChannel = 8;
break; break;
case SampleFormat::S16: case SampleFormat::S16:
od->asbd.mBitsPerChannel = 16; asbd.mBitsPerChannel = 16;
break; break;
case SampleFormat::S32: case SampleFormat::S32:
od->asbd.mBitsPerChannel = 32; asbd.mBitsPerChannel = 32;
break; break;
default: default:
audio_format.format = SampleFormat::S32; audio_format.format = SampleFormat::S32;
od->asbd.mBitsPerChannel = 32; asbd.mBitsPerChannel = 32;
break; break;
} }
if (IsBigEndian()) if (IsBigEndian())
od->asbd.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; asbd.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
od->asbd.mBytesPerPacket = audio_format.GetFrameSize(); asbd.mBytesPerPacket = audio_format.GetFrameSize();
od->asbd.mFramesPerPacket = 1; asbd.mFramesPerPacket = 1;
od->asbd.mBytesPerFrame = od->asbd.mBytesPerPacket; asbd.mBytesPerFrame = asbd.mBytesPerPacket;
od->asbd.mChannelsPerFrame = audio_format.channels; asbd.mChannelsPerFrame = audio_format.channels;
if (od->sync_sample_rate) { if (sync_sample_rate)
osx_output_sync_device_sample_rate(od->dev_id, od->asbd); osx_output_sync_device_sample_rate(dev_id, asbd);
}
OSStatus status = OSStatus status =
AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, kAudioUnitScope_Input, 0,
&od->asbd, &asbd,
sizeof(od->asbd)); sizeof(asbd));
if (status != noErr) if (status != noErr)
throw std::runtime_error("Unable to set format on OS X device"); throw std::runtime_error("Unable to set format on OS X device");
AURenderCallbackStruct callback; AURenderCallbackStruct callback;
callback.inputProc = osx_render; callback.inputProc = osx_render;
callback.inputProcRefCon = od; callback.inputProcRefCon = this;
status = status =
AudioUnitSetProperty(od->au, AudioUnitSetProperty(au,
kAudioUnitProperty_SetRenderCallback, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, kAudioUnitScope_Input, 0,
&callback, sizeof(callback)); &callback, sizeof(callback));
if (status != noErr) { if (status != noErr) {
AudioComponentInstanceDispose(od->au); AudioComponentInstanceDispose(au);
throw std::runtime_error("unable to set callback for OS X audio unit"); throw std::runtime_error("unable to set callback for OS X audio unit");
} }
status = AudioUnitInitialize(od->au); status = AudioUnitInitialize(au);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError("Unable to initialize OS X audio unit: %s", 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; 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) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError("Unable to set frame size: %s", throw FormatRuntimeError("Unable to set frame size: %s",
errormsg); errormsg);
} }
od->ring_buffer = new boost::lockfree::spsc_queue<uint8_t>(buffer_frame_size); ring_buffer = new boost::lockfree::spsc_queue<uint8_t>(buffer_frame_size);
status = AudioOutputUnitStart(od->au); status = AudioOutputUnitStart(au);
if (status != 0) { if (status != 0) {
AudioUnitUninitialize(od->au); AudioUnitUninitialize(au);
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError("unable to start audio output: %s", throw FormatRuntimeError("unable to start audio output: %s",
errormsg); errormsg);
} }
} }
static size_t size_t
osx_output_play(AudioOutput *ao, const void *chunk, size_t size) OSXOutput::Play(const void *chunk, size_t size)
{ {
OSXOutput *od = (OSXOutput *)ao; return ring_buffer->push((uint8_t *)chunk, size);
return od->ring_buffer->push((uint8_t *)chunk, size);
} }
static std::chrono::steady_clock::duration std::chrono::steady_clock::duration
osx_output_delay(AudioOutput *ao) noexcept OSXOutput::Delay() const noexcept
{ {
OSXOutput *od = (OSXOutput *)ao; return ring_buffer->write_available()
return od->ring_buffer->write_available()
? std::chrono::steady_clock::duration::zero() ? std::chrono::steady_clock::duration::zero()
: std::chrono::milliseconds(25); : std::chrono::milliseconds(25);
} }
@ -681,17 +672,6 @@ osx_output_delay(AudioOutput *ao) noexcept
const struct AudioOutputPlugin osx_output_plugin = { const struct AudioOutputPlugin osx_output_plugin = {
"osx", "osx",
osx_output_test_default_device, osx_output_test_default_device,
osx_output_init, &OSXOutput::Create,
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,
nullptr, nullptr,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "OpenALOutputPlugin.hxx" #include "OpenALOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include <unistd.h> #include <unistd.h>
@ -33,14 +32,10 @@
#include <OpenAL/alc.h> #include <OpenAL/alc.h>
#endif #endif
class OpenALOutput { class OpenALOutput final : AudioOutput {
friend struct AudioOutputWrapper<OpenALOutput>;
/* should be enough for buffer size = 2048 */ /* should be enough for buffer size = 2048 */
static constexpr unsigned NUM_BUFFERS = 16; static constexpr unsigned NUM_BUFFERS = 16;
AudioOutput base;
const char *device_name; const char *device_name;
ALCdevice *device; ALCdevice *device;
ALCcontext *context; ALCcontext *context;
@ -52,14 +47,18 @@ class OpenALOutput {
OpenALOutput(const ConfigBlock &block); OpenALOutput(const ConfigBlock &block);
static OpenALOutput *Create(EventLoop &event_loop, public:
const ConfigBlock &block); static AudioOutput *Create(EventLoop &,
const ConfigBlock &block) {
return new OpenALOutput(block);
}
void Open(AudioFormat &audio_format); private:
void Close(); void Open(AudioFormat &audio_format) override;
void Close() noexcept override;
gcc_pure gcc_pure
std::chrono::steady_clock::duration Delay() const noexcept { std::chrono::steady_clock::duration Delay() const noexcept override {
return filled < NUM_BUFFERS || HasProcessed() return filled < NUM_BUFFERS || HasProcessed()
? std::chrono::steady_clock::duration::zero() ? std::chrono::steady_clock::duration::zero()
/* we don't know exactly how long we must wait /* we don't know exactly how long we must wait
@ -68,9 +67,9 @@ class OpenALOutput {
: std::chrono::milliseconds(50); : 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: private:
gcc_pure gcc_pure
@ -138,7 +137,7 @@ OpenALOutput::SetupContext()
} }
OpenALOutput::OpenALOutput(const ConfigBlock &block) OpenALOutput::OpenALOutput(const ConfigBlock &block)
:base(openal_output_plugin), :AudioOutput(0),
device_name(block.GetBlockValue("device")) device_name(block.GetBlockValue("device"))
{ {
if (device_name == nullptr) if (device_name == nullptr)
@ -146,13 +145,7 @@ OpenALOutput::OpenALOutput(const ConfigBlock &block)
ALC_DEFAULT_DEVICE_SPECIFIER); ALC_DEFAULT_DEVICE_SPECIFIER);
} }
inline OpenALOutput * void
OpenALOutput::Create(EventLoop &, const ConfigBlock &block)
{
return new OpenALOutput(block);
}
inline void
OpenALOutput::Open(AudioFormat &audio_format) OpenALOutput::Open(AudioFormat &audio_format)
{ {
format = openal_audio_format(audio_format); format = openal_audio_format(audio_format);
@ -176,8 +169,8 @@ OpenALOutput::Open(AudioFormat &audio_format)
frequency = audio_format.sample_rate; frequency = audio_format.sample_rate;
} }
inline void void
OpenALOutput::Close() OpenALOutput::Close() noexcept
{ {
alcMakeContextCurrent(context); alcMakeContextCurrent(context);
alDeleteSources(1, &source); alDeleteSources(1, &source);
@ -186,7 +179,7 @@ OpenALOutput::Close()
alcCloseDevice(device); alcCloseDevice(device);
} }
inline size_t size_t
OpenALOutput::Play(const void *chunk, size_t size) OpenALOutput::Play(const void *chunk, size_t size)
{ {
if (alcGetCurrentContext() != context) if (alcGetCurrentContext() != context)
@ -214,8 +207,8 @@ OpenALOutput::Play(const void *chunk, size_t size)
return size; return size;
} }
inline void void
OpenALOutput::Cancel() OpenALOutput::Cancel() noexcept
{ {
filled = 0; filled = 0;
alcMakeContextCurrent(context); alcMakeContextCurrent(context);
@ -226,22 +219,9 @@ OpenALOutput::Cancel()
filled = 0; filled = 0;
} }
typedef AudioOutputWrapper<OpenALOutput> Wrapper;
const struct AudioOutputPlugin openal_output_plugin = { const struct AudioOutputPlugin openal_output_plugin = {
"openal", "openal",
nullptr, nullptr,
&Wrapper::Init, OpenALOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
nullptr,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
nullptr,
nullptr, nullptr,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "OssOutputPlugin.hxx" #include "OssOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "system/fd_util.h" #include "system/fd_util.h"
#include "system/Error.hxx" #include "system/Error.hxx"
@ -60,11 +59,7 @@
#include "util/Manual.hxx" #include "util/Manual.hxx"
#endif #endif
class OssOutput { class OssOutput final : AudioOutput {
friend struct AudioOutputWrapper<OssOutput>;
AudioOutput base;
#ifdef AFMT_S24_PACKED #ifdef AFMT_S24_PACKED
Manual<PcmExport> pcm_export; Manual<PcmExport> pcm_export;
#endif #endif
@ -84,32 +79,38 @@ class OssOutput {
*/ */
int oss_format; int oss_format;
#ifdef AFMT_S24_PACKED
static constexpr unsigned oss_flags = FLAG_ENABLE_DISABLE;
#else
static constexpr unsigned oss_flags = 0;
#endif
public: public:
explicit OssOutput(const char *_device=nullptr) explicit OssOutput(const char *_device=nullptr)
:base(oss_output_plugin), :AudioOutput(oss_flags),
fd(-1), device(_device) {} fd(-1), device(_device) {}
static OssOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &event_loop,
const ConfigBlock &block); const ConfigBlock &block);
#ifdef AFMT_S24_PACKED #ifdef AFMT_S24_PACKED
void Enable() { void Enable() override {
pcm_export.Construct(); pcm_export.Construct();
} }
void Disable() { void Disable() noexcept override {
pcm_export.Destruct(); pcm_export.Destruct();
} }
#endif #endif
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format) override;
void Close() { void Close() noexcept override {
DoClose(); DoClose();
} }
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: private:
/** /**
@ -225,7 +226,7 @@ oss_open_default()
throw std::runtime_error("error trying to open default OSS device"); throw std::runtime_error("error trying to open default OSS device");
} }
inline OssOutput * AudioOutput *
OssOutput::Create(EventLoop &, const ConfigBlock &block) OssOutput::Create(EventLoop &, const ConfigBlock &block)
{ {
const char *device = block.GetBlockValue("device"); const char *device = block.GetBlockValue("device");
@ -637,7 +638,7 @@ try {
throw; throw;
} }
inline void void
OssOutput::Open(AudioFormat &_audio_format) OssOutput::Open(AudioFormat &_audio_format)
try { try {
fd = open_cloexec(device, O_WRONLY, 0); fd = open_cloexec(device, O_WRONLY, 0);
@ -652,8 +653,8 @@ try {
throw; throw;
} }
inline void void
OssOutput::Cancel() OssOutput::Cancel() noexcept
{ {
if (fd >= 0) { if (fd >= 0) {
ioctl(fd, SNDCTL_DSP_RESET, 0); ioctl(fd, SNDCTL_DSP_RESET, 0);
@ -665,7 +666,7 @@ OssOutput::Cancel()
#endif #endif
} }
inline size_t size_t
OssOutput::Play(const void *chunk, size_t size) OssOutput::Play(const void *chunk, size_t size)
{ {
ssize_t ret; ssize_t ret;
@ -698,28 +699,9 @@ OssOutput::Play(const void *chunk, size_t size)
} }
} }
typedef AudioOutputWrapper<OssOutput> Wrapper;
const struct AudioOutputPlugin oss_output_plugin = { const struct AudioOutputPlugin oss_output_plugin = {
"oss", "oss",
oss_output_test_default_device, oss_output_test_default_device,
&Wrapper::Init, OssOutput::Create,
&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,
&oss_mixer_plugin, &oss_mixer_plugin,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "PipeOutputPlugin.hxx" #include "PipeOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "system/Error.hxx" #include "system/Error.hxx"
#include <string> #include <string>
@ -28,43 +27,36 @@
#include <stdio.h> #include <stdio.h>
class PipeOutput { class PipeOutput final : AudioOutput {
friend struct AudioOutputWrapper<PipeOutput>;
AudioOutput base;
const std::string cmd; const std::string cmd;
FILE *fh; FILE *fh;
PipeOutput(const ConfigBlock &block); PipeOutput(const ConfigBlock &block);
public: public:
static PipeOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &,
const ConfigBlock &block); 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); 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) PipeOutput::PipeOutput(const ConfigBlock &block)
:base(pipe_output_plugin), :AudioOutput(0),
cmd(block.GetBlockValue("command", "")) cmd(block.GetBlockValue("command", ""))
{ {
if (cmd.empty()) if (cmd.empty())
throw std::runtime_error("No \"command\" parameter specified"); throw std::runtime_error("No \"command\" parameter specified");
} }
inline PipeOutput *
PipeOutput::Create(EventLoop &, const ConfigBlock &block)
{
return new PipeOutput(block);
}
inline void inline void
PipeOutput::Open(gcc_unused AudioFormat &audio_format) PipeOutput::Open(gcc_unused AudioFormat &audio_format)
{ {
@ -83,22 +75,9 @@ PipeOutput::Play(const void *chunk, size_t size)
return nbytes; return nbytes;
} }
typedef AudioOutputWrapper<PipeOutput> Wrapper;
const struct AudioOutputPlugin pipe_output_plugin = { const struct AudioOutputPlugin pipe_output_plugin = {
"pipe", "pipe",
nullptr, nullptr,
&Wrapper::Init, &PipeOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
nullptr,
&Wrapper::Play,
nullptr,
nullptr,
nullptr,
nullptr, nullptr,
}; };

View File

@ -24,7 +24,6 @@
#include "lib/pulse/LogError.hxx" #include "lib/pulse/LogError.hxx"
#include "lib/pulse/LockGuard.hxx" #include "lib/pulse/LockGuard.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "mixer/plugins/PulseMixerPlugin.hxx" #include "mixer/plugins/PulseMixerPlugin.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -44,11 +43,7 @@
#define MPD_PULSE_NAME "Music Player Daemon" #define MPD_PULSE_NAME "Music Player Daemon"
class PulseOutput { class PulseOutput final : AudioOutput {
friend struct AudioOutputWrapper<PulseOutput>;
AudioOutput base;
const char *name; const char *name;
const char *server; const char *server;
const char *sink; const char *sink;
@ -94,19 +89,21 @@ public:
static bool TestDefaultDevice(); static bool TestDefaultDevice();
static PulseOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &,
const ConfigBlock &block); const ConfigBlock &block) {
return new PulseOutput(block);
}
void Enable(); void Enable() override;
void Disable(); void Disable() noexcept override;
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format) override;
void Close(); void Close() noexcept override;
std::chrono::steady_clock::duration Delay() noexcept; std::chrono::steady_clock::duration Delay() const noexcept override;
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;
bool Pause(); bool Pause() noexcept override;
private: private:
/** /**
@ -179,7 +176,7 @@ private:
}; };
PulseOutput::PulseOutput(const ConfigBlock &block) PulseOutput::PulseOutput(const ConfigBlock &block)
:base(pulse_output_plugin), :AudioOutput(FLAG_ENABLE_DISABLE|FLAG_PAUSE),
name(block.GetBlockValue("name", "mpd_pulse")), name(block.GetBlockValue("name", "mpd_pulse")),
server(block.GetBlockValue("server")), server(block.GetBlockValue("server")),
sink(block.GetBlockValue("sink")) sink(block.GetBlockValue("sink"))
@ -416,13 +413,7 @@ PulseOutput::SetupContext()
} }
} }
PulseOutput * void
PulseOutput::Create(EventLoop &, const ConfigBlock &block)
{
return new PulseOutput(block);
}
inline void
PulseOutput::Enable() PulseOutput::Enable()
{ {
assert(mainloop == nullptr); assert(mainloop == nullptr);
@ -458,8 +449,8 @@ PulseOutput::Enable()
pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_unlock(mainloop);
} }
inline void void
PulseOutput::Disable() PulseOutput::Disable() noexcept
{ {
assert(mainloop != nullptr); assert(mainloop != nullptr);
@ -607,7 +598,7 @@ PulseOutput::SetupStream(const pa_sample_spec &ss)
pulse_output_stream_write_cb, this); pulse_output_stream_write_cb, this);
} }
inline void void
PulseOutput::Open(AudioFormat &audio_format) PulseOutput::Open(AudioFormat &audio_format)
{ {
assert(mainloop != nullptr); assert(mainloop != nullptr);
@ -680,8 +671,8 @@ PulseOutput::Open(AudioFormat &audio_format)
pause = false; pause = false;
} }
inline void void
PulseOutput::Close() PulseOutput::Close() noexcept
{ {
assert(mainloop != nullptr); assert(mainloop != nullptr);
@ -744,8 +735,8 @@ PulseOutput::StreamPause(bool _pause)
"pa_stream_cork() has failed"); "pa_stream_cork() has failed");
} }
inline std::chrono::steady_clock::duration std::chrono::steady_clock::duration
PulseOutput::Delay() noexcept PulseOutput::Delay() const noexcept
{ {
Pulse::LockGuard lock(mainloop); Pulse::LockGuard lock(mainloop);
@ -758,7 +749,7 @@ PulseOutput::Delay() noexcept
return result; return result;
} }
inline size_t size_t
PulseOutput::Play(const void *chunk, size_t size) PulseOutput::Play(const void *chunk, size_t size)
{ {
assert(mainloop != nullptr); assert(mainloop != nullptr);
@ -807,8 +798,8 @@ PulseOutput::Play(const void *chunk, size_t size)
return size; return size;
} }
inline void void
PulseOutput::Cancel() PulseOutput::Cancel() noexcept
{ {
assert(mainloop != nullptr); assert(mainloop != nullptr);
assert(stream != nullptr); assert(stream != nullptr);
@ -834,8 +825,8 @@ PulseOutput::Cancel()
pulse_wait_for_operation(mainloop, o); pulse_wait_for_operation(mainloop, o);
} }
inline bool bool
PulseOutput::Pause() PulseOutput::Pause() noexcept
{ {
assert(mainloop != nullptr); assert(mainloop != nullptr);
assert(stream != nullptr); assert(stream != nullptr);
@ -875,23 +866,9 @@ pulse_output_test_default_device(void)
return PulseOutput::TestDefaultDevice(); return PulseOutput::TestDefaultDevice();
} }
typedef AudioOutputWrapper<PulseOutput> Wrapper;
const struct AudioOutputPlugin pulse_output_plugin = { const struct AudioOutputPlugin pulse_output_plugin = {
"pulse", "pulse",
pulse_output_test_default_device, pulse_output_test_default_device,
&Wrapper::Init, PulseOutput::Create,
&Wrapper::Finish,
&Wrapper::Enable,
&Wrapper::Disable,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
nullptr,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
&Wrapper::Pause,
&pulse_mixer_plugin, &pulse_mixer_plugin,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "RecorderOutputPlugin.hxx" #include "RecorderOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "tag/Format.hxx" #include "tag/Format.hxx"
#include "encoder/ToOutputStream.hxx" #include "encoder/ToOutputStream.hxx"
#include "encoder/EncoderInterface.hxx" #include "encoder/EncoderInterface.hxx"
@ -42,11 +41,7 @@
static constexpr Domain recorder_domain("recorder"); static constexpr Domain recorder_domain("recorder");
class RecorderOutput { class RecorderOutput final : AudioOutput {
friend struct AudioOutputWrapper<RecorderOutput>;
AudioOutput base;
/** /**
* The configured encoder plugin. * The configured encoder plugin.
*/ */
@ -81,20 +76,23 @@ class RecorderOutput {
delete prepared_encoder; delete prepared_encoder;
} }
static RecorderOutput *Create(EventLoop &event_loop, public:
const ConfigBlock &block); static AudioOutput *Create(EventLoop &, const ConfigBlock &block) {
return new RecorderOutput(block);
}
void Open(AudioFormat &audio_format); private:
void Close(); void Open(AudioFormat &audio_format) override;
void Close() noexcept override;
/** /**
* Writes pending data from the encoder to the output file. * Writes pending data from the encoder to the output file.
*/ */
void EncoderToFile(); 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: private:
gcc_pure gcc_pure
@ -114,7 +112,7 @@ private:
}; };
RecorderOutput::RecorderOutput(const ConfigBlock &block) RecorderOutput::RecorderOutput(const ConfigBlock &block)
:base(recorder_output_plugin) :AudioOutput(0)
{ {
/* read configuration */ /* read configuration */
@ -141,12 +139,6 @@ RecorderOutput::RecorderOutput(const ConfigBlock &block)
prepared_encoder = encoder_init(*encoder_plugin, block); prepared_encoder = encoder_init(*encoder_plugin, block);
} }
RecorderOutput *
RecorderOutput::Create(EventLoop &, const ConfigBlock &block)
{
return new RecorderOutput(block);
}
inline void inline void
RecorderOutput::EncoderToFile() RecorderOutput::EncoderToFile()
{ {
@ -155,7 +147,7 @@ RecorderOutput::EncoderToFile()
EncoderToOutputStream(*file, *encoder); EncoderToOutputStream(*file, *encoder);
} }
inline void void
RecorderOutput::Open(AudioFormat &audio_format) RecorderOutput::Open(AudioFormat &audio_format)
{ {
/* create the output file */ /* create the output file */
@ -227,8 +219,8 @@ RecorderOutput::Commit()
delete file; delete file;
} }
inline void void
RecorderOutput::Close() RecorderOutput::Close() noexcept
{ {
if (file == nullptr) { if (file == nullptr) {
/* not currently encoding to a file; nothing needs to /* not currently encoding to a file; nothing needs to
@ -305,7 +297,7 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path)
path.ToUTF8().c_str()); path.ToUTF8().c_str());
} }
inline void void
RecorderOutput::SendTag(const Tag &tag) RecorderOutput::SendTag(const Tag &tag)
{ {
if (HasDynamicPath()) { if (HasDynamicPath()) {
@ -347,7 +339,7 @@ RecorderOutput::SendTag(const Tag &tag)
encoder->SendTag(tag); encoder->SendTag(tag);
} }
inline size_t size_t
RecorderOutput::Play(const void *chunk, size_t size) RecorderOutput::Play(const void *chunk, size_t size)
{ {
if (file == nullptr) { if (file == nullptr) {
@ -365,22 +357,9 @@ RecorderOutput::Play(const void *chunk, size_t size)
return size; return size;
} }
typedef AudioOutputWrapper<RecorderOutput> Wrapper;
const struct AudioOutputPlugin recorder_output_plugin = { const struct AudioOutputPlugin recorder_output_plugin = {
"recorder", "recorder",
nullptr, nullptr,
&Wrapper::Init, &RecorderOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
&Wrapper::SendTag,
&Wrapper::Play,
nullptr,
nullptr,
nullptr,
nullptr, nullptr,
}; };

View File

@ -21,7 +21,6 @@
#include "config.h" #include "config.h"
#include "RoarOutputPlugin.hxx" #include "RoarOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
@ -36,11 +35,7 @@
#include <roaraudio.h> #include <roaraudio.h>
#undef new #undef new
class RoarOutput { class RoarOutput final : AudioOutput {
friend struct AudioOutputWrapper<RoarOutput>;
AudioOutput base;
const std::string host, name; const std::string host, name;
roar_vs_t * vss; roar_vs_t * vss;
@ -54,23 +49,20 @@ class RoarOutput {
public: public:
RoarOutput(const ConfigBlock &block); RoarOutput(const ConfigBlock &block);
operator AudioOutput *() { static AudioOutput *Create(EventLoop &, const ConfigBlock &block) {
return &base;
}
static RoarOutput *Create(EventLoop &, const ConfigBlock &block) {
return new RoarOutput(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; int GetVolume() const;
void SetVolume(unsigned volume); 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"); static constexpr Domain roar_output_domain("roar_output");
@ -86,7 +78,7 @@ GetConfiguredRole(const ConfigBlock &block) noexcept
} }
RoarOutput::RoarOutput(const ConfigBlock &block) RoarOutput::RoarOutput(const ConfigBlock &block)
:base(roar_output_plugin), :AudioOutput(0),
host(block.GetBlockValue("server", "")), host(block.GetBlockValue("server", "")),
name(block.GetBlockValue("name", "MPD")), name(block.GetBlockValue("name", "MPD")),
role(GetConfiguredRole(block)) role(GetConfiguredRole(block))
@ -172,7 +164,7 @@ roar_use_audio_format(struct roar_audio_info *info,
} }
} }
inline void void
RoarOutput::Open(AudioFormat &audio_format) RoarOutput::Open(AudioFormat &audio_format)
{ {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
@ -196,8 +188,8 @@ RoarOutput::Open(AudioFormat &audio_format)
alive = true; alive = true;
} }
inline void void
RoarOutput::Close() RoarOutput::Close() noexcept
{ {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
@ -209,8 +201,8 @@ RoarOutput::Close()
roar_disconnect(&con); roar_disconnect(&con);
} }
inline void void
RoarOutput::Cancel() RoarOutput::Cancel() noexcept
{ {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
@ -237,7 +229,7 @@ RoarOutput::Cancel()
alive = true; alive = true;
} }
inline size_t size_t
RoarOutput::Play(const void *chunk, size_t size) RoarOutput::Play(const void *chunk, size_t size)
{ {
if (vss == nullptr) if (vss == nullptr)
@ -296,7 +288,7 @@ roar_tag_convert(TagType type, bool *is_uuid)
} }
} }
inline void void
RoarOutput::SendTag(const Tag &tag) RoarOutput::SendTag(const Tag &tag)
{ {
if (vss == nullptr) if (vss == nullptr)
@ -344,22 +336,9 @@ RoarOutput::SendTag(const Tag &tag)
roar_vs_meta(vss, vals, cnt, &(err)); roar_vs_meta(vss, vals, cnt, &(err));
} }
typedef AudioOutputWrapper<RoarOutput> Wrapper;
const struct AudioOutputPlugin roar_output_plugin = { const struct AudioOutputPlugin roar_output_plugin = {
"roar", "roar",
nullptr, nullptr,
&Wrapper::Init, &RoarOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
&Wrapper::SendTag,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
nullptr,
&roar_mixer_plugin, &roar_mixer_plugin,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "ShoutOutputPlugin.hxx" #include "ShoutOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "encoder/EncoderInterface.hxx" #include "encoder/EncoderInterface.hxx"
#include "encoder/EncoderPlugin.hxx" #include "encoder/EncoderPlugin.hxx"
#include "encoder/EncoderList.hxx" #include "encoder/EncoderList.hxx"
@ -39,9 +38,7 @@
static constexpr unsigned DEFAULT_CONN_TIMEOUT = 2; static constexpr unsigned DEFAULT_CONN_TIMEOUT = 2;
struct ShoutOutput final { struct ShoutOutput final : AudioOutput {
AudioOutput base;
shout_t *shout_conn; shout_t *shout_conn;
shout_metadata_t *shout_meta; shout_metadata_t *shout_meta;
@ -58,17 +55,17 @@ struct ShoutOutput final {
explicit ShoutOutput(const ConfigBlock &block); explicit ShoutOutput(const ConfigBlock &block);
~ShoutOutput(); ~ShoutOutput();
static ShoutOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &event_loop,
const ConfigBlock &block); const ConfigBlock &block);
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format) override;
void Close(); void Close() noexcept override;
std::chrono::steady_clock::duration Delay() const noexcept; std::chrono::steady_clock::duration Delay() const noexcept override;
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 Cancel(); void Cancel() noexcept override;
bool Pause(); bool Pause() noexcept override;
private: private:
void WritePage(); void WritePage();
@ -113,11 +110,11 @@ ShoutSetAudioInfo(shout_t *shout_conn, const AudioFormat &audio_format)
} }
ShoutOutput::ShoutOutput(const ConfigBlock &block) ShoutOutput::ShoutOutput(const ConfigBlock &block)
:base(shout_output_plugin), :AudioOutput(FLAG_PAUSE),
shout_conn(shout_new()), shout_conn(shout_new()),
shout_meta(shout_metadata_new()) shout_meta(shout_metadata_new())
{ {
base.NeedFullyDefinedAudioFormat(); NeedFullyDefinedAudioFormat();
const char *host = require_block_string(block, "host"); const char *host = require_block_string(block, "host");
const char *mount = require_block_string(block, "mount"); const char *mount = require_block_string(block, "mount");
@ -250,7 +247,7 @@ ShoutOutput::~ShoutOutput()
delete prepared_encoder; delete prepared_encoder;
} }
ShoutOutput * AudioOutput *
ShoutOutput::Create(EventLoop &, const ConfigBlock &block) ShoutOutput::Create(EventLoop &, const ConfigBlock &block)
{ {
if (shout_init_count == 0) if (shout_init_count == 0)
@ -306,7 +303,7 @@ ShoutOutput::WritePage()
} }
void void
ShoutOutput::Close() ShoutOutput::Close() noexcept
{ {
try { try {
encoder->End(); encoder->End();
@ -326,7 +323,7 @@ ShoutOutput::Close()
} }
void void
ShoutOutput::Cancel() ShoutOutput::Cancel() noexcept
{ {
/* needs to be implemented for shout */ /* needs to be implemented for shout */
} }
@ -381,7 +378,7 @@ ShoutOutput::Play(const void *chunk, size_t size)
} }
bool bool
ShoutOutput::Pause() ShoutOutput::Pause() noexcept
{ {
static char silence[1020]; static char silence[1020];
@ -446,22 +443,9 @@ ShoutOutput::SendTag(const Tag &tag)
WritePage(); WritePage();
} }
typedef AudioOutputWrapper<ShoutOutput> Wrapper;
const struct AudioOutputPlugin shout_output_plugin = { const struct AudioOutputPlugin shout_output_plugin = {
"shout", "shout",
nullptr, nullptr,
&Wrapper::Init, &ShoutOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
&Wrapper::SendTag,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
&Wrapper::Pause,
nullptr, nullptr,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "SndioOutputPlugin.hxx" #include "SndioOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "Log.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"); static constexpr Domain sndio_output_domain("sndio_output");
class SndioOutput { class SndioOutput final : AudioOutput {
friend struct AudioOutputWrapper<SndioOutput>;
AudioOutput base;
const char *const device; const char *const device;
const unsigned buffer_time; /* in ms */ const unsigned buffer_time; /* in ms */
struct sio_hdl *sio_hdl; struct sio_hdl *sio_hdl;
@ -55,29 +52,25 @@ class SndioOutput {
public: public:
SndioOutput(const ConfigBlock &block); SndioOutput(const ConfigBlock &block);
static SndioOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &,
const ConfigBlock &block); const ConfigBlock &block) {
return new SndioOutput(block);
}
void Open(AudioFormat &audio_format); private:
void Close(); void Open(AudioFormat &audio_format) override;
size_t Play(const void *chunk, size_t size); void Close() noexcept override;
void Cancel(); size_t Play(const void *chunk, size_t size) override;
}; };
SndioOutput::SndioOutput(const ConfigBlock &block) SndioOutput::SndioOutput(const ConfigBlock &block)
:base(sndio_output_plugin), :AudioOutput(0),
device(block.GetBlockValue("device", SIO_DEVANY)), device(block.GetBlockValue("device", SIO_DEVANY)),
buffer_time(block.GetBlockValue("buffer_time", buffer_time(block.GetBlockValue("buffer_time",
MPD_SNDIO_BUFFER_TIME_MS)) MPD_SNDIO_BUFFER_TIME_MS))
{ {
} }
SndioOutput *
SndioOutput::Create(EventLoop &, const ConfigBlock &block)
{
return new SndioOutput(block);
}
static bool static bool
sndio_test_default_device() sndio_test_default_device()
{ {
@ -154,7 +147,7 @@ SndioOutput::Open(AudioFormat &audio_format)
} }
void void
SndioOutput::Close() SndioOutput::Close() noexcept
{ {
sio_close(sio_hdl); sio_close(sio_hdl);
} }
@ -170,22 +163,9 @@ SndioOutput::Play(const void *chunk, size_t size)
return n; return n;
} }
typedef AudioOutputWrapper<SndioOutput> Wrapper;
const struct AudioOutputPlugin sndio_output_plugin = { const struct AudioOutputPlugin sndio_output_plugin = {
"sndio", "sndio",
sndio_test_default_device, sndio_test_default_device,
&Wrapper::Init, &SndioOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
nullptr,
&Wrapper::Play,
nullptr,
nullptr,
nullptr,
nullptr, nullptr,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "SolarisOutputPlugin.hxx" #include "SolarisOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "system/FileDescriptor.hxx" #include "system/FileDescriptor.hxx"
#include "system/Error.hxx" #include "system/Error.hxx"
@ -50,30 +49,27 @@ struct audio_info {
#endif #endif
class SolarisOutput { class SolarisOutput final : AudioOutput {
friend struct AudioOutputWrapper<SolarisOutput>;
AudioOutput base;
/* configuration */ /* configuration */
const char *const device; const char *const device;
FileDescriptor fd; FileDescriptor fd;
explicit SolarisOutput(const ConfigBlock &block) explicit SolarisOutput(const ConfigBlock &block)
:base(solaris_output_plugin), :AudioOutput(0),
device(block.GetBlockValue("device", "/dev/audio")) {} device(block.GetBlockValue("device", "/dev/audio")) {}
public: public:
static SolarisOutput *Create(EventLoop &, const ConfigBlock &block) { static AudioOutput *Create(EventLoop &, const ConfigBlock &block) {
return new SolarisOutput(block); return new SolarisOutput(block);
} }
void Open(AudioFormat &audio_format); private:
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;
void Cancel(); void Cancel() noexcept override;
}; };
static bool static bool
@ -128,7 +124,7 @@ SolarisOutput::Open(AudioFormat &audio_format)
} }
void void
SolarisOutput::Close() SolarisOutput::Close() noexcept
{ {
fd.Close(); fd.Close();
} }
@ -144,27 +140,14 @@ SolarisOutput::Play(const void *chunk, size_t size)
} }
void void
SolarisOutput::Cancel() SolarisOutput::Cancel() noexcept
{ {
ioctl(fd.Get(), I_FLUSH); ioctl(fd.Get(), I_FLUSH);
} }
typedef AudioOutputWrapper<SolarisOutput> Wrapper;
const struct AudioOutputPlugin solaris_output_plugin = { const struct AudioOutputPlugin solaris_output_plugin = {
"solaris", "solaris",
solaris_output_test_default_device, solaris_output_test_default_device,
&Wrapper::Init, &SolarisOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
nullptr,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
nullptr,
nullptr, nullptr,
}; };

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "WinmmOutputPlugin.hxx" #include "WinmmOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "../Wrapper.hxx"
#include "pcm/PcmBuffer.hxx" #include "pcm/PcmBuffer.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
@ -39,11 +38,7 @@ struct WinmmBuffer {
WAVEHDR hdr; WAVEHDR hdr;
}; };
class WinmmOutput { class WinmmOutput final : AudioOutput {
friend struct AudioOutputWrapper<WinmmOutput>;
AudioOutput base;
const UINT device_id; const UINT device_id;
HWAVEOUT handle; HWAVEOUT handle;
@ -63,16 +58,17 @@ public:
return handle; return handle;
} }
static WinmmOutput *Create(EventLoop &, const ConfigBlock &block) { static AudioOutput *Create(EventLoop &, const ConfigBlock &block) {
return new WinmmOutput(block); return new WinmmOutput(block);
} }
void Open(AudioFormat &audio_format); private:
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;
void Drain(); void Drain() override;
void Cancel(); void Cancel() noexcept override;
private: private:
/** /**
@ -82,7 +78,7 @@ private:
void DrainAllBuffers(); void DrainAllBuffers();
void Stop(); void Stop() noexcept;
}; };
@ -148,7 +144,7 @@ get_device_id(const char *device_name)
} }
WinmmOutput::WinmmOutput(const ConfigBlock &block) WinmmOutput::WinmmOutput(const ConfigBlock &block)
:base(winmm_output_plugin), :AudioOutput(0),
device_id(get_device_id(block.GetBlockValue("device"))) device_id(get_device_id(block.GetBlockValue("device")))
{ {
} }
@ -202,7 +198,7 @@ WinmmOutput::Open(AudioFormat &audio_format)
} }
void void
WinmmOutput::Close() WinmmOutput::Close() noexcept
{ {
for (auto &i : buffers) for (auto &i : buffers)
i.buffer.Clear(); i.buffer.Clear();
@ -291,7 +287,7 @@ WinmmOutput::DrainAllBuffers()
} }
void void
WinmmOutput::Stop() WinmmOutput::Stop() noexcept
{ {
waveOutReset(handle); waveOutReset(handle);
@ -311,27 +307,14 @@ WinmmOutput::Drain()
} }
void void
WinmmOutput::Cancel() WinmmOutput::Cancel() noexcept
{ {
Stop(); Stop();
} }
typedef AudioOutputWrapper<WinmmOutput> Wrapper;
const struct AudioOutputPlugin winmm_output_plugin = { const struct AudioOutputPlugin winmm_output_plugin = {
"winmm", "winmm",
winmm_output_test_default_device, winmm_output_test_default_device,
&Wrapper::Init, WinmmOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
nullptr,
nullptr,
&Wrapper::Play,
&Wrapper::Drain,
&Wrapper::Cancel,
nullptr,
&winmm_mixer_plugin, &winmm_mixer_plugin,
}; };

View File

@ -26,8 +26,7 @@
#define MPD_OUTPUT_HTTPD_INTERNAL_H #define MPD_OUTPUT_HTTPD_INTERNAL_H
#include "HttpdClient.hxx" #include "HttpdClient.hxx"
#include "output/Wrapper.hxx" #include "output/Interface.hxx"
#include "output/Filtered.hxx"
#include "output/Timer.hxx" #include "output/Timer.hxx"
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
@ -49,11 +48,7 @@ class PreparedEncoder;
class Encoder; class Encoder;
struct Tag; struct Tag;
class HttpdOutput final : ServerSocket, DeferredMonitor { class HttpdOutput final : AudioOutput, ServerSocket, DeferredMonitor {
friend struct AudioOutputWrapper<HttpdOutput>;
AudioOutput base;
/** /**
* True if the audio output is open and accepts client * True if the audio output is open and accepts client
* connections. * connections.
@ -157,11 +152,9 @@ public:
HttpdOutput(EventLoop &_loop, const ConfigBlock &block); HttpdOutput(EventLoop &_loop, const ConfigBlock &block);
~HttpdOutput(); ~HttpdOutput();
static HttpdOutput *Create(EventLoop &event_loop, static AudioOutput *Create(EventLoop &event_loop,
const ConfigBlock &block); const ConfigBlock &block) {
return new HttpdOutput(event_loop, block);
static constexpr HttpdOutput *Cast(AudioOutput *ao) {
return &ContainerCast(*ao, &HttpdOutput::base);
} }
using DeferredMonitor::GetEventLoop; using DeferredMonitor::GetEventLoop;
@ -169,11 +162,11 @@ public:
void Bind(); void Bind();
void Unbind(); void Unbind();
void Enable() { void Enable() override {
Bind(); Bind();
} }
void Disable() { void Disable() noexcept override {
Unbind(); Unbind();
} }
@ -187,12 +180,12 @@ public:
/** /**
* Caller must lock the mutex. * Caller must lock the mutex.
*/ */
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format) override;
/** /**
* Caller must lock the mutex. * Caller must lock the mutex.
*/ */
void Close(); void Close() noexcept override;
/** /**
* Check whether there is at least one client. * Check whether there is at least one client.
@ -227,7 +220,7 @@ public:
void SendHeader(HttpdClient &client) const; void SendHeader(HttpdClient &client) const;
gcc_pure 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 * Reads data from the encoder (as much as available) and
@ -252,14 +245,14 @@ public:
*/ */
void EncodeAndPlay(const void *chunk, size_t size); 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 CancelAllClients();
void Cancel(); void Cancel() noexcept override;
bool Pause(); bool Pause() noexcept override;
private: private:
virtual void RunDeferred() override; virtual void RunDeferred() override;

View File

@ -50,8 +50,8 @@ const Domain httpd_output_domain("httpd_output");
inline inline
HttpdOutput::HttpdOutput(EventLoop &_loop, const ConfigBlock &block) HttpdOutput::HttpdOutput(EventLoop &_loop, const ConfigBlock &block)
:ServerSocket(_loop), DeferredMonitor(_loop), :AudioOutput(FLAG_ENABLE_DISABLE|FLAG_PAUSE),
base(httpd_output_plugin), ServerSocket(_loop), DeferredMonitor(_loop),
encoder(nullptr), unflushed_input(0), encoder(nullptr), unflushed_input(0),
metadata(nullptr) 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 * Creates a new #HttpdClient object and adds it into the
* HttpdOutput.clients linked list. * HttpdOutput.clients linked list.
@ -246,7 +240,7 @@ HttpdOutput::OpenEncoder(AudioFormat &audio_format)
unflushed_input = 0; unflushed_input = 0;
} }
inline void void
HttpdOutput::Open(AudioFormat &audio_format) HttpdOutput::Open(AudioFormat &audio_format)
{ {
assert(!open); assert(!open);
@ -264,8 +258,8 @@ HttpdOutput::Open(AudioFormat &audio_format)
pause = false; pause = false;
} }
inline void void
HttpdOutput::Close() HttpdOutput::Close() noexcept
{ {
assert(open); assert(open);
@ -300,7 +294,7 @@ HttpdOutput::SendHeader(HttpdClient &client) const
client.PushPage(header); client.PushPage(header);
} }
inline std::chrono::steady_clock::duration std::chrono::steady_clock::duration
HttpdOutput::Delay() const noexcept HttpdOutput::Delay() const noexcept
{ {
if (!LockHasClients() && pause) { if (!LockHasClients() && pause) {
@ -367,7 +361,7 @@ HttpdOutput::EncodeAndPlay(const void *chunk, size_t size)
BroadcastFromEncoder(); BroadcastFromEncoder();
} }
inline size_t size_t
HttpdOutput::Play(const void *chunk, size_t size) HttpdOutput::Play(const void *chunk, size_t size)
{ {
pause = false; pause = false;
@ -383,7 +377,7 @@ HttpdOutput::Play(const void *chunk, size_t size)
} }
bool bool
HttpdOutput::Pause() HttpdOutput::Pause() noexcept
{ {
pause = true; pause = true;
@ -395,7 +389,7 @@ HttpdOutput::Pause()
return true; return true;
} }
inline void void
HttpdOutput::SendTag(const Tag &tag) HttpdOutput::SendTag(const Tag &tag)
{ {
if (encoder->ImplementsTag()) { if (encoder->ImplementsTag()) {
@ -463,29 +457,16 @@ HttpdOutput::CancelAllClients()
} }
void void
HttpdOutput::Cancel() HttpdOutput::Cancel() noexcept
{ {
BlockingCall(GetEventLoop(), [this](){ BlockingCall(GetEventLoop(), [this](){
CancelAllClients(); CancelAllClients();
}); });
} }
typedef AudioOutputWrapper<HttpdOutput> Wrapper;
const struct AudioOutputPlugin httpd_output_plugin = { const struct AudioOutputPlugin httpd_output_plugin = {
"httpd", "httpd",
nullptr, nullptr,
&Wrapper::Init, &HttpdOutput::Create,
&Wrapper::Finish,
&Wrapper::Enable,
&Wrapper::Disable,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
&Wrapper::SendTag,
&Wrapper::Play,
nullptr,
&Wrapper::Cancel,
&Wrapper::Pause,
nullptr, nullptr,
}; };

View File

@ -24,7 +24,6 @@
#include "Play.hxx" #include "Play.hxx"
#include "AndroidSimpleBufferQueue.hxx" #include "AndroidSimpleBufferQueue.hxx"
#include "../../OutputAPI.hxx" #include "../../OutputAPI.hxx"
#include "../../Wrapper.hxx"
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#include "util/Macros.hxx" #include "util/Macros.hxx"
@ -37,14 +36,10 @@
#include <stdexcept> #include <stdexcept>
class SlesOutput { class SlesOutput final : AudioOutput {
friend struct AudioOutputWrapper<SlesOutput>;
static constexpr unsigned N_BUFFERS = 3; static constexpr unsigned N_BUFFERS = 3;
static constexpr size_t BUFFER_SIZE = 65536; static constexpr size_t BUFFER_SIZE = 65536;
AudioOutput base;
SLES::Object engine_object, mix_object, play_object; SLES::Object engine_object, mix_object, play_object;
SLES::Play play; SLES::Play play;
SLES::AndroidSimpleBufferQueue queue; SLES::AndroidSimpleBufferQueue queue;
@ -86,30 +81,28 @@ class SlesOutput {
*/ */
uint8_t buffers[N_BUFFERS][BUFFER_SIZE]; uint8_t buffers[N_BUFFERS][BUFFER_SIZE];
public: SlesOutput():AudioOutput(FLAG_PAUSE) {}
SlesOutput();
operator AudioOutput *() { public:
return &base; static AudioOutput *Create(EventLoop &, const ConfigBlock &) {
return new SlesOutput();
} }
static SlesOutput *Create(EventLoop &event_loop, private:
const ConfigBlock &block); void Open(AudioFormat &audio_format) override;
void Close() noexcept override;
void Open(AudioFormat &audio_format); std::chrono::steady_clock::duration Delay() const noexcept override {
void Close();
std::chrono::steady_clock::duration Delay() noexcept {
return pause && !cancel return pause && !cancel
? std::chrono::milliseconds(100) ? std::chrono::milliseconds(100)
: std::chrono::steady_clock::duration::zero(); : 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 Drain() override;
void Cancel(); void Cancel() noexcept override;
bool Pause(); bool Pause() noexcept override;
private: private:
void PlayedCallback(); void PlayedCallback();
@ -129,12 +122,7 @@ private:
static constexpr Domain sles_domain("sles"); static constexpr Domain sles_domain("sles");
SlesOutput::SlesOutput() void
:base(sles_output_plugin)
{
}
inline void
SlesOutput::Open(AudioFormat &audio_format) SlesOutput::Open(AudioFormat &audio_format)
{ {
SLresult result; SLresult result;
@ -300,8 +288,8 @@ SlesOutput::Open(AudioFormat &audio_format)
audio_format.format = SampleFormat::S16; audio_format.format = SampleFormat::S16;
} }
inline void void
SlesOutput::Close() SlesOutput::Close() noexcept
{ {
play.SetPlayState(SL_PLAYSTATE_STOPPED); play.SetPlayState(SL_PLAYSTATE_STOPPED);
play_object.Destroy(); play_object.Destroy();
@ -309,7 +297,7 @@ SlesOutput::Close()
engine_object.Destroy(); engine_object.Destroy();
} }
inline size_t size_t
SlesOutput::Play(const void *chunk, size_t size) SlesOutput::Play(const void *chunk, size_t size)
{ {
cancel = false; cancel = false;
@ -348,7 +336,7 @@ SlesOutput::Play(const void *chunk, size_t size)
return nbytes; return nbytes;
} }
inline void void
SlesOutput::Drain() SlesOutput::Drain()
{ {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
@ -359,8 +347,8 @@ SlesOutput::Drain()
cond.wait(mutex); cond.wait(mutex);
} }
inline void void
SlesOutput::Cancel() SlesOutput::Cancel() noexcept
{ {
pause = true; pause = true;
cancel = true; cancel = true;
@ -379,8 +367,8 @@ SlesOutput::Cancel()
filled = 0; filled = 0;
} }
inline bool bool
SlesOutput::Pause() SlesOutput::Pause() noexcept
{ {
cancel = false; cancel = false;
@ -415,28 +403,9 @@ sles_test_default_device()
return true; return true;
} }
inline SlesOutput *
SlesOutput::Create(EventLoop &, const ConfigBlock &)
{
return new SlesOutput();
}
typedef AudioOutputWrapper<SlesOutput> Wrapper;
const struct AudioOutputPlugin sles_output_plugin = { const struct AudioOutputPlugin sles_output_plugin = {
"sles", "sles",
sles_test_default_device, sles_test_default_device,
&Wrapper::Init, SlesOutput::Create,
&Wrapper::Finish,
nullptr,
nullptr,
&Wrapper::Open,
&Wrapper::Close,
&Wrapper::Delay,
nullptr,
&Wrapper::Play,
&Wrapper::Drain,
&Wrapper::Cancel,
&Wrapper::Pause,
nullptr, nullptr,
}; };

View File

@ -34,13 +34,15 @@
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <memory>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
static AudioOutput * static std::unique_ptr<AudioOutput>
load_audio_output(EventLoop &event_loop, const char *name) load_audio_output(EventLoop &event_loop, const char *name)
{ {
const auto *block = config_find_block(ConfigBlockOption::AUDIO_OUTPUT, 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", throw FormatRuntimeError("No such audio output plugin: %s",
plugin_name); plugin_name);
return ao_plugin_init(event_loop, *plugin, *block); return std::unique_ptr<AudioOutput>(ao_plugin_init(event_loop, *plugin,
*block));
} }
static void static void
@ -66,11 +69,11 @@ run_output(AudioOutput &ao, AudioFormat audio_format)
{ {
/* open the audio output */ /* open the audio output */
ao_plugin_enable(ao); ao.Enable();
AtScopeExit(&ao) { ao_plugin_disable(ao); }; AtScopeExit(&ao) { ao.Disable(); };
ao_plugin_open(ao, audio_format); ao.Open(audio_format);
AtScopeExit(&ao) { ao_plugin_close(ao); }; AtScopeExit(&ao) { ao.Close(); };
fprintf(stderr, "audio_format=%s\n", fprintf(stderr, "audio_format=%s\n",
ToString(audio_format).c_str()); 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; size_t play_length = (length / frame_size) * frame_size;
if (play_length > 0) { if (play_length > 0) {
size_t consumed = ao_plugin_play(ao, size_t consumed = ao.Play(buffer, play_length);
buffer, play_length);
assert(consumed <= length); assert(consumed <= length);
assert(consumed % frame_size == 0); assert(consumed % frame_size == 0);
@ -126,7 +128,7 @@ try {
/* initialize the audio output */ /* 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 */ /* parse the audio format */
@ -139,7 +141,7 @@ try {
/* cleanup and exit */ /* cleanup and exit */
ao_plugin_finish(ao); ao.reset();
config_global_finish(); config_global_finish();