From 68b79f97f331588f29fbf956d53a2e99f70e8b7f Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Tue, 28 Jan 2014 11:42:54 +0100
Subject: output: rename source files

---
 src/CommandLine.cxx                  |   2 +-
 src/mixer/MixerAll.cxx               |   2 +-
 src/output/Domain.cxx                |  23 +++
 src/output/Domain.hxx                |  25 +++
 src/output/Finish.cxx                |  51 ++++++
 src/output/Init.cxx                  | 329 +++++++++++++++++++++++++++++++++++
 src/output/Internal.hxx              | 302 ++++++++++++++++++++++++++++++++
 src/output/MultipleOutputs.cxx       |   4 +-
 src/output/OutputAPI.hxx             |   2 +-
 src/output/OutputCommand.cxx         |   2 +-
 src/output/OutputControl.cxx         |   4 +-
 src/output/OutputError.cxx           |  23 ---
 src/output/OutputError.hxx           |  25 ---
 src/output/OutputFinish.cxx          |  51 ------
 src/output/OutputInit.cxx            | 329 -----------------------------------
 src/output/OutputInternal.hxx        | 302 --------------------------------
 src/output/OutputList.cxx            | 100 -----------
 src/output/OutputList.hxx            |  35 ----
 src/output/OutputPlugin.cxx          |   2 +-
 src/output/OutputPrint.cxx           |   2 +-
 src/output/OutputState.cxx           |   4 +-
 src/output/OutputThread.cxx          |   4 +-
 src/output/Registry.cxx              | 100 +++++++++++
 src/output/Registry.hxx              |  35 ++++
 src/output/plugins/HttpdInternal.hxx |   2 +-
 25 files changed, 880 insertions(+), 880 deletions(-)
 create mode 100644 src/output/Domain.cxx
 create mode 100644 src/output/Domain.hxx
 create mode 100644 src/output/Finish.cxx
 create mode 100644 src/output/Init.cxx
 create mode 100644 src/output/Internal.hxx
 delete mode 100644 src/output/OutputError.cxx
 delete mode 100644 src/output/OutputError.hxx
 delete mode 100644 src/output/OutputFinish.cxx
 delete mode 100644 src/output/OutputInit.cxx
 delete mode 100644 src/output/OutputInternal.hxx
 delete mode 100644 src/output/OutputList.cxx
 delete mode 100644 src/output/OutputList.hxx
 create mode 100644 src/output/Registry.cxx
 create mode 100644 src/output/Registry.hxx

(limited to 'src')

diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx
index 19bc9d4bb..5500d43ba 100644
--- a/src/CommandLine.cxx
+++ b/src/CommandLine.cxx
@@ -27,7 +27,7 @@
 #include "db/DatabasePlugin.hxx"
 #include "decoder/DecoderList.hxx"
 #include "decoder/DecoderPlugin.hxx"
-#include "output/OutputList.hxx"
+#include "output/Registry.hxx"
 #include "output/OutputPlugin.hxx"
 #include "input/Registry.hxx"
 #include "input/InputPlugin.hxx"
diff --git a/src/mixer/MixerAll.cxx b/src/mixer/MixerAll.cxx
index c2fa903f2..ac40bc496 100644
--- a/src/mixer/MixerAll.cxx
+++ b/src/mixer/MixerAll.cxx
@@ -22,7 +22,7 @@
 #include "MixerControl.hxx"
 #include "MixerInternal.hxx"
 #include "MixerList.hxx"
-#include "output/OutputInternal.hxx"
+#include "output/Internal.hxx"
 #include "pcm/Volume.hxx"
 #include "util/Error.hxx"
 #include "util/Domain.hxx"
