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/Interface.hxx \
src/output/Filtered.cxx src/output/Filtered.hxx \
src/output/Wrapper.hxx \
src/output/Registry.cxx src/output/Registry.hxx \
src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx \
src/output/SharedPipeConsumer.cxx src/output/SharedPipeConsumer.hxx \

View File

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

View File

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

View File

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

View File

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

View File

@ -20,22 +20,33 @@
#ifndef MPD_AUDIO_OUTPUT_INTERFACE_HXX
#define MPD_AUDIO_OUTPUT_INTERFACE_HXX
struct AudioOutputPlugin;
#include <chrono>
struct AudioFormat;
struct Tag;
class AudioOutput {
/**
* The plugin which implements this output device.
*/
const AudioOutputPlugin &plugin;
const unsigned flags;
bool need_fully_defined_audio_format = false;
public:
AudioOutput(const AudioOutputPlugin &_plugin)
:plugin(_plugin) {}
protected:
static constexpr unsigned FLAG_ENABLE_DISABLE = 0x1;
static constexpr unsigned FLAG_PAUSE = 0x2;
const AudioOutputPlugin &GetPlugin() const {
return plugin;
public:
explicit AudioOutput(unsigned _flags):flags(_flags) {}
virtual ~AudioOutput() = default;
AudioOutput(const AudioOutput &) = delete;
AudioOutput &operator=(const AudioOutput &) = delete;
bool SupportsEnableDisable() const {
return flags & FLAG_ENABLE_DISABLE;
}
bool SupportsPause() const {
return flags & FLAG_PAUSE;
}
bool GetNeedFullyDefinedAudioFormat() const {
@ -50,6 +61,87 @@ public:
void NeedFullyDefinedAudioFormat() {
need_fully_defined_audio_format = true;
}
/**
* Enable the device. This may allocate resources, preparing
* for the device to be opened.
*
* Throws #std::runtime_error on error.
*/
virtual void Enable() {}
/**
* Disables the device. It is closed before this method is
* called.
*/
virtual void Disable() noexcept {}
/**
* Really open the device.
*
* Throws #std::runtime_error on error.
*
* @param audio_format the audio format in which data is going
* to be delivered; may be modified by the plugin
*/
virtual void Open(AudioFormat &audio_format) = 0;
/**
* Close the device.
*/
virtual void Close() noexcept = 0;
/**
* Returns a positive number if the output thread shall further
* delay the next call to Play() or Pause(), which will happen
* until this function returns 0. This should be implemented
* instead of doing a sleep inside the plugin, because this
* allows MPD to listen to commands meanwhile.
*
* @return the duration to wait
*/
virtual std::chrono::steady_clock::duration Delay() const noexcept {
return std::chrono::steady_clock::duration::zero();
}
/**
* Display metadata for the next chunk. Optional method,
* because not all devices can display metadata.
*/
virtual void SendTag(const Tag &) {}
/**
* Play a chunk of audio data.
*
* Throws #std::runtime_error on error.
*
* @return the number of bytes played
*/
virtual size_t Play(const void *chunk, size_t size) = 0;
/**
* Wait until the device has finished playing.
*/
virtual void Drain() {}
/**
* Try to cancel data which may still be in the device's
* buffers.
*/
virtual void Cancel() noexcept {}
/**
* Pause the device. If supported, it may perform a special
* action, which keeps the device open, but does not play
* anything. Output plugins like "shout" might want to play
* silence during pause, so their clients won't be
* disconnected. Plugins which do not support pausing will
* simply be closed, and have to be reopened when unpaused.
*
* @return false on error (output will be closed by caller),
* true for continue to pause
*/
virtual bool Pause() noexcept { return true; }
};
#endif

View File

@ -19,7 +19,6 @@
#include "config.h"
#include "OutputPlugin.hxx"
#include "Interface.hxx"
#include <assert.h>
@ -32,76 +31,3 @@ ao_plugin_init(EventLoop &event_loop,
return plugin.init(event_loop, block);
}
void
ao_plugin_finish(AudioOutput *ao) noexcept
{
ao->GetPlugin().finish(ao);
}
void
ao_plugin_enable(AudioOutput &ao)
{
if (ao.GetPlugin().enable != nullptr)
ao.GetPlugin().enable(&ao);
}
void
ao_plugin_disable(AudioOutput &ao) noexcept
{
if (ao.GetPlugin().disable != nullptr)
ao.GetPlugin().disable(&ao);
}
void
ao_plugin_open(AudioOutput &ao, AudioFormat &audio_format)
{
ao.GetPlugin().open(&ao, audio_format);
}
void
ao_plugin_close(AudioOutput &ao) noexcept
{
ao.GetPlugin().close(&ao);
}
std::chrono::steady_clock::duration
ao_plugin_delay(AudioOutput &ao) noexcept
{
return ao.GetPlugin().delay != nullptr
? ao.GetPlugin().delay(&ao)
: std::chrono::steady_clock::duration::zero();
}
void
ao_plugin_send_tag(AudioOutput &ao, const Tag &tag)
{
if (ao.GetPlugin().send_tag != nullptr)
ao.GetPlugin().send_tag(&ao, tag);
}
size_t
ao_plugin_play(AudioOutput &ao, const void *chunk, size_t size)
{
return ao.GetPlugin().play(&ao, chunk, size);
}
void
ao_plugin_drain(AudioOutput &ao)
{
if (ao.GetPlugin().drain != nullptr)
ao.GetPlugin().drain(&ao);
}
void
ao_plugin_cancel(AudioOutput &ao) noexcept
{
if (ao.GetPlugin().cancel != nullptr)
ao.GetPlugin().cancel(&ao);
}
bool
ao_plugin_pause(AudioOutput &ao)
{
return ao.GetPlugin().pause != nullptr && ao.GetPlugin().pause(&ao);
}

View File

@ -59,91 +59,6 @@ struct AudioOutputPlugin {
*/
AudioOutput *(*init)(EventLoop &event_loop, const ConfigBlock &block);
/**
* Free resources allocated by this device.
*/
void (*finish)(AudioOutput *data);
/**
* Enable the device. This may allocate resources, preparing
* for the device to be opened.
*
* Throws #std::runtime_error on error.
*/
void (*enable)(AudioOutput *data);
/**
* Disables the device. It is closed before this method is
* called.
*/
void (*disable)(AudioOutput *data);
/**
* Really open the device.
*
* Throws #std::runtime_error on error.
*
* @param audio_format the audio format in which data is going
* to be delivered; may be modified by the plugin
*/
void (*open)(AudioOutput *data, AudioFormat &audio_format);
/**
* Close the device.
*/
void (*close)(AudioOutput *data);
/**
* Returns a positive number if the output thread shall further
* delay the next call to play() or pause(), which will happen
* until this function returns 0. This should be implemented
* instead of doing a sleep inside the plugin, because this
* allows MPD to listen to commands meanwhile.
*
* @return the duration to wait
*/
std::chrono::steady_clock::duration (*delay)(AudioOutput *data) noexcept;
/**
* Display metadata for the next chunk. Optional method,
* because not all devices can display metadata.
*/
void (*send_tag)(AudioOutput *data, const Tag &tag);
/**
* Play a chunk of audio data.
*
* Throws #std::runtime_error on error.
*
* @return the number of bytes played
*/
size_t (*play)(AudioOutput *data,
const void *chunk, size_t size);
/**
* Wait until the device has finished playing.
*/
void (*drain)(AudioOutput *data);
/**
* Try to cancel data which may still be in the device's
* buffers.
*/
void (*cancel)(AudioOutput *data);
/**
* Pause the device. If supported, it may perform a special
* action, which keeps the device open, but does not play
* anything. Output plugins like "shout" might want to play
* silence during pause, so their clients won't be
* disconnected. Plugins which do not support pausing will
* simply be closed, and have to be reopened when unpaused.
*
* @return false on error (output will be closed by caller),
* true for continue to pause
*/
bool (*pause)(AudioOutput *data);
/**
* The mixer plugin associated with this output plugin. This
* may be nullptr if no mixer plugin is implemented. When
@ -167,38 +82,4 @@ ao_plugin_init(EventLoop &event_loop,
const AudioOutputPlugin &plugin,
const ConfigBlock &block);
void
ao_plugin_finish(AudioOutput *ao) noexcept;
void
ao_plugin_enable(AudioOutput &ao);
void
ao_plugin_disable(AudioOutput &ao) noexcept;
void
ao_plugin_open(AudioOutput &ao, AudioFormat &audio_format);
void
ao_plugin_close(AudioOutput &ao) noexcept;
gcc_pure
std::chrono::steady_clock::duration
ao_plugin_delay(AudioOutput &ao) noexcept;
void
ao_plugin_send_tag(AudioOutput &ao, const Tag &tag);
size_t
ao_plugin_play(AudioOutput &ao, const void *chunk, size_t size);
void
ao_plugin_drain(AudioOutput &ao);
void
ao_plugin_cancel(AudioOutput &ao) noexcept;
bool
ao_plugin_pause(AudioOutput &ao);
#endif

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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