output/Internal: move thread-specific stuff to AudioOutputControl
The AudioOutput struct (which is exposed to all plugins) should not be aware that it's being controlled by another thread.
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
AudioOutput::CommandFinished()
|
||||
AudioOutputControl::CommandFinished()
|
||||
{
|
||||
assert(command != Command::NONE);
|
||||
command = Command::NONE;
|
||||
@@ -98,11 +98,9 @@ AudioOutput::CloseFilter()
|
||||
}
|
||||
|
||||
inline void
|
||||
AudioOutput::Open()
|
||||
AudioOutput::Open(AudioFormat audio_format, const MusicPipe &pipe)
|
||||
{
|
||||
assert(request.audio_format.IsValid());
|
||||
|
||||
fail_timer.Reset();
|
||||
assert(audio_format.IsValid());
|
||||
|
||||
/* enable the device (just in case the last enable has failed) */
|
||||
Enable();
|
||||
@@ -110,7 +108,7 @@ AudioOutput::Open()
|
||||
AudioFormat f;
|
||||
|
||||
try {
|
||||
f = source.Open(request.audio_format, *request.pipe,
|
||||
f = source.Open(audio_format, pipe,
|
||||
prepared_replay_gain_filter,
|
||||
prepared_other_replay_gain_filter,
|
||||
prepared_filter);
|
||||
@@ -239,10 +237,10 @@ AudioOutput::CloseOutput(bool drain)
|
||||
* was issued
|
||||
*/
|
||||
inline bool
|
||||
AudioOutput::WaitForDelay()
|
||||
AudioOutputControl::WaitForDelay()
|
||||
{
|
||||
while (true) {
|
||||
const auto delay = ao_plugin_delay(*this);
|
||||
const auto delay = ao_plugin_delay(*output);
|
||||
if (delay <= std::chrono::steady_clock::duration::zero())
|
||||
return true;
|
||||
|
||||
@@ -254,14 +252,14 @@ AudioOutput::WaitForDelay()
|
||||
}
|
||||
|
||||
bool
|
||||
AudioOutput::FillSourceOrClose()
|
||||
AudioOutputControl::FillSourceOrClose()
|
||||
try {
|
||||
return source.Fill(mutex);
|
||||
return output->source.Fill(mutex);
|
||||
} catch (const std::runtime_error &e) {
|
||||
FormatError(e, "Failed to filter for output \"%s\" [%s]",
|
||||
name, plugin.name);
|
||||
GetName(), output->plugin.name);
|
||||
|
||||
Close(false);
|
||||
output->Close(false);
|
||||
|
||||
/* don't automatically reopen this device for 10
|
||||
seconds */
|
||||
@@ -270,23 +268,23 @@ try {
|
||||
}
|
||||
|
||||
inline bool
|
||||
AudioOutput::PlayChunk()
|
||||
AudioOutputControl::PlayChunk()
|
||||
{
|
||||
if (tags) {
|
||||
const auto *tag = source.ReadTag();
|
||||
if (output->tags) {
|
||||
const auto *tag = output->source.ReadTag();
|
||||
if (tag != nullptr) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
try {
|
||||
ao_plugin_send_tag(*this, *tag);
|
||||
ao_plugin_send_tag(*output, *tag);
|
||||
} catch (const std::runtime_error &e) {
|
||||
FormatError(e, "Failed to send tag to \"%s\" [%s]",
|
||||
name, plugin.name);
|
||||
GetName(), output->plugin.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (command == Command::NONE) {
|
||||
const auto data = source.PeekData();
|
||||
const auto data = output->source.PeekData();
|
||||
if (data.IsEmpty())
|
||||
break;
|
||||
|
||||
@@ -297,17 +295,17 @@ AudioOutput::PlayChunk()
|
||||
|
||||
try {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
nbytes = ao_plugin_play(*this, data.data, data.size);
|
||||
nbytes = ao_plugin_play(*output, data.data, data.size);
|
||||
assert(nbytes <= data.size);
|
||||
} catch (const std::runtime_error &e) {
|
||||
FormatError(e, "\"%s\" [%s] failed to play",
|
||||
name, plugin.name);
|
||||
GetName(), output->plugin.name);
|
||||
|
||||
nbytes = 0;
|
||||
}
|
||||
|
||||
if (nbytes == 0) {
|
||||
Close(false);
|
||||
output->Close(false);
|
||||
|
||||
/* don't automatically reopen this device for
|
||||
10 seconds */
|
||||
@@ -317,16 +315,16 @@ AudioOutput::PlayChunk()
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(nbytes % out_audio_format.GetFrameSize() == 0);
|
||||
assert(nbytes % output->out_audio_format.GetFrameSize() == 0);
|
||||
|
||||
source.ConsumeData(nbytes);
|
||||
output->source.ConsumeData(nbytes);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
AudioOutput::Play()
|
||||
AudioOutputControl::Play()
|
||||
{
|
||||
if (!FillSourceOrClose())
|
||||
/* no chunk available */
|
||||
@@ -351,7 +349,7 @@ AudioOutput::Play()
|
||||
give it a chance to refill the pipe before
|
||||
it runs empty */
|
||||
const ScopeUnlock unlock(mutex);
|
||||
client->ChunksConsumed();
|
||||
output->client->ChunksConsumed();
|
||||
n = 0;
|
||||
}
|
||||
|
||||
@@ -360,7 +358,7 @@ AudioOutput::Play()
|
||||
} while (FillSourceOrClose());
|
||||
|
||||
const ScopeUnlock unlock(mutex);
|
||||
client->ChunksConsumed();
|
||||
output->client->ChunksConsumed();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -397,26 +395,26 @@ AudioOutput::IteratePause()
|
||||
}
|
||||
|
||||
inline void
|
||||
AudioOutput::Pause()
|
||||
AudioOutputControl::Pause()
|
||||
{
|
||||
BeginPause();
|
||||
output->BeginPause();
|
||||
CommandFinished();
|
||||
|
||||
do {
|
||||
if (!WaitForDelay())
|
||||
break;
|
||||
|
||||
if (!IteratePause())
|
||||
if (!output->IteratePause())
|
||||
break;
|
||||
} while (command == Command::NONE);
|
||||
|
||||
pause = false;
|
||||
output->EndPause();
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::Task()
|
||||
AudioOutputControl::Task()
|
||||
{
|
||||
FormatThreadName("output:%s", name);
|
||||
FormatThreadName("output:%s", GetName());
|
||||
|
||||
try {
|
||||
SetThreadRealtime();
|
||||
@@ -438,7 +436,7 @@ AudioOutput::Task()
|
||||
last_error = nullptr;
|
||||
|
||||
try {
|
||||
Enable();
|
||||
output->Enable();
|
||||
} catch (const std::runtime_error &e) {
|
||||
LogError(e);
|
||||
fail_timer.Update();
|
||||
@@ -449,15 +447,17 @@ AudioOutput::Task()
|
||||
break;
|
||||
|
||||
case Command::DISABLE:
|
||||
Disable();
|
||||
output->Disable();
|
||||
CommandFinished();
|
||||
break;
|
||||
|
||||
case Command::OPEN:
|
||||
last_error = nullptr;
|
||||
fail_timer.Reset();
|
||||
|
||||
try {
|
||||
Open();
|
||||
output->Open(request.audio_format,
|
||||
*request.pipe);
|
||||
} catch (const std::runtime_error &e) {
|
||||
LogError(e);
|
||||
fail_timer.Update();
|
||||
@@ -468,13 +468,13 @@ AudioOutput::Task()
|
||||
break;
|
||||
|
||||
case Command::CLOSE:
|
||||
if (open)
|
||||
Close(false);
|
||||
if (IsOpen())
|
||||
output->Close(false);
|
||||
CommandFinished();
|
||||
break;
|
||||
|
||||
case Command::PAUSE:
|
||||
if (!open) {
|
||||
if (!output->open) {
|
||||
/* the output has failed after
|
||||
audio_output_all_pause() has
|
||||
submitted the PAUSE command; bail
|
||||
@@ -491,46 +491,46 @@ AudioOutput::Task()
|
||||
continue;
|
||||
|
||||
case Command::DRAIN:
|
||||
if (open) {
|
||||
if (output->open) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
ao_plugin_drain(*this);
|
||||
ao_plugin_drain(*output);
|
||||
}
|
||||
|
||||
CommandFinished();
|
||||
continue;
|
||||
|
||||
case Command::CANCEL:
|
||||
source.Cancel();
|
||||
output->source.Cancel();
|
||||
|
||||
if (open) {
|
||||
if (output->open) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
ao_plugin_cancel(*this);
|
||||
ao_plugin_cancel(*output);
|
||||
}
|
||||
|
||||
CommandFinished();
|
||||
continue;
|
||||
|
||||
case Command::KILL:
|
||||
Disable();
|
||||
source.Cancel();
|
||||
output->Disable();
|
||||
output->source.Cancel();
|
||||
CommandFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
if (open && allow_play && Play())
|
||||
if (output->open && allow_play && Play())
|
||||
/* don't wait for an event if there are more
|
||||
chunks in the pipe */
|
||||
continue;
|
||||
|
||||
if (command == Command::NONE) {
|
||||
woken_for_play = false;
|
||||
cond.wait(mutex);
|
||||
cond.wait(output->mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::StartThread()
|
||||
AudioOutputControl::StartThread()
|
||||
{
|
||||
assert(command == Command::NONE);
|
||||
|
||||
|
Reference in New Issue
Block a user