diff --git a/src/output/Domain.cxx b/src/output/Domain.cxx
new file mode 100644
index 000000000..878e5f3c5
--- /dev/null
+++ b/src/output/Domain.cxx
@@ -0,0 +1,23 @@
+/*
+ * 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 "Domain.hxx"
+#include "util/Domain.hxx"
+
+const Domain output_domain("output");
diff --git a/src/output/Domain.hxx b/src/output/Domain.hxx
new file mode 100644
index 000000000..e3a20142f
--- /dev/null
+++ b/src/output/Domain.hxx
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#ifndef MPD_OUTPUT_ERROR_HXX
+#define MPD_OUTPUT_ERROR_HXX
+
+extern const class Domain output_domain;
+
+#endif
diff --git a/src/output/Finish.cxx b/src/output/Finish.cxx
new file mode 100644
index 000000000..b3f94fe31
--- /dev/null
+++ b/src/output/Finish.cxx
@@ -0,0 +1,51 @@
+/*
+ * 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 "Internal.hxx"
+#include "OutputPlugin.hxx"
+#include "mixer/MixerControl.hxx"
+#include "filter/FilterInternal.hxx"
+
+#include <assert.h>
+
+void
+ao_base_finish(AudioOutput *ao)
+{
+	assert(!ao->open);
+	assert(!ao->fail_timer.IsDefined());
+	assert(!ao->thread.IsDefined());
+
+	if (ao->mixer != nullptr)
+		mixer_free(ao->mixer);
+
+	delete ao->replay_gain_filter;
+	delete ao->other_replay_gain_filter;
+	delete ao->filter;
+}
+
+void
+audio_output_free(AudioOutput *ao)
+{
+	assert(!ao->open);
+	assert(!ao->fail_timer.IsDefined());
+	assert(!ao->thread.IsDefined());
+
+	ao_plugin_finish(ao);
+}
diff --git a/src/output/Init.cxx b/src/output/Init.cxx
new file mode 100644
index 000000000..29c7137d9
--- /dev/null
+++ b/src/output/Init.cxx
@@ -0,0 +1,329 @@
+/*
+ * 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 "Internal.hxx"
+#include "Registry.hxx"
+#include "Domain.hxx"
+#include "OutputAPI.hxx"
+#include "filter/FilterConfig.hxx"
+#include "AudioParser.hxx"
+#include "mixer/MixerList.hxx"
+#include "mixer/MixerType.hxx"
+#include "mixer/MixerControl.hxx"
+#include "mixer/plugins/SoftwareMixerPlugin.hxx"
+#include "filter/FilterPlugin.hxx"
+#include "filter/FilterRegistry.hxx"
+#include "filter/plugins/AutoConvertFilterPlugin.hxx"
+#include "filter/plugins/ReplayGainFilterPlugin.hxx"
+#include "filter/plugins/ChainFilterPlugin.hxx"
+#include "config/ConfigError.hxx"
+#include "config/ConfigGlobal.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+#define AUDIO_OUTPUT_TYPE	"type"
+#define AUDIO_OUTPUT_NAME	"name"
+#define AUDIO_OUTPUT_FORMAT	"format"
+#define AUDIO_FILTERS		"filters"
+
+static const AudioOutputPlugin *
+audio_output_detect(Error &error)
+{
+	LogDefault(output_domain, "Attempt to detect audio output device");
+
+	audio_output_plugins_for_each(plugin) {
+		if (plugin->test_default_device == nullptr)
+			continue;
+
+		FormatDefault(output_domain,
+			      "Attempting to detect a %s audio device",
+			      plugin->name);
+		if (ao_plugin_test_default_device(plugin))
+			return plugin;
+	}
+
+	error.Set(output_domain, "Unable to detect an audio device");
+	return nullptr;
+}
+
+/**
+ * Determines the mixer type which should be used for the specified
+ * configuration block.
+ *
+ * This handles the deprecated options mixer_type (global) and
+ * mixer_enabled, if the mixer_type setting is not configured.
+ */
+gcc_pure
+static enum mixer_type
+audio_output_mixer_type(const config_param &param)
+{
+	/* read the local "mixer_type" setting */
+	const char *p = param.GetBlockValue("mixer_type");
+	if (p != nullptr)
+		return mixer_type_parse(p);
+
+	/* try the local "mixer_enabled" setting next (deprecated) */
+	if (!param.GetBlockValue("mixer_enabled", true))
+		return MIXER_TYPE_NONE;
+
+	/* fall back to the global "mixer_type" setting (also
+	   deprecated) */
+	return mixer_type_parse(config_get_string(CONF_MIXER_TYPE,
+						  "hardware"));
+}
+
+static Mixer *
+audio_output_load_mixer(AudioOutput *ao,
+			const config_param &param,
+			const struct mixer_plugin *plugin,
+			Filter &filter_chain,
+			Error &error)
+{
+	Mixer *mixer;
+
+	switch (audio_output_mixer_type(param)) {
+	case MIXER_TYPE_NONE:
+	case MIXER_TYPE_UNKNOWN:
+		return nullptr;
+
+	case MIXER_TYPE_HARDWARE:
+		if (plugin == nullptr)
+			return nullptr;
+
+		return mixer_new(plugin, ao, param, error);
+
+	case MIXER_TYPE_SOFTWARE:
+		mixer = mixer_new(&software_mixer_plugin, nullptr,
+				  config_param(),
+				  IgnoreError());
+		assert(mixer != nullptr);
+
+		filter_chain_append(filter_chain, "software_mixer",
+				    software_mixer_get_filter(mixer));
+		return mixer;
+	}
+
+	assert(false);
+	gcc_unreachable();
+}
+
+bool
+ao_base_init(AudioOutput *ao,
+	     const AudioOutputPlugin *plugin,
+	     const config_param &param, Error &error)
+{
+	assert(ao != nullptr);
+	assert(plugin != nullptr);
+	assert(plugin->finish != nullptr);
+	assert(plugin->open != nullptr);
+	assert(plugin->close != nullptr);
+	assert(plugin->play != nullptr);
+
+	if (!param.IsNull()) {
+		ao->name = param.GetBlockValue(AUDIO_OUTPUT_NAME);
+		if (ao->name == nullptr) {
+			error.Set(config_domain,
+				  "Missing \"name\" configuration");
+			return false;
+		}
+
+		const char *p = param.GetBlockValue(AUDIO_OUTPUT_FORMAT);
+		if (p != nullptr) {
+			bool success =
+				audio_format_parse(ao->config_audio_format,
+						   p, true, error);
+			if (!success)
+				return false;
+		} else
+			ao->config_audio_format.Clear();
+	} else {
+		ao->name = "default detected output";
+
+		ao->config_audio_format.Clear();
+	}
+
+	ao->plugin = plugin;
+	ao->tags = param.GetBlockValue("tags", true);
+	ao->always_on = param.GetBlockValue("always_on", false);
+	ao->enabled = param.GetBlockValue("enabled", true);
+	ao->really_enabled = false;
+	ao->open = false;
+	ao->pause = false;
+	ao->allow_play = true;
+	ao->in_playback_loop = false;
+	ao->woken_for_play = false;
+
+	/* set up the filter chain */
+
+	ao->filter = filter_chain_new();
+	assert(ao->filter != nullptr);
+
+	/* create the normalization filter (if configured) */
+
+	if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) {
+		Filter *normalize_filter =
+			filter_new(&normalize_filter_plugin, config_param(),
+				   IgnoreError());
+		assert(normalize_filter != nullptr);
+
+		filter_chain_append(*ao->filter, "normalize",
+				    autoconvert_filter_new(normalize_filter));
+	}
+
+	Error filter_error;
+	filter_chain_parse(*ao->filter,
+			   param.GetBlockValue(AUDIO_FILTERS, ""),
+			   filter_error);
+
+	// It's not really fatal - Part of the filter chain has been set up already
+	// and even an empty one will work (if only with unexpected behaviour)
+	if (filter_error.IsDefined())
+		FormatError(filter_error,
+			    "Failed to initialize filter chain for '%s'",
+			    ao->name);
+
+	ao->command = AO_COMMAND_NONE;
+
+	ao->mixer = nullptr;
+	ao->replay_gain_filter = nullptr;
+	ao->other_replay_gain_filter = nullptr;
+
+	/* done */
+
+	return true;
+}
+
+static bool
+audio_output_setup(AudioOutput *ao, const config_param &param,
+		   Error &error)
+{
+
+	/* create the replay_gain filter */
+
+	const char *replay_gain_handler =
+		param.GetBlockValue("replay_gain_handler", "software");
+
+	if (strcmp(replay_gain_handler, "none") != 0) {
+		ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
+						    param, IgnoreError());
+		assert(ao->replay_gain_filter != nullptr);
+
+		ao->replay_gain_serial = 0;
+
+		ao->other_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
+							  param,
+							  IgnoreError());
+		assert(ao->other_replay_gain_filter != nullptr);
+
+		ao->other_replay_gain_serial = 0;
+	} else {
+		ao->replay_gain_filter = nullptr;
+		ao->other_replay_gain_filter = nullptr;
+	}
+
+	/* set up the mixer */
+
+	Error mixer_error;
+	ao->mixer = audio_output_load_mixer(ao, param,
+					    ao->plugin->mixer_plugin,
+					    *ao->filter, mixer_error);
+	if (ao->mixer == nullptr && mixer_error.IsDefined())
+		FormatError(mixer_error,
+			    "Failed to initialize hardware mixer for '%s'",
+			    ao->name);
+
+	/* use the hardware mixer for replay gain? */
+
+	if (strcmp(replay_gain_handler, "mixer") == 0) {
+		if (ao->mixer != nullptr)
+			replay_gain_filter_set_mixer(ao->replay_gain_filter,
+						     ao->mixer, 100);
+		else
+			FormatError(output_domain,
+				    "No such mixer for output '%s'", ao->name);
+	} else if (strcmp(replay_gain_handler, "software") != 0 &&
+		   ao->replay_gain_filter != nullptr) {
+		error.Set(config_domain,
+			  "Invalid \"replay_gain_handler\" value");
+		return false;
+	}
+
+	/* the "convert" filter must be the last one in the chain */
+
+	ao->convert_filter = filter_new(&convert_filter_plugin, config_param(),
+					IgnoreError());
+	assert(ao->convert_filter != nullptr);
+
+	filter_chain_append(*ao->filter, "convert", ao->convert_filter);
+
+	return true;
+}
+
+AudioOutput *
+audio_output_new(const config_param &param,
+		 PlayerControl &pc,
+		 Error &error)
+{
+	const AudioOutputPlugin *plugin;
+
+	if (!param.IsNull()) {
+		const char *p;
+
+		p = param.GetBlockValue(AUDIO_OUTPUT_TYPE);
+		if (p == nullptr) {
+			error.Set(config_domain,
+				  "Missing \"type\" configuration");
+			return nullptr;
+		}
+
+		plugin = AudioOutputPlugin_get(p);
+		if (plugin == nullptr) {
+			error.Format(config_domain,
+				     "No such audio output plugin: %s", p);
+			return nullptr;
+		}
+	} else {
+		LogWarning(output_domain,
+			   "No 'AudioOutput' defined in config file");
+
+		plugin = audio_output_detect(error);
+		if (plugin == nullptr)
+			return nullptr;
+
+		FormatDefault(output_domain,
+			      "Successfully detected a %s audio device",
+			      plugin->name);
+	}
+
+	AudioOutput *ao = ao_plugin_init(plugin, param, error);
+	if (ao == nullptr)
+		return nullptr;
+
+	if (!audio_output_setup(ao, param, error)) {
+		ao_plugin_finish(ao);
+		return nullptr;
+	}
+
+	ao->player_control = &pc;
+	return ao;
+}
diff --git a/src/output/Internal.hxx b/src/output/Internal.hxx
new file mode 100644
index 000000000..31b6fd7c7
--- /dev/null
+++ b/src/output/Internal.hxx
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ */
+
+#ifndef MPD_OUTPUT_INTERNAL_HXX
+#define MPD_OUTPUT_INTERNAL_HXX
+
+#include "AudioFormat.hxx"
+#include "pcm/PcmBuffer.hxx"
+#include "pcm/PcmDither.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "thread/Thread.hxx"
+#include "system/PeriodClock.hxx"
+
+class Error;
+class Filter;
+class MusicPipe;
+struct config_param;
+struct PlayerControl;
+struct AudioOutputPlugin;
+
+enum audio_output_command {
+	AO_COMMAND_NONE = 0,
+	AO_COMMAND_ENABLE,
+	AO_COMMAND_DISABLE,
+	AO_COMMAND_OPEN,
+
+	/**
+	 * This command is invoked when the input audio format
+	 * changes.
+	 */
+	AO_COMMAND_REOPEN,
+
+	AO_COMMAND_CLOSE,
+	AO_COMMAND_PAUSE,
+
+	/**
+	 * Drains the internal (hardware) buffers of the device.  This
+	 * operation may take a while to complete.
+	 */
+	AO_COMMAND_DRAIN,
+
+	AO_COMMAND_CANCEL,
+	AO_COMMAND_KILL
+};
+
+struct AudioOutput {
+	/**
+	 * The device's configured display name.
+	 */
+	const char *name;
+
+	/**
+	 * The plugin which implements this output device.
+	 */
+	const AudioOutputPlugin *plugin;
+
+	/**
+	 * The #mixer object associated with this audio output device.
+	 * May be nullptr if none is available, or if software volume is
+	 * configured.
+	 */
+	class Mixer *mixer;
+
+	/**
+	 * Will this output receive tags from the decoder?  The
+	 * default is true, but it may be configured to false to
+	 * suppress sending tags to the output.
+	 */
+	bool tags;
+
+	/**
+	 * Shall this output always play something (i.e. silence),
+	 * even when playback is stopped?
+	 */
+	bool always_on;
+
+	/**
+	 * Has the user enabled this device?
+	 */
+	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.
+	 * It is protected with #mutex: write accesses inside the
+	 * output thread and read accesses outside of it may only be
+	 * performed while the lock is held.
+	 */
+	bool open;
+
+	/**
+	 * Is the device paused?  i.e. the output thread is in the
+	 * ao_pause() loop.
+	 */
+	bool pause;
+
+	/**
+	 * When this flag is set, the output thread will not do any
+	 * playback.  It will wait until the flag is cleared.
+	 *
+	 * This is used to synchronize the "clear" operation on the
+	 * shared music pipe during the CANCEL command.
+	 */
+	bool allow_play;
+
+	/**
+	 * True while the OutputThread is inside ao_play().  This
+	 * means the PlayerThread does not need to wake up the
+	 * OutputThread when new chunks are added to the MusicPipe,
+	 * because the OutputThread is already watching that.
+	 */
+	bool in_playback_loop;
+
+	/**
+	 * Has the OutputThread been woken up to play more chunks?
+	 * This is set by audio_output_play() and reset by ao_play()
+	 * to reduce the number of duplicate wakeups.
+	 */
+	bool woken_for_play;
+
+	/**
+	 * If not nullptr, the device has failed, and this timer is used
+	 * to estimate how long it should stay disabled (unless
+	 * explicitly reopened with "play").
+	 */
+	PeriodClock fail_timer;
+
+	/**
+	 * The configured audio format.
+	 */
+	AudioFormat config_audio_format;
+
+	/**
+	 * The audio_format in which audio data is received from the
+	 * player thread (which in turn receives it from the decoder).
+	 */
+	AudioFormat in_audio_format;
+
+	/**
+	 * The audio_format which is really sent to the device.  This
+	 * is basically config_audio_format (if configured) or
+	 * in_audio_format, but may have been modified by
+	 * plugin->open().
+	 */
+	AudioFormat out_audio_format;
+
+	/**
+	 * The buffer used to allocate the cross-fading result.
+	 */
+	PcmBuffer cross_fade_buffer;
+
+	/**
+	 * The dithering state for cross-fading two streams.
+	 */
+	PcmDither cross_fade_dither;
+
+	/**
+	 * The filter object of this audio output.  This is an
+	 * instance of chain_filter_plugin.
+	 */
+	Filter *filter;
+
+	/**
+	 * The replay_gain_filter_plugin instance of this audio
+	 * output.
+	 */
+	Filter *replay_gain_filter;
+
+	/**
+	 * The serial number of the last replay gain info.  0 means no
+	 * replay gain info was available.
+	 */
+	unsigned replay_gain_serial;
+
+	/**
+	 * The replay_gain_filter_plugin instance of this audio
+	 * output, to be applied to the second chunk during
+	 * cross-fading.
+	 */
+	Filter *other_replay_gain_filter;
+
+	/**
+	 * The serial number of the last replay gain info by the
+	 * "other" chunk during cross-fading.
+	 */
+	unsigned other_replay_gain_serial;
+
+	/**
+	 * The convert_filter_plugin instance of this audio output.
+	 * It is the last item in the filter chain, and is responsible
+	 * for converting the input data into the appropriate format
+	 * for this audio output.
+	 */
+	Filter *convert_filter;
+
+	/**
+	 * The thread handle, or nullptr if the output thread isn't
+	 * running.
+	 */
+	Thread thread;
+
+	/**
+	 * The next command to be performed by the output thread.
+	 */
+	enum audio_output_command command;
+
+	/**
+	 * The music pipe which provides music chunks to be played.
+	 */
+	const MusicPipe *pipe;
+
+	/**
+	 * This mutex protects #open, #fail_timer, #chunk and
+	 * #chunk_finished.
+	 */
+	Mutex mutex;
+
+	/**
+	 * This condition object wakes up the output thread after
+	 * #command has been set.
+	 */
+	Cond cond;
+
+	/**
+	 * The PlayerControl object which "owns" this output.  This
+	 * object is needed to signal command completion.
+	 */
+	PlayerControl *player_control;
+
+	/**
+	 * The #music_chunk which is currently being played.  All
+	 * chunks before this one may be returned to the
+	 * #music_buffer, because they are not going to be used by
+	 * this output anymore.
+	 */
+	const struct music_chunk *chunk;
+
+	/**
+	 * Has the output finished playing #chunk?
+	 */
+	bool chunk_finished;
+};
+
+/**
+ * Notify object used by the thread's client, i.e. we will send a
+ * notify signal to this object, expecting the caller to wait on it.
+ */
+extern struct notify audio_output_client_notify;
+
+static inline bool
+audio_output_is_open(const AudioOutput *ao)
+{
+	return ao->open;
+}
+
+static inline bool
+audio_output_command_is_finished(const AudioOutput *ao)
+{
+	return ao->command == AO_COMMAND_NONE;
+}
+
+AudioOutput *
+audio_output_new(const config_param &param,
+		 PlayerControl &pc,
+		 Error &error);
+
+bool
+ao_base_init(AudioOutput *ao,
+	     const AudioOutputPlugin *plugin,
+	     const config_param &param, Error &error);
+
+void
+ao_base_finish(AudioOutput *ao);
+
+void
+audio_output_free(AudioOutput *ao);
+
+#endif
diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx
index 7df89cc55..c83d8f02d 100644
--- a/src/output/MultipleOutputs.cxx
+++ b/src/output/MultipleOutputs.cxx
@@ -20,9 +20,9 @@
 #include "config.h"
 #include "MultipleOutputs.hxx"
 #include "PlayerControl.hxx"
