diff options
author | Max Kellermann <max@duempel.org> | 2014-01-27 08:20:25 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-01-28 09:20:53 +0100 |
commit | f5a923b9d16e4c63942a033d1bdb2ab150aae342 (patch) | |
tree | 6e3c39b305fd2a1da2a7b9c2b79a6737ca21a23b /src/output/OutputAll.cxx | |
parent | 36bab6ef066c6898a791dd15054301f80757b3f6 (diff) | |
download | mpd-f5a923b9d16e4c63942a033d1bdb2ab150aae342.tar.gz mpd-f5a923b9d16e4c63942a033d1bdb2ab150aae342.tar.xz mpd-f5a923b9d16e4c63942a033d1bdb2ab150aae342.zip |
OutputAll: convert to class, move instance to class Partition
Another big chunk of code for multi-player support.
Diffstat (limited to '')
-rw-r--r-- | src/output/OutputAll.cxx | 589 |
1 files changed, 0 insertions, 589 deletions
diff --git a/src/output/OutputAll.cxx b/src/output/OutputAll.cxx deleted file mode 100644 index ded9fa1e1..000000000 --- a/src/output/OutputAll.cxx +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "OutputAll.hxx" -#include "PlayerControl.hxx" -#include "OutputInternal.hxx" -#include "OutputControl.hxx" -#include "OutputError.hxx" -#include "MusicBuffer.hxx" -#include "MusicPipe.hxx" -#include "MusicChunk.hxx" -#include "system/FatalError.hxx" -#include "util/Error.hxx" -#include "config/ConfigData.hxx" -#include "config/ConfigGlobal.hxx" -#include "config/ConfigOption.hxx" -#include "notify.hxx" - -#include <assert.h> -#include <string.h> - -static AudioFormat input_audio_format; - -static struct audio_output **audio_outputs; -static unsigned int num_audio_outputs; - -/** - * The #MusicBuffer object where consumed chunks are returned. - */ -static MusicBuffer *g_music_buffer; - -/** - * The #MusicPipe object which feeds all audio outputs. It is filled - * by audio_output_all_play(). - */ -static MusicPipe *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] != nullptr); - - 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 nullptr; -} - -gcc_const -static unsigned -audio_output_config_count(void) -{ - unsigned int nr = 0; - const struct config_param *param = nullptr; - - 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(PlayerControl &pc) -{ - const struct config_param *param = nullptr; - unsigned int i; - Error error; - - num_audio_outputs = audio_output_config_count(); - audio_outputs = new audio_output *[num_audio_outputs]; - - const config_param empty; - - for (i = 0; i < num_audio_outputs; i++) - { - unsigned int j; - - param = config_get_next_param(CONF_AUDIO_OUTPUT, param); - if (param == nullptr) { - /* only allow param to be nullptr if there - just one audio output */ - assert(i == 0); - assert(num_audio_outputs == 1); - - param = ∅ - } - - audio_output *output = audio_output_new(*param, pc, error); - if (output == nullptr) { - if (param != nullptr) - FormatFatalError("line %i: %s", - param->line, - error.GetMessage()); - else - FatalError(error); - } - - audio_outputs[i] = output; - - /* require output names to be unique: */ - for (j = 0; j < i; j++) { - if (!strcmp(output->name, audio_outputs[j]->name)) { - FormatFatalError("output devices with identical " - "names: %s", 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]); - } - - delete[] audio_outputs; - audio_outputs = nullptr; - num_audio_outputs = 0; -} - -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; - - ao->mutex.lock(); - enabled = ao->really_enabled; - ao->mutex.unlock(); - - 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]; - - const ScopeLock protect(ao->mutex); - if (audio_output_is_open(ao) && - !audio_output_command_is_finished(ao)) - return false; - } - - return true; -} - -static void audio_output_wait_all(void) -{ - while (!audio_output_all_finished()) - audio_output_client_notify.Wait(); -} - -/** - * 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) -{ - const ScopeLock protect(ao->mutex); - - ao->fail_timer.Reset(); -} - -/** - * 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 (!input_audio_format.IsDefined()) - 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; -} - -void -audio_output_all_set_replay_gain_mode(ReplayGainMode mode) -{ - for (unsigned i = 0; i < num_audio_outputs; ++i) - audio_output_set_replay_gain_mode(audio_outputs[i], mode); -} - -bool -audio_output_all_play(struct music_chunk *chunk, Error &error) -{ - bool ret; - unsigned int i; - - assert(g_music_buffer != nullptr); - assert(g_mp != nullptr); - assert(chunk != nullptr); - assert(chunk->CheckFormat(input_audio_format)); - - ret = audio_output_all_update(); - if (!ret) { - /* TODO: obtain real error */ - error.Set(output_domain, "Failed to open audio output"); - return false; - } - - g_mp->Push(chunk); - - for (i = 0; i < num_audio_outputs; ++i) - audio_output_play(audio_outputs[i]); - - return true; -} - -bool -audio_output_all_open(const AudioFormat audio_format, - MusicBuffer &buffer, - Error &error) -{ - bool ret = false, enabled = false; - unsigned int i; - - assert(g_music_buffer == nullptr || g_music_buffer == &buffer); - assert((g_mp == nullptr) == (g_music_buffer == nullptr)); - - g_music_buffer = &buffer; - - /* the audio format must be the same as existing chunks in the - pipe */ - assert(g_mp == nullptr || g_mp->CheckFormat(audio_format)); - - if (g_mp == nullptr) - g_mp = new MusicPipe(); - else - /* if the pipe hasn't been cleared, the the audio - format must not have changed */ - assert(g_mp->IsEmpty() || 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) - error.Set(output_domain, "All audio outputs are disabled"); - else if (!ret) - /* TODO: obtain real error */ - error.Set(output_domain, "Failed to open audio output"); - - 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 == nullptr) - return false; - - assert(chunk == ao->chunk || g_mp->Contains(ao->chunk)); - - if (chunk != ao->chunk) { - assert(chunk->next != nullptr); - return true; - } - - return ao->chunk_finished && chunk->next == nullptr; -} - -/** - * 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) { - struct audio_output *ao = audio_outputs[i]; - - const ScopeLock protect(ao->mutex); - if (!chunk_is_consumed_in(ao, chunk)) - 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(gcc_unused const struct music_chunk *chunk, bool *locked) -{ - assert(chunk->next == nullptr); - assert(g_mp->Contains(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 */ - ao->mutex.lock(); - locked[i] = ao->open; - - if (!locked[i]) { - ao->mutex.unlock(); - continue; - } - - assert(ao->chunk == chunk); - assert(ao->chunk_finished); - ao->chunk = nullptr; - } -} - -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 != nullptr); - assert(g_mp != nullptr); - - while ((chunk = g_mp->Peek()) != nullptr) { - assert(!g_mp->IsEmpty()); - - if (!chunk_is_consumed(chunk)) - /* at least one output is not finished playing - this chunk */ - return g_mp->GetSize(); - - 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 == nullptr; - 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 = g_mp->Shift(); - 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]) - audio_outputs[i]->mutex.unlock(); - - /* return the chunk to the buffer */ - g_music_buffer->Return(shifted); - } - - return 0; -} - -bool -audio_output_all_wait(PlayerControl &pc, unsigned threshold) -{ - pc.Lock(); - - if (audio_output_all_check() < threshold) { - pc.Unlock(); - return true; - } - - pc.Wait(); - pc.Unlock(); - - 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 != nullptr) - g_mp->Clear(*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 != nullptr) { - assert(g_music_buffer != nullptr); - - g_mp->Clear(*g_music_buffer); - delete g_mp; - g_mp = nullptr; - } - - g_music_buffer = nullptr; - - input_audio_format.Clear(); - - 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 != nullptr) { - assert(g_music_buffer != nullptr); - - g_mp->Clear(*g_music_buffer); - delete g_mp; - g_mp = nullptr; - } - - g_music_buffer = nullptr; - - input_audio_format.Clear(); - - 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; -} |