diff --git a/src/audio.c b/src/audio.c index bbc7fd0eb..b38c47d72 100644 --- a/src/audio.c +++ b/src/audio.c @@ -397,6 +397,19 @@ int playAudio(const char *playChunk, size_t size) return 0; } +void audio_output_pause_all(void) +{ + unsigned int i; + + syncAudioDeviceStates(); + + for (i = 0; i < audioOutputArraySize; ++i) + if (audio_output_is_open(&audioOutputArray[i])) + audio_output_pause(&audioOutputArray[i]); + + audio_output_wait_all(); +} + void dropBufferedAudio(void) { unsigned int i; diff --git a/src/audio.h b/src/audio.h index 4e34b0f61..3ffa2dc55 100644 --- a/src/audio.h +++ b/src/audio.h @@ -45,6 +45,8 @@ int openAudioDevice(const struct audio_format *audioFormat); int playAudio(const char *playChunk, size_t size); +void audio_output_pause_all(void); + void dropBufferedAudio(void); void closeAudioDevice(void); diff --git a/src/audioOutputs/audioOutput_shout.c b/src/audioOutputs/audioOutput_shout.c index b55c06cc9..6b59044d8 100644 --- a/src/audioOutputs/audioOutput_shout.c +++ b/src/audioOutputs/audioOutput_shout.c @@ -87,7 +87,7 @@ static void free_shout_data(struct shout_data *sd) } \ } -static void *my_shout_init_driver(mpd_unused struct audio_output *audio_output, +static void *my_shout_init_driver(struct audio_output *audio_output, const struct audio_format *audio_format, ConfigParam *param) { @@ -104,6 +104,7 @@ static void *my_shout_init_driver(mpd_unused struct audio_output *audio_output, int public; sd = new_shout_data(); + sd->audio_output = audio_output; if (shout_init_count == 0) shout_init(); @@ -516,6 +517,21 @@ static int my_shout_play(void *data, return 0; } +static void my_shout_pause(void *data) +{ + struct shout_data *sd = (struct shout_data *)data; + static const char silence[1020]; + int ret; + + /* play silence until the player thread sends us a command */ + + while (sd->opened && !audio_output_is_pending(sd->audio_output)) { + ret = my_shout_play(data, silence, sizeof(silence)); + if (ret != 0) + break; + } +} + static void my_shout_set_tag(void *data, const struct tag *tag) { @@ -539,6 +555,7 @@ const struct audio_output_plugin shoutPlugin = { .finish = my_shout_finish_driver, .open = my_shout_open_device, .play = my_shout_play, + .pause = my_shout_pause, .cancel = my_shout_drop_buffered_audio, .close = my_shout_close_device, .send_tag = my_shout_set_tag, diff --git a/src/audioOutputs/audioOutput_shout.h b/src/audioOutputs/audioOutput_shout.h index ddab852df..bd525fd6d 100644 --- a/src/audioOutputs/audioOutput_shout.h +++ b/src/audioOutputs/audioOutput_shout.h @@ -59,6 +59,8 @@ struct shout_buffer { }; struct shout_data { + struct audio_output *audio_output; + shout_t *shout_conn; shout_metadata_t *shout_meta; int shout_error; diff --git a/src/output_api.h b/src/output_api.h index 96acff775..1cad66e60 100644 --- a/src/output_api.h +++ b/src/output_api.h @@ -81,6 +81,16 @@ struct audio_output_plugin { */ int (*play)(void *data, const char *playChunk, size_t size); + /** + * Pause the device. If supported, it may perform a special + * action, which keeps the device open, but does not play + * anything. Output plugins like "shout" might want to play + * silence during pause, so their clients won't be + * disconnected. Plugins which do not support pausing will + * simply be closed, and have to be reopened when unpaused. + */ + void (*pause)(void *data); + /** * Try to cancel data which may still be in the device's * buffers. @@ -104,6 +114,7 @@ enum audio_output_command { AO_COMMAND_OPEN, AO_COMMAND_CLOSE, AO_COMMAND_PLAY, + AO_COMMAND_PAUSE, AO_COMMAND_CANCEL, AO_COMMAND_SEND_TAG, AO_COMMAND_KILL diff --git a/src/output_control.c b/src/output_control.c index ac9394bd7..4724aeeea 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -103,6 +103,11 @@ void audio_output_play(struct audio_output *audioOutput, ao_command_async(audioOutput, AO_COMMAND_PLAY); } +void audio_output_pause(struct audio_output *audioOutput) +{ + ao_command_async(audioOutput, AO_COMMAND_PAUSE); +} + void audio_output_cancel(struct audio_output *audioOutput) { ao_command_async(audioOutput, AO_COMMAND_CANCEL); diff --git a/src/output_control.h b/src/output_control.h index b83a67b2f..7a31bb68f 100644 --- a/src/output_control.h +++ b/src/output_control.h @@ -41,6 +41,9 @@ audio_output_signal(struct audio_output *ao); void audio_output_play(struct audio_output *audioOutput, const char *playChunk, size_t size); + +void audio_output_pause(struct audio_output *audioOutput); + 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_thread.c b/src/output_thread.c index 4bc5d8fcb..c2f4f6d3c 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -63,6 +63,20 @@ static void ao_play(struct audio_output *ao) ao_command_finished(ao); } +static void ao_pause(struct audio_output *ao) +{ + if (ao->plugin->pause != NULL) { + /* pause is supported */ + ao_command_finished(ao); + ao->plugin->pause(ao->data); + } else { + /* pause is not supported - simply close the device */ + ao->plugin->close(ao->data); + ao->open = 0; + ao_command_finished(ao); + } +} + static void *audio_output_task(void *arg) { struct audio_output *ao = arg; @@ -95,6 +109,10 @@ static void *audio_output_task(void *arg) ao_play(ao); break; + case AO_COMMAND_PAUSE: + ao_pause(ao); + break; + case AO_COMMAND_CANCEL: ao->plugin->cancel(ao->data); ao_command_finished(ao); diff --git a/src/player_thread.c b/src/player_thread.c index 171bd71c4..48715dd20 100644 --- a/src/player_thread.c +++ b/src/player_thread.c @@ -143,7 +143,7 @@ static void processDecodeInput(int *pause_r, unsigned int *bbp_r, *pause_r = 1; } else if (*pause_r) { dropBufferedAudio(); - closeAudioDevice(); + audio_output_pause_all(); } break;