-#include "OutputInternal.hxx"
+#include "Internal.hxx"
 #include "OutputControl.hxx"
-#include "OutputError.hxx"
+#include "Domain.hxx"
 #include "MusicBuffer.hxx"
 #include "MusicPipe.hxx"
 #include "MusicChunk.hxx"
diff --git a/src/output/OutputAPI.hxx b/src/output/OutputAPI.hxx
index cfbc43196..e0fd6eec8 100644
--- a/src/output/OutputAPI.hxx
+++ b/src/output/OutputAPI.hxx
@@ -23,7 +23,7 @@
 // IWYU pragma: begin_exports
 
 #include "OutputPlugin.hxx"
-#include "OutputInternal.hxx"
+#include "Internal.hxx"
 #include "AudioFormat.hxx"
 #include "tag/Tag.hxx"
 #include "config/ConfigData.hxx"
diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx
index 6d5690575..6afb70cf1 100644
--- a/src/output/OutputCommand.cxx
+++ b/src/output/OutputCommand.cxx
@@ -27,7 +27,7 @@
 #include "config.h"
 #include "OutputCommand.hxx"
 #include "MultipleOutputs.hxx"
-#include "OutputInternal.hxx"
+#include "Internal.hxx"
 #include "PlayerControl.hxx"
 #include "mixer/MixerControl.hxx"
 #include "Idle.hxx"
