From 2b782b82eaadeff36fa230ce3ac30893eb6581e3 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 24 Sep 2008 07:23:19 +0200 Subject: output: semi-asynchronous playback Send an output buffer to all output plugins at the same time, instead of waiting for each of them separately. Make several functions non-blocking, and introduce the new function audio_output_wait_all() to synchronize with all audio output threads. --- src/audio.c | 76 ++++++++++++++++++++++++++++++++++++++++++--------- src/output_control.c | 22 +++++++++------ src/output_control.h | 4 +-- src/output_internal.h | 12 ++++++++ 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/audio.c b/src/audio.c index f3de2285e..f027be588 100644 --- a/src/audio.c +++ b/src/audio.c @@ -213,6 +213,31 @@ int isCurrentAudioFormat(const struct audio_format *audioFormat) return audio_format_equals(audioFormat, &audio_buffer.format); } +static void audio_output_wait(struct audio_output *ao) +{ + while (!audio_output_command_is_finished(ao)) + notify_wait(&audio_output_client_notify); +} + +static void audio_output_wait_all(void) +{ + unsigned i; + + while (1) { + int finished = 1; + + for (i = 0; i < audioOutputArraySize; ++i) + if (audioDeviceStates[i] == DEVICE_ON && + !audio_output_command_is_finished(&audioOutputArray[i])) + finished = 0; + + if (finished) + break; + + notify_wait(&audio_output_client_notify); + }; +} + static void syncAudioDeviceStates(void) { struct audio_output *audioOutput; @@ -239,6 +264,7 @@ static void syncAudioDeviceStates(void) break; case DEVICE_DISABLE: audio_output_cancel(audioOutput); + audio_output_wait(audioOutput); audio_output_close(audioOutput); audioDeviceStates[i] = DEVICE_OFF; } @@ -255,19 +281,39 @@ static int flushAudioBuffer(void) syncAudioDeviceStates(); - for (i = 0; i < audioOutputArraySize; ++i) { - if (audioDeviceStates[i] != DEVICE_ON) - continue; - err = audio_output_play(&audioOutputArray[i], - audio_buffer.buffer, - audio_buffer.position); - if (!err) - ret = 0; - else if (err < 0) - /* device should already be closed if the play - * func returned an error */ - audioDeviceStates[i] = DEVICE_ENABLE; - } + for (i = 0; i < audioOutputArraySize; ++i) + if (audioDeviceStates[i] == DEVICE_ON) + audio_output_play(&audioOutputArray[i], + audio_buffer.buffer, + audio_buffer.position); + + while (1) { + int finished = 1; + + for (i = 0; i < audioOutputArraySize; ++i) { + const struct audio_output *ao = &audioOutputArray[i]; + + if (audioDeviceStates[i] != DEVICE_ON) + continue; + + if (audio_output_command_is_finished(ao)) { + err = audio_output_get_result(ao); + if (!err) + ret = 0; + else if (err < 0) + /* device should already be + closed if the play func + returned an error */ + audioDeviceStates[i] = DEVICE_ENABLE; + } else + finished = 0; + } + + if (finished) + break; + + notify_wait(&audio_output_client_notify); + }; audio_buffer.position = 0; @@ -370,6 +416,8 @@ void dropBufferedAudio(void) if (audioDeviceStates[i] == DEVICE_ON) audio_output_cancel(&audioOutputArray[i]); } + + audio_output_wait_all(); } void closeAudioDevice(void) @@ -399,6 +447,8 @@ void sendMetadataToAudioDevice(const struct tag *tag) for (i = 0; i < audioOutputArraySize; ++i) if (audioDeviceStates[i] == DEVICE_ON) audio_output_send_tag(&audioOutputArray[i], tag); + + audio_output_wait_all(); } int enableAudioDevice(unsigned int device) diff --git a/src/output_control.c b/src/output_control.c index 34e51aa41..3be1f6cd6 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -41,6 +41,14 @@ static void ao_command(struct audio_output *ao, enum audio_output_command cmd) ao_command_wait(ao); } +static void ao_command_async(struct audio_output *ao, + enum audio_output_command cmd) +{ + assert(ao->command == AO_COMMAND_NONE); + ao->command = cmd; + notify_signal(&ao->notify); +} + int audio_output_open(struct audio_output *audioOutput, const struct audio_format *audioFormat) { @@ -78,22 +86,20 @@ int audio_output_open(struct audio_output *audioOutput, return ret; } -int audio_output_play(struct audio_output *audioOutput, - const char *playChunk, size_t size) +void audio_output_play(struct audio_output *audioOutput, + const char *playChunk, size_t size) { if (!audioOutput->open) - return -1; + return; audioOutput->args.play.data = playChunk; audioOutput->args.play.size = size; - ao_command(audioOutput, AO_COMMAND_PLAY); - - return audioOutput->result; + ao_command_async(audioOutput, AO_COMMAND_PLAY); } void audio_output_cancel(struct audio_output *audioOutput) { - ao_command(audioOutput, AO_COMMAND_CANCEL); + ao_command_async(audioOutput, AO_COMMAND_CANCEL); } void audio_output_close(struct audio_output *audioOutput) @@ -120,5 +126,5 @@ void audio_output_send_tag(struct audio_output *audioOutput, return; audioOutput->args.tag = tag; - ao_command(audioOutput, AO_COMMAND_SEND_TAG); + ao_command_async(audioOutput, AO_COMMAND_SEND_TAG); } diff --git a/src/output_control.h b/src/output_control.h index e16229e21..92973f6c1 100644 --- a/src/output_control.h +++ b/src/output_control.h @@ -30,8 +30,8 @@ struct tag; int audio_output_init(struct audio_output *, ConfigParam * param); int audio_output_open(struct audio_output *audioOutput, const struct audio_format *audioFormat); -int audio_output_play(struct audio_output *audioOutput, - const char *playChunk, size_t size); +void audio_output_play(struct audio_output *audioOutput, + const char *playChunk, size_t size); void audio_output_cancel(struct audio_output *audioOutput); void audio_output_close(struct audio_output *audioOutput); void audio_output_finish(struct audio_output *audioOutput); diff --git a/src/output_internal.h b/src/output_internal.h index 5c11a9521..2455a549a 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -109,4 +109,16 @@ struct audio_output { */ extern struct notify audio_output_client_notify; +static inline int +audio_output_command_is_finished(const struct audio_output *ao) +{ + return ao->command == AO_COMMAND_NONE; +} + +static inline int +audio_output_get_result(const struct audio_output *ao) +{ + return ao->result; +} + #endif -- cgit v1.2.3