aboutsummaryrefslogtreecommitdiffstats
path: root/src/output_all.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/output_all.c')
-rw-r--r--src/output_all.c590
1 files changed, 0 insertions, 590 deletions
diff --git a/src/output_all.c b/src/output_all.c
deleted file mode 100644
index f56cd04ee..000000000
--- a/src/output_all.c
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "output_all.h"
-#include "output_internal.h"
-#include "output_control.h"
-#include "chunk.h"
-#include "conf.h"
-#include "pipe.h"
-#include "buffer.h"
-#include "player_control.h"
-#include "mpd_error.h"
-#include "notify.h"
-
-#ifndef NDEBUG
-#include "chunk.h"
-#endif
-
-#include <assert.h>
-#include <string.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "output"
-
-static struct audio_format input_audio_format;
-
-static struct audio_output **audio_outputs;
-static unsigned int num_audio_outputs;
-
-/**
- * The #music_buffer object where consumed chunks are returned.
- */
-static struct music_buffer *g_music_buffer;
-
-/**
- * The #music_pipe object which feeds all audio outputs. It is filled
- * by audio_output_all_play().
- */
-static struct music_pipe *g_mp;
-
-/**
- * The "elapsed_time" stamp of the most recently finished chunk.
- */
-static float audio_output_all_elapsed_time = -1.0;
-
-unsigned int audio_output_count(void)
-{
- return num_audio_outputs;
-}
-
-struct audio_output *
-audio_output_get(unsigned i)
-{
- assert(i < num_audio_outputs);
-
- assert(audio_outputs[i] != NULL);
-
- return audio_outputs[i];
-}
-
-struct audio_output *
-audio_output_find(const char *name)
-{
- for (unsigned i = 0; i < num_audio_outputs; ++i) {
- struct audio_output *ao = audio_output_get(i);
-
- if (strcmp(ao->name, name) == 0)
- return ao;
- }
-
- /* name not found */
- return NULL;
-}
-
-static unsigned
-audio_output_config_count(void)
-{
- unsigned int nr = 0;
- const struct config_param *param = NULL;
-
- while ((param = config_get_next_param(CONF_AUDIO_OUTPUT, param)))
- nr++;
- if (!nr)
- nr = 1; /* we'll always have at least one device */
- return nr;
-}
-
-void
-audio_output_all_init(struct player_control *pc)
-{
- const struct config_param *param = NULL;
- unsigned int i;
- GError *error = NULL;
-
- notify_init(&audio_output_client_notify);
-
- num_audio_outputs = audio_output_config_count();
- audio_outputs = g_new(struct audio_output *, num_audio_outputs);
-
- for (i = 0; i < num_audio_outputs; i++)
- {
- unsigned int j;
-
- param = config_get_next_param(CONF_AUDIO_OUTPUT, param);
-
- /* only allow param to be NULL if there just one audioOutput */
- assert(param || (num_audio_outputs == 1));
-
- struct audio_output *output = audio_output_new(param, pc, &error);
- if (output == NULL) {
- if (param != NULL)
- MPD_ERROR("line %i: %s",
- param->line, error->message);
- else
- MPD_ERROR("%s", error->message);
- }
-
- audio_outputs[i] = output;
-
- /* require output names to be unique: */
- for (j = 0; j < i; j++) {
- if (!strcmp(output->name, audio_outputs[j]->name)) {
- MPD_ERROR("output devices with identical "
- "names: %s\n", output->name);
- }
- }
- }
-}
-
-void
-audio_output_all_finish(void)
-{
- unsigned int i;
-
- for (i = 0; i < num_audio_outputs; i++) {
- audio_output_disable(audio_outputs[i]);
- audio_output_finish(audio_outputs[i]);
- }
-
- g_free(audio_outputs);
- audio_outputs = NULL;
- num_audio_outputs = 0;
-
- 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
- * command.
- */
-static bool
-audio_output_all_finished(void)
-{
- for (unsigned i = 0; i < num_audio_outputs; ++i) {
- struct audio_output *ao = audio_outputs[i];
- bool not_finished;
-
- g_mutex_lock(ao->mutex);
- not_finished = audio_output_is_open(ao) &&
- !audio_output_command_is_finished(ao);
- g_mutex_unlock(ao->mutex);
-
- if (not_finished)
- return false;
- }
-
- return true;
-}
-
-static void audio_output_wait_all(void)
-{
- while (!audio_output_all_finished())
- notify_wait(&audio_output_client_notify);
-}
-
-/**
- * Signals all audio outputs which are open.
- */
-static void
-audio_output_allow_play_all(void)
-{
- for (unsigned i = 0; i < num_audio_outputs; ++i)
- audio_output_allow_play(audio_outputs[i]);
-}
-
-static void
-audio_output_reset_reopen(struct audio_output *ao)
-{
- g_mutex_lock(ao->mutex);
-
- if (!ao->open && ao->fail_timer != NULL) {
- g_timer_destroy(ao->fail_timer);
- ao->fail_timer = NULL;
- }
-
- g_mutex_unlock(ao->mutex);
-}
-
-/**
- * Resets the "reopen" flag on all audio devices. MPD should
- * immediately retry to open the device instead of waiting for the
- * timeout when the user wants to start playback.
- */
-static void
-audio_output_all_reset_reopen(void)
-{
- for (unsigned i = 0; i < num_audio_outputs; ++i) {
- struct audio_output *ao = audio_outputs[i];
-
- audio_output_reset_reopen(ao);
- }
-}
-
-/**
- * Opens all output devices which are enabled, but closed.
- *
- * @return true if there is at least open output device which is open
- */
-static bool
-audio_output_all_update(void)
-{
- unsigned int i;
- bool ret = false;
-
- if (!audio_format_defined(&input_audio_format))
- return false;
-
- for (i = 0; i < num_audio_outputs; ++i)
- ret = audio_output_update(audio_outputs[i],
- &input_audio_format, g_mp) || ret;
-
- return ret;
-}
-
-bool
-audio_output_all_play(struct music_chunk *chunk)
-{
- bool ret;
- unsigned int i;
-
- assert(g_music_buffer != NULL);
- assert(g_mp != NULL);
- assert(chunk != NULL);
- assert(music_chunk_check_format(chunk, &input_audio_format));
-
- ret = audio_output_all_update();
- if (!ret)
- return false;
-
- music_pipe_push(g_mp, chunk);
-
- for (i = 0; i < num_audio_outputs; ++i)
- audio_output_play(audio_outputs[i]);
-
- return true;
-}
-
-bool
-audio_output_all_open(const struct audio_format *audio_format,
- struct music_buffer *buffer)
-{
- bool ret = false, enabled = false;
- unsigned int i;
-
- assert(audio_format != NULL);
- assert(buffer != NULL);
- assert(g_music_buffer == NULL || g_music_buffer == buffer);
- assert((g_mp == NULL) == (g_music_buffer == NULL));
-
- g_music_buffer = buffer;
-
- /* the audio format must be the same as existing chunks in the
- pipe */
- assert(g_mp == NULL || music_pipe_check_format(g_mp, audio_format));
-
- if (g_mp == NULL)
- g_mp = music_pipe_new();
- else
- /* if the pipe hasn't been cleared, the the audio
- format must not have changed */
- assert(music_pipe_empty(g_mp) ||
- audio_format_equals(audio_format,
- &input_audio_format));
-
- input_audio_format = *audio_format;
-
- audio_output_all_reset_reopen();
- audio_output_all_enable_disable();
- audio_output_all_update();
-
- for (i = 0; i < num_audio_outputs; ++i) {
- if (audio_outputs[i]->enabled)
- enabled = true;
-
- if (audio_outputs[i]->open)
- ret = true;
- }
-
- if (!enabled)
- g_warning("All audio outputs are disabled");
-
- if (!ret)
- /* close all devices if there was an error */
- audio_output_all_close();
-
- return ret;
-}
-
-/**
- * Has the specified audio output already consumed this chunk?
- */
-static bool
-chunk_is_consumed_in(const struct audio_output *ao,
- const struct music_chunk *chunk)
-{
- if (!ao->open)
- return true;
-
- if (ao->chunk == NULL)
- return false;
-
- assert(chunk == ao->chunk || music_pipe_contains(g_mp, ao->chunk));
-
- if (chunk != ao->chunk) {
- assert(chunk->next != NULL);
- return true;
- }
-
- return ao->chunk_finished && chunk->next == NULL;
-}
-
-/**
- * Has this chunk been consumed by all audio outputs?
- */
-static bool
-chunk_is_consumed(const struct music_chunk *chunk)
-{
- for (unsigned i = 0; i < num_audio_outputs; ++i) {
- const struct audio_output *ao = audio_outputs[i];
- bool consumed;
-
- g_mutex_lock(ao->mutex);
- consumed = chunk_is_consumed_in(ao, chunk);
- g_mutex_unlock(ao->mutex);
-
- if (!consumed)
- return false;
- }
-
- return true;
-}
-
-/**
- * There's only one chunk left in the pipe (#g_mp), and all audio
- * outputs have consumed it already. Clear the reference.
- */
-static void
-clear_tail_chunk(G_GNUC_UNUSED const struct music_chunk *chunk, bool *locked)
-{
- assert(chunk->next == NULL);
- assert(music_pipe_contains(g_mp, chunk));
-
- for (unsigned i = 0; i < num_audio_outputs; ++i) {
- struct audio_output *ao = audio_outputs[i];
-
- /* this mutex will be unlocked by the caller when it's
- ready */
- g_mutex_lock(ao->mutex);
- locked[i] = ao->open;
-
- if (!locked[i]) {
- g_mutex_unlock(ao->mutex);
- continue;
- }
-
- assert(ao->chunk == chunk);
- assert(ao->chunk_finished);
- ao->chunk = NULL;
- }
-}
-
-unsigned
-audio_output_all_check(void)
-{
- const struct music_chunk *chunk;
- bool is_tail;
- struct music_chunk *shifted;
- bool locked[num_audio_outputs];
-
- assert(g_music_buffer != NULL);
- assert(g_mp != NULL);
-
- while ((chunk = music_pipe_peek(g_mp)) != NULL) {
- assert(!music_pipe_empty(g_mp));
-
- if (!chunk_is_consumed(chunk))
- /* at least one output is not finished playing
- this chunk */
- return music_pipe_size(g_mp);
-
- if (chunk->length > 0 && chunk->times >= 0.0)
- /* only update elapsed_time if the chunk
- provides a defined value */
- audio_output_all_elapsed_time = chunk->times;
-
- is_tail = chunk->next == NULL;
- if (is_tail)
- /* this is the tail of the pipe - clear the
- chunk reference in all outputs */
- clear_tail_chunk(chunk, locked);
-
- /* remove the chunk from the pipe */
- shifted = music_pipe_shift(g_mp);
- assert(shifted == chunk);
-
- if (is_tail)
- /* unlock all audio outputs which were locked
- by clear_tail_chunk() */
- for (unsigned i = 0; i < num_audio_outputs; ++i)
- if (locked[i])
- g_mutex_unlock(audio_outputs[i]->mutex);
-
- /* return the chunk to the buffer */
- music_buffer_return(g_music_buffer, shifted);
- }
-
- return 0;
-}
-
-bool
-audio_output_all_wait(struct player_control *pc, unsigned threshold)
-{
- player_lock(pc);
-
- if (audio_output_all_check() < threshold) {
- player_unlock(pc);
- return true;
- }
-
- player_wait(pc);
- player_unlock(pc);
-
- return audio_output_all_check() < threshold;
-}
-
-void
-audio_output_all_pause(void)
-{
- unsigned int i;
-
- audio_output_all_update();
-
- for (i = 0; i < num_audio_outputs; ++i)
- audio_output_pause(audio_outputs[i]);
-
- audio_output_wait_all();
-}
-
-void
-audio_output_all_drain(void)
-{
- for (unsigned i = 0; i < num_audio_outputs; ++i)
- audio_output_drain_async(audio_outputs[i]);
-
- audio_output_wait_all();
-}
-
-void
-audio_output_all_cancel(void)
-{
- unsigned int i;
-
- /* send the cancel() command to all audio outputs */
-
- for (i = 0; i < num_audio_outputs; ++i)
- audio_output_cancel(audio_outputs[i]);
-
- audio_output_wait_all();
-
- /* clear the music pipe and return all chunks to the buffer */
-
- if (g_mp != NULL)
- music_pipe_clear(g_mp, g_music_buffer);
-
- /* the audio outputs are now waiting for a signal, to
- synchronize the cleared music pipe */
-
- audio_output_allow_play_all();
-
- /* invalidate elapsed_time */
-
- audio_output_all_elapsed_time = -1.0;
-}
-
-void
-audio_output_all_close(void)
-{
- unsigned int i;
-
- for (i = 0; i < num_audio_outputs; ++i)
- audio_output_close(audio_outputs[i]);
-
- if (g_mp != NULL) {
- assert(g_music_buffer != NULL);
-
- music_pipe_clear(g_mp, g_music_buffer);
- music_pipe_free(g_mp);
- g_mp = NULL;
- }
-
- g_music_buffer = NULL;
-
- audio_format_clear(&input_audio_format);
-
- audio_output_all_elapsed_time = -1.0;
-}
-
-void
-audio_output_all_release(void)
-{
- unsigned int i;
-
- for (i = 0; i < num_audio_outputs; ++i)
- audio_output_release(audio_outputs[i]);
-
- if (g_mp != NULL) {
- assert(g_music_buffer != NULL);
-
- music_pipe_clear(g_mp, g_music_buffer);
- music_pipe_free(g_mp);
- g_mp = NULL;
- }
-
- g_music_buffer = NULL;
-
- audio_format_clear(&input_audio_format);
-
- audio_output_all_elapsed_time = -1.0;
-}
-
-void
-audio_output_all_song_border(void)
-{
- /* clear the elapsed_time pointer at the beginning of a new
- song */
- audio_output_all_elapsed_time = 0.0;
-}
-
-float
-audio_output_all_get_elapsed_time(void)
-{
- return audio_output_all_elapsed_time;
-}