diff options
Diffstat (limited to 'src/output_control.c')
-rw-r--r-- | src/output_control.c | 152 |
1 files changed, 122 insertions, 30 deletions
diff --git a/src/output_control.c b/src/output_control.c index 16c0dbb75..795d04a8c 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -23,6 +23,8 @@ #include "output_thread.h" #include "mixer_control.h" #include "mixer_plugin.h" +#include "filter_plugin.h" +#include "notify.h" #include <assert.h> #include <stdlib.h> @@ -38,8 +40,9 @@ struct notify audio_output_client_notify; static void ao_command_wait(struct audio_output *ao) { while (ao->command != AO_COMMAND_NONE) { - notify_signal(&ao->notify); + g_mutex_unlock(ao->mutex); notify_wait(&audio_output_client_notify); + g_mutex_lock(ao->mutex); } } @@ -47,6 +50,7 @@ static void ao_command(struct audio_output *ao, enum audio_output_command cmd) { assert(ao->command == AO_COMMAND_NONE); ao->command = cmd; + g_cond_signal(ao->cond); ao_command_wait(ao); } @@ -55,7 +59,46 @@ static void ao_command_async(struct audio_output *ao, { assert(ao->command == AO_COMMAND_NONE); ao->command = cmd; - notify_signal(&ao->notify); + g_cond_signal(ao->cond); +} + +void +audio_output_enable(struct audio_output *ao) +{ + if (ao->thread == NULL) { + if (ao->plugin->enable == NULL) { + /* 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; + return; + } + + audio_output_thread_start(ao); + } + + g_mutex_lock(ao->mutex); + ao_command(ao, AO_COMMAND_ENABLE); + g_mutex_unlock(ao->mutex); +} + +void +audio_output_disable(struct audio_output *ao) +{ + if (ao->thread == NULL) { + if (ao->plugin->disable == NULL) + ao->really_enabled = false; + else + /* if there's no thread yet, the device cannot + be enabled */ + assert(!ao->really_enabled); + + return; + } + + g_mutex_lock(ao->mutex); + ao_command(ao, AO_COMMAND_DISABLE); + g_mutex_unlock(ao->mutex); } static bool @@ -85,6 +128,10 @@ audio_output_open(struct audio_output *ao, /* we're not using audio_output_cancel() here, because that function is asynchronous */ ao_command(ao, AO_COMMAND_CANCEL); + + /* the audio output is now waiting for a + signal; wake it up immediately */ + g_cond_signal(ao->cond); } return true; @@ -93,33 +140,47 @@ audio_output_open(struct audio_output *ao, ao->in_audio_format = *audio_format; ao->chunk = NULL; - if (!ao->config_audio_format) { - if (ao->open) - audio_output_close(ao); - - /* no audio format is configured: copy in->out, let - the output's open() method determine the effective - out_audio_format */ - ao->out_audio_format = ao->in_audio_format; - } - ao->pipe = mp; if (ao->thread == NULL) audio_output_thread_start(ao); + ao_command(ao, ao->open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN); open = ao->open; - if (!open) { - ao_command(ao, AO_COMMAND_OPEN); - open = ao->open; - } - if (open && ao->mixer != NULL) - mixer_open(ao->mixer); + if (open && ao->mixer != NULL) { + GError *error = NULL; + + if (!mixer_open(ao->mixer, &error)) { + g_warning("Failed to open mixer for '%s': %s", + ao->name, error->message); + g_error_free(error); + } + } return open; } +/** + * Same as audio_output_close(), but expects the lock to be held by + * the caller. + */ +static void +audio_output_close_locked(struct audio_output *ao) +{ + if (ao->mixer != NULL) + mixer_auto_close(ao->mixer); + + assert(!ao->open || ao->fail_timer == NULL); + + if (ao->open) + ao_command(ao, AO_COMMAND_CLOSE); + else if (ao->fail_timer != NULL) { + g_timer_destroy(ao->fail_timer); + ao->fail_timer = NULL; + } +} + bool audio_output_update(struct audio_output *ao, const struct audio_format *audio_format, @@ -127,23 +188,31 @@ audio_output_update(struct audio_output *ao, { assert(mp != NULL); - if (ao->enabled) { + g_mutex_lock(ao->mutex); + + if (ao->enabled && ao->really_enabled) { if (ao->fail_timer == NULL || - g_timer_elapsed(ao->fail_timer, NULL) > REOPEN_AFTER) - return audio_output_open(ao, audio_format, mp); + g_timer_elapsed(ao->fail_timer, NULL) > REOPEN_AFTER) { + bool success = audio_output_open(ao, audio_format, mp); + g_mutex_unlock(ao->mutex); + return success; + } } else if (audio_output_is_open(ao)) - audio_output_close(ao); + audio_output_close_locked(ao); + g_mutex_unlock(ao->mutex); return false; } void audio_output_play(struct audio_output *ao) { - if (!ao->open) - return; + g_mutex_lock(ao->mutex); + + if (audio_output_is_open(ao)) + g_cond_signal(ao->cond); - notify_signal(&ao->notify); + g_mutex_unlock(ao->mutex); } void audio_output_pause(struct audio_output *ao) @@ -154,27 +223,46 @@ void audio_output_pause(struct audio_output *ao) mixer_auto_close()) */ mixer_auto_close(ao->mixer); - ao_command_async(ao, AO_COMMAND_PAUSE); + g_mutex_lock(ao->mutex); + if (audio_output_is_open(ao)) + ao_command_async(ao, AO_COMMAND_PAUSE); + g_mutex_unlock(ao->mutex); +} + +void +audio_output_drain_async(struct audio_output *ao) +{ + g_mutex_lock(ao->mutex); + if (audio_output_is_open(ao)) + ao_command_async(ao, AO_COMMAND_DRAIN); + g_mutex_unlock(ao->mutex); } void audio_output_cancel(struct audio_output *ao) { - ao_command_async(ao, AO_COMMAND_CANCEL); + g_mutex_lock(ao->mutex); + if (audio_output_is_open(ao)) + ao_command_async(ao, AO_COMMAND_CANCEL); + g_mutex_unlock(ao->mutex); } void audio_output_close(struct audio_output *ao) { - assert(!ao->open || ao->fail_timer == NULL); - if (ao->mixer != NULL) mixer_auto_close(ao->mixer); + g_mutex_lock(ao->mutex); + + assert(!ao->open || ao->fail_timer == NULL); + if (ao->open) ao_command(ao, AO_COMMAND_CLOSE); else if (ao->fail_timer != NULL) { g_timer_destroy(ao->fail_timer); ao->fail_timer = NULL; } + + g_mutex_unlock(ao->mutex); } void audio_output_finish(struct audio_output *ao) @@ -184,7 +272,9 @@ void audio_output_finish(struct audio_output *ao) assert(ao->fail_timer == NULL); if (ao->thread != NULL) { + g_mutex_lock(ao->mutex); ao_command(ao, AO_COMMAND_KILL); + g_mutex_unlock(ao->mutex); g_thread_join(ao->thread); } @@ -193,6 +283,8 @@ void audio_output_finish(struct audio_output *ao) ao_plugin_finish(ao->plugin, ao->data); - notify_deinit(&ao->notify); + g_cond_free(ao->cond); g_mutex_free(ao->mutex); + + filter_free(ao->filter); } |