summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiguel Freitas <miguelfreitas@users.sourceforge.net>2006-08-12 23:57:29 +0000
committerMiguel Freitas <miguelfreitas@users.sourceforge.net>2006-08-12 23:57:29 +0000
commita001dcbaed1398598a5271ae869f2adacc1ab146 (patch)
treefe98b3d0807590d03a3cfafe7101062bbde8501b
parentaf78d2543c41a5407e688b077c664cc6d386bc97 (diff)
downloadxine-lib-a001dcbaed1398598a5271ae869f2adacc1ab146.tar.gz
xine-lib-a001dcbaed1398598a5271ae869f2adacc1ab146.tar.bz2
JACK audio driver by Chris Cannam
CVS patchset: 8178 CVS date: 2006/08/12 23:57:29
-rw-r--r--AUTHORS5
-rw-r--r--ChangeLog1
-rw-r--r--configure.ac34
-rw-r--r--src/audio_out/Makefile.am10
-rw-r--r--src/audio_out/audio_jack_out.c447
5 files changed, 495 insertions, 2 deletions
diff --git a/AUTHORS b/AUTHORS
index 6cf83a89e..7999a8d49 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -561,5 +561,8 @@ Claudio Ciccani <klan@users.sf.net>
Maximilian Schwerin <mschwerin@users.sourceforge.net>
support for Vorbis-style comments in FLAC files
-
+
+Chris Cannam <chris.cannam@ferventsoftware.com>
+ JACK audio driver
+
(let us know if we've forgotten anyone)
diff --git a/ChangeLog b/ChangeLog
index 795c8e4a8..f81922410 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,7 @@ xine-lib (1.1.3)
* Mark Polypaudio audio plugin as deprecated, as upstream changed APIs at
least two times in the mean time.
* Allow 0 for DVD title/chapter (navigation or full title).
+ * New experimental JACK audio driver.
xine-lib (1.1.2)
* Security fixes:
diff --git a/configure.ac b/configure.ac
index 0f5f03b22..f452e011a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1379,6 +1379,40 @@ AM_CONDITIONAL(HAVE_FUSIONSOUND, test x"$no_fusionsound" != "xyes")
dnl ---------------------------------------------
+dnl JACK support
+dnl ---------------------------------------------
+
+AC_ARG_ENABLE([jack],
+ AC_HELP_STRING([--disable-jack], [Disable optional JACK support]),
+ [
+ case "${enableval}" in
+ yes) jack=yes ;;
+ no) jack=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-jack) ;;
+ esac
+ ],
+ [jack=auto])
+
+if test "x${jack}" != xno ; then
+ PKG_CHECK_MODULES(JACK, [ jack >= 0.100 ],
+ HAVE_JACK=1,
+ [
+ HAVE_JACK=0
+ if test "x$jack" = xyes ; then
+ AC_MSG_ERROR([*** JACK support not found])
+ fi
+ ])
+else
+ HAVE_JACK=0
+fi
+
+AC_SUBST(JACK_CFLAGS)
+AC_SUBST(JACK_LIBS)
+AC_SUBST(HAVE_JACK)
+AM_CONDITIONAL([HAVE_JACK], [test "x$HAVE_JACK" = x1])
+
+
+dnl ---------------------------------------------
dnl gnome-vfs support
dnl ---------------------------------------------
diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am
index 996b61925..3eeac31fc 100644
--- a/src/audio_out/Makefile.am
+++ b/src/audio_out/Makefile.am
@@ -51,6 +51,9 @@ if HAVE_FUSIONSOUND
fusionsound_module = xineplug_ao_out_fusionsound.la
endif
+if HAVE_JACK
+jack_module = xineplug_ao_out_jack.la
+endif
##
# IMPORTANT:
@@ -69,7 +72,8 @@ lib_LTLIBRARIES = xineplug_ao_out_none.la xineplug_ao_out_file.la \
$(polypaudio_module) \
$(pulseaudio_module) \
$(directx2_module) \
- $(fusionsound_module)
+ $(fusionsound_module) \
+ $(jack_module)
#lib_LTLIBRARIES = \
# $(alsa_module) \
@@ -155,3 +159,7 @@ xineplug_ao_out_fusionsound_la_LIBADD = $(FUSIONSOUND_LIBS) $(XINE_LIB)
xineplug_ao_out_fusionsound_la_CFLAGS = $(VISIBILITY_FLAG) $(FUSIONSOUND_CFLAGS)
xineplug_ao_out_fusionsound_la_LDFLAGS = -avoid-version -module
+xineplug_ao_out_jack_la_SOURCES = audio_jack_out.c
+xineplug_ao_out_jack_la_LIBADD = $(JACK_LIBS) $(XINE_LIB)
+xineplug_ao_out_jack_la_CFLAGS = $(VISIBILITY_FLAG) $(JACK_CFLAGS)
+xineplug_ao_out_jack_la_LDFLAGS = -avoid-version -module
diff --git a/src/audio_out/audio_jack_out.c b/src/audio_out/audio_jack_out.c
new file mode 100644
index 000000000..1bc070527
--- /dev/null
+++ b/src/audio_out/audio_jack_out.c
@@ -0,0 +1,447 @@
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "audio_out.h"
+
+#include <jack/jack.h>
+
+#define AO_OUT_JACK_IFACE_VERSION 8
+
+#define GAP_TOLERANCE AO_MAX_GAP
+#define BUFSIZE 81920
+
+typedef struct jack_driver_s {
+
+ ao_driver_t ao_driver;
+ xine_t *xine;
+
+ int capabilities;
+
+ int32_t sample_rate;
+ uint32_t num_channels;
+ uint32_t bits_per_sample;
+ uint32_t bytes_per_frame;
+
+ jack_client_t *client;
+ jack_port_t *port_1;
+ jack_port_t *port_2;
+ float buf_1[BUFSIZE];
+ float buf_2[BUFSIZE];
+ uint32_t buf_read;
+ uint32_t buf_write;
+
+ uint32_t volume;
+ uint32_t mute;
+
+} jack_driver_t;
+
+typedef struct {
+ audio_driver_class_t driver_class;
+ config_values_t *config;
+ xine_t *xine;
+} jack_class_t;
+
+
+static int jack_process(jack_nframes_t nframes, void *arg)
+{
+ jack_driver_t *this = (jack_driver_t *)arg;
+ uint32_t local_buf_read = this->buf_read;
+ uint32_t local_buf_write = this->buf_write;
+ uint32_t src_channel, target_channel;
+ uint32_t frame;
+ float *buf, *out;
+ float gain = 0;
+
+ if (!this->client) return 0;
+
+ if (!this->mute) {
+ gain = (float)this->volume / 100.0;
+ }
+
+ for (target_channel = 0; target_channel < 2; ++target_channel) {
+
+ if (target_channel < this->num_channels) src_channel = target_channel;
+ else src_channel = 0;
+
+ jack_port_t *port = (target_channel ? this->port_2 : this->port_1);
+ if (!port) continue;
+
+ buf = (src_channel ? this->buf_2 : this->buf_1);
+ out = (float *)jack_port_get_buffer(port, nframes);
+
+ local_buf_read = this->buf_read;
+ frame = 0;
+
+ while (frame < nframes && local_buf_read != local_buf_write) {
+
+ // local_buf_write doesn't change during this process,
+ // so we can safely defer updating buf_read until after
+
+ out[frame++] = buf[local_buf_read] * gain;
+ if (++local_buf_read == BUFSIZE) local_buf_read = 0;
+ }
+
+ if (frame < nframes) {
+// printf("jack_process: underrun: %u required, %u available\n",
+// nframes, frame);
+ while (frame < nframes) {
+ out[frame++] = 0.0f;
+ }
+ }
+ }
+
+ this->buf_read = local_buf_read;
+
+
+// printf("jack_process: buf_read %u, buf_write %u\n", this->buf_read, this->buf_write);
+
+ return 0;
+}
+
+
+static void jack_shutdown(void *arg)
+{
+ jack_driver_t *this = (jack_driver_t *)arg;
+ this->client = 0;
+}
+
+/*
+ * open the audio device for writing to
+ */
+static int ao_jack_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+
+ if (bits != 16) {
+ fprintf(stderr, "ao_jack_open: bits=%u expected %u\n", bits, 16);
+ return 0;
+ }
+
+ rate = jack_get_sample_rate(this->client);
+ fprintf(stderr, "ao_jack_open: JACK sample rate is %u\n", rate);
+
+ switch (mode) {
+ case AO_CAP_MODE_MONO:
+ this->num_channels = 1;
+ break;
+ case AO_CAP_MODE_STEREO:
+ this->num_channels = 2;
+ break;
+ }
+
+ this->buf_read = this->buf_write = 0;
+ this->sample_rate = rate;
+ this->bits_per_sample = bits;
+ this->capabilities = AO_CAP_16BITS | AO_CAP_MODE_MONO | \
+ AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL;
+ this->bytes_per_frame = this->num_channels * (bits / 8);
+
+ fprintf(stderr, "ao_jack_open: bits=%d rate=%d, mode=%d OK\n", bits, rate, mode);
+
+ return rate;
+}
+
+
+static int ao_jack_num_channels(ao_driver_t *this_gen)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ return this->num_channels;
+}
+
+static int ao_jack_bytes_per_frame(ao_driver_t *this_gen)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ return this->bytes_per_frame;
+}
+
+static int ao_jack_get_gap_tolerance (ao_driver_t *this_gen)
+{
+ return GAP_TOLERANCE;
+}
+
+static int last_write_space = 0;
+
+static int ao_jack_write(ao_driver_t *this_gen, int16_t *data,
+ uint32_t num_frames)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ uint32_t frame, channel;
+
+ uint32_t local_buf_read = this->buf_read;
+ uint32_t local_buf_write = this->buf_write;
+ uint32_t space = (local_buf_read + BUFSIZE - local_buf_write - 1) % BUFSIZE;
+ uint32_t first_frame = 0;
+
+ int c = 0;
+ while (space < num_frames) {
+ if (++c == 10) return 0;
+ usleep(10000);
+ local_buf_read = this->buf_read;
+ space = (local_buf_read + BUFSIZE - local_buf_write - 1) % BUFSIZE;
+ }
+
+// if (space < num_frames) return 0;
+
+// printf("ao_jack_write: %u frames on %u channels, space is %u\n", num_frames, this->num_channels, space);
+ last_write_space = space;
+
+ for (frame = first_frame; frame < num_frames; ++frame) {
+ for (channel = 0; channel < this->num_channels; ++channel) {
+ float *buf = (channel ? this->buf_2 : this->buf_1);
+ int16_t sample = data[frame * this->num_channels + channel];
+ buf[local_buf_write] = ((float)sample) / 32767.0f;
+// printf("%6f ", buf[local_buf_write]);
+// if (++c == 8) { printf("\n"); c = 0; }
+ }
+ if (++local_buf_write == BUFSIZE) local_buf_write = 0;
+ }
+
+ this->buf_write = local_buf_write;
+
+// printf("ao_jack_write: buf_read %u, buf_write %u\n", this->buf_read, this->buf_write);
+
+ return 1;
+}
+
+static int ao_jack_delay (ao_driver_t *this_gen)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+
+ uint32_t local_buf_read = this->buf_read;
+ uint32_t local_buf_write = this->buf_write;
+
+ int delay = 0;
+
+ if (local_buf_write > local_buf_read) {
+ delay = local_buf_write - local_buf_read;
+ } else {
+ delay = ((local_buf_write + BUFSIZE - local_buf_read) % BUFSIZE);
+ }
+
+ return delay;// - jack_get_buffer_size(this->client);
+}
+
+static void ao_jack_close(ao_driver_t *this_gen)
+{
+ // nothing
+}
+
+static uint32_t ao_jack_get_capabilities (ao_driver_t *this_gen) {
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ return this->capabilities;
+}
+
+static void ao_jack_exit(ao_driver_t *this_gen)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ jack_client_t *client = this->client;
+ ao_jack_close(this_gen);
+ this->client = 0;
+ if (client) jack_client_close(client);
+ free (this);
+}
+
+static int ao_jack_get_property (ao_driver_t *this_gen, int property) {
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+
+ switch(property) {
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+// printf("ao_jack_get_property(AO_PROP_MIXER_VOL): %u\n", this->volume);
+ return this->volume;
+ break;
+ case AO_PROP_MUTE_VOL:
+// printf("ao_jack_get_property(AO_PROP_MUTE_VOL): %u\n", this->mute);
+ return this->mute;
+ break;
+ }
+
+ return 0;
+}
+
+static int ao_jack_set_property (ao_driver_t *this_gen, int property, int value) {
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+
+ switch(property) {
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+// printf("ao_jack_set_property(AO_PROP_MIXER_VOL): %u\n", value);
+ this->volume = value;
+ break;
+ case AO_PROP_MUTE_VOL:
+// printf("ao_jack_get_property(AO_PROP_MUTE_VOL): %u\n", value);
+ this->mute = value;
+ break;
+ }
+
+ return ~value;
+}
+
+static int ao_jack_ctrl(ao_driver_t *this_gen, int cmd, ...) {
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+
+ switch (cmd) {
+
+ case AO_CTRL_PLAY_PAUSE:
+ break;
+
+ case AO_CTRL_PLAY_RESUME:
+ break;
+
+ case AO_CTRL_FLUSH_BUFFERS:
+// fprintf(stderr, "ao_jack_ctrl(AO_CTRL_FLUSH_BUFFERS)\n");
+ this->buf_write = this->buf_read = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static ao_driver_t *open_jack_plugin (audio_driver_class_t *class_gen,
+ const void *data)
+{
+ jack_class_t *class = (jack_class_t *) class_gen;
+ jack_driver_t *this;
+ jack_client_t *client;
+ uint32_t rate;
+ const char **port_names;
+ int i;
+
+ if ((client = jack_client_new("xine")) == 0) {
+
+ char name[20];
+ sprintf(name, "xine (%d)", (int)getpid());
+
+ if ((client = jack_client_new(name)) == 0) {
+ fprintf(stderr, "\nopen_jack_plugin: Error: Failed to connect to JACK server\n");
+ fprintf(stderr, "open_jack_plugin: (did you start 'jackd' server?)\n");
+ return 0;
+ }
+ }
+
+ this = (jack_driver_t *) xine_xmalloc (sizeof (jack_driver_t));
+
+ this->client = client;
+
+ jack_set_process_callback(client, jack_process, this);
+ jack_on_shutdown(client, jack_shutdown, this);
+
+ rate = jack_get_sample_rate(client);
+ fprintf(stderr, "open_jack_plugin: JACK sample rate is %u\n", rate);
+
+ // We support up to 2-channel output
+
+ for (i = 0; i < 2; ++i) {
+ jack_port_t *port = jack_port_register
+ (client, (i ? "out_r" : "out_l"),
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if (!port) {
+ fprintf(stderr, "ao_jack_open: failed to register port %u\n", i);
+ }
+ if (i) this->port_2 = port;
+ else this->port_1 = port;
+ }
+
+ this->buf_read = this->buf_write = 0;
+ this->volume = 100;
+ this->mute = 0;
+
+ if (jack_activate(client)) {
+ fprintf(stderr, "ao_jack_open: failed to activate JACK client\n");
+ return 0;
+ }
+
+ if ((port_names = jack_get_ports(client, NULL, NULL,
+ JackPortIsPhysical | JackPortIsInput)) != NULL) {
+ if (port_names[0]) {
+ jack_connect(client, jack_port_name(this->port_1), port_names[0]);
+ }
+ if (port_names[1] && this->port_2) {
+ jack_connect(client, jack_port_name(this->port_2), port_names[1]);
+ }
+ free(port_names);
+ }
+
+ this->sample_rate = rate;
+
+ this->xine = class->xine;
+ this->capabilities = AO_CAP_FLOAT32 | AO_CAP_MODE_MONO |
+ AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL;
+
+ this->ao_driver.get_capabilities = ao_jack_get_capabilities;
+ this->ao_driver.get_property = ao_jack_get_property;
+ this->ao_driver.set_property = ao_jack_set_property;
+ this->ao_driver.open = ao_jack_open;
+ this->ao_driver.num_channels = ao_jack_num_channels;
+ this->ao_driver.bytes_per_frame = ao_jack_bytes_per_frame;
+ this->ao_driver.delay = ao_jack_delay;
+ this->ao_driver.write = ao_jack_write;
+ this->ao_driver.close = ao_jack_close;
+ this->ao_driver.exit = ao_jack_exit;
+ this->ao_driver.get_gap_tolerance = ao_jack_get_gap_tolerance;
+ this->ao_driver.control = ao_jack_ctrl;
+
+ fprintf(stderr, "jack open_jack_plugin returning %p\n", (void *)(&this->ao_driver));
+ return &this->ao_driver;
+}
+
+/*
+ * class functions
+ */
+
+static char* get_identifier (audio_driver_class_t *this_gen) {
+ return "jack";
+}
+
+static char* get_description (audio_driver_class_t *this_gen) {
+ return _("xine output plugin for JACK Audio Connection Kit");
+}
+
+static void dispose_class (audio_driver_class_t *this_gen) {
+
+ jack_class_t *this = (jack_class_t *) this_gen;
+ free (this);
+}
+
+static void *init_class (xine_t *xine, void *data) {
+
+ jack_class_t *this;
+
+ this = (jack_class_t *) xine_xmalloc (sizeof (jack_class_t));
+
+ this->driver_class.open_plugin = open_jack_plugin;
+ this->driver_class.get_identifier = get_identifier;
+ this->driver_class.get_description = get_description;
+ this->driver_class.dispose = dispose_class;
+
+ this->config = xine->config;
+ this->xine = xine;
+
+ fprintf(stderr, "jack init_class returning %p\n", (void *)this);
+
+ return this;
+}
+
+static ao_info_t ao_info_jack = {
+ 6
+};
+
+/*
+ * exported plugin catalog entry
+ */
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_AUDIO_OUT, AO_OUT_JACK_IFACE_VERSION, "jack", XINE_VERSION_CODE /* XINE_VERSION_CODE */, &ao_info_jack, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+