From 0adfd8ae76cb669cde111b87ada8481aa9354e6e Mon Sep 17 00:00:00 2001
From: Warren Dukes <warren.dukes@gmail.com>
Date: Tue, 2 Nov 2004 12:46:52 +0000
Subject: move audioOutput plugins to audioOutput directory

git-svn-id: https://svn.musicpd.org/mpd/trunk@2470 09075e82-0dd4-0310-85a5-a0d7c8717e4f
---
 src/Makefile.am                     |   9 +-
 src/audioOutput.h                   |   2 -
 src/audioOutput/audioOutput_ao.c    | 243 +++++++++++++++
 src/audioOutput/audioOutput_oss.c   | 199 ++++++++++++
 src/audioOutput/audioOutput_shout.c | 581 +++++++++++++++++++++++++++++++++++
 src/audioOutput_ao.c                | 243 ---------------
 src/audioOutput_oss.c               | 200 ------------
 src/audioOutput_shout.c             | 584 ------------------------------------
 8 files changed, 1029 insertions(+), 1032 deletions(-)
 create mode 100644 src/audioOutput/audioOutput_ao.c
 create mode 100644 src/audioOutput/audioOutput_oss.c
 create mode 100644 src/audioOutput/audioOutput_shout.c
 delete mode 100644 src/audioOutput_ao.c
 delete mode 100644 src/audioOutput_oss.c
 delete mode 100644 src/audioOutput_shout.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 2fced704c..b8cb508a8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,11 @@
 bin_PROGRAMS = mpd
 SUBDIRS = $(ID3_SUBDIR) $(MAD_SUBDIR) $(MP4FF_SUBDIR)
 
+mpd_audioOutput = \
+	audioOutput/audioOutput_ao.c \
+	audioOutput/audioOutput_oss.c \
+	audioOutput/audioOutput_shout.c
+
 mpd_inputPlugins = \
 	inputPlugins/aac_plugin.c \
 	inputPlugins/audiofile_plugin.c \
@@ -54,12 +59,10 @@ mpd_headers = \
 
 mpd_SOURCES = \
 	$(mpd_headers) \
+	$(mpd_audioOutput) \
 	$(mpd_inputPlugins) \
 	audio.c \
 	audioOutput.c \
-	audioOutput_ao.c \
-	audioOutput_oss.c \
-	audioOutput_shout.c \
 	buffer2array.c \
 	charConv.c \
 	command.c \
diff --git a/src/audioOutput.h b/src/audioOutput.h
index 49c7110f9..ce1a2949e 100644
--- a/src/audioOutput.h
+++ b/src/audioOutput.h
@@ -26,8 +26,6 @@
 #include "tag.h"
 #include "conf.h"
 
