diff options
Diffstat (limited to 'src/output_control.c')
-rw-r--r-- | src/output_control.c | 187 |
1 files changed, 131 insertions, 56 deletions
diff --git a/src/output_control.c b/src/output_control.c index ecd226821..0823b667b 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2009 The Music Player Daemon Project + * Copyright (C) 2003-2010 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,12 +17,15 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "config.h" #include "output_control.h" #include "output_api.h" #include "output_internal.h" #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 +41,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,29 +51,55 @@ 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); } -/** - * Like ao_command(), but assumes the object is locked by the caller. - */ -static void -ao_command_locked(struct audio_output *ao, enum audio_output_command cmd) +static void ao_command_async(struct audio_output *ao, + enum audio_output_command cmd) { assert(ao->command == AO_COMMAND_NONE); ao->command = cmd; + 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_unlock(ao->mutex); - ao_command_wait(ao); g_mutex_lock(ao->mutex); + ao_command(ao, AO_COMMAND_ENABLE); + g_mutex_unlock(ao->mutex); } -static void ao_command_async(struct audio_output *ao, - enum audio_output_command cmd) +void +audio_output_disable(struct audio_output *ao) { - assert(ao->command == AO_COMMAND_NONE); - ao->command = cmd; - notify_signal(&ao->notify); + 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 void @@ -85,6 +115,7 @@ audio_output_open(struct audio_output *ao, { bool open; + assert(audio_format_valid(audio_format)); assert(mp != NULL); if (ao->fail_timer != NULL) { @@ -94,9 +125,13 @@ audio_output_open(struct audio_output *ao, if (ao->open && audio_format_equals(audio_format, &ao->in_audio_format)) { - assert(ao->pipe == mp); + assert(ao->pipe == mp || + (ao->always_on && ao->pause)); if (ao->pause) { + ao->chunk = NULL; + ao->pipe = mp; + /* unpause with the CANCEL command; this is a hack, but suits well for forcing the thread to leave the ao_pause() thread, and we need @@ -104,7 +139,11 @@ audio_output_open(struct audio_output *ao, /* we're not using audio_output_cancel() here, because that function is asynchronous */ - ao_command_locked(ao, AO_COMMAND_CANCEL); + 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; @@ -113,33 +152,49 @@ 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_locked(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_locked(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) +{ + assert(ao != NULL); + + 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, @@ -149,28 +204,29 @@ audio_output_update(struct audio_output *ao, g_mutex_lock(ao->mutex); - if (ao->enabled) { + if (ao->enabled && ao->really_enabled) { if (ao->fail_timer == NULL || g_timer_elapsed(ao->fail_timer, NULL) > REOPEN_AFTER) { - bool ret = audio_output_open(ao, audio_format, mp); + bool success = audio_output_open(ao, audio_format, mp); g_mutex_unlock(ao->mutex); - return ret; + return success; } } else if (audio_output_is_open(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); - notify_signal(&ao->notify); + if (audio_output_is_open(ao)) + g_cond_signal(ao->cond); + + g_mutex_unlock(ao->mutex); } void audio_output_pause(struct audio_output *ao) @@ -181,29 +237,36 @@ 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_cancel(struct audio_output *ao) +void +audio_output_drain_async(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_DRAIN); + g_mutex_unlock(ao->mutex); } -static void -audio_output_close_locked(struct audio_output *ao) +void audio_output_cancel(struct audio_output *ao) { - assert(ao != NULL); - assert(!ao->open || ao->fail_timer == NULL); - - if (ao->mixer != NULL) - mixer_auto_close(ao->mixer); + g_mutex_lock(ao->mutex); + if (audio_output_is_open(ao)) + ao_command_async(ao, AO_COMMAND_CANCEL); + g_mutex_unlock(ao->mutex); +} - if (ao->open) - ao_command_locked(ao, AO_COMMAND_CLOSE); - else if (ao->fail_timer != NULL) { - g_timer_destroy(ao->fail_timer); - ao->fail_timer = NULL; - } +void +audio_output_release(struct audio_output *ao) +{ + if (ao->always_on) + audio_output_pause(ao); + else + audio_output_close(ao); } void audio_output_close(struct audio_output *ao) @@ -223,7 +286,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); } @@ -232,6 +297,16 @@ 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); + + if (ao->replay_gain_filter != NULL) + filter_free(ao->replay_gain_filter); + + if (ao->other_replay_gain_filter != NULL) + filter_free(ao->other_replay_gain_filter); + + filter_free(ao->filter); + + pcm_buffer_deinit(&ao->cross_fade_buffer); } |