aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac18
-rw-r--r--doc/mpd.conf.520
-rw-r--r--src/Makefile.am1
-rw-r--r--src/audio.c2
-rw-r--r--src/audioOutputs/audioOutput_pulse.c193
5 files changed, 226 insertions, 8 deletions
diff --git a/configure.ac b/configure.ac
index 7e84f7fcc..5c2b0cfd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,6 +39,7 @@ AC_ARG_ENABLE(ipv6,[ --disable-ipv6 disable IPv6 support (default: enable)]
AC_ARG_ENABLE(sun,[ --disable-sun disable sun support (default: enable)],[enable_sun=$enableval],[enable_sun=yes])
AC_ARG_ENABLE(oss,[ --disable-oss disable OSS support (default: enable)],[enable_oss=$enableval],[enable_oss=yes])
AC_ARG_ENABLE(alsa,[ --disable-alsa disable ALSA support (default: enable)],[enable_alsa=$enableval],[enable_alsa=yes])
+AC_ARG_ENABLE(pulse,[ --disable-pulse disable support for the PulseAudio sound server (default: enable)],[enable_pulse=$enableval],[enable_pulse=yes])
AC_ARG_ENABLE(mvp,[ --enable-mvp enable support for Hauppauge Media MVP (default: disable)],[enable_mvp=$enableval],[enable_mvp=no])
AC_ARG_ENABLE(oggvorbis,[ --disable-oggvorbis disable Ogg Vorbis support (default: enable)],[enable_oggvorbis=$enableval],enable_oggvorbis=yes)
AC_ARG_ENABLE(oggflac,[ --disable-oggflac disable OggFLAC support (default: enable)],[enable_oggflac=$enableval],enable_oggflac=yes)
@@ -147,6 +148,12 @@ if test x$enable_oss = xyes; then
AC_CHECK_HEADER(sys/soundcard.h,[enable_oss=yes;AC_DEFINE(HAVE_OSS,1,[Define to enable OSS])],[AC_MSG_WARN(Soundcard headers not found -- disabling OSS support);enable_oss=no])
fi
+if test x$enable_pulse = xyes; then
+ PKG_CHECK_MODULES([PULSE], [libpulse-simple],
+ [enable_pulse=yes;AC_DEFINE([HAVE_PULSE], 1, [Define to enable PulseAudio])] MPD_LIBS="$MPD_LIBS $PULSE_LIBS" MPD_CFLAGS="$MPD_CFLAGS $PULSE_CFLAGS",
+ [enable_pulse=no;AC_MSG_WARN([PulseAudio not found -- disabling])])
+fi
+
if test x$enable_mvp = xyes; then
AC_DEFINE(HAVE_MVP,1,[Define to enable Hauppauge Media MVP support])
fi
@@ -636,9 +643,9 @@ else
fi
if test x$enable_sun = xyes; then
- echo " Sun support ...... ............enabled"
+ echo " Sun support ...................enabled"
else
- echo " Sun support ...... ............disabled"
+ echo " Sun support ...................disabled"
fi
if test x$enable_osx = xyes; then
@@ -647,6 +654,12 @@ else
echo " OS X support ..................disabled"
fi
+if test x$enable_pulse = xyes; then
+ echo " PulseAudio support ............enabled"
+else
+ echo " PulseAudio support ............disabled"
+fi
+
if test x$enable_mvp = xyes; then
echo " Media MVP support .............enabled"
else
@@ -667,6 +680,7 @@ if test x$enable_ao = xno &&
test x$enable_sun = xno &&
test x$enable_alsa = xno &&
test x$enable_osx = xno &&
+ test x$enable_pulse = xno &&
test x$enable_mvp = xno; then
AC_MSG_ERROR("No Audio Output types configured!")
fi
diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5
index 6a5336e92..06f675131 100644
--- a/doc/mpd.conf.5
+++ b/doc/mpd.conf.5
@@ -186,14 +186,14 @@ This specifies whether relative or absolute paths for song filenames are used
when saving playlists. The default is "no".
.SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP
-.B name <name>
-This specifies a unique name for the audio output.
-.TP
.B type <type>
This specifies the audio output type. Depending on what audio output support
-mpd was built with possible values could be "alsa", "oss", "osx", "mvp", "ao",
-or "shout" (see \fBREQUIRED SHOUT OUTPUT PARAMETERS\fP and \fBOPTIONAL SHOUT
-OUTPUT PARAMETERS\fP).
+mpd was built with possible values could be "alsa", "oss", "osx", "pulse",
+"mvp", "ao", or "shout" (see \fBREQUIRED SHOUT OUTPUT PARAMETERS\fP and
+\fBOPTIONAL SHOUT OUTPUT PARAMETERS\fP).
+.TP
+.B name <name>
+This specifies a unique name for the audio output.
.SH OPTIONAL AUDIO OUTPUT PARAMETERS
.TP
.B format <sample_rate:bits:channels>
@@ -207,6 +207,14 @@ This specifies the device to use for audio output. Used by the alsa and oss
output types. The default for oss is "/dev/dsp"; the default for alsa is
"hw:0,0".
.TP
+.B server <server list>
+A space separated list of servers to try to connect to. Used only by the pulse
+audio output. The default is to let PulseAudio choose a server.
+.TP
+.B sink <sink>
+The sink to output to. Used only by the pulse audio output. The default is to
+let PulseAudio choose a sink.
+.TP
.B driver <driver>
This specifies the libao driver to use for audio output. Used only by the ao
output type. Possible values depend on what libao drivers are available. See
diff --git a/src/Makefile.am b/src/Makefile.am
index 9197c3d69..91669ec40 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,7 @@ mpd_audioOutputs = \
audioOutputs/audioOutput_ao.c \
audioOutputs/audioOutput_oss.c \
audioOutputs/audioOutput_osx.c \
+ audioOutputs/audioOutput_pulse.c \
audioOutputs/audioOutput_mvp.c \
audioOutputs/audioOutput_shout.c
diff --git a/src/audio.c b/src/audio.c
index 3469a6825..308dcc3e9 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -75,6 +75,7 @@ extern AudioOutputPlugin alsaPlugin;
extern AudioOutputPlugin aoPlugin;
extern AudioOutputPlugin ossPlugin;
extern AudioOutputPlugin osxPlugin;
+extern AudioOutputPlugin pulsePlugin;
extern AudioOutputPlugin mvpPlugin;
extern AudioOutputPlugin shoutPlugin;
@@ -88,6 +89,7 @@ void initAudioDriver() {
loadAudioOutputPlugin(&aoPlugin);
loadAudioOutputPlugin(&ossPlugin);
loadAudioOutputPlugin(&osxPlugin);
+ loadAudioOutputPlugin(&pulsePlugin);
loadAudioOutputPlugin(&mvpPlugin);
loadAudioOutputPlugin(&shoutPlugin);
diff --git a/src/audioOutputs/audioOutput_pulse.c b/src/audioOutputs/audioOutput_pulse.c
new file mode 100644
index 000000000..8dcf09f87
--- /dev/null
+++ b/src/audioOutputs/audioOutput_pulse.c
@@ -0,0 +1,193 @@
+#include "../audioOutput.h"
+
+#include <stdlib.h>
+
+#ifdef HAVE_PULSE
+
+#define MPD_PULSE_NAME "mpd"
+#define MPD_PULSE_STREAM_NAME "mpd"
+
+#include "../conf.h"
+#include "../log.h"
+
+#include <pulse/simple.h>
+#include <pulse/error.h>
+
+typedef struct _PulseData {
+ char * server;
+ char * sink;
+ pa_simple * s;
+} PulseData;
+
+static PulseData * newPulseData()
+{
+ PulseData * ret;
+
+ ret = malloc(sizeof(PulseData));
+ ret->server = NULL;
+ ret->sink = NULL;
+ ret->s = NULL;
+ return ret;
+}
+
+static void freePulseData(PulseData * ad)
+{
+ if (ad->server) free(ad->server);
+ if (ad->sink) free(ad->sink);
+ free(ad);
+}
+
+static int pulse_initDriver(AudioOutput * audioOutput, ConfigParam * param)
+{
+ BlockParam * server = NULL;
+ BlockParam * sink = NULL;
+ PulseData * ad;
+
+ if (param) {
+ server = getBlockParam(param, "server");
+ sink = getBlockParam(param, "sink");
+ }
+
+ ad = newPulseData();
+ ad->server = server ? strdup(server->value) : NULL;
+ ad->sink = sink ? strdup(sink->value) : NULL;
+ audioOutput->data = ad;
+
+ return 0;
+}
+
+static void pulse_finishDriver(AudioOutput * audioOutput)
+{
+ freePulseData((PulseData *) audioOutput->data);
+}
+
+static int pulse_testDefault()
+{
+ pa_simple * s;
+ pa_sample_spec ss;
+ int error;
+
+ ss.format = PA_SAMPLE_S16NE;
+ ss.rate = 44100;
+ ss.channels = 2;
+
+ s = pa_simple_new(NULL, MPD_PULSE_NAME, PA_STREAM_PLAYBACK, NULL,
+ MPD_PULSE_STREAM_NAME, &ss, NULL, NULL, &error);
+ if (!s) {
+ WARNING("Cannot connect to default PulseAudio server: %s\n",
+ pa_strerror(error));
+ return -1;
+ }
+
+ pa_simple_free(s);
+
+ return 0;
+}
+
+static int pulse_openDevice(AudioOutput * audioOutput)
+{
+ PulseData * ad;
+ AudioFormat * audioFormat;
+ pa_sample_spec ss;
+ int error;
+
+ ad = audioOutput->data;
+ audioFormat = &audioOutput->outAudioFormat;
+
+ if (audioFormat->bits != 16) {
+ ERROR("PulseAudio doesn't support %i bit audio\n",
+ audioFormat->bits);
+ return -1;
+ }
+
+ ss.format = PA_SAMPLE_S16NE;
+ ss.rate = audioFormat->sampleRate;
+ ss.channels = audioFormat->channels;
+
+ ad->s = pa_simple_new(ad->server, MPD_PULSE_NAME, PA_STREAM_PLAYBACK,
+ ad->sink, MPD_PULSE_STREAM_NAME, &ss,
+ NULL, NULL, &error);
+ if (!ad->s) {
+ ERROR("Cannot connect to server in PulseAudio output " \
+ "\"%s\": %s\n", audioOutput->name, pa_strerror(error));
+ return -1;
+ }
+
+ audioOutput->open = 1;
+
+ DEBUG("PulseAudio output \"%s\" connected and playing %i bit, %i " \
+ "channel audio at %i Hz\n", audioOutput->name, audioFormat->bits,
+ audioFormat->channels, audioFormat->sampleRate);
+
+ return 0;
+}
+
+static void pulse_dropBufferedAudio(AudioOutput * audioOutput)
+{
+ PulseData * ad;
+ int error;
+
+ ad = audioOutput->data;
+ if (pa_simple_flush(ad->s, &error) < 0)
+ WARNING("Flush failed in PulseAudio output \"%s\": %s\n",
+ audioOutput->name, pa_strerror(error));
+}
+
+static void pulse_closeDevice(AudioOutput * audioOutput)
+{
+ PulseData * ad;
+
+ ad = audioOutput->data;
+ if (ad->s) {
+ pa_simple_drain(ad->s, NULL);
+ pa_simple_free(ad->s);
+ }
+
+ audioOutput->open = 0;
+}
+
+static int pulse_playAudio(AudioOutput * audioOutput, char * playChunk,
+ int size)
+{
+ PulseData * ad;
+ int error;
+
+ ad = audioOutput->data;
+
+ if (pa_simple_write(ad->s, playChunk, size, &error) < 0) {
+ ERROR("PulseAudio output \"%s\" disconnecting due to write " \
+ "error: %s\n", audioOutput->name, pa_strerror(error));
+ pulse_closeDevice(audioOutput);
+ return -1;
+ }
+
+ return 0;
+}
+
+AudioOutputPlugin pulsePlugin = {
+ "pulse",
+ pulse_testDefault,
+ pulse_initDriver,
+ pulse_finishDriver,
+ pulse_openDevice,
+ pulse_playAudio,
+ pulse_dropBufferedAudio,
+ pulse_closeDevice,
+ NULL, /* sendMetadataFunc */
+};
+
+#else /* HAVE_PULSE */
+
+AudioOutputPlugin pulsePlugin =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif /* HAVE_PULSE */