diff options
-rw-r--r-- | src/main.c | 4 | ||||
-rw-r--r-- | src/output/pulse_output_plugin.c | 2 | ||||
-rw-r--r-- | src/output_all.c | 19 | ||||
-rw-r--r-- | src/output_all.h | 7 | ||||
-rw-r--r-- | src/output_command.c | 9 | ||||
-rw-r--r-- | src/output_control.c | 37 | ||||
-rw-r--r-- | src/output_control.h | 12 | ||||
-rw-r--r-- | src/output_init.c | 1 | ||||
-rw-r--r-- | src/output_internal.h | 8 | ||||
-rw-r--r-- | src/output_plugin.h | 34 | ||||
-rw-r--r-- | src/output_thread.c | 50 | ||||
-rw-r--r-- | src/player_control.c | 6 | ||||
-rw-r--r-- | src/player_control.h | 9 | ||||
-rw-r--r-- | src/player_thread.c | 10 | ||||
-rw-r--r-- | src/playlist_state.c | 6 |
15 files changed, 213 insertions, 1 deletions
diff --git a/src/main.c b/src/main.c index 4b61ab39e..dfd5d49d8 100644 --- a/src/main.c +++ b/src/main.c @@ -374,6 +374,10 @@ int main(int argc, char *argv[]) config_global_check(); + /* enable all audio outputs (if not already done by + playlist_state_restore() */ + pc_update_audio(); + /* run the main loop */ g_main_loop_run(main_loop); diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c index 39c8222c5..88cdebfde 100644 --- a/src/output/pulse_output_plugin.c +++ b/src/output/pulse_output_plugin.c @@ -309,6 +309,8 @@ pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, pa_threaded_mainloop_unlock(po->mainloop); + po->stream = NULL; + return po; } diff --git a/src/output_all.c b/src/output_all.c index b7a42a6a3..a16be7386 100644 --- a/src/output_all.c +++ b/src/output_all.c @@ -153,6 +153,25 @@ audio_output_all_finish(void) notify_deinit(&audio_output_client_notify); } +void +audio_output_all_enable_disable(void) +{ + for (unsigned i = 0; i < num_audio_outputs; i++) { + struct audio_output *ao = &audio_outputs[i]; + bool enabled; + + g_mutex_lock(ao->mutex); + enabled = ao->really_enabled; + g_mutex_unlock(ao->mutex); + + if (ao->enabled != enabled) { + if (ao->enabled) + audio_output_enable(ao); + else + audio_output_disable(ao); + } + } +} /** * Determine if all (active) outputs have finished the current diff --git a/src/output_all.h b/src/output_all.h index e124d3150..2f5101f1b 100644 --- a/src/output_all.h +++ b/src/output_all.h @@ -66,6 +66,13 @@ struct audio_output * audio_output_find(const char *name); /** + * Checks the "enabled" flag of all audio outputs, and if one has + * changed, commit the change. + */ +void +audio_output_all_enable_disable(void); + +/** * Opens all audio outputs which are not disabled. * * @param audio_format the preferred audio format, or NULL to reuse diff --git a/src/output_command.c b/src/output_command.c index 9d948c8cc..b47890043 100644 --- a/src/output_command.c +++ b/src/output_command.c @@ -29,6 +29,7 @@ #include "output_internal.h" #include "output_plugin.h" #include "mixer_control.h" +#include "player_control.h" #include "idle.h" extern unsigned audio_output_state_version; @@ -42,10 +43,14 @@ audio_output_enable_index(unsigned idx) return false; ao = audio_output_get(idx); + if (ao->enabled) + return true; ao->enabled = true; idle_add(IDLE_OUTPUT); + pc_update_audio(); + ++audio_output_state_version; return true; @@ -61,6 +66,8 @@ audio_output_disable_index(unsigned idx) return false; ao = audio_output_get(idx); + if (!ao->enabled) + return true; ao->enabled = false; idle_add(IDLE_OUTPUT); @@ -71,6 +78,8 @@ audio_output_disable_index(unsigned idx) idle_add(IDLE_MIXER); } + pc_update_audio(); + ++audio_output_state_version; return true; diff --git a/src/output_control.c b/src/output_control.c index b833fb08d..6512cbe74 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -59,6 +59,41 @@ static void ao_command_async(struct audio_output *ao, notify_signal(&ao->notify); } +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); + } + + ao_command(ao, AO_COMMAND_ENABLE); +} + +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; + } + + ao_command(ao, AO_COMMAND_DISABLE); +} + static bool audio_output_open(struct audio_output *ao, const struct audio_format *audio_format, @@ -122,7 +157,7 @@ audio_output_update(struct audio_output *ao, { assert(mp != NULL); - if (ao->enabled) { + 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); diff --git a/src/output_control.h b/src/output_control.h index 72e3ed468..b2e48fa8d 100644 --- a/src/output_control.h +++ b/src/output_control.h @@ -41,6 +41,18 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, GError **error_r); /** + * Enables the device. + */ +void +audio_output_enable(struct audio_output *ao); + +/** + * Disables the device. + */ +void +audio_output_disable(struct audio_output *ao); + +/** * Opens or closes the device, depending on the "enabled" flag. * * @return true if the device is open diff --git a/src/output_init.c b/src/output_init.c index 745b63e30..5cb9ac92c 100644 --- a/src/output_init.c +++ b/src/output_init.c @@ -180,6 +180,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, ao->plugin = plugin; ao->enabled = config_get_block_bool(param, "enabled", true); + ao->really_enabled = false; ao->open = false; ao->pause = false; ao->fail_timer = NULL; diff --git a/src/output_internal.h b/src/output_internal.h index f27a10ec7..0c25348a0 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -27,6 +27,8 @@ enum audio_output_command { AO_COMMAND_NONE = 0, + AO_COMMAND_ENABLE, + AO_COMMAND_DISABLE, AO_COMMAND_OPEN, /** @@ -71,6 +73,12 @@ struct audio_output { bool enabled; /** + * Is this device actually enabled, i.e. the "enable" method + * has succeeded? + */ + bool really_enabled; + + /** * Is the device (already) open and functional? * * This attribute may only be modified by the output thread. diff --git a/src/output_plugin.h b/src/output_plugin.h index 13dba0d0b..3a9748d46 100644 --- a/src/output_plugin.h +++ b/src/output_plugin.h @@ -67,6 +67,24 @@ struct audio_output_plugin { void (*finish)(void *data); /** + * Enable the device. This may allocate resources, preparing + * for the device to be opened. Enabling a device cannot + * fail: if an error occurs during that, it should be reported + * by the open() method. + * + * @param error_r location to store the error occuring, or + * NULL to ignore errors + * @return true on success, false on error + */ + bool (*enable)(void *data, GError **error_r); + + /** + * Disables the device. It is closed before this method is + * called. + */ + void (*disable)(void *data); + + /** * Really open the device. * * @param audio_format the audio format in which data is going @@ -150,6 +168,22 @@ ao_plugin_finish(const struct audio_output_plugin *plugin, void *data) } static inline bool +ao_plugin_enable(const struct audio_output_plugin *plugin, void *data, + GError **error_r) +{ + return plugin->enable != NULL + ? plugin->enable(data, error_r) + : true; +} + +static inline void +ao_plugin_disable(const struct audio_output_plugin *plugin, void *data) +{ + if (plugin->disable != NULL) + plugin->disable(data); +} + +static inline bool ao_plugin_open(const struct audio_output_plugin *plugin, void *data, struct audio_format *audio_format, GError **error) diff --git a/src/output_thread.c b/src/output_thread.c index 9eb2478b0..4bae2f162 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -42,6 +42,40 @@ static void ao_command_finished(struct audio_output *ao) notify_signal(&audio_output_client_notify); } +static bool +ao_enable(struct audio_output *ao) +{ + GError *error = NULL; + + if (ao->really_enabled) + return true; + + if (!ao_plugin_enable(ao->plugin, ao->data, &error)) { + g_warning("Failed to enable \"%s\" [%s]: %s\n", + ao->name, ao->plugin->name, error->message); + g_error_free(error); + return false; + } + + ao->really_enabled = true; + return true; +} + +static void +ao_close(struct audio_output *ao); + +static void +ao_disable(struct audio_output *ao) +{ + if (ao->open) + ao_close(ao); + + if (ao->really_enabled) { + ao->really_enabled = false; + ao_plugin_disable(ao->plugin, ao->data); + } +} + static void ao_open(struct audio_output *ao) { @@ -54,6 +88,12 @@ ao_open(struct audio_output *ao) assert(ao->pipe != NULL); assert(ao->chunk == NULL); + /* enable the device (just in case the last enable has failed) */ + + if (!ao_enable(ao)) + /* still no luck */ + return; + /* open the filter */ filter_audio_format = filter_open(ao->filter, &ao->in_audio_format, @@ -321,6 +361,16 @@ static gpointer audio_output_task(gpointer arg) case AO_COMMAND_NONE: break; + case AO_COMMAND_ENABLE: + ao_enable(ao); + ao_command_finished(ao); + break; + + case AO_COMMAND_DISABLE: + ao_disable(ao); + ao_command_finished(ao); + break; + case AO_COMMAND_OPEN: ao_open(ao); ao_command_finished(ao); diff --git a/src/player_control.c b/src/player_control.c index 25a0320c5..23ae136ac 100644 --- a/src/player_control.c +++ b/src/player_control.c @@ -100,6 +100,12 @@ pc_stop(void) } void +pc_update_audio(void) +{ + player_command(PLAYER_COMMAND_UPDATE_AUDIO); +} + +void pc_kill(void) { assert(pc.thread != NULL); diff --git a/src/player_control.h b/src/player_control.h index e9a43e844..c0f1d4f07 100644 --- a/src/player_control.h +++ b/src/player_control.h @@ -39,6 +39,12 @@ enum player_command { PLAYER_COMMAND_SEEK, PLAYER_COMMAND_CLOSE_AUDIO, + /** + * At least one audio_output.enabled flag has been modified; + * commit those changes to the output threads. + */ + PLAYER_COMMAND_UPDATE_AUDIO, + /** player_control.next_song has been updated */ PLAYER_COMMAND_QUEUE, @@ -152,6 +158,9 @@ void pc_stop(void); void +pc_update_audio(void); + +void pc_enqueue_song(struct song *song); /** diff --git a/src/player_thread.c b/src/player_thread.c index f68f61a04..1794ad404 100644 --- a/src/player_thread.c +++ b/src/player_thread.c @@ -350,6 +350,11 @@ static void player_process_command(struct player *player) case PLAYER_COMMAND_CLOSE_AUDIO: break; + case PLAYER_COMMAND_UPDATE_AUDIO: + audio_output_all_enable_disable(); + player_command_finished(); + break; + case PLAYER_COMMAND_QUEUE: assert(pc.next_song != NULL); assert(!player->queued); @@ -805,6 +810,11 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg) break; + case PLAYER_COMMAND_UPDATE_AUDIO: + audio_output_all_enable_disable(); + player_command_finished(); + break; + case PLAYER_COMMAND_EXIT: dc_quit(); audio_output_all_close(); diff --git a/src/playlist_state.c b/src/playlist_state.c index d97fb1369..b0cf961f7 100644 --- a/src/playlist_state.c +++ b/src/playlist_state.c @@ -187,6 +187,12 @@ playlist_state_restore(const char *line, FILE *fp, struct playlist *playlist) if (!queue_valid_position(&playlist->queue, current)) current = 0; + /* enable all devices for the first time; this must be + called here, after the audio output states were + restored, before playback begins */ + if (state != PLAYER_STATE_STOP) + pc_update_audio(); + if (state == PLAYER_STATE_STOP /* && config_option */) playlist->current = current; else if (seek_time == 0) |