-#define AUDIO_AO_DRIVER_DEFAULT	"default"
-
 typedef struct _AudioOutput AudioOutput;
 
 typedef int (* AudioOutputInitDriverFunc) (AudioOutput * audioOutput, 
diff --git a/src/audioOutput/audioOutput_ao.c b/src/audioOutput/audioOutput_ao.c
new file mode 100644
index 000000000..366c42c1d
--- /dev/null
+++ b/src/audioOutput/audioOutput_ao.c
@@ -0,0 +1,243 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "../audioOutput.h"
+#include "../conf.h"
+#include "../log.h"
+#include "../sig_handlers.h"
+
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+
+#include <ao/ao.h>
+
+static int driverInitCount = 0;
+
+typedef struct _AoData {
+	int writeSize;
+	int driverId;
+	ao_option * options;
+	ao_device * device;
+} AoData;
+
+static AoData * newAoData() {
+	AoData * ret = malloc(sizeof(AoData));
+	ret->device = NULL;
+	ret->options = NULL;
+
+	return ret;
+}
+
+static void audioOutputAo_error() {
+	if(errno==AO_ENOTLIVE) {
+		ERROR("not a live ao device\n");
+	}
+	else if(errno==AO_EOPENDEVICE) {
+		ERROR("not able to open audio device\n");
+	}
+	else if(errno==AO_EBADOPTION) {
+		ERROR("bad driver option\n");
+	}
+}
+
+static int audioOutputAo_initDriver(AudioOutput * audioOutput,
+		ConfigParam * param)
+{
+	ao_info * ai;
+	char * dup;
+	char * stk1;
+	char * stk2;
+	char * n1;
+	char * key;
+	char * value;
+	char * test;
+	AoData * ad  = newAoData();
+	BlockParam * blockParam;
+
+	audioOutput->data = ad;
+
+	if((blockParam = getBlockParam(param, "write_size"))) {
+		ad->writeSize = strtol(blockParam->value, &test, 10);
+		if (*test!='\0') {
+			ERROR("\"%s\" is not a valid write size at line %i\n",
+				blockParam->value, blockParam->line);
+			exit(EXIT_FAILURE);
+		}
+	}
+	else ad->writeSize = 1024;
+
+	if(driverInitCount == 0) {
+		ao_initialize();
+	}
+	driverInitCount++;
+
+	blockParam = getBlockParam(param, "driver");
+
+	if(!blockParam || 0 == strcmp(blockParam->value,"default")) {
+		ad->driverId = ao_default_driver_id();
+	}
+	else if((ad->driverId = 
+		ao_driver_id(blockParam->value))<0) {
+		ERROR("\"%s\" is not a valid ao driver at line %i\n",
+			blockParam->value, blockParam->line);
+		exit(EXIT_FAILURE);
+	}
+	
+	if((ai = ao_driver_info(ad->driverId))==NULL) {
+		ERROR("problems getting driver info for device defined at "
+				"line %i\n", param->line);
+		ERROR("you may not have permission to the audio device\n");
+		exit(EXIT_FAILURE);
+	}
+
+	blockParam = getBlockParam(param, "options");
+
+	if(blockParam) {
+		dup = strdup(blockParam->value);
+	}
+	else dup = strdup("");
+
+	if(strlen(dup)) {
+		stk1 = NULL;
+		n1 = strtok_r(dup,";",&stk1);
+		while(n1) {
+			stk2 = NULL;
+			key = strtok_r(n1,"=",&stk2);
+			if(!key) {
+				ERROR("problems parsing "
+					"ao_driver_options \"%s\"\n", n1);
+				exit(EXIT_FAILURE);
+			}
+			/*found = 0;
+			for(i=0;i<ai->option_count;i++) {
+				if(strcmp(ai->options[i],key)==0) {
+					found = 1;
+					break;
+				}
+			}
+			if(!found) {
+				ERROR("\"%s\" is not an option for "
+					 "\"%s\" ao driver\n",key,
+					 ai->short_name);
+				exit(EXIT_FAILURE);
+			}*/
+			value = strtok_r(NULL,"",&stk2);
+			if(!value) {
+				ERROR("problems parsing "
+					"ao_driver_options \"%s\"\n", n1);
+				exit(EXIT_FAILURE);
+			}
+			ao_append_option(&ad->options,key,value);
+			n1 = strtok_r(NULL,";",&stk1);
+		}
+	}
+	free(dup);
+
+	return 0;
+}
+
+static void freeAoData(AoData * ad) {
+	ao_free_options(ad->options);
+	free(ad);
+}
+
+static void audioOutputAo_finishDriver(AudioOutput * audioOutput) {
+	AoData * ad = (AoData *)audioOutput->data;
+	freeAoData(ad);
+
+	driverInitCount--;
+
+	if(driverInitCount == 0) ao_shutdown();
+}
+
+static void audioOutputAo_closeDevice(AudioOutput * audioOutput) {
+	AoData * ad = (AoData *) audioOutput->data;
+
+	if(ad->device) {
+		blockSignals();
+		ao_close(ad->device);
+		ad->device = NULL;
+		unblockSignals();
+	}
+
+	audioOutput->open = 0;
+}
+
+static int audioOutputAo_openDevice(AudioOutput * audioOutput,
+		AudioFormat * audioFormat) 
+{
+	ao_sample_format format;
+	AoData * ad = (AoData *)audioOutput->data;
+
+	if(ad->device) {
+		audioOutputAo_closeDevice(audioOutput);
+	}
+
+	format.bits = audioFormat->bits;
+	format.rate = audioFormat->sampleRate;
+	format.byte_format = AO_FMT_NATIVE;
+	format.channels = audioFormat->channels;
+
+	blockSignals();
+	ad->device = ao_open_live(ad->driverId, &format, ad->options);
+	unblockSignals();
+
+	if(ad->device==NULL) return -1;
+
+	audioOutput->open = 1;
+
+	return 0;
+}
+
+
+static int audioOutputAo_play(AudioOutput * audioOutput, char * playChunk, 
+		int size) 
+{
+	int send;
+	AoData * ad = (AoData *)audioOutput->data;
+
+	if(ad->device==NULL) return -1;
+	
+	while(size>0) {
+		send = ad->writeSize > size ? size : ad->writeSize;
+		
+		if(ao_play(ad->device, playChunk, send)==0) {
+			audioOutputAo_error();
+			ERROR("closing audio device due to write error\n");
+			audioOutputAo_closeDevice(audioOutput);
+			return -1;
+		}
+
+		playChunk+=send;
+		size-=send;
+	}
+
+	return 0;
+}
+
+AudioOutputPlugin aoPlugin = 
+{
+	"ao",
+	audioOutputAo_initDriver,
+	audioOutputAo_finishDriver,
+	audioOutputAo_openDevice,
+	audioOutputAo_play,
+	audioOutputAo_closeDevice,
+	NULL /* sendMetadataFunc */
+};
diff --git a/src/audioOutput/audioOutput_oss.c b/src/audioOutput/audioOutput_oss.c
new file mode 100644
index 000000000..c8e86b7a4
--- /dev/null
+++ b/src/audioOutput/audioOutput_oss.c
@@ -0,0 +1,199 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * OSS audio output (c) 2004 by Eric Wong <eric@petta-tech.com>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "../audioOutput.h"
+
+#include <stdlib.h>
+
+#ifdef HAVE_OSS
+
+#include "../conf.h"
+#include "../log.h"
+#include "../sig_handlers.h"
+
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+# include <soundcard.h>
+#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
+# include <sys/soundcard.h>
+#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
+
+typedef struct _OssData {
+	int fd;
+	char * device;
+} OssData;
+
+static OssData * newOssData() {
+	OssData * ret = malloc(sizeof(OssData));
+
+	ret->device = NULL;
+	ret->fd = -1;
+
+	return ret;
+}
+
+static void freeOssData(OssData * od) {
+	if(od->device) free(od->device);
+
+	free(od);
+}
+
+static int oss_initDriver(AudioOutput * audioOutput, ConfigParam * param) {
+	BlockParam * bp = getBlockParam(param, "device");
+	OssData * od = newOssData();
+	
+	audioOutput->data = od;
+
+	if(!bp) {
+		int fd;
+
+		if(0 <= (fd = open("/dev/sound/dsp", O_WRONLY))) {
+			od->device = strdup("/dev/sound/dsp");
+			close(fd);
+		}
+		else if(0 <= (fd = open("/dev/dsp", O_WRONLY))) {
+			od->device = strdup("/dev/dsp");
+			close(fd);
+		}
+		else {
+			ERROR("Error trying to open default OSS device "
+				"specified at line %i\n", param->line);
+			ERROR("Specify a OSS device and/or check your "
+				"permissions\n");
+			exit(EXIT_FAILURE);
+		}
+
+		od->fd = -1;
+
+		return 0;
+	}
+
+	od->device = strdup(bp->value);
+
+	return 0;
+}
+
+static void oss_finishDriver(AudioOutput * audioOutput) {
+	OssData * od = audioOutput->data;
+
+	freeOssData(od);
+}
+
+static int oss_openDevice(AudioOutput * audioOutput, AudioFormat * audioFormat) 
+{
+	OssData * od = audioOutput->data;
+#ifdef WORDS_BIGENDIAN
+	int i = AFMT_S16_BE;
+#else
+	int i = AFMT_S16_LE;
+#endif
+	
+	if((od->fd = open(od->device, O_WRONLY)) < 0)
+		goto fail;
+	if(ioctl(od->fd, SNDCTL_DSP_SETFMT, &i))
+		goto fail;
+	if(ioctl(od->fd, SNDCTL_DSP_CHANNELS, &audioFormat->channels))
+		goto fail;
+	if(ioctl(od->fd, SNDCTL_DSP_SPEED, &audioFormat->sampleRate))
+		goto fail;
+	if(ioctl(od->fd, SNDCTL_DSP_SAMPLESIZE, &audioFormat->bits))
+		goto fail;
+	/*i = 1; if (ioctl(od->fd,SNDCTL_DSP_STEREO,&i)) err != 32; */
+
+	audioOutput->open = 1;
+
+	return 0;
+
+fail:
+	if(od->fd >= 0) close(od->fd);
+	audioOutput->open = 0;
+	ERROR("Error opening OSS device \"%s\": %s\n", od->device, 
+			strerror(errno));
+	return -1;
+}
+
+static void oss_closeDevice(AudioOutput * audioOutput) {
+	OssData * od = audioOutput->data;
+
+	if(od->fd >= 0) {
+		close(od->fd);
+		od->fd = -1;
+	}
+
+	audioOutput->open = 0;
+}
+
+static int oss_playAudio(AudioOutput * audioOutput, char * playChunk, 
+		int size) 
+{
+	OssData * od = audioOutput->data;
+	int ret;
+
+	while (size > 0) {
+		ret = write(od->fd, playChunk, size);
+		if(ret<0) {
+			ERROR("closing audio device due to write error\n");
+			oss_closeDevice(audioOutput);
+			return -1;
+		}
+		playChunk += ret;
+		size -= ret;
+	}
+
+	return 0;
+}
+
+AudioOutputPlugin ossPlugin =
+{
+	"oss",
+	oss_initDriver,
+	oss_finishDriver,
+	oss_openDevice,
+	oss_playAudio,
+	oss_closeDevice,
+	NULL /* sendMetadataFunc */
+};
+
+#else /* HAVE OSS */
+
+AudioOutputPlugin ossPlugin =
+{
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL /* sendMetadataFunc */
+};
+
+#endif /* HAVE_OSS */
+
+
diff --git a/src/audioOutput/audioOutput_shout.c b/src/audioOutput/audioOutput_shout.c
new file mode 100644
index 000000000..8b8e789fb
--- /dev/null
+++ b/src/audioOutput/audioOutput_shout.c
@@ -0,0 +1,581 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "../audioOutput.h"
+
+#include <stdlib.h>
+
+#ifdef HAVE_SHOUT
+
+#include "../conf.h"
+#include "../log.h"
+#include "../sig_handlers.h"
+#include "../pcm_utils.h"
+
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+
+#include <shout/shout.h>
+#include <vorbis/vorbisenc.h>
+#include <vorbis/codec.h>
+
+static int shoutInitCount = 0;
+
+/* lots of this code blatantly stolent from bossogg/bossao2 */
+
+typedef struct _ShoutData {
+	shout_t * shoutConn;
+
+	ogg_stream_state os;
+	ogg_page og;
+	ogg_packet op;
+	ogg_packet header_main;
+	ogg_packet header_comments;
+	ogg_packet header_codebooks;
+	
+	vorbis_dsp_state vd;
+	vorbis_block vb;
+	vorbis_info vi;
+	vorbis_comment vc;
+
+	float quality;
+	int bitrate;
+	AudioFormat outAudioFormat;
+	AudioFormat inAudioFormat;
+
+	char * convBuffer;
+	size_t convBufferLen;
+	/* shoud we convert the audio to a different format? */
+	int audioFormatConvert;
+
+	int opened;
+
+	MpdTag * tag;
+	int tagToSend;
+} ShoutData;
+
+static ShoutData * newShoutData() {
+	ShoutData * ret = malloc(sizeof(ShoutData));
+
+	ret->shoutConn = shout_new();
+	ret->convBuffer = NULL;
+	ret->convBufferLen = 0;
+	ret->opened = 0;
+	ret->tag = NULL;
+	ret->tagToSend = 0;
+	ret->bitrate = -1;
+	ret->quality = -1.0;
+
+	return ret;
+}
+
+static void freeShoutData(ShoutData * sd) {
+	if(sd->shoutConn) shout_free(sd->shoutConn);
+	if(sd->tag) freeMpdTag(sd->tag);
+	if(sd->convBuffer) free(sd->convBuffer);
+
+	free(sd);
+}
+
+#define checkBlockParam(name) { \
+	blockParam = getBlockParam(param, name); \
+	if(!blockParam) { \
+		ERROR("no \"%s\" defined for shout device defined at line " \
+				"%i\n", name, param->line); \
+		exit(EXIT_FAILURE); \
+	} \
+}
+
+static int myShout_initDriver(AudioOutput * audioOutput, ConfigParam * param) {
+	ShoutData * sd;
+	char * test;
+	int port;
+	char * host;
+	char * mount;
+	char * passwd;
+	char * user;
+	char * name;
+	BlockParam * blockParam;
+
+	sd = newShoutData();
+
+	if(shoutInitCount == 0) shout_init();
+
+	shoutInitCount++;
+
+	checkBlockParam("host");
+	host = blockParam->value;
+
+	checkBlockParam("mount");
+	mount = blockParam->value;
+
+	checkBlockParam("port");
+
+	port = strtol(blockParam->value, &test, 10);
+
+	if(*test != '\0' || port <= 0) {
+		ERROR("shout port \"%s\" is not a positive integer, line %i\n", 
+				blockParam->value, blockParam->line);
+		exit(EXIT_FAILURE);
+	}
+
+	checkBlockParam("password");
+	passwd = blockParam->value;
+
+	checkBlockParam("name");
+	name = blockParam->value;
+
+	checkBlockParam("user");
+	user = blockParam->value;
+
+	blockParam = getBlockParam(param, "quality");
+
+	if(blockParam) {
+		int line = blockParam->line;
+
+		sd->quality = strtod(blockParam->value, &test);
+
+		if(*test != '\0' || sd->quality < 0.0 || sd->quality > 10.0) {
+			ERROR("shout quality \"%s\" is not a number in the "
+				"rage 0-10, line %i\n", blockParam->value,
+				blockParam->line);
+			exit(EXIT_FAILURE);
+		}
+
+		blockParam = getBlockParam(param, "bitrate");
+
+		if(blockParam) {
+			ERROR("quality (line %i) and bitrate (line %i) are "
+				"both defined for shout output\n", line,
+				blockParam->line);
+			exit(EXIT_FAILURE);
+		}
+	}
+	else {
+		blockParam = getBlockParam(param, "bitrate");
+
+		if(!blockParam) {
+			ERROR("neither bitrate nor quality defined for shout "
+				"output at line %i\n", param->line);
+			exit(EXIT_FAILURE);
+		}
+
+		sd->bitrate = strtol(blockParam->value, &test, 10);
+
+		if(*test != '\0' || sd->bitrate <= 0) {
+			ERROR("bitrate at line %i should be a positve integer "
+				"\n", blockParam->line);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	checkBlockParam("format");
+
+	if(0 != parseAudioConfig(&(sd->outAudioFormat), blockParam->value)) {
+		ERROR("error parsing format at line %i\n", blockParam->line);
+		exit(EXIT_FAILURE);
+	}
+
+	if(shout_set_host(sd->shoutConn, host) !=  SHOUTERR_SUCCESS ||
+		shout_set_port(sd->shoutConn, port) != SHOUTERR_SUCCESS ||
+		shout_set_password(sd->shoutConn, passwd) != SHOUTERR_SUCCESS ||
+		shout_set_mount(sd->shoutConn, mount) != SHOUTERR_SUCCESS ||
+		shout_set_name(sd->shoutConn, name) != SHOUTERR_SUCCESS ||
+		shout_set_user(sd->shoutConn, user) != SHOUTERR_SUCCESS ||
+		shout_set_format(sd->shoutConn, SHOUT_FORMAT_VORBIS) 
+			!= SHOUTERR_SUCCESS ||
+		shout_set_protocol(sd->shoutConn, SHOUT_PROTOCOL_HTTP)
+			!= SHOUTERR_SUCCESS ||
+		shout_set_agent(sd->shoutConn, "MPD") != SHOUTERR_SUCCESS)
+	{
+		ERROR("error configuring shout: %s\n", 
+				shout_get_error(sd->shoutConn));
+		exit(EXIT_FAILURE);
+	}
+
+	{
+		char temp[11];
+		memset(temp, 0, sizeof(temp));
+	
+		snprintf(temp, sizeof(temp), "%d", sd->outAudioFormat.channels);
+		shout_set_audio_info(sd->shoutConn, SHOUT_AI_CHANNELS, temp);
+
+		snprintf(temp, sizeof(temp), "%d", 
+				sd->outAudioFormat.sampleRate);
+		shout_set_audio_info(sd->shoutConn, SHOUT_AI_SAMPLERATE, temp);
+
+		if(sd->quality >= 0) {
+			snprintf(temp, sizeof(temp), "%2.2f", sd->quality);
+			shout_set_audio_info(sd->shoutConn, SHOUT_AI_QUALITY,
+					temp);
+		}
+		else {
+			snprintf(temp, sizeof(temp), "%d", sd->bitrate);
+			shout_set_audio_info(sd->shoutConn, SHOUT_AI_BITRATE,
+					temp);
+		}
+	}
+
+	audioOutput->data = sd;
+
+	return 0;
+}
+
+/*static void finishEncoder(ShoutData * sd) {
+	vorbis_analysis_wrote(&sd->vd, 0);
+
+	while(vorbis_analysis_blockout(&sd->vd, &sd->vb) == 1) {
+		vorbis_analysis(&sd->vb, NULL);
+		vorbis_bitrate_addblock(&sd->vb);
+		while(vorbis_bitrate_flushpacket(&sd->vd, &sd->op)) {
+			ogg_stream_packetin(&sd->os, &sd->op);
+		}
+	}
+}
+
+static void flushEncoder(ShoutData * sd) {
+	while(1 == ogg_stream_pageout(&sd->os, &sd->og));
+}*/
+
+static void clearEncoder(ShoutData * sd) {
+	/*finishEncoder(sd);
+	flushEncoder(sd);*/
+
+	vorbis_comment_clear(&sd->vc);
+	ogg_stream_clear(&sd->os);
+	vorbis_block_clear(&sd->vb);
+	vorbis_dsp_clear(&sd->vd);
+	vorbis_info_clear(&sd->vi);
+}
+
+static void myShout_closeShoutConn(ShoutData * sd) {
+	if(sd->opened) {
+		clearEncoder(sd);
+
+		if(shout_close(sd->shoutConn) != SHOUTERR_SUCCESS) {
+			ERROR("problem closing connection to shout server: "
+				"%s\n", shout_get_error(sd->shoutConn));
+		}
+	}
+
+	sd->opened = 0;
+}
+
+static void myShout_finishDriver(AudioOutput * audioOutput) {
+	ShoutData * sd = (ShoutData *)audioOutput->data;
+
+	myShout_closeShoutConn(sd);
+
+	freeShoutData(sd);
+
+	shoutInitCount--;
+
+	if(shoutInitCount == 0) shout_shutdown();
+}
+
+static void myShout_closeDevice(AudioOutput * audioOutput) {
+	audioOutput->open = 0;
+}
+
+static int myShout_handleError(ShoutData * sd, int err) {
+	switch(err) {
+	case SHOUTERR_SUCCESS:
+		break;
+	case SHOUTERR_UNCONNECTED:
+	case SHOUTERR_SOCKET:
+		ERROR("Lost shout connection\n");
+		return -1;
+	default:
+		ERROR("shout: error: %s\n", shout_get_error(sd->shoutConn));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int write_page(ShoutData * sd) {
+	int err = 0;
+
+	shout_sync(sd->shoutConn);
+	err = shout_send(sd->shoutConn, sd->og.header, sd->og.header_len);
+	if(myShout_handleError(sd, err) < 0) return -1;
+	err = shout_send(sd->shoutConn, sd->og.body, sd->og.body_len);
+	if(myShout_handleError(sd, err) < 0) return -1;
+
+	return 0;
+}
+
+#define addTag(name, value) { \
+	if(value) vorbis_comment_add_tag(&(sd->vc), name, value); \
+}
+
+static void copyTagToVorbisComment(ShoutData * sd) {
+	if(sd->tag) {
+		addTag("ARTIST", sd->tag->artist);
+		addTag("ALBUM", sd->tag->album);
+		addTag("TITLE", sd->tag->title);
+	}
+}
+
+static int initEncoder(ShoutData * sd) {
+	vorbis_info_init(&(sd->vi));
+
+	if(sd->quality >= 0) {
+		if( 0 != vorbis_encode_init_vbr(&(sd->vi),
+			sd->outAudioFormat.channels, 
+			sd->outAudioFormat.sampleRate, sd->quality*0.1) )
+		{
+			ERROR("problem seting up vorbis encoder for shout\n");
+			vorbis_info_clear(&(sd->vi));
+			return -1;
+		}
+	}
+	else {
+		if( 0 != vorbis_encode_init(&(sd->vi), 
+			sd->outAudioFormat.channels, 
+			sd->outAudioFormat.sampleRate, -1.0,
+			sd->bitrate*1000, -1.0) )
+		{
+			ERROR("problem seting up vorbis encoder for shout\n");
+			vorbis_info_clear(&(sd->vi));
+			return -1;
+		}
+	}
+
+	vorbis_analysis_init(&(sd->vd), &(sd->vi));
+	vorbis_block_init (&(sd->vd), &(sd->vb));
+
+	ogg_stream_init(&(sd->os), rand());
+
+	vorbis_comment_init(&(sd->vc));
+
+	return 0;
+}
+
+static int myShout_openShoutConn(AudioOutput * audioOutput) {
+	ShoutData * sd = (ShoutData *)audioOutput->data;
+
+	if(shout_open(sd->shoutConn) != SHOUTERR_SUCCESS)
+	{
+		ERROR("problem opening connection to shout server: %s\n",
+				shout_get_error(sd->shoutConn));
+
+		return -1;
+	}
+
+	if(initEncoder(sd) < 0) {
+		shout_close(sd->shoutConn);
+		return -1;
+	}
+
+	copyTagToVorbisComment(sd);
+
+	vorbis_analysis_headerout(&(sd->vd), &(sd->vc), &(sd->header_main),
+			&(sd->header_comments), &(sd->header_codebooks));
+
+	ogg_stream_packetin(&(sd->os), &(sd->header_main));
+	ogg_stream_packetin(&(sd->os), &(sd->header_comments));
+	ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
+
+	sd->opened = 1;
+	sd->tagToSend = 0;
+
+	while(ogg_stream_flush(&(sd->os), &(sd->og)))
+	{
+		if(write_page(sd) < 0) {
+			myShout_closeShoutConn(sd);
+			return -1;
+		}
+	}
+
+	/*if(sd->tag) freeMpdTag(sd->tag);
+	sd->tag = NULL;*/
+
+	return 0;
+}
+
+static int myShout_openDevice(AudioOutput * audioOutput,
+		AudioFormat * audioFormat) 
+{
+	ShoutData * sd = (ShoutData *)audioOutput->data;
+
+	memcpy(&(sd->inAudioFormat), audioFormat, sizeof(AudioFormat));
+
+	if(0 == memcmp(&(sd->inAudioFormat), &(sd->outAudioFormat), 
+			sizeof(AudioFormat))) 
+	{
+		sd->audioFormatConvert = 0;
+	}
+	else sd->audioFormatConvert = 1;
+
+	audioOutput->open = 1;
+
+	if(sd->opened) return 0;
+
+	if(myShout_openShoutConn(audioOutput) < 0) {
+		audioOutput->open = 0;
+		return -1;
+	}
+
+	return 0;
+}
+
+static void myShout_convertAudioFormat(ShoutData * sd, char ** chunkArgPtr,
+		int * sizeArgPtr)
+{
+	int size = pcm_sizeOfOutputBufferForAudioFormatConversion(
+			&(sd->inAudioFormat), *sizeArgPtr, 
+			&(sd->outAudioFormat));
+
+	if(size > sd->convBufferLen) {
+		sd->convBuffer = realloc(sd->convBuffer, size);
+		sd->convBufferLen = size;
+	}
+
+	pcm_convertAudioFormat(&(sd->inAudioFormat), *chunkArgPtr, *sizeArgPtr,
+			&(sd->outAudioFormat), sd->convBuffer);
+	
+	*sizeArgPtr = size;
+	*chunkArgPtr = sd->convBuffer;
+}
+
+static void myShout_sendMetadata(ShoutData * sd) {
+	ogg_int64_t granulepos = sd->vd.granulepos;
+
+	if(!sd->opened || !sd->tag) return;
+
+	clearEncoder(sd);
+	if(initEncoder(sd) < 0) return;
+
+	sd->vd.granulepos = granulepos;
+
+	copyTagToVorbisComment(sd);
+
+	vorbis_analysis_headerout(&(sd->vd), &(sd->vc), &(sd->header_main),
+			&(sd->header_comments), &(sd->header_codebooks));
+
+	ogg_stream_packetin(&(sd->os), &(sd->header_main));
+	ogg_stream_packetin(&(sd->os), &(sd->header_comments));
+	ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
+
+	while(ogg_stream_flush(&(sd->os), &(sd->og)))
+	{
+		if(write_page(sd) < 0) {
+			myShout_closeShoutConn(sd);
+			return;
+		}
+	}
+
+	/*if(sd->tag) freeMpdTag(sd->tag);
+	sd->tag = NULL;*/
+	sd->tagToSend = 0;
+}
+
+static int myShout_play(AudioOutput * audioOutput, char * playChunk, int size) {
+	int i,j;
+	ShoutData * sd = (ShoutData *)audioOutput->data;
+	float ** vorbbuf;
+	int samples;
+	int bytes = sd->outAudioFormat.bits/8;
+
+	if(sd->opened && sd->tagToSend) myShout_sendMetadata(sd);
+
+	if(!sd->opened) {
+		if(myShout_openShoutConn(audioOutput) < 0) {
+			return -1;
+		}
+	}
+
+	if(sd->audioFormatConvert) {
+		myShout_convertAudioFormat(sd, &playChunk, &size);
+	}
+
+	samples = size/(bytes*sd->outAudioFormat.channels);
+
+	/* this is for only 16-bit audio */
+
+	vorbbuf = vorbis_analysis_buffer(&(sd->vd), samples);
+
+	for(i=0; i<samples; i++) {
+		for(j=0; j<sd->outAudioFormat.channels; j++) {
+			vorbbuf[j][i] = (*((mpd_sint16 *)playChunk)) / 32768.0;
+			playChunk += bytes;
+		}
+	}
+
+	vorbis_analysis_wrote(&(sd->vd), samples);
+
+	while(1 == vorbis_analysis_blockout(&(sd->vd), &(sd->vb))) {
+		vorbis_analysis(&(sd->vb), NULL);
+		vorbis_bitrate_addblock(&(sd->vb));
+
+		while(vorbis_bitrate_flushpacket(&(sd->vd), &(sd->op))) {
+			ogg_stream_packetin(&(sd->os), &(sd->op));
+		}
+	}
+
+
+	while(ogg_stream_pageout(&(sd->os), &(sd->og)) != 0) {
+		if(write_page(sd) < 0) {
+			myShout_closeShoutConn(sd);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static void myShout_setTag(AudioOutput * audioOutput, MpdTag * tag) {
+	ShoutData * sd = (ShoutData *)audioOutput->data;
+
+	if(sd->tag) freeMpdTag(sd->tag);
+	sd->tag = NULL;
+	sd->tagToSend = 0;
+
+	if(!tag) return;
+
+	sd->tag = mpdTagDup(tag);
+	sd->tagToSend = 1;
+}
+
+AudioOutputPlugin shoutPlugin = 
+{
+	"shout",
+	myShout_initDriver,
+	myShout_finishDriver,
+	myShout_openDevice,
+	myShout_play,
+	myShout_closeDevice,
+	myShout_setTag
+};
+
+#else
+
+AudioOutputPlugin shoutPlugin = 
+{
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+#endif
diff --git a/src/audioOutput_ao.c b/src/audioOutput_ao.c
deleted file mode 100644
index e328c1bb4..000000000
--- a/src/audioOutput_ao.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/* the Music Player Daemon (MPD)
- * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
- * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "audioOutput.h"
-#include "conf.h"
-#include "log.h"
-#include "sig_handlers.h"
-
-#include <string.h>
-#include <assert.h>
-#include <signal.h>
-
-#include <ao/ao.h>
-
-static int driverInitCount = 0;
-
-typedef struct _AoData {
-	int writeSize;
-	int driverId;
-	ao_option * options;
-	ao_device * device;
-} AoData;
-
-static AoData * newAoData() {
-	AoData * ret = malloc(sizeof(AoData));
-	ret->device = NULL;
-	ret->options = NULL;
-
-	return ret;
-}
-
-static void audioOutputAo_error() {
-	if(errno==AO_ENOTLIVE) {
-		ERROR("not a live ao device\n");
-	}
-	else if(errno==AO_EOPENDEVICE) {
-		ERROR("not able to open audio device\n");
-	}
-	else if(errno==AO_EBADOPTION) {
-		ERROR("bad driver option\n");
-	}
-}
-
-static int audioOutputAo_initDriver(AudioOutput * audioOutput,
-		ConfigParam * param)
-{
-	ao_info * ai;
-	char * dup;
-	char * stk1;
-	char * stk2;
-	char * n1;
-	char * key;
-	char * value;
-	char * test;
-	AoData * ad  = newAoData();
-	BlockParam * blockParam;
-
-	audioOutput->data = ad;
-
-	if((blockParam = getBlockParam(param, "write_size"))) {
-		ad->writeSize = strtol(blockParam->value, &test, 10);
-		if (*test!='\0') {
-			ERROR("\"%s\" is not a valid write size at line %i\n",
-				blockParam->value, blockParam->line);
-			exit(EXIT_FAILURE);
-		}
-	}
-	else ad->writeSize = 1024;
-
-	if(driverInitCount == 0) {
-		ao_initialize();
-	}
-	driverInitCount++;
-
-	blockParam = getBlockParam(param, "driver");
-
-	if(!blockParam || 0 == strcmp(blockParam->value,"default")) {
-		ad->driverId = ao_default_driver_id();
-	}
-	else if((ad->driverId = 
-		ao_driver_id(blockParam->value))<0) {
-		ERROR("\"%s\" is not a valid ao driver at line %i\n",
-			blockParam->value, blockParam->line);
-		exit(EXIT_FAILURE);
-	}
-	
-	if((ai = ao_driver_info(ad->driverId))==NULL) {
-		ERROR("problems getting driver info for device defined at "
-				"line %i\n", param->line);
-		ERROR("you may not have permission to the audio device\n");
-		exit(EXIT_FAILURE);
-	}
-
-	blockParam = getBlockParam(param, "options");
-
-	if(blockParam) {
-		dup = strdup(blockParam->value);
-	}
-	else dup = strdup("");
-
-	if(strlen(dup)) {
-		stk1 = NULL;
-		n1 = strtok_r(dup,";",&stk1);
-		while(n1) {
-			stk2 = NULL;
-			key = strtok_r(n1,"=",&stk2);
-			if(!key) {
-				ERROR("problems parsing "
-					"ao_driver_options \"%s\"\n", n1);
-				exit(EXIT_FAILURE);
-			}
-			/*found = 0;
-			for(i=0;i<ai->option_count;i++) {
-				if(strcmp(ai->options[i],key)==0) {
-					found = 1;
-					break;
-				}
-			}
-			if(!found) {
-				ERROR("\"%s\" is not an option for "
-					 "\"%s\" ao driver\n",key,
-					 ai->short_name);
-				exit(EXIT_FAILURE);
-			}*/
-			value = strtok_r(NULL,"",&stk2);
-			if(!value) {
-				ERROR("problems parsing "
-					"ao_driver_options \"%s\"\n", n1);
-				exit(EXIT_FAILURE);
-			}
-			ao_append_option(&ad->options,key,value);
-			n1 = strtok_r(NULL,";",&stk1);
-		}
-	}
-	free(dup);
-
-	return 0;
-}
-
-static void freeAoData(AoData * ad) {
-	ao_free_options(ad->options);
-	free(ad);
-}
-
-static void audioOutputAo_finishDriver(AudioOutput * audioOutput) {
-	AoData * ad = (AoData *)audioOutput->data;
-	freeAoData(ad);
-
-	driverInitCount--;
-
-	if(driverInitCount == 0) ao_shutdown();
-}
-
-static void audioOutputAo_closeDevice(AudioOutput * audioOutput) {
-	AoData * ad = (AoData *) audioOutput->data;
-
-	if(ad->device) {
-		blockSignals();
-		ao_close(ad->device);
-		ad->device = NULL;
-		unblockSignals();
-	}
-
-	audioOutput->open = 0;
-}
-
-static int audioOutputAo_openDevice(AudioOutput * audioOutput,
-		AudioFormat * audioFormat) 
-{
-	ao_sample_format format;
-	AoData * ad = (AoData *)audioOutput->data;
-
-	if(ad->device) {
-		audioOutputAo_closeDevice(audioOutput);
-	}
-
-	format.bits = audioFormat->bits;
-	format.rate = audioFormat->sampleRate;
-	format.byte_format = AO_FMT_NATIVE;
-	format.channels = audioFormat->channels;
-
-	blockSignals();
-	ad->device = ao_open_live(ad->driverId, &format, ad->options);
-	unblockSignals();
-
-	if(ad->device==NULL) return -1;
-
-	audioOutput->open = 1;
-
-	return 0;
-}
-
-
-static int audioOutputAo_play(AudioOutput * audioOutput, char * playChunk, 
-		int size) 
-{
-	int send;
-	AoData * ad = (AoData *)audioOutput->data;
-
-	if(ad->device==NULL) return -1;
-	
-	while(size>0) {
-		send = ad->writeSize > size ? size : ad->writeSize;
-		
-		if(ao_play(ad->device, playChunk, send)==0) {
-			audioOutputAo_error();
-			ERROR("closing audio device due to write error\n");
-			audioOutputAo_closeDevice(audioOutput);
-			return -1;
-		}
-
-		playChunk+=send;
-		size-=send;
-	}
-
-	return 0;
-}
-
-AudioOutputPlugin aoPlugin = 
-{
-	"ao",
-	audioOutputAo_initDriver,
-	audioOutputAo_finishDriver,
-	audioOutputAo_openDevice,
-	audioOutputAo_play,
-	audioOutputAo_closeDevice,
-	NULL /* sendMetadataFunc */
-};
diff --git a/src/audioOutput_oss.c b/src/audioOutput_oss.c
deleted file mode 100644
index c677c90d2..000000000
--- a/src/audioOutput_oss.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/* the Music Player Daemon (MPD)
- * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
- * This project's homepage is: http://www.musicpd.org
- *
- * OSS audio output (c) 2004 by Eric Wong <eric@petta-tech.com>
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "../config.h"
-#include "audioOutput.h"
-
-#include <stdlib.h>
-
-#ifdef HAVE_OSS
-
-#include "conf.h"
-#include "log.h"
-#include "sig_handlers.h"
-
-#include <string.h>
-#include <assert.h>
-#include <signal.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-
-#if defined(__OpenBSD__) || defined(__NetBSD__)
-# include <soundcard.h>
-#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
-# include <sys/soundcard.h>
-#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
-
-typedef struct _OssData {
-	int fd;
-	char * device;
-} OssData;
-
-static OssData * newOssData() {
-	OssData * ret = malloc(sizeof(OssData));
-
-	ret->device = NULL;
-	ret->fd = -1;
-
-	return ret;
-}
-
-static void freeOssData(OssData * od) {
-	if(od->device) free(od->device);
-
-	free(od);
-}
-
-static int oss_initDriver(AudioOutput * audioOutput, ConfigParam * param) {
-	BlockParam * bp = getBlockParam(param, "device");
-	OssData * od = newOssData();
-	
-	audioOutput->data = od;
-
-	if(!bp) {
-		int fd;
-
-		if(0 <= (fd = open("/dev/sound/dsp", O_WRONLY))) {
-			od->device = strdup("/dev/sound/dsp");
-			close(fd);
-		}
-		else if(0 <= (fd = open("/dev/dsp", O_WRONLY))) {
-			od->device = strdup("/dev/dsp");
-			close(fd);
-		}
-		else {
-			ERROR("Error trying to open default OSS device "
-				"specified at line %i\n", param->line);
-			ERROR("Specify a OSS device and/or check your "
-				"permissions\n");
-			exit(EXIT_FAILURE);
-		}
-
-		od->fd = -1;
-
-		return 0;
-	}
-
-	od->device = strdup(bp->value);
-
-	return 0;
-}
-
-static void oss_finishDriver(AudioOutput * audioOutput) {
-	OssData * od = audioOutput->data;
-
-	freeOssData(od);
-}
-
-static int oss_openDevice(AudioOutput * audioOutput, AudioFormat * audioFormat) 
-{
-	OssData * od = audioOutput->data;
-#ifdef WORDS_BIGENDIAN
-	int i = AFMT_S16_BE;
-#else
-	int i = AFMT_S16_LE;
-#endif
-	
-	if((od->fd = open(od->device, O_WRONLY)) < 0)
-		goto fail;
-	if(ioctl(od->fd, SNDCTL_DSP_SETFMT, &i))
-		goto fail;
-	if(ioctl(od->fd, SNDCTL_DSP_CHANNELS, &audioFormat->channels))
-		goto fail;
-	if(ioctl(od->fd, SNDCTL_DSP_SPEED, &audioFormat->sampleRate))
-		goto fail;
-	if(ioctl(od->fd, SNDCTL_DSP_SAMPLESIZE, &audioFormat->bits))
-		goto fail;
-	/*i = 1; if (ioctl(od->fd,SNDCTL_DSP_STEREO,&i)) err != 32; */
-
-	audioOutput->open = 1;
-
-	return 0;
-
-fail:
-	if(od->fd >= 0) close(od->fd);
-	audioOutput->open = 0;
-	ERROR("Error opening OSS device \"%s\": %s\n", od->device, 
-			strerror(errno));
-	return -1;
-}
-
-static void oss_closeDevice(AudioOutput * audioOutput) {
-	OssData * od = audioOutput->data;
-
-	if(od->fd >= 0) {
-		close(od->fd);
-		od->fd = -1;
-	}
-
-	audioOutput->open = 0;
-}
-
-static int oss_playAudio(AudioOutput * audioOutput, char * playChunk, 
-		int size) 
-{
-	OssData * od = audioOutput->data;
-	int ret;
-
-	while (size > 0) {
-		ret = write(od->fd, playChunk, size);
-		if(ret<0) {
-			ERROR("closing audio device due to write error\n");
-			oss_closeDevice(audioOutput);
-			return -1;
-		}
-		playChunk += ret;
-		size -= ret;
-	}
-
-	return 0;
-}
-
-AudioOutputPlugin ossPlugin =
-{
-	"oss",
-	oss_initDriver,
-	oss_finishDriver,
-	oss_openDevice,
-	oss_playAudio,
-	oss_closeDevice,
-	NULL /* sendMetadataFunc */
-};
-
-#else /* HAVE OSS */
-
-AudioOutputPlugin ossPlugin =
-{
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL /* sendMetadataFunc */
-};
-
-#endif /* HAVE_OSS */
-
-
diff --git a/src/audioOutput_shout.c b/src/audioOutput_shout.c
deleted file mode 100644
index 7ffdbb31b..000000000
--- a/src/audioOutput_shout.c
+++ /dev/null
@@ -1,584 +0,0 @@
-/* the Music Player Daemon (MPD)
- * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
- * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "../config.h"
-
-#include "audioOutput.h"
-
-#ifdef HAVE_SHOUT
-
-#include "conf.h"
-#include "log.h"
-#include "sig_handlers.h"
-#include "pcm_utils.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <signal.h>
-
-#include <shout/shout.h>
-#include <vorbis/vorbisenc.h>
-#include <vorbis/codec.h>
-
-static int shoutInitCount = 0;
-
-/* lots of this code blatantly stolent from bossogg/bossao2 */
-
-typedef struct _ShoutData {
-	shout_t * shoutConn;
-
-	ogg_stream_state os;
-	ogg_page og;
-	ogg_packet op;
-	ogg_packet header_main;
-	ogg_packet header_comments;
-	ogg_packet header_codebooks;
-	
-	vorbis_dsp_state vd;
-	vorbis_block vb;
-	vorbis_info vi;
-	vorbis_comment vc;
-
-	float quality;
-	int bitrate;
-	AudioFormat outAudioFormat;
-	AudioFormat inAudioFormat;
-
-	char * convBuffer;
-	size_t convBufferLen;
-	/* shoud we convert the audio to a different format? */
-	int audioFormatConvert;
-
-	int opened;
-
-	MpdTag * tag;
-	int tagToSend;
-} ShoutData;
-
-static ShoutData * newShoutData() {
-	ShoutData * ret = malloc(sizeof(ShoutData));
-
-	ret->shoutConn = shout_new();
-	ret->convBuffer = NULL;
-	ret->convBufferLen = 0;
-	ret->opened = 0;
-	ret->tag = NULL;
-	ret->tagToSend = 0;
-	ret->bitrate = -1;
-	ret->quality = -1.0;
-
-	return ret;
-}
-
-static void freeShoutData(ShoutData * sd) {
-	if(sd->shoutConn) shout_free(sd->shoutConn);
-	if(sd->tag) freeMpdTag(sd->tag);
-	if(sd->convBuffer) free(sd->convBuffer);
-
-	free(sd);
-}
-
-#define checkBlockParam(name) { \
-	blockParam = getBlockParam(param, name); \
-	if(!blockParam) { \
-		ERROR("no \"%s\" defined for shout device defined at line " \
-				"%i\n", name, param->line); \
-		exit(EXIT_FAILURE); \
-	} \
-}
-
-static int myShout_initDriver(AudioOutput * audioOutput, ConfigParam * param) {
-	ShoutData * sd;
-	char * test;
-	int port;
-	char * host;
-	char * mount;
-	char * passwd;
-	char * user;
-	char * name;
-	BlockParam * blockParam;
-
-	sd = newShoutData();
-
-	if(shoutInitCount == 0) shout_init();
-
-	shoutInitCount++;
-
-	checkBlockParam("host");
-	host = blockParam->value;
-
-	checkBlockParam("mount");
-	mount = blockParam->value;
-
-	checkBlockParam("port");
-
-	port = strtol(blockParam->value, &test, 10);
-
-	if(*test != '\0' || port <= 0) {
-		ERROR("shout port \"%s\" is not a positive integer, line %i\n", 
-				blockParam->value, blockParam->line);
-		exit(EXIT_FAILURE);
-	}
-
-	checkBlockParam("password");
-	passwd = blockParam->value;
-
-	checkBlockParam("name");
-	name = blockParam->value;
-
-	checkBlockParam("user");
-	user = blockParam->value;
-
-	blockParam = getBlockParam(param, "quality");
-
-	if(blockParam) {
-		int line = blockParam->line;
-
-		sd->quality = strtod(blockParam->value, &test);
-
-		if(*test != '\0' || sd->quality < 0.0 || sd->quality > 10.0) {
-			ERROR("shout quality \"%s\" is not a number in the "
-				"rage 0-10, line %i\n", blockParam->value,
-				blockParam->line);
-			exit(EXIT_FAILURE);
-		}
-
-		blockParam = getBlockParam(param, "bitrate");
-
-		if(blockParam) {
-			ERROR("quality (line %i) and bitrate (line %i) are "
-				"both defined for shout output\n", line,
-				blockParam->line);
-			exit(EXIT_FAILURE);
-		}
-	}
-	else {
-		blockParam = getBlockParam(param, "bitrate");
-
-		if(!blockParam) {
-			ERROR("neither bitrate nor quality defined for shout "
-				"output at line %i\n", param->line);
-			exit(EXIT_FAILURE);
-		}
-
-		sd->bitrate = strtol(blockParam->value, &test, 10);
-
-		if(*test != '\0' || sd->bitrate <= 0) {
-			ERROR("bitrate at line %i should be a positve integer "
-				"\n", blockParam->line);
-			exit(EXIT_FAILURE);
-		}
-	}
-
-	checkBlockParam("format");
-
-	if(0 != parseAudioConfig(&(sd->outAudioFormat), blockParam->value)) {
-		ERROR("error parsing format at line %i\n", blockParam->line);
-		exit(EXIT_FAILURE);
-	}
-
-	if(shout_set_host(sd->shoutConn, host) !=  SHOUTERR_SUCCESS ||
-		shout_set_port(sd->shoutConn, port) != SHOUTERR_SUCCESS ||
-		shout_set_password(sd->shoutConn, passwd) != SHOUTERR_SUCCESS ||
-		shout_set_mount(sd->shoutConn, mount) != SHOUTERR_SUCCESS ||
-		shout_set_name(sd->shoutConn, name) != SHOUTERR_SUCCESS ||
-		shout_set_user(sd->shoutConn, user) != SHOUTERR_SUCCESS ||
-		shout_set_format(sd->shoutConn, SHOUT_FORMAT_VORBIS) 
-			!= SHOUTERR_SUCCESS ||
-		shout_set_protocol(sd->shoutConn, SHOUT_PROTOCOL_HTTP)
-			!= SHOUTERR_SUCCESS ||
-		shout_set_agent(sd->shoutConn, "MPD") != SHOUTERR_SUCCESS)
-	{
-		ERROR("error configuring shout: %s\n", 
-				shout_get_error(sd->shoutConn));
-		exit(EXIT_FAILURE);
-	}
-
-	{
-		char temp[11];
-		memset(temp, 0, sizeof(temp));
-	
-		snprintf(temp, sizeof(temp), "%d", sd->outAudioFormat.channels);
-		shout_set_audio_info(sd->shoutConn, SHOUT_AI_CHANNELS, temp);
-
-		snprintf(temp, sizeof(temp), "%d", 
-				sd->outAudioFormat.sampleRate);
-		shout_set_audio_info(sd->shoutConn, SHOUT_AI_SAMPLERATE, temp);
-
-		if(sd->quality >= 0) {
-			snprintf(temp, sizeof(temp), "%2.2f", sd->quality);
-			shout_set_audio_info(sd->shoutConn, SHOUT_AI_QUALITY,
-					temp);
-		}
-		else {
-			snprintf(temp, sizeof(temp), "%d", sd->bitrate);
-			shout_set_audio_info(sd->shoutConn, SHOUT_AI_BITRATE,
-					temp);
-		}
-	}
-
-	audioOutput->data = sd;
-
-	return 0;
-}
-
-/*static void finishEncoder(ShoutData * sd) {
-	vorbis_analysis_wrote(&sd->vd, 0);
-
-	while(vorbis_analysis_blockout(&sd->vd, &sd->vb) == 1) {
-		vorbis_analysis(&sd->vb, NULL);
-		vorbis_bitrate_addblock(&sd->vb);
-		while(vorbis_bitrate_flushpacket(&sd->vd, &sd->op)) {
-			ogg_stream_packetin(&sd->os, &sd->op);
-		}
-	}
-}
-
-static void flushEncoder(ShoutData * sd) {
-	while(1 == ogg_stream_pageout(&sd->os, &sd->og));
-}*/
-
-static void clearEncoder(ShoutData * sd) {
-	/*finishEncoder(sd);
-	flushEncoder(sd);*/
-
-	vorbis_comment_clear(&sd->vc);
-	ogg_stream_clear(&sd->os);
-	vorbis_block_clear(&sd->vb);
-	vorbis_dsp_clear(&sd->vd);
-	vorbis_info_clear(&sd->vi);
-}
-
-static void myShout_closeShoutConn(ShoutData * sd) {
-	if(sd->opened) {
-		clearEncoder(sd);
-
-		if(shout_close(sd->shoutConn) != SHOUTERR_SUCCESS) {
-			ERROR("problem closing connection to shout server: "
-				"%s\n", shout_get_error(sd->shoutConn));
-		}
-	}
-
-	sd->opened = 0;
-}
-
-static void myShout_finishDriver(AudioOutput * audioOutput) {
-	ShoutData * sd = (ShoutData *)audioOutput->data;
-
-	myShout_closeShoutConn(sd);
-
-	freeShoutData(sd);
-
-	shoutInitCount--;
-
-	if(shoutInitCount == 0) shout_shutdown();
-}
-
-static void myShout_closeDevice(AudioOutput * audioOutput) {
-	audioOutput->open = 0;
-}
-
-static int myShout_handleError(ShoutData * sd, int err) {
-	switch(err) {
-	case SHOUTERR_SUCCESS:
-		break;
-	case SHOUTERR_UNCONNECTED:
-	case SHOUTERR_SOCKET:
-		ERROR("Lost shout connection\n");
-		return -1;
-	default:
-		ERROR("shout: error: %s\n", shout_get_error(sd->shoutConn));
-		return -1;
-	}
-
-	return 0;
-}
-
-static int write_page(ShoutData * sd) {
-	int err = 0;
-
-	shout_sync(sd->shoutConn);
-	err = shout_send(sd->shoutConn, sd->og.header, sd->og.header_len);
-	if(myShout_handleError(sd, err) < 0) return -1;
-	err = shout_send(sd->shoutConn, sd->og.body, sd->og.body_len);
-	if(myShout_handleError(sd, err) < 0) return -1;
-
-	return 0;
-}
-
-#define addTag(name, value) { \
-	if(value) vorbis_comment_add_tag(&(sd->vc), name, value); \
-}
-
-static void copyTagToVorbisComment(ShoutData * sd) {
-	if(sd->tag) {
-		addTag("ARTIST", sd->tag->artist);
-		addTag("ALBUM", sd->tag->album);
-		addTag("TITLE", sd->tag->title);
-	}
-}
-
-static int initEncoder(ShoutData * sd) {
-	vorbis_info_init(&(sd->vi));
-
-	if(sd->quality >= 0) {
-		if( 0 != vorbis_encode_init_vbr(&(sd->vi),
-			sd->outAudioFormat.channels, 
-			sd->outAudioFormat.sampleRate, sd->quality*0.1) )
-		{
-			ERROR("problem seting up vorbis encoder for shout\n");
-			vorbis_info_clear(&(sd->vi));
-			return -1;
-		}
-	}
-	else {
-		if( 0 != vorbis_encode_init(&(sd->vi), 
-			sd->outAudioFormat.channels, 
-			sd->outAudioFormat.sampleRate, -1.0,
-			sd->bitrate*1000, -1.0) )
-		{
-			ERROR("problem seting up vorbis encoder for shout\n");
-			vorbis_info_clear(&(sd->vi));
-			return -1;
-		}
-	}
-
-	vorbis_analysis_init(&(sd->vd), &(sd->vi));
-	vorbis_block_init (&(sd->vd), &(sd->vb));
-
-	ogg_stream_init(&(sd->os), rand());
-
-	vorbis_comment_init(&(sd->vc));
-
-	return 0;
-}
-
-static int myShout_openShoutConn(AudioOutput * audioOutput) {
-	ShoutData * sd = (ShoutData *)audioOutput->data;
-
-	if(shout_open(sd->shoutConn) != SHOUTERR_SUCCESS)
-	{
-		ERROR("problem opening connection to shout server: %s\n",
-				shout_get_error(sd->shoutConn));
-
-		return -1;
-	}
-
-	if(initEncoder(sd) < 0) {
-		shout_close(sd->shoutConn);
-		return -1;
-	}
-
-	copyTagToVorbisComment(sd);
-
-	vorbis_analysis_headerout(&(sd->vd), &(sd->vc), &(sd->header_main),
-			&(sd->header_comments), &(sd->header_codebooks));
-
-	ogg_stream_packetin(&(sd->os), &(sd->header_main));
-	ogg_stream_packetin(&(sd->os), &(sd->header_comments));
-	ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
-
-	sd->opened = 1;
-	sd->tagToSend = 0;
-
-	while(ogg_stream_flush(&(sd->os), &(sd->og)))
-	{
-		if(write_page(sd) < 0) {
-			myShout_closeShoutConn(sd);
-			return -1;
-		}
-	}
-
-	/*if(sd->tag) freeMpdTag(sd->tag);
-	sd->tag = NULL;*/
-
-	return 0;
-}
-
-static int myShout_openDevice(AudioOutput * audioOutput,
-		AudioFormat * audioFormat) 
-{
-	ShoutData * sd = (ShoutData *)audioOutput->data;
-
-	memcpy(&(sd->inAudioFormat), audioFormat, sizeof(AudioFormat));
-
-	if(0 == memcmp(&(sd->inAudioFormat), &(sd->outAudioFormat), 
-			sizeof(AudioFormat))) 
-	{
-		sd->audioFormatConvert = 0;
-	}
-	else sd->audioFormatConvert = 1;
-
-	audioOutput->open = 1;
-
-	if(sd->opened) return 0;
-
-	if(myShout_openShoutConn(audioOutput) < 0) {
-		audioOutput->open = 0;
-		return -1;
-	}
-
-	return 0;
-}
-
-static void myShout_convertAudioFormat(ShoutData * sd, char ** chunkArgPtr,
-		int * sizeArgPtr)
-{
-	int size = pcm_sizeOfOutputBufferForAudioFormatConversion(
-			&(sd->inAudioFormat), *sizeArgPtr, 
-			&(sd->outAudioFormat));
-
-	if(size > sd->convBufferLen) {
-		sd->convBuffer = realloc(sd->convBuffer, size);
-		sd->convBufferLen = size;
-	}
-
-	pcm_convertAudioFormat(&(sd->inAudioFormat), *chunkArgPtr, *sizeArgPtr,
-			&(sd->outAudioFormat), sd->convBuffer);
-	
-	*sizeArgPtr = size;
-	*chunkArgPtr = sd->convBuffer;
-}
-
-static void myShout_sendMetadata(ShoutData * sd) {
-	ogg_int64_t granulepos = sd->vd.granulepos;
-
-	if(!sd->opened || !sd->tag) return;
-
-	clearEncoder(sd);
-	if(initEncoder(sd) < 0) return;
-
-	sd->vd.granulepos = granulepos;
-
-	copyTagToVorbisComment(sd);
-
-	vorbis_analysis_headerout(&(sd->vd), &(sd->vc), &(sd->header_main),
-			&(sd->header_comments), &(sd->header_codebooks));
-
-	ogg_stream_packetin(&(sd->os), &(sd->header_main));
-	ogg_stream_packetin(&(sd->os), &(sd->header_comments));
-	ogg_stream_packetin(&(sd->os), &(sd->header_codebooks));
-
-	while(ogg_stream_flush(&(sd->os), &(sd->og)))
-	{
-		if(write_page(sd) < 0) {
-			myShout_closeShoutConn(sd);
-			return;
-		}
-	}
-
-	/*if(sd->tag) freeMpdTag(sd->tag);
-	sd->tag = NULL;*/
-	sd->tagToSend = 0;
-}
-
-static int myShout_play(AudioOutput * audioOutput, char * playChunk, int size) {
-	int i,j;
-	ShoutData * sd = (ShoutData *)audioOutput->data;
-	float ** vorbbuf;
-	int samples;
-	int bytes = sd->outAudioFormat.bits/8;
-
-	if(sd->opened && sd->tagToSend) myShout_sendMetadata(sd);
-
-	if(!sd->opened) {
-		if(myShout_openShoutConn(audioOutput) < 0) {
-			return -1;
-		}
-	}
-
-	if(sd->audioFormatConvert) {
-		myShout_convertAudioFormat(sd, &playChunk, &size);
-	}
-
-	samples = size/(bytes*sd->outAudioFormat.channels);
-
-	/* this is for only 16-bit audio */
-
-	vorbbuf = vorbis_analysis_buffer(&(sd->vd), samples);
-
-	for(i=0; i<samples; i++) {
-		for(j=0; j<sd->outAudioFormat.channels; j++) {
-			vorbbuf[j][i] = (*((mpd_sint16 *)playChunk)) / 32768.0;
-			playChunk += bytes;
-		}
-	}
-
-	vorbis_analysis_wrote(&(sd->vd), samples);
-
-	while(1 == vorbis_analysis_blockout(&(sd->vd), &(sd->vb))) {
-		vorbis_analysis(&(sd->vb), NULL);
-		vorbis_bitrate_addblock(&(sd->vb));
-
-		while(vorbis_bitrate_flushpacket(&(sd->vd), &(sd->op))) {
-			ogg_stream_packetin(&(sd->os), &(sd->op));
-		}
-	}
-
-
-	while(ogg_stream_pageout(&(sd->os), &(sd->og)) != 0) {
-		if(write_page(sd) < 0) {
-			myShout_closeShoutConn(sd);
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-static void myShout_setTag(AudioOutput * audioOutput, MpdTag * tag) {
-	ShoutData * sd = (ShoutData *)audioOutput->data;
-
-	if(sd->tag) freeMpdTag(sd->tag);
-	sd->tag = NULL;
-	sd->tagToSend = 0;
-
-	if(!tag) return;
-
-	sd->tag = mpdTagDup(tag);
-	sd->tagToSend = 1;
-}
-
-AudioOutputPlugin shoutPlugin = 
-{
-	"shout",
-	myShout_initDriver,
-	myShout_finishDriver,
-	myShout_openDevice,
-	myShout_play,
-	myShout_closeDevice,
-	myShout_setTag
-};
-
-#else
-
-#include <stdlib.h>
-
-AudioOutputPlugin shoutPlugin = 
-{
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL
-};
-
-#endif
-- 
cgit v1.2.3