output/Interface: convert to abstract class
Yet another C-style vtable replaced with C++.
This commit is contained in:
parent
1cf7f3d87c
commit
31bad5f7af
|
@ -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 \
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue