output: move functions into the AudioOutput struct
This commit is contained in:
parent
cb7366f472
commit
4657a3bd0f
@ -963,9 +963,9 @@ OUTPUT_API_SRC = \
|
||||
src/output/Internal.hxx \
|
||||
src/output/Registry.cxx src/output/Registry.hxx \
|
||||
src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx \
|
||||
src/output/OutputThread.cxx src/output/OutputThread.hxx \
|
||||
src/output/OutputThread.cxx \
|
||||
src/output/Domain.cxx src/output/Domain.hxx \
|
||||
src/output/OutputControl.cxx src/output/OutputControl.hxx \
|
||||
src/output/OutputControl.cxx \
|
||||
src/output/OutputState.cxx src/output/OutputState.hxx \
|
||||
src/output/OutputPrint.cxx src/output/OutputPrint.hxx \
|
||||
src/output/OutputCommand.cxx src/output/OutputCommand.hxx \
|
||||
|
@ -147,14 +147,11 @@ audio_output_load_mixer(AudioOutput *ao,
|
||||
}
|
||||
|
||||
bool
|
||||
ao_base_init(AudioOutput *ao,
|
||||
const config_param ¶m, Error &error)
|
||||
AudioOutput::Configure(const config_param ¶m, Error &error)
|
||||
{
|
||||
assert(ao != nullptr);
|
||||
|
||||
if (!param.IsNull()) {
|
||||
ao->name = param.GetBlockValue(AUDIO_OUTPUT_NAME);
|
||||
if (ao->name == nullptr) {
|
||||
name = param.GetBlockValue(AUDIO_OUTPUT_NAME);
|
||||
if (name == nullptr) {
|
||||
error.Set(config_domain,
|
||||
"Missing \"name\" configuration");
|
||||
return false;
|
||||
@ -163,26 +160,26 @@ ao_base_init(AudioOutput *ao,
|
||||
const char *p = param.GetBlockValue(AUDIO_OUTPUT_FORMAT);
|
||||
if (p != nullptr) {
|
||||
bool success =
|
||||
audio_format_parse(ao->config_audio_format,
|
||||
audio_format_parse(config_audio_format,
|
||||
p, true, error);
|
||||
if (!success)
|
||||
return false;
|
||||
} else
|
||||
ao->config_audio_format.Clear();
|
||||
config_audio_format.Clear();
|
||||
} else {
|
||||
ao->name = "default detected output";
|
||||
name = "default detected output";
|
||||
|
||||
ao->config_audio_format.Clear();
|
||||
config_audio_format.Clear();
|
||||
}
|
||||
|
||||
ao->tags = param.GetBlockValue("tags", true);
|
||||
ao->always_on = param.GetBlockValue("always_on", false);
|
||||
ao->enabled = param.GetBlockValue("enabled", true);
|
||||
tags = param.GetBlockValue("tags", true);
|
||||
always_on = param.GetBlockValue("always_on", false);
|
||||
enabled = param.GetBlockValue("enabled", true);
|
||||
|
||||
/* set up the filter chain */
|
||||
|
||||
ao->filter = filter_chain_new();
|
||||
assert(ao->filter != nullptr);
|
||||
filter = filter_chain_new();
|
||||
assert(filter != nullptr);
|
||||
|
||||
/* create the normalization filter (if configured) */
|
||||
|
||||
@ -192,12 +189,12 @@ ao_base_init(AudioOutput *ao,
|
||||
IgnoreError());
|
||||
assert(normalize_filter != nullptr);
|
||||
|
||||
filter_chain_append(*ao->filter, "normalize",
|
||||
filter_chain_append(*filter, "normalize",
|
||||
autoconvert_filter_new(normalize_filter));
|
||||
}
|
||||
|
||||
Error filter_error;
|
||||
filter_chain_parse(*ao->filter,
|
||||
filter_chain_parse(*filter,
|
||||
param.GetBlockValue(AUDIO_FILTERS, ""),
|
||||
filter_error);
|
||||
|
||||
@ -206,7 +203,7 @@ ao_base_init(AudioOutput *ao,
|
||||
if (filter_error.IsDefined())
|
||||
FormatError(filter_error,
|
||||
"Failed to initialize filter chain for '%s'",
|
||||
ao->name);
|
||||
name);
|
||||
|
||||
/* done */
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "AudioFormat.hxx"
|
||||
#include "pcm/PcmBuffer.hxx"
|
||||
#include "pcm/PcmDither.hxx"
|
||||
#include "ReplayGainInfo.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
#include "thread/Thread.hxx"
|
||||
@ -266,6 +267,106 @@ struct AudioOutput {
|
||||
|
||||
AudioOutput(const AudioOutputPlugin &_plugin);
|
||||
~AudioOutput();
|
||||
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
|
||||
void StartThread();
|
||||
void StopThread();
|
||||
|
||||
void Finish();
|
||||
|
||||
bool IsOpen() const {
|
||||
return open;
|
||||
}
|
||||
|
||||
bool IsCommandFinished() const {
|
||||
return command == AO_COMMAND_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for command completion.
|
||||
*
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
void WaitForCommand();
|
||||
|
||||
/**
|
||||
* Sends a command, but does not wait for completion.
|
||||
*
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
void CommandAsync(audio_output_command cmd);
|
||||
|
||||
/**
|
||||
* Sends a command to the #AudioOutput object and waits for
|
||||
* completion.
|
||||
*
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
void CommandWait(audio_output_command cmd);
|
||||
|
||||
/**
|
||||
* Lock the #AudioOutput object and execute the command
|
||||
* synchronously.
|
||||
*/
|
||||
void LockCommandWait(audio_output_command cmd);
|
||||
|
||||
/**
|
||||
* Enables the device.
|
||||
*/
|
||||
void LockEnableWait();
|
||||
|
||||
/**
|
||||
* Disables the device.
|
||||
*/
|
||||
void LockDisableWait();
|
||||
|
||||
void LockPauseAsync();
|
||||
|
||||
/**
|
||||
* Same LockCloseWait(), but expects the lock to be
|
||||
* held by the caller.
|
||||
*/
|
||||
void CloseWait();
|
||||
void LockCloseWait();
|
||||
|
||||
/**
|
||||
* Closes the audio output, but if the "always_on" flag is set, put it
|
||||
* into pause mode instead.
|
||||
*/
|
||||
void LockRelease();
|
||||
|
||||
void SetReplayGainMode(ReplayGainMode mode);
|
||||
|
||||
/**
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
bool Open(const AudioFormat audio_format, const MusicPipe &mp);
|
||||
|
||||
/**
|
||||
* Opens or closes the device, depending on the "enabled"
|
||||
* flag.
|
||||
*
|
||||
* @return true if the device is open
|
||||
*/
|
||||
bool LockUpdate(const AudioFormat audio_format,
|
||||
const MusicPipe &mp);
|
||||
|
||||
void LockPlay();
|
||||
|
||||
void LockDrainAsync();
|
||||
|
||||
/**
|
||||
* Clear the "allow_play" flag and send the "CANCEL" command
|
||||
* asynchronously. To finish the operation, the caller has to
|
||||
* call LockAllowPlay().
|
||||
*/
|
||||
void LockCancelAsync();
|
||||
|
||||
/**
|
||||
* Set the "allow_play" and signal the thread.
|
||||
*/
|
||||
void LockAllowPlay();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -274,27 +375,11 @@ struct AudioOutput {
|
||||
*/
|
||||
extern struct notify audio_output_client_notify;
|
||||
|
||||
static inline bool
|
||||
audio_output_is_open(const AudioOutput *ao)
|
||||
{
|
||||
return ao->open;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
audio_output_command_is_finished(const AudioOutput *ao)
|
||||
{
|
||||
return ao->command == AO_COMMAND_NONE;
|
||||
}
|
||||
|
||||
AudioOutput *
|
||||
audio_output_new(const config_param ¶m,
|
||||
PlayerControl &pc,
|
||||
Error &error);
|
||||
|
||||
bool
|
||||
ao_base_init(AudioOutput *ao,
|
||||
const config_param ¶m, Error &error);
|
||||
|
||||
void
|
||||
audio_output_free(AudioOutput *ao);
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "MultipleOutputs.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
#include "Internal.hxx"
|
||||
#include "OutputControl.hxx"
|
||||
#include "Domain.hxx"
|
||||
#include "MusicBuffer.hxx"
|
||||
#include "MusicPipe.hxx"
|
||||
@ -45,8 +44,8 @@ MultipleOutputs::MultipleOutputs()
|
||||
MultipleOutputs::~MultipleOutputs()
|
||||
{
|
||||
for (auto i : outputs) {
|
||||
audio_output_disable(i);
|
||||
audio_output_finish(i);
|
||||
i->LockDisableWait();
|
||||
i->Finish();
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,9 +110,9 @@ MultipleOutputs::EnableDisable()
|
||||
|
||||
if (ao->enabled != enabled) {
|
||||
if (ao->enabled)
|
||||
audio_output_enable(ao);
|
||||
ao->LockEnableWait();
|
||||
else
|
||||
audio_output_disable(ao);
|
||||
ao->LockDisableWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,8 +122,7 @@ MultipleOutputs::AllFinished() const
|
||||
{
|
||||
for (auto ao : outputs) {
|
||||
const ScopeLock protect(ao->mutex);
|
||||
if (audio_output_is_open(ao) &&
|
||||
!audio_output_command_is_finished(ao))
|
||||
if (ao->IsOpen() && !ao->IsCommandFinished())
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -142,7 +140,7 @@ void
|
||||
MultipleOutputs::AllowPlay()
|
||||
{
|
||||
for (auto ao : outputs)
|
||||
audio_output_allow_play(ao);
|
||||
ao->LockAllowPlay();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -169,7 +167,7 @@ MultipleOutputs::Update()
|
||||
return false;
|
||||
|
||||
for (auto ao : outputs)
|
||||
ret = audio_output_update(ao, input_audio_format, *pipe)
|
||||
ret = ao->LockUpdate(input_audio_format, *pipe)
|
||||
|| ret;
|
||||
|
||||
return ret;
|
||||
@ -179,7 +177,7 @@ void
|
||||
MultipleOutputs::SetReplayGainMode(ReplayGainMode mode)
|
||||
{
|
||||
for (auto ao : outputs)
|
||||
audio_output_set_replay_gain_mode(ao, mode);
|
||||
ao->SetReplayGainMode(mode);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -199,7 +197,7 @@ MultipleOutputs::Play(music_chunk *chunk, Error &error)
|
||||
pipe->Push(chunk);
|
||||
|
||||
for (auto ao : outputs)
|
||||
audio_output_play(ao);
|
||||
ao->LockPlay();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -387,7 +385,7 @@ MultipleOutputs::Pause()
|
||||
Update();
|
||||
|
||||
for (auto ao : outputs)
|
||||
audio_output_pause(ao);
|
||||
ao->LockPauseAsync();
|
||||
|
||||
WaitAll();
|
||||
}
|
||||
@ -396,7 +394,7 @@ void
|
||||
MultipleOutputs::Drain()
|
||||
{
|
||||
for (auto ao : outputs)
|
||||
audio_output_drain_async(ao);
|
||||
ao->LockDrainAsync();
|
||||
|
||||
WaitAll();
|
||||
}
|
||||
@ -407,7 +405,7 @@ MultipleOutputs::Cancel()
|
||||
/* send the cancel() command to all audio outputs */
|
||||
|
||||
for (auto ao : outputs)
|
||||
audio_output_cancel(ao);
|
||||
ao->LockCancelAsync();
|
||||
|
||||
WaitAll();
|
||||
|
||||
@ -430,7 +428,7 @@ void
|
||||
MultipleOutputs::Close()
|
||||
{
|
||||
for (auto ao : outputs)
|
||||
audio_output_close(ao);
|
||||
ao->LockCloseWait();
|
||||
|
||||
if (pipe != nullptr) {
|
||||
assert(buffer != nullptr);
|
||||
@ -451,7 +449,7 @@ void
|
||||
MultipleOutputs::Release()
|
||||
{
|
||||
for (auto ao : outputs)
|
||||
audio_output_release(ao);
|
||||
ao->LockRelease();
|
||||
|
||||
if (pipe != nullptr) {
|
||||
assert(buffer != nullptr);
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
@ -19,8 +18,6 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "OutputControl.hxx"
|
||||
#include "OutputThread.hxx"
|
||||
#include "Internal.hxx"
|
||||
#include "OutputPlugin.hxx"
|
||||
#include "Domain.hxx"
|
||||
@ -38,126 +35,97 @@ static constexpr unsigned REOPEN_AFTER = 10;
|
||||
|
||||
struct notify audio_output_client_notify;
|
||||
|
||||
/**
|
||||
* Waits for command completion.
|
||||
*
|
||||
* @param ao the #AudioOutput instance; must be locked
|
||||
*/
|
||||
static void ao_command_wait(AudioOutput *ao)
|
||||
void
|
||||
AudioOutput::WaitForCommand()
|
||||
{
|
||||
while (ao->command != AO_COMMAND_NONE) {
|
||||
ao->mutex.unlock();
|
||||
while (!IsCommandFinished()) {
|
||||
mutex.unlock();
|
||||
audio_output_client_notify.Wait();
|
||||
ao->mutex.lock();
|
||||
mutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a command to the #AudioOutput object, but does not wait for
|
||||
* completion.
|
||||
*
|
||||
* @param ao the #AudioOutput instance; must be locked
|
||||
*/
|
||||
static void ao_command_async(AudioOutput *ao,
|
||||
enum audio_output_command cmd)
|
||||
void
|
||||
AudioOutput::CommandAsync(audio_output_command cmd)
|
||||
{
|
||||
assert(ao->command == AO_COMMAND_NONE);
|
||||
ao->command = cmd;
|
||||
ao->cond.signal();
|
||||
}
|
||||
assert(IsCommandFinished());
|
||||
|
||||
/**
|
||||
* Sends a command to the #AudioOutput object and waits for
|
||||
* completion.
|
||||
*
|
||||
* @param ao the #AudioOutput instance; must be locked
|
||||
*/
|
||||
static void
|
||||
ao_command(AudioOutput *ao, enum audio_output_command cmd)
|
||||
{
|
||||
ao_command_async(ao, cmd);
|
||||
ao_command_wait(ao);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the #AudioOutput object and execute the command
|
||||
* synchronously.
|
||||
*/
|
||||
static void
|
||||
ao_lock_command(AudioOutput *ao, enum audio_output_command cmd)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
ao_command(ao, cmd);
|
||||
command = cmd;
|
||||
cond.signal();
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_set_replay_gain_mode(AudioOutput *ao,
|
||||
ReplayGainMode mode)
|
||||
AudioOutput::CommandWait(audio_output_command cmd)
|
||||
{
|
||||
if (ao->replay_gain_filter != nullptr)
|
||||
replay_gain_filter_set_mode(ao->replay_gain_filter, mode);
|
||||
if (ao->other_replay_gain_filter != nullptr)
|
||||
replay_gain_filter_set_mode(ao->other_replay_gain_filter, mode);
|
||||
CommandAsync(cmd);
|
||||
WaitForCommand();
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_enable(AudioOutput *ao)
|
||||
AudioOutput::LockCommandWait(audio_output_command cmd)
|
||||
{
|
||||
if (!ao->thread.IsDefined()) {
|
||||
if (ao->plugin.enable == nullptr) {
|
||||
const ScopeLock protect(mutex);
|
||||
CommandWait(cmd);
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::SetReplayGainMode(ReplayGainMode mode)
|
||||
{
|
||||
if (replay_gain_filter != nullptr)
|
||||
replay_gain_filter_set_mode(replay_gain_filter, mode);
|
||||
if (other_replay_gain_filter != nullptr)
|
||||
replay_gain_filter_set_mode(other_replay_gain_filter, mode);
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::LockEnableWait()
|
||||
{
|
||||
if (!thread.IsDefined()) {
|
||||
if (plugin.enable == nullptr) {
|
||||
/* don't bother to start the thread now if the
|
||||
device doesn't even have a enable() method;
|
||||
just assign the variable and we're done */
|
||||
ao->really_enabled = true;
|
||||
really_enabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
audio_output_thread_start(ao);
|
||||
StartThread();
|
||||
}
|
||||
|
||||
ao_lock_command(ao, AO_COMMAND_ENABLE);
|
||||
LockCommandWait(AO_COMMAND_ENABLE);
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_disable(AudioOutput *ao)
|
||||
AudioOutput::LockDisableWait()
|
||||
{
|
||||
if (!ao->thread.IsDefined()) {
|
||||
if (ao->plugin.disable == nullptr)
|
||||
ao->really_enabled = false;
|
||||
if (!thread.IsDefined()) {
|
||||
if (plugin.disable == nullptr)
|
||||
really_enabled = false;
|
||||
else
|
||||
/* if there's no thread yet, the device cannot
|
||||
be enabled */
|
||||
assert(!ao->really_enabled);
|
||||
assert(!really_enabled);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ao_lock_command(ao, AO_COMMAND_DISABLE);
|
||||
LockCommandWait(AO_COMMAND_DISABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object must be locked (and unlocked) by the caller.
|
||||
*/
|
||||
static bool
|
||||
audio_output_open(AudioOutput *ao,
|
||||
const AudioFormat audio_format,
|
||||
const MusicPipe &mp)
|
||||
inline bool
|
||||
AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
|
||||
{
|
||||
bool open;
|
||||
|
||||
assert(ao != nullptr);
|
||||
assert(ao->allow_play);
|
||||
assert(allow_play);
|
||||
assert(audio_format.IsValid());
|
||||
|
||||
ao->fail_timer.Reset();
|
||||
fail_timer.Reset();
|
||||
|
||||
if (ao->open && audio_format == ao->in_audio_format) {
|
||||
assert(ao->pipe == &mp ||
|
||||
(ao->always_on && ao->pause));
|
||||
if (open && audio_format == in_audio_format) {
|
||||
assert(pipe == &mp || (always_on && pause));
|
||||
|
||||
if (ao->pause) {
|
||||
ao->chunk = nullptr;
|
||||
ao->pipe = ∓
|
||||
if (pause) {
|
||||
chunk = nullptr;
|
||||
pipe = ∓
|
||||
|
||||
/* unpause with the CANCEL command; this is a
|
||||
hack, but suits well for forcing the thread
|
||||
@ -166,160 +134,162 @@ audio_output_open(AudioOutput *ao,
|
||||
|
||||
/* we're not using audio_output_cancel() here,
|
||||
because that function is asynchronous */
|
||||
ao_command(ao, AO_COMMAND_CANCEL);
|
||||
CommandWait(AO_COMMAND_CANCEL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ao->in_audio_format = audio_format;
|
||||
ao->chunk = nullptr;
|
||||
in_audio_format = audio_format;
|
||||
chunk = nullptr;
|
||||
|
||||
ao->pipe = ∓
|
||||
pipe = ∓
|
||||
|
||||
if (!ao->thread.IsDefined())
|
||||
audio_output_thread_start(ao);
|
||||
if (!thread.IsDefined())
|
||||
StartThread();
|
||||
|
||||
ao_command(ao, ao->open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN);
|
||||
open = ao->open;
|
||||
CommandWait(open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN);
|
||||
const bool open2 = open;
|
||||
|
||||
if (open && ao->mixer != nullptr) {
|
||||
if (open2 && mixer != nullptr) {
|
||||
Error error;
|
||||
if (!mixer_open(ao->mixer, error))
|
||||
if (!mixer_open(mixer, error))
|
||||
FormatWarning(output_domain,
|
||||
"Failed to open mixer for '%s'",
|
||||
ao->name);
|
||||
"Failed to open mixer for '%s'", name);
|
||||
}
|
||||
|
||||
return open;
|
||||
return open2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as audio_output_close(), but expects the lock to be held by
|
||||
* the caller.
|
||||
*/
|
||||
static void
|
||||
audio_output_close_locked(AudioOutput *ao)
|
||||
void
|
||||
AudioOutput::CloseWait()
|
||||
{
|
||||
assert(ao != nullptr);
|
||||
assert(ao->allow_play);
|
||||
assert(allow_play);
|
||||
|
||||
if (ao->mixer != nullptr)
|
||||
mixer_auto_close(ao->mixer);
|
||||
if (mixer != nullptr)
|
||||
mixer_auto_close(mixer);
|
||||
|
||||
assert(!ao->open || !ao->fail_timer.IsDefined());
|
||||
assert(!open || !fail_timer.IsDefined());
|
||||
|
||||
if (ao->open)
|
||||
ao_command(ao, AO_COMMAND_CLOSE);
|
||||
if (open)
|
||||
CommandWait(AO_COMMAND_CLOSE);
|
||||
else
|
||||
ao->fail_timer.Reset();
|
||||
fail_timer.Reset();
|
||||
}
|
||||
|
||||
bool
|
||||
audio_output_update(AudioOutput *ao,
|
||||
const AudioFormat audio_format,
|
||||
AudioOutput::LockUpdate(const AudioFormat audio_format,
|
||||
const MusicPipe &mp)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
if (ao->enabled && ao->really_enabled) {
|
||||
if (ao->fail_timer.Check(REOPEN_AFTER * 1000)) {
|
||||
return audio_output_open(ao, audio_format, mp);
|
||||
if (enabled && really_enabled) {
|
||||
if (fail_timer.Check(REOPEN_AFTER * 1000)) {
|
||||
return Open(audio_format, mp);
|
||||
}
|
||||
} else if (audio_output_is_open(ao))
|
||||
audio_output_close_locked(ao);
|
||||
} else if (IsOpen())
|
||||
CloseWait();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_play(AudioOutput *ao)
|
||||
AudioOutput::LockPlay()
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
assert(ao->allow_play);
|
||||
assert(allow_play);
|
||||
|
||||
if (audio_output_is_open(ao) && !ao->in_playback_loop &&
|
||||
!ao->woken_for_play) {
|
||||
ao->woken_for_play = true;
|
||||
ao->cond.signal();
|
||||
if (IsOpen() && !in_playback_loop && !woken_for_play) {
|
||||
woken_for_play = true;
|
||||
cond.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void audio_output_pause(AudioOutput *ao)
|
||||
void
|
||||
AudioOutput::LockPauseAsync()
|
||||
{
|
||||
if (ao->mixer != nullptr && ao->plugin.pause == nullptr)
|
||||
if (mixer != nullptr && plugin.pause == nullptr)
|
||||
/* the device has no pause mode: close the mixer,
|
||||
unless its "global" flag is set (checked by
|
||||
mixer_auto_close()) */
|
||||
mixer_auto_close(ao->mixer);
|
||||
mixer_auto_close(mixer);
|
||||
|
||||
const ScopeLock protect(ao->mutex);
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
assert(ao->allow_play);
|
||||
if (audio_output_is_open(ao))
|
||||
ao_command_async(ao, AO_COMMAND_PAUSE);
|
||||
assert(allow_play);
|
||||
if (IsOpen())
|
||||
CommandAsync(AO_COMMAND_PAUSE);
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_drain_async(AudioOutput *ao)
|
||||
AudioOutput::LockDrainAsync()
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
assert(ao->allow_play);
|
||||
if (audio_output_is_open(ao))
|
||||
ao_command_async(ao, AO_COMMAND_DRAIN);
|
||||
assert(allow_play);
|
||||
if (IsOpen())
|
||||
CommandAsync(AO_COMMAND_DRAIN);
|
||||
}
|
||||
|
||||
void audio_output_cancel(AudioOutput *ao)
|
||||
void
|
||||
AudioOutput::LockCancelAsync()
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
if (audio_output_is_open(ao)) {
|
||||
ao->allow_play = false;
|
||||
ao_command_async(ao, AO_COMMAND_CANCEL);
|
||||
if (IsOpen()) {
|
||||
allow_play = false;
|
||||
CommandAsync(AO_COMMAND_CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_allow_play(AudioOutput *ao)
|
||||
AudioOutput::LockAllowPlay()
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
ao->allow_play = true;
|
||||
if (audio_output_is_open(ao))
|
||||
ao->cond.signal();
|
||||
allow_play = true;
|
||||
if (IsOpen())
|
||||
cond.signal();
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_release(AudioOutput *ao)
|
||||
AudioOutput::LockRelease()
|
||||
{
|
||||
if (ao->always_on)
|
||||
audio_output_pause(ao);
|
||||
if (always_on)
|
||||
LockPauseAsync();
|
||||
else
|
||||
audio_output_close(ao);
|
||||
LockCloseWait();
|
||||
}
|
||||
|
||||
void audio_output_close(AudioOutput *ao)
|
||||
void
|
||||
AudioOutput::LockCloseWait()
|
||||
{
|
||||
assert(ao != nullptr);
|
||||
assert(!ao->open || !ao->fail_timer.IsDefined());
|
||||
assert(!open || !fail_timer.IsDefined());
|
||||
|
||||
const ScopeLock protect(ao->mutex);
|
||||
audio_output_close_locked(ao);
|
||||
const ScopeLock protect(mutex);
|
||||
CloseWait();
|
||||
}
|
||||
|
||||
void audio_output_finish(AudioOutput *ao)
|
||||
void
|
||||
AudioOutput::StopThread()
|
||||
{
|
||||
audio_output_close(ao);
|
||||
assert(thread.IsDefined());
|
||||
assert(allow_play);
|
||||
|
||||
assert(!ao->fail_timer.IsDefined());
|
||||
|
||||
if (ao->thread.IsDefined()) {
|
||||
assert(ao->allow_play);
|
||||
ao_lock_command(ao, AO_COMMAND_KILL);
|
||||
ao->thread.Join();
|
||||
}
|
||||
|
||||
audio_output_free(ao);
|
||||
LockCommandWait(AO_COMMAND_KILL);
|
||||
thread.Join();
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::Finish()
|
||||
{
|
||||
LockCloseWait();
|
||||
|
||||
assert(!fail_timer.IsDefined());
|
||||
|
||||
if (thread.IsDefined())
|
||||
StopThread();
|
||||
|
||||
audio_output_free(this);
|
||||
}
|
||||
|
@ -20,75 +20,6 @@
|
||||
#ifndef MPD_OUTPUT_CONTROL_HXX
|
||||
#define MPD_OUTPUT_CONTROL_HXX
|
||||
|
||||
#include "ReplayGainInfo.hxx"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct AudioOutput;
|
||||
struct AudioFormat;
|
||||
struct config_param;
|
||||
class MusicPipe;
|
||||
|
||||
void
|
||||
audio_output_set_replay_gain_mode(AudioOutput *ao,
|
||||
ReplayGainMode mode);
|
||||
|
||||
/**
|
||||
* Enables the device.
|
||||
*/
|
||||
void
|
||||
audio_output_enable(AudioOutput *ao);
|
||||
|
||||
/**
|
||||
* Disables the device.
|
||||
*/
|
||||
void
|
||||
audio_output_disable(AudioOutput *ao);
|
||||
|
||||
/**
|
||||
* Opens or closes the device, depending on the "enabled" flag.
|
||||
*
|
||||
* @return true if the device is open
|
||||
*/
|
||||
bool
|
||||
audio_output_update(AudioOutput *ao,
|
||||
AudioFormat audio_format,
|
||||
const MusicPipe &mp);
|
||||
|
||||
void
|
||||
audio_output_play(AudioOutput *ao);
|
||||
|
||||
void
|
||||
audio_output_pause(AudioOutput *ao);
|
||||
|
||||
void
|
||||
audio_output_drain_async(AudioOutput *ao);
|
||||
|
||||
/**
|
||||
* Clear the "allow_play" flag and send the "CANCEL" command
|
||||
* asynchronously. To finish the operation, the caller has to call
|
||||
* audio_output_allow_play().
|
||||
*/
|
||||
void
|
||||
audio_output_cancel(AudioOutput *ao);
|
||||
|
||||
/**
|
||||
* Set the "allow_play" and signal the thread.
|
||||
*/
|
||||
void
|
||||
audio_output_allow_play(AudioOutput *ao);
|
||||
|
||||
void
|
||||
audio_output_close(AudioOutput *ao);
|
||||
|
||||
/**
|
||||
* Closes the audio output, but if the "always_on" flag is set, put it
|
||||
* into pause mode instead.
|
||||
*/
|
||||
void
|
||||
audio_output_release(AudioOutput *ao);
|
||||
|
||||
void
|
||||
audio_output_finish(AudioOutput *ao);
|
||||
|
||||
#endif
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "OutputThread.hxx"
|
||||
#include "Internal.hxx"
|
||||
#include "OutputAPI.hxx"
|
||||
#include "Domain.hxx"
|
||||
@ -680,11 +679,12 @@ audio_output_task(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
void audio_output_thread_start(AudioOutput *ao)
|
||||
void
|
||||
AudioOutput::StartThread()
|
||||
{
|
||||
assert(ao->command == AO_COMMAND_NONE);
|
||||
assert(command == AO_COMMAND_NONE);
|
||||
|
||||
Error error;
|
||||
if (!ao->thread.Start(audio_output_task, ao, error))
|
||||
if (!thread.Start(audio_output_task, this, error))
|
||||
FatalError(error);
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 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_THREAD_HXX
|
||||
#define MPD_OUTPUT_THREAD_HXX
|
||||
|
||||
struct AudioOutput;
|
||||
|
||||
void
|
||||
audio_output_thread_start(AudioOutput *ao);
|
||||
|
||||
#endif
|
@ -125,7 +125,7 @@ struct AlsaOutput {
|
||||
}
|
||||
|
||||
bool Init(const config_param ¶m, Error &error) {
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -46,7 +46,7 @@ struct AoOutput {
|
||||
:base(ao_output_plugin) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error) {
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
|
@ -52,7 +52,7 @@ struct FifoOutput {
|
||||
created(false) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error) {
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
|
||||
bool Create(Error &error);
|
||||
|
@ -137,7 +137,7 @@ HttpdOutput::Configure(const config_param ¶m, Error &error)
|
||||
inline bool
|
||||
HttpdOutput::Init(const config_param ¶m, Error &error)
|
||||
{
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
|
||||
static AudioOutput *
|
||||
|
@ -83,7 +83,7 @@ struct JackOutput {
|
||||
:base(jack_output_plugin) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error_r) {
|
||||
return ao_base_init(&base, param, error_r);
|
||||
return base.Configure(param, error_r);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -33,7 +33,7 @@ struct NullOutput {
|
||||
:base(null_output_plugin) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error) {
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -84,7 +84,7 @@ static AudioOutput *
|
||||
osx_output_init(const config_param ¶m, Error &error)
|
||||
{
|
||||
OSXOutput *oo = new OSXOutput();
|
||||
if (!ao_base_init(&oo->base, param, error)) {
|
||||
if (!oo->base.Configure(param, error)) {
|
||||
delete oo;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ struct OpenALOutput {
|
||||
:base(openal_output_plugin) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error_r) {
|
||||
return ao_base_init(&base, param, error_r);
|
||||
return base.Configure(param, error_r);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,7 @@ struct OssOutput {
|
||||
fd(-1), device(nullptr) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error_r) {
|
||||
return ao_base_init(&base, param, error_r);
|
||||
return base.Configure(param, error_r);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -38,7 +38,7 @@ struct PipeOutput {
|
||||
:base(pipe_output_plugin) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error) {
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
|
@ -334,7 +334,7 @@ pulse_output_init(const config_param ¶m, Error &error)
|
||||
g_setenv("PULSE_PROP_media.role", "music", true);
|
||||
|
||||
po = new PulseOutput();
|
||||
if (!ao_base_init(&po->base, param, error)) {
|
||||
if (!po->base.Configure(param, error)) {
|
||||
delete po;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ struct RecorderOutput {
|
||||
:base(recorder_output_plugin) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error_r) {
|
||||
return ao_base_init(&base, param, error_r);
|
||||
return base.Configure(param, error_r);
|
||||
}
|
||||
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
|
@ -58,7 +58,7 @@ public:
|
||||
}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error) {
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
|
||||
void Configure(const config_param ¶m);
|
||||
|
@ -69,7 +69,7 @@ struct ShoutOutput final {
|
||||
}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error) {
|
||||
return ao_base_init(&base, param, error);
|
||||
return base.Configure(param, error);
|
||||
}
|
||||
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
|
@ -61,7 +61,7 @@ struct SolarisOutput {
|
||||
:base(solaris_output_plugin) {}
|
||||
|
||||
bool Initialize(const config_param ¶m, Error &error_r) {
|
||||
return ao_base_init(&base, param, error_r);
|
||||
return base.Configure(param, error_r);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ struct WinmmOutput {
|
||||
WinmmBuffer buffers[8];
|
||||
unsigned next_buffer;
|
||||
|
||||
WinmmBuffer()
|
||||
WinmmOutput()
|
||||
:base(winmm_output_plugin) {}
|
||||
};
|
||||
|
||||
@ -115,7 +115,7 @@ static AudioOutput *
|
||||
winmm_output_init(const config_param ¶m, Error &error)
|
||||
{
|
||||
WinmmOutput *wo = new WinmmOutput();
|
||||
if (!ao_base_init(&wo->base, param, error)) {
|
||||
if (!wo->base.Configure(param, error)) {
|
||||
delete wo;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "output/OutputControl.hxx"
|
||||
#include "output/Internal.hxx"
|
||||
#include "output/OutputPlugin.hxx"
|
||||
#include "config/ConfigData.hxx"
|
||||
|
Loading…
Reference in New Issue
Block a user