diff options
-rw-r--r-- | configure.ac | 16 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/audio.c | 2 | ||||
-rw-r--r-- | src/audioOutputs/audioOutput_jack.c | 380 | ||||
-rw-r--r-- | src/inputPlugins/aac_plugin.c | 4 | ||||
-rw-r--r-- | src/inputPlugins/mp4_plugin.c | 2 |
6 files changed, 403 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac index 5c363baaa..3242d8b97 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,7 @@ AC_ARG_ENABLE(ipv6,[ --disable-ipv6 disable IPv6 support (default: ena 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(jack,[ --disable-jack disable jack support (default: enable)],[enable_jack=$enableval],[enable_jack=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) @@ -190,6 +191,14 @@ if test x$enable_alsa = xyes; then AM_PATH_ALSA(0.9.0,[AC_DEFINE(HAVE_ALSA,1,[Define to enable ALSA support]) MPD_LIBS="$MPD_LIBS $ALSA_LIBS" MPD_CFLAGS="$MPD_CFLAGS $ALSA_CFLAGS"],enable_alsa=no) fi +if test "x$enable_jack" = "xyes"; then + PKG_CHECK_MODULES(JACK, [jack >= 0.4], + [AC_DEFINE(HAVE_JACK,1,[Define to enable JACK support]) + MPD_LIBS="$MPD_LIBS $JACK_LIBS" + MPD_CFLAGS="$MPD_CFLAGS $JACK_CFLAGS"], + [enable_jack=no]) +fi + if test x$enable_iconv = xyes; then if test "x$iconv_libraries" != "x" ; then ICONV_LIBS="-L$iconv_libraries" @@ -601,6 +610,12 @@ else echo " ALSA support ..................disabled" fi +if test x$enable_jack = xyes; then + echo " JACK support ..................enabled" +else + echo " JACK support ..................disabled" +fi + if test x$enable_sun = xyes; then echo " Sun support ...................enabled" else @@ -640,6 +655,7 @@ if test x$enable_ao = xno && test x$enable_alsa = xno && test x$enable_osx = xno && test x$enable_pulse = xno && + test x$enable_jack = xno && test x$enable_mvp = xno; then AC_MSG_ERROR([No Audio Output types configured!]) fi diff --git a/src/Makefile.am b/src/Makefile.am index e7f301703..082e33eb0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,8 @@ mpd_audioOutputs = \ audioOutputs/audioOutput_osx.c \ audioOutputs/audioOutput_pulse.c \ audioOutputs/audioOutput_mvp.c \ - audioOutputs/audioOutput_shout.c + audioOutputs/audioOutput_shout.c \ + audioOutputs/audioOutput_jack.c mpd_inputPlugins = \ inputPlugins/_flac_common.c \ diff --git a/src/audio.c b/src/audio.c index ef77d7e28..45067e27c 100644 --- a/src/audio.c +++ b/src/audio.c @@ -96,6 +96,7 @@ extern AudioOutputPlugin osxPlugin; extern AudioOutputPlugin pulsePlugin; extern AudioOutputPlugin mvpPlugin; extern AudioOutputPlugin shoutPlugin; +extern AudioOutputPlugin jackPlugin; void loadAudioDrivers(void) { @@ -107,6 +108,7 @@ void loadAudioDrivers(void) loadAudioOutputPlugin(&pulsePlugin); loadAudioOutputPlugin(&mvpPlugin); loadAudioOutputPlugin(&shoutPlugin); + loadAudioOutputPlugin(&jackPlugin); } /* make sure initPlayerData is called before this function!! */ diff --git a/src/audioOutputs/audioOutput_jack.c b/src/audioOutputs/audioOutput_jack.c new file mode 100644 index 000000000..8f67cfcdf --- /dev/null +++ b/src/audioOutputs/audioOutput_jack.c @@ -0,0 +1,380 @@ +/* jack plug in for the Music Player Daemon (MPD) + * (c)2006 by anarch(anarchsss@gmail.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" + +#ifdef HAVE_JACK + +#include <stdlib.h> +#include <errno.h> + +#include "../conf.h" +#include "../log.h" +#include "../sig_handlers.h" + +#include <sys/types.h> +#include <string.h> +#include <assert.h> +#include <signal.h> +#include <unistd.h> + +#include <jack/jack.h> +#include <jack/types.h> +#include <jack/ringbuffer.h> + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +int ringbuf_sz = 32768; +char *ports[2] = {NULL, NULL}; + +typedef struct _JackData { + jack_options_t options; + jack_port_t *ports[2]; + jack_client_t *client; + jack_default_audio_sample_t **in; + jack_ringbuffer_t *ringbuffer[2]; + int bps; + int shutdown; + int nports; + int our_xrun; +} JackData; + +JackData *jd = NULL; + +static JackData *newJackData (void) +{ + JackData *ret; + + ret = calloc (sizeof (JackData), 1); + ret->options = JackNullOption; + + return ret; +} + +static void disconnect_jack (JackData *jd) +{ + jack_deactivate (jd->client); + jack_client_close (jd->client); + + ERROR ("disconnect_jack (pid=%d)\n", getpid ()); +} + +static void jack_finishDriver (AudioOutput * audioOutput) +{ + JackData *jd = audioOutput->data; + + disconnect_jack (jd); + + free (jd); + free (ports[0]); + free (ports[1]); +} + +static int srate (jack_nframes_t rate, void *data) +{ + JackData *jd = (JackData *) ((AudioOutput*) data)->data; + AudioFormat *audioFormat = &(((AudioOutput*) data)->outAudioFormat); + + audioFormat->sampleRate = (int) jack_get_sample_rate (jd->client); + + return 0; +} + +static int process (jack_nframes_t nframes, void *arg) +{ + size_t i; + JackData *jd = (JackData *) arg; + jack_default_audio_sample_t *out[2]; + size_t avail_data, avail_frames; + + if ( nframes <= 0 ) + return 0; + + out[0] = jack_port_get_buffer (jd->ports[0], nframes); + out[1] = jack_port_get_buffer (jd->ports[1], nframes); + + avail_data = jack_ringbuffer_read_space (jd->ringbuffer[1]); + + if ( avail_data > 0 ) { + avail_frames = avail_data / sizeof (jack_default_audio_sample_t); + if (avail_frames > nframes) { + avail_frames = nframes; + avail_data = nframes * sizeof (jack_default_audio_sample_t); + } + jack_ringbuffer_read (jd->ringbuffer[0], (char *)out[0], avail_data); + jack_ringbuffer_read (jd->ringbuffer[1], (char *)out[1], avail_data); + + if (avail_frames < nframes) { + jd->our_xrun = 1; + for (i = avail_frames; i < nframes; i++) { + out[0][i] = out[1][i] = 0.0; + } + } + } else { + //ERROR ("avail_data=%d, no play (pid=%d)!\n", avail_data, getpid ()); + + for (i = 0; i < nframes; i++) { + out[0][i] = 0.0; + out[1][i] = 0.0; + } + } + + return 0; +} + +static void shutdown_callback (void *arg) +{ + JackData *jd = (JackData *) arg; + jd->shutdown = 1; +} + +static void set_audioformat (AudioOutput *audioOutput) +{ + JackData *jd = audioOutput->data; + AudioFormat *audioFormat = &audioOutput->outAudioFormat; + + audioFormat->sampleRate = (int) jack_get_sample_rate (jd->client); + ERROR ("samplerate = %d\n", audioFormat->sampleRate); + jd->nports = audioFormat->channels = 2; + audioFormat->bits = 16; + jd->bps = audioFormat->channels * + audioFormat->channels * + audioFormat->sampleRate; +} + +static int connect_jack (AudioOutput *audioOutput) +{ + JackData *jd = audioOutput->data; + const char **jports; + + if ( (jd->client = jack_client_new ("mpd")) == NULL ) { + ERROR ("jack server not running?\n"); + return -1; + } + + jd->ringbuffer[0] = jack_ringbuffer_create (ringbuf_sz); + jd->ringbuffer[1] = jack_ringbuffer_create (ringbuf_sz); + + jack_set_process_callback (jd->client, process, (void *)jd); + jack_set_sample_rate_callback (jd->client, (JackProcessCallback)srate, + (void *)audioOutput); + jack_on_shutdown (jd->client, shutdown_callback, (void *)jd); + + if ( jack_activate (jd->client) ) { + ERROR ("cannot activate client"); + jack_ringbuffer_free (jd->ringbuffer[0]); + jack_ringbuffer_free (jd->ringbuffer[1]); + return -1; + } + + jd->ports[0] = jack_port_register (jd->client, "left", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if ( !jd->ports[0] ) { + ERROR ("Cannot register output port.\n"); + jack_ringbuffer_free (jd->ringbuffer[0]); + jack_ringbuffer_free (jd->ringbuffer[1]); + return -1; + } + + jd->ports[1] = jack_port_register (jd->client, "right", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if ( !jd->ports[1] ) { + ERROR ("Cannot register output port.\n"); + jack_ringbuffer_free (jd->ringbuffer[0]); + jack_ringbuffer_free (jd->ringbuffer[1]); + return -1; + } + + memset (jd->ringbuffer[0]->buf, 0, jd->ringbuffer[0]->size); + memset (jd->ringbuffer[1]->buf, 0, jd->ringbuffer[1]->size); + + /* hay que buscar que hay */ + if ( !ports[1] && (jports = jack_get_ports (jd->client, NULL, NULL, + JackPortIsPhysical| + JackPortIsInput)) ) { + ports[0] = (char *) jports[0]; + ports[1] = (char *) ( jports[1] ? jports[1] : jports[0] ); + ERROR ("jports: %s %s\n", ports[0], ports[1]); + free (jports); + } + + if ( ports[1] ) { + if ( (jack_connect (jd->client, "mpd:left", ports[0])) != 0 ) { + ERROR ("%s is not a valid Jack Client / Port ", ports[0]); + jack_ringbuffer_free (jd->ringbuffer[0]); + jack_ringbuffer_free (jd->ringbuffer[1]); + return -1; + } + if ( (jack_connect (jd->client, "mpd:right", ports[1])) != 0 ) { + ERROR ("%s is not a valid Jack Client / Port ", ports[1]); + jack_ringbuffer_free (jd->ringbuffer[0]); + jack_ringbuffer_free (jd->ringbuffer[1]); + return -1; + } + } + + ERROR ("connect_jack (pid=%d)\n", getpid ()); + return 1; +} + +static int jack_initDriver (AudioOutput *audioOutput, ConfigParam *param) +{ + BlockParam *bp; + char *endptr; + int val; + char *cp = NULL; + + if ( param ) { + bp = getBlockParam (param, "ports"); + if ( bp ) { + cp = strdup (bp->value); + ports[0] = strdup (strtok (cp, " ,")); + ports[1] = strdup (strtok (NULL, " ,")); + free (cp); + } + + bp = getBlockParam (param, "ringbuffer_size"); + if ( bp ) { + errno = 0; + val = strtol (bp->value, &endptr, 10); + + if ( errno == 0 && endptr != bp->value) { + ringbuf_sz = val; + ERROR ("ringbuffer_size=%d\n", ringbuf_sz); + } else { + ERROR ("%s is not a number; ringbuf_size=%d\n", + bp->value, ringbuf_sz); + } + } + } + + ERROR ("jack_initDriver (pid=%d)\n", getpid ()); + + return 0; + +} + +static int jack_testDefault(void) +{ + return 0; +} + +static int jack_openDevice (AudioOutput *audioOutput) +{ + if ( !jd ) { + jd = newJackData (); + audioOutput->data = jd; + + if ( !connect_jack (audioOutput) ) { + free (jd); + return -1; + } + } + + set_audioformat (audioOutput); + audioOutput->open = 1; + + ERROR ("jack_openDevice (pid=%d)!\n", getpid ()); + return 0; +} + + +static void jack_closeDevice(AudioOutput * audioOutput) +{ + audioOutput->open = 0; + ERROR ("jack_closeDevice (pid=%d)!\n", getpid ()); +} + +static void jack_dropBufferedAudio (AudioOutput * audioOutput) +{ + +} + +static int jack_playAudio(AudioOutput * audioOutput, char *buff, int size) +{ + JackData *jd = audioOutput->data; + size_t remain = size; + size_t pos = 0; + + if ( jd->shutdown ) { + ERROR ("Refusing to play, because there is no client thread.\n"); + return 0; + } + + if ( jd->our_xrun ) { + ERROR ("xrun\n"); + jd->our_xrun = 0; + } + + while (remain && !jd->shutdown) { + size_t space; + + if ( (space = jack_ringbuffer_write_space (jd->ringbuffer[0])) + > sizeof (jack_default_audio_sample_t) ) { + size_t to_write; + + to_write = MIN (space, remain); + remain -= to_write; + to_write /= 4; + //ERROR ("\t\tto_write=%d remain=%d (%d)\n", to_write, remain, to_write * 2 * 2); + while (to_write--) { + jack_default_audio_sample_t sample; + + sample = *(short *)(buff + pos); + sample /= 32768; + pos += 2; + jack_ringbuffer_write (jd->ringbuffer[0], + (char *)&sample, + sizeof (sample)); + + + sample = *(short *)(buff + pos); + sample /= 32768; + pos += 2; + jack_ringbuffer_write (jd->ringbuffer[1], + (char *)&sample, + sizeof (sample)); + } + } else { + usleep (ringbuf_sz / (float)(jd->bps) * 100000.0); + } + } + + return 0; + +} + +AudioOutputPlugin jackPlugin = { + "jack", + jack_testDefault, + jack_initDriver, + jack_finishDriver, + jack_openDevice, + jack_playAudio, + jack_dropBufferedAudio, + jack_closeDevice, + NULL, /* sendMetadataFunc */ +}; + + +#else /* HAVE JACK */ + +DISABLED_AUDIO_OUTPUT_PLUGIN(jackPlugin) + +#endif /* HAVE_JACK */ diff --git a/src/inputPlugins/aac_plugin.c b/src/inputPlugins/aac_plugin.c index 07906152c..04000e746 100644 --- a/src/inputPlugins/aac_plugin.c +++ b/src/inputPlugins/aac_plugin.c @@ -237,7 +237,7 @@ static float getAacFloatTotalTime(char *file) size_t fileread, tagsize; faacDecHandle decoder; faacDecConfigurationPtr config; - unsigned int sampleRate; + unsigned long sampleRate; unsigned char channels; InputStream inStream; long bread; @@ -293,7 +293,7 @@ static int aac_decode(OutputBuffer * cb, DecoderControl * dc, char *path) faacDecFrameInfo frameInfo; faacDecConfigurationPtr config; long bread; - unsigned int sampleRate; + unsigned long sampleRate; unsigned char channels; int eof = 0; unsigned int sampleCount; diff --git a/src/inputPlugins/mp4_plugin.c b/src/inputPlugins/mp4_plugin.c index 6c659cbb6..b3439056d 100644 --- a/src/inputPlugins/mp4_plugin.c +++ b/src/inputPlugins/mp4_plugin.c @@ -100,7 +100,7 @@ static int mp4_decode(OutputBuffer * cb, DecoderControl * dc, char *path) faacDecConfigurationPtr config; unsigned char *mp4Buffer; unsigned int mp4BufferSize; - uint32_t sampleRate; + unsigned long sampleRate; unsigned char channels; long sampleId; long numSamples; |