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