aboutsummaryrefslogtreecommitdiffstats
path: root/src/output
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/output/AlsaOutputPlugin.cxx855
-rw-r--r--src/output/AlsaOutputPlugin.hxx25
-rw-r--r--src/output/HttpdClient.cxx444
-rw-r--r--src/output/HttpdClient.hxx186
-rw-r--r--src/output/HttpdInternal.hxx204
-rw-r--r--src/output/HttpdOutputPlugin.cxx570
-rw-r--r--src/output/HttpdOutputPlugin.hxx25
-rw-r--r--src/output/NullOutputPlugin.cxx136
-rw-r--r--src/output/NullOutputPlugin.hxx25
-rw-r--r--src/output/OSXOutputPlugin.cxx437
-rw-r--r--src/output/OSXOutputPlugin.hxx25
-rw-r--r--src/output/OssOutputPlugin.cxx793
-rw-r--r--src/output/OssOutputPlugin.hxx25
-rw-r--r--src/output/RoarOutputPlugin.cxx393
-rw-r--r--src/output/RoarOutputPlugin.hxx33
-rw-r--r--src/output/alsa_output_plugin.c819
-rw-r--r--src/output/alsa_output_plugin.h25
-rw-r--r--src/output/fifo_output_plugin.c1
-rw-r--r--src/output/httpd_client.c764
-rw-r--r--src/output/httpd_client.h71
-rw-r--r--src/output/httpd_internal.h138
-rw-r--r--src/output/httpd_output_plugin.c623
-rw-r--r--src/output/httpd_output_plugin.h25
-rw-r--r--src/output/null_output_plugin.c129
-rw-r--r--src/output/null_output_plugin.h25
-rw-r--r--src/output/oss_output_plugin.c788
-rw-r--r--src/output/oss_output_plugin.h25
-rw-r--r--src/output/osx_output_plugin.c438
-rw-r--r--src/output/osx_output_plugin.h25
-rw-r--r--src/output/pulse_output_plugin.c2
-rw-r--r--src/output/pulse_output_plugin.h12
-rw-r--r--src/output/roar_output_plugin.c401
-rw-r--r--src/output/roar_output_plugin.h35
-rw-r--r--src/output/shout_output_plugin.c104
-rw-r--r--src/output/winmm_output_plugin.c1
-rw-r--r--src/output/winmm_output_plugin.h1
-rw-r--r--src/output_all.c590
-rw-r--r--src/output_all.h166
-rw-r--r--src/output_command.c87
-rw-r--r--src/output_command.h46
-rw-r--r--src/output_control.c336
-rw-r--r--src/output_control.h94
-rw-r--r--src/output_finish.c60
-rw-r--r--src/output_init.c332
-rw-r--r--src/output_internal.h29
-rw-r--r--src/output_list.c106
-rw-r--r--src/output_list.h33
-rw-r--r--src/output_plugin.c109
-rw-r--r--src/output_plugin.h15
-rw-r--r--src/output_print.c45
-rw-r--r--src/output_print.h33
-rw-r--r--src/output_state.c91
-rw-r--r--src/output_state.h45
-rw-r--r--src/output_thread.c685
-rw-r--r--src/output_thread.h27
55 files changed, 4280 insertions, 7277 deletions
diff --git a/src/output/AlsaOutputPlugin.cxx b/src/output/AlsaOutputPlugin.cxx
new file mode 100644
index 000000000..1badeb63d
--- /dev/null
+++ b/src/output/AlsaOutputPlugin.cxx
@@ -0,0 +1,855 @@
+/*
+ * Copyright (C) 2003-2013 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 "AlsaOutputPlugin.hxx"
+#include "output_api.h"
+#include "mixer_list.h"
+#include "pcm_export.h"
+
+#include <glib.h>
+#include <alsa/asoundlib.h>
+
+#include <string>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "alsa"
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+
+static const char default_device[] = "default";
+
+enum {
+ MPD_ALSA_BUFFER_TIME_US = 500000,
+};
+
+#define MPD_ALSA_RETRY_NR 5
+
+typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
+ snd_pcm_uframes_t size);
+
+struct AlsaOutput {
+ struct audio_output base;
+
+ struct pcm_export_state pcm_export;
+
+ /**
+ * The configured name of the ALSA device; empty for the
+ * default device
+ */
+ std::string device;
+
+ /** use memory mapped I/O? */
+ bool use_mmap;
+
+ /**
+ * Enable DSD over USB according to the dCS suggested
+ * standard?
+ *
+ * @see http://www.dcsltd.co.uk/page/assets/DSDoverUSB.pdf
+ */
+ bool dsd_usb;
+
+ /** libasound's buffer_time setting (in microseconds) */
+ unsigned int buffer_time;
+
+ /** libasound's period_time setting (in microseconds) */
+ unsigned int period_time;
+
+ /** the mode flags passed to snd_pcm_open */
+ int mode;
+
+ /** the libasound PCM device handle */
+ snd_pcm_t *pcm;
+
+ /**
+ * a pointer to the libasound writei() function, which is
+ * snd_pcm_writei() or snd_pcm_mmap_writei(), depending on the
+ * use_mmap configuration
+ */
+ alsa_writei_t *writei;
+
+ /**
+ * The size of one audio frame passed to method play().
+ */
+ size_t in_frame_size;
+
+ /**
+ * The size of one audio frame passed to libasound.
+ */
+ size_t out_frame_size;
+
+ /**
+ * The size of one period, in number of frames.
+ */
+ snd_pcm_uframes_t period_frames;
+
+ /**
+ * The number of frames written in the current period.
+ */
+ snd_pcm_uframes_t period_position;
+
+ /**
+ * This buffer gets allocated after opening the ALSA device.
+ * It contains silence samples, enough to fill one period (see
+ * #period_frames).
+ */
+ void *silence;
+
+ AlsaOutput():mode(0), writei(snd_pcm_writei) {
+ }
+
+ bool Init(const config_param *param, GError **error_r) {
+ return ao_base_init(&base, &alsa_output_plugin,
+ param, error_r);
+ }
+
+ void Deinit() {
+ ao_base_finish(&base);
+ }
+};
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+alsa_output_quark(void)
+{
+ return g_quark_from_static_string("alsa_output");
+}
+
+static const char *
+alsa_device(const AlsaOutput *ad)
+{
+ return ad->device.empty() ? default_device : ad->device.c_str();
+}
+
+static void
+alsa_configure(AlsaOutput *ad, const struct config_param *param)
+{
+ ad->device = config_get_block_string(param, "device", "");
+
+ ad->use_mmap = config_get_block_bool(param, "use_mmap", false);
+
+ ad->dsd_usb = config_get_block_bool(param, "dsd_usb", false);
+
+ ad->buffer_time = config_get_block_unsigned(param, "buffer_time",
+ MPD_ALSA_BUFFER_TIME_US);
+ ad->period_time = config_get_block_unsigned(param, "period_time", 0);
+
+#ifdef SND_PCM_NO_AUTO_RESAMPLE
+ if (!config_get_block_bool(param, "auto_resample", true))
+ ad->mode |= SND_PCM_NO_AUTO_RESAMPLE;
+#endif
+
+#ifdef SND_PCM_NO_AUTO_CHANNELS
+ if (!config_get_block_bool(param, "auto_channels", true))
+ ad->mode |= SND_PCM_NO_AUTO_CHANNELS;
+#endif
+
+#ifdef SND_PCM_NO_AUTO_FORMAT
+ if (!config_get_block_bool(param, "auto_format", true))
+ ad->mode |= SND_PCM_NO_AUTO_FORMAT;
+#endif
+}
+
+static struct audio_output *
+alsa_init(const struct config_param *param, GError **error_r)
+{
+ AlsaOutput *ad = new AlsaOutput();
+
+ if (!ad->Init(param, error_r)) {
+ delete ad;
+ return NULL;
+ }
+
+ alsa_configure(ad, param);
+
+ return &ad->base;
+}
+
+static void
+alsa_finish(struct audio_output *ao)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ ad->Deinit();
+ delete ad;
+
+ /* free libasound's config cache */
+ snd_config_update_free_global();
+}
+
+static bool
+alsa_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ pcm_export_init(&ad->pcm_export);
+ return true;
+}
+
+static void
+alsa_output_disable(struct audio_output *ao)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ pcm_export_deinit(&ad->pcm_export);
+}
+
+static bool
+alsa_test_default_device(void)
+{
+ snd_pcm_t *handle;
+
+ int ret = snd_pcm_open(&handle, default_device,
+ SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ if (ret) {
+ g_message("Error opening default ALSA device: %s\n",
+ snd_strerror(-ret));
+ return false;
+ } else
+ snd_pcm_close(handle);
+
+ return true;
+}
+
+static snd_pcm_format_t
+get_bitformat(enum sample_format sample_format)
+{
+ switch (sample_format) {
+ case SAMPLE_FORMAT_UNDEFINED:
+ case SAMPLE_FORMAT_DSD:
+ return SND_PCM_FORMAT_UNKNOWN;
+
+ case SAMPLE_FORMAT_S8:
+ return SND_PCM_FORMAT_S8;
+
+ case SAMPLE_FORMAT_S16:
+ return SND_PCM_FORMAT_S16;
+
+ case SAMPLE_FORMAT_S24_P32:
+ return SND_PCM_FORMAT_S24;
+
+ case SAMPLE_FORMAT_S32:
+ return SND_PCM_FORMAT_S32;
+
+ case SAMPLE_FORMAT_FLOAT:
+ return SND_PCM_FORMAT_FLOAT;
+ }
+
+ assert(false);
+ return SND_PCM_FORMAT_UNKNOWN;
+}
+
+static snd_pcm_format_t
+byteswap_bitformat(snd_pcm_format_t fmt)
+{
+ switch(fmt) {
+ case SND_PCM_FORMAT_S16_LE: return SND_PCM_FORMAT_S16_BE;
+ case SND_PCM_FORMAT_S24_LE: return SND_PCM_FORMAT_S24_BE;
+ case SND_PCM_FORMAT_S32_LE: return SND_PCM_FORMAT_S32_BE;
+ case SND_PCM_FORMAT_S16_BE: return SND_PCM_FORMAT_S16_LE;
+ case SND_PCM_FORMAT_S24_BE: return SND_PCM_FORMAT_S24_LE;
+
+ case SND_PCM_FORMAT_S24_3BE:
+ return SND_PCM_FORMAT_S24_3LE;
+
+ case SND_PCM_FORMAT_S24_3LE:
+ return SND_PCM_FORMAT_S24_3BE;
+
+ case SND_PCM_FORMAT_S32_BE: return SND_PCM_FORMAT_S32_LE;
+ default: return SND_PCM_FORMAT_UNKNOWN;
+ }
+}
+
+static snd_pcm_format_t
+alsa_to_packed_format(snd_pcm_format_t fmt)
+{
+ switch (fmt) {
+ case SND_PCM_FORMAT_S24_LE:
+ return SND_PCM_FORMAT_S24_3LE;
+
+ case SND_PCM_FORMAT_S24_BE:
+ return SND_PCM_FORMAT_S24_3BE;
+
+ default:
+ return SND_PCM_FORMAT_UNKNOWN;
+ }
+}
+
+static int
+alsa_try_format_or_packed(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
+ snd_pcm_format_t fmt, bool *packed_r)
+{
+ int err = snd_pcm_hw_params_set_format(pcm, hwparams, fmt);
+ if (err == 0)
+ *packed_r = false;
+
+ if (err != -EINVAL)
+ return err;
+
+ fmt = alsa_to_packed_format(fmt);
+ if (fmt == SND_PCM_FORMAT_UNKNOWN)
+ return -EINVAL;
+
+ err = snd_pcm_hw_params_set_format(pcm, hwparams, fmt);
+ if (err == 0)
+ *packed_r = true;
+
+ return err;
+}
+
+/**
+ * Attempts to configure the specified sample format, and tries the
+ * reversed host byte order if was not supported.
+ */
+static int
+alsa_output_try_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
+ enum sample_format sample_format,
+ bool *packed_r, bool *reverse_endian_r)
+{
+ snd_pcm_format_t alsa_format = get_bitformat(sample_format);
+ if (alsa_format == SND_PCM_FORMAT_UNKNOWN)
+ return -EINVAL;
+
+ int err = alsa_try_format_or_packed(pcm, hwparams, alsa_format,
+ packed_r);
+ if (err == 0)
+ *reverse_endian_r = false;
+
+ if (err != -EINVAL)
+ return err;
+
+ alsa_format = byteswap_bitformat(alsa_format);
+ if (alsa_format == SND_PCM_FORMAT_UNKNOWN)
+ return -EINVAL;
+
+ err = alsa_try_format_or_packed(pcm, hwparams, alsa_format, packed_r);
+ if (err == 0)
+ *reverse_endian_r = true;
+
+ return err;
+}
+
+/**
+ * Configure a sample format, and probe other formats if that fails.
+ */
+static int
+alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
+ struct audio_format *audio_format,
+ bool *packed_r, bool *reverse_endian_r)
+{
+ /* try the input format first */
+
+ int err = alsa_output_try_format(pcm, hwparams,
+ sample_format(audio_format->format),
+ packed_r, reverse_endian_r);
+
+ /* if unsupported by the hardware, try other formats */
+
+ static const enum sample_format probe_formats[] = {
+ SAMPLE_FORMAT_S24_P32,
+ SAMPLE_FORMAT_S32,
+ SAMPLE_FORMAT_S16,
+ SAMPLE_FORMAT_S8,
+ SAMPLE_FORMAT_UNDEFINED,
+ };
+
+ for (unsigned i = 0;
+ err == -EINVAL && probe_formats[i] != SAMPLE_FORMAT_UNDEFINED;
+ ++i) {
+ const enum sample_format mpd_format = probe_formats[i];
+ if (mpd_format == audio_format->format)
+ continue;
+
+ err = alsa_output_try_format(pcm, hwparams, mpd_format,
+ packed_r, reverse_endian_r);
+ if (err == 0)
+ audio_format->format = mpd_format;
+ }
+
+ return err;
+}
+
+/**
+ * Set up the snd_pcm_t object which was opened by the caller. Set up
+ * the configured settings and the audio format.
+ */
+static bool
+alsa_setup(AlsaOutput *ad, struct audio_format *audio_format,
+ bool *packed_r, bool *reverse_endian_r, GError **error)
+{
+ unsigned int sample_rate = audio_format->sample_rate;
+ unsigned int channels = audio_format->channels;
+ int err;
+ const char *cmd = NULL;
+ int retry = MPD_ALSA_RETRY_NR;
+ unsigned int period_time, period_time_ro;
+ unsigned int buffer_time;
+
+ period_time_ro = period_time = ad->period_time;
+configure_hw:
+ /* configure HW params */
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_hw_params_alloca(&hwparams);
+ cmd = "snd_pcm_hw_params_any";
+ err = snd_pcm_hw_params_any(ad->pcm, hwparams);
+ if (err < 0)
+ goto error;
+
+ if (ad->use_mmap) {
+ err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
+ SND_PCM_ACCESS_MMAP_INTERLEAVED);
+ if (err < 0) {
+ g_warning("Cannot set mmap'ed mode on ALSA device \"%s\": %s\n",
+ alsa_device(ad), snd_strerror(-err));
+ g_warning("Falling back to direct write mode\n");
+ ad->use_mmap = false;
+ } else
+ ad->writei = snd_pcm_mmap_writei;
+ }
+
+ if (!ad->use_mmap) {
+ cmd = "snd_pcm_hw_params_set_access";
+ err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (err < 0)
+ goto error;
+ ad->writei = snd_pcm_writei;
+ }
+
+ err = alsa_output_setup_format(ad->pcm, hwparams, audio_format,
+ packed_r, reverse_endian_r);
+ if (err < 0) {
+ g_set_error(error, alsa_output_quark(), err,
+ "ALSA device \"%s\" does not support format %s: %s",
+ alsa_device(ad),
+ sample_format_to_string(sample_format(audio_format->format)),
+ snd_strerror(-err));
+ return false;
+ }
+
+ snd_pcm_format_t format;
+ if (snd_pcm_hw_params_get_format(hwparams, &format) == 0)
+ g_debug("format=%s (%s)", snd_pcm_format_name(format),
+ snd_pcm_format_description(format));
+
+ err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams,
+ &channels);
+ if (err < 0) {
+ g_set_error(error, alsa_output_quark(), err,
+ "ALSA device \"%s\" does not support %i channels: %s",
+ alsa_device(ad), (int)audio_format->channels,
+ snd_strerror(-err));
+ return false;
+ }
+ audio_format->channels = (int8_t)channels;
+
+ err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams,
+ &sample_rate, NULL);
+ if (err < 0 || sample_rate == 0) {
+ g_set_error(error, alsa_output_quark(), err,
+ "ALSA device \"%s\" does not support %u Hz audio",
+ alsa_device(ad), audio_format->sample_rate);
+ return false;
+ }
+ audio_format->sample_rate = sample_rate;
+
+ snd_pcm_uframes_t buffer_size_min, buffer_size_max;
+ snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
+ snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
+ unsigned buffer_time_min, buffer_time_max;
+ snd_pcm_hw_params_get_buffer_time_min(hwparams, &buffer_time_min, 0);
+ snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time_max, 0);
+ g_debug("buffer: size=%u..%u time=%u..%u",
+ (unsigned)buffer_size_min, (unsigned)buffer_size_max,
+ buffer_time_min, buffer_time_max);
+
+ snd_pcm_uframes_t period_size_min, period_size_max;
+ snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, 0);
+ snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, 0);
+ unsigned period_time_min, period_time_max;
+ snd_pcm_hw_params_get_period_time_min(hwparams, &period_time_min, 0);
+ snd_pcm_hw_params_get_period_time_max(hwparams, &period_time_max, 0);
+ g_debug("period: size=%u..%u time=%u..%u",
+ (unsigned)period_size_min, (unsigned)period_size_max,
+ period_time_min, period_time_max);
+
+ if (ad->buffer_time > 0) {
+ buffer_time = ad->buffer_time;
+ cmd = "snd_pcm_hw_params_set_buffer_time_near";
+ err = snd_pcm_hw_params_set_buffer_time_near(ad->pcm, hwparams,
+ &buffer_time, NULL);
+ if (err < 0)
+ goto error;
+ } else {
+ err = snd_pcm_hw_params_get_buffer_time(hwparams, &buffer_time,
+ NULL);
+ if (err < 0)
+ buffer_time = 0;
+ }
+
+ if (period_time_ro == 0 && buffer_time >= 10000) {
+ period_time_ro = period_time = buffer_time / 4;
+
+ g_debug("default period_time = buffer_time/4 = %u/4 = %u",
+ buffer_time, period_time);
+ }
+
+ if (period_time_ro > 0) {
+ period_time = period_time_ro;
+ cmd = "snd_pcm_hw_params_set_period_time_near";
+ err = snd_pcm_hw_params_set_period_time_near(ad->pcm, hwparams,
+ &period_time, NULL);
+ if (err < 0)
+ goto error;
+ }
+
+ cmd = "snd_pcm_hw_params";
+ err = snd_pcm_hw_params(ad->pcm, hwparams);
+ if (err == -EPIPE && --retry > 0 && period_time_ro > 0) {
+ period_time_ro = period_time_ro >> 1;
+ goto configure_hw;
+ } else if (err < 0)
+ goto error;
+ if (retry != MPD_ALSA_RETRY_NR)
+ g_debug("ALSA period_time set to %d\n", period_time);
+
+ snd_pcm_uframes_t alsa_buffer_size;
+ cmd = "snd_pcm_hw_params_get_buffer_size";
+ err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
+ if (err < 0)
+ goto error;
+
+ snd_pcm_uframes_t alsa_period_size;
+ cmd = "snd_pcm_hw_params_get_period_size";
+ err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
+ NULL);
+ if (err < 0)
+ goto error;
+
+ /* configure SW params */
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_sw_params_alloca(&swparams);
+
+ cmd = "snd_pcm_sw_params_current";
+ err = snd_pcm_sw_params_current(ad->pcm, swparams);
+ if (err < 0)
+ goto error;
+
+ cmd = "snd_pcm_sw_params_set_start_threshold";
+ err = snd_pcm_sw_params_set_start_threshold(ad->pcm, swparams,
+ alsa_buffer_size -
+ alsa_period_size);
+ if (err < 0)
+ goto error;
+
+ cmd = "snd_pcm_sw_params_set_avail_min";
+ err = snd_pcm_sw_params_set_avail_min(ad->pcm, swparams,
+ alsa_period_size);
+ if (err < 0)
+ goto error;
+
+ cmd = "snd_pcm_sw_params";
+ err = snd_pcm_sw_params(ad->pcm, swparams);
+ if (err < 0)
+ goto error;
+
+ g_debug("buffer_size=%u period_size=%u",
+ (unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
+
+ if (alsa_period_size == 0)
+ /* this works around a SIGFPE bug that occurred when
+ an ALSA driver indicated period_size==0; this
+ caused a division by zero in alsa_play(). By using
+ the fallback "1", we make sure that this won't
+ happen again. */
+ alsa_period_size = 1;
+
+ ad->period_frames = alsa_period_size;
+ ad->period_position = 0;
+
+ ad->silence = g_malloc(snd_pcm_frames_to_bytes(ad->pcm,
+ alsa_period_size));
+ snd_pcm_format_set_silence(format, ad->silence,
+ alsa_period_size * channels);
+
+ return true;
+
+error:
+ g_set_error(error, alsa_output_quark(), err,
+ "Error opening ALSA device \"%s\" (%s): %s",
+ alsa_device(ad), cmd, snd_strerror(-err));
+ return false;
+}
+
+static bool
+alsa_setup_dsd(AlsaOutput *ad, struct audio_format *audio_format,
+ bool *shift8_r, bool *packed_r, bool *reverse_endian_r,
+ GError **error_r)
+{
+ assert(ad->dsd_usb);
+ assert(audio_format->format == SAMPLE_FORMAT_DSD);
+
+ /* pass 24 bit to alsa_setup() */
+
+ struct audio_format usb_format = *audio_format;
+ usb_format.format = SAMPLE_FORMAT_S24_P32;
+ usb_format.sample_rate /= 2;
+
+ const struct audio_format check = usb_format;
+
+ if (!alsa_setup(ad, &usb_format, packed_r, reverse_endian_r, error_r))
+ return false;
+
+ /* if the device allows only 32 bit, shift all DSD-over-USB
+ samples left by 8 bit and leave the lower 8 bit cleared;
+ the DSD-over-USB documentation does not specify whether
+ this is legal, but there is anecdotical evidence that this
+ is possible (and the only option for some devices) */
+ *shift8_r = usb_format.format == SAMPLE_FORMAT_S32;
+ if (usb_format.format == SAMPLE_FORMAT_S32)
+ usb_format.format = SAMPLE_FORMAT_S24_P32;
+
+ if (!audio_format_equals(&usb_format, &check)) {
+ /* no bit-perfect playback, which is required
+ for DSD over USB */
+ g_set_error(error_r, alsa_output_quark(), 0,
+ "Failed to configure DSD-over-USB on ALSA device \"%s\"",
+ alsa_device(ad));
+ g_free(ad->silence);
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+alsa_setup_or_dsd(AlsaOutput *ad, struct audio_format *audio_format,
+ GError **error_r)
+{
+ bool shift8 = false, packed, reverse_endian;
+
+ const bool dsd_usb = ad->dsd_usb &&
+ audio_format->format == SAMPLE_FORMAT_DSD;
+ const bool success = dsd_usb
+ ? alsa_setup_dsd(ad, audio_format,
+ &shift8, &packed, &reverse_endian,
+ error_r)
+ : alsa_setup(ad, audio_format, &packed, &reverse_endian,
+ error_r);
+ if (!success)
+ return false;
+
+ pcm_export_open(&ad->pcm_export,
+ sample_format(audio_format->format),
+ audio_format->channels,
+ dsd_usb, shift8, packed, reverse_endian);
+ return true;
+}
+
+static bool
+alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ int err = snd_pcm_open(&ad->pcm, alsa_device(ad),
+ SND_PCM_STREAM_PLAYBACK, ad->mode);
+ if (err < 0) {
+ g_set_error(error, alsa_output_quark(), err,
+ "Failed to open ALSA device \"%s\": %s",
+ alsa_device(ad), snd_strerror(err));
+ return false;
+ }
+
+ g_debug("opened %s type=%s", snd_pcm_name(ad->pcm),
+ snd_pcm_type_name(snd_pcm_type(ad->pcm)));
+
+ if (!alsa_setup_or_dsd(ad, audio_format, error)) {
+ snd_pcm_close(ad->pcm);
+ return false;
+ }
+
+ ad->in_frame_size = audio_format_frame_size(audio_format);
+ ad->out_frame_size = pcm_export_frame_size(&ad->pcm_export,
+ audio_format);
+
+ return true;
+}
+
+/**
+ * Write silence to the ALSA device.
+ */
+static void
+alsa_write_silence(AlsaOutput *ad, snd_pcm_uframes_t nframes)
+{
+ ad->writei(ad->pcm, ad->silence, nframes);
+}
+
+static int
+alsa_recover(AlsaOutput *ad, int err)
+{
+ if (err == -EPIPE) {
+ g_debug("Underrun on ALSA device \"%s\"\n", alsa_device(ad));
+ } else if (err == -ESTRPIPE) {
+ g_debug("ALSA device \"%s\" was suspended\n", alsa_device(ad));
+ }
+
+ switch (snd_pcm_state(ad->pcm)) {
+ case SND_PCM_STATE_PAUSED:
+ err = snd_pcm_pause(ad->pcm, /* disable */ 0);
+ break;
+ case SND_PCM_STATE_SUSPENDED:
+ err = snd_pcm_resume(ad->pcm);
+ if (err == -EAGAIN)
+ return 0;
+ /* fall-through to snd_pcm_prepare: */
+ case SND_PCM_STATE_SETUP:
+ case SND_PCM_STATE_XRUN:
+ ad->period_position = 0;
+ err = snd_pcm_prepare(ad->pcm);
+
+ if (err == 0) {
+ /* this works around a driver bug observed on
+ the Raspberry Pi: after snd_pcm_drop(), the
+ whole ring buffer must be invalidated, but
+ the snd_pcm_prepare() call above makes the
+ driver play random data that just happens
+ to be still in the buffer; by adding and
+ cancelling some silence, this bug does not
+ occur */
+ alsa_write_silence(ad, ad->period_frames);
+
+ /* cancel the silence data right away to avoid
+ increasing latency; even though this
+ function call invalidates the portion of
+ silence, the driver seems to avoid the
+ bug */
+ snd_pcm_reset(ad->pcm);
+ }
+
+ break;
+ case SND_PCM_STATE_DISCONNECTED:
+ break;
+ /* this is no error, so just keep running */
+ case SND_PCM_STATE_RUNNING:
+ err = 0;
+ break;
+ default:
+ /* unknown state, do nothing */
+ break;
+ }
+
+ return err;
+}
+
+static void
+alsa_drain(struct audio_output *ao)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
+ return;
+
+ if (ad->period_position > 0) {
+ /* generate some silence to finish the partial
+ period */
+ snd_pcm_uframes_t nframes =
+ ad->period_frames - ad->period_position;
+ alsa_write_silence(ad, nframes);
+ }
+
+ snd_pcm_drain(ad->pcm);
+
+ ad->period_position = 0;
+}
+
+static void
+alsa_cancel(struct audio_output *ao)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ ad->period_position = 0;
+
+ snd_pcm_drop(ad->pcm);
+}
+
+static void
+alsa_close(struct audio_output *ao)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ snd_pcm_close(ad->pcm);
+ g_free(ad->silence);
+}
+
+static size_t
+alsa_play(struct audio_output *ao, const void *chunk, size_t size,
+ GError **error)
+{
+ AlsaOutput *ad = (AlsaOutput *)ao;
+
+ assert(size % ad->in_frame_size == 0);
+
+ chunk = pcm_export(&ad->pcm_export, chunk, size, &size);
+
+ assert(size % ad->out_frame_size == 0);
+
+ size /= ad->out_frame_size;
+
+ while (true) {
+ snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size);
+ if (ret > 0) {
+ ad->period_position = (ad->period_position + ret)
+ % ad->period_frames;
+
+ size_t bytes_written = ret * ad->out_frame_size;
+ return pcm_export_source_size(&ad->pcm_export,
+ bytes_written);
+ }
+
+ if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
+ alsa_recover(ad, ret) < 0) {
+ g_set_error(error, alsa_output_quark(), errno,
+ "%s", snd_strerror(-errno));
+ return 0;
+ }
+ }
+}
+
+const struct audio_output_plugin alsa_output_plugin = {
+ "alsa",
+ alsa_test_default_device,
+ alsa_init,
+ alsa_finish,
+ alsa_output_enable,
+ alsa_output_disable,
+ alsa_open,
+ alsa_close,
+ nullptr,
+ nullptr,
+ alsa_play,
+ alsa_drain,
+ alsa_cancel,
+ nullptr,
+
+ &alsa_mixer_plugin,
+};
diff --git a/src/output/AlsaOutputPlugin.hxx b/src/output/AlsaOutputPlugin.hxx
new file mode 100644
index 000000000..dc7e639a8
--- /dev/null
+++ b/src/output/AlsaOutputPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_ALSA_OUTPUT_PLUGIN_HXX
+#define MPD_ALSA_OUTPUT_PLUGIN_HXX
+
+extern const struct audio_output_plugin alsa_output_plugin;
+
+#endif
diff --git a/src/output/HttpdClient.cxx b/src/output/HttpdClient.cxx
new file mode 100644
index 000000000..0a00ee2f9
--- /dev/null
+++ b/src/output/HttpdClient.cxx
@@ -0,0 +1,444 @@
+/*
+ * 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 "HttpdClient.hxx"
+#include "HttpdInternal.hxx"
+#include "util/fifo_buffer.h"
+#include "Page.hxx"
+#include "IcyMetaDataServer.hxx"
+#include "SocketError.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "httpd_output"
+
+HttpdClient::~HttpdClient()
+{
+ if (state == RESPONSE) {
+ if (current_page != nullptr)
+ current_page->Unref();
+
+ for (auto page : pages)
+ page->Unref();
+ }
+
+ if (metadata)
+ metadata->Unref();
+}
+
+void
+HttpdClient::Close()
+{
+ httpd->RemoveClient(*this);
+}
+
+void
+HttpdClient::LockClose()
+{
+ const ScopeLock protect(httpd->mutex);
+ Close();
+}
+
+void
+HttpdClient::BeginResponse()
+{
+ assert(state != RESPONSE);
+
+ state = RESPONSE;
+ current_page = nullptr;
+
+ httpd->SendHeader(*this);
+}
+
+/**
+ * Handle a line of the HTTP request.
+ */
+bool
+HttpdClient::HandleLine(const char *line)
+{
+ assert(state != RESPONSE);
+
+ if (state == REQUEST) {
+ if (strncmp(line, "GET /", 5) != 0) {
+ /* only GET is supported */
+ g_warning("malformed request line from client");
+ return false;
+ }
+
+ line = strchr(line + 5, ' ');
+ if (line == nullptr || strncmp(line + 1, "HTTP/", 5) != 0) {
+ /* HTTP/0.9 without request headers */
+ BeginResponse();
+ return true;
+ }
+
+ /* after the request line, request headers follow */
+ state = HEADERS;
+ return true;
+ } else {
+ if (*line == 0) {
+ /* empty line: request is finished */
+ BeginResponse();
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
+ /* Send icy metadata */
+ metadata_requested = metadata_supported;
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
+ /* Send as dlna */
+ dlna_streaming_requested = true;
+ /* metadata is not supported by dlna streaming, so disable it */
+ metadata_supported = false;
+ metadata_requested = false;
+ return true;
+ }
+
+ /* expect more request headers */
+ return true;
+ }
+}
+
+/**
+ * Sends the status line and response headers to the client.
+ */
+bool
+HttpdClient::SendResponse()
+{
+ char buffer[1024];
+ assert(state == RESPONSE);
+
+ if (dlna_streaming_requested) {
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 206 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: 10000\r\n"
+ "Content-RangeX: 0-1000000/1000000\r\n"
+ "transferMode.dlna.org: Streaming\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "Connection: close\r\n"
+ "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
+ "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
+ "\r\n",
+ httpd->content_type);
+
+ } else if (metadata_requested) {
+ gchar *metadata_header;
+
+ metadata_header =
+ icy_server_metadata_header(httpd->name, httpd->genre,
+ httpd->website,
+ httpd->content_type,
+ metaint);
+
+ g_strlcpy(buffer, metadata_header, sizeof(buffer));
+
+ g_free(metadata_header);
+
+ } else { /* revert to a normal HTTP request */
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Connection: close\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache, no-store\r\n"
+ "\r\n",
+ httpd->content_type);
+ }
+
+ ssize_t nbytes = SocketMonitor::Write(buffer, strlen(buffer));
+ if (gcc_unlikely(nbytes < 0)) {
+ const SocketErrorMessage msg;
+ g_warning("failed to write to client: %s", (const char *)msg);
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+HttpdClient::HttpdClient(HttpdOutput *_httpd, int _fd, EventLoop &_loop,
+ bool _metadata_supported)
+ :BufferedSocket(_fd, _loop),
+ httpd(_httpd),
+ state(REQUEST),
+ dlna_streaming_requested(false),
+ metadata_supported(_metadata_supported),
+ metadata_requested(false), metadata_sent(true),
+ metaint(8192), /*TODO: just a std value */
+ metadata(nullptr),
+ metadata_current_position(0), metadata_fill(0)
+{
+}
+
+size_t
+HttpdClient::GetQueueSize() const
+{
+ if (state != RESPONSE)
+ return 0;
+
+ size_t size = 0;
+ for (auto page : pages)
+ size += page->size;
+ return size;
+}
+
+void
+HttpdClient::CancelQueue()
+{
+ if (state != RESPONSE)
+ return;
+
+ for (auto page : pages)
+ page->Unref();
+ pages.clear();
+
+ if (current_page == nullptr)
+ CancelWrite();
+}
+
+ssize_t
+HttpdClient::TryWritePage(const Page &page, size_t position)
+{
+ assert(position < page.size);
+
+ return Write(page.data + position, page.size - position);
+}
+
+ssize_t
+HttpdClient::TryWritePageN(const Page &page, size_t position, ssize_t n)
+{
+ return n >= 0
+ ? Write(page.data + position, n)
+ : TryWritePage(page, position);
+}
+
+ssize_t
+HttpdClient::GetBytesTillMetaData() const
+{
+ if (metadata_requested &&
+ current_page->size - current_position > metaint - metadata_fill)
+ return metaint - metadata_fill;
+
+ return -1;
+}
+
+inline bool
+HttpdClient::TryWrite()
+{
+ const ScopeLock protect(httpd->mutex);
+
+ assert(state == RESPONSE);
+
+ if (current_page == nullptr) {
+ if (pages.empty()) {
+ /* another thread has removed the event source
+ while this thread was waiting for
+ httpd->mutex */
+ CancelWrite();
+ return true;
+ }
+
+ current_page = pages.front();
+ pages.pop_front();
+ current_position = 0;
+ }
+
+ const ssize_t bytes_to_write = GetBytesTillMetaData();
+ if (bytes_to_write == 0) {
+ if (!metadata_sent) {
+ ssize_t nbytes = TryWritePage(*metadata,
+ metadata_current_position);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ metadata_current_position += nbytes;
+
+ if (metadata->size - metadata_current_position == 0) {
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ metadata_sent = true;
+ }
+ } else {
+ guchar empty_data = 0;
+
+ ssize_t nbytes = Write(&empty_data, 1);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ }
+ } else {
+ ssize_t nbytes =
+ TryWritePageN(*current_page, current_position,
+ bytes_to_write);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ current_position += nbytes;
+ assert(current_position <= current_page->size);
+
+ if (metadata_requested)
+ metadata_fill += nbytes;
+
+ if (current_position >= current_page->size) {
+ current_page->Unref();
+ current_page = nullptr;
+
+ if (pages.empty())
+ /* all pages are sent: remove the
+ event source */
+ CancelWrite();
+ }
+ }
+
+ return true;
+}
+
+void
+HttpdClient::PushPage(Page *page)
+{
+ if (state != RESPONSE)
+ /* the client is still writing the HTTP request */
+ return;
+
+ page->Ref();
+ pages.push_back(page);
+
+ ScheduleWrite();
+}
+
+void
+HttpdClient::PushMetaData(Page *page)
+{
+ if (metadata) {
+ metadata->Unref();
+ metadata = nullptr;
+ }
+
+ g_return_if_fail (page);
+
+ page->Ref();
+ metadata = page;
+ metadata_sent = false;
+}
+
+bool
+HttpdClient::OnSocketReady(unsigned flags)
+{
+ if (!BufferedSocket::OnSocketReady(flags))
+ return false;
+
+ if (flags & WRITE)
+ if (!TryWrite())
+ return false;
+
+ return true;
+}
+
+BufferedSocket::InputResult
+HttpdClient::OnSocketInput(const void *data, size_t length)
+{
+ if (state == RESPONSE) {
+ g_warning("unexpected input from client");
+ LockClose();
+ return InputResult::CLOSED;
+ }
+
+ const char *line = (const char *)data;
+ const char *newline = (const char *)memchr(line, '\n', length);
+ if (newline == nullptr)
+ return InputResult::MORE;
+
+ ConsumeInput(newline + 1 - line);
+
+ if (newline > line && newline[-1] == '\r')
+ --newline;
+
+ /* terminate the string at the end of the line; the const_cast
+ is a dirty hack */
+ *const_cast<char *>(newline) = 0;
+
+ if (!HandleLine(line)) {
+ assert(state == RESPONSE);
+ LockClose();
+ return InputResult::CLOSED;
+ }
+
+ if (state == RESPONSE && !SendResponse())
+ return InputResult::CLOSED;
+
+ return InputResult::AGAIN;
+}
+
+void
+HttpdClient::OnSocketError(GError *error)
+{
+ g_warning("error on HTTP client: %s", error->message);
+ g_error_free(error);
+}
+
+void
+HttpdClient::OnSocketClosed()
+{
+ LockClose();
+}
diff --git a/src/output/HttpdClient.hxx b/src/output/HttpdClient.hxx
new file mode 100644
index 000000000..46196d2e3
--- /dev/null
+++ b/src/output/HttpdClient.hxx
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2003-2013 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_HTTPD_CLIENT_HXX
+#define MPD_OUTPUT_HTTPD_CLIENT_HXX
+
+#include "event/BufferedSocket.hxx"
+#include "gcc.h"
+
+#include <list>
+
+#include <stddef.h>
+
+struct HttpdOutput;
+class Page;
+
+class HttpdClient final : public BufferedSocket {
+ /**
+ * The httpd output object this client is connected to.
+ */
+ HttpdOutput *const httpd;
+
+ /**
+ * The current state of the client.
+ */
+ enum {
+ /** reading the request line */
+ REQUEST,
+
+ /** reading the request headers */
+ HEADERS,
+
+ /** sending the HTTP response */
+ RESPONSE,
+ } state;
+
+ /**
+ * A queue of #Page objects to be sent to the client.
+ */
+ std::list<Page *> pages;
+
+ /**
+ * The #page which is currently being sent to the client.
+ */
+ Page *current_page;
+
+ /**
+ * The amount of bytes which were already sent from
+ * #current_page.
+ */
+ size_t current_position;
+
+ /**
+ * If DLNA streaming was an option.
+ */
+ bool dlna_streaming_requested;
+
+ /* ICY */
+
+ /**
+ * Do we support sending Icy-Metadata to the client? This is
+ * disabled if the httpd audio output uses encoder tags.
+ */
+ bool metadata_supported;
+
+ /**
+ * If we should sent icy metadata.
+ */
+ bool metadata_requested;
+
+ /**
+ * If the current metadata was already sent to the client.
+ */
+ bool metadata_sent;
+
+ /**
+ * The amount of streaming data between each metadata block
+ */
+ guint metaint;
+
+ /**
+ * The metadata as #Page which is currently being sent to the client.
+ */
+ Page *metadata;
+
+ /*
+ * The amount of bytes which were already sent from the metadata.
+ */
+ size_t metadata_current_position;
+
+ /**
+ * The amount of streaming data sent to the client
+ * since the last icy information was sent.
+ */
+ guint metadata_fill;
+
+public:
+ /**
+ * @param httpd the HTTP output device
+ * @param fd the socket file descriptor
+ */
+ HttpdClient(HttpdOutput *httpd, int _fd, EventLoop &_loop,
+ bool _metadata_supported);
+
+ /**
+ * Note: this does not remove the client from the
+ * #HttpdOutput object.
+ */
+ ~HttpdClient();
+
+ /**
+ * Frees the client and removes it from the server's client list.
+ */
+ void Close();
+
+ void LockClose();
+
+ /**
+ * Returns the total size of this client's page queue.
+ */
+ gcc_pure
+ size_t GetQueueSize() const;
+
+ /**
+ * Clears the page queue.
+ */
+ void CancelQueue();
+
+ /**
+ * Handle a line of the HTTP request.
+ */
+ bool HandleLine(const char *line);
+
+ /**
+ * Switch the client to the "RESPONSE" state.
+ */
+ void BeginResponse();
+
+ /**
+ * Sends the status line and response headers to the client.
+ */
+ bool SendResponse();
+
+ gcc_pure
+ ssize_t GetBytesTillMetaData() const;
+
+ ssize_t TryWritePage(const Page &page, size_t position);
+ ssize_t TryWritePageN(const Page &page, size_t position, ssize_t n);
+
+ bool TryWrite();
+
+ /**
+ * Appends a page to the client's queue.
+ */
+ void PushPage(Page *page);
+
+ /**
+ * Sends the passed metadata.
+ */
+ void PushMetaData(Page *page);
+
+protected:
+ virtual bool OnSocketReady(unsigned flags) override;
+ virtual InputResult OnSocketInput(const void *data,
+ size_t length) override;
+ virtual void OnSocketError(GError *error) override;
+ virtual void OnSocketClosed() override;
+};
+
+#endif
diff --git a/src/output/HttpdInternal.hxx b/src/output/HttpdInternal.hxx
new file mode 100644
index 000000000..4b526bcde
--- /dev/null
+++ b/src/output/HttpdInternal.hxx
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+/** \file
+ *
+ * Internal declarations for the "httpd" audio output plugin.
+ */
+
+#ifndef MPD_OUTPUT_HTTPD_INTERNAL_H
+#define MPD_OUTPUT_HTTPD_INTERNAL_H
+
+#include "output_internal.h"
+#include "timer.h"
+#include "thread/Mutex.hxx"
+#include "event/ServerSocket.hxx"
+
+#include <forward_list>
+
+struct config_param;
+class EventLoop;
+class ServerSocket;
+class HttpdClient;
+class Page;
+
+struct HttpdOutput final : private ServerSocket {
+ struct audio_output base;
+
+ /**
+ * True if the audio output is open and accepts client
+ * connections.
+ */
+ bool open;
+
+ /**
+ * The configured encoder plugin.
+ */
+ struct encoder *encoder;
+
+ /**
+ * Number of bytes which were fed into the encoder, without
+ * ever receiving new output. This is used to estimate
+ * whether MPD should manually flush the encoder, to avoid
+ * buffer underruns in the client.
+ */
+ size_t unflushed_input;
+
+ /**
+ * The MIME type produced by the #encoder.
+ */
+ const char *content_type;
+
+ /**
+ * This mutex protects the listener socket and the client
+ * list.
+ */
+ mutable Mutex mutex;
+
+ /**
+ * A #timer object to synchronize this output with the
+ * wallclock.
+ */
+ struct timer *timer;
+
+ /**
+ * The header page, which is sent to every client on connect.
+ */
+ Page *header;
+
+ /**
+ * The metadata, which is sent to every client.
+ */
+ Page *metadata;
+
+ /**
+ * The configured name.
+ */
+ char const *name;
+ /**
+ * The configured genre.
+ */
+ char const *genre;
+ /**
+ * The configured website address.
+ */
+ char const *website;
+
+ /**
+ * A linked list containing all clients which are currently
+ * connected.
+ */
+ std::forward_list<HttpdClient> clients;
+
+ /**
+ * A temporary buffer for the httpd_output_read_page()
+ * function.
+ */
+ char buffer[32768];
+
+ /**
+ * The maximum and current number of clients connected
+ * at the same time.
+ */
+ guint clients_max, clients_cnt;
+
+ HttpdOutput(EventLoop &_loop);
+ ~HttpdOutput();
+
+ bool Configure(const config_param *param, GError **error_r);
+
+ bool Bind(GError **error_r);
+ void Unbind();
+
+ /**
+ * Caller must lock the mutex.
+ */
+ bool OpenEncoder(struct audio_format *audio_format,
+ GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ bool Open(struct audio_format *audio_format, GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ void Close();
+
+ /**
+ * Check whether there is at least one client.
+ *
+ * Caller must lock the mutex.
+ */
+ gcc_pure
+ bool HasClients() const {
+ return !clients.empty();
+ }
+
+ /**
+ * Check whether there is at least one client.
+ */
+ gcc_pure
+ bool LockHasClients() const {
+ const ScopeLock protect(mutex);
+ return HasClients();
+ }
+
+ void AddClient(int fd);
+
+ /**
+ * Removes a client from the httpd_output.clients linked list.
+ */
+ void RemoveClient(HttpdClient &client);
+
+ /**
+ * Sends the encoder header to the client. This is called
+ * right after the response headers have been sent.
+ */
+ void SendHeader(HttpdClient &client) const;
+
+ /**
+ * Reads data from the encoder (as much as available) and
+ * returns it as a new #page object.
+ */
+ Page *ReadPage();
+
+ /**
+ * Broadcasts a page struct to all clients.
+ *
+ * Mutext must not be locked.
+ */
+ void BroadcastPage(Page *page);
+
+ /**
+ * Broadcasts data from the encoder to all clients.
+ */
+ void BroadcastFromEncoder();
+
+ bool EncodeAndPlay(const void *chunk, size_t size, GError **error_r);
+
+ void SendTag(const struct tag *tag);
+
+private:
+ virtual void OnAccept(int fd, const sockaddr &address,
+ size_t address_length, int uid) override;
+};
+
+#endif
diff --git a/src/output/HttpdOutputPlugin.cxx b/src/output/HttpdOutputPlugin.cxx
new file mode 100644
index 000000000..cb515e657
--- /dev/null
+++ b/src/output/HttpdOutputPlugin.cxx
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2003-2013 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 "HttpdOutputPlugin.hxx"
+#include "HttpdInternal.hxx"
+#include "HttpdClient.hxx"
+#include "output_api.h"
+#include "encoder_plugin.h"
+#include "encoder_list.h"
+#include "resolver.h"
+#include "Page.hxx"
+#include "IcyMetaDataServer.hxx"
+#include "fd_util.h"
+#include "Main.hxx"
+
+#include <assert.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBWRAP
+#include <sys/socket.h> /* needed for AF_UNIX */
+#include <tcpd.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "httpd_output"
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+httpd_output_quark(void)
+{
+ return g_quark_from_static_string("httpd_output");
+}
+
+inline
+HttpdOutput::HttpdOutput(EventLoop &_loop)
+ :ServerSocket(_loop),
+ encoder(nullptr), unflushed_input(0),
+ metadata(nullptr)
+{
+}
+
+HttpdOutput::~HttpdOutput()
+{
+ if (metadata != nullptr)
+ metadata->Unref();
+
+ if (encoder != nullptr)
+ encoder_finish(encoder);
+
+}
+
+inline bool
+HttpdOutput::Bind(GError **error_r)
+{
+ open = false;
+
+ const ScopeLock protect(mutex);
+ return ServerSocket::Open(error_r);
+}
+
+inline void
+HttpdOutput::Unbind()
+{
+ assert(!open);
+
+ const ScopeLock protect(mutex);
+ ServerSocket::Close();
+}
+
+inline bool
+HttpdOutput::Configure(const config_param *param, GError **error_r)
+{
+ /* read configuration */
+ name = config_get_block_string(param, "name", "Set name in config");
+ genre = config_get_block_string(param, "genre", "Set genre in config");
+ website = config_get_block_string(param, "website",
+ "Set website in config");
+
+ guint port = config_get_block_unsigned(param, "port", 8000);
+
+ const char *encoder_name =
+ config_get_block_string(param, "encoder", "vorbis");
+ const struct encoder_plugin *encoder_plugin =
+ encoder_plugin_get(encoder_name);
+ if (encoder_plugin == NULL) {
+ g_set_error(error_r, httpd_output_quark(), 0,
+ "No such encoder: %s", encoder_name);
+ return false;
+ }
+
+ clients_max = config_get_block_unsigned(param,"max_clients", 0);
+
+ /* set up bind_to_address */
+
+ const char *bind_to_address =
+ config_get_block_string(param, "bind_to_address", NULL);
+ bool success = bind_to_address != NULL &&
+ strcmp(bind_to_address, "any") != 0
+ ? AddHost(bind_to_address, port, error_r)
+ : AddPort(port, error_r);
+ if (!success)
+ return false;
+
+ /* initialize encoder */
+
+ encoder = encoder_init(encoder_plugin, param, error_r);
+ if (encoder == nullptr)
+ return false;
+
+ /* determine content type */
+ content_type = encoder_get_mime_type(encoder);
+ if (content_type == nullptr)
+ content_type = "application/octet-stream";
+
+ return true;
+}
+
+static struct audio_output *
+httpd_output_init(const struct config_param *param,
+ GError **error_r)
+{
+ HttpdOutput *httpd = new HttpdOutput(*main_loop);
+
+ if (!ao_base_init(&httpd->base, &httpd_output_plugin, param,
+ error_r)) {
+ delete httpd;
+ return nullptr;
+ }
+
+ if (!httpd->Configure(param, error_r)) {
+ ao_base_finish(&httpd->base);
+ delete httpd;
+ return nullptr;
+ }
+
+ return &httpd->base;
+}
+
+#if GCC_CHECK_VERSION(4,6) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-offsetof"
+#endif
+
+static inline constexpr HttpdOutput *
+Cast(audio_output *ao)
+{
+ return (HttpdOutput *)((char *)ao - offsetof(HttpdOutput, base));
+}
+
+#if GCC_CHECK_VERSION(4,6) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+static void
+httpd_output_finish(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ ao_base_finish(&httpd->base);
+ delete httpd;
+}
+
+/**
+ * Creates a new #HttpdClient object and adds it into the
+ * HttpdOutput.clients linked list.
+ */
+inline void
+HttpdOutput::AddClient(int fd)
+{
+ clients.emplace_front(this, fd, GetEventLoop(),
+ encoder->plugin->tag == NULL);
+ ++clients_cnt;
+
+ /* pass metadata to client */
+ if (metadata != nullptr)
+ clients.front().PushMetaData(metadata);
+}
+
+void
+HttpdOutput::OnAccept(int fd, const sockaddr &address,
+ size_t address_length, gcc_unused int uid)
+{
+ /* the listener socket has become readable - a client has
+ connected */
+
+#ifdef HAVE_LIBWRAP
+ if (address.sa_family != AF_UNIX) {
+ char *hostaddr = sockaddr_to_string(&address, address_length, NULL);
+ const char *progname = g_get_prgname();
+
+ struct request_info req;
+ request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
+
+ fromhost(&req);
+
+ if (!hosts_access(&req)) {
+ /* tcp wrappers says no */
+ g_warning("libwrap refused connection (libwrap=%s) from %s",
+ progname, hostaddr);
+ g_free(hostaddr);
+ close_socket(fd);
+ return;
+ }
+
+ g_free(hostaddr);
+ }
+#else
+ (void)address;
+ (void)address_length;
+#endif /* HAVE_WRAP */
+
+ const ScopeLock protect(mutex);
+
+ if (fd >= 0) {
+ /* can we allow additional client */
+ if (open && (clients_max == 0 || clients_cnt < clients_max))
+ AddClient(fd);
+ else
+ close_socket(fd);
+ } else if (fd < 0 && errno != EINTR) {
+ g_warning("accept() failed: %s", g_strerror(errno));
+ }
+}
+
+Page *
+HttpdOutput::ReadPage()
+{
+ if (unflushed_input >= 65536) {
+ /* we have fed a lot of input into the encoder, but it
+ didn't give anything back yet - flush now to avoid
+ buffer underruns */
+ encoder_flush(encoder, NULL);
+ unflushed_input = 0;
+ }
+
+ size_t size = 0;
+ do {
+ size_t nbytes = encoder_read(encoder,
+ buffer + size,
+ sizeof(buffer) - size);
+ if (nbytes == 0)
+ break;
+
+ unflushed_input = 0;
+
+ size += nbytes;
+ } while (size < sizeof(buffer));
+
+ if (size == 0)
+ return NULL;
+
+ return Page::Copy(buffer, size);
+}
+
+static bool
+httpd_output_enable(struct audio_output *ao, GError **error_r)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ return httpd->Bind(error_r);
+}
+
+static void
+httpd_output_disable(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ httpd->Unbind();
+}
+
+inline bool
+HttpdOutput::OpenEncoder(struct audio_format *audio_format, GError **error)
+{
+ if (!encoder_open(encoder, audio_format, error))
+ return false;
+
+ /* we have to remember the encoder header, i.e. the first
+ bytes of encoder output after opening it, because it has to
+ be sent to every new client */
+ header = ReadPage();
+
+ unflushed_input = 0;
+
+ return true;
+}
+
+inline bool
+HttpdOutput::Open(struct audio_format *audio_format, GError **error_r)
+{
+ assert(!open);
+ assert(clients.empty());
+
+ /* open the encoder */
+
+ if (!OpenEncoder(audio_format, error_r))
+ return false;
+
+ /* initialize other attributes */
+
+ clients_cnt = 0;
+ timer = timer_new(audio_format);
+
+ open = true;
+
+ return true;
+}
+
+static bool
+httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
+ GError **error)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ assert(httpd->clients.empty());
+
+ const ScopeLock protect(httpd->mutex);
+ return httpd->Open(audio_format, error);
+}
+
+inline void
+HttpdOutput::Close()
+{
+ assert(open);
+
+ open = false;
+
+ timer_free(timer);
+
+ clients.clear();
+
+ if (header != NULL)
+ header->Unref();
+
+ encoder_close(encoder);
+}
+
+static void
+httpd_output_close(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ const ScopeLock protect(httpd->mutex);
+ httpd->Close();
+}
+
+void
+HttpdOutput::RemoveClient(HttpdClient &client)
+{
+ assert(clients_cnt > 0);
+
+ for (auto prev = clients.before_begin(), i = std::next(prev);;
+ prev = i, i = std::next(prev)) {
+ assert(i != clients.end());
+ if (&*i == &client) {
+ clients.erase_after(prev);
+ clients_cnt--;
+ break;
+ }
+ }
+}
+
+void
+HttpdOutput::SendHeader(HttpdClient &client) const
+{
+ if (header != NULL)
+ client.PushPage(header);
+}
+
+static unsigned
+httpd_output_delay(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (!httpd->LockHasClients() && httpd->base.pause) {
+ /* if there's no client and this output is paused,
+ then httpd_output_pause() will not do anything, it
+ will not fill the buffer and it will not update the
+ timer; therefore, we reset the timer here */
+ timer_reset(httpd->timer);
+
+ /* some arbitrary delay that is long enough to avoid
+ consuming too much CPU, and short enough to notice
+ new clients quickly enough */
+ return 1000;
+ }
+
+ return httpd->timer->started
+ ? timer_delay(httpd->timer)
+ : 0;
+}
+
+void
+HttpdOutput::BroadcastPage(Page *page)
+{
+ assert(page != NULL);
+
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
+ client.PushPage(page);
+}
+
+void
+HttpdOutput::BroadcastFromEncoder()
+{
+ mutex.lock();
+ for (auto &client : clients) {
+ if (client.GetQueueSize() > 256 * 1024) {
+ g_debug("client is too slow, flushing its queue");
+ client.CancelQueue();
+ }
+ }
+ mutex.unlock();
+
+ Page *page;
+ while ((page = ReadPage()) != nullptr) {
+ BroadcastPage(page);
+ page->Unref();
+ }
+}
+
+inline bool
+HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, GError **error_r)
+{
+ if (!encoder_write(encoder, chunk, size, error_r))
+ return false;
+
+ unflushed_input += size;
+
+ BroadcastFromEncoder();
+ return true;
+}
+
+static size_t
+httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
+ GError **error_r)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (httpd->LockHasClients()) {
+ if (!httpd->EncodeAndPlay(chunk, size, error_r))
+ return 0;
+ }
+
+ if (!httpd->timer->started)
+ timer_start(httpd->timer);
+ timer_add(httpd->timer, size);
+
+ return size;
+}
+
+static bool
+httpd_output_pause(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (httpd->LockHasClients()) {
+ static const char silence[1020] = { 0 };
+ return httpd_output_play(ao, silence, sizeof(silence),
+ NULL) > 0;
+ } else {
+ return true;
+ }
+}
+
+inline void
+HttpdOutput::SendTag(const struct tag *tag)
+{
+ assert(tag != NULL);
+
+ if (encoder->plugin->tag != NULL) {
+ /* embed encoder tags */
+
+ /* flush the current stream, and end it */
+
+ encoder_pre_tag(encoder, NULL);
+ BroadcastFromEncoder();
+
+ /* send the tag to the encoder - which starts a new
+ stream now */
+
+ encoder_tag(encoder, tag, NULL);
+
+ /* the first page generated by the encoder will now be
+ used as the new "header" page, which is sent to all
+ new clients */
+
+ Page *page = ReadPage();
+ if (page != NULL) {
+ if (header != NULL)
+ header->Unref();
+ header = page;
+ BroadcastPage(page);
+ }
+ } else {
+ /* use Icy-Metadata */
+
+ if (metadata != NULL)
+ metadata->Unref();
+
+ static constexpr tag_type types[] = {
+ TAG_ALBUM, TAG_ARTIST, TAG_TITLE,
+ TAG_NUM_OF_ITEM_TYPES
+ };
+
+ metadata = icy_server_metadata_page(tag, &types[0]);
+ if (metadata != NULL) {
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
+ client.PushMetaData(metadata);
+ }
+ }
+}
+
+static void
+httpd_output_tag(struct audio_output *ao, const struct tag *tag)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ httpd->SendTag(tag);
+}
+
+static void
+httpd_output_cancel(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ const ScopeLock protect(httpd->mutex);
+ for (auto &client : httpd->clients)
+ client.CancelQueue();
+}
+
+const struct audio_output_plugin httpd_output_plugin = {
+ "httpd",
+ nullptr,
+ httpd_output_init,
+ httpd_output_finish,
+ httpd_output_enable,
+ httpd_output_disable,
+ httpd_output_open,
+ httpd_output_close,
+ httpd_output_delay,
+ httpd_output_tag,
+ httpd_output_play,
+ nullptr,
+ httpd_output_cancel,
+ httpd_output_pause,
+ nullptr,
+};
diff --git a/src/output/HttpdOutputPlugin.hxx b/src/output/HttpdOutputPlugin.hxx
new file mode 100644
index 000000000..c74d2bd4a
--- /dev/null
+++ b/src/output/HttpdOutputPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_HTTPD_OUTPUT_PLUGIN_HXX
+#define MPD_HTTPD_OUTPUT_PLUGIN_HXX
+
+extern const struct audio_output_plugin httpd_output_plugin;
+
+#endif
diff --git a/src/output/NullOutputPlugin.cxx b/src/output/NullOutputPlugin.cxx
new file mode 100644
index 000000000..3596cbdcb
--- /dev/null
+++ b/src/output/NullOutputPlugin.cxx
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2003-2013 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 "NullOutputPlugin.hxx"
+#include "output_api.h"
+#include "timer.h"
+
+#include <glib.h>
+
+#include <assert.h>
+
+struct NullOutput {
+ struct audio_output base;
+
+ bool sync;
+
+ struct timer *timer;
+};
+
+static struct audio_output *
+null_init(const struct config_param *param, GError **error_r)
+{
+ NullOutput *nd = g_new(NullOutput, 1);
+
+ if (!ao_base_init(&nd->base, &null_output_plugin, param, error_r)) {
+ g_free(nd);
+ return NULL;
+ }
+
+ nd->sync = config_get_block_bool(param, "sync", true);
+
+ return &nd->base;
+}
+
+static void
+null_finish(struct audio_output *ao)
+{
+ NullOutput *nd = (NullOutput *)ao;
+
+ ao_base_finish(&nd->base);
+ g_free(nd);
+}
+
+static bool
+null_open(struct audio_output *ao, struct audio_format *audio_format,
+ G_GNUC_UNUSED GError **error)
+{
+ NullOutput *nd = (NullOutput *)ao;
+
+ if (nd->sync)
+ nd->timer = timer_new(audio_format);
+
+ return true;
+}
+
+static void
+null_close(struct audio_output *ao)
+{
+ NullOutput *nd = (NullOutput *)ao;
+
+ if (nd->sync)
+ timer_free(nd->timer);
+}
+
+static unsigned
+null_delay(struct audio_output *ao)
+{
+ NullOutput *nd = (NullOutput *)ao;
+
+ return nd->sync && nd->timer->started
+ ? timer_delay(nd->timer)
+ : 0;
+}
+
+static size_t
+null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size,
+ G_GNUC_UNUSED GError **error)
+{
+ NullOutput *nd = (NullOutput *)ao;
+ struct timer *timer = nd->timer;
+
+ if (!nd->sync)
+ return size;
+
+ if (!timer->started)
+ timer_start(timer);
+ timer_add(timer, size);
+
+ return size;
+}
+
+static void
+null_cancel(struct audio_output *ao)
+{
+ NullOutput *nd = (NullOutput *)ao;
+
+ if (!nd->sync)
+ return;
+
+ timer_reset(nd->timer);
+}
+
+const struct audio_output_plugin null_output_plugin = {
+ "null",
+ nullptr,
+ null_init,
+ null_finish,
+ nullptr,
+ nullptr,
+ null_open,
+ null_close,
+ null_delay,
+ nullptr,
+ null_play,
+ nullptr,
+ null_cancel,
+ nullptr,
+ nullptr,
+};
diff --git a/src/output/NullOutputPlugin.hxx b/src/output/NullOutputPlugin.hxx
new file mode 100644
index 000000000..a58f1cb13
--- /dev/null
+++ b/src/output/NullOutputPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_NULL_OUTPUT_PLUGIN_HXX
+#define MPD_NULL_OUTPUT_PLUGIN_HXX
+
+extern const struct audio_output_plugin null_output_plugin;
+
+#endif
diff --git a/src/output/OSXOutputPlugin.cxx b/src/output/OSXOutputPlugin.cxx
new file mode 100644
index 000000000..5a04fe1db
--- /dev/null
+++ b/src/output/OSXOutputPlugin.cxx
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2003-2013 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 "OSXOutputPlugin.hxx"
+#include "output_api.h"
+#include "util/fifo_buffer.h"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+
+#include <glib.h>
+#include <CoreAudio/AudioHardware.h>
+#include <AudioUnit/AudioUnit.h>
+#include <CoreServices/CoreServices.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "osx"
+
+struct OSXOutput {
+ struct audio_output base;
+
+ /* configuration settings */
+ OSType component_subtype;
+ /* only applicable with kAudioUnitSubType_HALOutput */
+ const char *device_name;
+
+ AudioUnit au;
+ Mutex mutex;
+ Cond condition;
+
+ struct fifo_buffer *buffer;
+};
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+osx_output_quark(void)
+{
+ return g_quark_from_static_string("osx_output");
+}
+
+static bool
+osx_output_test_default_device(void)
+{
+ /* on a Mac, this is always the default plugin, if nothing
+ else is configured */
+ return true;
+}
+
+static void
+osx_output_configure(OSXOutput *oo, const struct config_param *param)
+{
+ const char *device = config_get_block_string(param, "device", NULL);
+
+ if (device == NULL || 0 == strcmp(device, "default")) {
+ oo->component_subtype = kAudioUnitSubType_DefaultOutput;
+ oo->device_name = NULL;
+ }
+ else if (0 == strcmp(device, "system")) {
+ oo->component_subtype = kAudioUnitSubType_SystemOutput;
+ oo->device_name = NULL;
+ }
+ else {
+ oo->component_subtype = kAudioUnitSubType_HALOutput;
+ /* XXX am I supposed to g_strdup() this? */
+ oo->device_name = device;
+ }
+}
+
+static struct audio_output *
+osx_output_init(const struct config_param *param, GError **error_r)
+{
+ OSXOutput *oo = new OSXOutput();
+ if (!ao_base_init(&oo->base, &osx_output_plugin, param, error_r)) {
+ delete oo;
+ return NULL;
+ }
+
+ osx_output_configure(oo, param);
+
+ return &oo->base;
+}
+
+static void
+osx_output_finish(struct audio_output *ao)
+{
+ OSXOutput *oo = (OSXOutput *)ao;
+
+ delete oo;
+}
+
+static bool
+osx_output_set_device(OSXOutput *oo, GError **error)
+{
+ bool ret = true;
+ OSStatus status;
+ UInt32 size, numdevices;
+ AudioDeviceID *deviceids = NULL;
+ char name[256];
+ unsigned int i;
+
+ if (oo->component_subtype != kAudioUnitSubType_HALOutput)
+ goto done;
+
+ /* how many audio devices are there? */
+ status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &size,
+ NULL);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine number of OS X audio devices: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+
+ /* what are the available audio device IDs? */
+ numdevices = size / sizeof(AudioDeviceID);
+ deviceids = new AudioDeviceID[numdevices];
+ status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &size,
+ deviceids);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine OS X audio device IDs: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+
+ /* which audio device matches oo->device_name? */
+ for (i = 0; i < numdevices; i++) {
+ size = sizeof(name);
+ status = AudioDeviceGetProperty(deviceids[i], 0, false,
+ kAudioDevicePropertyDeviceName,
+ &size, name);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine OS X device name "
+ "(device %u): %s",
+ (unsigned int) deviceids[i],
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+ if (strcmp(oo->device_name, name) == 0) {
+ g_debug("found matching device: ID=%u, name=%s",
+ (unsigned int) deviceids[i], name);
+ break;
+ }
+ }
+ if (i == numdevices) {
+ g_warning("Found no audio device with name '%s' "
+ "(will use default audio device)",
+ oo->device_name);
+ goto done;
+ }
+
+ status = AudioUnitSetProperty(oo->au,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ 0,
+ &(deviceids[i]),
+ sizeof(AudioDeviceID));
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to set OS X audio output device: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+ g_debug("set OS X audio output device ID=%u, name=%s",
+ (unsigned int) deviceids[i], name);
+
+done:
+ delete[] deviceids;
+ return ret;
+}
+
+static OSStatus
+osx_render(void *vdata,
+ G_GNUC_UNUSED AudioUnitRenderActionFlags *io_action_flags,
+ G_GNUC_UNUSED const AudioTimeStamp *in_timestamp,
+ G_GNUC_UNUSED UInt32 in_bus_number,
+ G_GNUC_UNUSED UInt32 in_number_frames,
+ AudioBufferList *buffer_list)
+{
+ OSXOutput *od = (OSXOutput *) vdata;
+ AudioBuffer *buffer = &buffer_list->mBuffers[0];
+ size_t buffer_size = buffer->mDataByteSize;
+
+ assert(od->buffer != NULL);
+
+ od->mutex.lock();
+
+ size_t nbytes;
+ const void *src = fifo_buffer_read(od->buffer, &nbytes);
+
+ if (src != NULL) {
+ if (nbytes > buffer_size)
+ nbytes = buffer_size;
+
+ memcpy(buffer->mData, src, nbytes);
+ fifo_buffer_consume(od->buffer, nbytes);
+ } else
+ nbytes = 0;
+
+ od->condition.signal();
+ od->mutex.unlock();
+
+ buffer->mDataByteSize = nbytes;
+
+ unsigned i;
+ for (i = 1; i < buffer_list->mNumberBuffers; ++i) {
+ buffer = &buffer_list->mBuffers[i];
+ buffer->mDataByteSize = 0;
+ }
+
+ return 0;
+}
+
+static bool
+osx_output_enable(struct audio_output *ao, GError **error_r)
+{
+ OSXOutput *oo = (OSXOutput *)ao;
+
+ ComponentDescription desc;
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = oo->component_subtype;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ Component comp = FindNextComponent(NULL, &desc);
+ if (comp == 0) {
+ g_set_error(error_r, osx_output_quark(), 0,
+ "Error finding OS X component");
+ return false;
+ }
+
+ OSStatus status = OpenAComponent(comp, &oo->au);
+ if (status != noErr) {
+ g_set_error(error_r, osx_output_quark(), status,
+ "Unable to open OS X component: %s",
+ GetMacOSStatusCommentString(status));
+ return false;
+ }
+
+ if (!osx_output_set_device(oo, error_r)) {
+ CloseComponent(oo->au);
+ return false;
+ }
+
+ AURenderCallbackStruct callback;
+ callback.inputProc = osx_render;
+ callback.inputProcRefCon = oo;
+
+ ComponentResult result =
+ AudioUnitSetProperty(oo->au,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0,
+ &callback, sizeof(callback));
+ if (result != noErr) {
+ CloseComponent(oo->au);
+ g_set_error(error_r, osx_output_quark(), result,
+ "unable to set callback for OS X audio unit");
+ return false;
+ }
+
+ return true;
+}
+
+static void
+osx_output_disable(struct audio_output *ao)
+{
+ OSXOutput *oo = (OSXOutput *)ao;
+
+ CloseComponent(oo->au);
+}
+
+static void
+osx_output_cancel(struct audio_output *ao)
+{
+ OSXOutput *od = (OSXOutput *)ao;
+
+ const ScopeLock protect(od->mutex);
+ fifo_buffer_clear(od->buffer);
+}
+
+static void
+osx_output_close(struct audio_output *ao)
+{
+ OSXOutput *od = (OSXOutput *)ao;
+
+ AudioOutputUnitStop(od->au);
+ AudioUnitUninitialize(od->au);
+
+ fifo_buffer_free(od->buffer);
+}
+
+static bool
+osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
+{
+ OSXOutput *od = (OSXOutput *)ao;
+
+ AudioStreamBasicDescription stream_description;
+ stream_description.mSampleRate = audio_format->sample_rate;
+ stream_description.mFormatID = kAudioFormatLinearPCM;
+ stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
+
+ switch (audio_format->format) {
+ case SAMPLE_FORMAT_S8:
+ stream_description.mBitsPerChannel = 8;
+ break;
+
+ case SAMPLE_FORMAT_S16:
+ stream_description.mBitsPerChannel = 16;
+ break;
+
+ case SAMPLE_FORMAT_S32:
+ stream_description.mBitsPerChannel = 32;
+ break;
+
+ default:
+ audio_format->format = SAMPLE_FORMAT_S32;
+ stream_description.mBitsPerChannel = 32;
+ break;
+ }
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
+#endif
+
+ stream_description.mBytesPerPacket =
+ audio_format_frame_size(audio_format);
+ stream_description.mFramesPerPacket = 1;
+ stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
+ stream_description.mChannelsPerFrame = audio_format->channels;
+
+ ComponentResult result =
+ AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 0,
+ &stream_description,
+ sizeof(stream_description));
+ if (result != noErr) {
+ g_set_error(error, osx_output_quark(), result,
+ "Unable to set format on OS X device");
+ return false;
+ }
+
+ OSStatus status = AudioUnitInitialize(od->au);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to initialize OS X audio unit: %s",
+ GetMacOSStatusCommentString(status));
+ return false;
+ }
+
+ /* create a buffer of 1s */
+ od->buffer = fifo_buffer_new(audio_format->sample_rate *
+ audio_format_frame_size(audio_format));
+
+ status = AudioOutputUnitStart(od->au);
+ if (status != 0) {
+ AudioUnitUninitialize(od->au);
+ g_set_error(error, osx_output_quark(), status,
+ "unable to start audio output: %s",
+ GetMacOSStatusCommentString(status));
+ return false;
+ }
+
+ return true;
+}
+
+static size_t
+osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
+ G_GNUC_UNUSED GError **error)
+{
+ OSXOutput *od = (OSXOutput *)ao;
+
+ const ScopeLock protect(od->mutex);
+
+ void *dest;
+ size_t max_length;
+
+ while (true) {
+ dest = fifo_buffer_write(od->buffer, &max_length);
+ if (dest != NULL)
+ break;
+
+ /* wait for some free space in the buffer */
+ od->condition.wait(od->mutex);
+ }
+
+ if (size > max_length)
+ size = max_length;
+
+ memcpy(dest, chunk, size);
+ fifo_buffer_append(od->buffer, size);
+
+ return size;
+}
+
+const struct audio_output_plugin osx_output_plugin = {
+ "osx",
+ osx_output_test_default_device,
+ osx_output_init,
+ osx_output_finish,
+ osx_output_enable,
+ osx_output_disable,
+ osx_output_open,
+ osx_output_close,
+ nullptr,
+ nullptr,
+ osx_output_play,
+ nullptr,
+ osx_output_cancel,
+ nullptr,
+ nullptr,
+};
diff --git a/src/output/OSXOutputPlugin.hxx b/src/output/OSXOutputPlugin.hxx
new file mode 100644
index 000000000..2a4172880
--- /dev/null
+++ b/src/output/OSXOutputPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_OSX_OUTPUT_PLUGIN_HXX
+#define MPD_OSX_OUTPUT_PLUGIN_HXX
+
+extern const struct audio_output_plugin osx_output_plugin;
+
+#endif
diff --git a/src/output/OssOutputPlugin.cxx b/src/output/OssOutputPlugin.cxx
new file mode 100644
index 000000000..ace88b6f4
--- /dev/null
+++ b/src/output/OssOutputPlugin.cxx
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2003-2013 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 "OssOutputPlugin.hxx"
+#include "output_api.h"
+#include "mixer_list.h"
+#include "fd_util.h"
+
+#include <glib.h>
+
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "oss"
+
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+# include <soundcard.h>
+#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
+# include <sys/soundcard.h>
+#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
+
+/* We got bug reports from FreeBSD users who said that the two 24 bit
+ formats generate white noise on FreeBSD, but 32 bit works. This is
+ a workaround until we know what exactly is expected by the kernel
+ audio drivers. */
+#ifndef __linux__
+#undef AFMT_S24_PACKED
+#undef AFMT_S24_NE
+#endif
+
+#ifdef AFMT_S24_PACKED
+#include "pcm_export.h"
+#endif
+
+struct oss_data {
+ struct audio_output base;
+
+#ifdef AFMT_S24_PACKED
+ struct pcm_export_state pcm_export;
+#endif
+
+ int fd;
+ const char *device;
+
+ /**
+ * The current input audio format. This is needed to reopen
+ * the device after cancel().
+ */
+ struct audio_format audio_format;
+
+ /**
+ * The current OSS audio format. This is needed to reopen the
+ * device after cancel().
+ */
+ int oss_format;
+};
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+oss_output_quark(void)
+{
+ return g_quark_from_static_string("oss_output");
+}
+
+static struct oss_data *
+oss_data_new(void)
+{
+ struct oss_data *ret = g_new(struct oss_data, 1);
+
+ ret->device = NULL;
+ ret->fd = -1;
+
+ return ret;
+}
+
+static void
+oss_data_free(struct oss_data *od)
+{
+ g_free(od);
+}
+
+enum oss_stat {
+ OSS_STAT_NO_ERROR = 0,
+ OSS_STAT_NOT_CHAR_DEV = -1,
+ OSS_STAT_NO_PERMS = -2,
+ OSS_STAT_DOESN_T_EXIST = -3,
+ OSS_STAT_OTHER = -4,
+};
+
+static enum oss_stat
+oss_stat_device(const char *device, int *errno_r)
+{
+ struct stat st;
+
+ if (0 == stat(device, &st)) {
+ if (!S_ISCHR(st.st_mode)) {
+ return OSS_STAT_NOT_CHAR_DEV;
+ }
+ } else {
+ *errno_r = errno;
+
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ return OSS_STAT_DOESN_T_EXIST;
+ case EACCES:
+ return OSS_STAT_NO_PERMS;
+ default:
+ return OSS_STAT_OTHER;
+ }
+ }
+
+ return OSS_STAT_NO_ERROR;
+}
+
+static const char *default_devices[] = { "/dev/sound/dsp", "/dev/dsp" };
+
+static bool
+oss_output_test_default_device(void)
+{
+ int fd, i;
+
+ for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
+ fd = open_cloexec(default_devices[i], O_WRONLY, 0);
+
+ if (fd >= 0) {
+ close(fd);
+ return true;
+ }
+ g_warning("Error opening OSS device \"%s\": %s\n",
+ default_devices[i], g_strerror(errno));
+ }
+
+ return false;
+}
+
+static struct audio_output *
+oss_open_default(GError **error)
+{
+ int err[G_N_ELEMENTS(default_devices)];
+ enum oss_stat ret[G_N_ELEMENTS(default_devices)];
+
+ for (int i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
+ ret[i] = oss_stat_device(default_devices[i], &err[i]);
+ if (ret[i] == OSS_STAT_NO_ERROR) {
+ struct oss_data *od = oss_data_new();
+ if (!ao_base_init(&od->base, &oss_output_plugin, NULL,
+ error)) {
+ g_free(od);
+ return NULL;
+ }
+
+ od->device = default_devices[i];
+ return &od->base;
+ }
+ }
+
+ for (int i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
+ const char *dev = default_devices[i];
+ switch(ret[i]) {
+ case OSS_STAT_NO_ERROR:
+ /* never reached */
+ break;
+ case OSS_STAT_DOESN_T_EXIST:
+ g_warning("%s not found\n", dev);
+ break;
+ case OSS_STAT_NOT_CHAR_DEV:
+ g_warning("%s is not a character device\n", dev);
+ break;
+ case OSS_STAT_NO_PERMS:
+ g_warning("%s: permission denied\n", dev);
+ break;
+ case OSS_STAT_OTHER:
+ g_warning("Error accessing %s: %s\n",
+ dev, g_strerror(err[i]));
+ }
+ }
+
+ g_set_error(error, oss_output_quark(), 0,
+ "error trying to open default OSS device");
+ return NULL;
+}
+
+static struct audio_output *
+oss_output_init(const struct config_param *param, GError **error)
+{
+ const char *device = config_get_block_string(param, "device", NULL);
+ if (device != NULL) {
+ struct oss_data *od = oss_data_new();
+ if (!ao_base_init(&od->base, &oss_output_plugin, param,
+ error)) {
+ g_free(od);
+ return NULL;
+ }
+
+ od->device = device;
+ return &od->base;
+ }
+
+ return oss_open_default(error);
+}
+
+static void
+oss_output_finish(struct audio_output *ao)
+{
+ struct oss_data *od = (struct oss_data *)ao;
+
+ ao_base_finish(&od->base);
+ oss_data_free(od);
+}
+
+#ifdef AFMT_S24_PACKED
+
+static bool
+oss_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
+{
+ struct oss_data *od = (struct oss_data *)ao;
+
+ pcm_export_init(&od->pcm_export);
+ return true;
+}
+
+static void
+oss_output_disable(struct audio_output *ao)
+{
+ struct oss_data *od = (struct oss_data *)ao;
+
+ pcm_export_deinit(&od->pcm_export);
+}
+
+#endif
+
+static void
+oss_close(struct oss_data *od)
+{
+ if (od->fd >= 0)
+ close(od->fd);
+ od->fd = -1;
+}
+
+/**
+ * A tri-state type for oss_try_ioctl().
+ */
+enum oss_setup_result {
+ SUCCESS,
+ ERROR,
+ UNSUPPORTED,
+};
+
+/**
+ * Invoke an ioctl on the OSS file descriptor. On success, SUCCESS is
+ * returned. If the parameter is not supported, UNSUPPORTED is
+ * returned. Any other failure returns ERROR and allocates a GError.
+ */
+static enum oss_setup_result
+oss_try_ioctl_r(int fd, unsigned long request, int *value_r,
+ const char *msg, GError **error_r)
+{
+ assert(fd >= 0);
+ assert(value_r != NULL);
+ assert(msg != NULL);
+ assert(error_r == NULL || *error_r == NULL);
+
+ int ret = ioctl(fd, request, value_r);
+ if (ret >= 0)
+ return SUCCESS;
+
+ if (errno == EINVAL)
+ return UNSUPPORTED;
+
+ g_set_error(error_r, oss_output_quark(), errno,
+ "%s: %s", msg, g_strerror(errno));
+ return ERROR;
+}
+
+/**
+ * Invoke an ioctl on the OSS file descriptor. On success, SUCCESS is
+ * returned. If the parameter is not supported, UNSUPPORTED is
+ * returned. Any other failure returns ERROR and allocates a GError.
+ */
+static enum oss_setup_result
+oss_try_ioctl(int fd, unsigned long request, int value,
+ const char *msg, GError **error_r)
+{
+ return oss_try_ioctl_r(fd, request, &value, msg, error_r);
+}
+
+/**
+ * Set up the channel number, and attempts to find alternatives if the
+ * specified number is not supported.
+ */
+static bool
+oss_setup_channels(int fd, struct audio_format *audio_format, GError **error_r)
+{
+ const char *const msg = "Failed to set channel count";
+ int channels = audio_format->channels;
+ enum oss_setup_result result =
+ oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels, msg, error_r);
+ switch (result) {
+ case SUCCESS:
+ if (!audio_valid_channel_count(channels))
+ break;
+
+ audio_format->channels = channels;
+ return true;
+
+ case ERROR:
+ return false;
+
+ case UNSUPPORTED:
+ break;
+ }
+
+ for (unsigned i = 1; i < 2; ++i) {
+ if (i == audio_format->channels)
+ /* don't try that again */
+ continue;
+
+ channels = i;
+ result = oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels,
+ msg, error_r);
+ switch (result) {
+ case SUCCESS:
+ if (!audio_valid_channel_count(channels))
+ break;
+
+ audio_format->channels = channels;
+ return true;
+
+ case ERROR:
+ return false;
+
+ case UNSUPPORTED:
+ break;
+ }
+ }
+
+ g_set_error(error_r, oss_output_quark(), EINVAL, "%s", msg);
+ return false;
+}
+
+/**
+ * Set up the sample rate, and attempts to find alternatives if the
+ * specified sample rate is not supported.
+ */
+static bool
+oss_setup_sample_rate(int fd, struct audio_format *audio_format,
+ GError **error_r)
+{
+ const char *const msg = "Failed to set sample rate";
+ int sample_rate = audio_format->sample_rate;
+ enum oss_setup_result result =
+ oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
+ msg, error_r);
+ switch (result) {
+ case SUCCESS:
+ if (!audio_valid_sample_rate(sample_rate))
+ break;
+
+ audio_format->sample_rate = sample_rate;
+ return true;
+
+ case ERROR:
+ return false;
+
+ case UNSUPPORTED:
+ break;
+ }
+
+ static const int sample_rates[] = { 48000, 44100, 0 };
+ for (unsigned i = 0; sample_rates[i] != 0; ++i) {
+ sample_rate = sample_rates[i];
+ if (sample_rate == (int)audio_format->sample_rate)
+ continue;
+
+ result = oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
+ msg, error_r);
+ switch (result) {
+ case SUCCESS:
+ if (!audio_valid_sample_rate(sample_rate))
+ break;
+
+ audio_format->sample_rate = sample_rate;
+ return true;
+
+ case ERROR:
+ return false;
+
+ case UNSUPPORTED:
+ break;
+ }
+ }
+
+ g_set_error(error_r, oss_output_quark(), EINVAL, "%s", msg);
+ return false;
+}
+
+/**
+ * Convert a MPD sample format to its OSS counterpart. Returns
+ * AFMT_QUERY if there is no direct counterpart.
+ */
+static int
+sample_format_to_oss(enum sample_format format)
+{
+ switch (format) {
+ case SAMPLE_FORMAT_UNDEFINED:
+ case SAMPLE_FORMAT_FLOAT:
+ case SAMPLE_FORMAT_DSD:
+ return AFMT_QUERY;
+
+ case SAMPLE_FORMAT_S8:
+ return AFMT_S8;
+
+ case SAMPLE_FORMAT_S16:
+ return AFMT_S16_NE;
+
+ case SAMPLE_FORMAT_S24_P32:
+#ifdef AFMT_S24_NE
+ return AFMT_S24_NE;
+#else
+ return AFMT_QUERY;
+#endif
+
+ case SAMPLE_FORMAT_S32:
+#ifdef AFMT_S32_NE
+ return AFMT_S32_NE;
+#else
+ return AFMT_QUERY;
+#endif
+ }
+
+ return AFMT_QUERY;
+}
+
+/**
+ * Convert an OSS sample format to its MPD counterpart. Returns
+ * SAMPLE_FORMAT_UNDEFINED if there is no direct counterpart.
+ */
+static enum sample_format
+sample_format_from_oss(int format)
+{
+ switch (format) {
+ case AFMT_S8:
+ return SAMPLE_FORMAT_S8;
+
+ case AFMT_S16_NE:
+ return SAMPLE_FORMAT_S16;
+
+#ifdef AFMT_S24_PACKED
+ case AFMT_S24_PACKED:
+ return SAMPLE_FORMAT_S24_P32;
+#endif
+
+#ifdef AFMT_S24_NE
+ case AFMT_S24_NE:
+ return SAMPLE_FORMAT_S24_P32;
+#endif
+
+#ifdef AFMT_S32_NE
+ case AFMT_S32_NE:
+ return SAMPLE_FORMAT_S32;
+#endif
+
+ default:
+ return SAMPLE_FORMAT_UNDEFINED;
+ }
+}
+
+/**
+ * Probe one sample format.
+ *
+ * @return the selected sample format or SAMPLE_FORMAT_UNDEFINED on
+ * error
+ */
+static enum oss_setup_result
+oss_probe_sample_format(int fd, enum sample_format sample_format,
+ enum sample_format *sample_format_r,
+ int *oss_format_r,
+#ifdef AFMT_S24_PACKED
+ struct pcm_export_state *pcm_export,
+#endif
+ GError **error_r)
+{
+ int oss_format = sample_format_to_oss(sample_format);
+ if (oss_format == AFMT_QUERY)
+ return UNSUPPORTED;
+
+ enum oss_setup_result result =
+ oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE,
+ &oss_format,
+ "Failed to set sample format", error_r);
+
+#ifdef AFMT_S24_PACKED
+ if (result == UNSUPPORTED && sample_format == SAMPLE_FORMAT_S24_P32) {
+ /* if the driver doesn't support padded 24 bit, try
+ packed 24 bit */
+ oss_format = AFMT_S24_PACKED;
+ result = oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE,
+ &oss_format,
+ "Failed to set sample format", error_r);
+ }
+#endif
+
+ if (result != SUCCESS)
+ return result;
+
+ sample_format = sample_format_from_oss(oss_format);
+ if (sample_format == SAMPLE_FORMAT_UNDEFINED)
+ return UNSUPPORTED;
+
+ *sample_format_r = sample_format;
+ *oss_format_r = oss_format;
+
+#ifdef AFMT_S24_PACKED
+ pcm_export_open(pcm_export, sample_format, 0, false, false,
+ oss_format == AFMT_S24_PACKED,
+ oss_format == AFMT_S24_PACKED &&
+ G_BYTE_ORDER != G_LITTLE_ENDIAN);
+#endif
+
+ return SUCCESS;
+}
+
+/**
+ * Set up the sample format, and attempts to find alternatives if the
+ * specified format is not supported.
+ */
+static bool
+oss_setup_sample_format(int fd, struct audio_format *audio_format,
+ int *oss_format_r,
+#ifdef AFMT_S24_PACKED
+ struct pcm_export_state *pcm_export,
+#endif
+ GError **error_r)
+{
+ enum sample_format mpd_format;
+ enum oss_setup_result result =
+ oss_probe_sample_format(fd, sample_format(audio_format->format),
+ &mpd_format, oss_format_r,
+#ifdef AFMT_S24_PACKED
+ pcm_export,
+#endif
+ error_r);
+ switch (result) {
+ case SUCCESS:
+ audio_format->format = mpd_format;
+ return true;
+
+ case ERROR:
+ return false;
+
+ case UNSUPPORTED:
+ break;
+ }
+
+ if (result != UNSUPPORTED)
+ return result == SUCCESS;
+
+ /* the requested sample format is not available - probe for
+ other formats supported by MPD */
+
+ static const enum sample_format sample_formats[] = {
+ SAMPLE_FORMAT_S24_P32,
+ SAMPLE_FORMAT_S32,
+ SAMPLE_FORMAT_S16,
+ SAMPLE_FORMAT_S8,
+ SAMPLE_FORMAT_UNDEFINED /* sentinel */
+ };
+
+ for (unsigned i = 0; sample_formats[i] != SAMPLE_FORMAT_UNDEFINED; ++i) {
+ mpd_format = sample_formats[i];
+ if (mpd_format == audio_format->format)
+ /* don't try that again */
+ continue;
+
+ result = oss_probe_sample_format(fd, mpd_format,
+ &mpd_format, oss_format_r,
+#ifdef AFMT_S24_PACKED
+ pcm_export,
+#endif
+ error_r);
+ switch (result) {
+ case SUCCESS:
+ audio_format->format = mpd_format;
+ return true;
+
+ case ERROR:
+ return false;
+
+ case UNSUPPORTED:
+ break;
+ }
+ }
+
+ g_set_error_literal(error_r, oss_output_quark(), EINVAL,
+ "Failed to set sample format");
+ return false;
+}
+
+/**
+ * Sets up the OSS device which was opened before.
+ */
+static bool
+oss_setup(struct oss_data *od, struct audio_format *audio_format,
+ GError **error_r)
+{
+ return oss_setup_channels(od->fd, audio_format, error_r) &&
+ oss_setup_sample_rate(od->fd, audio_format, error_r) &&
+ oss_setup_sample_format(od->fd, audio_format, &od->oss_format,
+#ifdef AFMT_S24_PACKED
+ &od->pcm_export,
+#endif
+ error_r);
+}
+
+/**
+ * Reopen the device with the saved audio_format, without any probing.
+ */
+static bool
+oss_reopen(struct oss_data *od, GError **error_r)
+{
+ assert(od->fd < 0);
+
+ od->fd = open_cloexec(od->device, O_WRONLY, 0);
+ if (od->fd < 0) {
+ g_set_error(error_r, oss_output_quark(), errno,
+ "Error opening OSS device \"%s\": %s",
+ od->device, g_strerror(errno));
+ return false;
+ }
+
+ enum oss_setup_result result;
+
+ const char *const msg1 = "Failed to set channel count";
+ result = oss_try_ioctl(od->fd, SNDCTL_DSP_CHANNELS,
+ od->audio_format.channels, msg1, error_r);
+ if (result != SUCCESS) {
+ oss_close(od);
+ if (result == UNSUPPORTED)
+ g_set_error(error_r, oss_output_quark(), EINVAL,
+ "%s", msg1);
+ return false;
+ }
+
+ const char *const msg2 = "Failed to set sample rate";
+ result = oss_try_ioctl(od->fd, SNDCTL_DSP_SPEED,
+ od->audio_format.sample_rate, msg2, error_r);
+ if (result != SUCCESS) {
+ oss_close(od);
+ if (result == UNSUPPORTED)
+ g_set_error(error_r, oss_output_quark(), EINVAL,
+ "%s", msg2);
+ return false;
+ }
+
+ const char *const msg3 = "Failed to set sample format";
+ result = oss_try_ioctl(od->fd, SNDCTL_DSP_SAMPLESIZE,
+ od->oss_format,
+ msg3, error_r);
+ if (result != SUCCESS) {
+ oss_close(od);
+ if (result == UNSUPPORTED)
+ g_set_error(error_r, oss_output_quark(), EINVAL,
+ "%s", msg3);
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+oss_output_open(struct audio_output *ao, struct audio_format *audio_format,
+ GError **error)
+{
+ struct oss_data *od = (struct oss_data *)ao;
+
+ od->fd = open_cloexec(od->device, O_WRONLY, 0);
+ if (od->fd < 0) {
+ g_set_error(error, oss_output_quark(), errno,
+ "Error opening OSS device \"%s\": %s",
+ od->device, g_strerror(errno));
+ return false;
+ }
+
+ if (!oss_setup(od, audio_format, error)) {
+ oss_close(od);
+ return false;
+ }
+
+ od->audio_format = *audio_format;
+ return true;
+}
+
+static void
+oss_output_close(struct audio_output *ao)
+{
+ struct oss_data *od = (struct oss_data *)ao;
+
+ oss_close(od);
+}
+
+static void
+oss_output_cancel(struct audio_output *ao)
+{
+ struct oss_data *od = (struct oss_data *)ao;
+
+ if (od->fd >= 0) {
+ ioctl(od->fd, SNDCTL_DSP_RESET, 0);
+ oss_close(od);
+ }
+}
+
+static size_t
+oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
+ GError **error)
+{
+ struct oss_data *od = (struct oss_data *)ao;
+ ssize_t ret;
+
+ /* reopen the device since it was closed by dropBufferedAudio */
+ if (od->fd < 0 && !oss_reopen(od, error))
+ return 0;
+
+#ifdef AFMT_S24_PACKED
+ chunk = pcm_export(&od->pcm_export, chunk, size, &size);
+#endif
+
+ while (true) {
+ ret = write(od->fd, chunk, size);
+ if (ret > 0) {
+#ifdef AFMT_S24_PACKED
+ ret = pcm_export_source_size(&od->pcm_export, ret);
+#endif
+ return ret;
+ }
+
+ if (ret < 0 && errno != EINTR) {
+ g_set_error(error, oss_output_quark(), errno,
+ "Write error on %s: %s",
+ od->device, g_strerror(errno));
+ return 0;
+ }
+ }
+}
+
+const struct audio_output_plugin oss_output_plugin = {
+ "oss",
+ oss_output_test_default_device,
+ oss_output_init,
+ oss_output_finish,
+#ifdef AFMT_S24_PACKED
+ oss_output_enable,
+ oss_output_disable,
+#else
+ nullptr,
+ nullptr,
+#endif
+ oss_output_open,
+ oss_output_close,
+ nullptr,
+ nullptr,
+ oss_output_play,
+ nullptr,
+ oss_output_cancel,
+ nullptr,
+
+ &oss_mixer_plugin,
+};
diff --git a/src/output/OssOutputPlugin.hxx b/src/output/OssOutputPlugin.hxx
new file mode 100644
index 000000000..6c5c9530b
--- /dev/null
+++ b/src/output/OssOutputPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_OSS_OUTPUT_PLUGIN_HXX
+#define MPD_OSS_OUTPUT_PLUGIN_HXX
+
+extern const struct audio_output_plugin oss_output_plugin;
+
+#endif
diff --git a/src/output/RoarOutputPlugin.cxx b/src/output/RoarOutputPlugin.cxx
new file mode 100644
index 000000000..43aeb09a2
--- /dev/null
+++ b/src/output/RoarOutputPlugin.cxx
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
+ * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
+ *
+ * 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 "RoarOutputPlugin.hxx"
+#include "output_api.h"
+#include "mixer_list.h"
+#include "thread/Mutex.hxx"
+
+#include <glib.h>
+
+#include <roaraudio.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "roaraudio"
+
+struct RoarOutput {
+ struct audio_output base;
+
+ roar_vs_t * vss;
+ int err;
+ char *host;
+ char *name;
+ int role;
+ struct roar_connection con;
+ struct roar_audio_info info;
+ Mutex mutex;
+ volatile bool alive;
+
+ RoarOutput()
+ :err(ROAR_ERROR_NONE),
+ host(nullptr), name(nullptr) {}
+
+ ~RoarOutput() {
+ g_free(host);
+ g_free(name);
+ }
+};
+
+static inline GQuark
+roar_output_quark(void)
+{
+ return g_quark_from_static_string("roar_output");
+}
+
+static int
+roar_output_get_volume_locked(RoarOutput *roar)
+{
+ if (roar->vss == nullptr || !roar->alive)
+ return -1;
+
+ float l, r;
+ int error;
+ if (roar_vs_volume_get(roar->vss, &l, &r, &error) < 0)
+ return -1;
+
+ return (l + r) * 50;
+}
+
+int
+roar_output_get_volume(RoarOutput *roar)
+{
+ const ScopeLock protect(roar->mutex);
+ return roar_output_get_volume_locked(roar);
+}
+
+static bool
+roar_output_set_volume_locked(RoarOutput *roar, unsigned volume)
+{
+ assert(volume <= 100);
+
+ if (roar->vss == nullptr || !roar->alive)
+ return false;
+
+ int error;
+ float level = volume / 100.0;
+
+ roar_vs_volume_mono(roar->vss, level, &error);
+ return true;
+}
+
+bool
+roar_output_set_volume(RoarOutput *roar, unsigned volume)
+{
+ const ScopeLock protect(roar->mutex);
+ return roar_output_set_volume_locked(roar, volume);
+}
+
+static void
+roar_configure(RoarOutput *self, const struct config_param *param)
+{
+ self->host = config_dup_block_string(param, "server", nullptr);
+ self->name = config_dup_block_string(param, "name", "MPD");
+
+ const char *role = config_get_block_string(param, "role", "music");
+ self->role = role != nullptr
+ ? roar_str2role(role)
+ : ROAR_ROLE_MUSIC;
+}
+
+static struct audio_output *
+roar_init(const struct config_param *param, GError **error_r)
+{
+ RoarOutput *self = new RoarOutput();
+
+ if (!ao_base_init(&self->base, &roar_output_plugin, param, error_r)) {
+ delete self;
+ return nullptr;
+ }
+
+ roar_configure(self, param);
+ return &self->base;
+}
+
+static void
+roar_finish(struct audio_output *ao)
+{
+ RoarOutput *self = (RoarOutput *)ao;
+
+ ao_base_finish(&self->base);
+ delete self;
+}
+
+static void
+roar_use_audio_format(struct roar_audio_info *info,
+ struct audio_format *audio_format)
+{
+ info->rate = audio_format->sample_rate;
+ info->channels = audio_format->channels;
+ info->codec = ROAR_CODEC_PCM_S;
+
+ switch (audio_format->format) {
+ case SAMPLE_FORMAT_UNDEFINED:
+ info->bits = 16;
+ audio_format->format = SAMPLE_FORMAT_S16;
+ break;
+
+ case SAMPLE_FORMAT_S8:
+ info->bits = 8;
+ break;
+
+ case SAMPLE_FORMAT_S16:
+ info->bits = 16;
+ break;
+
+ case SAMPLE_FORMAT_S24_P32:
+ info->bits = 32;
+ audio_format->format = SAMPLE_FORMAT_S32;
+ break;
+
+ case SAMPLE_FORMAT_S32:
+ info->bits = 32;
+ break;
+ }
+}
+
+static bool
+roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
+{
+ RoarOutput *self = (RoarOutput *)ao;
+ const ScopeLock protect(self->mutex);
+
+ if (roar_simple_connect(&(self->con), self->host, self->name) < 0)
+ {
+ g_set_error(error, roar_output_quark(), 0,
+ "Failed to connect to Roar server");
+ return false;
+ }
+
+ self->vss = roar_vs_new_from_con(&(self->con), &(self->err));
+
+ if (self->vss == nullptr || self->err != ROAR_ERROR_NONE)
+ {
+ g_set_error(error, roar_output_quark(), 0,
+ "Failed to connect to server");
+ return false;
+ }
+
+ roar_use_audio_format(&self->info, audio_format);
+
+ if (roar_vs_stream(self->vss, &(self->info), ROAR_DIR_PLAY,
+ &(self->err)) < 0)
+ {
+ g_set_error(error, roar_output_quark(), 0, "Failed to start stream");
+ return false;
+ }
+ roar_vs_role(self->vss, self->role, &(self->err));
+ self->alive = true;
+
+ return true;
+}
+
+static void
+roar_close(struct audio_output *ao)
+{
+ RoarOutput *self = (RoarOutput *)ao;
+ const ScopeLock protect(self->mutex);
+
+ self->alive = false;
+
+ if (self->vss != nullptr)
+ roar_vs_close(self->vss, ROAR_VS_TRUE, &(self->err));
+ self->vss = nullptr;
+ roar_disconnect(&(self->con));
+}
+
+static void
+roar_cancel_locked(RoarOutput *self)
+{
+ if (self->vss == nullptr)
+ return;
+
+ roar_vs_t *vss = self->vss;
+ self->vss = nullptr;
+ roar_vs_close(vss, ROAR_VS_TRUE, &(self->err));
+ self->alive = false;
+
+ vss = roar_vs_new_from_con(&(self->con), &(self->err));
+ if (vss == nullptr)
+ return;
+
+ if (roar_vs_stream(vss, &(self->info), ROAR_DIR_PLAY,
+ &(self->err)) < 0) {
+ roar_vs_close(vss, ROAR_VS_TRUE, &(self->err));
+ g_warning("Failed to start stream");
+ return;
+ }
+
+ roar_vs_role(vss, self->role, &(self->err));
+ self->vss = vss;
+ self->alive = true;
+}
+
+static void
+roar_cancel(struct audio_output *ao)
+{
+ RoarOutput *self = (RoarOutput *)ao;
+
+ const ScopeLock protect(self->mutex);
+ roar_cancel_locked(self);
+}
+
+static size_t
+roar_play(struct audio_output *ao, const void *chunk, size_t size, GError **error)
+{
+ RoarOutput *self = (RoarOutput *)ao;
+ ssize_t rc;
+
+ if (self->vss == nullptr)
+ {
+ g_set_error(error, roar_output_quark(), 0, "Connection is invalid");
+ return 0;
+ }
+
+ rc = roar_vs_write(self->vss, chunk, size, &(self->err));
+ if ( rc <= 0 )
+ {
+ g_set_error(error, roar_output_quark(), 0, "Failed to play data");
+ return 0;
+ }
+
+ return rc;
+}
+
+static const char*
+roar_tag_convert(enum tag_type type, bool *is_uuid)
+{
+ *is_uuid = false;
+ switch (type)
+ {
+ case TAG_ARTIST:
+ case TAG_ALBUM_ARTIST:
+ return "AUTHOR";
+ case TAG_ALBUM:
+ return "ALBUM";
+ case TAG_TITLE:
+ return "TITLE";
+ case TAG_TRACK:
+ return "TRACK";
+ case TAG_NAME:
+ return "NAME";
+ case TAG_GENRE:
+ return "GENRE";
+ case TAG_DATE:
+ return "DATE";
+ case TAG_PERFORMER:
+ return "PERFORMER";
+ case TAG_COMMENT:
+ return "COMMENT";
+ case TAG_DISC:
+ return "DISCID";
+ case TAG_COMPOSER:
+#ifdef ROAR_META_TYPE_COMPOSER
+ return "COMPOSER";
+#else
+ return "AUTHOR";
+#endif
+ case TAG_MUSICBRAINZ_ARTISTID:
+ case TAG_MUSICBRAINZ_ALBUMID:
+ case TAG_MUSICBRAINZ_ALBUMARTISTID:
+ case TAG_MUSICBRAINZ_TRACKID:
+ *is_uuid = true;
+ return "HASH";
+
+ default:
+ return nullptr;
+ }
+}
+
+static void
+roar_send_tag(struct audio_output *ao, const struct tag *meta)
+{
+ RoarOutput *self = (RoarOutput *)ao;
+
+ if (self->vss == nullptr)
+ return;
+
+ const ScopeLock protect(self->mutex);
+
+ size_t cnt = 1;
+ struct roar_keyval vals[32];
+ memset(vals, 0, sizeof(vals));
+ char uuid_buf[32][64];
+
+ char timebuf[16];
+ snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d",
+ meta->time / 3600, (meta->time % 3600) / 60, meta->time % 60);
+
+ vals[0].key = g_strdup("LENGTH");
+ vals[0].value = timebuf;
+
+ for (unsigned i = 0; i < meta->num_items && cnt < 32; i++)
+ {
+ bool is_uuid = false;
+ const char *key = roar_tag_convert(meta->items[i]->type, &is_uuid);
+ if (key != nullptr)
+ {
+ if (is_uuid)
+ {
+ snprintf(uuid_buf[cnt], sizeof(uuid_buf[0]), "{UUID}%s",
+ meta->items[i]->value);
+ vals[cnt].key = g_strdup(key);
+ vals[cnt].value = uuid_buf[cnt];
+ }
+ else
+ {
+ vals[cnt].key = g_strdup(key);
+ vals[cnt].value = meta->items[i]->value;
+ }
+ cnt++;
+ }
+ }
+
+ roar_vs_meta(self->vss, vals, cnt, &(self->err));
+
+ for (unsigned i = 0; i < 32; i++)
+ g_free(vals[i].key);
+}
+
+const struct audio_output_plugin roar_output_plugin = {
+ "roar",
+ nullptr,
+ roar_init,
+ roar_finish,
+ nullptr,
+ nullptr,
+ roar_open,
+ roar_close,
+ nullptr,
+ roar_send_tag,
+ roar_play,
+ nullptr,
+ roar_cancel,
+ nullptr,
+ &roar_mixer_plugin,
+};
diff --git a/src/output/RoarOutputPlugin.hxx b/src/output/RoarOutputPlugin.hxx
new file mode 100644
index 000000000..faa4b4d5c
--- /dev/null
+++ b/src/output/RoarOutputPlugin.hxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2013 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_ROAR_OUTPUT_PLUGIN_H
+#define MPD_ROAR_OUTPUT_PLUGIN_H
+
+struct RoarOutput;
+
+extern const struct audio_output_plugin roar_output_plugin;
+
+int
+roar_output_get_volume(RoarOutput *roar);
+
+bool
+roar_output_set_volume(RoarOutput *roar, unsigned volume);
+
+#endif
diff --git a/src/output/alsa_output_plugin.c b/src/output/alsa_output_plugin.c
deleted file mode 100644
index d8b184273..000000000
--- a/src/output/alsa_output_plugin.c
+++ /dev/null
@@ -1,819 +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 "alsa_output_plugin.h"
-#include "output_api.h"
-#include "mixer_list.h"
-#include "pcm_export.h"
-
-#include <glib.h>
-#include <alsa/asoundlib.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "alsa"
-
-#define ALSA_PCM_NEW_HW_PARAMS_API
-#define ALSA_PCM_NEW_SW_PARAMS_API
-
-static const char default_device[] = "default";
-
-enum {
- MPD_ALSA_BUFFER_TIME_US = 500000,
-};
-
-#define MPD_ALSA_RETRY_NR 5
-
-typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
- snd_pcm_uframes_t size);
-
-struct alsa_data {
- struct audio_output base;
-
- struct pcm_export_state export;
-
- /** the configured name of the ALSA device; NULL for the
- default device */
- char *device;
-
- /** use memory mapped I/O? */
- bool use_mmap;
-
- /**
- * Enable DSD over USB according to the dCS suggested
- * standard?
- *
- * @see http://www.dcsltd.co.uk/page/assets/DSDoverUSB.pdf
- */
- bool dsd_usb;
-
- /** libasound's buffer_time setting (in microseconds) */
- unsigned int buffer_time;
-
- /** libasound's period_time setting (in microseconds) */
- unsigned int period_time;
-
- /** the mode flags passed to snd_pcm_open */
- int mode;
-
- /** the libasound PCM device handle */
- snd_pcm_t *pcm;
-
- /**
- * a pointer to the libasound writei() function, which is
- * snd_pcm_writei() or snd_pcm_mmap_writei(), depending on the
- * use_mmap configuration
- */
- alsa_writei_t *writei;
-
- /**
- * The size of one audio frame passed to method play().
- */
- size_t in_frame_size;
-
- /**
- * The size of one audio frame passed to libasound.
- */
- size_t out_frame_size;
-
- /**
- * The size of one period, in number of frames.
- */
- snd_pcm_uframes_t period_frames;
-
- /**
- * The number of frames written in the current period.
- */
- snd_pcm_uframes_t period_position;
-};
-
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-alsa_output_quark(void)
-{
- return g_quark_from_static_string("alsa_output");
-}
-
-static const char *
-alsa_device(const struct alsa_data *ad)
-{
- return ad->device != NULL ? ad->device : default_device;
-}
-
-static struct alsa_data *
-alsa_data_new(void)
-{
- struct alsa_data *ret = g_new(struct alsa_data, 1);
-
- ret->mode = 0;
- ret->writei = snd_pcm_writei;
-
- return ret;
-}
-
-static void
-alsa_configure(struct alsa_data *ad, const struct config_param *param)
-{
- ad->device = config_dup_block_string(param, "device", NULL);
-
- ad->use_mmap = config_get_block_bool(param, "use_mmap", false);
-
- ad->dsd_usb = config_get_block_bool(param, "dsd_usb", false);
-
- ad->buffer_time = config_get_block_unsigned(param, "buffer_time",
- MPD_ALSA_BUFFER_TIME_US);
- ad->period_time = config_get_block_unsigned(param, "period_time", 0);
-
-#ifdef SND_PCM_NO_AUTO_RESAMPLE
- if (!config_get_block_bool(param, "auto_resample", true))
- ad->mode |= SND_PCM_NO_AUTO_RESAMPLE;
-#endif
-
-#ifdef SND_PCM_NO_AUTO_CHANNELS
- if (!config_get_block_bool(param, "auto_channels", true))
- ad->mode |= SND_PCM_NO_AUTO_CHANNELS;
-#endif
-
-#ifdef SND_PCM_NO_AUTO_FORMAT
- if (!config_get_block_bool(param, "auto_format", true))
- ad->mode |= SND_PCM_NO_AUTO_FORMAT;
-#endif
-}
-
-static struct audio_output *
-alsa_init(const struct config_param *param, GError **error_r)
-{
- struct alsa_data *ad = alsa_data_new();
-
- if (!ao_base_init(&ad->base, &alsa_output_plugin, param, error_r)) {
- g_free(ad);
- return NULL;
- }
-
- alsa_configure(ad, param);
-
- return &ad->base;
-}
-
-static void
-alsa_finish(struct audio_output *ao)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
-
- ao_base_finish(&ad->base);
-
- g_free(ad->device);
- g_free(ad);
-
- /* free libasound's config cache */
- snd_config_update_free_global();
-}
-
-static bool
-alsa_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
-
- pcm_export_init(&ad->export);
- return true;
-}
-
-static void
-alsa_output_disable(struct audio_output *ao)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
-
- pcm_export_deinit(&ad->export);
-}
-
-static bool
-alsa_test_default_device(void)
-{
- snd_pcm_t *handle;
-
- int ret = snd_pcm_open(&handle, default_device,
- SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
- if (ret) {
- g_message("Error opening default ALSA device: %s\n",
- snd_strerror(-ret));
- return false;
- } else
- snd_pcm_close(handle);
-
- return true;
-}
-
-static snd_pcm_format_t
-get_bitformat(enum sample_format sample_format)
-{
- switch (sample_format) {
- case SAMPLE_FORMAT_UNDEFINED:
- case SAMPLE_FORMAT_DSD:
- return SND_PCM_FORMAT_UNKNOWN;
-
- case SAMPLE_FORMAT_S8:
- return SND_PCM_FORMAT_S8;
-
- case SAMPLE_FORMAT_S16:
- return SND_PCM_FORMAT_S16;
-
- case SAMPLE_FORMAT_S24_P32:
- return SND_PCM_FORMAT_S24;
-
- case SAMPLE_FORMAT_S32:
- return SND_PCM_FORMAT_S32;
-
- case SAMPLE_FORMAT_FLOAT:
- return SND_PCM_FORMAT_FLOAT;
- }
-
- assert(false);
- return SND_PCM_FORMAT_UNKNOWN;
-}
-
-static snd_pcm_format_t
-byteswap_bitformat(snd_pcm_format_t fmt)
-{
- switch(fmt) {
- case SND_PCM_FORMAT_S16_LE: return SND_PCM_FORMAT_S16_BE;
- case SND_PCM_FORMAT_S24_LE: return SND_PCM_FORMAT_S24_BE;
- case SND_PCM_FORMAT_S32_LE: return SND_PCM_FORMAT_S32_BE;
- case SND_PCM_FORMAT_S16_BE: return SND_PCM_FORMAT_S16_LE;
- case SND_PCM_FORMAT_S24_BE: return SND_PCM_FORMAT_S24_LE;
-
- case SND_PCM_FORMAT_S24_3BE:
- return SND_PCM_FORMAT_S24_3LE;
-
- case SND_PCM_FORMAT_S24_3LE:
- return SND_PCM_FORMAT_S24_3BE;
-
- case SND_PCM_FORMAT_S32_BE: return SND_PCM_FORMAT_S32_LE;
- default: return SND_PCM_FORMAT_UNKNOWN;
- }
-}
-
-static snd_pcm_format_t
-alsa_to_packed_format(snd_pcm_format_t fmt)
-{
- switch (fmt) {
- case SND_PCM_FORMAT_S24_LE:
- return SND_PCM_FORMAT_S24_3LE;
-
- case SND_PCM_FORMAT_S24_BE:
- return SND_PCM_FORMAT_S24_3BE;
-
- default:
- return SND_PCM_FORMAT_UNKNOWN;
- }
-}
-
-static int
-alsa_try_format_or_packed(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
- snd_pcm_format_t fmt, bool *packed_r)
-{
- int err = snd_pcm_hw_params_set_format(pcm, hwparams, fmt);
- if (err == 0)
- *packed_r = false;
-
- if (err != -EINVAL)
- return err;
-
- fmt = alsa_to_packed_format(fmt);
- if (fmt == SND_PCM_FORMAT_UNKNOWN)
- return -EINVAL;
-
- err = snd_pcm_hw_params_set_format(pcm, hwparams, fmt);
- if (err == 0)
- *packed_r = true;
-
- return err;
-}
-
-/**
- * Attempts to configure the specified sample format, and tries the
- * reversed host byte order if was not supported.
- */
-static int
-alsa_output_try_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
- enum sample_format sample_format,
- bool *packed_r, bool *reverse_endian_r)
-{
- snd_pcm_format_t alsa_format = get_bitformat(sample_format);
- if (alsa_format == SND_PCM_FORMAT_UNKNOWN)
- return -EINVAL;
-
- int err = alsa_try_format_or_packed(pcm, hwparams, alsa_format,
- packed_r);
- if (err == 0)
- *reverse_endian_r = false;
-
- if (err != -EINVAL)
- return err;
-
- alsa_format = byteswap_bitformat(alsa_format);
- if (alsa_format == SND_PCM_FORMAT_UNKNOWN)
- return -EINVAL;
-
- err = alsa_try_format_or_packed(pcm, hwparams, alsa_format, packed_r);
- if (err == 0)
- *reverse_endian_r = true;
-
- return err;
-}
-
-/**
- * Configure a sample format, and probe other formats if that fails.
- */
-static int
-alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
- struct audio_format *audio_format,
- bool *packed_r, bool *reverse_endian_r)
-{
- /* try the input format first */
-
- int err = alsa_output_try_format(pcm, hwparams, audio_format->format,
- packed_r, reverse_endian_r);
-
- /* if unsupported by the hardware, try other formats */
-
- static const enum sample_format probe_formats[] = {
- SAMPLE_FORMAT_S24_P32,
- SAMPLE_FORMAT_S32,
- SAMPLE_FORMAT_S16,
- SAMPLE_FORMAT_S8,
- SAMPLE_FORMAT_UNDEFINED,
- };
-
- for (unsigned i = 0;
- err == -EINVAL && probe_formats[i] != SAMPLE_FORMAT_UNDEFINED;
- ++i) {
- const enum sample_format mpd_format = probe_formats[i];
- if (mpd_format == audio_format->format)
- continue;
-
- err = alsa_output_try_format(pcm, hwparams, mpd_format,
- packed_r, reverse_endian_r);
- if (err == 0)
- audio_format->format = mpd_format;
- }
-
- return err;
-}
-
-/**
- * Set up the snd_pcm_t object which was opened by the caller. Set up
- * the configured settings and the audio format.
- */
-static bool
-alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
- bool *packed_r, bool *reverse_endian_r, GError **error)
-{
- snd_pcm_hw_params_t *hwparams;
- snd_pcm_sw_params_t *swparams;
- unsigned int sample_rate = audio_format->sample_rate;
- unsigned int channels = audio_format->channels;
- snd_pcm_uframes_t alsa_buffer_size;
- snd_pcm_uframes_t alsa_period_size;
- int err;
- const char *cmd = NULL;
- int retry = MPD_ALSA_RETRY_NR;
- unsigned int period_time, period_time_ro;
- unsigned int buffer_time;
-
- period_time_ro = period_time = ad->period_time;
-configure_hw:
- /* configure HW params */
- snd_pcm_hw_params_alloca(&hwparams);
- cmd = "snd_pcm_hw_params_any";
- err = snd_pcm_hw_params_any(ad->pcm, hwparams);
- if (err < 0)
- goto error;
-
- if (ad->use_mmap) {
- err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
- SND_PCM_ACCESS_MMAP_INTERLEAVED);
- if (err < 0) {
- g_warning("Cannot set mmap'ed mode on ALSA device \"%s\": %s\n",
- alsa_device(ad), snd_strerror(-err));
- g_warning("Falling back to direct write mode\n");
- ad->use_mmap = false;
- } else
- ad->writei = snd_pcm_mmap_writei;
- }
-
- if (!ad->use_mmap) {
- cmd = "snd_pcm_hw_params_set_access";
- err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
- SND_PCM_ACCESS_RW_INTERLEAVED);
- if (err < 0)
- goto error;
- ad->writei = snd_pcm_writei;
- }
-
- err = alsa_output_setup_format(ad->pcm, hwparams, audio_format,
- packed_r, reverse_endian_r);
- if (err < 0) {
- g_set_error(error, alsa_output_quark(), err,
- "ALSA device \"%s\" does not support format %s: %s",
- alsa_device(ad),
- sample_format_to_string(audio_format->format),
- snd_strerror(-err));
- return false;
- }
-
- snd_pcm_format_t format;
- if (snd_pcm_hw_params_get_format(hwparams, &format) == 0)
- g_debug("format=%s (%s)", snd_pcm_format_name(format),
- snd_pcm_format_description(format));
-
- err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams,
- &channels);
- if (err < 0) {
- g_set_error(error, alsa_output_quark(), err,
- "ALSA device \"%s\" does not support %i channels: %s",
- alsa_device(ad), (int)audio_format->channels,
- snd_strerror(-err));
- return false;
- }
- audio_format->channels = (int8_t)channels;
-
- err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams,
- &sample_rate, NULL);
- if (err < 0 || sample_rate == 0) {
- g_set_error(error, alsa_output_quark(), err,
- "ALSA device \"%s\" does not support %u Hz audio",
- alsa_device(ad), audio_format->sample_rate);
- return false;
- }
- audio_format->sample_rate = sample_rate;
-
- snd_pcm_uframes_t buffer_size_min, buffer_size_max;
- snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
- snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
- unsigned buffer_time_min, buffer_time_max;
- snd_pcm_hw_params_get_buffer_time_min(hwparams, &buffer_time_min, 0);
- snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time_max, 0);
- g_debug("buffer: size=%u..%u time=%u..%u",
- (unsigned)buffer_size_min, (unsigned)buffer_size_max,
- buffer_time_min, buffer_time_max);
-
- snd_pcm_uframes_t period_size_min, period_size_max;
- snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, 0);
- snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, 0);
- unsigned period_time_min, period_time_max;
- snd_pcm_hw_params_get_period_time_min(hwparams, &period_time_min, 0);
- snd_pcm_hw_params_get_period_time_max(hwparams, &period_time_max, 0);
- g_debug("period: size=%u..%u time=%u..%u",
- (unsigned)period_size_min, (unsigned)period_size_max,
- period_time_min, period_time_max);
-
- if (ad->buffer_time > 0) {
- buffer_time = ad->buffer_time;
- cmd = "snd_pcm_hw_params_set_buffer_time_near";
- err = snd_pcm_hw_params_set_buffer_time_near(ad->pcm, hwparams,
- &buffer_time, NULL);
- if (err < 0)
- goto error;
- } else {
- err = snd_pcm_hw_params_get_buffer_time(hwparams, &buffer_time,
- NULL);
- if (err < 0)
- buffer_time = 0;
- }
-
- if (period_time_ro == 0 && buffer_time >= 10000) {
- period_time_ro = period_time = buffer_time / 4;
-
- g_debug("default period_time = buffer_time/4 = %u/4 = %u",
- buffer_time, period_time);
- }
-
- if (period_time_ro > 0) {
- period_time = period_time_ro;
- cmd = "snd_pcm_hw_params_set_period_time_near";
- err = snd_pcm_hw_params_set_period_time_near(ad->pcm, hwparams,
- &period_time, NULL);
- if (err < 0)
- goto error;
- }
-
- cmd = "snd_pcm_hw_params";
- err = snd_pcm_hw_params(ad->pcm, hwparams);
- if (err == -EPIPE && --retry > 0 && period_time_ro > 0) {
- period_time_ro = period_time_ro >> 1;
- goto configure_hw;
- } else if (err < 0)
- goto error;
- if (retry != MPD_ALSA_RETRY_NR)
- g_debug("ALSA period_time set to %d\n", period_time);
-
- cmd = "snd_pcm_hw_params_get_buffer_size";
- err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
- if (err < 0)
- goto error;
-
- cmd = "snd_pcm_hw_params_get_period_size";
- err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
- NULL);
- if (err < 0)
- goto error;
-
- /* configure SW params */
- snd_pcm_sw_params_alloca(&swparams);
-
- cmd = "snd_pcm_sw_params_current";
- err = snd_pcm_sw_params_current(ad->pcm, swparams);
- if (err < 0)
- goto error;
-
- cmd = "snd_pcm_sw_params_set_start_threshold";
- err = snd_pcm_sw_params_set_start_threshold(ad->pcm, swparams,
- alsa_buffer_size -
- alsa_period_size);
- if (err < 0)
- goto error;
-
- cmd = "snd_pcm_sw_params_set_avail_min";
- err = snd_pcm_sw_params_set_avail_min(ad->pcm, swparams,
- alsa_period_size);
- if (err < 0)
- goto error;
-
- cmd = "snd_pcm_sw_params";
- err = snd_pcm_sw_params(ad->pcm, swparams);
- if (err < 0)
- goto error;
-
- g_debug("buffer_size=%u period_size=%u",
- (unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
-
- if (alsa_period_size == 0)
- /* this works around a SIGFPE bug that occurred when
- an ALSA driver indicated period_size==0; this
- caused a division by zero in alsa_play(). By using
- the fallback "1", we make sure that this won't
- happen again. */
- alsa_period_size = 1;
-
- ad->period_frames = alsa_period_size;
- ad->period_position = 0;
-
- return true;
-
-error:
- g_set_error(error, alsa_output_quark(), err,
- "Error opening ALSA device \"%s\" (%s): %s",
- alsa_device(ad), cmd, snd_strerror(-err));
- return false;
-}
-
-static bool
-alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format,
- bool *shift8_r, bool *packed_r, bool *reverse_endian_r,
- GError **error_r)
-{
- assert(ad->dsd_usb);
- assert(audio_format->format == SAMPLE_FORMAT_DSD);
-
- /* pass 24 bit to alsa_setup() */
-
- struct audio_format usb_format = *audio_format;
- usb_format.format = SAMPLE_FORMAT_S24_P32;
- usb_format.sample_rate /= 2;
-
- const struct audio_format check = usb_format;
-
- if (!alsa_setup(ad, &usb_format, packed_r, reverse_endian_r, error_r))
- return false;
-
- /* if the device allows only 32 bit, shift all DSD-over-USB
- samples left by 8 bit and leave the lower 8 bit cleared;
- the DSD-over-USB documentation does not specify whether
- this is legal, but there is anecdotical evidence that this
- is possible (and the only option for some devices) */
- *shift8_r = usb_format.format == SAMPLE_FORMAT_S32;
- if (usb_format.format == SAMPLE_FORMAT_S32)
- usb_format.format = SAMPLE_FORMAT_S24_P32;
-
- if (!audio_format_equals(&usb_format, &check)) {
- /* no bit-perfect playback, which is required
- for DSD over USB */
- g_set_error(error_r, alsa_output_quark(), 0,
- "Failed to configure DSD-over-USB on ALSA device \"%s\"",
- alsa_device(ad));
- return false;
- }
-
- return true;
-}
-
-static bool
-alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
- GError **error_r)
-{
- bool shift8 = false, packed, reverse_endian;
-
- const bool dsd_usb = ad->dsd_usb &&
- audio_format->format == SAMPLE_FORMAT_DSD;
- const bool success = dsd_usb
- ? alsa_setup_dsd(ad, audio_format,
- &shift8, &packed, &reverse_endian,
- error_r)
- : alsa_setup(ad, audio_format, &packed, &reverse_endian,
- error_r);
- if (!success)
- return false;
-
- pcm_export_open(&ad->export,
- audio_format->format, audio_format->channels,
- dsd_usb, shift8, packed, reverse_endian);
- return true;
-}
-
-static bool
-alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
- int err;
- bool success;
-
- err = snd_pcm_open(&ad->pcm, alsa_device(ad),
- SND_PCM_STREAM_PLAYBACK, ad->mode);
- if (err < 0) {
- g_set_error(error, alsa_output_quark(), err,
- "Failed to open ALSA device \"%s\": %s",
- alsa_device(ad), snd_strerror(err));
- return false;
- }
-
- g_debug("opened %s type=%s", snd_pcm_name(ad->pcm),
- snd_pcm_type_name(snd_pcm_type(ad->pcm)));
-
- success = alsa_setup_or_dsd(ad, audio_format, error);
- if (!success) {
- snd_pcm_close(ad->pcm);
- return false;
- }
-
- ad->in_frame_size = audio_format_frame_size(audio_format);
- ad->out_frame_size = pcm_export_frame_size(&ad->export, audio_format);
-
- return true;
-}
-
-static int
-alsa_recover(struct alsa_data *ad, int err)
-{
- if (err == -EPIPE) {
- g_debug("Underrun on ALSA device \"%s\"\n", alsa_device(ad));
- } else if (err == -ESTRPIPE) {
- g_debug("ALSA device \"%s\" was suspended\n", alsa_device(ad));
- }
-
- switch (snd_pcm_state(ad->pcm)) {
- case SND_PCM_STATE_PAUSED:
- err = snd_pcm_pause(ad->pcm, /* disable */ 0);
- break;
- case SND_PCM_STATE_SUSPENDED:
- err = snd_pcm_resume(ad->pcm);
- if (err == -EAGAIN)
- return 0;
- /* fall-through to snd_pcm_prepare: */
- case SND_PCM_STATE_SETUP:
- case SND_PCM_STATE_XRUN:
- ad->period_position = 0;
- err = snd_pcm_prepare(ad->pcm);
- break;
- case SND_PCM_STATE_DISCONNECTED:
- break;
- /* this is no error, so just keep running */
- case SND_PCM_STATE_RUNNING:
- err = 0;
- break;
- default:
- /* unknown state, do nothing */
- break;
- }
-
- return err;
-}
-
-static void
-alsa_drain(struct audio_output *ao)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
-
- if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
- return;
-
- if (ad->period_position > 0) {
- /* generate some silence to finish the partial
- period */
- snd_pcm_uframes_t nframes =
- ad->period_frames - ad->period_position;
- size_t nbytes = nframes * ad->out_frame_size;
- void *buffer = g_malloc(nbytes);
- snd_pcm_hw_params_t *params;
- snd_pcm_format_t format;
- unsigned channels;
-
- snd_pcm_hw_params_alloca(&params);
- snd_pcm_hw_params_current(ad->pcm, params);
- snd_pcm_hw_params_get_format(params, &format);
- snd_pcm_hw_params_get_channels(params, &channels);
-
- snd_pcm_format_set_silence(format, buffer, nframes * channels);
- ad->writei(ad->pcm, buffer, nframes);
- g_free(buffer);
- }
-
- snd_pcm_drain(ad->pcm);
-
- ad->period_position = 0;
-}
-
-static void
-alsa_cancel(struct audio_output *ao)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
-
- ad->period_position = 0;
-
- snd_pcm_drop(ad->pcm);
-}
-
-static void
-alsa_close(struct audio_output *ao)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
-
- snd_pcm_close(ad->pcm);
-}
-
-static size_t
-alsa_play(struct audio_output *ao, const void *chunk, size_t size,
- GError **error)
-{
- struct alsa_data *ad = (struct alsa_data *)ao;
-
- assert(size % ad->in_frame_size == 0);
-
- chunk = pcm_export(&ad->export, chunk, size, &size);
-
- assert(size % ad->out_frame_size == 0);
-
- size /= ad->out_frame_size;
-
- while (true) {
- snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size);
- if (ret > 0) {
- ad->period_position = (ad->period_position + ret)
- % ad->period_frames;
-
- size_t bytes_written = ret * ad->out_frame_size;
- return pcm_export_source_size(&ad->export,
- bytes_written);
- }
-
- if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
- alsa_recover(ad, ret) < 0) {
- g_set_error(error, alsa_output_quark(), errno,
- "%s", snd_strerror(-errno));
- return 0;
- }
- }
-}
-
-const struct audio_output_plugin alsa_output_plugin = {
- .name = "alsa",
- .test_default_device = alsa_test_default_device,
- .init = alsa_init,
- .finish = alsa_finish,
- .enable = alsa_output_enable,
- .disable = alsa_output_disable,
- .open = alsa_open,
- .play = alsa_play,
- .drain = alsa_drain,
- .cancel = alsa_cancel,
- .close = alsa_close,
-
- .mixer_plugin = &alsa_mixer_plugin,
-};
diff --git a/src/output/alsa_output_plugin.h b/src/output/alsa_output_plugin.h
deleted file mode 100644
index daa1f3615..000000000
--- a/src/output/alsa_output_plugin.h
+++ /dev/null
@@ -1,25 +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.
- */
-
-#ifndef MPD_ALSA_OUTPUT_PLUGIN_H
-#define MPD_ALSA_OUTPUT_PLUGIN_H
-
-extern const struct audio_output_plugin alsa_output_plugin;
-
-#endif
diff --git a/src/output/fifo_output_plugin.c b/src/output/fifo_output_plugin.c
index 022be0b4a..3d6171ae2 100644
--- a/src/output/fifo_output_plugin.c
+++ b/src/output/fifo_output_plugin.c
@@ -20,7 +20,6 @@
#include "config.h"
#include "fifo_output_plugin.h"
#include "output_api.h"
-#include "utils.h"
#include "timer.h"
#include "fd_util.h"
#include "open.h"
diff --git a/src/output/httpd_client.c b/src/output/httpd_client.c
deleted file mode 100644
index 72de90457..000000000
--- a/src/output/httpd_client.c
+++ /dev/null
@@ -1,764 +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 "httpd_client.h"
-#include "httpd_internal.h"
-#include "fifo_buffer.h"
-#include "page.h"
-#include "icy_server.h"
-#include "glib_socket.h"
-
-#include <stdbool.h>
-#include <assert.h>
-#include <string.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "httpd_output"
-
-struct httpd_client {
- /**
- * The httpd output object this client is connected to.
- */
- struct httpd_output *httpd;
-
- /**
- * The TCP socket.
- */
- GIOChannel *channel;
-
- /**
- * The GLib main loop source id for reading from the socket,
- * and to detect errors.
- */
- guint read_source_id;
-
- /**
- * The GLib main loop source id for writing to the socket. If
- * 0, then there is no event source currently (because there
- * are no queued pages).
- */
- guint write_source_id;
-
- /**
- * For buffered reading. This pointer is only valid while the
- * HTTP request is read.
- */
- struct fifo_buffer *input;
-
- /**
- * The current state of the client.
- */
- enum {
- /** reading the request line */
- REQUEST,
-
- /** reading the request headers */
- HEADERS,
-
- /** sending the HTTP response */
- RESPONSE,
- } state;
-
- /**
- * A queue of #page objects to be sent to the client.
- */
- GQueue *pages;
-
- /**
- * The #page which is currently being sent to the client.
- */
- struct page *current_page;
-
- /**
- * The amount of bytes which were already sent from
- * #current_page.
- */
- size_t current_position;
-
- /**
- * If DLNA streaming was an option.
- */
- bool dlna_streaming_requested;
-
- /* ICY */
-
- /**
- * Do we support sending Icy-Metadata to the client? This is
- * disabled if the httpd audio output uses encoder tags.
- */
- bool metadata_supported;
-
- /**
- * If we should sent icy metadata.
- */
- bool metadata_requested;
-
- /**
- * If the current metadata was already sent to the client.
- */
- bool metadata_sent;
-
- /**
- * The amount of streaming data between each metadata block
- */
- guint metaint;
-
- /**
- * The metadata as #page which is currently being sent to the client.
- */
- struct page *metadata;
-
- /*
- * The amount of bytes which were already sent from the metadata.
- */
- size_t metadata_current_position;
-
- /**
- * The amount of streaming data sent to the client
- * since the last icy information was sent.
- */
- guint metadata_fill;
-};
-
-static void
-httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct page *page = data;
-
- page_unref(page);
-}
-
-void
-httpd_client_free(struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->state == RESPONSE) {
- if (client->write_source_id != 0)
- g_source_remove(client->write_source_id);
-
- if (client->current_page != NULL)
- page_unref(client->current_page);
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_free(client->pages);
- } else
- fifo_buffer_free(client->input);
-
- if (client->metadata)
- page_unref (client->metadata);
-
- g_source_remove(client->read_source_id);
- g_io_channel_unref(client->channel);
- g_free(client);
-}
-
-/**
- * Frees the client and removes it from the server's client list.
- */
-static void
-httpd_client_close(struct httpd_client *client)
-{
- assert(client != NULL);
-
- httpd_output_remove_client(client->httpd, client);
- httpd_client_free(client);
-}
-
-/**
- * Switch the client to the "RESPONSE" state.
- */
-static void
-httpd_client_begin_response(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- client->state = RESPONSE;
- client->write_source_id = 0;
- client->pages = g_queue_new();
- client->current_page = NULL;
-
- httpd_output_send_header(client->httpd, client);
-}
-
-/**
- * Handle a line of the HTTP request.
- */
-static bool
-httpd_client_handle_line(struct httpd_client *client, const char *line)
-{
- assert(client->state != RESPONSE);
-
- if (client->state == REQUEST) {
- if (strncmp(line, "GET /", 5) != 0) {
- /* only GET is supported */
- g_warning("malformed request line from client");
- return false;
- }
-
- line = strchr(line + 5, ' ');
- if (line == NULL || strncmp(line + 1, "HTTP/", 5) != 0) {
- /* HTTP/0.9 without request headers */
- httpd_client_begin_response(client);
- return true;
- }
-
- /* after the request line, request headers follow */
- client->state = HEADERS;
- return true;
- } else {
- if (*line == 0) {
- /* empty line: request is finished */
- httpd_client_begin_response(client);
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
- /* Send icy metadata */
- client->metadata_requested =
- client->metadata_supported;
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
- /* Send as dlna */
- client->dlna_streaming_requested = true;
- /* metadata is not supported by dlna streaming, so disable it */
- client->metadata_supported = false;
- client->metadata_requested = false;
- return true;
- }
-
- /* expect more request headers */
- return true;
- }
-}
-
-/**
- * Check if a complete line of input is present in the input buffer,
- * and duplicates it. It is removed from the input buffer. The
- * return value has to be freed with g_free().
- */
-static char *
-httpd_client_read_line(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- const char *p, *newline;
- size_t length;
- char *line;
-
- p = fifo_buffer_read(client->input, &length);
- if (p == NULL)
- /* empty input buffer */
- return NULL;
-
- newline = memchr(p, '\n', length);
- if (newline == NULL)
- /* incomplete line */
- return NULL;
-
- line = g_strndup(p, newline - p);
- fifo_buffer_consume(client->input, newline - p + 1);
-
- /* remove trailing whitespace (e.g. '\r') */
- return g_strchomp(line);
-}
-
-/**
- * Sends the status line and response headers to the client.
- */
-static bool
-httpd_client_send_response(struct httpd_client *client)
-{
- char buffer[1024];
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->state == RESPONSE);
-
- if (client->dlna_streaming_requested) {
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 206 OK\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: 10000\r\n"
- "Content-RangeX: 0-1000000/1000000\r\n"
- "transferMode.dlna.org: Streaming\r\n"
- "Accept-Ranges: bytes\r\n"
- "Connection: close\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
- "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
- "\r\n",
- client->httpd->content_type);
-
- } else if (client->metadata_requested) {
- gchar *metadata_header;
-
- metadata_header = icy_server_metadata_header(
- client->httpd->name,
- client->httpd->genre,
- client->httpd->website,
- client->httpd->content_type,
- client->metaint);
-
- g_strlcpy(buffer, metadata_header, sizeof(buffer));
-
- g_free(metadata_header);
-
- } else { /* revert to a normal HTTP request */
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: %s\r\n"
- "Connection: close\r\n"
- "Pragma: no-cache\r\n"
- "Cache-Control: no-cache, no-store\r\n"
- "\r\n",
- client->httpd->content_type);
- }
-
- status = g_io_channel_write_chars(client->channel,
- buffer, strlen(buffer),
- &bytes_written, &error);
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- case G_IO_STATUS_AGAIN:
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- return false;
-}
-
-/**
- * Data has been received from the client and it is appended to the
- * input buffer.
- */
-static bool
-httpd_client_received(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- char *line;
- bool success;
-
- while ((line = httpd_client_read_line(client)) != NULL) {
- success = httpd_client_handle_line(client, line);
- g_free(line);
- if (!success) {
- assert(client->state != RESPONSE);
- return false;
- }
-
- if (client->state == RESPONSE) {
- if (!fifo_buffer_is_empty(client->input)) {
- g_warning("unexpected input from client");
- return false;
- }
-
- fifo_buffer_free(client->input);
-
- return httpd_client_send_response(client);
- }
- }
-
- return true;
-}
-
-static bool
-httpd_client_read(struct httpd_client *client)
-{
- char *p;
- size_t max_length;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_read;
-
- if (client->state == RESPONSE) {
- /* the client has already sent the request, and he
- must not send more */
- char buffer[1];
-
- status = g_io_channel_read_chars(client->channel, buffer,
- sizeof(buffer), &bytes_read,
- NULL);
- if (status == G_IO_STATUS_NORMAL)
- g_warning("unexpected input from client");
-
- return false;
- }
-
- p = fifo_buffer_write(client->input, &max_length);
- if (p == NULL) {
- g_warning("buffer overflow");
- return false;
- }
-
- status = g_io_channel_read_chars(client->channel, p, max_length,
- &bytes_read, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- fifo_buffer_append(client->input, bytes_read);
- return httpd_client_received(client);
-
- case G_IO_STATUS_AGAIN:
- /* try again later, after select() */
- return true;
-
- case G_IO_STATUS_EOF:
- /* peer disconnected */
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
- g_warning("failed to read from client: %s",
- error->message);
- g_error_free(error);
- return false;
- }
-
- /* unreachable */
- return false;
-}
-
-static gboolean
-httpd_client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- bool ret;
-
- g_mutex_lock(httpd->mutex);
-
- if (condition == G_IO_IN && httpd_client_read(client)) {
- ret = true;
- } else {
- httpd_client_close(client);
- ret = false;
- }
-
- g_mutex_unlock(httpd->mutex);
-
- return ret;
-}
-
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported)
-{
- struct httpd_client *client = g_new(struct httpd_client, 1);
-
- client->httpd = httpd;
-
- client->channel = g_io_channel_new_socket(fd);
-
- /* GLib is responsible for closing the file descriptor */
- g_io_channel_set_close_on_unref(client->channel, true);
- /* NULL encoding means the stream is binary safe */
- g_io_channel_set_encoding(client->channel, NULL, NULL);
- /* we prefer to do buffering */
- g_io_channel_set_buffered(client->channel, false);
-
- client->read_source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
- httpd_client_in_event, client);
-
- client->input = fifo_buffer_new(4096);
- client->state = REQUEST;
-
- client->dlna_streaming_requested = false;
- client->metadata_supported = metadata_supported;
- client->metadata_requested = false;
- client->metadata_sent = true;
- client->metaint = 8192; /*TODO: just a std value */
- client->metadata = NULL;
- client->metadata_current_position = 0;
- client->metadata_fill = 0;
-
- return client;
-}
-
-static void
-httpd_client_add_page_size(gpointer data, gpointer user_data)
-{
- struct page *page = data;
- size_t *size = user_data;
-
- *size += page->size;
-}
-
-size_t
-httpd_client_queue_size(const struct httpd_client *client)
-{
- size_t size = 0;
-
- if (client->state != RESPONSE)
- return 0;
-
- g_queue_foreach(client->pages, httpd_client_add_page_size, &size);
- return size;
-}
-
-void
-httpd_client_cancel(struct httpd_client *client)
-{
- if (client->state != RESPONSE)
- return;
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_clear(client->pages);
-
- if (client->write_source_id != 0 && client->current_page == NULL) {
- g_source_remove(client->write_source_id);
- client->write_source_id = 0;
- }
-}
-
-static GIOStatus
-write_page_to_channel(GIOChannel *channel,
- const struct page *page, size_t position,
- gsize *bytes_written_r, GError **error)
-{
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- return g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- page->size - position,
- bytes_written_r, error);
-}
-
-static GIOStatus
-write_n_bytes_to_channel(GIOChannel *channel, const struct page *page,
- size_t position, gint n,
- gsize *bytes_written_r, GError **error)
-{
- GIOStatus status;
-
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- if (n == -1) {
- status = write_page_to_channel (channel, page, position,
- bytes_written_r, error);
- } else {
- status = g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- n, bytes_written_r, error);
- }
-
- return status;
-}
-
-static gint
-bytes_left_till_metadata (struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->metadata_requested &&
- client->current_page->size - client->current_position
- > client->metaint - client->metadata_fill)
- return client->metaint - client->metadata_fill;
-
- return -1;
-}
-
-static gboolean
-httpd_client_out_event(GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition, gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
- gint bytes_to_write;
-
- g_mutex_lock(httpd->mutex);
-
- assert(condition == G_IO_OUT);
- assert(client->state == RESPONSE);
-
- if (client->write_source_id == 0) {
- /* another thread has removed the event source while
- this thread was waiting for httpd->mutex */
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- if (client->current_page == NULL) {
- client->current_page = g_queue_pop_head(client->pages);
- client->current_position = 0;
- }
-
- bytes_to_write = bytes_left_till_metadata(client);
-
- if (bytes_to_write == 0) {
- gint metadata_to_write;
-
- metadata_to_write = client->metadata_current_position;
-
- if (!client->metadata_sent) {
- status = write_page_to_channel(source,
- client->metadata,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (client->metadata->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- client->metadata_sent = true;
- }
- } else {
- struct page *empty_meta;
- guchar empty_data = 0;
-
- empty_meta = page_new_copy(&empty_data, 1);
-
- status = write_page_to_channel(source,
- empty_meta,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (empty_meta->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- }
- }
-
- bytes_written = 0;
- } else {
- status = write_n_bytes_to_channel(source, client->current_page,
- client->current_position, bytes_to_write,
- &bytes_written, &error);
- }
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- client->current_position += bytes_written;
- assert(client->current_position <= client->current_page->size);
-
- if (client->metadata_requested)
- client->metadata_fill += bytes_written;
-
- if (client->current_position >= client->current_page->size) {
- page_unref(client->current_page);
- client->current_page = NULL;
-
- if (g_queue_is_empty(client->pages)) {
- /* all pages are sent: remove the
- event source */
- client->write_source_id = 0;
-
- g_mutex_unlock(httpd->mutex);
- return false;
- }
- }
-
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_AGAIN:
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-}
-
-void
-httpd_client_send(struct httpd_client *client, struct page *page)
-{
- if (client->state != RESPONSE)
- /* the client is still writing the HTTP request */
- return;
-
- page_ref(page);
- g_queue_push_tail(client->pages, page);
-
- if (client->write_source_id == 0)
- client->write_source_id =
- g_io_add_watch(client->channel, G_IO_OUT,
- httpd_client_out_event, client);
-}
-
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page)
-{
- if (client->metadata) {
- page_unref(client->metadata);
- client->metadata = NULL;
- }
-
- g_return_if_fail (page);
-
- page_ref(page);
- client->metadata = page;
- client->metadata_sent = false;
-}
diff --git a/src/output/httpd_client.h b/src/output/httpd_client.h
deleted file mode 100644
index 739163f42..000000000
--- a/src/output/httpd_client.h
+++ /dev/null
@@ -1,71 +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.
- */
-
-#ifndef MPD_OUTPUT_HTTPD_CLIENT_H
-#define MPD_OUTPUT_HTTPD_CLIENT_H
-
-#include <glib.h>
-
-#include <stdbool.h>
-
-struct httpd_client;
-struct httpd_output;
-struct page;
-
-/**
- * Creates a new #httpd_client object
- *
- * @param httpd the HTTP output device
- * @param fd the socket file descriptor
- */
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported);
-
-/**
- * Frees memory and resources allocated by the #httpd_client object.
- * This does not remove it from the #httpd_output object.
- */
-void
-httpd_client_free(struct httpd_client *client);
-
-/**
- * Returns the total size of this client's page queue.
- */
-size_t
-httpd_client_queue_size(const struct httpd_client *client);
-
-/**
- * Clears the page queue.
- */
-void
-httpd_client_cancel(struct httpd_client *client);
-
-/**
- * Appends a page to the client's queue.
- */
-void
-httpd_client_send(struct httpd_client *client, struct page *page);
-
-/**
- * Sends the passed metadata.
- */
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page);
-
-#endif
diff --git a/src/output/httpd_internal.h b/src/output/httpd_internal.h
deleted file mode 100644
index 5dcb8ab9b..000000000
--- a/src/output/httpd_internal.h
+++ /dev/null
@@ -1,138 +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.
- */
-
-/** \file
- *
- * Internal declarations for the "httpd" audio output plugin.
- */
-
-#ifndef MPD_OUTPUT_HTTPD_INTERNAL_H
-#define MPD_OUTPUT_HTTPD_INTERNAL_H
-
-#include "output_internal.h"
-#include "timer.h"
-
-#include <glib.h>
-
-#include <stdbool.h>
-
-struct httpd_client;
-
-struct httpd_output {
- struct audio_output base;
-
- /**
- * True if the audio output is open and accepts client
- * connections.
- */
- bool open;
-
- /**
- * The configured encoder plugin.
- */
- struct encoder *encoder;
-
- /**
- * Number of bytes which were fed into the encoder, without
- * ever receiving new output. This is used to estimate
- * whether MPD should manually flush the encoder, to avoid
- * buffer underruns in the client.
- */
- size_t unflushed_input;
-
- /**
- * The MIME type produced by the #encoder.
- */
- const char *content_type;
-
- /**
- * This mutex protects the listener socket and the client
- * list.
- */
- GMutex *mutex;
-
- /**
- * A #timer object to synchronize this output with the
- * wallclock.
- */
- struct timer *timer;
-
- /**
- * The listener socket.
- */
- struct server_socket *server_socket;
-
- /**
- * The header page, which is sent to every client on connect.
- */
- struct page *header;
-
- /**
- * The metadata, which is sent to every client.
- */
- struct page *metadata;
-
- /**
- * The configured name.
- */
- char const *name;
- /**
- * The configured genre.
- */
- char const *genre;
- /**
- * The configured website address.
- */
- char const *website;
-
- /**
- * A linked list containing all clients which are currently
- * connected.
- */
- GList *clients;
-
- /**
- * A temporary buffer for the httpd_output_read_page()
- * function.
- */
- char buffer[32768];
-
- /**
- * The maximum and current number of clients connected
- * at the same time.
- */
- guint clients_max, clients_cnt;
-};
-
-/**
- * Removes a client from the httpd_output.clients linked list.
- */
-void
-httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client);
-
-/**
- * Sends the encoder header to the client. This is called right after
- * the response headers have been sent.
- */
-void
-httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client);
-
-#endif
diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c
deleted file mode 100644
index 1d730df7f..000000000
--- a/src/output/httpd_output_plugin.c
+++ /dev/null
@@ -1,623 +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 "httpd_output_plugin.h"
-#include "httpd_internal.h"
-#include "httpd_client.h"
-#include "output_api.h"
-#include "encoder_plugin.h"
-#include "encoder_list.h"
-#include "resolver.h"
-#include "page.h"
-#include "icy_server.h"
-#include "fd_util.h"
-#include "server_socket.h"
-
-#include <assert.h>
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-
-#ifdef HAVE_LIBWRAP
-#include <sys/socket.h> /* needed for AF_UNIX */
-#include <tcpd.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "httpd_output"
-
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-httpd_output_quark(void)
-{
- return g_quark_from_static_string("httpd_output");
-}
-
-/**
- * Check whether there is at least one client.
- *
- * Caller must lock the mutex.
- */
-G_GNUC_PURE
-static bool
-httpd_output_has_clients(const struct httpd_output *httpd)
-{
- return httpd->clients != NULL;
-}
-
-/**
- * Check whether there is at least one client.
- */
-G_GNUC_PURE
-static bool
-httpd_output_lock_has_clients(const struct httpd_output *httpd)
-{
- g_mutex_lock(httpd->mutex);
- bool result = httpd_output_has_clients(httpd);
- g_mutex_unlock(httpd->mutex);
- return result;
-}
-
-static void
-httpd_listen_in_event(int fd, const struct sockaddr *address,
- size_t address_length, int uid, void *ctx);
-
-static bool
-httpd_output_bind(struct httpd_output *httpd, GError **error_r)
-{
- httpd->open = false;
-
- g_mutex_lock(httpd->mutex);
- bool success = server_socket_open(httpd->server_socket, error_r);
- g_mutex_unlock(httpd->mutex);
-
- return success;
-}
-
-static void
-httpd_output_unbind(struct httpd_output *httpd)
-{
- assert(!httpd->open);
-
- g_mutex_lock(httpd->mutex);
- server_socket_close(httpd->server_socket);
- g_mutex_unlock(httpd->mutex);
-}
-
-static struct audio_output *
-httpd_output_init(const struct config_param *param,
- GError **error)
-{
- struct httpd_output *httpd = g_new(struct httpd_output, 1);
- if (!ao_base_init(&httpd->base, &httpd_output_plugin, param, error)) {
- g_free(httpd);
- return NULL;
- }
-
- /* read configuration */
- httpd->name =
- config_get_block_string(param, "name", "Set name in config");
- httpd->genre =
- config_get_block_string(param, "genre", "Set genre in config");
- httpd->website =
- config_get_block_string(param, "website", "Set website in config");
-
- guint port = config_get_block_unsigned(param, "port", 8000);
-
- const char *encoder_name =
- config_get_block_string(param, "encoder", "vorbis");
- const struct encoder_plugin *encoder_plugin =
- encoder_plugin_get(encoder_name);
- if (encoder_plugin == NULL) {
- g_set_error(error, httpd_output_quark(), 0,
- "No such encoder: %s", encoder_name);
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- httpd->clients_max = config_get_block_unsigned(param,"max_clients", 0);
-
- /* set up bind_to_address */
-
- httpd->server_socket = server_socket_new(httpd_listen_in_event, httpd);
-
- const char *bind_to_address =
- config_get_block_string(param, "bind_to_address", NULL);
- bool success = bind_to_address != NULL &&
- strcmp(bind_to_address, "any") != 0
- ? server_socket_add_host(httpd->server_socket, bind_to_address,
- port, error)
- : server_socket_add_port(httpd->server_socket, port, error);
- if (!success) {
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- /* initialize metadata */
- httpd->metadata = NULL;
- httpd->unflushed_input = 0;
-
- /* initialize encoder */
-
- httpd->encoder = encoder_init(encoder_plugin, param, error);
- if (httpd->encoder == NULL) {
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- /* determine content type */
- httpd->content_type = encoder_get_mime_type(httpd->encoder);
- if (httpd->content_type == NULL) {
- httpd->content_type = "application/octet-stream";
- }
-
- httpd->mutex = g_mutex_new();
-
- return &httpd->base;
-}
-
-static void
-httpd_output_finish(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd->metadata)
- page_unref(httpd->metadata);
-
- encoder_finish(httpd->encoder);
- server_socket_free(httpd->server_socket);
- g_mutex_free(httpd->mutex);
- ao_base_finish(&httpd->base);
- g_free(httpd);
-}
-
-/**
- * Creates a new #httpd_client object and adds it into the
- * httpd_output.clients linked list.
- */
-static void
-httpd_client_add(struct httpd_output *httpd, int fd)
-{
- struct httpd_client *client =
- httpd_client_new(httpd, fd,
- httpd->encoder->plugin->tag == NULL);
-
- httpd->clients = g_list_prepend(httpd->clients, client);
- httpd->clients_cnt++;
-
- /* pass metadata to client */
- if (httpd->metadata)
- httpd_client_send_metadata(client, httpd->metadata);
-}
-
-static void
-httpd_listen_in_event(int fd, const struct sockaddr *address,
- size_t address_length, G_GNUC_UNUSED int uid, void *ctx)
-{
- struct httpd_output *httpd = ctx;
-
- /* the listener socket has become readable - a client has
- connected */
-
-#ifdef HAVE_LIBWRAP
- if (address->sa_family != AF_UNIX) {
- char *hostaddr = sockaddr_to_string(address, address_length, NULL);
- const char *progname = g_get_prgname();
-
- struct request_info req;
- request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
-
- fromhost(&req);
-
- if (!hosts_access(&req)) {
- /* tcp wrappers says no */
- g_warning("libwrap refused connection (libwrap=%s) from %s",
- progname, hostaddr);
- g_free(hostaddr);
- close_socket(fd);
- g_mutex_unlock(httpd->mutex);
- return;
- }
-
- g_free(hostaddr);
- }
-#else
- (void)address;
- (void)address_length;
-#endif /* HAVE_WRAP */
-
- g_mutex_lock(httpd->mutex);
-
- if (fd >= 0) {
- /* can we allow additional client */
- if (httpd->open &&
- (httpd->clients_max == 0 ||
- httpd->clients_cnt < httpd->clients_max))
- httpd_client_add(httpd, fd);
- else
- close_socket(fd);
- } else if (fd < 0 && errno != EINTR) {
- g_warning("accept() failed: %s", g_strerror(errno));
- }
-
- g_mutex_unlock(httpd->mutex);
-}
-
-/**
- * Reads data from the encoder (as much as available) and returns it
- * as a new #page object.
- */
-static struct page *
-httpd_output_read_page(struct httpd_output *httpd)
-{
- if (httpd->unflushed_input >= 65536) {
- /* we have fed a lot of input into the encoder, but it
- didn't give anything back yet - flush now to avoid
- buffer underruns */
- encoder_flush(httpd->encoder, NULL);
- httpd->unflushed_input = 0;
- }
-
- size_t size = 0;
- do {
- size_t nbytes = encoder_read(httpd->encoder,
- httpd->buffer + size,
- sizeof(httpd->buffer) - size);
- if (nbytes == 0)
- break;
-
- httpd->unflushed_input = 0;
-
- size += nbytes;
- } while (size < sizeof(httpd->buffer));
-
- if (size == 0)
- return NULL;
-
- return page_new_copy(httpd->buffer, size);
-}
-
-static bool
-httpd_output_encoder_open(struct httpd_output *httpd,
- struct audio_format *audio_format,
- GError **error)
-{
- if (!encoder_open(httpd->encoder, audio_format, error))
- return false;
-
- /* we have to remember the encoder header, i.e. the first
- bytes of encoder output after opening it, because it has to
- be sent to every new client */
- httpd->header = httpd_output_read_page(httpd);
-
- httpd->unflushed_input = 0;
-
- return true;
-}
-
-static bool
-httpd_output_enable(struct audio_output *ao, GError **error_r)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- return httpd_output_bind(httpd, error_r);
-}
-
-static void
-httpd_output_disable(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- httpd_output_unbind(httpd);
-}
-
-static bool
-httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
- GError **error)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
-
- /* open the encoder */
-
- if (!httpd_output_encoder_open(httpd, audio_format, error)) {
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- /* initialize other attributes */
-
- httpd->clients = NULL;
- httpd->clients_cnt = 0;
- httpd->timer = timer_new(audio_format);
-
- httpd->open = true;
-
- g_mutex_unlock(httpd->mutex);
- return true;
-}
-
-static void
-httpd_client_delete(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_free(client);
-}
-
-static void
-httpd_output_close(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
-
- httpd->open = false;
-
- timer_free(httpd->timer);
-
- g_list_foreach(httpd->clients, httpd_client_delete, NULL);
- g_list_free(httpd->clients);
-
- if (httpd->header != NULL)
- page_unref(httpd->header);
-
- encoder_close(httpd->encoder);
-
- g_mutex_unlock(httpd->mutex);
-}
-
-void
-httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client)
-{
- assert(httpd != NULL);
- assert(client != NULL);
-
- httpd->clients = g_list_remove(httpd->clients, client);
- httpd->clients_cnt--;
-}
-
-void
-httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client)
-{
- if (httpd->header != NULL)
- httpd_client_send(client, httpd->header);
-}
-
-static unsigned
-httpd_output_delay(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (!httpd_output_lock_has_clients(httpd) && httpd->base.pause) {
- /* if there's no client and this output is paused,
- then httpd_output_pause() will not do anything, it
- will not fill the buffer and it will not update the
- timer; therefore, we reset the timer here */
- timer_reset(httpd->timer);
-
- /* some arbitrary delay that is long enough to avoid
- consuming too much CPU, and short enough to notice
- new clients quickly enough */
- return 1000;
- }
-
- return httpd->timer->started
- ? timer_delay(httpd->timer)
- : 0;
-}
-
-static void
-httpd_client_check_queue(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- if (httpd_client_queue_size(client) > 256 * 1024) {
- g_debug("client is too slow, flushing its queue");
- httpd_client_cancel(client);
- }
-}
-
-static void
-httpd_client_send_page(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *page = user_data;
-
- httpd_client_send(client, page);
-}
-
-/**
- * Broadcasts a page struct to all clients.
- */
-static void
-httpd_output_broadcast_page(struct httpd_output *httpd, struct page *page)
-{
- assert(page != NULL);
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_send_page, page);
- g_mutex_unlock(httpd->mutex);
-}
-
-/**
- * Broadcasts data from the encoder to all clients.
- */
-static void
-httpd_output_encoder_to_clients(struct httpd_output *httpd)
-{
- struct page *page;
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_check_queue, NULL);
- g_mutex_unlock(httpd->mutex);
-
- while ((page = httpd_output_read_page(httpd)) != NULL) {
- httpd_output_broadcast_page(httpd, page);
- page_unref(page);
- }
-}
-
-static bool
-httpd_output_encode_and_play(struct httpd_output *httpd,
- const void *chunk, size_t size, GError **error)
-{
- if (!encoder_write(httpd->encoder, chunk, size, error))
- return false;
-
- httpd->unflushed_input += size;
-
- httpd_output_encoder_to_clients(httpd);
-
- return true;
-}
-
-static size_t
-httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
- GError **error_r)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd_output_lock_has_clients(httpd)) {
- if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
- return 0;
- }
-
- if (!httpd->timer->started)
- timer_start(httpd->timer);
- timer_add(httpd->timer, size);
-
- return size;
-}
-
-static bool
-httpd_output_pause(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd_output_lock_has_clients(httpd)) {
- static const char silence[1020];
- return httpd_output_play(ao, silence, sizeof(silence),
- NULL) > 0;
- } else {
- return true;
- }
-}
-
-static void
-httpd_send_metadata(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *icy_metadata = user_data;
-
- httpd_client_send_metadata(client, icy_metadata);
-}
-
-static void
-httpd_output_tag(struct audio_output *ao, const struct tag *tag)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- assert(tag != NULL);
-
- if (httpd->encoder->plugin->tag != NULL) {
- /* embed encoder tags */
-
- /* flush the current stream, and end it */
-
- encoder_pre_tag(httpd->encoder, NULL);
- httpd_output_encoder_to_clients(httpd);
-
- /* send the tag to the encoder - which starts a new
- stream now */
-
- encoder_tag(httpd->encoder, tag, NULL);
-
- /* the first page generated by the encoder will now be
- used as the new "header" page, which is sent to all
- new clients */
-
- struct page *page = httpd_output_read_page(httpd);
- if (page != NULL) {
- if (httpd->header != NULL)
- page_unref(httpd->header);
- httpd->header = page;
- httpd_output_broadcast_page(httpd, page);
- }
- } else {
- /* use Icy-Metadata */
-
- if (httpd->metadata != NULL)
- page_unref (httpd->metadata);
-
- httpd->metadata =
- icy_server_metadata_page(tag, TAG_ALBUM,
- TAG_ARTIST, TAG_TITLE,
- TAG_NUM_OF_ITEM_TYPES);
- if (httpd->metadata != NULL) {
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients,
- httpd_send_metadata, httpd->metadata);
- g_mutex_unlock(httpd->mutex);
- }
- }
-}
-
-static void
-httpd_client_cancel_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_cancel(client);
-}
-
-static void
-httpd_output_cancel(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_cancel_callback, NULL);
- g_mutex_unlock(httpd->mutex);
-}
-
-const struct audio_output_plugin httpd_output_plugin = {
- .name = "httpd",
- .init = httpd_output_init,
- .finish = httpd_output_finish,
- .enable = httpd_output_enable,
- .disable = httpd_output_disable,
- .open = httpd_output_open,
- .close = httpd_output_close,
- .delay = httpd_output_delay,
- .send_tag = httpd_output_tag,
- .play = httpd_output_play,
- .pause = httpd_output_pause,
- .cancel = httpd_output_cancel,
-};
diff --git a/src/output/httpd_output_plugin.h b/src/output/httpd_output_plugin.h
deleted file mode 100644
index d0eb1533f..000000000
--- a/src/output/httpd_output_plugin.h
+++ /dev/null
@@ -1,25 +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.
- */
-
-#ifndef MPD_HTTPD_OUTPUT_PLUGIN_H
-#define MPD_HTTPD_OUTPUT_PLUGIN_H
-
-extern const struct audio_output_plugin httpd_output_plugin;
-
-#endif
diff --git a/src/output/null_output_plugin.c b/src/output/null_output_plugin.c
deleted file mode 100644
index 9d7588fff..000000000
--- a/src/output/null_output_plugin.c
+++ /dev/null
@@ -1,129 +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 "null_output_plugin.h"
-#include "output_api.h"
-#include "timer.h"
-
-#include <glib.h>
-
-#include <assert.h>
-
-struct null_data {
- struct audio_output base;
-
- bool sync;
-
- struct timer *timer;
-};
-
-static struct audio_output *
-null_init(const struct config_param *param, GError **error_r)
-{
- struct null_data *nd = g_new(struct null_data, 1);
-
- if (!ao_base_init(&nd->base, &null_output_plugin, param, error_r)) {
- g_free(nd);
- return NULL;
- }
-
- nd->sync = config_get_block_bool(param, "sync", true);
-
- return &nd->base;
-}
-
-static void
-null_finish(struct audio_output *ao)
-{
- struct null_data *nd = (struct null_data *)ao;
-
- ao_base_finish(&nd->base);
- g_free(nd);
-}
-
-static bool
-null_open(struct audio_output *ao, struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error)
-{
- struct null_data *nd = (struct null_data *)ao;
-
- if (nd->sync)
- nd->timer = timer_new(audio_format);
-
- return true;
-}
-
-static void
-null_close(struct audio_output *ao)
-{
- struct null_data *nd = (struct null_data *)ao;
-
- if (nd->sync)
- timer_free(nd->timer);
-}
-
-static unsigned
-null_delay(struct audio_output *ao)
-{
- struct null_data *nd = (struct null_data *)ao;
-
- return nd->sync && nd->timer->started
- ? timer_delay(nd->timer)
- : 0;
-}
-
-static size_t
-null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size,
- G_GNUC_UNUSED GError **error)
-{
- struct null_data *nd = (struct null_data *)ao;
- struct timer *timer = nd->timer;
-
- if (!nd->sync)
- return size;
-
- if (!timer->started)
- timer_start(timer);
- timer_add(timer, size);
-
- return size;
-}
-
-static void
-null_cancel(struct audio_output *ao)
-{
- struct null_data *nd = (struct null_data *)ao;
-
- if (!nd->sync)
- return;
-
- timer_reset(nd->timer);
-}
-
-const struct audio_output_plugin null_output_plugin = {
- .name = "null",
- .init = null_init,
- .finish = null_finish,
- .open = null_open,
- .close = null_close,
- .delay = null_delay,
- .play = null_play,
- .cancel = null_cancel,
-};
diff --git a/src/output/null_output_plugin.h b/src/output/null_output_plugin.h
deleted file mode 100644
index 392bf0aa3..000000000
--- a/src/output/null_output_plugin.h
+++ /dev/null
@@ -1,25 +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.
- */
-
-#ifndef MPD_NULL_OUTPUT_PLUGIN_H
-#define MPD_NULL_OUTPUT_PLUGIN_H
-
-extern const struct audio_output_plugin null_output_plugin;
-
-#endif
diff --git a/src/output/oss_output_plugin.c b/src/output/oss_output_plugin.c
deleted file mode 100644
index e366a4537..000000000
--- a/src/output/oss_output_plugin.c
+++ /dev/null
@@ -1,788 +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 "oss_output_plugin.h"
-#include "output_api.h"
-#include "mixer_list.h"
-#include "fd_util.h"
-#include "glib_compat.h"
-
-#include <glib.h>
-
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <assert.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "oss"
-
-#if defined(__OpenBSD__) || defined(__NetBSD__)
-# include <soundcard.h>
-#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
-# include <sys/soundcard.h>
-#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
-
-/* We got bug reports from FreeBSD users who said that the two 24 bit
- formats generate white noise on FreeBSD, but 32 bit works. This is
- a workaround until we know what exactly is expected by the kernel
- audio drivers. */
-#ifndef __linux__
-#undef AFMT_S24_PACKED
-#undef AFMT_S24_NE
-#endif
-
-#ifdef AFMT_S24_PACKED
-#include "pcm_export.h"
-#endif
-
-struct oss_data {
- struct audio_output base;
-
-#ifdef AFMT_S24_PACKED
- struct pcm_export_state export;
-#endif
-
- int fd;
- const char *device;
-
- /**
- * The current input audio format. This is needed to reopen
- * the device after cancel().
- */
- struct audio_format audio_format;
-
- /**
- * The current OSS audio format. This is needed to reopen the
- * device after cancel().
- */
- int oss_format;
-};
-
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-oss_output_quark(void)
-{
- return g_quark_from_static_string("oss_output");
-}
-
-static struct oss_data *
-oss_data_new(void)
-{
- struct oss_data *ret = g_new(struct oss_data, 1);
-
- ret->device = NULL;
- ret->fd = -1;
-
- return ret;
-}
-
-static void
-oss_data_free(struct oss_data *od)
-{
- g_free(od);
-}
-
-enum oss_stat {
- OSS_STAT_NO_ERROR = 0,
- OSS_STAT_NOT_CHAR_DEV = -1,
- OSS_STAT_NO_PERMS = -2,
- OSS_STAT_DOESN_T_EXIST = -3,
- OSS_STAT_OTHER = -4,
-};
-
-static enum oss_stat
-oss_stat_device(const char *device, int *errno_r)
-{
- struct stat st;
-
- if (0 == stat(device, &st)) {
- if (!S_ISCHR(st.st_mode)) {
- return OSS_STAT_NOT_CHAR_DEV;
- }
- } else {
- *errno_r = errno;
-
- switch (errno) {
- case ENOENT:
- case ENOTDIR:
- return OSS_STAT_DOESN_T_EXIST;
- case EACCES:
- return OSS_STAT_NO_PERMS;
- default:
- return OSS_STAT_OTHER;
- }
- }
-
- return OSS_STAT_NO_ERROR;
-}
-
-static const char *default_devices[] = { "/dev/sound/dsp", "/dev/dsp" };
-
-static bool
-oss_output_test_default_device(void)
-{
- int fd, i;
-
- for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
- fd = open_cloexec(default_devices[i], O_WRONLY, 0);
-
- if (fd >= 0) {
- close(fd);
- return true;
- }
- g_warning("Error opening OSS device \"%s\": %s\n",
- default_devices[i], g_strerror(errno));
- }
-
- return false;
-}
-
-static struct audio_output *
-oss_open_default(GError **error)
-{
- int i;
- int err[G_N_ELEMENTS(default_devices)];
- enum oss_stat ret[G_N_ELEMENTS(default_devices)];
-
- for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
- ret[i] = oss_stat_device(default_devices[i], &err[i]);
- if (ret[i] == OSS_STAT_NO_ERROR) {
- struct oss_data *od = oss_data_new();
- if (!ao_base_init(&od->base, &oss_output_plugin, NULL,
- error)) {
- g_free(od);
- return NULL;
- }
-
- od->device = default_devices[i];
- return &od->base;
- }
- }
-
- for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
- const char *dev = default_devices[i];
- switch(ret[i]) {
- case OSS_STAT_NO_ERROR:
- /* never reached */
- break;
- case OSS_STAT_DOESN_T_EXIST:
- g_warning("%s not found\n", dev);
- break;
- case OSS_STAT_NOT_CHAR_DEV:
- g_warning("%s is not a character device\n", dev);
- break;
- case OSS_STAT_NO_PERMS:
- g_warning("%s: permission denied\n", dev);
- break;
- case OSS_STAT_OTHER:
- g_warning("Error accessing %s: %s\n",
- dev, g_strerror(err[i]));
- }
- }
-
- g_set_error(error, oss_output_quark(), 0,
- "error trying to open default OSS device");
- return NULL;
-}
-
-static struct audio_output *
-oss_output_init(const struct config_param *param, GError **error)
-{
- const char *device = config_get_block_string(param, "device", NULL);
- if (device != NULL) {
- struct oss_data *od = oss_data_new();
- if (!ao_base_init(&od->base, &oss_output_plugin, param,
- error)) {
- g_free(od);
- return NULL;
- }
-
- od->device = device;
- return &od->base;
- }
-
- return oss_open_default(error);
-}
-
-static void
-oss_output_finish(struct audio_output *ao)
-{
- struct oss_data *od = (struct oss_data *)ao;
-
- ao_base_finish(&od->base);
- oss_data_free(od);
-}
-
-#ifdef AFMT_S24_PACKED
-
-static bool
-oss_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
-{
- struct oss_data *od = (struct oss_data *)ao;
-
- pcm_export_init(&od->export);
- return true;
-}
-
-static void
-oss_output_disable(struct audio_output *ao)
-{
- struct oss_data *od = (struct oss_data *)ao;
-
- pcm_export_deinit(&od->export);
-}
-
-#endif
-
-static void
-oss_close(struct oss_data *od)
-{
- if (od->fd >= 0)
- close(od->fd);
- od->fd = -1;
-}
-
-/**
- * A tri-state type for oss_try_ioctl().
- */
-enum oss_setup_result {
- SUCCESS,
- ERROR,
- UNSUPPORTED,
-};
-
-/**
- * Invoke an ioctl on the OSS file descriptor. On success, SUCCESS is
- * returned. If the parameter is not supported, UNSUPPORTED is
- * returned. Any other failure returns ERROR and allocates a GError.
- */
-static enum oss_setup_result
-oss_try_ioctl_r(int fd, unsigned long request, int *value_r,
- const char *msg, GError **error_r)
-{
- assert(fd >= 0);
- assert(value_r != NULL);
- assert(msg != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- int ret = ioctl(fd, request, value_r);
- if (ret >= 0)
- return SUCCESS;
-
- if (errno == EINVAL)
- return UNSUPPORTED;
-
- g_set_error(error_r, oss_output_quark(), errno,
- "%s: %s", msg, g_strerror(errno));
- return ERROR;
-}
-
-/**
- * Invoke an ioctl on the OSS file descriptor. On success, SUCCESS is
- * returned. If the parameter is not supported, UNSUPPORTED is
- * returned. Any other failure returns ERROR and allocates a GError.
- */
-static enum oss_setup_result
-oss_try_ioctl(int fd, unsigned long request, int value,
- const char *msg, GError **error_r)
-{
- return oss_try_ioctl_r(fd, request, &value, msg, error_r);
-}
-
-/**
- * Set up the channel number, and attempts to find alternatives if the
- * specified number is not supported.
- */
-static bool
-oss_setup_channels(int fd, struct audio_format *audio_format, GError **error_r)
-{
- const char *const msg = "Failed to set channel count";
- int channels = audio_format->channels;
- enum oss_setup_result result =
- oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels, msg, error_r);
- switch (result) {
- case SUCCESS:
- if (!audio_valid_channel_count(channels))
- break;
-
- audio_format->channels = channels;
- return true;
-
- case ERROR:
- return false;
-
- case UNSUPPORTED:
- break;
- }
-
- for (unsigned i = 1; i < 2; ++i) {
- if (i == audio_format->channels)
- /* don't try that again */
- continue;
-
- channels = i;
- result = oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels,
- msg, error_r);
- switch (result) {
- case SUCCESS:
- if (!audio_valid_channel_count(channels))
- break;
-
- audio_format->channels = channels;
- return true;
-
- case ERROR:
- return false;
-
- case UNSUPPORTED:
- break;
- }
- }
-
- g_set_error(error_r, oss_output_quark(), EINVAL, "%s", msg);
- return false;
-}
-
-/**
- * Set up the sample rate, and attempts to find alternatives if the
- * specified sample rate is not supported.
- */
-static bool
-oss_setup_sample_rate(int fd, struct audio_format *audio_format,
- GError **error_r)
-{
- const char *const msg = "Failed to set sample rate";
- int sample_rate = audio_format->sample_rate;
- enum oss_setup_result result =
- oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
- msg, error_r);
- switch (result) {
- case SUCCESS:
- if (!audio_valid_sample_rate(sample_rate))
- break;
-
- audio_format->sample_rate = sample_rate;
- return true;
-
- case ERROR:
- return false;
-
- case UNSUPPORTED:
- break;
- }
-
- static const int sample_rates[] = { 48000, 44100, 0 };
- for (unsigned i = 0; sample_rates[i] != 0; ++i) {
- sample_rate = sample_rates[i];
- if (sample_rate == (int)audio_format->sample_rate)
- continue;
-
- result = oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
- msg, error_r);
- switch (result) {
- case SUCCESS:
- if (!audio_valid_sample_rate(sample_rate))
- break;
-
- audio_format->sample_rate = sample_rate;
- return true;
-
- case ERROR:
- return false;
-
- case UNSUPPORTED:
- break;
- }
- }
-
- g_set_error(error_r, oss_output_quark(), EINVAL, "%s", msg);
- return false;
-}
-
-/**
- * Convert a MPD sample format to its OSS counterpart. Returns
- * AFMT_QUERY if there is no direct counterpart.
- */
-static int
-sample_format_to_oss(enum sample_format format)
-{
- switch (format) {
- case SAMPLE_FORMAT_UNDEFINED:
- case SAMPLE_FORMAT_FLOAT:
- case SAMPLE_FORMAT_DSD:
- return AFMT_QUERY;
-
- case SAMPLE_FORMAT_S8:
- return AFMT_S8;
-
- case SAMPLE_FORMAT_S16:
- return AFMT_S16_NE;
-
- case SAMPLE_FORMAT_S24_P32:
-#ifdef AFMT_S24_NE
- return AFMT_S24_NE;
-#else
- return AFMT_QUERY;
-#endif
-
- case SAMPLE_FORMAT_S32:
-#ifdef AFMT_S32_NE
- return AFMT_S32_NE;
-#else
- return AFMT_QUERY;
-#endif
- }
-
- return AFMT_QUERY;
-}
-
-/**
- * Convert an OSS sample format to its MPD counterpart. Returns
- * SAMPLE_FORMAT_UNDEFINED if there is no direct counterpart.
- */
-static enum sample_format
-sample_format_from_oss(int format)
-{
- switch (format) {
- case AFMT_S8:
- return SAMPLE_FORMAT_S8;
-
- case AFMT_S16_NE:
- return SAMPLE_FORMAT_S16;
-
-#ifdef AFMT_S24_PACKED
- case AFMT_S24_PACKED:
- return SAMPLE_FORMAT_S24_P32;
-#endif
-
-#ifdef AFMT_S24_NE
- case AFMT_S24_NE:
- return SAMPLE_FORMAT_S24_P32;
-#endif
-
-#ifdef AFMT_S32_NE
- case AFMT_S32_NE:
- return SAMPLE_FORMAT_S32;
-#endif
-
- default:
- return SAMPLE_FORMAT_UNDEFINED;
- }
-}
-
-/**
- * Probe one sample format.
- *
- * @return the selected sample format or SAMPLE_FORMAT_UNDEFINED on
- * error
- */
-static enum oss_setup_result
-oss_probe_sample_format(int fd, enum sample_format sample_format,
- enum sample_format *sample_format_r,
- int *oss_format_r,
-#ifdef AFMT_S24_PACKED
- struct pcm_export_state *export,
-#endif
- GError **error_r)
-{
- int oss_format = sample_format_to_oss(sample_format);
- if (oss_format == AFMT_QUERY)
- return UNSUPPORTED;
-
- enum oss_setup_result result =
- oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE,
- &oss_format,
- "Failed to set sample format", error_r);
-
-#ifdef AFMT_S24_PACKED
- if (result == UNSUPPORTED && sample_format == SAMPLE_FORMAT_S24_P32) {
- /* if the driver doesn't support padded 24 bit, try
- packed 24 bit */
- oss_format = AFMT_S24_PACKED;
- result = oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE,
- &oss_format,
- "Failed to set sample format", error_r);
- }
-#endif
-
- if (result != SUCCESS)
- return result;
-
- sample_format = sample_format_from_oss(oss_format);
- if (sample_format == SAMPLE_FORMAT_UNDEFINED)
- return UNSUPPORTED;
-
- *sample_format_r = sample_format;
- *oss_format_r = oss_format;
-
-#ifdef AFMT_S24_PACKED
- pcm_export_open(export, sample_format, 0, false, false,
- oss_format == AFMT_S24_PACKED,
- oss_format == AFMT_S24_PACKED &&
- G_BYTE_ORDER != G_LITTLE_ENDIAN);
-#endif
-
- return SUCCESS;
-}
-
-/**
- * Set up the sample format, and attempts to find alternatives if the
- * specified format is not supported.
- */
-static bool
-oss_setup_sample_format(int fd, struct audio_format *audio_format,
- int *oss_format_r,
-#ifdef AFMT_S24_PACKED
- struct pcm_export_state *export,
-#endif
- GError **error_r)
-{
- enum sample_format mpd_format;
- enum oss_setup_result result =
- oss_probe_sample_format(fd, audio_format->format,
- &mpd_format, oss_format_r,
-#ifdef AFMT_S24_PACKED
- export,
-#endif
- error_r);
- switch (result) {
- case SUCCESS:
- audio_format->format = mpd_format;
- return true;
-
- case ERROR:
- return false;
-
- case UNSUPPORTED:
- break;
- }
-
- if (result != UNSUPPORTED)
- return result == SUCCESS;
-
- /* the requested sample format is not available - probe for
- other formats supported by MPD */
-
- static const enum sample_format sample_formats[] = {
- SAMPLE_FORMAT_S24_P32,
- SAMPLE_FORMAT_S32,
- SAMPLE_FORMAT_S16,
- SAMPLE_FORMAT_S8,
- SAMPLE_FORMAT_UNDEFINED /* sentinel */
- };
-
- for (unsigned i = 0; sample_formats[i] != SAMPLE_FORMAT_UNDEFINED; ++i) {
- mpd_format = sample_formats[i];
- if (mpd_format == audio_format->format)
- /* don't try that again */
- continue;
-
- result = oss_probe_sample_format(fd, mpd_format,
- &mpd_format, oss_format_r,
-#ifdef AFMT_S24_PACKED
- export,
-#endif
- error_r);
- switch (result) {
- case SUCCESS:
- audio_format->format = mpd_format;
- return true;
-
- case ERROR:
- return false;
-
- case UNSUPPORTED:
- break;
- }
- }
-
- g_set_error_literal(error_r, oss_output_quark(), EINVAL,
- "Failed to set sample format");
- return false;
-}
-
-/**
- * Sets up the OSS device which was opened before.
- */
-static bool
-oss_setup(struct oss_data *od, struct audio_format *audio_format,
- GError **error_r)
-{
- return oss_setup_channels(od->fd, audio_format, error_r) &&
- oss_setup_sample_rate(od->fd, audio_format, error_r) &&
- oss_setup_sample_format(od->fd, audio_format, &od->oss_format,
-#ifdef AFMT_S24_PACKED
- &od->export,
-#endif
- error_r);
-}
-
-/**
- * Reopen the device with the saved audio_format, without any probing.
- */
-static bool
-oss_reopen(struct oss_data *od, GError **error_r)
-{
- assert(od->fd < 0);
-
- od->fd = open_cloexec(od->device, O_WRONLY, 0);
- if (od->fd < 0) {
- g_set_error(error_r, oss_output_quark(), errno,
- "Error opening OSS device \"%s\": %s",
- od->device, g_strerror(errno));
- return false;
- }
-
- enum oss_setup_result result;
-
- const char *const msg1 = "Failed to set channel count";
- result = oss_try_ioctl(od->fd, SNDCTL_DSP_CHANNELS,
- od->audio_format.channels, msg1, error_r);
- if (result != SUCCESS) {
- oss_close(od);
- if (result == UNSUPPORTED)
- g_set_error(error_r, oss_output_quark(), EINVAL,
- "%s", msg1);
- return false;
- }
-
- const char *const msg2 = "Failed to set sample rate";
- result = oss_try_ioctl(od->fd, SNDCTL_DSP_SPEED,
- od->audio_format.sample_rate, msg2, error_r);
- if (result != SUCCESS) {
- oss_close(od);
- if (result == UNSUPPORTED)
- g_set_error(error_r, oss_output_quark(), EINVAL,
- "%s", msg2);
- return false;
- }
-
- const char *const msg3 = "Failed to set sample format";
- result = oss_try_ioctl(od->fd, SNDCTL_DSP_SAMPLESIZE,
- od->oss_format,
- msg3, error_r);
- if (result != SUCCESS) {
- oss_close(od);
- if (result == UNSUPPORTED)
- g_set_error(error_r, oss_output_quark(), EINVAL,
- "%s", msg3);
- return false;
- }
-
- return true;
-}
-
-static bool
-oss_output_open(struct audio_output *ao, struct audio_format *audio_format,
- GError **error)
-{
- struct oss_data *od = (struct oss_data *)ao;
-
- od->fd = open_cloexec(od->device, O_WRONLY, 0);
- if (od->fd < 0) {
- g_set_error(error, oss_output_quark(), errno,
- "Error opening OSS device \"%s\": %s",
- od->device, g_strerror(errno));
- return false;
- }
-
- if (!oss_setup(od, audio_format, error)) {
- oss_close(od);
- return false;
- }
-
- od->audio_format = *audio_format;
- return true;
-}
-
-static void
-oss_output_close(struct audio_output *ao)
-{
- struct oss_data *od = (struct oss_data *)ao;
-
- oss_close(od);
-}
-
-static void
-oss_output_cancel(struct audio_output *ao)
-{
- struct oss_data *od = (struct oss_data *)ao;
-
- if (od->fd >= 0) {
- ioctl(od->fd, SNDCTL_DSP_RESET, 0);
- oss_close(od);
- }
-}
-
-static size_t
-oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
- GError **error)
-{
- struct oss_data *od = (struct oss_data *)ao;
- ssize_t ret;
-
- /* reopen the device since it was closed by dropBufferedAudio */
- if (od->fd < 0 && !oss_reopen(od, error))
- return 0;
-
-#ifdef AFMT_S24_PACKED
- chunk = pcm_export(&od->export, chunk, size, &size);
-#endif
-
- while (true) {
- ret = write(od->fd, chunk, size);
- if (ret > 0) {
-#ifdef AFMT_S24_PACKED
- ret = pcm_export_source_size(&od->export, ret);
-#endif
- return ret;
- }
-
- if (ret < 0 && errno != EINTR) {
- g_set_error(error, oss_output_quark(), errno,
- "Write error on %s: %s",
- od->device, g_strerror(errno));
- return 0;
- }
- }
-}
-
-const struct audio_output_plugin oss_output_plugin = {
- .name = "oss",
- .test_default_device = oss_output_test_default_device,
- .init = oss_output_init,
- .finish = oss_output_finish,
-#ifdef AFMT_S24_PACKED
- .enable = oss_output_enable,
- .disable = oss_output_disable,
-#endif
- .open = oss_output_open,
- .close = oss_output_close,
- .play = oss_output_play,
- .cancel = oss_output_cancel,
-
- .mixer_plugin = &oss_mixer_plugin,
-};
diff --git a/src/output/oss_output_plugin.h b/src/output/oss_output_plugin.h
deleted file mode 100644
index 2aecc2b3a..000000000
--- a/src/output/oss_output_plugin.h
+++ /dev/null
@@ -1,25 +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.
- */
-
-#ifndef MPD_OSS_OUTPUT_PLUGIN_H
-#define MPD_OSS_OUTPUT_PLUGIN_H
-
-extern const struct audio_output_plugin oss_output_plugin;
-
-#endif
diff --git a/src/output/osx_output_plugin.c b/src/output/osx_output_plugin.c
deleted file mode 100644
index cd51fca20..000000000
--- a/src/output/osx_output_plugin.c
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Copyright (C) 2003-2012 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 "osx_output_plugin.h"
-#include "output_api.h"
-#include "fifo_buffer.h"
-
-#include <glib.h>
-#include <CoreAudio/AudioHardware.h>
-#include <AudioUnit/AudioUnit.h>
-#include <CoreServices/CoreServices.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "osx"
-
-struct osx_output {
- struct audio_output base;
-
- /* configuration settings */
- OSType component_subtype;
- /* only applicable with kAudioUnitSubType_HALOutput */
- const char *device_name;
-
- AudioUnit au;
- GMutex *mutex;
- GCond *condition;
-
- struct fifo_buffer *buffer;
-};
-
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-osx_output_quark(void)
-{
- return g_quark_from_static_string("osx_output");
-}
-
-static bool
-osx_output_test_default_device(void)
-{
- /* on a Mac, this is always the default plugin, if nothing
- else is configured */
- return true;
-}
-
-static void
-osx_output_configure(struct osx_output *oo, const struct config_param *param)
-{
- const char *device = config_get_block_string(param, "device", NULL);
-
- if (device == NULL || 0 == strcmp(device, "default")) {
- oo->component_subtype = kAudioUnitSubType_DefaultOutput;
- oo->device_name = NULL;
- }
- else if (0 == strcmp(device, "system")) {
- oo->component_subtype = kAudioUnitSubType_SystemOutput;
- oo->device_name = NULL;
- }
- else {
- oo->component_subtype = kAudioUnitSubType_HALOutput;
- /* XXX am I supposed to g_strdup() this? */
- oo->device_name = device;
- }
-}
-
-static struct audio_output *
-osx_output_init(const struct config_param *param, GError **error_r)
-{
- struct osx_output *oo = g_new(struct osx_output, 1);
- if (!ao_base_init(&oo->base, &osx_output_plugin, param, error_r)) {
- g_free(oo);
- return NULL;
- }
-
- osx_output_configure(oo, param);
- oo->mutex = g_mutex_new();
- oo->condition = g_cond_new();
-
- return &oo->base;
-}
-
-static void
-osx_output_finish(struct audio_output *ao)
-{
- struct osx_output *od = (struct osx_output *)ao;
-
- g_mutex_free(od->mutex);
- g_cond_free(od->condition);
- g_free(od);
-}
-
-static bool
-osx_output_set_device(struct osx_output *oo, GError **error)
-{
- bool ret = true;
- OSStatus status;
- UInt32 size, numdevices;
- AudioDeviceID *deviceids = NULL;
- char name[256];
- unsigned int i;
-
- if (oo->component_subtype != kAudioUnitSubType_HALOutput)
- goto done;
-
- /* how many audio devices are there? */
- status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
- &size,
- NULL);
- if (status != noErr) {
- g_set_error(error, osx_output_quark(), status,
- "Unable to determine number of OS X audio devices: %s",
- GetMacOSStatusCommentString(status));
- ret = false;
- goto done;
- }
-
- /* what are the available audio device IDs? */
- numdevices = size / sizeof(AudioDeviceID);
- deviceids = g_malloc(size);
- status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
- &size,
- deviceids);
- if (status != noErr) {
- g_set_error(error, osx_output_quark(), status,
- "Unable to determine OS X audio device IDs: %s",
- GetMacOSStatusCommentString(status));
- ret = false;
- goto done;
- }
-
- /* which audio device matches oo->device_name? */
- for (i = 0; i < numdevices; i++) {
- size = sizeof(name);
- status = AudioDeviceGetProperty(deviceids[i], 0, false,
- kAudioDevicePropertyDeviceName,
- &size, name);
- if (status != noErr) {
- g_set_error(error, osx_output_quark(), status,
- "Unable to determine OS X device name "
- "(device %u): %s",
- (unsigned int) deviceids[i],
- GetMacOSStatusCommentString(status));
- ret = false;
- goto done;
- }
- if (strcmp(oo->device_name, name) == 0) {
- g_debug("found matching device: ID=%u, name=%s",
- (unsigned int) deviceids[i], name);
- break;
- }
- }
- if (i == numdevices) {
- g_warning("Found no audio device with name '%s' "
- "(will use default audio device)",
- oo->device_name);
- goto done;
- }
-
- status = AudioUnitSetProperty(oo->au,
- kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global,
- 0,
- &(deviceids[i]),
- sizeof(AudioDeviceID));
- if (status != noErr) {
- g_set_error(error, osx_output_quark(), status,
- "Unable to set OS X audio output device: %s",
- GetMacOSStatusCommentString(status));
- ret = false;
- goto done;
- }
- g_debug("set OS X audio output device ID=%u, name=%s",
- (unsigned int) deviceids[i], name);
-
-done:
- if (deviceids != NULL)
- g_free(deviceids);
- return ret;
-}
-
-static OSStatus
-osx_render(void *vdata,
- G_GNUC_UNUSED AudioUnitRenderActionFlags *io_action_flags,
- G_GNUC_UNUSED const AudioTimeStamp *in_timestamp,
- G_GNUC_UNUSED UInt32 in_bus_number,
- G_GNUC_UNUSED UInt32 in_number_frames,
- AudioBufferList *buffer_list)
-{
- struct osx_output *od = (struct osx_output *) vdata;
- AudioBuffer *buffer = &buffer_list->mBuffers[0];
- size_t buffer_size = buffer->mDataByteSize;
-
- assert(od->buffer != NULL);
-
- g_mutex_lock(od->mutex);
-
- size_t nbytes;
- const void *src = fifo_buffer_read(od->buffer, &nbytes);
-
- if (src != NULL) {
- if (nbytes > buffer_size)
- nbytes = buffer_size;
-
- memcpy(buffer->mData, src, nbytes);
- fifo_buffer_consume(od->buffer, nbytes);
- } else
- nbytes = 0;
-
- g_cond_signal(od->condition);
- g_mutex_unlock(od->mutex);
-
- buffer->mDataByteSize = nbytes;
-
- unsigned i;
- for (i = 1; i < buffer_list->mNumberBuffers; ++i) {
- buffer = &buffer_list->mBuffers[i];
- buffer->mDataByteSize = 0;
- }
-
- return 0;
-}
-
-static bool
-osx_output_enable(struct audio_output *ao, GError **error_r)
-{
- struct osx_output *oo = (struct osx_output *)ao;
-
- ComponentDescription desc;
- desc.componentType = kAudioUnitType_Output;
- desc.componentSubType = oo->component_subtype;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-
- Component comp = FindNextComponent(NULL, &desc);
- if (comp == 0) {
- g_set_error(error_r, osx_output_quark(), 0,
- "Error finding OS X component");
- return false;
- }
-
- OSStatus status = OpenAComponent(comp, &oo->au);
- if (status != noErr) {
- g_set_error(error_r, osx_output_quark(), status,
- "Unable to open OS X component: %s",
- GetMacOSStatusCommentString(status));
- return false;
- }
-
- if (!osx_output_set_device(oo, error_r)) {
- CloseComponent(oo->au);
- return false;
- }
-
- AURenderCallbackStruct callback;
- callback.inputProc = osx_render;
- callback.inputProcRefCon = oo;
-
- ComponentResult result =
- AudioUnitSetProperty(oo->au,
- kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Input, 0,
- &callback, sizeof(callback));
- if (result != noErr) {
- CloseComponent(oo->au);
- g_set_error(error_r, osx_output_quark(), result,
- "unable to set callback for OS X audio unit");
- return false;
- }
-
- return true;
-}
-
-static void
-osx_output_disable(struct audio_output *ao)
-{
- struct osx_output *oo = (struct osx_output *)ao;
-
- CloseComponent(oo->au);
-}
-
-static void
-osx_output_cancel(struct audio_output *ao)
-{
- struct osx_output *od = (struct osx_output *)ao;
-
- g_mutex_lock(od->mutex);
- fifo_buffer_clear(od->buffer);
- g_mutex_unlock(od->mutex);
-}
-
-static void
-osx_output_close(struct audio_output *ao)
-{
- struct osx_output *od = (struct osx_output *)ao;
-
- AudioOutputUnitStop(od->au);
- AudioUnitUninitialize(od->au);
-
- fifo_buffer_free(od->buffer);
-}
-
-static bool
-osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
-{
- struct osx_output *od = (struct osx_output *)ao;
-
- AudioStreamBasicDescription stream_description;
- stream_description.mSampleRate = audio_format->sample_rate;
- stream_description.mFormatID = kAudioFormatLinearPCM;
- stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
-
- switch (audio_format->format) {
- case SAMPLE_FORMAT_S8:
- stream_description.mBitsPerChannel = 8;
- break;
-
- case SAMPLE_FORMAT_S16:
- stream_description.mBitsPerChannel = 16;
- break;
-
- case SAMPLE_FORMAT_S32:
- stream_description.mBitsPerChannel = 32;
- break;
-
- default:
- audio_format->format = SAMPLE_FORMAT_S32;
- stream_description.mBitsPerChannel = 32;
- break;
- }
-
-#if G_BYTE_ORDER == G_BIG_ENDIAN
- stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-#endif
-
- stream_description.mBytesPerPacket =
- audio_format_frame_size(audio_format);
- stream_description.mFramesPerPacket = 1;
- stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
- stream_description.mChannelsPerFrame = audio_format->channels;
-
- ComponentResult result =
- AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Input, 0,
- &stream_description,
- sizeof(stream_description));
- if (result != noErr) {
- g_set_error(error, osx_output_quark(), result,
- "Unable to set format on OS X device");
- return false;
- }
-
- OSStatus status = AudioUnitInitialize(od->au);
- if (status != noErr) {
- g_set_error(error, osx_output_quark(), status,
- "Unable to initialize OS X audio unit: %s",
- GetMacOSStatusCommentString(status));
- return false;
- }
-
- /* create a buffer of 1s */
- od->buffer = fifo_buffer_new(audio_format->sample_rate *
- audio_format_frame_size(audio_format));
-
- status = AudioOutputUnitStart(od->au);
- if (status != 0) {
- AudioUnitUninitialize(od->au);
- g_set_error(error, osx_output_quark(), status,
- "unable to start audio output: %s",
- GetMacOSStatusCommentString(status));
- return false;
- }
-
- return true;
-}
-
-static size_t
-osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
- G_GNUC_UNUSED GError **error)
-{
- struct osx_output *od = (struct osx_output *)ao;
-
- g_mutex_lock(od->mutex);
-
- void *dest;
- size_t max_length;
-
- while (true) {
- dest = fifo_buffer_write(od->buffer, &max_length);
- if (dest != NULL)
- break;
-
- /* wait for some free space in the buffer */
- g_cond_wait(od->condition, od->mutex);
- }
-
- if (size > max_length)
- size = max_length;
-
- memcpy(dest, chunk, size);
- fifo_buffer_append(od->buffer, size);
-
- g_mutex_unlock(od->mutex);
-
- return size;
-}
-
-const struct audio_output_plugin osx_output_plugin = {
- .name = "osx",
- .test_default_device = osx_output_test_default_device,
- .init = osx_output_init,
- .finish = osx_output_finish,
- .enable = osx_output_enable,
- .disable = osx_output_disable,
- .open = osx_output_open,
- .close = osx_output_close,
- .play = osx_output_play,
- .cancel = osx_output_cancel,
-};
diff --git a/src/output/osx_output_plugin.h b/src/output/osx_output_plugin.h
deleted file mode 100644
index 814702d4f..000000000
--- a/src/output/osx_output_plugin.h
+++ /dev/null
@@ -1,25 +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.
- */
-
-#ifndef MPD_OSX_OUTPUT_PLUGIN_H
-#define MPD_OSX_OUTPUT_PLUGIN_H
-
-extern const struct audio_output_plugin osx_output_plugin;
-
-#endif
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index e267427df..457fa9f04 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -21,7 +21,7 @@
#include "pulse_output_plugin.h"
#include "output_api.h"
#include "mixer_list.h"
-#include "mixer/pulse_mixer_plugin.h"
+#include "mixer/PulseMixerPlugin.h"
#include <glib.h>
diff --git a/src/output/pulse_output_plugin.h b/src/output/pulse_output_plugin.h
index 02a51f27b..bcc8004a7 100644
--- a/src/output/pulse_output_plugin.h
+++ b/src/output/pulse_output_plugin.h
@@ -20,9 +20,9 @@
#ifndef MPD_PULSE_OUTPUT_PLUGIN_H
#define MPD_PULSE_OUTPUT_PLUGIN_H
-#include <stdbool.h>
+#include "gerror.h"
-#include <glib.h>
+#include <stdbool.h>
struct pulse_output;
struct pulse_mixer;
@@ -30,6 +30,10 @@ struct pa_cvolume;
extern const struct audio_output_plugin pulse_output_plugin;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
pulse_output_lock(struct pulse_output *po);
@@ -46,4 +50,8 @@ bool
pulse_output_set_volume(struct pulse_output *po,
const struct pa_cvolume *volume, GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/output/roar_output_plugin.c b/src/output/roar_output_plugin.c
deleted file mode 100644
index 1c2c48321..000000000
--- a/src/output/roar_output_plugin.c
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
- * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
- * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
- *
- * 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 "roar_output_plugin.h"
-#include "output_api.h"
-#include "mixer_list.h"
-#include "roar_output_plugin.h"
-
-#include <glib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <roaraudio.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "roaraudio"
-
-typedef struct roar
-{
- struct audio_output base;
-
- roar_vs_t * vss;
- int err;
- char *host;
- char *name;
- int role;
- struct roar_connection con;
- struct roar_audio_info info;
- GMutex *lock;
- volatile bool alive;
-} roar_t;
-
-static inline GQuark
-roar_output_quark(void)
-{
- return g_quark_from_static_string("roar_output");
-}
-
-static int
-roar_output_get_volume_locked(struct roar *roar)
-{
- if (roar->vss == NULL || !roar->alive)
- return -1;
-
- float l, r;
- int error;
- if (roar_vs_volume_get(roar->vss, &l, &r, &error) < 0)
- return -1;
-
- return (l + r) * 50;
-}
-
-int
-roar_output_get_volume(struct roar *roar)
-{
- g_mutex_lock(roar->lock);
- int volume = roar_output_get_volume_locked(roar);
- g_mutex_unlock(roar->lock);
- return volume;
-}
-
-static bool
-roar_output_set_volume_locked(struct roar *roar, unsigned volume)
-{
- assert(volume <= 100);
-
- if (roar->vss == NULL || !roar->alive)
- return false;
-
- int error;
- float level = volume / 100.0;
-
- roar_vs_volume_mono(roar->vss, level, &error);
- return true;
-}
-
-bool
-roar_output_set_volume(struct roar *roar, unsigned volume)
-{
- g_mutex_lock(roar->lock);
- bool success = roar_output_set_volume_locked(roar, volume);
- g_mutex_unlock(roar->lock);
- return success;
-}
-
-static void
-roar_configure(struct roar * self, const struct config_param *param)
-{
- self->host = config_dup_block_string(param, "server", NULL);
- self->name = config_dup_block_string(param, "name", "MPD");
-
- const char *role = config_get_block_string(param, "role", "music");
- self->role = role != NULL
- ? roar_str2role(role)
- : ROAR_ROLE_MUSIC;
-}
-
-static struct audio_output *
-roar_init(const struct config_param *param, GError **error_r)
-{
- struct roar *self = g_new0(struct roar, 1);
-
- if (!ao_base_init(&self->base, &roar_output_plugin, param, error_r)) {
- g_free(self);
- return NULL;
- }
-
- self->lock = g_mutex_new();
- self->err = ROAR_ERROR_NONE;
- roar_configure(self, param);
- return &self->base;
-}
-
-static void
-roar_finish(struct audio_output *ao)
-{
- struct roar *self = (struct roar *)ao;
-
- g_free(self->host);
- g_free(self->name);
- g_mutex_free(self->lock);
-
- ao_base_finish(&self->base);
- g_free(self);
-}
-
-static void
-roar_use_audio_format(struct roar_audio_info *info,
- struct audio_format *audio_format)
-{
- info->rate = audio_format->sample_rate;
- info->channels = audio_format->channels;
- info->codec = ROAR_CODEC_PCM_S;
-
- switch (audio_format->format) {
- case SAMPLE_FORMAT_UNDEFINED:
- info->bits = 16;
- audio_format->format = SAMPLE_FORMAT_S16;
- break;
-
- case SAMPLE_FORMAT_S8:
- info->bits = 8;
- break;
-
- case SAMPLE_FORMAT_S16:
- info->bits = 16;
- break;
-
- case SAMPLE_FORMAT_S24_P32:
- info->bits = 32;
- audio_format->format = SAMPLE_FORMAT_S32;
- break;
-
- case SAMPLE_FORMAT_S32:
- info->bits = 32;
- break;
- }
-}
-
-static bool
-roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
-{
- struct roar *self = (struct roar *)ao;
- g_mutex_lock(self->lock);
-
- if (roar_simple_connect(&(self->con), self->host, self->name) < 0)
- {
- g_set_error(error, roar_output_quark(), 0,
- "Failed to connect to Roar server");
- g_mutex_unlock(self->lock);
- return false;
- }
-
- self->vss = roar_vs_new_from_con(&(self->con), &(self->err));
-
- if (self->vss == NULL || self->err != ROAR_ERROR_NONE)
- {
- g_set_error(error, roar_output_quark(), 0,
- "Failed to connect to server");
- g_mutex_unlock(self->lock);
- return false;
- }
-
- roar_use_audio_format(&self->info, audio_format);
-
- if (roar_vs_stream(self->vss, &(self->info), ROAR_DIR_PLAY,
- &(self->err)) < 0)
- {
- g_set_error(error, roar_output_quark(), 0, "Failed to start stream");
- g_mutex_unlock(self->lock);
- return false;
- }
- roar_vs_role(self->vss, self->role, &(self->err));
- self->alive = true;
-
- g_mutex_unlock(self->lock);
- return true;
-}
-
-static void
-roar_close(struct audio_output *ao)
-{
- struct roar *self = (struct roar *)ao;
- g_mutex_lock(self->lock);
- self->alive = false;
-
- if (self->vss != NULL)
- roar_vs_close(self->vss, ROAR_VS_TRUE, &(self->err));
- self->vss = NULL;
- roar_disconnect(&(self->con));
- g_mutex_unlock(self->lock);
-}
-
-static void
-roar_cancel_locked(struct roar *self)
-{
- if (self->vss == NULL)
- return;
-
- roar_vs_t *vss = self->vss;
- self->vss = NULL;
- roar_vs_close(vss, ROAR_VS_TRUE, &(self->err));
- self->alive = false;
-
- vss = roar_vs_new_from_con(&(self->con), &(self->err));
- if (vss == NULL)
- return;
-
- if (roar_vs_stream(vss, &(self->info), ROAR_DIR_PLAY,
- &(self->err)) < 0) {
- roar_vs_close(vss, ROAR_VS_TRUE, &(self->err));
- g_warning("Failed to start stream");
- return;
- }
-
- roar_vs_role(vss, self->role, &(self->err));
- self->vss = vss;
- self->alive = true;
-}
-
-static void
-roar_cancel(struct audio_output *ao)
-{
- struct roar *self = (struct roar *)ao;
-
- g_mutex_lock(self->lock);
- roar_cancel_locked(self);
- g_mutex_unlock(self->lock);
-}
-
-static size_t
-roar_play(struct audio_output *ao, const void *chunk, size_t size, GError **error)
-{
- struct roar *self = (struct roar *)ao;
- ssize_t rc;
-
- if (self->vss == NULL)
- {
- g_set_error(error, roar_output_quark(), 0, "Connection is invalid");
- return 0;
- }
-
- rc = roar_vs_write(self->vss, chunk, size, &(self->err));
- if ( rc <= 0 )
- {
- g_set_error(error, roar_output_quark(), 0, "Failed to play data");
- return 0;
- }
-
- return rc;
-}
-
-static const char*
-roar_tag_convert(enum tag_type type, bool *is_uuid)
-{
- *is_uuid = false;
- switch (type)
- {
- case TAG_ARTIST:
- case TAG_ALBUM_ARTIST:
- return "AUTHOR";
- case TAG_ALBUM:
- return "ALBUM";
- case TAG_TITLE:
- return "TITLE";
- case TAG_TRACK:
- return "TRACK";
- case TAG_NAME:
- return "NAME";
- case TAG_GENRE:
- return "GENRE";
- case TAG_DATE:
- return "DATE";
- case TAG_PERFORMER:
- return "PERFORMER";
- case TAG_COMMENT:
- return "COMMENT";
- case TAG_DISC:
- return "DISCID";
- case TAG_COMPOSER:
-#ifdef ROAR_META_TYPE_COMPOSER
- return "COMPOSER";
-#else
- return "AUTHOR";
-#endif
- case TAG_MUSICBRAINZ_ARTISTID:
- case TAG_MUSICBRAINZ_ALBUMID:
- case TAG_MUSICBRAINZ_ALBUMARTISTID:
- case TAG_MUSICBRAINZ_TRACKID:
- *is_uuid = true;
- return "HASH";
-
- default:
- return NULL;
- }
-}
-
-static void
-roar_send_tag(struct audio_output *ao, const struct tag *meta)
-{
- struct roar *self = (struct roar *)ao;
-
- if (self->vss == NULL)
- return;
-
- g_mutex_lock(self->lock);
- size_t cnt = 1;
- struct roar_keyval vals[32];
- memset(vals, 0, sizeof(vals));
- char uuid_buf[32][64];
-
- char timebuf[16];
- snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d",
- meta->time / 3600, (meta->time % 3600) / 60, meta->time % 60);
-
- vals[0].key = g_strdup("LENGTH");
- vals[0].value = timebuf;
-
- for (unsigned i = 0; i < meta->num_items && cnt < 32; i++)
- {
- bool is_uuid = false;
- const char *key = roar_tag_convert(meta->items[i]->type, &is_uuid);
- if (key != NULL)
- {
- if (is_uuid)
- {
- snprintf(uuid_buf[cnt], sizeof(uuid_buf[0]), "{UUID}%s",
- meta->items[i]->value);
- vals[cnt].key = g_strdup(key);
- vals[cnt].value = uuid_buf[cnt];
- }
- else
- {
- vals[cnt].key = g_strdup(key);
- vals[cnt].value = meta->items[i]->value;
- }
- cnt++;
- }
- }
-
- roar_vs_meta(self->vss, vals, cnt, &(self->err));
-
- for (unsigned i = 0; i < 32; i++)
- g_free(vals[i].key);
-
- g_mutex_unlock(self->lock);
-}
-
-const struct audio_output_plugin roar_output_plugin = {
- .name = "roar",
- .init = roar_init,
- .finish = roar_finish,
- .open = roar_open,
- .play = roar_play,
- .cancel = roar_cancel,
- .close = roar_close,
- .send_tag = roar_send_tag,
-
- .mixer_plugin = &roar_mixer_plugin
-};
diff --git a/src/output/roar_output_plugin.h b/src/output/roar_output_plugin.h
deleted file mode 100644
index 78b628cc4..000000000
--- a/src/output/roar_output_plugin.h
+++ /dev/null
@@ -1,35 +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.
- */
-
-#ifndef MPD_ROAR_OUTPUT_PLUGIN_H
-#define MPD_ROAR_OUTPUT_PLUGIN_H
-
-#include <stdbool.h>
-
-struct roar;
-
-extern const struct audio_output_plugin roar_output_plugin;
-
-int
-roar_output_get_volume(struct roar *roar);
-
-bool
-roar_output_set_volume(struct roar *roar, unsigned volume);
-
-#endif
diff --git a/src/output/shout_output_plugin.c b/src/output/shout_output_plugin.c
index 56456a0ea..56d7a88b1 100644
--- a/src/output/shout_output_plugin.c
+++ b/src/output/shout_output_plugin.c
@@ -97,23 +97,22 @@ static void free_shout_data(struct shout_data *sd)
g_free(sd);
}
-#define check_block_param(name) { \
- block_param = config_get_block_param(param, name); \
- if (!block_param) { \
- MPD_ERROR("no \"%s\" defined for shout device defined at line " \
- "%i\n", name, param->line); \
- } \
- }
+gcc_pure
+static const char *
+require_block_string(const struct config_param *param, const char *name)
+{
+ const char *value = config_get_block_string(param, name, NULL);
+ if (value == NULL)
+ MPD_ERROR("no \"%s\" defined for shout device defined at line " \
+ "%i\n", name, param->line); \
-static struct audio_output *
-my_shout_init_driver(const struct config_param *param,
- GError **error)
+ return value;
+}
+
+static bool
+my_shout_configure(struct shout_data *sd, const struct config_param *param,
+ GError **error)
{
- struct shout_data *sd = new_shout_data();
- if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
- free_shout_data(sd);
- return NULL;
- }
const struct audio_format *audio_format =
&sd->base.config_audio_format;
@@ -125,30 +124,18 @@ my_shout_init_driver(const struct config_param *param,
return NULL;
}
- if (shout_init_count == 0)
- shout_init();
-
- shout_init_count++;
-
- const struct block_param *block_param;
- check_block_param("host");
- char *host = block_param->value;
-
- check_block_param("mount");
- char *mount = block_param->value;
+ const char *host = require_block_string(param, "host");
+ const char *mount = require_block_string(param, "mount");
unsigned port = config_get_block_unsigned(param, "port", 0);
if (port == 0) {
g_set_error(error, shout_output_quark(), 0,
"shout port must be configured");
- goto failure;
+ return false;
}
- check_block_param("password");
- const char *passwd = block_param->value;
-
- check_block_param("name");
- const char *name = block_param->value;
+ const char *passwd = require_block_string(param, "password");
+ const char *name = require_block_string(param, "name");
bool public = config_get_block_bool(param, "public", false);
@@ -164,21 +151,21 @@ my_shout_init_driver(const struct config_param *param,
"shout quality \"%s\" is not a number in the "
"range -1 to 10, line %i",
value, param->line);
- goto failure;
+ return false;
}
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
g_set_error(error, shout_output_quark(), 0,
"quality and bitrate are "
"both defined");
- goto failure;
+ return false;
}
} else {
value = config_get_block_string(param, "bitrate", NULL);
if (value == NULL) {
g_set_error(error, shout_output_quark(), 0,
"neither bitrate nor quality defined");
- goto failure;
+ return false;
}
char *test;
@@ -187,7 +174,7 @@ my_shout_init_driver(const struct config_param *param,
if (*test != '\0' || sd->bitrate <= 0) {
g_set_error(error, shout_output_quark(), 0,
"bitrate must be a positive integer");
- goto failure;
+ return false;
}
}
@@ -199,12 +186,12 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"couldn't find shout encoder plugin \"%s\"",
encoding);
- goto failure;
+ return false;
}
sd->encoder = encoder_init(encoder_plugin, param, error);
if (sd->encoder == NULL)
- goto failure;
+ return false;
unsigned shout_format;
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
@@ -220,7 +207,7 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"you cannot stream \"%s\" to shoutcast, use mp3",
encoding);
- goto failure;
+ return false;
} else if (0 == strcmp(value, "shoutcast"))
protocol = SHOUT_PROTOCOL_ICY;
else if (0 == strcmp(value, "icecast1"))
@@ -232,7 +219,7 @@ my_shout_init_driver(const struct config_param *param,
"shout protocol \"%s\" is not \"shoutcast\" or "
"\"icecast1\"or \"icecast2\"",
value);
- goto failure;
+ return false;
}
} else {
protocol = SHOUT_PROTOCOL_HTTP;
@@ -251,7 +238,7 @@ my_shout_init_driver(const struct config_param *param,
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
/* optional paramters */
@@ -262,21 +249,21 @@ my_shout_init_driver(const struct config_param *param,
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "description", NULL);
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "url", NULL);
if (value != NULL && shout_set_url(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
{
@@ -301,12 +288,31 @@ my_shout_init_driver(const struct config_param *param,
}
}
- return &sd->base;
+ return true;
+}
-failure:
- ao_base_finish(&sd->base);
- free_shout_data(sd);
- return NULL;
+static struct audio_output *
+my_shout_init_driver(const struct config_param *param,
+ GError **error)
+{
+ struct shout_data *sd = new_shout_data();
+ if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (!my_shout_configure(sd, param, error)) {
+ ao_base_finish(&sd->base);
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (shout_init_count == 0)
+ shout_init();
+
+ shout_init_count++;
+
+ return &sd->base;
}
static bool
diff --git a/src/output/winmm_output_plugin.c b/src/output/winmm_output_plugin.c
index 4d95834b9..c1b3af126 100644
--- a/src/output/winmm_output_plugin.c
+++ b/src/output/winmm_output_plugin.c
@@ -26,7 +26,6 @@
#include <stdlib.h>
#include <string.h>
-#include <windows.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "winmm_output"
diff --git a/src/output/winmm_output_plugin.h b/src/output/winmm_output_plugin.h
index 0605530e1..364356483 100644
--- a/src/output/winmm_output_plugin.h
+++ b/src/output/winmm_output_plugin.h
@@ -25,6 +25,7 @@
#ifdef ENABLE_WINMM_OUTPUT
#include <windows.h>
+#include <mmsystem.h>
struct winmm_output;
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;
-}
diff --git a/src/output_all.h b/src/output_all.h
deleted file mode 100644
index 4eeb94f13..000000000
--- a/src/output_all.h
+++ /dev/null
@@ -1,166 +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.
- */
-
-/*
- * Functions for dealing with all configured (enabled) audion outputs
- * at once.
- *
- */
-
-#ifndef OUTPUT_ALL_H
-#define OUTPUT_ALL_H
-
-#include <stdbool.h>
-#include <stddef.h>
-
-struct audio_format;
-struct music_buffer;
-struct music_chunk;
-struct player_control;
-
-/**
- * Global initialization: load audio outputs from the configuration
- * file and initialize them.
- */
-void
-audio_output_all_init(struct player_control *pc);
-
-/**
- * Global finalization: free memory occupied by audio outputs. All
- */
-void
-audio_output_all_finish(void);
-
-/**
- * Returns the total number of audio output devices, including those
- * who are disabled right now.
- */
-unsigned int audio_output_count(void);
-
-/**
- * Returns the "i"th audio output device.
- */
-struct audio_output *
-audio_output_get(unsigned i);
-
-/**
- * Returns the audio output device with the specified name. Returns
- * NULL if the name does not exist.
- */
-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
- * the previous format
- * @param buffer the #music_buffer where consumed #music_chunk objects
- * should be returned
- * @return true on success, false on failure
- */
-bool
-audio_output_all_open(const struct audio_format *audio_format,
- struct music_buffer *buffer);
-
-/**
- * Closes all audio outputs.
- */
-void
-audio_output_all_close(void);
-
-/**
- * Closes all audio outputs. Outputs with the "always_on" flag are
- * put into pause mode.
- */
-void
-audio_output_all_release(void);
-
-/**
- * Enqueue a #music_chunk object for playing, i.e. pushes it to a
- * #music_pipe.
- *
- * @param chunk the #music_chunk object to be played
- * @return true on success, false if no audio output was able to play
- * (all closed then)
- */
-bool
-audio_output_all_play(struct music_chunk *chunk);
-
-/**
- * Checks if the output devices have drained their music pipe, and
- * returns the consumed music chunks to the #music_buffer.
- *
- * @return the number of chunks to play left in the #music_pipe
- */
-unsigned
-audio_output_all_check(void);
-
-/**
- * Checks if the size of the #music_pipe is below the #threshold. If
- * not, it attempts to synchronize with all output threads, and waits
- * until another #music_chunk is finished.
- *
- * @param threshold the maximum number of chunks in the pipe
- * @return true if there are less than #threshold chunks in the pipe
- */
-bool
-audio_output_all_wait(struct player_control *pc, unsigned threshold);
-
-/**
- * Puts all audio outputs into pause mode. Most implementations will
- * simply close it then.
- */
-void
-audio_output_all_pause(void);
-
-/**
- * Drain all audio outputs.
- */
-void
-audio_output_all_drain(void);
-
-/**
- * Try to cancel data which may still be in the device's buffers.
- */
-void
-audio_output_all_cancel(void);
-
-/**
- * Indicate that a new song will begin now.
- */
-void
-audio_output_all_song_border(void);
-
-/**
- * Returns the "elapsed_time" stamp of the most recently finished
- * chunk. A negative value is returned when no chunk has been
- * finished yet.
- */
-float
-audio_output_all_get_elapsed_time(void);
-
-#endif
diff --git a/src/output_command.c b/src/output_command.c
deleted file mode 100644
index 3988f350a..000000000
--- a/src/output_command.c
+++ /dev/null
@@ -1,87 +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.
- */
-
-/*
- * Glue functions for controlling the audio outputs over the MPD
- * protocol. These functions perform extra validation on all
- * parameters, because they might be from an untrusted source.
- *
- */
-
-#include "config.h"
-#include "output_command.h"
-#include "output_all.h"
-#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;
-
-bool
-audio_output_enable_index(unsigned idx)
-{
- struct audio_output *ao;
-
- if (idx >= audio_output_count())
- return false;
-
- ao = audio_output_get(idx);
- if (ao->enabled)
- return true;
-
- ao->enabled = true;
- idle_add(IDLE_OUTPUT);
-
- pc_update_audio(ao->player_control);
-
- ++audio_output_state_version;
-
- return true;
-}
-
-bool
-audio_output_disable_index(unsigned idx)
-{
- struct audio_output *ao;
- struct mixer *mixer;
-
- if (idx >= audio_output_count())
- return false;
-
- ao = audio_output_get(idx);
- if (!ao->enabled)
- return true;
-
- ao->enabled = false;
- idle_add(IDLE_OUTPUT);
-
- mixer = ao->mixer;
- if (mixer != NULL) {
- mixer_close(mixer);
- idle_add(IDLE_MIXER);
- }
-
- pc_update_audio(ao->player_control);
-
- ++audio_output_state_version;
-
- return true;
-}
diff --git a/src/output_command.h b/src/output_command.h
deleted file mode 100644
index eda30acc8..000000000
--- a/src/output_command.h
+++ /dev/null
@@ -1,46 +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.
- */
-
-/*
- * Glue functions for controlling the audio outputs over the MPD
- * protocol. These functions perform extra validation on all
- * parameters, because they might be from an untrusted source.
- *
- */
-
-#ifndef OUTPUT_COMMAND_H
-#define OUTPUT_COMMAND_H
-
-#include <stdbool.h>
-
-/**
- * Enables an audio output. Returns false if the specified output
- * does not exist.
- */
-bool
-audio_output_enable_index(unsigned idx);
-
-/**
- * Disables an audio output. Returns false if the specified output
- * does not exist.
- */
-bool
-audio_output_disable_index(unsigned idx);
-
-#endif
diff --git a/src/output_control.c b/src/output_control.c
deleted file mode 100644
index 7b95be49b..000000000
--- a/src/output_control.c
+++ /dev/null
@@ -1,336 +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_control.h"
-#include "output_api.h"
-#include "output_internal.h"
-#include "output_thread.h"
-#include "mixer_control.h"
-#include "mixer_plugin.h"
-#include "filter_plugin.h"
-#include "notify.h"
-
-#include <assert.h>
-#include <stdlib.h>
-
-enum {
- /** after a failure, wait this number of seconds before
- automatically reopening the device */
- REOPEN_AFTER = 10,
-};
-
-struct notify audio_output_client_notify;
-
-/**
- * Waits for command completion.
- *
- * @param ao the #audio_output instance; must be locked
- */
-static void ao_command_wait(struct audio_output *ao)
-{
- while (ao->command != AO_COMMAND_NONE) {
- g_mutex_unlock(ao->mutex);
- notify_wait(&audio_output_client_notify);
- g_mutex_lock(ao->mutex);
- }
-}
-
-/**
- * Sends a command to the #audio_output object, but does not wait for
- * completion.
- *
- * @param ao the #audio_output instance; must be locked
- */
-static void ao_command_async(struct audio_output *ao,
- enum audio_output_command cmd)
-{
- assert(ao->command == AO_COMMAND_NONE);
- ao->command = cmd;
- g_cond_signal(ao->cond);
-}
-
-/**
- * Sends a command to the #audio_output object and waits for
- * completion.
- *
- * @param ao the #audio_output instance; must be locked
- */
-static void
-ao_command(struct audio_output *ao, enum audio_output_command cmd)
-{
- ao_command_async(ao, cmd);
- ao_command_wait(ao);
-}
-
-/**
- * Lock the #audio_output object and execute the command
- * synchronously.
- */
-static void
-ao_lock_command(struct audio_output *ao, enum audio_output_command cmd)
-{
- g_mutex_lock(ao->mutex);
- ao_command(ao, cmd);
- g_mutex_unlock(ao->mutex);
-}
-
-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_lock_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_lock_command(ao, AO_COMMAND_DISABLE);
-}
-
-/**
- * Object must be locked (and unlocked) by the caller.
- */
-static bool
-audio_output_open(struct audio_output *ao,
- const struct audio_format *audio_format,
- const struct music_pipe *mp)
-{
- bool open;
-
- assert(ao != NULL);
- assert(ao->allow_play);
- assert(audio_format_valid(audio_format));
- assert(mp != NULL);
-
- if (ao->fail_timer != NULL) {
- g_timer_destroy(ao->fail_timer);
- ao->fail_timer = NULL;
- }
-
- if (ao->open &&
- audio_format_equals(audio_format, &ao->in_audio_format)) {
- assert(ao->pipe == mp ||
- (ao->always_on && ao->pause));
-
- if (ao->pause) {
- ao->chunk = NULL;
- ao->pipe = mp;
-
- /* unpause with the CANCEL command; this is a
- hack, but suits well for forcing the thread
- to leave the ao_pause() thread, and we need
- to flush the device buffer anyway */
-
- /* we're not using audio_output_cancel() here,
- because that function is asynchronous */
- ao_command(ao, AO_COMMAND_CANCEL);
- }
-
- return true;
- }
-
- ao->in_audio_format = *audio_format;
- ao->chunk = NULL;
-
- ao->pipe = mp;
-
- if (ao->thread == NULL)
- audio_output_thread_start(ao);
-
- ao_command(ao, ao->open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN);
- open = ao->open;
-
- if (open && ao->mixer != NULL) {
- GError *error = NULL;
-
- if (!mixer_open(ao->mixer, &error)) {
- g_warning("Failed to open mixer for '%s': %s",
- ao->name, error->message);
- g_error_free(error);
- }
- }
-
- return open;
-}
-
-/**
- * Same as audio_output_close(), but expects the lock to be held by
- * the caller.
- */
-static void
-audio_output_close_locked(struct audio_output *ao)
-{
- assert(ao != NULL);
- assert(ao->allow_play);
-
- if (ao->mixer != NULL)
- mixer_auto_close(ao->mixer);
-
- assert(!ao->open || ao->fail_timer == NULL);
-
- if (ao->open)
- ao_command(ao, AO_COMMAND_CLOSE);
- else if (ao->fail_timer != NULL) {
- g_timer_destroy(ao->fail_timer);
- ao->fail_timer = NULL;
- }
-}
-
-bool
-audio_output_update(struct audio_output *ao,
- const struct audio_format *audio_format,
- const struct music_pipe *mp)
-{
- assert(mp != NULL);
-
- g_mutex_lock(ao->mutex);
-
- if (ao->enabled && ao->really_enabled) {
- if (ao->fail_timer == NULL ||
- g_timer_elapsed(ao->fail_timer, NULL) > REOPEN_AFTER) {
- bool success = audio_output_open(ao, audio_format, mp);
- g_mutex_unlock(ao->mutex);
- return success;
- }
- } else if (audio_output_is_open(ao))
- audio_output_close_locked(ao);
-
- g_mutex_unlock(ao->mutex);
- return false;
-}
-
-void
-audio_output_play(struct audio_output *ao)
-{
- g_mutex_lock(ao->mutex);
-
- assert(ao->allow_play);
-
- if (audio_output_is_open(ao))
- g_cond_signal(ao->cond);
-
- g_mutex_unlock(ao->mutex);
-}
-
-void audio_output_pause(struct audio_output *ao)
-{
- if (ao->mixer != NULL && ao->plugin->pause == NULL)
- /* the device has no pause mode: close the mixer,
- unless its "global" flag is set (checked by
- mixer_auto_close()) */
- mixer_auto_close(ao->mixer);
-
- g_mutex_lock(ao->mutex);
- assert(ao->allow_play);
- if (audio_output_is_open(ao))
- ao_command_async(ao, AO_COMMAND_PAUSE);
- g_mutex_unlock(ao->mutex);
-}
-
-void
-audio_output_drain_async(struct audio_output *ao)
-{
- g_mutex_lock(ao->mutex);
- assert(ao->allow_play);
- if (audio_output_is_open(ao))
- ao_command_async(ao, AO_COMMAND_DRAIN);
- g_mutex_unlock(ao->mutex);
-}
-
-void audio_output_cancel(struct audio_output *ao)
-{
- g_mutex_lock(ao->mutex);
-
- if (audio_output_is_open(ao)) {
- ao->allow_play = false;
- ao_command_async(ao, AO_COMMAND_CANCEL);
- }
-
- g_mutex_unlock(ao->mutex);
-}
-
-void
-audio_output_allow_play(struct audio_output *ao)
-{
- g_mutex_lock(ao->mutex);
-
- ao->allow_play = true;
- if (audio_output_is_open(ao))
- g_cond_signal(ao->cond);
-
- g_mutex_unlock(ao->mutex);
-}
-
-void
-audio_output_release(struct audio_output *ao)
-{
- if (ao->always_on)
- audio_output_pause(ao);
- else
- audio_output_close(ao);
-}
-
-void audio_output_close(struct audio_output *ao)
-{
- assert(ao != NULL);
- assert(!ao->open || ao->fail_timer == NULL);
-
- g_mutex_lock(ao->mutex);
- audio_output_close_locked(ao);
- g_mutex_unlock(ao->mutex);
-}
-
-void audio_output_finish(struct audio_output *ao)
-{
- audio_output_close(ao);
-
- assert(ao->fail_timer == NULL);
-
- if (ao->thread != NULL) {
- assert(ao->allow_play);
- ao_lock_command(ao, AO_COMMAND_KILL);
- g_thread_join(ao->thread);
- ao->thread = NULL;
- }
-
- audio_output_free(ao);
-}
diff --git a/src/output_control.h b/src/output_control.h
deleted file mode 100644
index 874a53518..000000000
--- a/src/output_control.h
+++ /dev/null
@@ -1,94 +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.
- */
-
-#ifndef MPD_OUTPUT_CONTROL_H
-#define MPD_OUTPUT_CONTROL_H
-
-#include <glib.h>
-
-#include <stddef.h>
-#include <stdbool.h>
-
-struct audio_output;
-struct audio_format;
-struct config_param;
-struct music_pipe;
-struct player_control;
-
-static inline GQuark
-audio_output_quark(void)
-{
- return g_quark_from_static_string("audio_output");
-}
-
-/**
- * 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
- */
-bool
-audio_output_update(struct audio_output *ao,
- const struct audio_format *audio_format,
- const struct music_pipe *mp);
-
-void
-audio_output_play(struct audio_output *ao);
-
-void audio_output_pause(struct audio_output *ao);
-
-void
-audio_output_drain_async(struct audio_output *ao);
-
-/**
- * Clear the "allow_play" flag and send the "CANCEL" command
- * asynchronously. To finish the operation, the caller has to call
- * audio_output_allow_play().
- */
-void audio_output_cancel(struct audio_output *ao);
-
-/**
- * Set the "allow_play" and signal the thread.
- */
-void
-audio_output_allow_play(struct audio_output *ao);
-
-void audio_output_close(struct audio_output *ao);
-
-/**
- * Closes the audio output, but if the "always_on" flag is set, put it
- * into pause mode instead.
- */
-void
-audio_output_release(struct audio_output *ao);
-
-void audio_output_finish(struct audio_output *ao);
-
-#endif
diff --git a/src/output_finish.c b/src/output_finish.c
deleted file mode 100644
index e11b43675..000000000
--- a/src/output_finish.c
+++ /dev/null
@@ -1,60 +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_internal.h"
-#include "output_plugin.h"
-#include "mixer_control.h"
-#include "filter_plugin.h"
-
-#include <assert.h>
-
-void
-ao_base_finish(struct audio_output *ao)
-{
- assert(!ao->open);
- assert(ao->fail_timer == NULL);
- assert(ao->thread == NULL);
-
- if (ao->mixer != NULL)
- mixer_free(ao->mixer);
-
- g_cond_free(ao->cond);
- g_mutex_free(ao->mutex);
-
- if (ao->replay_gain_filter != NULL)
- filter_free(ao->replay_gain_filter);
-
- if (ao->other_replay_gain_filter != NULL)
- filter_free(ao->other_replay_gain_filter);
-
- filter_free(ao->filter);
-
- pcm_buffer_deinit(&ao->cross_fade_buffer);
-}
-
-void
-audio_output_free(struct audio_output *ao)
-{
- assert(!ao->open);
- assert(ao->fail_timer == NULL);
- assert(ao->thread == NULL);
-
- ao_plugin_finish(ao);
-}
diff --git a/src/output_init.c b/src/output_init.c
deleted file mode 100644
index c3b808e94..000000000
--- a/src/output_init.c
+++ /dev/null
@@ -1,332 +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_control.h"
-#include "output_api.h"
-#include "output_internal.h"
-#include "output_list.h"
-#include "audio_parser.h"
-#include "mixer_control.h"
-#include "mixer_type.h"
-#include "mixer_list.h"
-#include "mixer/software_mixer_plugin.h"
-#include "filter_plugin.h"
-#include "filter_registry.h"
-#include "filter_config.h"
-#include "filter/chain_filter_plugin.h"
-#include "filter/autoconvert_filter_plugin.h"
-#include "filter/replay_gain_filter_plugin.h"
-
-#include <glib.h>
-
-#include <assert.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "output"
-
-#define AUDIO_OUTPUT_TYPE "type"
-#define AUDIO_OUTPUT_NAME "name"
-#define AUDIO_OUTPUT_FORMAT "format"
-#define AUDIO_FILTERS "filters"
-
-static const struct audio_output_plugin *
-audio_output_detect(GError **error)
-{
- g_warning("Attempt to detect audio output device");
-
- audio_output_plugins_for_each(plugin) {
- if (plugin->test_default_device == NULL)
- continue;
-
- g_warning("Attempting to detect a %s audio device",
- plugin->name);
- if (ao_plugin_test_default_device(plugin))
- return plugin;
- }
-
- g_set_error(error, audio_output_quark(), 0,
- "Unable to detect an audio device");
- return NULL;
-}
-
-/**
- * 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.
- */
-static enum mixer_type
-audio_output_mixer_type(const struct config_param *param)
-{
- /* read the local "mixer_type" setting */
- const char *p = config_get_block_string(param, "mixer_type", NULL);
- if (p != NULL)
- return mixer_type_parse(p);
-
- /* try the local "mixer_enabled" setting next (deprecated) */
- if (!config_get_block_bool(param, "mixer_enabled", true))
- return MIXER_TYPE_NONE;
-
- /* fall back to the global "mixer_type" setting (also
- deprecated) */
- return mixer_type_parse(config_get_string("mixer_type", "hardware"));
-}
-
-static struct mixer *
-audio_output_load_mixer(struct audio_output *ao,
- const struct config_param *param,
- const struct mixer_plugin *plugin,
- struct filter *filter_chain,
- GError **error_r)
-{
- struct mixer *mixer;
-
- switch (audio_output_mixer_type(param)) {
- case MIXER_TYPE_NONE:
- case MIXER_TYPE_UNKNOWN:
- return NULL;
-
- case MIXER_TYPE_HARDWARE:
- if (plugin == NULL)
- return NULL;
-
- return mixer_new(plugin, ao, param, error_r);
-
- case MIXER_TYPE_SOFTWARE:
- mixer = mixer_new(&software_mixer_plugin, NULL, NULL, NULL);
- assert(mixer != NULL);
-
- filter_chain_append(filter_chain,
- software_mixer_get_filter(mixer));
- return mixer;
- }
-
- assert(false);
- return NULL;
-}
-
-bool
-ao_base_init(struct audio_output *ao,
- const struct audio_output_plugin *plugin,
- const struct config_param *param, GError **error_r)
-{
- assert(ao != NULL);
- assert(plugin != NULL);
- assert(plugin->finish != NULL);
- assert(plugin->open != NULL);
- assert(plugin->close != NULL);
- assert(plugin->play != NULL);
-
- GError *error = NULL;
-
- if (param) {
- const char *p;
-
- ao->name = config_get_block_string(param, AUDIO_OUTPUT_NAME,
- NULL);
- if (ao->name == NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
- "Missing \"name\" configuration");
- return false;
- }
-
- p = config_get_block_string(param, AUDIO_OUTPUT_FORMAT,
- NULL);
- if (p != NULL) {
- bool success =
- audio_format_parse(&ao->config_audio_format,
- p, true, error_r);
- if (!success)
- return false;
- } else
- audio_format_clear(&ao->config_audio_format);
- } else {
- ao->name = "default detected output";
-
- audio_format_clear(&ao->config_audio_format);
- }
-
- ao->plugin = plugin;
- ao->always_on = config_get_block_bool(param, "always_on", false);
- ao->enabled = config_get_block_bool(param, "enabled", true);
- ao->really_enabled = false;
- ao->open = false;
- ao->pause = false;
- ao->allow_play = true;
- ao->fail_timer = NULL;
-
- pcm_buffer_init(&ao->cross_fade_buffer);
-
- /* set up the filter chain */
-
- ao->filter = filter_chain_new();
- assert(ao->filter != NULL);
-
- /* create the normalization filter (if configured) */
-
- if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) {
- struct filter *normalize_filter =
- filter_new(&normalize_filter_plugin, NULL, NULL);
- assert(normalize_filter != NULL);
-
- filter_chain_append(ao->filter,
- autoconvert_filter_new(normalize_filter));
- }
-
- filter_chain_parse(ao->filter,
- config_get_block_string(param, AUDIO_FILTERS, ""),
- &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 (error != NULL) {
- g_warning("Failed to initialize filter chain for '%s': %s",
- ao->name, error->message);
- g_error_free(error);
- }
-
- ao->thread = NULL;
- ao->command = AO_COMMAND_NONE;
- ao->mutex = g_mutex_new();
- ao->cond = g_cond_new();
-
- ao->mixer = NULL;
- ao->replay_gain_filter = NULL;
- ao->other_replay_gain_filter = NULL;
-
- /* done */
-
- return true;
-}
-
-static bool
-audio_output_setup(struct audio_output *ao, const struct config_param *param,
- GError **error_r)
-{
-
- /* create the replay_gain filter */
-
- const char *replay_gain_handler =
- config_get_block_string(param, "replay_gain_handler",
- "software");
-
- if (strcmp(replay_gain_handler, "none") != 0) {
- ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin,
- param, NULL);
- assert(ao->replay_gain_filter != NULL);
-
- ao->replay_gain_serial = 0;
-
- ao->other_replay_gain_filter = filter_new(&replay_gain_filter_plugin,
- param, NULL);
- assert(ao->other_replay_gain_filter != NULL);
-
- ao->other_replay_gain_serial = 0;
- } else {
- ao->replay_gain_filter = NULL;
- ao->other_replay_gain_filter = NULL;
- }
-
- /* set up the mixer */
-
- GError *error = NULL;
- ao->mixer = audio_output_load_mixer(ao, param,
- ao->plugin->mixer_plugin,
- ao->filter, &error);
- if (ao->mixer == NULL && error != NULL) {
- g_warning("Failed to initialize hardware mixer for '%s': %s",
- ao->name, error->message);
- g_error_free(error);
- }
-
- /* use the hardware mixer for replay gain? */
-
- if (strcmp(replay_gain_handler, "mixer") == 0) {
- if (ao->mixer != NULL)
- replay_gain_filter_set_mixer(ao->replay_gain_filter,
- ao->mixer, 100);
- else
- g_warning("No such mixer for output '%s'", ao->name);
- } else if (strcmp(replay_gain_handler, "software") != 0 &&
- ao->replay_gain_filter != NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
- "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, NULL, NULL);
- assert(ao->convert_filter != NULL);
-
- filter_chain_append(ao->filter, ao->convert_filter);
-
- return true;
-}
-
-struct audio_output *
-audio_output_new(const struct config_param *param,
- struct player_control *pc,
- GError **error_r)
-{
- const struct audio_output_plugin *plugin;
-
- if (param) {
- const char *p;
-
- p = config_get_block_string(param, AUDIO_OUTPUT_TYPE, NULL);
- if (p == NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
- "Missing \"type\" configuration");
- return false;
- }
-
- plugin = audio_output_plugin_get(p);
- if (plugin == NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
- "No such audio output plugin: %s", p);
- return false;
- }
- } else {
- g_warning("No \"%s\" defined in config file\n",
- CONF_AUDIO_OUTPUT);
-
- plugin = audio_output_detect(error_r);
- if (plugin == NULL)
- return false;
-
- g_message("Successfully detected a %s audio device",
- plugin->name);
- }
-
- struct audio_output *ao = ao_plugin_init(plugin, param, error_r);
- if (ao == NULL)
- return NULL;
-
- if (!audio_output_setup(ao, param, error_r)) {
- ao_plugin_finish(ao);
- return NULL;
- }
-
- ao->player_control = pc;
- return ao;
-}
diff --git a/src/output_internal.h b/src/output_internal.h
index 9d975d789..201962a72 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -27,6 +27,12 @@
#include <time.h>
+#ifdef __cplusplus
+class Filter;
+#else
+typedef void *Filter;
+#endif
+
struct config_param;
enum audio_output_command {
@@ -73,6 +79,13 @@ struct audio_output {
struct 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?
*/
@@ -149,13 +162,13 @@ struct audio_output {
* The filter object of this audio output. This is an
* instance of chain_filter_plugin.
*/
- struct filter *filter;
+ Filter *filter;
/**
* The replay_gain_filter_plugin instance of this audio
* output.
*/
- struct filter *replay_gain_filter;
+ Filter *replay_gain_filter;
/**
* The serial number of the last replay gain info. 0 means no
@@ -168,7 +181,7 @@ struct audio_output {
* output, to be applied to the second chunk during
* cross-fading.
*/
- struct filter *other_replay_gain_filter;
+ Filter *other_replay_gain_filter;
/**
* The serial number of the last replay gain info by the
@@ -182,7 +195,7 @@ struct audio_output {
* for converting the input data into the appropriate format
* for this audio output.
*/
- struct filter *convert_filter;
+ Filter *convert_filter;
/**
* The thread handle, or NULL if the output thread isn't
@@ -250,6 +263,10 @@ audio_output_command_is_finished(const struct audio_output *ao)
return ao->command == AO_COMMAND_NONE;
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct audio_output *
audio_output_new(const struct config_param *param,
struct player_control *pc,
@@ -266,4 +283,8 @@ ao_base_finish(struct audio_output *ao);
void
audio_output_free(struct audio_output *ao);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/output_list.c b/src/output_list.c
deleted file mode 100644
index 835c02bba..000000000
--- a/src/output_list.c
+++ /dev/null
@@ -1,106 +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_list.h"
-#include "output_api.h"
-#include "output/alsa_output_plugin.h"
-#include "output/ao_output_plugin.h"
-#include "output/ffado_output_plugin.h"
-#include "output/fifo_output_plugin.h"
-#include "output/httpd_output_plugin.h"
-#include "output/jack_output_plugin.h"
-#include "output/mvp_output_plugin.h"
-#include "output/null_output_plugin.h"
-#include "output/openal_output_plugin.h"
-#include "output/oss_output_plugin.h"
-#include "output/osx_output_plugin.h"
-#include "output/pipe_output_plugin.h"
-#include "output/pulse_output_plugin.h"
-#include "output/recorder_output_plugin.h"
-#include "output/roar_output_plugin.h"
-#include "output/shout_output_plugin.h"
-#include "output/solaris_output_plugin.h"
-#include "output/winmm_output_plugin.h"
-
-const struct audio_output_plugin *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_MVP
- &mvp_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
-#ifdef ENABLE_FFADO_OUTPUT
- &ffado_output_plugin,
-#endif
- NULL
-};
-
-const struct audio_output_plugin *
-audio_output_plugin_get(const char *name)
-{
- audio_output_plugins_for_each(plugin)
- if (strcmp(plugin->name, name) == 0)
- return plugin;
-
- return NULL;
-}
diff --git a/src/output_list.h b/src/output_list.h
deleted file mode 100644
index 185ada716..000000000
--- a/src/output_list.h
+++ /dev/null
@@ -1,33 +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.
- */
-
-#ifndef MPD_OUTPUT_LIST_H
-#define MPD_OUTPUT_LIST_H
-
-extern const struct audio_output_plugin *const audio_output_plugins[];
-
-const struct audio_output_plugin *
-audio_output_plugin_get(const char *name);
-
-#define audio_output_plugins_for_each(plugin) \
- for (const struct audio_output_plugin *plugin, \
- *const*output_plugin_iterator = &audio_output_plugins[0]; \
- (plugin = *output_plugin_iterator) != NULL; ++output_plugin_iterator)
-
-#endif
diff --git a/src/output_plugin.c b/src/output_plugin.c
deleted file mode 100644
index 221570c1c..000000000
--- a/src/output_plugin.c
+++ /dev/null
@@ -1,109 +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_plugin.h"
-#include "output_internal.h"
-
-struct audio_output *
-ao_plugin_init(const struct audio_output_plugin *plugin,
- const struct config_param *param,
- GError **error)
-{
- assert(plugin != NULL);
- assert(plugin->init != NULL);
-
- return plugin->init(param, error);
-}
-
-void
-ao_plugin_finish(struct audio_output *ao)
-{
- ao->plugin->finish(ao);
-}
-
-bool
-ao_plugin_enable(struct audio_output *ao, GError **error_r)
-{
- return ao->plugin->enable != NULL
- ? ao->plugin->enable(ao, error_r)
- : true;
-}
-
-void
-ao_plugin_disable(struct audio_output *ao)
-{
- if (ao->plugin->disable != NULL)
- ao->plugin->disable(ao);
-}
-
-bool
-ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format,
- GError **error)
-{
- return ao->plugin->open(ao, audio_format, error);
-}
-
-void
-ao_plugin_close(struct audio_output *ao)
-{
- ao->plugin->close(ao);
-}
-
-unsigned
-ao_plugin_delay(struct audio_output *ao)
-{
- return ao->plugin->delay != NULL
- ? ao->plugin->delay(ao)
- : 0;
-}
-
-void
-ao_plugin_send_tag(struct audio_output *ao, const struct tag *tag)
-{
- if (ao->plugin->send_tag != NULL)
- ao->plugin->send_tag(ao, tag);
-}
-
-size_t
-ao_plugin_play(struct audio_output *ao, const void *chunk, size_t size,
- GError **error)
-{
- return ao->plugin->play(ao, chunk, size, error);
-}
-
-void
-ao_plugin_drain(struct audio_output *ao)
-{
- if (ao->plugin->drain != NULL)
- ao->plugin->drain(ao);
-}
-
-void
-ao_plugin_cancel(struct audio_output *ao)
-{
- if (ao->plugin->cancel != NULL)
- ao->plugin->cancel(ao);
-}
-
-bool
-ao_plugin_pause(struct audio_output *ao)
-{
- return ao->plugin->pause != NULL && ao->plugin->pause(ao);
-}
diff --git a/src/output_plugin.h b/src/output_plugin.h
index 209ca6221..2b71ba6a6 100644
--- a/src/output_plugin.h
+++ b/src/output_plugin.h
@@ -20,7 +20,8 @@
#ifndef MPD_OUTPUT_PLUGIN_H
#define MPD_OUTPUT_PLUGIN_H
-#include <glib.h>
+#include "gcc.h"
+#include "gerror.h"
#include <stdbool.h>
#include <stddef.h>
@@ -165,7 +166,11 @@ ao_plugin_test_default_device(const struct audio_output_plugin *plugin)
: false;
}
-G_GNUC_MALLOC
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gcc_malloc
struct audio_output *
ao_plugin_init(const struct audio_output_plugin *plugin,
const struct config_param *param,
@@ -187,7 +192,7 @@ ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format,
void
ao_plugin_close(struct audio_output *ao);
-G_GNUC_PURE
+gcc_pure
unsigned
ao_plugin_delay(struct audio_output *ao);
@@ -207,4 +212,8 @@ ao_plugin_cancel(struct audio_output *ao);
bool
ao_plugin_pause(struct audio_output *ao);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/output_print.c b/src/output_print.c
deleted file mode 100644
index 483648ca2..000000000
--- a/src/output_print.c
+++ /dev/null
@@ -1,45 +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.
- */
-
-/*
- * Protocol specific code for the audio output library.
- *
- */
-
-#include "config.h"
-#include "output_print.h"
-#include "output_internal.h"
-#include "output_all.h"
-#include "client.h"
-
-void
-printAudioDevices(struct client *client)
-{
- unsigned n = audio_output_count();
-
- for (unsigned i = 0; i < n; ++i) {
- const struct audio_output *ao = audio_output_get(i);
-
- client_printf(client,
- "outputid: %i\n"
- "outputname: %s\n"
- "outputenabled: %i\n",
- i, ao->name, ao->enabled);
- }
-}
diff --git a/src/output_print.h b/src/output_print.h
deleted file mode 100644
index e02f4e9f5..000000000
--- a/src/output_print.h
+++ /dev/null
@@ -1,33 +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.
- */
-
-/*
- * Protocol specific code for the audio output library.
- *
- */
-
-#ifndef OUTPUT_PRINT_H
-#define OUTPUT_PRINT_H
-
-struct client;
-
-void
-printAudioDevices(struct client *client);
-
-#endif
diff --git a/src/output_state.c b/src/output_state.c
deleted file mode 100644
index 7bcafb36b..000000000
--- a/src/output_state.c
+++ /dev/null
@@ -1,91 +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.
- */
-
-/*
- * Saving and loading the audio output states to/from the state file.
- *
- */
-
-#include "config.h"
-#include "output_state.h"
-#include "output_internal.h"
-#include "output_all.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define AUDIO_DEVICE_STATE "audio_device_state:"
-
-unsigned audio_output_state_version;
-
-void
-audio_output_state_save(FILE *fp)
-{
- unsigned n = audio_output_count();
-
- assert(n > 0);
-
- for (unsigned i = 0; i < n; ++i) {
- const struct audio_output *ao = audio_output_get(i);
-
- fprintf(fp, AUDIO_DEVICE_STATE "%d:%s\n",
- ao->enabled, ao->name);
- }
-}
-
-bool
-audio_output_state_read(const char *line)
-{
- long value;
- char *endptr;
- const char *name;
- struct audio_output *ao;
-
- if (!g_str_has_prefix(line, AUDIO_DEVICE_STATE))
- return false;
-
- line += sizeof(AUDIO_DEVICE_STATE) - 1;
-
- value = strtol(line, &endptr, 10);
- if (*endptr != ':' || (value != 0 && value != 1))
- return false;
-
- if (value != 0)
- /* state is "enabled": no-op */
- return true;
-
- name = endptr + 1;
- ao = audio_output_find(name);
- if (ao == NULL) {
- g_debug("Ignoring device state for '%s'", name);
- return true;
- }
-
- ao->enabled = false;
- return true;
-}
-
-unsigned
-audio_output_state_get_version(void)
-{
- return audio_output_state_version;
-}
diff --git a/src/output_state.h b/src/output_state.h
deleted file mode 100644
index 320a3520a..000000000
--- a/src/output_state.h
+++ /dev/null
@@ -1,45 +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.
- */
-
-/*
- * Saving and loading the audio output states to/from the state file.
- *
- */
-
-#ifndef OUTPUT_STATE_H
-#define OUTPUT_STATE_H
-
-#include <stdbool.h>
-#include <stdio.h>
-
-bool
-audio_output_state_read(const char *line);
-
-void
-audio_output_state_save(FILE *fp);
-
-/**
- * Generates a version number for the current state of the audio
- * outputs. This is used by timer_save_state_file() to determine
- * whether the state has changed and the state file should be saved.
- */
-unsigned
-audio_output_state_get_version(void);
-
-#endif
diff --git a/src/output_thread.c b/src/output_thread.c
deleted file mode 100644
index 4eef2ccdd..000000000
--- a/src/output_thread.c
+++ /dev/null
@@ -1,685 +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_thread.h"
-#include "output_api.h"
-#include "output_internal.h"
-#include "chunk.h"
-#include "pipe.h"
-#include "player_control.h"
-#include "pcm_mix.h"
-#include "filter_plugin.h"
-#include "filter/convert_filter_plugin.h"
-#include "filter/replay_gain_filter_plugin.h"
-#include "mpd_error.h"
-#include "notify.h"
-#include "gcc.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "output"
-
-static void ao_command_finished(struct audio_output *ao)
-{
- assert(ao->command != AO_COMMAND_NONE);
- ao->command = AO_COMMAND_NONE;
-
- g_mutex_unlock(ao->mutex);
- notify_signal(&audio_output_client_notify);
- g_mutex_lock(ao->mutex);
-}
-
-static bool
-ao_enable(struct audio_output *ao)
-{
- GError *error = NULL;
- bool success;
-
- if (ao->really_enabled)
- return true;
-
- g_mutex_unlock(ao->mutex);
- success = ao_plugin_enable(ao, &error);
- g_mutex_lock(ao->mutex);
- if (!success) {
- 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, bool drain);
-
-static void
-ao_disable(struct audio_output *ao)
-{
- if (ao->open)
- ao_close(ao, false);
-
- if (ao->really_enabled) {
- ao->really_enabled = false;
-
- g_mutex_unlock(ao->mutex);
- ao_plugin_disable(ao);
- g_mutex_lock(ao->mutex);
- }
-}
-
-static const struct audio_format *
-ao_filter_open(struct audio_output *ao,
- struct audio_format *audio_format,
- GError **error_r)
-{
- assert(audio_format_valid(audio_format));
-
- /* the replay_gain filter cannot fail here */
- if (ao->replay_gain_filter != NULL)
- filter_open(ao->replay_gain_filter, audio_format, error_r);
- if (ao->other_replay_gain_filter != NULL)
- filter_open(ao->other_replay_gain_filter, audio_format,
- error_r);
-
- const struct audio_format *af
- = filter_open(ao->filter, audio_format, error_r);
- if (af == NULL) {
- if (ao->replay_gain_filter != NULL)
- filter_close(ao->replay_gain_filter);
- if (ao->other_replay_gain_filter != NULL)
- filter_close(ao->other_replay_gain_filter);
- }
-
- return af;
-}
-
-static void
-ao_filter_close(struct audio_output *ao)
-{
- if (ao->replay_gain_filter != NULL)
- filter_close(ao->replay_gain_filter);
- if (ao->other_replay_gain_filter != NULL)
- filter_close(ao->other_replay_gain_filter);
-
- filter_close(ao->filter);
-}
-
-static void
-ao_open(struct audio_output *ao)
-{
- bool success;
- GError *error = NULL;
- const struct audio_format *filter_audio_format;
- struct audio_format_string af_string;
-
- assert(!ao->open);
- assert(ao->pipe != NULL);
- assert(ao->chunk == NULL);
- assert(audio_format_valid(&ao->in_audio_format));
-
- if (ao->fail_timer != NULL) {
- /* this can only happen when this
- output thread fails while
- audio_output_open() is run in the
- player thread */
- g_timer_destroy(ao->fail_timer);
- ao->fail_timer = 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 = ao_filter_open(ao, &ao->in_audio_format, &error);
- if (filter_audio_format == NULL) {
- g_warning("Failed to open filter for \"%s\" [%s]: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
-
- ao->fail_timer = g_timer_new();
- return;
- }
-
- assert(audio_format_valid(filter_audio_format));
-
- ao->out_audio_format = *filter_audio_format;
- audio_format_mask_apply(&ao->out_audio_format,
- &ao->config_audio_format);
-
- g_mutex_unlock(ao->mutex);
- success = ao_plugin_open(ao, &ao->out_audio_format, &error);
- g_mutex_lock(ao->mutex);
-
- assert(!ao->open);
-
- if (!success) {
- g_warning("Failed to open \"%s\" [%s]: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
-
- ao_filter_close(ao);
- ao->fail_timer = g_timer_new();
- return;
- }
-
- convert_filter_set(ao->convert_filter, &ao->out_audio_format);
-
- ao->open = true;
-
- g_debug("opened plugin=%s name=\"%s\" "
- "audio_format=%s",
- ao->plugin->name, ao->name,
- audio_format_to_string(&ao->out_audio_format, &af_string));
-
- if (!audio_format_equals(&ao->in_audio_format,
- &ao->out_audio_format))
- g_debug("converting from %s",
- audio_format_to_string(&ao->in_audio_format,
- &af_string));
-}
-
-static void
-ao_close(struct audio_output *ao, bool drain)
-{
- assert(ao->open);
-
- ao->pipe = NULL;
-
- ao->chunk = NULL;
- ao->open = false;
-
- g_mutex_unlock(ao->mutex);
-
- if (drain)
- ao_plugin_drain(ao);
- else
- ao_plugin_cancel(ao);
-
- ao_plugin_close(ao);
- ao_filter_close(ao);
-
- g_mutex_lock(ao->mutex);
-
- g_debug("closed plugin=%s name=\"%s\"", ao->plugin->name, ao->name);
-}
-
-static void
-ao_reopen_filter(struct audio_output *ao)
-{
- const struct audio_format *filter_audio_format;
- GError *error = NULL;
-
- ao_filter_close(ao);
- filter_audio_format = ao_filter_open(ao, &ao->in_audio_format, &error);
- if (filter_audio_format == NULL) {
- g_warning("Failed to open filter for \"%s\" [%s]: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
-
- /* this is a little code duplication fro ao_close(),
- but we cannot call this function because we must
- not call filter_close(ao->filter) again */
-
- ao->pipe = NULL;
-
- ao->chunk = NULL;
- ao->open = false;
- ao->fail_timer = g_timer_new();
-
- g_mutex_unlock(ao->mutex);
- ao_plugin_close(ao);
- g_mutex_lock(ao->mutex);
-
- return;
- }
-
- convert_filter_set(ao->convert_filter, &ao->out_audio_format);
-}
-
-static void
-ao_reopen(struct audio_output *ao)
-{
- if (!audio_format_fully_defined(&ao->config_audio_format)) {
- if (ao->open) {
- const struct music_pipe *mp = ao->pipe;
- ao_close(ao, true);
- ao->pipe = mp;
- }
-
- /* no audio format is configured: copy in->out, let
- the output's open() method determine the effective
- out_audio_format */
- ao->out_audio_format = ao->in_audio_format;
- audio_format_mask_apply(&ao->out_audio_format,
- &ao->config_audio_format);
- }
-
- if (ao->open)
- /* the audio format has changed, and all filters have
- to be reconfigured */
- ao_reopen_filter(ao);
- else
- ao_open(ao);
-}
-
-/**
- * Wait until the output's delay reaches zero.
- *
- * @return true if playback should be continued, false if a command
- * was issued
- */
-static bool
-ao_wait(struct audio_output *ao)
-{
- while (true) {
- unsigned delay = ao_plugin_delay(ao);
- if (delay == 0)
- return true;
-
- GTimeVal tv;
- g_get_current_time(&tv);
- g_time_val_add(&tv, delay * 1000);
- (void)g_cond_timed_wait(ao->cond, ao->mutex, &tv);
-
- if (ao->command != AO_COMMAND_NONE)
- return false;
- }
-}
-
-static const char *
-ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
- struct filter *replay_gain_filter,
- unsigned *replay_gain_serial_p,
- size_t *length_r)
-{
- assert(chunk != NULL);
- assert(!music_chunk_is_empty(chunk));
- assert(music_chunk_check_format(chunk, &ao->in_audio_format));
-
- const char *data = chunk->data;
- size_t length = chunk->length;
-
- (void)ao;
-
- assert(length % audio_format_frame_size(&ao->in_audio_format) == 0);
-
- if (length > 0 && replay_gain_filter != NULL) {
- if (chunk->replay_gain_serial != *replay_gain_serial_p) {
- replay_gain_filter_set_info(replay_gain_filter,
- chunk->replay_gain_serial != 0
- ? &chunk->replay_gain_info
- : NULL);
- *replay_gain_serial_p = chunk->replay_gain_serial;
- }
-
- GError *error = NULL;
- data = filter_filter(replay_gain_filter, data, length,
- &length, &error);
- if (data == NULL) {
- g_warning("\"%s\" [%s] failed to filter: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
- return NULL;
- }
- }
-
- *length_r = length;
- return data;
-}
-
-static const char *
-ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
- size_t *length_r)
-{
- GError *error = NULL;
-
- size_t length;
- const char *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
- &ao->replay_gain_serial, &length);
- if (data == NULL)
- return NULL;
-
- if (length == 0) {
- /* empty chunk, nothing to do */
- *length_r = 0;
- return data;
- }
-
- /* cross-fade */
-
- if (chunk->other != NULL) {
- size_t other_length;
- const char *other_data =
- ao_chunk_data(ao, chunk->other,
- ao->other_replay_gain_filter,
- &ao->other_replay_gain_serial,
- &other_length);
- if (other_data == NULL)
- return NULL;
-
- if (other_length == 0) {
- *length_r = 0;
- return data;
- }
-
- /* if the "other" chunk is longer, then that trailer
- is used as-is, without mixing; it is part of the
- "next" song being faded in, and if there's a rest,
- it means cross-fading ends here */
-
- if (length > other_length)
- length = other_length;
-
- char *dest = pcm_buffer_get(&ao->cross_fade_buffer,
- other_length);
- memcpy(dest, other_data, other_length);
- if (!pcm_mix(dest, data, length, ao->in_audio_format.format,
- 1.0 - chunk->mix_ratio)) {
- g_warning("Cannot cross-fade format %s",
- sample_format_to_string(ao->in_audio_format.format));
- return NULL;
- }
-
- data = dest;
- length = other_length;
- }
-
- /* apply filter chain */
-
- data = filter_filter(ao->filter, data, length, &length, &error);
- if (data == NULL) {
- g_warning("\"%s\" [%s] failed to filter: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
- return NULL;
- }
-
- *length_r = length;
- return data;
-}
-
-static bool
-ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
-{
- GError *error = NULL;
-
- assert(ao != NULL);
- assert(ao->filter != NULL);
-
- if (chunk->tag != NULL) {
- g_mutex_unlock(ao->mutex);
- ao_plugin_send_tag(ao, chunk->tag);
- g_mutex_lock(ao->mutex);
- }
-
- size_t size;
-#if GCC_CHECK_VERSION(4,7)
- /* workaround -Wmaybe-uninitialized false positive */
- size = 0;
-#endif
- const char *data = ao_filter_chunk(ao, chunk, &size);
- if (data == NULL) {
- ao_close(ao, false);
-
- /* don't automatically reopen this device for 10
- seconds */
- ao->fail_timer = g_timer_new();
- return false;
- }
-
- while (size > 0 && ao->command == AO_COMMAND_NONE) {
- size_t nbytes;
-
- if (!ao_wait(ao))
- break;
-
- g_mutex_unlock(ao->mutex);
- nbytes = ao_plugin_play(ao, data, size, &error);
- g_mutex_lock(ao->mutex);
- if (nbytes == 0) {
- /* play()==0 means failure */
- g_warning("\"%s\" [%s] failed to play: %s",
- ao->name, ao->plugin->name, error->message);
- g_error_free(error);
-
- ao_close(ao, false);
-
- /* don't automatically reopen this device for
- 10 seconds */
- assert(ao->fail_timer == NULL);
- ao->fail_timer = g_timer_new();
-
- return false;
- }
-
- assert(nbytes <= size);
- assert(nbytes % audio_format_frame_size(&ao->out_audio_format) == 0);
-
- data += nbytes;
- size -= nbytes;
- }
-
- return true;
-}
-
-static const struct music_chunk *
-ao_next_chunk(struct audio_output *ao)
-{
- return ao->chunk != NULL
- /* continue the previous play() call */
- ? ao->chunk->next
- /* get the first chunk from the pipe */
- : music_pipe_peek(ao->pipe);
-}
-
-/**
- * Plays all remaining chunks, until the tail of the pipe has been
- * reached (and no more chunks are queued), or until a command is
- * received.
- *
- * @return true if at least one chunk has been available, false if the
- * tail of the pipe was already reached
- */
-static bool
-ao_play(struct audio_output *ao)
-{
- bool success;
- const struct music_chunk *chunk;
-
- assert(ao->pipe != NULL);
-
- chunk = ao_next_chunk(ao);
- if (chunk == NULL)
- /* no chunk available */
- return false;
-
- ao->chunk_finished = false;
-
- while (chunk != NULL && ao->command == AO_COMMAND_NONE) {
- assert(!ao->chunk_finished);
-
- ao->chunk = chunk;
-
- success = ao_play_chunk(ao, chunk);
- if (!success) {
- assert(ao->chunk == NULL);
- break;
- }
-
- assert(ao->chunk == chunk);
- chunk = chunk->next;
- }
-
- ao->chunk_finished = true;
-
- g_mutex_unlock(ao->mutex);
- player_lock_signal(ao->player_control);
- g_mutex_lock(ao->mutex);
-
- return true;
-}
-
-static void ao_pause(struct audio_output *ao)
-{
- bool ret;
-
- g_mutex_unlock(ao->mutex);
- ao_plugin_cancel(ao);
- g_mutex_lock(ao->mutex);
-
- ao->pause = true;
- ao_command_finished(ao);
-
- do {
- if (!ao_wait(ao))
- break;
-
- g_mutex_unlock(ao->mutex);
- ret = ao_plugin_pause(ao);
- g_mutex_lock(ao->mutex);
-
- if (!ret) {
- ao_close(ao, false);
- break;
- }
- } while (ao->command == AO_COMMAND_NONE);
-
- ao->pause = false;
-}
-
-static gpointer audio_output_task(gpointer arg)
-{
- struct audio_output *ao = arg;
-
- g_mutex_lock(ao->mutex);
-
- while (1) {
- switch (ao->command) {
- 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);
- break;
-
- case AO_COMMAND_REOPEN:
- ao_reopen(ao);
- ao_command_finished(ao);
- break;
-
- case AO_COMMAND_CLOSE:
- assert(ao->open);
- assert(ao->pipe != NULL);
-
- ao_close(ao, false);
- ao_command_finished(ao);
- break;
-
- case AO_COMMAND_PAUSE:
- if (!ao->open) {
- /* the output has failed after
- audio_output_all_pause() has
- submitted the PAUSE command; bail
- out */
- ao_command_finished(ao);
- break;
- }
-
- ao_pause(ao);
- /* don't "break" here: this might cause
- ao_play() to be called when command==CLOSE
- ends the paused state - "continue" checks
- the new command first */
- continue;
-
- case AO_COMMAND_DRAIN:
- if (ao->open) {
- assert(ao->chunk == NULL);
- assert(music_pipe_peek(ao->pipe) == NULL);
-
- g_mutex_unlock(ao->mutex);
- ao_plugin_drain(ao);
- g_mutex_lock(ao->mutex);
- }
-
- ao_command_finished(ao);
- continue;
-
- case AO_COMMAND_CANCEL:
- ao->chunk = NULL;
-
- if (ao->open) {
- g_mutex_unlock(ao->mutex);
- ao_plugin_cancel(ao);
- g_mutex_lock(ao->mutex);
- }
-
- ao_command_finished(ao);
- continue;
-
- case AO_COMMAND_KILL:
- ao->chunk = NULL;
- ao_command_finished(ao);
- g_mutex_unlock(ao->mutex);
- return NULL;
- }
-
- if (ao->open && ao->allow_play && ao_play(ao))
- /* don't wait for an event if there are more
- chunks in the pipe */
- continue;
-
- if (ao->command == AO_COMMAND_NONE)
- g_cond_wait(ao->cond, ao->mutex);
- }
-}
-
-void audio_output_thread_start(struct audio_output *ao)
-{
- GError *e = NULL;
-
- assert(ao->command == AO_COMMAND_NONE);
-
- if (!(ao->thread = g_thread_create(audio_output_task, ao, true, &e)))
- MPD_ERROR("Failed to spawn output task: %s\n", e->message);
-}
diff --git a/src/output_thread.h b/src/output_thread.h
deleted file mode 100644
index 5ad9a7527..000000000
--- a/src/output_thread.h
+++ /dev/null
@@ -1,27 +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.
- */
-
-#ifndef MPD_OUTPUT_THREAD_H
-#define MPD_OUTPUT_THREAD_H
-
-struct audio_output;
-
-void audio_output_thread_start(struct audio_output *ao);
-
-#endif