-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 I tried the patch, and I ran in the following problem: When I enable the output "on the fly", mpd (sometimes) starts outputting this: * strange error flushing buffer ...
On occasion mpd crashes with a floating point exception. (only with mp3 output enabled). Q Eric Wollesen wrote: > Configuration options for "shout_mp3" are the same as for "shout". > --- > configure.ac | 33 ++ > m4/lame.m4 | 108 +++++ > src/Makefile.am | 1 + > src/audio.c | 1 + > src/audioOutput.h | 1 + > src/audioOutputs/audioOutput_shout_mp3.c | 676 > ++++++++++++++++++++++++++++++ > 6 files changed, 820 insertions(+), 0 deletions(-) > create mode 100644 m4/lame.m4 > create mode 100644 src/audioOutputs/audioOutput_shout_mp3.c > > diff --git a/configure.ac b/configure.ac > index 6067bc4..f7ecc6c 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -71,6 +71,8 @@ fi > > AC_ARG_ENABLE(ao,[ --enable-ao enable support for libao > (default: disable)],[enable_ao=$enableval],[enable_ao=no]) > AC_ARG_ENABLE(shout,[ --disable-shout disable support for streaming > through shout (default: enable)],[enable_shout=$enableval],[enable_shout=yes]) > +AC_ARG_ENABLE(shout_mp3,[ --disable-shout_mp3 disable support for > mp3 > +streaming through shout (default: > enable)],[enable_shout_mp3=$enableval],[enable_shout_mp3=yes]) > AC_ARG_ENABLE(iconv,[ --disable-iconv disable iconv support > (default: enable)],[enable_iconv=$enableval],[enable_iconv=yes]) > AC_ARG_ENABLE(ipv6,[ --disable-ipv6 disable IPv6 support (default: > enable)],[enable_ipv6=$enableval],[enable_ipv6=yes]) > AC_ARG_ENABLE(tcp,[ --disable-tcp disable support for clients > connecting via TCP (default: > enable)],[enable_tcp=$enableval],[enable_tcp=yes]) > @@ -85,6 +87,7 @@ AC_ARG_ENABLE(oggvorbis,[ --disable-oggvorbis disable > Ogg Vorbis support (d > AC_ARG_ENABLE(oggflac,[ --disable-oggflac disable OggFLAC support > (default: enable)],[enable_oggflac=$enableval],enable_oggflac=yes) > AC_ARG_ENABLE(flac,[ --disable-flac disable flac support (default: > enable)],[enable_flac=$enableval],[enable_flac=yes]) > AC_ARG_ENABLE(mp3,[ --disable-mp3 disable mp3 support (default: > enable)],[enable_mp3=$enableval],[enable_mp3=yes]) > +AC_ARG_ENABLE(lame,[ --disable-lame disable lame support > (default: enable)],[enable_lame=$enableval],[enable_lame=yes]) > AC_ARG_ENABLE(aac,[ --disable-aac disable AAC support (default: > enable)],[enable_aac=$enableval],[enable_aac=yes]) > AC_ARG_ENABLE(audiofile,[ --disable-audiofile disable audiofile > support, disables wave support (default: > enable)],[enable_audiofile=$enableval],[enable_audiofile=yes]) > AC_ARG_ENABLE(mod,[ --enable-mod enable MOD support (default: > disable)],[enable_mod=$enableval],[enable_mod=yes]) > @@ -193,6 +196,13 @@ if test x$enable_shout = xyes; then > fi > fi > > +if test x$enable_shout_mp3 = xyes; then > + if test x$enable_lame = xno; then > + AC_MSG_WARN([disabling shout_mp3 streaming support because > lame is not enabled]) > + enable_shout_mp3=no > + fi > +fi > + > if test x$enable_ao = xyes; then > XIPH_PATH_AO([AC_DEFINE(HAVE_AO, 1, [Define to play with ao]) > MPD_LIBS="$MPD_LIBS $AO_LIBS" MPD_CFLAGS="$MPD_CFLAGS $AO_CFLAGS"], > enable_ao=no) > fi > @@ -201,6 +211,10 @@ if test x$enable_shout = xyes; then > XIPH_PATH_SHOUT([AC_DEFINE(HAVE_SHOUT, 1, [Define to enable libshout > support]) MPD_LIBS="$MPD_LIBS $SHOUT_LIBS" MPD_CFLAGS="$MPD_CFLAGS > $SHOUT_CFLAGS"], enable_shout=no) > fi > > +if test x$enable_shout_mp3 = xyes; then > + XIPH_PATH_SHOUT([AC_DEFINE(HAVE_SHOUT_MP3, 1, [Define to enable mp3 > libshout support]) MPD_LIBS="$MPD_LIBS $SHOUT_LIBS" MPD_CFLAGS="$MPD_CFLAGS > $SHOUT_CFLAGS"], enable_shout_mp3=no) > +fi > + > 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 > @@ -354,6 +368,12 @@ if test x$enable_mp3 = xyes; then > fi > fi > > +if test x$enable_lame = xyes; then > + AM_PATH_LAME([MPD_LIBS="$MPD_LIBS $LAME_LIBS" > MPD_CFLAGS="$MPD_CFLAGS $LAME_CFLAGS"], > + [enable_lame=no;AC_MSG_WARN(You need lame -- disabling lame > support)]) > +fi > + > + > if test x$enable_mpc = xyes; then > if test "x$mpcdec_libraries" != "x" ; then > MPCDEC_LIBS="-L$mpcdec_libraries" > @@ -743,11 +763,18 @@ else > echo " Shout streaming support .......disabled" > fi > > +if test x$enable_shout_mp3 = xyes; then > + echo " Shout mp3 streaming support ...enabled" > +else > + echo " Shout mp3 streaming support ...disabled" > +fi > + > echo "" > > if test x$enable_ao = xno && > test x$enable_oss = xno && > test x$enable_shout = xno && > + test x$enable_shout_mp3 = xno && > test x$enable_alsa = xno && > test x$enable_osx = xno && > test x$enable_pulse = xno && > @@ -771,6 +798,12 @@ else > echo " mp3 support ...................disabled" > fi > > +if test x$enable_lame = xyes; then > + echo " lame support ..................enabled" > +else > + echo " lame support ..................disabled" > +fi > + > if test x$enable_oggvorbis = xyes; then > echo " Ogg Vorbis support ............enabled" > if test x$use_tremor = xyes; then > diff --git a/m4/lame.m4 b/m4/lame.m4 > new file mode 100644 > index 0000000..5ebf550 > --- /dev/null > +++ b/m4/lame.m4 > @@ -0,0 +1,108 @@ > +dnl borrowed from oddsock.org > +dnl AM_PATH_LAME([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) > +dnl Test for liblame, and define LAME_CFLAGS and LAME_LIBS > +dnl > +AC_DEFUN([AM_PATH_LAME], > +[dnl > +dnl Get the cflags and libraries > +dnl > +AC_ARG_WITH(lame,[ --with-lame=PFX Prefix where liblame is installed > (optional)], lame_prefix="$withval", lame_prefix="") > +AC_ARG_WITH(lame-libraries,[ --with-lame-libraries=DIR Directory where > liblame library is installed (optional)], lame_libraries="$withval", > lame_libraries="") > +AC_ARG_WITH(lame-includes,[ --with-lame-includes=DIR Directory where > liblame header files are installed (optional)], lame_includes="$withval", > lame_includes="") > +AC_ARG_ENABLE(lametest, [ --disable-lametest Do not try to compile > and run a test liblame program],, enable_lametest=yes) > + > +if test "x$lame_prefix" != "xno" ; then > + > + if test "x$lame_libraries" != "x" ; then > + LAME_LIBS="-L$lame_libraries" > + elif test "x$lame_prefix" != "x" ; then > + LAME_LIBS="-L$lame_prefix/lib" > + elif test "x$prefix" != "xNONE" ; then > + LAME_LIBS="-L$prefix/lib" > + fi > + > + LAME_LIBS="$LAME_LIBS -lmp3lame -lm" > + > + if test "x$lame_includes" != "x" ; then > + LAME_CFLAGS="-I$lame_includes" > + elif test "x$lame_prefix" != "x" ; then > + LAME_CFLAGS="-I$lame_prefix/include" > + elif test "x$prefix" != "xNONE"; then > + LAME_CFLAGS="-I$prefix/include" > + fi > + > + AC_MSG_CHECKING(for liblame) > + no_lame="" > + > + > + if test "x$enable_lametest" = "xyes" ; then > + ac_save_CFLAGS="$CFLAGS" > + ac_save_LIBS="$LIBS" > + CFLAGS="$CFLAGS $LAME_CFLAGS" > + LIBS="$LIBS $LAME_LIBS" > +dnl > +dnl Now check if the installed liblame is sufficiently new. > +dnl > + rm -f conf.lametest > + AC_TRY_RUN([ > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <lame/lame.h> > + > +int main () > +{ > + system("touch conf.lametest"); > + return 0; > +} > + > +],, no_lame=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) > + CFLAGS="$ac_save_CFLAGS" > + LIBS="$ac_save_LIBS" > + fi > + > + if test "x$no_lame" = "x" ; then > + AC_MSG_RESULT(yes) > + ifelse([$1], , :, [$1]) > + else > + AC_MSG_RESULT(no) > + if test -f conf.lametest ; then > + : > + else > + echo "*** Could not run liblame test program, checking why..." > + CFLAGS="$CFLAGS $LAME_CFLAGS" > + LIBS="$LIBS $LAME_LIBS" > + AC_TRY_LINK([ > +#include <stdio.h> > +#include <lame/lame.h> > +], [ return 0; ], > + [ echo "*** The test program compiled, but did not run. This usually > means" > + echo "*** that the run-time linker is not finding liblame or finding > the wrong" > + echo "*** version of liblame. If it is not finding liblame, you'll > need to set your" > + echo "*** LD_LIBRARY_PATH environment variable, or edit > /etc/ld.so.conf to point" > + echo "*** to the installed location Also, make sure you have run > ldconfig if that" > + echo "*** is required on your system" > + echo "***" > + echo "*** If you have an old version installed, it is best to remove > it, although" > + echo "*** you may also be able to get things to work by modifying > LD_LIBRARY_PATH"], > + [ echo "*** The test program failed to compile or link. See the file > config.log for the" > + echo "*** exact error that occured. This usually means liblame was > incorrectly installed" > + echo "*** or that you have moved liblame since it was installed." ]) > + CFLAGS="$ac_save_CFLAGS" > + LIBS="$ac_save_LIBS" > + fi > + LAME_CFLAGS="" > + LAME_LIBS="" > + ifelse([$2], , :, [$2]) > + fi > + AC_DEFINE(HAVE_LAME, 1, [Define if you have liblame.]) > + use_lame="1" > +else > + LAME_CFLAGS="" > + LAME_LIBS="" > +fi > + AC_SUBST(LAME_CFLAGS) > + AC_SUBST(LAME_LIBS) > + rm -f conf.lametest > +]) > + > diff --git a/src/Makefile.am b/src/Makefile.am > index 6c52dde..512e84e 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -3,6 +3,7 @@ SUBDIRS = $(MP4FF_SUBDIR) > > mpd_audioOutputs = \ > audioOutputs/audioOutput_shout.c \ > + audioOutputs/audioOutput_shout_mp3.c \ > audioOutputs/audioOutput_null.c \ > audioOutputs/audioOutput_fifo.c \ > audioOutputs/audioOutput_alsa.c \ > diff --git a/src/audio.c b/src/audio.c > index 34b74e6..3334ff0 100644 > --- a/src/audio.c > +++ b/src/audio.c > @@ -85,6 +85,7 @@ void loadAudioDrivers(void) > { > initAudioOutputPlugins(); > loadAudioOutputPlugin(&shoutPlugin); > + loadAudioOutputPlugin(&shoutMp3Plugin); > loadAudioOutputPlugin(&nullPlugin); > loadAudioOutputPlugin(&fifoPlugin); > loadAudioOutputPlugin(&alsaPlugin); > diff --git a/src/audioOutput.h b/src/audioOutput.h > index 7574f5a..b98334e 100644 > --- a/src/audioOutput.h > +++ b/src/audioOutput.h > @@ -109,6 +109,7 @@ void sendMetadataToAudioOutput(AudioOutput * audioOutput, > MpdTag * tag); > void printAllOutputPluginTypes(FILE * fp); > > extern AudioOutputPlugin shoutPlugin; > +extern AudioOutputPlugin shoutMp3Plugin; > extern AudioOutputPlugin nullPlugin; > extern AudioOutputPlugin fifoPlugin; > extern AudioOutputPlugin alsaPlugin; > diff --git a/src/audioOutputs/audioOutput_shout_mp3.c > b/src/audioOutputs/audioOutput_shout_mp3.c > new file mode 100644 > index 0000000..da76a21 > --- /dev/null > +++ b/src/audioOutputs/audioOutput_shout_mp3.c > @@ -0,0 +1,676 @@ > +/* the Music Player Daemon (MPD) > + * Copyright (C) 2003-2007 by Warren Dukes ([EMAIL PROTECTED]) > + * 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" > + > +#ifdef HAVE_SHOUT_MP3 > + > +#include "../conf.h" > +#include "../log.h" > +#include "../pcm_utils.h" > +#include "../timer.h" > + > +#include <shout/shout.h> > +#include <lame/lame.h> > + > +#define CONN_ATTEMPT_INTERVAL 60 > +#define DEFAULT_CONN_TIMEOUT 2 > +#define MP3_BUF_SIZE 1048576 > + > +static int shoutInitCount; > + > +/* lots of this code blatantly stolent from bossogg/bossao2 */ > + > +typedef struct _ShoutData { > + shout_t *shoutConn; > + int shoutError; > + shout_metadata_t *shoutMeta; > + > + lame_global_flags *gfp; > + unsigned char mp3buf[MP3_BUF_SIZE]; > + unsigned int mp3buf_full; > + > + float quality; > + int bitrate; > + > + int opened; > + > + MpdTag *tag; > + int tagToSend; > + > + int timeout; > + int connAttempts; > + time_t lastAttempt; > + > + Timer *timer; > + > + /* just a pointer to audioOutput->outAudioFormat */ > + AudioFormat *audioFormat; > +} ShoutData; > + > +static ShoutData *newShoutData(void) > +{ > + ShoutData *ret = xmalloc(sizeof(ShoutData)); > + > + ret->shoutConn = shout_new(); > + ret->shoutMeta = shout_metadata_new(); > + ret->opened = 0; > + ret->tag = NULL; > + ret->tagToSend = 0; > + ret->bitrate = -1; > + ret->quality = -2.0; > + ret->timeout = DEFAULT_CONN_TIMEOUT; > + ret->connAttempts = 0; > + ret->lastAttempt = 0; > + ret->audioFormat = NULL; > + ret->timer = NULL; > + > + return ret; > +} > + > +static void freeShoutData(ShoutData * sd) > +{ > + if (sd->shoutMeta) > + shout_metadata_free(sd->shoutMeta); > + if (sd->shoutConn) > + shout_free(sd->shoutConn); > + if (sd->tag) > + freeMpdTag(sd->tag); > + if (sd->timer) > + timer_free(sd->timer); > + > + free(sd); > +} > + > +#define checkBlockParam(name) { \ > + blockParam = getBlockParam(param, name); \ > + if (!blockParam) { \ > + FATAL("no \"%s\" defined for shout_mp3 device defined at > line " \ > + "%i\n", name, param->line); \ > + } \ > +} > + > +static int myShoutMp3_initDriver(AudioOutput * audioOutput, > + ConfigParam * param) > +{ > + ShoutData *sd; > + char *test; > + int port; > + char *host; > + char *mount; > + char *passwd; > + const char *user; > + char *name; > + BlockParam *blockParam; > + int public; > + > + 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) { > + FATAL("shout port \"%s\" is not a positive integer, line > %i\n", > + blockParam->value, blockParam->line); > + } > + > + checkBlockParam("password"); > + passwd = blockParam->value; > + > + checkBlockParam("name"); > + name = blockParam->value; > + > + public = getBoolBlockParam(param, "public", 1); > + if (public == CONF_BOOL_UNSET) > + public = 0; > + > + blockParam = getBlockParam(param, "user"); > + if (blockParam) > + user = blockParam->value; > + else > + user = "source"; > + > + blockParam = getBlockParam(param, "quality"); > + > + if (blockParam) { > + int line = blockParam->line; > + > + sd->quality = strtod(blockParam->value, &test); > + > + if (*test != '\0' || sd->quality < -1.0 || sd->quality > > 10.0) { > + FATAL("shout quality \"%s\" is not a number in the " > + "range -1 to 10, line %i\n", blockParam->value, > + blockParam->line); > + } > + > + blockParam = getBlockParam(param, "bitrate"); > + > + if (blockParam) { > + FATAL("quality (line %i) and bitrate (line %i) are " > + "both defined for shout_mp3 output\n", line, > + blockParam->line); > + } > + } else { > + blockParam = getBlockParam(param, "bitrate"); > + > + if (!blockParam) { > + FATAL("neither bitrate nor quality defined for > shout_mp3 " > + "output at line %i\n", param->line); > + } > + > + sd->bitrate = strtol(blockParam->value, &test, 10); > + > + if (*test != '\0' || sd->bitrate <= 0) { > + FATAL("bitrate at line %i should be a positive > integer " > + "\n", blockParam->line); > + } > + } > + > + checkBlockParam("format"); > + sd->audioFormat = &audioOutput->outAudioFormat; > + > + 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_public(sd->shoutConn, public) != SHOUTERR_SUCCESS || > + shout_set_nonblocking(sd->shoutConn, 1) != SHOUTERR_SUCCESS || > + shout_set_format(sd->shoutConn, SHOUT_FORMAT_MP3) > + != SHOUTERR_SUCCESS || > + shout_set_protocol(sd->shoutConn, SHOUT_PROTOCOL_HTTP) > + != SHOUTERR_SUCCESS || > + shout_set_agent(sd->shoutConn, "MPD") != SHOUTERR_SUCCESS) { > + FATAL("error configuring shout_mp3 defined at line %i: %s\n", > + param->line, shout_get_error(sd->shoutConn)); > + } > + > + /* optional paramters */ > + blockParam = getBlockParam(param, "timeout"); > + if (blockParam) { > + sd->timeout = (int)strtol(blockParam->value, &test, 10); > + if (*test != '\0' || sd->timeout <= 0) { > + FATAL("shout timeout is not a positive integer, " > + "line %i\n", blockParam->line); > + } > + } > + > + blockParam = getBlockParam(param, "genre"); > + if (blockParam && shout_set_genre(sd->shoutConn, blockParam->value)) > { > + FATAL("error configuring shout_mp3 defined at line %i: %s\n", > + param->line, shout_get_error(sd->shoutConn)); > + } > + > + blockParam = getBlockParam(param, "description"); > + if (blockParam && shout_set_description(sd->shoutConn, > + blockParam->value)) { > + FATAL("error configuring shout_mp3 defined at line %i: %s\n", > + param->line, shout_get_error(sd->shoutConn)); > + } > + > + { > + char temp[11]; > + memset(temp, 0, sizeof(temp)); > + > + snprintf(temp, sizeof(temp), "%d", > sd->audioFormat->channels); > + shout_set_audio_info(sd->shoutConn, SHOUT_AI_CHANNELS, temp); > + > + snprintf(temp, sizeof(temp), "%d", > sd->audioFormat->sampleRate); > + > + shout_set_audio_info(sd->shoutConn, SHOUT_AI_SAMPLERATE, > temp); > + > + if (sd->quality >= -1.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 int myShoutMp3_handleError(ShoutData * sd, int err) > +{ > + switch (err) { > + case SHOUTERR_SUCCESS: > + break; > + case SHOUTERR_UNCONNECTED: > + case SHOUTERR_SOCKET: > + ERROR("Lost shout_mp3 connection to %s:%i: %s\n", > + shout_get_host(sd->shoutConn), > + shout_get_port(sd->shoutConn), > + shout_get_error(sd->shoutConn)); > + sd->shoutError = 1; > + return -1; > + default: > + ERROR("shout_mp3: connection to %s:%i error: %s\n", > + shout_get_host(sd->shoutConn), > + shout_get_port(sd->shoutConn), > + shout_get_error(sd->shoutConn)); > + sd->shoutError = 1; > + return -1; > + } > + > + return 0; > +} > + > +static int write_page(ShoutData * sd) > +{ > + int err; > + > + shout_sync(sd->shoutConn); > + err = shout_send(sd->shoutConn, sd->mp3buf, sd->mp3buf_full); > + if (myShoutMp3_handleError(sd, err) < 0) > + return -1; > + > + return 0; > +} > + > +static void finishEncoder(ShoutData * sd) > +{ > + /* Does lame require anything to be done here? */ > +} > + > +static int flushEncoder(ShoutData * sd) > +{ > + /* Does lame require anything to be done here? */ > + return 0; > +} > + > +static void clearEncoder(ShoutData * sd) > +{ > + finishEncoder(sd); > + while (1 == flushEncoder(sd)) { > + if (!sd->shoutError) > + write_page(sd); > + } > + > + lame_close(sd->gfp); > +} > + > +static void myShoutMp3_closeShoutConn(ShoutData * sd) > +{ > + if (sd->opened) > + clearEncoder(sd); > + > + if (shout_get_connected(sd->shoutConn) != SHOUTERR_UNCONNECTED && > + shout_close(sd->shoutConn) != SHOUTERR_SUCCESS) { > + ERROR("problem closing connection to shout_mp3 server: %s\n", > + shout_get_error(sd->shoutConn)); > + } > + > + sd->opened = 0; > +} > + > +static void myShoutMp3_finishDriver(AudioOutput * audioOutput) > +{ > + ShoutData *sd = (ShoutData *) audioOutput->data; > + > + myShoutMp3_closeShoutConn(sd); > + > + freeShoutData(sd); > + > + shoutInitCount--; > + > + if (shoutInitCount == 0) > + shout_shutdown(); > +} > + > +static void myShoutMp3_dropBufferedAudio(AudioOutput * audioOutput) > +{ > + ShoutData *sd = (ShoutData *)audioOutput->data; > + timer_reset(sd->timer); > + > + /* needs to be implemented for shout_mp3 */ > +} > + > +static void myShoutMp3_closeDevice(AudioOutput * audioOutput) > +{ > + ShoutData *sd = (ShoutData *) audioOutput->data; > + > + myShoutMp3_closeShoutConn(sd); > + > + if (sd->timer) { > + timer_free(sd->timer); > + sd->timer = NULL; > + } > + > + audioOutput->open = 0; > +} > + > +static void sendMetadata(ShoutData *sd) > +{ > + const size_t tag_size = 1024; > + char song[tag_size]; > + char artist[tag_size]; > + char title[tag_size]; > + > + if (sd->tag) { > + int i; > + > + for (i = 0; i < sd->tag->numOfItems; i++) { > + switch (sd->tag->items[i].type) { > + case TAG_ITEM_ARTIST: > + snprintf(artist, tag_size, "%s", > + sd->tag->items[i].value); > + break; > + case TAG_ITEM_TITLE: > + snprintf(title, tag_size, "%s", > + sd->tag->items[i].value); > + break; > + } > + } > + snprintf(song, tag_size, "%s - %s", title, artist); > + shout_metadata_add(sd->shoutMeta, "song", song); > + if (SHOUTERR_SUCCESS != shout_set_metadata(sd->shoutConn, > + sd->shoutMeta)) { > + ERROR("error setting shout_mp3 metadata\n"); > + return; > + } > + } > +} > + > +static int initEncoder(ShoutData * sd) > +{ > + if (NULL == (sd->gfp = lame_init())) { > + ERROR("problem setting up lame encoder for shout_mp3\n"); > + lame_close(sd->gfp); /* necessary? */ > + return -1; > + } > + > + if (sd->quality >= -1.0) { > + if (0 != lame_set_VBR(sd->gfp, vbr_rh)) { > + ERROR("problem setting up lame encoder for > shout_mp3\n"); > + lame_close(sd->gfp); > + return -1; > + } > + if (0 != lame_set_VBR_q(sd->gfp, sd->quality)) { > + ERROR("problem setting up lame encoder for > shout_mp3\n"); > + lame_close(sd->gfp); > + return -1; > + } > + } else { > + if (0 != lame_set_brate(sd->gfp, sd->bitrate)) { > + ERROR("problem setting up lame encoder for > shout_mp3\n"); > + lame_close(sd->gfp); > + return -1; > + } > + } > + > + if (0 != lame_set_num_channels(sd->gfp, > + sd->audioFormat->channels)) { > + ERROR("problem setting up lame encoder for shout\n"); > + lame_close(sd->gfp); > + return -1; > + } > + > + if (0 != lame_set_in_samplerate(sd->gfp, > + sd->audioFormat->sampleRate)) { > + ERROR("problem setting up lame encoder for shout\n"); > + lame_close(sd->gfp); > + return -1; > + } > + > + if (0 > lame_init_params(sd->gfp)) { > + ERROR("problem setting up lame encoder for shout\n"); > + lame_close(sd->gfp); > + return -1; > + } > + > + return 0; > +} > + > +static int myShoutMp3_connect(ShoutData *sd) > +{ > + time_t t = time(NULL); > + int state = shout_get_connected(sd->shoutConn); > + > + /* already connected */ > + if (state == SHOUTERR_CONNECTED) > + return 0; > + > + /* waiting to connect */ > + if (state == SHOUTERR_BUSY && sd->connAttempts != 0) { > + /* timeout waiting to connect */ > + if ((t - sd->lastAttempt) > sd->timeout) { > + ERROR("timeout connecting to shout server %s:%i " > + "(attempt %i)\n", > + shout_get_host(sd->shoutConn), > + shout_get_port(sd->shoutConn), > + sd->connAttempts); > + return -1; > + } > + > + return 1; > + } > + > + /* we're in some funky state, so just reset it to unconnected */ > + if (state != SHOUTERR_UNCONNECTED) > + shout_close(sd->shoutConn); > + > + /* throttle new connection attempts */ > + if (sd->connAttempts != 0 && > + (t - sd->lastAttempt) <= CONN_ATTEMPT_INTERVAL) { > + return -1; > + } > + > + /* initiate a new connection */ > + > + sd->connAttempts++; > + sd->lastAttempt = t; > + > + state = shout_open(sd->shoutConn); > + switch (state) { > + case SHOUTERR_SUCCESS: > + case SHOUTERR_CONNECTED: > + return 0; > + case SHOUTERR_BUSY: > + return 1; > + default: > + ERROR("problem opening connection to shout server %s:%i " > + "(attempt %i): %s\n", > + shout_get_host(sd->shoutConn), > + shout_get_port(sd->shoutConn), > + sd->connAttempts, shout_get_error(sd->shoutConn)); > + return -1; > + } > +} > + > +static int myShoutMp3_openShoutConn(AudioOutput * audioOutput) > +{ > + ShoutData *sd = (ShoutData *) audioOutput->data; > + int status; > + > + status = myShoutMp3_connect(sd); > + if (status != 0) > + return status; > + > + if (initEncoder(sd) < 0) { > + shout_close(sd->shoutConn); > + return -1; > + } > + > + sd->shoutError = 0; > + > + sendMetadata(sd); > + > + sd->opened = 1; > + sd->tagToSend = 0; > + > + while (lame_encode_flush(sd->gfp, sd->mp3buf, sd->mp3buf_full)) { > + if (write_page(sd) < 0) { > + myShoutMp3_closeShoutConn(sd); > + return -1; > + } > + } > + > + sd->connAttempts = 0; > + > + return 0; > +} > + > +static int myShoutMp3_openDevice(AudioOutput * audioOutput) > +{ > + ShoutData *sd = (ShoutData *) audioOutput->data; > + > + if (!sd->opened && myShoutMp3_openShoutConn(audioOutput) < 0) > + return -1; > + > + if (sd->timer) > + timer_free(sd->timer); > + > + sd->timer = timer_new(&audioOutput->outAudioFormat); > + > + audioOutput->open = 1; > + > + return 0; > +} > + > +static void myShoutMp3_sendMetadata(ShoutData * sd) > +{ > + if (!sd->opened || !sd->tag) > + return; > + > + clearEncoder(sd); > + if (initEncoder(sd) < 0) > + return; > + > + sendMetadata(sd); > + > + while (lame_encode_flush(sd->gfp, sd->mp3buf, sd->mp3buf_full)) { > + if (write_page(sd) < 0) { > + myShoutMp3_closeShoutConn(sd); > + return -1; > + } > + } > + > + /*if(sd->tag) freeMpdTag(sd->tag); > + sd->tag = NULL; */ > + sd->tagToSend = 0; > +} > + > +static int myShoutMp3_play(AudioOutput * audioOutput, > + const char *playChunk, size_t size) > +{ > + unsigned int i; > + int j; > + ShoutData *sd = (ShoutData *) audioOutput->data; > + float lamebuf[2][50000]; /* make me dynamic? */ > + unsigned int samples; > + int bytes = sd->audioFormat->bits / 8; > + int status; > + > + if (!sd->timer->started) > + timer_start(sd->timer); > + > + timer_add(sd->timer, size); > + > + if (sd->opened && sd->tagToSend) > + myShoutMp3_sendMetadata(sd); > + > + if (!sd->opened) { > + status = myShoutMp3_openShoutConn(audioOutput); > + if (status < 0) { > + myShoutMp3_closeDevice(audioOutput); > + return -1; > + } else if (status > 0) { > + timer_sync(sd->timer); > + return 0; > + } > + } > + > + samples = size / (bytes * sd->audioFormat->channels); > + > + /* this is for only 16-bit audio */ > + > + for (i = 0; i < samples; i++) { > + for (j = 0; j < sd->audioFormat->channels; j++) { > + lamebuf[j][i] = (*((mpd_sint16 *) playChunk)); > + playChunk += bytes; > + } > + } > + > + if (0 > (sd->mp3buf_full = > + (lame_encode_buffer_float(sd->gfp, lamebuf[0], lamebuf[1], > + samples, sd->mp3buf, > + MP3_BUF_SIZE)))) { > + ERROR("problem encoding lame buffer for shout_mp3\n"); > + lame_close(sd->gfp); > + myShoutMp3_closeShoutConn(sd); > + return -1; > + } > + > + if (write_page(sd) < 0) { > + myShoutMp3_closeDevice(audioOutput); > + return -1; > + } > + > + return 0; > +} > + > +static void myShoutMp3_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 shoutMp3Plugin = { > + "shout_mp3", > + NULL, > + myShoutMp3_initDriver, > + myShoutMp3_finishDriver, > + myShoutMp3_openDevice, > + myShoutMp3_play, > + myShoutMp3_dropBufferedAudio, > + myShoutMp3_closeDevice, > + myShoutMp3_setTag, > +}; > + > +#else > + > +DISABLED_AUDIO_OUTPUT_PLUGIN(shoutMp3Plugin) > +#endif -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.9 (GNU/Linux) Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org iEYEARECAAYFAkizz24ACgkQ14/fqnT0YgOzuwCgiZphN8LZqPKKDUePZ5tnlcxn /MMAn2LjEG5V1LQezG55DFs/eIhfG4PE =PSgQ -----END PGP SIGNATURE----- ------------------------------------------------------------------------- This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world http://moblin-contest.org/redirect.php?banner_id=100&url=/ _______________________________________________ Musicpd-dev-team mailing list Musicpd-dev-team@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team