summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.c4
-rw-r--r--src/output/pulse_output_plugin.c2
-rw-r--r--src/output_all.c19
-rw-r--r--src/output_all.h7
-rw-r--r--src/output_command.c9
-rw-r--r--src/output_control.c37
-rw-r--r--src/output_control.h12
-rw-r--r--src/output_init.c1
-rw-r--r--src/output_internal.h8
-rw-r--r--src/output_plugin.h34
-rw-r--r--src/output_thread.c50
-rw-r--r--src/player_control.c6
-rw-r--r--src/player_control.h9
-rw-r--r--src/player_thread.c10
-rw-r--r--src/playlist_state.c6
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)