output/Interface: define a new struct AudioOutput
Hide struct FilteredAudioOutput from the plugins, preparing for hiding MPD's core internals.
This commit is contained in:
parent
e11229494e
commit
bea5681fd8
11
Makefile.am
11
Makefile.am
@ -1373,6 +1373,7 @@ OUTPUT_LIBS = \
|
|||||||
OUTPUT_API_SRC = \
|
OUTPUT_API_SRC = \
|
||||||
src/output/Client.hxx \
|
src/output/Client.hxx \
|
||||||
src/output/OutputAPI.hxx \
|
src/output/OutputAPI.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/Wrapper.hxx \
|
||||||
src/output/Registry.cxx src/output/Registry.hxx \
|
src/output/Registry.cxx src/output/Registry.hxx \
|
||||||
@ -2105,11 +2106,10 @@ test_run_convert_LDADD = \
|
|||||||
libutil.a
|
libutil.a
|
||||||
|
|
||||||
test_run_output_LDADD = $(MPD_LIBS) \
|
test_run_output_LDADD = $(MPD_LIBS) \
|
||||||
$(PCM_LIBS) \
|
|
||||||
$(OUTPUT_LIBS) \
|
$(OUTPUT_LIBS) \
|
||||||
$(ENCODER_LIBS) \
|
$(ENCODER_LIBS) \
|
||||||
libmixer_plugins.a \
|
libmixer_plugins.a \
|
||||||
$(FILTER_LIBS) \
|
$(PCM_LIBS) \
|
||||||
$(TAG_LIBS) \
|
$(TAG_LIBS) \
|
||||||
libconf.a \
|
libconf.a \
|
||||||
libbasic.a \
|
libbasic.a \
|
||||||
@ -2123,13 +2123,10 @@ test_run_output_LDADD = $(MPD_LIBS) \
|
|||||||
test_run_output_SOURCES = test/run_output.cxx \
|
test_run_output_SOURCES = test/run_output.cxx \
|
||||||
src/Log.cxx src/LogBackend.cxx \
|
src/Log.cxx src/LogBackend.cxx \
|
||||||
src/output/Domain.cxx \
|
src/output/Domain.cxx \
|
||||||
src/output/Init.cxx src/output/Finish.cxx src/output/Registry.cxx \
|
src/output/Registry.cxx \
|
||||||
src/output/OutputPlugin.cxx \
|
src/output/OutputPlugin.cxx \
|
||||||
src/mixer/MixerControl.cxx \
|
src/mixer/MixerControl.cxx \
|
||||||
src/mixer/MixerType.cxx \
|
src/mixer/MixerType.cxx
|
||||||
src/filter/FilterPlugin.cxx \
|
|
||||||
src/filter/FilterConfig.cxx \
|
|
||||||
src/filter/Observer.cxx
|
|
||||||
|
|
||||||
test_read_mixer_LDADD = \
|
test_read_mixer_LDADD = \
|
||||||
libpcm.a \
|
libpcm.a \
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "Filtered.hxx"
|
#include "Filtered.hxx"
|
||||||
|
#include "Interface.hxx"
|
||||||
#include "OutputPlugin.hxx"
|
#include "OutputPlugin.hxx"
|
||||||
#include "Domain.hxx"
|
#include "Domain.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
@ -31,22 +32,22 @@
|
|||||||
bool
|
bool
|
||||||
FilteredAudioOutput::SupportsEnableDisable() const noexcept
|
FilteredAudioOutput::SupportsEnableDisable() const noexcept
|
||||||
{
|
{
|
||||||
assert((plugin.enable == nullptr) == (plugin.disable == nullptr));
|
assert((output->plugin.enable == nullptr) == (output->plugin.disable == nullptr));
|
||||||
|
|
||||||
return plugin.enable != nullptr;
|
return output->plugin.enable != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
FilteredAudioOutput::SupportsPause() const noexcept
|
FilteredAudioOutput::SupportsPause() const noexcept
|
||||||
{
|
{
|
||||||
return plugin.pause != nullptr;
|
return output->plugin.pause != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FilteredAudioOutput::Enable()
|
FilteredAudioOutput::Enable()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
ao_plugin_enable(*this);
|
ao_plugin_enable(*output);
|
||||||
} 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()));
|
||||||
@ -56,7 +57,7 @@ FilteredAudioOutput::Enable()
|
|||||||
void
|
void
|
||||||
FilteredAudioOutput::Disable() noexcept
|
FilteredAudioOutput::Disable() noexcept
|
||||||
{
|
{
|
||||||
ao_plugin_disable(*this);
|
ao_plugin_disable(*output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -76,7 +77,7 @@ FilteredAudioOutput::OpenOutputAndConvert(AudioFormat desired_audio_format)
|
|||||||
out_audio_format = desired_audio_format;
|
out_audio_format = desired_audio_format;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ao_plugin_open(*this, out_audio_format);
|
ao_plugin_open(*output, 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()));
|
||||||
@ -90,7 +91,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(*this);
|
ao_plugin_close(*output);
|
||||||
|
|
||||||
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
|
||||||
@ -119,7 +120,7 @@ FilteredAudioOutput::CloseOutput(bool drain) noexcept
|
|||||||
else
|
else
|
||||||
Cancel();
|
Cancel();
|
||||||
|
|
||||||
ao_plugin_close(*this);
|
ao_plugin_close(*output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -148,31 +149,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(*this);
|
return ao_plugin_delay(*output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FilteredAudioOutput::SendTag(const Tag &tag)
|
FilteredAudioOutput::SendTag(const Tag &tag)
|
||||||
{
|
{
|
||||||
ao_plugin_send_tag(*this, tag);
|
ao_plugin_send_tag(*output, 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(*this, data, size);
|
return ao_plugin_play(*output, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FilteredAudioOutput::Drain()
|
FilteredAudioOutput::Drain()
|
||||||
{
|
{
|
||||||
ao_plugin_drain(*this);
|
ao_plugin_drain(*output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FilteredAudioOutput::Cancel() noexcept
|
FilteredAudioOutput::Cancel() noexcept
|
||||||
{
|
{
|
||||||
ao_plugin_cancel(*this);
|
ao_plugin_cancel(*output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -185,7 +186,7 @@ bool
|
|||||||
FilteredAudioOutput::IteratePause() noexcept
|
FilteredAudioOutput::IteratePause() noexcept
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return ao_plugin_pause(*this);
|
return ao_plugin_pause(*output);
|
||||||
} 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());
|
||||||
|
@ -33,7 +33,7 @@ class Mixer;
|
|||||||
class MixerListener;
|
class MixerListener;
|
||||||
struct MusicChunk;
|
struct MusicChunk;
|
||||||
struct ConfigBlock;
|
struct ConfigBlock;
|
||||||
struct AudioOutputPlugin;
|
struct AudioOutput;
|
||||||
struct ReplayGainConfig;
|
struct ReplayGainConfig;
|
||||||
struct Tag;
|
struct Tag;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* The plugin which implements this output device.
|
* The plugin which implements this output device.
|
||||||
*/
|
*/
|
||||||
const AudioOutputPlugin &plugin;
|
AudioOutput *const output;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The #mixer object associated with this audio output device.
|
* The #mixer object associated with this audio output device.
|
||||||
@ -120,18 +120,11 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Throws #std::runtime_error on error.
|
* Throws #std::runtime_error on error.
|
||||||
*/
|
*/
|
||||||
FilteredAudioOutput(const AudioOutputPlugin &_plugin,
|
FilteredAudioOutput(AudioOutput &_output,
|
||||||
const ConfigBlock &block);
|
const ConfigBlock &block);
|
||||||
|
|
||||||
~FilteredAudioOutput();
|
~FilteredAudioOutput();
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugins shall call this method if they require an
|
|
||||||
* "audio_format" setting which evaluates
|
|
||||||
* AudioFormat::IsFullyDefined().
|
|
||||||
*/
|
|
||||||
void NeedFullyDefinedAudioFormat();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Configure(const ConfigBlock &block);
|
void Configure(const ConfigBlock &block);
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ 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
|
||||||
@ -43,5 +45,5 @@ FilteredAudioOutput::BeginDestroy() noexcept
|
|||||||
void
|
void
|
||||||
FilteredAudioOutput::FinishDestroy() noexcept
|
FilteredAudioOutput::FinishDestroy() noexcept
|
||||||
{
|
{
|
||||||
ao_plugin_finish(this);
|
ao_plugin_finish(output);
|
||||||
}
|
}
|
||||||
|
@ -50,25 +50,21 @@
|
|||||||
#define AUDIO_OUTPUT_FORMAT "format"
|
#define AUDIO_OUTPUT_FORMAT "format"
|
||||||
#define AUDIO_FILTERS "filters"
|
#define AUDIO_FILTERS "filters"
|
||||||
|
|
||||||
FilteredAudioOutput::FilteredAudioOutput(const AudioOutputPlugin &_plugin,
|
FilteredAudioOutput::FilteredAudioOutput(AudioOutput &_output,
|
||||||
const ConfigBlock &block)
|
const ConfigBlock &block)
|
||||||
:plugin(_plugin)
|
:output(&_output)
|
||||||
{
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
const auto &plugin = output->plugin;
|
||||||
assert(plugin.finish != nullptr);
|
assert(plugin.finish != nullptr);
|
||||||
assert(plugin.open != nullptr);
|
assert(plugin.open != nullptr);
|
||||||
assert(plugin.close != nullptr);
|
assert(plugin.close != nullptr);
|
||||||
assert(plugin.play != nullptr);
|
assert(plugin.play != nullptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
Configure(block);
|
Configure(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
FilteredAudioOutput::NeedFullyDefinedAudioFormat()
|
|
||||||
{
|
|
||||||
if (!config_audio_format.IsFullyDefined())
|
|
||||||
throw std::runtime_error("Need full audio format specification");
|
|
||||||
}
|
|
||||||
|
|
||||||
static const AudioOutputPlugin *
|
static const AudioOutputPlugin *
|
||||||
audio_output_detect()
|
audio_output_detect()
|
||||||
{
|
{
|
||||||
@ -176,7 +172,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, plugin.name);
|
name, output->plugin.name);
|
||||||
log_name = buffer;
|
log_name = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +207,9 @@ FilteredAudioOutput::Setup(EventLoop &event_loop,
|
|||||||
MixerListener &mixer_listener,
|
MixerListener &mixer_listener,
|
||||||
const ConfigBlock &block)
|
const ConfigBlock &block)
|
||||||
{
|
{
|
||||||
|
if (output->need_fully_defined_audio_format &&
|
||||||
|
!config_audio_format.IsFullyDefined())
|
||||||
|
throw std::runtime_error("Need full audio format specification");
|
||||||
|
|
||||||
/* create the replay_gain filter */
|
/* create the replay_gain filter */
|
||||||
|
|
||||||
@ -234,7 +233,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,
|
||||||
plugin.mixer_plugin,
|
output->plugin.mixer_plugin,
|
||||||
*prepared_filter,
|
*prepared_filter,
|
||||||
mixer_listener);
|
mixer_listener);
|
||||||
} catch (const std::runtime_error &e) {
|
} catch (const std::runtime_error &e) {
|
||||||
@ -295,13 +294,22 @@ audio_output_new(EventLoop &event_loop,
|
|||||||
auto *ao = ao_plugin_init(event_loop, *plugin, block);
|
auto *ao = ao_plugin_init(event_loop, *plugin, block);
|
||||||
assert(ao != nullptr);
|
assert(ao != nullptr);
|
||||||
|
|
||||||
|
FilteredAudioOutput *f;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ao->Setup(event_loop, replay_gain_config,
|
f = new FilteredAudioOutput(*ao, block);
|
||||||
mixer_listener, block);
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ao_plugin_finish(ao);
|
ao_plugin_finish(ao);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ao;
|
try {
|
||||||
|
f->Setup(event_loop, replay_gain_config,
|
||||||
|
mixer_listener, block);
|
||||||
|
} catch (...) {
|
||||||
|
delete f;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
49
src/output/Interface.hxx
Normal file
49
src/output/Interface.hxx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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_AUDIO_OUTPUT_INTERFACE_HXX
|
||||||
|
#define MPD_AUDIO_OUTPUT_INTERFACE_HXX
|
||||||
|
|
||||||
|
struct AudioOutputPlugin;
|
||||||
|
struct FilteredAudioOutput;
|
||||||
|
|
||||||
|
struct AudioOutput {
|
||||||
|
/**
|
||||||
|
* The plugin which implements this output device.
|
||||||
|
*/
|
||||||
|
const AudioOutputPlugin &plugin;
|
||||||
|
|
||||||
|
FilteredAudioOutput *parent;
|
||||||
|
|
||||||
|
bool need_fully_defined_audio_format = false;
|
||||||
|
|
||||||
|
AudioOutput(const AudioOutputPlugin &_plugin)
|
||||||
|
:plugin(_plugin) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugins shall call this method if they require an
|
||||||
|
* "audio_format" setting which evaluates
|
||||||
|
* AudioFormat::IsFullyDefined().
|
||||||
|
*/
|
||||||
|
void NeedFullyDefinedAudioFormat() {
|
||||||
|
need_fully_defined_audio_format = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -23,7 +23,7 @@
|
|||||||
// IWYU pragma: begin_exports
|
// IWYU pragma: begin_exports
|
||||||
|
|
||||||
#include "OutputPlugin.hxx"
|
#include "OutputPlugin.hxx"
|
||||||
#include "Filtered.hxx"
|
#include "Interface.hxx"
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "config/Block.hxx"
|
#include "config/Block.hxx"
|
||||||
|
@ -19,9 +19,11 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "OutputPlugin.hxx"
|
#include "OutputPlugin.hxx"
|
||||||
#include "Filtered.hxx"
|
#include "Interface.hxx"
|
||||||
|
|
||||||
FilteredAudioOutput *
|
#include <assert.h>
|
||||||
|
|
||||||
|
AudioOutput *
|
||||||
ao_plugin_init(EventLoop &event_loop,
|
ao_plugin_init(EventLoop &event_loop,
|
||||||
const AudioOutputPlugin &plugin,
|
const AudioOutputPlugin &plugin,
|
||||||
const ConfigBlock &block)
|
const ConfigBlock &block)
|
||||||
@ -32,39 +34,39 @@ ao_plugin_init(EventLoop &event_loop,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_finish(FilteredAudioOutput *ao) noexcept
|
ao_plugin_finish(AudioOutput *ao) noexcept
|
||||||
{
|
{
|
||||||
ao->plugin.finish(ao);
|
ao->plugin.finish(ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_enable(FilteredAudioOutput &ao)
|
ao_plugin_enable(AudioOutput &ao)
|
||||||
{
|
{
|
||||||
if (ao.plugin.enable != nullptr)
|
if (ao.plugin.enable != nullptr)
|
||||||
ao.plugin.enable(&ao);
|
ao.plugin.enable(&ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_disable(FilteredAudioOutput &ao) noexcept
|
ao_plugin_disable(AudioOutput &ao) noexcept
|
||||||
{
|
{
|
||||||
if (ao.plugin.disable != nullptr)
|
if (ao.plugin.disable != nullptr)
|
||||||
ao.plugin.disable(&ao);
|
ao.plugin.disable(&ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_open(FilteredAudioOutput &ao, AudioFormat &audio_format)
|
ao_plugin_open(AudioOutput &ao, AudioFormat &audio_format)
|
||||||
{
|
{
|
||||||
ao.plugin.open(&ao, audio_format);
|
ao.plugin.open(&ao, audio_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_close(FilteredAudioOutput &ao) noexcept
|
ao_plugin_close(AudioOutput &ao) noexcept
|
||||||
{
|
{
|
||||||
ao.plugin.close(&ao);
|
ao.plugin.close(&ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::steady_clock::duration
|
std::chrono::steady_clock::duration
|
||||||
ao_plugin_delay(FilteredAudioOutput &ao) noexcept
|
ao_plugin_delay(AudioOutput &ao) noexcept
|
||||||
{
|
{
|
||||||
return ao.plugin.delay != nullptr
|
return ao.plugin.delay != nullptr
|
||||||
? ao.plugin.delay(&ao)
|
? ao.plugin.delay(&ao)
|
||||||
@ -72,34 +74,34 @@ ao_plugin_delay(FilteredAudioOutput &ao) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_send_tag(FilteredAudioOutput &ao, const Tag &tag)
|
ao_plugin_send_tag(AudioOutput &ao, const Tag &tag)
|
||||||
{
|
{
|
||||||
if (ao.plugin.send_tag != nullptr)
|
if (ao.plugin.send_tag != nullptr)
|
||||||
ao.plugin.send_tag(&ao, tag);
|
ao.plugin.send_tag(&ao, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
ao_plugin_play(FilteredAudioOutput &ao, const void *chunk, size_t size)
|
ao_plugin_play(AudioOutput &ao, const void *chunk, size_t size)
|
||||||
{
|
{
|
||||||
return ao.plugin.play(&ao, chunk, size);
|
return ao.plugin.play(&ao, chunk, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_drain(FilteredAudioOutput &ao)
|
ao_plugin_drain(AudioOutput &ao)
|
||||||
{
|
{
|
||||||
if (ao.plugin.drain != nullptr)
|
if (ao.plugin.drain != nullptr)
|
||||||
ao.plugin.drain(&ao);
|
ao.plugin.drain(&ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_cancel(FilteredAudioOutput &ao) noexcept
|
ao_plugin_cancel(AudioOutput &ao) noexcept
|
||||||
{
|
{
|
||||||
if (ao.plugin.cancel != nullptr)
|
if (ao.plugin.cancel != nullptr)
|
||||||
ao.plugin.cancel(&ao);
|
ao.plugin.cancel(&ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ao_plugin_pause(FilteredAudioOutput &ao)
|
ao_plugin_pause(AudioOutput &ao)
|
||||||
{
|
{
|
||||||
return ao.plugin.pause != nullptr && ao.plugin.pause(&ao);
|
return ao.plugin.pause != nullptr && ao.plugin.pause(&ao);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
struct ConfigBlock;
|
struct ConfigBlock;
|
||||||
struct AudioFormat;
|
struct AudioFormat;
|
||||||
struct Tag;
|
struct Tag;
|
||||||
struct FilteredAudioOutput;
|
struct AudioOutput;
|
||||||
struct MixerPlugin;
|
struct MixerPlugin;
|
||||||
class EventLoop;
|
class EventLoop;
|
||||||
|
|
||||||
@ -57,12 +57,12 @@ struct AudioOutputPlugin {
|
|||||||
* @param param the configuration section, or nullptr if there is
|
* @param param the configuration section, or nullptr if there is
|
||||||
* no configuration
|
* no configuration
|
||||||
*/
|
*/
|
||||||
FilteredAudioOutput *(*init)(EventLoop &event_loop, const ConfigBlock &block);
|
AudioOutput *(*init)(EventLoop &event_loop, const ConfigBlock &block);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free resources allocated by this device.
|
* Free resources allocated by this device.
|
||||||
*/
|
*/
|
||||||
void (*finish)(FilteredAudioOutput *data);
|
void (*finish)(AudioOutput *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable the device. This may allocate resources, preparing
|
* Enable the device. This may allocate resources, preparing
|
||||||
@ -70,13 +70,13 @@ struct AudioOutputPlugin {
|
|||||||
*
|
*
|
||||||
* Throws #std::runtime_error on error.
|
* Throws #std::runtime_error on error.
|
||||||
*/
|
*/
|
||||||
void (*enable)(FilteredAudioOutput *data);
|
void (*enable)(AudioOutput *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables the device. It is closed before this method is
|
* Disables the device. It is closed before this method is
|
||||||
* called.
|
* called.
|
||||||
*/
|
*/
|
||||||
void (*disable)(FilteredAudioOutput *data);
|
void (*disable)(AudioOutput *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Really open the device.
|
* Really open the device.
|
||||||
@ -86,12 +86,12 @@ struct AudioOutputPlugin {
|
|||||||
* @param audio_format the audio format in which data is going
|
* @param audio_format the audio format in which data is going
|
||||||
* to be delivered; may be modified by the plugin
|
* to be delivered; may be modified by the plugin
|
||||||
*/
|
*/
|
||||||
void (*open)(FilteredAudioOutput *data, AudioFormat &audio_format);
|
void (*open)(AudioOutput *data, AudioFormat &audio_format);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the device.
|
* Close the device.
|
||||||
*/
|
*/
|
||||||
void (*close)(FilteredAudioOutput *data);
|
void (*close)(AudioOutput *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a positive number if the output thread shall further
|
* Returns a positive number if the output thread shall further
|
||||||
@ -102,13 +102,13 @@ struct AudioOutputPlugin {
|
|||||||
*
|
*
|
||||||
* @return the duration to wait
|
* @return the duration to wait
|
||||||
*/
|
*/
|
||||||
std::chrono::steady_clock::duration (*delay)(FilteredAudioOutput *data) noexcept;
|
std::chrono::steady_clock::duration (*delay)(AudioOutput *data) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display metadata for the next chunk. Optional method,
|
* Display metadata for the next chunk. Optional method,
|
||||||
* because not all devices can display metadata.
|
* because not all devices can display metadata.
|
||||||
*/
|
*/
|
||||||
void (*send_tag)(FilteredAudioOutput *data, const Tag &tag);
|
void (*send_tag)(AudioOutput *data, const Tag &tag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play a chunk of audio data.
|
* Play a chunk of audio data.
|
||||||
@ -117,19 +117,19 @@ struct AudioOutputPlugin {
|
|||||||
*
|
*
|
||||||
* @return the number of bytes played
|
* @return the number of bytes played
|
||||||
*/
|
*/
|
||||||
size_t (*play)(FilteredAudioOutput *data,
|
size_t (*play)(AudioOutput *data,
|
||||||
const void *chunk, size_t size);
|
const void *chunk, size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait until the device has finished playing.
|
* Wait until the device has finished playing.
|
||||||
*/
|
*/
|
||||||
void (*drain)(FilteredAudioOutput *data);
|
void (*drain)(AudioOutput *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to cancel data which may still be in the device's
|
* Try to cancel data which may still be in the device's
|
||||||
* buffers.
|
* buffers.
|
||||||
*/
|
*/
|
||||||
void (*cancel)(FilteredAudioOutput *data);
|
void (*cancel)(AudioOutput *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pause the device. If supported, it may perform a special
|
* Pause the device. If supported, it may perform a special
|
||||||
@ -142,7 +142,7 @@ struct AudioOutputPlugin {
|
|||||||
* @return false on error (output will be closed by caller),
|
* @return false on error (output will be closed by caller),
|
||||||
* true for continue to pause
|
* true for continue to pause
|
||||||
*/
|
*/
|
||||||
bool (*pause)(FilteredAudioOutput *data);
|
bool (*pause)(AudioOutput *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mixer plugin associated with this output plugin. This
|
* The mixer plugin associated with this output plugin. This
|
||||||
@ -162,43 +162,43 @@ ao_plugin_test_default_device(const AudioOutputPlugin *plugin)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gcc_malloc
|
gcc_malloc
|
||||||
FilteredAudioOutput *
|
AudioOutput *
|
||||||
ao_plugin_init(EventLoop &event_loop,
|
ao_plugin_init(EventLoop &event_loop,
|
||||||
const AudioOutputPlugin &plugin,
|
const AudioOutputPlugin &plugin,
|
||||||
const ConfigBlock &block);
|
const ConfigBlock &block);
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_finish(FilteredAudioOutput *ao) noexcept;
|
ao_plugin_finish(AudioOutput *ao) noexcept;
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_enable(FilteredAudioOutput &ao);
|
ao_plugin_enable(AudioOutput &ao);
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_disable(FilteredAudioOutput &ao) noexcept;
|
ao_plugin_disable(AudioOutput &ao) noexcept;
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_open(FilteredAudioOutput &ao, AudioFormat &audio_format);
|
ao_plugin_open(AudioOutput &ao, AudioFormat &audio_format);
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_close(FilteredAudioOutput &ao) noexcept;
|
ao_plugin_close(AudioOutput &ao) noexcept;
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
std::chrono::steady_clock::duration
|
std::chrono::steady_clock::duration
|
||||||
ao_plugin_delay(FilteredAudioOutput &ao) noexcept;
|
ao_plugin_delay(AudioOutput &ao) noexcept;
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_send_tag(FilteredAudioOutput &ao, const Tag &tag);
|
ao_plugin_send_tag(AudioOutput &ao, const Tag &tag);
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
ao_plugin_play(FilteredAudioOutput &ao, const void *chunk, size_t size);
|
ao_plugin_play(AudioOutput &ao, const void *chunk, size_t size);
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_drain(FilteredAudioOutput &ao);
|
ao_plugin_drain(AudioOutput &ao);
|
||||||
|
|
||||||
void
|
void
|
||||||
ao_plugin_cancel(FilteredAudioOutput &ao) noexcept;
|
ao_plugin_cancel(AudioOutput &ao) noexcept;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ao_plugin_pause(FilteredAudioOutput &ao);
|
ao_plugin_pause(AudioOutput &ao);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,78 +20,79 @@
|
|||||||
#ifndef MPD_OUTPUT_WRAPPER_HXX
|
#ifndef MPD_OUTPUT_WRAPPER_HXX
|
||||||
#define MPD_OUTPUT_WRAPPER_HXX
|
#define MPD_OUTPUT_WRAPPER_HXX
|
||||||
|
|
||||||
#include "Filtered.hxx"
|
#include "Interface.hxx"
|
||||||
#include "util/Cast.hxx"
|
#include "util/Cast.hxx"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
struct ConfigBlock;
|
struct ConfigBlock;
|
||||||
|
struct AudioFormat;
|
||||||
struct Tag;
|
struct Tag;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
struct AudioOutputWrapper {
|
struct AudioOutputWrapper {
|
||||||
static T &Cast(FilteredAudioOutput &ao) {
|
static T &Cast(AudioOutput &ao) {
|
||||||
return ContainerCast(ao, &T::base);
|
return ContainerCast(ao, &T::base);
|
||||||
}
|
}
|
||||||
|
|
||||||
static FilteredAudioOutput *Init(EventLoop &event_loop,
|
static AudioOutput *Init(EventLoop &event_loop,
|
||||||
const ConfigBlock &block) {
|
const ConfigBlock &block) {
|
||||||
T *t = T::Create(event_loop, block);
|
T *t = T::Create(event_loop, block);
|
||||||
return &t->base;
|
return &t->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Finish(FilteredAudioOutput *ao) {
|
static void Finish(AudioOutput *ao) {
|
||||||
T *t = &Cast(*ao);
|
T *t = &Cast(*ao);
|
||||||
delete t;
|
delete t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Enable(FilteredAudioOutput *ao) {
|
static void Enable(AudioOutput *ao) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
t.Enable();
|
t.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Disable(FilteredAudioOutput *ao) {
|
static void Disable(AudioOutput *ao) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
t.Disable();
|
t.Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Open(FilteredAudioOutput *ao, AudioFormat &audio_format) {
|
static void Open(AudioOutput *ao, AudioFormat &audio_format) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
t.Open(audio_format);
|
t.Open(audio_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Close(FilteredAudioOutput *ao) {
|
static void Close(AudioOutput *ao) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
t.Close();
|
t.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
static std::chrono::steady_clock::duration Delay(FilteredAudioOutput *ao) noexcept {
|
static std::chrono::steady_clock::duration Delay(AudioOutput *ao) noexcept {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
return t.Delay();
|
return t.Delay();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SendTag(FilteredAudioOutput *ao, const Tag &tag) {
|
static void SendTag(AudioOutput *ao, const Tag &tag) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
t.SendTag(tag);
|
t.SendTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t Play(FilteredAudioOutput *ao, const void *chunk, size_t size) {
|
static size_t Play(AudioOutput *ao, const void *chunk, size_t size) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
return t.Play(chunk, size);
|
return t.Play(chunk, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Drain(FilteredAudioOutput *ao) {
|
static void Drain(AudioOutput *ao) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
t.Drain();
|
t.Drain();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Cancel(FilteredAudioOutput *ao) {
|
static void Cancel(AudioOutput *ao) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
t.Cancel();
|
t.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Pause(FilteredAudioOutput *ao) {
|
static bool Pause(AudioOutput *ao) {
|
||||||
T &t = Cast(*ao);
|
T &t = Cast(*ao);
|
||||||
return t.Pause();
|
return t.Pause();
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ class AlsaOutput final
|
|||||||
|
|
||||||
friend struct AudioOutputWrapper<AlsaOutput>;
|
friend struct AudioOutputWrapper<AlsaOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
Manual<PcmExport> pcm_export;
|
Manual<PcmExport> pcm_export;
|
||||||
|
|
||||||
@ -414,7 +414,7 @@ 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),
|
:MultiSocketMonitor(loop), DeferredMonitor(loop),
|
||||||
base(alsa_output_plugin, block),
|
base(alsa_output_plugin),
|
||||||
device(block.GetBlockValue("device", "")),
|
device(block.GetBlockValue("device", "")),
|
||||||
#ifdef ENABLE_DSD
|
#ifdef ENABLE_DSD
|
||||||
dop(block.GetBlockValue("dop", false) ||
|
dop(block.GetBlockValue("dop", false) ||
|
||||||
|
@ -40,7 +40,7 @@ static unsigned ao_output_ref;
|
|||||||
class AoOutput {
|
class AoOutput {
|
||||||
friend struct AudioOutputWrapper<AoOutput>;
|
friend struct AudioOutputWrapper<AoOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const size_t write_size;
|
const size_t write_size;
|
||||||
int driver;
|
int driver;
|
||||||
@ -95,7 +95,7 @@ MakeAoError()
|
|||||||
}
|
}
|
||||||
|
|
||||||
AoOutput::AoOutput(const ConfigBlock &block)
|
AoOutput::AoOutput(const ConfigBlock &block)
|
||||||
:base(ao_output_plugin, block),
|
:base(ao_output_plugin),
|
||||||
write_size(block.GetBlockValue("write_size", 1024u))
|
write_size(block.GetBlockValue("write_size", 1024u))
|
||||||
{
|
{
|
||||||
if (ao_output_ref == 0) {
|
if (ao_output_ref == 0) {
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
class FifoOutput {
|
class FifoOutput {
|
||||||
friend struct AudioOutputWrapper<FifoOutput>;
|
friend struct AudioOutputWrapper<FifoOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const AllocatedPath path;
|
const AllocatedPath path;
|
||||||
std::string path_utf8;
|
std::string path_utf8;
|
||||||
@ -75,7 +75,7 @@ public:
|
|||||||
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, block),
|
:base(fifo_output_plugin),
|
||||||
path(block.GetPath("path"))
|
path(block.GetPath("path"))
|
||||||
{
|
{
|
||||||
if (path.IsNull())
|
if (path.IsNull())
|
||||||
|
@ -48,7 +48,7 @@ class 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);
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
size_t write_size;
|
size_t write_size;
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ class HaikuOutput {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
HaikuOutput(const ConfigBlock &block)
|
HaikuOutput(const ConfigBlock &block)
|
||||||
:base(haiku_output_plugin, block),
|
:base(haiku_output_plugin),
|
||||||
/* 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)) {}
|
||||||
|
|
||||||
|
@ -43,7 +43,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 {
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* libjack options passed to jack_client_open().
|
* libjack options passed to jack_client_open().
|
||||||
@ -162,7 +162,7 @@ parse_port_list(const char *source, std::string dest[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
JackOutput::JackOutput(const ConfigBlock &block)
|
JackOutput::JackOutput(const ConfigBlock &block)
|
||||||
:base(jack_output_plugin, block),
|
:base(jack_output_plugin),
|
||||||
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))
|
||||||
{
|
{
|
||||||
@ -443,7 +443,7 @@ JackOutput::Disable()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static FilteredAudioOutput *
|
static AudioOutput *
|
||||||
mpd_jack_init(EventLoop &, const ConfigBlock &block)
|
mpd_jack_init(EventLoop &, const ConfigBlock &block)
|
||||||
{
|
{
|
||||||
jack_set_error_function(mpd_jack_error);
|
jack_set_error_function(mpd_jack_error);
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
class NullOutput {
|
class NullOutput {
|
||||||
friend struct AudioOutputWrapper<NullOutput>;
|
friend struct AudioOutputWrapper<NullOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const bool sync;
|
const bool sync;
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ class NullOutput {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
NullOutput(const ConfigBlock &block)
|
NullOutput(const ConfigBlock &block)
|
||||||
:base(null_output_plugin, block),
|
:base(null_output_plugin),
|
||||||
sync(block.GetBlockValue("sync", true)) {}
|
sync(block.GetBlockValue("sync", true)) {}
|
||||||
|
|
||||||
static NullOutput *Create(EventLoop &event_loop,
|
static NullOutput *Create(EventLoop &event_loop,
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
struct OSXOutput {
|
struct OSXOutput {
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
/* configuration settings */
|
/* configuration settings */
|
||||||
OSType component_subtype;
|
OSType component_subtype;
|
||||||
@ -80,7 +80,7 @@ osx_output_test_default_device(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
OSXOutput::OSXOutput(const ConfigBlock &block)
|
OSXOutput::OSXOutput(const ConfigBlock &block)
|
||||||
:base(osx_output_plugin, block)
|
:base(osx_output_plugin)
|
||||||
{
|
{
|
||||||
const char *device = block.GetBlockValue("device");
|
const char *device = block.GetBlockValue("device");
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ OSXOutput::OSXOutput(const ConfigBlock &block)
|
|||||||
sync_sample_rate = block.GetBlockValue("sync_sample_rate", false);
|
sync_sample_rate = block.GetBlockValue("sync_sample_rate", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static FilteredAudioOutput *
|
static AudioOutput *
|
||||||
osx_output_init(EventLoop &, const ConfigBlock &block)
|
osx_output_init(EventLoop &, const ConfigBlock &block)
|
||||||
{
|
{
|
||||||
OSXOutput *oo = new OSXOutput(block);
|
OSXOutput *oo = new OSXOutput(block);
|
||||||
@ -128,7 +128,7 @@ osx_output_init(EventLoop &, const ConfigBlock &block)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
osx_output_finish(FilteredAudioOutput *ao)
|
osx_output_finish(AudioOutput *ao)
|
||||||
{
|
{
|
||||||
OSXOutput *oo = (OSXOutput *)ao;
|
OSXOutput *oo = (OSXOutput *)ao;
|
||||||
|
|
||||||
@ -514,7 +514,7 @@ osx_render(void *vdata,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
osx_output_enable(FilteredAudioOutput *ao)
|
osx_output_enable(AudioOutput *ao)
|
||||||
{
|
{
|
||||||
char errormsg[1024];
|
char errormsg[1024];
|
||||||
OSXOutput *oo = (OSXOutput *)ao;
|
OSXOutput *oo = (OSXOutput *)ao;
|
||||||
@ -550,7 +550,7 @@ osx_output_enable(FilteredAudioOutput *ao)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
osx_output_disable(FilteredAudioOutput *ao)
|
osx_output_disable(AudioOutput *ao)
|
||||||
{
|
{
|
||||||
OSXOutput *oo = (OSXOutput *)ao;
|
OSXOutput *oo = (OSXOutput *)ao;
|
||||||
|
|
||||||
@ -562,7 +562,7 @@ osx_output_disable(FilteredAudioOutput *ao)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
osx_output_close(FilteredAudioOutput *ao)
|
osx_output_close(AudioOutput *ao)
|
||||||
{
|
{
|
||||||
OSXOutput *od = (OSXOutput *)ao;
|
OSXOutput *od = (OSXOutput *)ao;
|
||||||
|
|
||||||
@ -573,7 +573,7 @@ osx_output_close(FilteredAudioOutput *ao)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
osx_output_open(FilteredAudioOutput *ao, AudioFormat &audio_format)
|
osx_output_open(AudioOutput *ao, AudioFormat &audio_format)
|
||||||
{
|
{
|
||||||
char errormsg[1024];
|
char errormsg[1024];
|
||||||
OSXOutput *od = (OSXOutput *)ao;
|
OSXOutput *od = (OSXOutput *)ao;
|
||||||
@ -663,14 +663,14 @@ osx_output_open(FilteredAudioOutput *ao, AudioFormat &audio_format)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
osx_output_play(FilteredAudioOutput *ao, const void *chunk, size_t size)
|
osx_output_play(AudioOutput *ao, const void *chunk, size_t size)
|
||||||
{
|
{
|
||||||
OSXOutput *od = (OSXOutput *)ao;
|
OSXOutput *od = (OSXOutput *)ao;
|
||||||
return od->ring_buffer->push((uint8_t *)chunk, size);
|
return od->ring_buffer->push((uint8_t *)chunk, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::chrono::steady_clock::duration
|
static std::chrono::steady_clock::duration
|
||||||
osx_output_delay(FilteredAudioOutput *ao) noexcept
|
osx_output_delay(AudioOutput *ao) noexcept
|
||||||
{
|
{
|
||||||
OSXOutput *od = (OSXOutput *)ao;
|
OSXOutput *od = (OSXOutput *)ao;
|
||||||
return od->ring_buffer->write_available()
|
return od->ring_buffer->write_available()
|
||||||
|
@ -39,7 +39,7 @@ class 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;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const char *device_name;
|
const char *device_name;
|
||||||
ALCdevice *device;
|
ALCdevice *device;
|
||||||
@ -138,7 +138,7 @@ OpenALOutput::SetupContext()
|
|||||||
}
|
}
|
||||||
|
|
||||||
OpenALOutput::OpenALOutput(const ConfigBlock &block)
|
OpenALOutput::OpenALOutput(const ConfigBlock &block)
|
||||||
:base(openal_output_plugin, block),
|
:base(openal_output_plugin),
|
||||||
device_name(block.GetBlockValue("device"))
|
device_name(block.GetBlockValue("device"))
|
||||||
{
|
{
|
||||||
if (device_name == nullptr)
|
if (device_name == nullptr)
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
class OssOutput {
|
class OssOutput {
|
||||||
friend struct AudioOutputWrapper<OssOutput>;
|
friend struct AudioOutputWrapper<OssOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
#ifdef AFMT_S24_PACKED
|
#ifdef AFMT_S24_PACKED
|
||||||
Manual<PcmExport> pcm_export;
|
Manual<PcmExport> pcm_export;
|
||||||
@ -85,8 +85,8 @@ class OssOutput {
|
|||||||
int oss_format;
|
int oss_format;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OssOutput(const ConfigBlock &block, const char *_device=nullptr)
|
explicit OssOutput(const char *_device=nullptr)
|
||||||
:base(oss_output_plugin, block),
|
:base(oss_output_plugin),
|
||||||
fd(-1), device(_device) {}
|
fd(-1), device(_device) {}
|
||||||
|
|
||||||
static OssOutput *Create(EventLoop &event_loop,
|
static OssOutput *Create(EventLoop &event_loop,
|
||||||
@ -192,11 +192,10 @@ oss_open_default()
|
|||||||
int err[ARRAY_SIZE(default_devices)];
|
int err[ARRAY_SIZE(default_devices)];
|
||||||
enum oss_stat ret[ARRAY_SIZE(default_devices)];
|
enum oss_stat ret[ARRAY_SIZE(default_devices)];
|
||||||
|
|
||||||
const ConfigBlock empty;
|
|
||||||
for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
|
for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
|
||||||
ret[i] = oss_stat_device(default_devices[i], &err[i]);
|
ret[i] = oss_stat_device(default_devices[i], &err[i]);
|
||||||
if (ret[i] == OSS_STAT_NO_ERROR)
|
if (ret[i] == OSS_STAT_NO_ERROR)
|
||||||
return new OssOutput(empty, default_devices[i]);
|
return new OssOutput(default_devices[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
|
for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
|
||||||
@ -231,7 +230,7 @@ OssOutput::Create(EventLoop &, const ConfigBlock &block)
|
|||||||
{
|
{
|
||||||
const char *device = block.GetBlockValue("device");
|
const char *device = block.GetBlockValue("device");
|
||||||
if (device != nullptr)
|
if (device != nullptr)
|
||||||
return new OssOutput(block, device);
|
return new OssOutput(device);
|
||||||
|
|
||||||
return oss_open_default();
|
return oss_open_default();
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
class PipeOutput {
|
class PipeOutput {
|
||||||
friend struct AudioOutputWrapper<PipeOutput>;
|
friend struct AudioOutputWrapper<PipeOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const std::string cmd;
|
const std::string cmd;
|
||||||
FILE *fh;
|
FILE *fh;
|
||||||
@ -52,7 +52,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
PipeOutput::PipeOutput(const ConfigBlock &block)
|
PipeOutput::PipeOutput(const ConfigBlock &block)
|
||||||
:base(pipe_output_plugin, block),
|
:base(pipe_output_plugin),
|
||||||
cmd(block.GetBlockValue("command", ""))
|
cmd(block.GetBlockValue("command", ""))
|
||||||
{
|
{
|
||||||
if (cmd.empty())
|
if (cmd.empty())
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
class PulseOutput {
|
class PulseOutput {
|
||||||
friend struct AudioOutputWrapper<PulseOutput>;
|
friend struct AudioOutputWrapper<PulseOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *server;
|
const char *server;
|
||||||
@ -179,7 +179,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
PulseOutput::PulseOutput(const ConfigBlock &block)
|
PulseOutput::PulseOutput(const ConfigBlock &block)
|
||||||
:base(pulse_output_plugin, block),
|
:base(pulse_output_plugin),
|
||||||
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"))
|
||||||
|
@ -45,7 +45,7 @@ static constexpr Domain recorder_domain("recorder");
|
|||||||
class RecorderOutput {
|
class RecorderOutput {
|
||||||
friend struct AudioOutputWrapper<RecorderOutput>;
|
friend struct AudioOutputWrapper<RecorderOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configured encoder plugin.
|
* The configured encoder plugin.
|
||||||
@ -114,7 +114,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
RecorderOutput::RecorderOutput(const ConfigBlock &block)
|
RecorderOutput::RecorderOutput(const ConfigBlock &block)
|
||||||
:base(recorder_output_plugin, block)
|
:base(recorder_output_plugin)
|
||||||
{
|
{
|
||||||
/* read configuration */
|
/* read configuration */
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
class RoarOutput {
|
class RoarOutput {
|
||||||
friend struct AudioOutputWrapper<RoarOutput>;
|
friend struct AudioOutputWrapper<RoarOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const std::string host, name;
|
const std::string host, name;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class RoarOutput {
|
|||||||
public:
|
public:
|
||||||
RoarOutput(const ConfigBlock &block);
|
RoarOutput(const ConfigBlock &block);
|
||||||
|
|
||||||
operator FilteredAudioOutput *() {
|
operator AudioOutput *() {
|
||||||
return &base;
|
return &base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ GetConfiguredRole(const ConfigBlock &block) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
RoarOutput::RoarOutput(const ConfigBlock &block)
|
RoarOutput::RoarOutput(const ConfigBlock &block)
|
||||||
:base(roar_output_plugin, block),
|
:base(roar_output_plugin),
|
||||||
host(block.GetBlockValue("server", "")),
|
host(block.GetBlockValue("server", "")),
|
||||||
name(block.GetBlockValue("name", "MPD")),
|
name(block.GetBlockValue("name", "MPD")),
|
||||||
role(GetConfiguredRole(block))
|
role(GetConfiguredRole(block))
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
static constexpr unsigned DEFAULT_CONN_TIMEOUT = 2;
|
static constexpr unsigned DEFAULT_CONN_TIMEOUT = 2;
|
||||||
|
|
||||||
struct ShoutOutput final {
|
struct ShoutOutput final {
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
shout_t *shout_conn;
|
shout_t *shout_conn;
|
||||||
shout_metadata_t *shout_meta;
|
shout_metadata_t *shout_meta;
|
||||||
@ -113,7 +113,7 @@ ShoutSetAudioInfo(shout_t *shout_conn, const AudioFormat &audio_format)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShoutOutput::ShoutOutput(const ConfigBlock &block)
|
ShoutOutput::ShoutOutput(const ConfigBlock &block)
|
||||||
:base(shout_output_plugin, block),
|
:base(shout_output_plugin),
|
||||||
shout_conn(shout_new()),
|
shout_conn(shout_new()),
|
||||||
shout_meta(shout_metadata_new())
|
shout_meta(shout_metadata_new())
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,7 @@ static constexpr Domain sndio_output_domain("sndio_output");
|
|||||||
|
|
||||||
class SndioOutput {
|
class SndioOutput {
|
||||||
friend struct AudioOutputWrapper<SndioOutput>;
|
friend struct AudioOutputWrapper<SndioOutput>;
|
||||||
FilteredAudioOutput base;
|
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;
|
||||||
@ -65,7 +65,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
SndioOutput::SndioOutput(const ConfigBlock &block)
|
SndioOutput::SndioOutput(const ConfigBlock &block)
|
||||||
:base(sndio_output_plugin, block),
|
:base(sndio_output_plugin),
|
||||||
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))
|
||||||
|
@ -53,7 +53,7 @@ struct audio_info {
|
|||||||
class SolarisOutput {
|
class SolarisOutput {
|
||||||
friend struct AudioOutputWrapper<SolarisOutput>;
|
friend struct AudioOutputWrapper<SolarisOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
/* configuration */
|
/* configuration */
|
||||||
const char *const device;
|
const char *const device;
|
||||||
@ -61,7 +61,7 @@ class SolarisOutput {
|
|||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
explicit SolarisOutput(const ConfigBlock &block)
|
explicit SolarisOutput(const ConfigBlock &block)
|
||||||
:base(solaris_output_plugin, block),
|
:base(solaris_output_plugin),
|
||||||
device(block.GetBlockValue("device", "/dev/audio")) {}
|
device(block.GetBlockValue("device", "/dev/audio")) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -42,7 +42,7 @@ struct WinmmBuffer {
|
|||||||
class WinmmOutput {
|
class WinmmOutput {
|
||||||
friend struct AudioOutputWrapper<WinmmOutput>;
|
friend struct AudioOutputWrapper<WinmmOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
const UINT device_id;
|
const UINT device_id;
|
||||||
HWAVEOUT handle;
|
HWAVEOUT handle;
|
||||||
@ -148,7 +148,7 @@ get_device_id(const char *device_name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
WinmmOutput::WinmmOutput(const ConfigBlock &block)
|
WinmmOutput::WinmmOutput(const ConfigBlock &block)
|
||||||
:base(winmm_output_plugin, block),
|
:base(winmm_output_plugin),
|
||||||
device_id(get_device_id(block.GetBlockValue("device")))
|
device_id(get_device_id(block.GetBlockValue("device")))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ struct Tag;
|
|||||||
class HttpdOutput final : ServerSocket, DeferredMonitor {
|
class HttpdOutput final : ServerSocket, DeferredMonitor {
|
||||||
friend struct AudioOutputWrapper<HttpdOutput>;
|
friend struct AudioOutputWrapper<HttpdOutput>;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the audio output is open and accepts client
|
* True if the audio output is open and accepts client
|
||||||
@ -160,7 +160,7 @@ public:
|
|||||||
static HttpdOutput *Create(EventLoop &event_loop,
|
static HttpdOutput *Create(EventLoop &event_loop,
|
||||||
const ConfigBlock &block);
|
const ConfigBlock &block);
|
||||||
|
|
||||||
static constexpr HttpdOutput *Cast(FilteredAudioOutput *ao) {
|
static constexpr HttpdOutput *Cast(AudioOutput *ao) {
|
||||||
return &ContainerCast(*ao, &HttpdOutput::base);
|
return &ContainerCast(*ao, &HttpdOutput::base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ 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),
|
:ServerSocket(_loop), DeferredMonitor(_loop),
|
||||||
base(httpd_output_plugin, block),
|
base(httpd_output_plugin),
|
||||||
encoder(nullptr), unflushed_input(0),
|
encoder(nullptr), unflushed_input(0),
|
||||||
metadata(nullptr)
|
metadata(nullptr)
|
||||||
{
|
{
|
||||||
|
@ -43,7 +43,7 @@ class 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;
|
||||||
|
|
||||||
FilteredAudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
SLES::Object engine_object, mix_object, play_object;
|
SLES::Object engine_object, mix_object, play_object;
|
||||||
SLES::Play play;
|
SLES::Play play;
|
||||||
@ -87,9 +87,9 @@ class SlesOutput {
|
|||||||
uint8_t buffers[N_BUFFERS][BUFFER_SIZE];
|
uint8_t buffers[N_BUFFERS][BUFFER_SIZE];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SlesOutput(const ConfigBlock &block);
|
SlesOutput();
|
||||||
|
|
||||||
operator FilteredAudioOutput *() {
|
operator AudioOutput *() {
|
||||||
return &base;
|
return &base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,8 +129,8 @@ private:
|
|||||||
|
|
||||||
static constexpr Domain sles_domain("sles");
|
static constexpr Domain sles_domain("sles");
|
||||||
|
|
||||||
SlesOutput::SlesOutput(const ConfigBlock &block)
|
SlesOutput::SlesOutput()
|
||||||
:base(sles_output_plugin, block)
|
:base(sles_output_plugin)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,9 +416,9 @@ sles_test_default_device()
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline SlesOutput *
|
inline SlesOutput *
|
||||||
SlesOutput::Create(EventLoop &, const ConfigBlock &block)
|
SlesOutput::Create(EventLoop &, const ConfigBlock &)
|
||||||
{
|
{
|
||||||
return new SlesOutput(block);
|
return new SlesOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef AudioOutputWrapper<SlesOutput> Wrapper;
|
typedef AudioOutputWrapper<SlesOutput> Wrapper;
|
||||||
|
@ -18,19 +18,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "output/Filtered.hxx"
|
#include "output/Interface.hxx"
|
||||||
|
#include "output/Registry.hxx"
|
||||||
#include "output/OutputPlugin.hxx"
|
#include "output/OutputPlugin.hxx"
|
||||||
#include "config/Param.hxx"
|
#include "config/Param.hxx"
|
||||||
#include "config/ConfigGlobal.hxx"
|
#include "config/ConfigGlobal.hxx"
|
||||||
#include "config/ConfigOption.hxx"
|
#include "config/ConfigOption.hxx"
|
||||||
#include "Idle.hxx"
|
#include "config/Block.hxx"
|
||||||
#include "Main.hxx"
|
|
||||||
#include "event/Thread.hxx"
|
#include "event/Thread.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
#include "AudioParser.hxx"
|
#include "AudioParser.hxx"
|
||||||
#include "ReplayGainConfig.hxx"
|
|
||||||
#include "pcm/PcmConvert.hxx"
|
#include "pcm/PcmConvert.hxx"
|
||||||
#include "filter/FilterRegistry.hxx"
|
|
||||||
#include "util/StringBuffer.hxx"
|
#include "util/StringBuffer.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
@ -42,28 +40,29 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
const FilterPlugin *
|
static AudioOutput *
|
||||||
filter_plugin_by_name(gcc_unused const char *name) noexcept
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FilteredAudioOutput *
|
|
||||||
load_audio_output(EventLoop &event_loop, const char *name)
|
load_audio_output(EventLoop &event_loop, const char *name)
|
||||||
{
|
{
|
||||||
const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT,
|
const auto *block = config_find_block(ConfigBlockOption::AUDIO_OUTPUT,
|
||||||
"name", name);
|
"name", name);
|
||||||
if (param == NULL)
|
if (block == nullptr)
|
||||||
throw FormatRuntimeError("No such configured audio output: %s\n",
|
throw FormatRuntimeError("No such configured audio output: %s",
|
||||||
name);
|
name);
|
||||||
|
|
||||||
return audio_output_new(event_loop, ReplayGainConfig(), *param,
|
const char *plugin_name = block->GetBlockValue("type");
|
||||||
*(MixerListener *)nullptr);
|
if (plugin_name == nullptr)
|
||||||
|
throw std::runtime_error("Missing \"type\" configuration");
|
||||||
|
|
||||||
|
const auto *plugin = AudioOutputPlugin_get(plugin_name);
|
||||||
|
if (plugin == nullptr)
|
||||||
|
throw FormatRuntimeError("No such audio output plugin: %s",
|
||||||
|
plugin_name);
|
||||||
|
|
||||||
|
return ao_plugin_init(event_loop, *plugin, *block);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
run_output(FilteredAudioOutput &ao, AudioFormat audio_format)
|
run_output(AudioOutput &ao, AudioFormat audio_format)
|
||||||
{
|
{
|
||||||
/* open the audio output */
|
/* open the audio output */
|
||||||
|
|
||||||
@ -140,8 +139,7 @@ try {
|
|||||||
|
|
||||||
/* cleanup and exit */
|
/* cleanup and exit */
|
||||||
|
|
||||||
ao->BeginDestroy();
|
ao_plugin_finish(ao);
|
||||||
ao->FinishDestroy();
|
|
||||||
|
|
||||||
config_global_finish();
|
config_global_finish();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user