diff --git a/src/output/OutputControl.cxx b/src/output/OutputControl.cxx
index f6844911f..5b4c4f487 100644
--- a/src/output/OutputControl.cxx
+++ b/src/output/OutputControl.cxx
@@ -21,9 +21,9 @@
 #include "config.h"
 #include "OutputControl.hxx"
 #include "OutputThread.hxx"
-#include "OutputInternal.hxx"
+#include "Internal.hxx"
 #include "OutputPlugin.hxx"
-#include "OutputError.hxx"
+#include "Domain.hxx"
 #include "mixer/MixerControl.hxx"
 #include "notify.hxx"
 #include "filter/plugins/ReplayGainFilterPlugin.hxx"
diff --git a/src/output/OutputError.cxx b/src/output/OutputError.cxx
deleted file mode 100644
index 9d4128912..000000000
--- a/src/output/OutputError.cxx
+++ /dev/null
@@ -1,23 +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 "OutputError.hxx"
-#include "util/Domain.hxx"
-
-const Domain output_domain("output");
diff --git a/src/output/OutputError.hxx b/src/output/OutputError.hxx
deleted file mode 100644
index e3a20142f..000000000
--- a/src/output/OutputError.hxx
+++ /dev/null
@@ -1,25 +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.
- */
-
-#ifndef MPD_OUTPUT_ERROR_HXX
-#define MPD_OUTPUT_ERROR_HXX
-
-extern const class Domain output_domain;
-
-#endif
diff --git a/src/output/OutputFinish.cxx b/src/output/OutputFinish.cxx
deleted file mode 100644
index 1f405aaf9..000000000
--- a/src/output/OutputFinish.cxx
+++ /dev/null
@@ -1,51 +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 "OutputInternal.hxx"
-#include "OutputPlugin.hxx"
-#include "mixer/MixerControl.hxx"
-#include "filter/FilterInternal.hxx"
-
-#include <assert.h>
-
-void
-ao_base_finish(AudioOutput *ao)
-{
-	assert(!ao->open);
-	assert(!ao->fail_timer.IsDefined());
-	assert(!ao->thread.IsDefined());
-
-	if (ao->mixer != nullptr)
-		mixer_free(ao->mixer);
-
-	delete ao->replay_gain_filter;
-	delete ao->other_replay_gain_filter;
-	delete ao->filter;
-}
-
-void
-audio_output_free(AudioOutput *ao)
-{
-	assert(!ao->open);
-	assert(!ao->fail_timer.IsDefined());
-	assert(!ao->thread.IsDefined());
-
-	ao_plugin_finish(ao);
-}
diff --git a/src/output/OutputInit.cxx b/src/output/OutputInit.cxx
deleted file mode 100644
index f91e5b5e3..000000000
--- a/src/output/OutputInit.cxx
+++ /dev/null
@@ -1,329 +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 "OutputInternal.hxx"
-#include "OutputList.hxx"
-#include "OutputError.hxx"
-#include "OutputAPI.hxx"
-#include "filter/FilterConfig.hxx"
-#include "AudioParser.hxx"
-#include "mixer/MixerList.hxx"
-#include "mixer/MixerType.hxx"
-#include "mixer/MixerControl.hxx"
-#include "mixer/plugins/SoftwareMixerPlugin.hxx"
-#include "filter/FilterPlugin.hxx"
-#include "filter/FilterRegistry.hxx"
-#include "filter/plugins/AutoConvertFilterPlugin.hxx"
-#include "filter/plugins/ReplayGainFilterPlugin.hxx"
-#include "filter/plugins/ChainFilterPlugin.hxx"
-#include "config/ConfigError.hxx"
-#include "config/ConfigGlobal.hxx"
-#include "util/Error.hxx"
-#include "Log.hxx"
-
-#include <assert.h>
-#include <string.h>
-
-#define AUDIO_OUTPUT_TYPE	"type"
-#define AUDIO_OUTPUT_NAME	"name"
-#define AUDIO_OUTPUT_FORMAT	"format"
-#define AUDIO_FILTERS		"filters"
-
-static const AudioOutputPlugin *
-audio_output_detect(Error &error)
-{
-	LogDefault(output_domain, "Attempt to detect audio output device");
-
-	audio_output_plugins_for_each(plugin) {
-		if (plugin->test_default_device == nullptr)
-			continue;
-
-		FormatDefault(output_domain,
-			      "Attempting to detect a %s audio device",
-			      plugin->name);
-		if (ao_plugin_test_default_device(plugin))
-			return plugin;
-	}
-
-	error.Set(output_domain, "Unable to detect an audio device");
-	return nullptr;
-}
-
-/**
- * Determines the mixer type which should be used for the specified
- * configuration block.
- *
- * This handles the deprecated options mixer_type (global) and
- * mixer_enabled, if the mixer_type setting is not configured.
- */
-gcc_pure
-static enum mixer_type
-audio_output_mixer_type(const config_param &param)
-{
-	/* read the local "mixer_type" setting */
-	const char *p = param.GetBlockValue("mixer_type");
-	if (p != nullptr)
-		return mixer_type_parse(p);
-
-	/* try the local "mixer_enabled" setting next (deprecated) */
-	if (!param.GetBlockValue("mixer_enabled", true))
-		return MIXER_TYPE_NONE;
-
-	/* fall back to the global "mixer_type" setting (also
-	   deprecated) */
-	return mixer_type_parse(config_get_string(CONF_MIXER_TYPE,
-						  "hardware"));
-}
-
-static Mixer *
-audio_output_load_mixer(AudioOutput *ao,
-			const config_param &param,
-			const struct mixer_plugin *plugin,
-			Filter &filter_chain,
-			Error &error)
-{
-	Mixer *mixer;
-
-	switch (audio_output_mixer_type(param)) {
-	case MIXER_TYPE_NONE:
-	case MIXER_TYPE_UNKNOWN:
-		return nullptr;
-
-	case MIXER_TYPE_HARDWARE:
-		if (plugin == nullptr)
-			return nullptr;
-
-		return mixer_new(plugin, ao, param, error);
-
-	case MIXER_TYPE_SOFTWARE:
-		mixer = mixer_new(&software_mixer_plugin, nullptr,
-				  config_param(),
-				  IgnoreError());
-		assert(mixer != nullptr);
-
-		filter_chain_append(filter_chain, "software_mixer",
-				    software_mixer_get_filter(mixer));
-		return mixer;
-	}
-
-	assert(false);
-	gcc_unreachable();
-}
-
-bool
-ao_base_init(AudioOutput *ao,
-	     const AudioOutputPlugin *plugin,
-	     const config_param &param, Error &error)
-{
-	assert(ao != nullptr);
-	assert(plugin != nullptr);
-	assert(plugin->finish != nullptr);
-	assert(plugin->open != nullptr);
-	assert(plugin->close != nullptr);
-	assert(plugin->play != nullptr);
-
-	if (!param.IsNull()) {
-		ao->name = param.GetBlockValue(AUDIO_OUTPUT_NAME);
-		if (ao->name == nullptr) {
-			error.Set(config_domain,
-				  "Missing \"name\" configuration");
-			return false;
-		}
-
-		const char *p = param.GetBlockValue(AUDIO_OUTPUT_FORMAT);
-		if (p != nullptr) {
-			bool success =
-				audio_format_parse(ao->config_audio_format,
-						   p, true, error);
-			if (!success)
-				return false;
-		} else
-			ao->config_audio_format.Clear();
-	} else {
-		ao->name = "default detected output";
-
-		ao->config_audio_format.Clear();
-	}
-
-	ao->plugin = plugin;
-	ao->tags = param.GetBlockValue("tags", true);
-	ao->always_on = param.GetBlockValue("always_on", false);
-	ao->enabled = param.GetBlockValue("enabled", true);
-	ao->really_enabled = false;
-	ao->open = false;
-	ao->pause = false;
-	ao->allow_play = true;
-	ao->in_playback_loop = false;
-	ao->woken_for_play = false;
-
-	/* set up the filter chain */
-
-	ao->filter = filter_chain_new();
-	assert(ao->filter != nullptr);
-
-	/* create the normalization filter (if configured) */
-
-	if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) {
-		Filter *normalize_filter =
-			filter_new(&normalize_filter_plugin, config_param(),
-				   IgnoreError());
-		assert(normalize_filter != nullptr);
-
-		filter_chain_append(*ao->filter, "normalize",
-				    autoconvert_filter_new(normalize_filter));
-	}
-
-	Error filter_error;
-	filter_chain_parse(*ao->filter,
-			   param.GetBlockValue(AUDIO_FILTERS, ""),
-			   filter_error);
-
-	// It's not really fatal - Part of the filter chain has been set up already
-	// and even an empty one will work (if only with unexpected behaviour)
-	if (filter_error.IsDefined())
-		FormatError(filter_error,
-			    "Failed to initialize filter chain for '%s'",
-			    ao->name);
-
-	ao->command = AO_COMMAND_NONE;
-
-	ao->mixer = nullptr;
-	ao->replay_gain_filter = nullptr;
-	ao->other_replay_gain_filter = nullptr;
-
-	/* done */
-
-	return true;
-}
-
-static bool
-audio_output_setup(AudioOutput *ao, const config_param &param,
-		   Error &error)
-{
-
-	/* create the replay_gain filter */
-
-	const char *replay_gain_handler =
-		param.GetBlockValue("replay_gain_handler", "software");
-
-	if (strcmp(replay_gain_handler, "none") != 0) {
-		ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
-						    param, IgnoreError());
-		assert(ao->replay_gain_filter != nullptr);
-
-		ao->replay_gain_serial = 0;
-
-		ao->other_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
-							  param,
-							  IgnoreError());
-		assert(ao->other_replay_gain_filter != nullptr);
-
-		ao->other_replay_gain_serial = 0;
-	} else {
-		ao->replay_gain_filter = nullptr;
-		ao->other_replay_gain_filter = nullptr;
-	}
-
-	/* set up the mixer */
-
-	Error mixer_error;
-	ao->mixer = audio_output_load_mixer(ao, param,
-					    ao->plugin->mixer_plugin,
-					    *ao->filter, mixer_error);
-	if (ao->mixer == nullptr && mixer_error.IsDefined())
-		FormatError(mixer_error,
-			    "Failed to initialize hardware mixer for '%s'",
-			    ao->name);
-
-	/* use the hardware mixer for replay gain? */
-
-	if (strcmp(replay_gain_handler, "mixer") == 0) {
-		if (ao->mixer != nullptr)
-			replay_gain_filter_set_mixer(ao->replay_gain_filter,
-						     ao->mixer, 100);
-		else
-			FormatError(output_domain,
-				    "No such mixer for output '%s'", ao->name);
-	} else if (strcmp(replay_gain_handler, "software") != 0 &&
-		   ao->replay_gain_filter != nullptr) {
-		error.Set(config_domain,
-			  "Invalid \"replay_gain_handler\" value");
-		return false;
-	}
-
-	/* the "convert" filter must be the last one in the chain */
-
-	ao->convert_filter = filter_new(&convert_filter_plugin, config_param(),
-					IgnoreError());
-	assert(ao->convert_filter != nullptr);
-
-	filter_chain_append(*ao->filter, "convert", ao->convert_filter);
-
-	return true;
-}
-
-AudioOutput *
-audio_output_new(const config_param &param,
-		 PlayerControl &pc,
-		 Error &error)
-{
-	const AudioOutputPlugin *plugin;
-
-	if (!param.IsNull()) {
-		const char *p;
-
-		p = param.GetBlockValue(AUDIO_OUTPUT_TYPE);
-		if (p == nullptr) {
-			error.Set(config_domain,
-				  "Missing \"type\" configuration");
-			return nullptr;
-		}
-
-		plugin = AudioOutputPlugin_get(p);
-		if (plugin == nullptr) {
-			error.Format(config_domain,
-				     "No such audio output plugin: %s", p);
-			return nullptr;
-		}
-	} else {
-		LogWarning(output_domain,
-			   "No 'AudioOutput' defined in config file");
-
-		plugin = audio_output_detect(error);
-		if (plugin == nullptr)
-			return nullptr;
-
-		FormatDefault(output_domain,
-			      "Successfully detected a %s audio device",
-			      plugin->name);
-	}
-
-	AudioOutput *ao = ao_plugin_init(plugin, param, error);
-	if (ao == nullptr)
-		return nullptr;
-
-	if (!audio_output_setup(ao, param, error)) {
-		ao_plugin_finish(ao);
-		return nullptr;
-	}
-
-	ao->player_control = &pc;
-	return ao;
-}
diff --git a/src/output/OutputInternal.hxx b/src/output/OutputInternal.hxx
deleted file mode 100644
index 31b6fd7c7..000000000
--- a/src/output/OutputInternal.hxx
+++ /dev/null
@@ -1,302 +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.
- */
-
-#ifndef MPD_OUTPUT_INTERNAL_HXX
-#define MPD_OUTPUT_INTERNAL_HXX
-
-#include "AudioFormat.hxx"
-#include "pcm/PcmBuffer.hxx"
-#include "pcm/PcmDither.hxx"
-#include "thread/Mutex.hxx"
-#include "thread/Cond.hxx"
-#include "thread/Thread.hxx"
-#include "system/PeriodClock.hxx"
-
-class Error;
-class Filter;
-class MusicPipe;
-struct config_param;
-struct PlayerControl;
-struct AudioOutputPlugin;
-
-enum audio_output_command {
-	AO_COMMAND_NONE = 0,
-	AO_COMMAND_ENABLE,
-	AO_COMMAND_DISABLE,
-	AO_COMMAND_OPEN,
-
-	/**
-	 * This command is invoked when the input audio format
-	 * changes.
-	 */
-	AO_COMMAND_REOPEN,
-
-	AO_COMMAND_CLOSE,
-	AO_COMMAND_PAUSE,
-
-	/**
-	 * Drains the internal (hardware) buffers of the device.  This
-	 * operation may take a while to complete.
-	 */
-	AO_COMMAND_DRAIN,
-
-	AO_COMMAND_CANCEL,
-	AO_COMMAND_KILL
-};
-
-struct AudioOutput {
-	/**
-	 * The device's configured display name.
-	 */
-	const char *name;
-
-	/**
-	 * The plugin which implements this output device.
-	 */
-	const AudioOutputPlugin *plugin;
-
-	/**
-	 * The #mixer object associated with this audio output device.
-	 * May be nullptr if none is available, or if software volume is
-	 * configured.
-	 */
-	class Mixer *mixer;
-
-	/**
-	 * Will this output receive tags from the decoder?  The
-	 * default is true, but it may be configured to false to
-	 * suppress sending tags to the output.
-	 */
-	bool tags;
-
-	/**
-	 * Shall this output always play something (i.e. silence),
-	 * even when playback is stopped?
-	 */
-	bool always_on;
-
-	/**
-	 * Has the user enabled this device?
-	 */
-	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.
-	 * It is protected with #mutex: write accesses inside the
-	 * output thread and read accesses outside of it may only be
-	 * performed while the lock is held.
-	 */
-	bool open;
-
-	/**
-	 * Is the device paused?  i.e. the output thread is in the
-	 * ao_pause() loop.
-	 */
-	bool pause;
-
-	/**
-	 * When this flag is set, the output thread will not do any
-	 * playback.  It will wait until the flag is cleared.
-	 *
-	 * This is used to synchronize the "clear" operation on the
-	 * shared music pipe during the CANCEL command.
-	 */
-	bool allow_play;
-
-	/**
-	 * True while the OutputThread is inside ao_play().  This
-	 * means the PlayerThread does not need to wake up the
-	 * OutputThread when new chunks are added to the MusicPipe,
-	 * because the OutputThread is already watching that.
-	 */
-	bool in_playback_loop;
-
-	/**
-	 * Has the OutputThread been woken up to play more chunks?
-	 * This is set by audio_output_play() and reset by ao_play()
-	 * to reduce the number of duplicate wakeups.
-	 */
-	bool woken_for_play;
-
-	/**
-	 * If not nullptr, the device has failed, and this timer is used
-	 * to estimate how long it should stay disabled (unless
-	 * explicitly reopened with "play").
-	 */
-	PeriodClock fail_timer;
-
-	/**
-	 * The configured audio format.
-	 */
-	AudioFormat config_audio_format;
-
-	/**
-	 * The audio_format in which audio data is received from the
-	 * player thread (which in turn receives it from the decoder).
-	 */
-	AudioFormat in_audio_format;
-
-	/**
-	 * The audio_format which is really sent to the device.  This
-	 * is basically config_audio_format (if configured) or
-	 * in_audio_format, but may have been modified by
-	 * plugin->open().
-	 */
-	AudioFormat out_audio_format;
-
-	/**
-	 * The buffer used to allocate the cross-fading result.
-	 */
-	PcmBuffer cross_fade_buffer;
-
-	/**
-	 * The dithering state for cross-fading two streams.
-	 */
-	PcmDither cross_fade_dither;
-
-	/**
-	 * The filter object of this audio output.  This is an
-	 * instance of chain_filter_plugin.
-	 */
-	Filter *filter;
-
-	/**
-	 * The replay_gain_filter_plugin instance of this audio
-	 * output.
-	 */
-	Filter *replay_gain_filter;
-
-	/**
-	 * The serial number of the last replay gain info.  0 means no
-	 * replay gain info was available.
-	 */
-	unsigned replay_gain_serial;
-
-	/**
-	 * The replay_gain_filter_plugin instance of this audio
-	 * output, to be applied to the second chunk during
-	 * cross-fading.
-	 */
-	Filter *other_replay_gain_filter;
-
-	/**
-	 * The serial number of the last replay gain info by the
-	 * "other" chunk during cross-fading.
-	 */
-	unsigned other_replay_gain_serial;
-
-	/**
-	 * The convert_filter_plugin instance of this audio output.
-	 * It is the last item in the filter chain, and is responsible
-	 * for converting the input data into the appropriate format
-	 * for this audio output.
-	 */
-	Filter *convert_filter;
-
-	/**
-	 * The thread handle, or nullptr if the output thread isn't
-	 * running.
-	 */
-	Thread thread;
-
-	/**
-	 * The next command to be performed by the output thread.
-	 */
-	enum audio_output_command command;
-
-	/**
-	 * The music pipe which provides music chunks to be played.
-	 */
-	const MusicPipe *pipe;
-
-	/**
-	 * This mutex protects #open, #fail_timer, #chunk and
-	 * #chunk_finished.
-	 */
-	Mutex mutex;
-
-	/**
-	 * This condition object wakes up the output thread after
-	 * #command has been set.
-	 */
-	Cond cond;
-
-	/**
-	 * The PlayerControl object which "owns" this output.  This
-	 * object is needed to signal command completion.
-	 */
-	PlayerControl *player_control;
-
-	/**
-	 * The #music_chunk which is currently being played.  All
-	 * chunks before this one may be returned to the
-	 * #music_buffer, because they are not going to be used by
-	 * this output anymore.
-	 */
-	const struct music_chunk *chunk;
-
-	/**
-	 * Has the output finished playing #chunk?
-	 */
-	bool chunk_finished;
-};
-
-/**
- * Notify object used by the thread's client, i.e. we will send a
- * notify signal to this object, expecting the caller to wait on it.
- */
-extern struct notify audio_output_client_notify;
-
-static inline bool
-audio_output_is_open(const AudioOutput *ao)
-{
-	return ao->open;
-}
-
-static inline bool
-audio_output_command_is_finished(const AudioOutput *ao)
-{
-	return ao->command == AO_COMMAND_NONE;
-}
-
-AudioOutput *
-audio_output_new(const config_param &param,
-		 PlayerControl &pc,
-		 Error &error);
-
-bool
-ao_base_init(AudioOutput *ao,
-	     const AudioOutputPlugin *plugin,
-	     const config_param &param, Error &error);
-
-void
-ao_base_finish(AudioOutput *ao);
-
-void
-audio_output_free(AudioOutput *ao);
-
-#endif
diff --git a/src/output/OutputList.cxx b/src/output/OutputList.cxx
deleted file mode 100644
index c523d4cbc..000000000
--- a/src/output/OutputList.cxx
+++ /dev/null
@@ -1,100 +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 "OutputList.hxx"
-#include "OutputAPI.hxx"
-#include "plugins/AlsaOutputPlugin.hxx"
-#include "plugins/AoOutputPlugin.hxx"
-#include "plugins/FifoOutputPlugin.hxx"
-#include "plugins/HttpdOutputPlugin.hxx"
-#include "plugins/JackOutputPlugin.hxx"
-#include "plugins/NullOutputPlugin.hxx"
-#include "plugins/OpenALOutputPlugin.hxx"
-#include "plugins/OssOutputPlugin.hxx"
-#include "plugins/OSXOutputPlugin.hxx"
-#include "plugins/PipeOutputPlugin.hxx"
-#include "plugins/PulseOutputPlugin.hxx"
-#include "plugins/RecorderOutputPlugin.hxx"
-#include "plugins/RoarOutputPlugin.hxx"
-#include "plugins/ShoutOutputPlugin.hxx"
-#include "plugins/SolarisOutputPlugin.hxx"
-#include "plugins/WinmmOutputPlugin.hxx"
-
-#include <string.h>
-
-const AudioOutputPlugin *const audio_output_plugins[] = {
-#ifdef HAVE_SHOUT
-	&shout_output_plugin,
-#endif
-	&null_output_plugin,
-#ifdef HAVE_FIFO
-	&fifo_output_plugin,
-#endif
-#ifdef ENABLE_PIPE_OUTPUT
-	&pipe_output_plugin,
-#endif
-#ifdef HAVE_ALSA
-	&alsa_output_plugin,
-#endif
-#ifdef HAVE_ROAR
-	&roar_output_plugin,
-#endif
-#ifdef HAVE_AO
-	&ao_output_plugin,
-#endif
-#ifdef HAVE_OSS
-	&oss_output_plugin,
-#endif
-#ifdef HAVE_OPENAL
-	&openal_output_plugin,
-#endif
-#ifdef HAVE_OSX
-	&osx_output_plugin,
-#endif
-#ifdef ENABLE_SOLARIS_OUTPUT
-	&solaris_output_plugin,
-#endif
-#ifdef HAVE_PULSE
-	&pulse_output_plugin,
-#endif
-#ifdef HAVE_JACK
-	&jack_output_plugin,
-#endif
-#ifdef ENABLE_HTTPD_OUTPUT
-	&httpd_output_plugin,
-#endif
-#ifdef ENABLE_RECORDER_OUTPUT
-	&recorder_output_plugin,
-#endif
-#ifdef ENABLE_WINMM_OUTPUT
-	&winmm_output_plugin,
-#endif
-	nullptr
-};
-
-const AudioOutputPlugin *
-AudioOutputPlugin_get(const char *name)
-{
-	audio_output_plugins_for_each(plugin)
-		if (strcmp(plugin->name, name) == 0)
-			return plugin;
-
-	return nullptr;
-}
diff --git a/src/output/OutputList.hxx b/src/output/OutputList.hxx
deleted file mode 100644
index bc9c1ae2b..000000000
--- a/src/output/OutputList.hxx
+++ /dev/null
@@ -1,35 +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.
- */
-
-#ifndef MPD_OUTPUT_LIST_HXX
-#define MPD_OUTPUT_LIST_HXX
-
-struct AudioOutputPlugin;
-
-extern const AudioOutputPlugin *const audio_output_plugins[];
-
-const AudioOutputPlugin *
-AudioOutputPlugin_get(const char *name);
-
-#define audio_output_plugins_for_each(plugin) \
-	for (const AudioOutputPlugin *plugin, \
-		*const*output_plugin_iterator = &audio_output_plugins[0]; \
-		(plugin = *output_plugin_iterator) != nullptr; ++output_plugin_iterator)
-
-#endif
diff --git a/src/output/OutputPlugin.cxx b/src/output/OutputPlugin.cxx
index 1c8d3bb48..45b979d3c 100644
--- a/src/output/OutputPlugin.cxx
+++ b/src/output/OutputPlugin.cxx
@@ -19,7 +19,7 @@
 
 #include "config.h"
 #include "OutputPlugin.hxx"
-#include "OutputInternal.hxx"
+#include "Internal.hxx"
 
 AudioOutput *
 ao_plugin_init(const AudioOutputPlugin *plugin,
diff --git a/src/output/OutputPrint.cxx b/src/output/OutputPrint.cxx
index b9fb79607..414a86e32 100644
--- a/src/output/OutputPrint.cxx
+++ b/src/output/OutputPrint.cxx
@@ -25,7 +25,7 @@
 #include "config.h"
 #include "OutputPrint.hxx"
 #include "MultipleOutputs.hxx"
-#include "OutputInternal.hxx"
+#include "Internal.hxx"
 #include "client/Client.hxx"
 
 void
diff --git a/src/output/OutputState.cxx b/src/output/OutputState.cxx
index f717e3cbb..7f3a8da7b 100644
--- a/src/output/OutputState.cxx
+++ b/src/output/OutputState.cxx
@@ -25,8 +25,8 @@
 #include "config.h"
 #include "OutputState.hxx"
 #include "MultipleOutputs.hxx"
-#include "OutputInternal.hxx"
-#include "OutputError.hxx"
+#include "Internal.hxx"
+#include "Domain.hxx"
 #include "Log.hxx"
 #include "util/StringUtil.hxx"
 
diff --git a/src/output/OutputThread.cxx b/src/output/OutputThread.cxx
index f2ea048df..449198c8b 100644
--- a/src/output/OutputThread.cxx
+++ b/src/output/OutputThread.cxx
@@ -19,9 +19,9 @@
 
 #include "config.h"
 #include "OutputThread.hxx"
-#include "OutputInternal.hxx"
+#include "Internal.hxx"
 #include "OutputAPI.hxx"
-#include "OutputError.hxx"
+#include "Domain.hxx"
 #include "pcm/PcmMix.hxx"
 #include "notify.hxx"
 #include "filter/FilterInternal.hxx"
diff --git a/src/output/Registry.cxx b/src/output/Registry.cxx
new file mode 100644
index 000000000..6ce96fe57
--- /dev/null
+++ b/src/output/Registry.cxx
@@ -0,0 +1,100 @@
+/*
+ * 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 "Registry.hxx"
+#include "OutputAPI.hxx"
+#include "plugins/AlsaOutputPlugin.hxx"
+#include "plugins/AoOutputPlugin.hxx"
+#include "plugins/FifoOutputPlugin.hxx"
+#include "plugins/HttpdOutputPlugin.hxx"
+#include "plugins/JackOutputPlugin.hxx"
+#include "plugins/NullOutputPlugin.hxx"
+#include "plugins/OpenALOutputPlugin.hxx"
+#include "plugins/OssOutputPlugin.hxx"
+#include "plugins/OSXOutputPlugin.hxx"
+#include "plugins/PipeOutputPlugin.hxx"
+#include "plugins/PulseOutputPlugin.hxx"
+#include "plugins/RecorderOutputPlugin.hxx"
+#include "plugins/RoarOutputPlugin.hxx"
+#include "plugins/ShoutOutputPlugin.hxx"
+#include "plugins/SolarisOutputPlugin.hxx"
+#include "plugins/WinmmOutputPlugin.hxx"
+
+#include <string.h>
+
+const AudioOutputPlugin *const audio_output_plugins[] = {
+#ifdef HAVE_SHOUT
+	&shout_output_plugin,
+#endif
+	&null_output_plugin,
+#ifdef HAVE_FIFO
+	&fifo_output_plugin,
+#endif
+#ifdef ENABLE_PIPE_OUTPUT
+	&pipe_output_plugin,
+#endif
+#ifdef HAVE_ALSA
+	&alsa_output_plugin,
+#endif
+#ifdef HAVE_ROAR
+	&roar_output_plugin,
+#endif
+#ifdef HAVE_AO
+	&ao_output_plugin,
+#endif
+#ifdef HAVE_OSS
+	&oss_output_plugin,
+#endif
+#ifdef HAVE_OPENAL
+	&openal_output_plugin,
+#endif
+#ifdef HAVE_OSX
+	&osx_output_plugin,
+#endif
+#ifdef ENABLE_SOLARIS_OUTPUT
+	&solaris_output_plugin,
+#endif
+#ifdef HAVE_PULSE
+	&pulse_output_plugin,
+#endif
+#ifdef HAVE_JACK
+	&jack_output_plugin,
+#endif
+#ifdef ENABLE_HTTPD_OUTPUT
+	&httpd_output_plugin,
+#endif
+#ifdef ENABLE_RECORDER_OUTPUT
+	&recorder_output_plugin,
+#endif
+#ifdef ENABLE_WINMM_OUTPUT
+	&winmm_output_plugin,
+#endif
+	nullptr
+};
+
+const AudioOutputPlugin *
+AudioOutputPlugin_get(const char *name)
+{
+	audio_output_plugins_for_each(plugin)
+		if (strcmp(plugin->name, name) == 0)
+			return plugin;
+
+	return nullptr;
+}
diff --git a/src/output/Registry.hxx b/src/output/Registry.hxx
new file mode 100644
index 000000000..bc9c1ae2b
--- /dev/null
+++ b/src/output/Registry.hxx
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef MPD_OUTPUT_LIST_HXX
+#define MPD_OUTPUT_LIST_HXX
+
+struct AudioOutputPlugin;
+
+extern const AudioOutputPlugin *const audio_output_plugins[];
+
+const AudioOutputPlugin *
+AudioOutputPlugin_get(const char *name);
+
+#define audio_output_plugins_for_each(plugin) \
+	for (const AudioOutputPlugin *plugin, \
+		*const*output_plugin_iterator = &audio_output_plugins[0]; \
+		(plugin = *output_plugin_iterator) != nullptr; ++output_plugin_iterator)
+
+#endif
diff --git a/src/output/plugins/HttpdInternal.hxx b/src/output/plugins/HttpdInternal.hxx
index 1d48c3ad5..ad728949f 100644
--- a/src/output/plugins/HttpdInternal.hxx
+++ b/src/output/plugins/HttpdInternal.hxx
@@ -25,7 +25,7 @@
 #ifndef MPD_OUTPUT_HTTPD_INTERNAL_H
 #define MPD_OUTPUT_HTTPD_INTERNAL_H
 
-#include "../OutputInternal.hxx"
+#include "../Internal.hxx"
 #include "Timer.hxx"
 #include "thread/Mutex.hxx"
 #include "event/ServerSocket.hxx"
-- 
cgit v1.2.3