changeset 325:ea321d1dae48 trunk

[svn] JACKd output plugin via external contractor james@develia.org.
author nenolod
date Mon, 19 Dec 2005 08:58:27 -0800
parents fbafca56b6a8
children 429b73ffcb18
files Plugins/Output/Makefile.am Plugins/Output/jack/Makefile.am Plugins/Output/jack/bio2jack.c Plugins/Output/jack/bio2jack.h Plugins/Output/jack/configure.c Plugins/Output/jack/jack.c Plugins/Output/jack/jack.h Plugins/Output/jack/xconvert.h configure.ac m4/jack.m4
diffstat 10 files changed, 3670 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/Plugins/Output/Makefile.am	Mon Dec 19 08:45:45 2005 -0800
+++ b/Plugins/Output/Makefile.am	Mon Dec 19 08:58:27 2005 -0800
@@ -1,2 +1,2 @@
-ALL_PLUGINS = crossfade OSS esd alsa disk_writer
+ALL_PLUGINS = crossfade OSS jack esd alsa disk_writer
 SUBDIRS = $(OUTPUT_PLUGINS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/jack/Makefile.am	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,14 @@
+
+libjackout_la_SOURCES = jack.c configure.c bio2jack.h bio2jack.c xconvert.h
+
+if HAVE_JACK
+
+lib_LTLIBRARIES = libjackout.la
+
+endif
+
+libdir = $(plugindir)/$(OUTPUT_PLUGIN_DIR)
+
+AM_CFLAGS = @GTK_CFLAGS@ @JACK_CFLAGS@ -Wall @SAMPLERATE_CFLAGS@
+LIBS = @GTK_LIBS@ @JACK_LIBS@ @SAMPLERATE_LIBS@ -lpthread
+libjackout_la_LDFLAGS = -module -avoid-version
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/jack/bio2jack.c	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,2612 @@
+/*
+ * Copyright 2003-2004 Chris Morgan <cmorgan@alum.wpi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* NOTE: All functions that take a jack_driver_t* do NOT lock the device, in order to get a */
+/*       jack_driver_t* you must call getDriver() which will pthread_mutex_lock() */
+
+#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 <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <samplerate.h>
+
+#include "bio2jack.h"
+
+/* enable/disable TRACING through the JACK_Callback() function */
+/* this can sometimes be too much information */
+#define TRACE_CALLBACK          0
+
+/* set to 1 for verbose output */
+#define VERBOSE_OUTPUT          0
+
+/* set to 1 to enable debug messages */
+#define DEBUG_OUTPUT            0
+
+/* set to 1 to enable tracing */
+#define TRACE_ENABLE            0
+
+/* set to 1 to enable the function timers */
+#define TIMER_ENABLE            0
+
+/* set to 1 to enable tracing of getDriver() and releaseDriver() */
+#define TRACE_getReleaseDevice  0
+
+#define ENABLE_WARNINGS         0
+
+#define DEFAULT_RB_SIZE         16384
+
+#define OUTFILE stderr
+
+#if TIMER_ENABLE
+/* This seemingly construct makes timing arbitrary functions really easy
+   all you have to do is place a 'TIMER("start\n")' at the beginning and
+   a 'TIMER("stop\n")' at the end of any function and this does the rest
+   (naturally you can place any printf-compliant text you like in the argument
+   along with the associated values). */
+static struct timeval timer_now;
+#define TIMER(format,args...) gettimeofday(&timer_now,0); \
+  fprintf(OUTFILE, "%ld.%06ld: %s::%s(%d) "format, timer_now.tv_sec, timer_now.tv_usec, __FILE__, __FUNCTION__, __LINE__, ##args)
+#else
+#define TIMER(...)
+#endif
+
+#if TRACE_ENABLE
+#define TRACE(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args);	\
+         fflush(OUTFILE);
+#else
+#define TRACE(...)
+#endif
+
+#if DEBUG_OUTPUT
+#define DEBUG(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args);	\
+         fflush(OUTFILE);
+#else
+#define DEBUG(...)
+#endif
+
+#if TRACE_CALLBACK
+#define CALLBACK_TRACE(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args);	\
+         fflush(OUTFILE);
+#else
+#define CALLBACK_TRACE(...)
+#endif
+
+#if ENABLE_WARNINGS
+#define WARN(format,args...) fprintf(OUTFILE, "WARN: %s::%s(%d) "format, __FILE__,__FUNCTION__,__LINE__,##args);	\
+         fflush(OUTFILE);
+#else
+#define WARN(...)
+#endif
+
+#define ERR(format,args...) fprintf(OUTFILE, "ERR: %s::%s(%d) "format, __FILE__,__FUNCTION__,__LINE__,##args);	\
+         fflush(OUTFILE);
+
+#define min(a,b)   (((a) < (b)) ? (a) : (b))
+#define max(a,b)   (((a) < (b)) ? (b) : (a))
+
+#define MAX_OUTPUT_PORTS  10
+#define MAX_INPUT_PORTS   10
+
+typedef struct jack_driver_s
+{
+  bool allocated;                       /* whether or not this device has been allocated */
+
+  int deviceID;                         /* id of this device */
+  int clientCtr;                        /* to prevent overlapping client ids */
+  long jack_sample_rate;                /* jack samples(frames) per second */
+
+  long client_sample_rate;              /* client samples(frames) per second */
+  double output_sample_rate_ratio;      /* ratio between jack's output rate & ours */
+  double input_sample_rate_ratio;       /* ratio between our input rate & jack's */
+
+  unsigned long num_input_channels;     /* number of input channels(1 is mono, 2 stereo etc..) */
+  unsigned long num_output_channels;    /* number of output channels(1 is mono, 2 stereo etc..) */
+
+  unsigned long bits_per_channel;       /* number of bits per channel (only 8 & 16 are currently supported) */
+
+  unsigned long bytes_per_output_frame; /* (num_output_channels * bits_per_channel) / 8 */
+  unsigned long bytes_per_input_frame;  /* (num_input_channels * bits_per_channel) / 8 */
+
+  unsigned long bytes_per_jack_output_frame;    /* (num_output_channels * bits_per_channel) / 8 */
+  unsigned long bytes_per_jack_input_frame;     /* (num_input_channels * bits_per_channel) / 8 */
+
+  unsigned long latencyMS;      /* latency in ms between writing and actual audio output of the written data */
+
+  long clientBytesInJack;       /* number of INPUT bytes(from the client of bio2jack) we wrote to jack(not necessary the number of bytes we wrote to jack) */
+  long jack_buffer_size;        /* size of the buffer jack will pass in to the process callback */
+
+  unsigned long callback_buffer1_size;  /* number of bytes in the buffer allocated for processing data in JACK_Callback */
+  char *callback_buffer1;
+  unsigned long callback_buffer2_size;  /* number of bytes in the buffer allocated for processing data in JACK_Callback */
+  char *callback_buffer2;
+
+  unsigned long rw_buffer1_size;        /* number of bytes in the buffer allocated for processing data in JACK_(Read|Write) */
+  char *rw_buffer1;
+
+  struct timeval previousTime;  /* time of last JACK_Callback() write to jack, allows for MS accurate bytes played  */
+
+  unsigned long written_client_bytes;   /* input bytes we wrote to jack, not necessarily actual bytes we wrote to jack due to channel and other conversion */
+  unsigned long played_client_bytes;    /* input bytes that jack has played */
+
+  unsigned long client_bytes;   /* total bytes written by the client of bio2jack via JACK_Write() */
+
+  jack_port_t *output_port[MAX_OUTPUT_PORTS];   /* output ports */
+  jack_port_t *input_port[MAX_OUTPUT_PORTS];    /* input ports */
+
+  jack_client_t *client;        /* pointer to jack client */
+
+  char **jack_port_name;        /* user given strings for the port names, can be NULL */
+  unsigned int jack_port_name_count;    /* the number of port names given */
+
+  unsigned long jack_output_port_flags; /* flags to be passed to jack when opening the output ports */
+  unsigned long jack_input_port_flags;  /* flags to be passed to jack when opening the output ports */
+
+  jack_ringbuffer_t *pPlayPtr;  /* the playback ringbuffer */
+  jack_ringbuffer_t *pRecPtr;   /* the recording ringbuffer */
+
+  SRC_STATE *output_src;        /* SRC object for the output stream */
+  SRC_STATE *input_src;         /* SRC object for the output stream */
+
+  enum status_enum state;       /* one of PLAYING, PAUSED, STOPPED, CLOSED, RESET etc */
+
+  unsigned int volume[MAX_OUTPUT_PORTS];        /* percentage of sample value to preserve, 100 would be no attenuation */
+  enum JACK_VOLUME_TYPE volumeEffectType;       /* linear or dbAttenuation, if dbAttenuation volume is the number of dBs of
+                                                   attenuation to apply, 0 volume being no attenuation, full volume */
+
+  long position_byte_offset;    /* an offset that we will apply to returned position queries to achieve */
+                                /* the position that the user of the driver desires set */
+
+  bool in_use;                  /* true if this device is currently in use */
+
+  pthread_mutex_t mutex;        /* mutex to lock this specific device */
+
+  /* variables used for trying to restart the connection to jack */
+  bool jackd_died;              /* true if jackd has died and we should try to restart it */
+  struct timeval last_reconnect_attempt;
+} jack_driver_t;
+
+
+static char *client_name;       /* the name bio2jack will use when creating a new
+                                   jack client. client_name_%deviceID% will be used */
+
+
+static bool do_sample_rate_conversion;  /* whether the client has requested sample rate conversion,
+                                           default to on for improved compatibility */
+
+/*
+  Which SRC converter function we should use when doing sample rate conversion.
+  Default to the fastest of the 'good quality' set.
+ */
+static int preferred_src_converter = SRC_SINC_FASTEST;
+
+static bool init_done = 0;      /* just to prevent clients from calling JACK_Init twice, that would be very bad */
+
+static enum JACK_PORT_CONNECTION_MODE port_connection_mode = CONNECT_ALL;
+
+/* enable/disable code that allows us to close a device without actually closing the jack device */
+/* this works around the issue where jack doesn't always close devices by the time the close function call returns */
+#define JACK_CLOSE_HACK 1
+
+typedef jack_default_audio_sample_t sample_t;
+typedef jack_nframes_t nframes_t;
+
+/* allocate devices for output */
+/* if you increase this past 10, you might want to update 'out_client_name = ... ' in JACK_OpenDevice */
+#define MAX_OUTDEVICES 10
+static jack_driver_t outDev[MAX_OUTDEVICES];
+
+static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER;        /* this is to lock the entire outDev array
+                                                                           to make managing it in a threaded
+                                                                           environment sane */
+
+#if JACK_CLOSE_HACK
+static void JACK_CloseDevice(jack_driver_t * drv, bool close_client);
+#else
+static void JACK_CloseDevice(jack_driver_t * drv);
+#endif
+
+
+/* Prototypes */
+static int JACK_OpenDevice(jack_driver_t * drv);
+static unsigned long JACK_GetBytesFreeSpaceFromDriver(jack_driver_t * drv);
+static void JACK_ResetFromDriver(jack_driver_t * drv);
+static long JACK_GetPositionFromDriver(jack_driver_t * drv,
+                                       enum pos_enum position, int type);
+static void JACK_CleanupDriver(jack_driver_t * drv);
+
+
+/* Return the difference between two timeval structures in terms of milliseconds */
+long
+TimeValDifference(struct timeval *start, struct timeval *end)
+{
+  double long ms;               /* milliseconds value */
+
+  ms = end->tv_sec - start->tv_sec;     /* compute seconds difference */
+  ms *= (double) 1000;          /* convert to milliseconds */
+
+  ms += (double) (end->tv_usec - start->tv_usec) / (double) 1000;       /* add on microseconds difference */
+
+  return (long) ms;
+}
+
+/* get a device and lock the devices mutex */
+/* */
+/* also attempt to reconnect to jack since this function is called from */
+/* most other bio2jack functions it provides a good point to attempt reconnection */
+/* */
+/* Ok, I know this looks complicated and it kind of is. The point is that when you're
+   trying to trace mutexes it's more important to know *who* called us than just that
+   we were called.  This uses from pre-processor trickery so that the fprintf is actually
+   placed in the function making the getDriver call.  Thus, the __FUNCTION__ and __LINE__
+   macros will actually reference our caller, rather than getDriver.  The reason the 
+   fprintf call is passes as a parameter is because this macro has to still return a
+   jack_driver_t* and we want to log both before *and* after the getDriver call for
+   easier detection of blocked calls.
+ */
+#if TRACE_getReleaseDevice
+#define getDriver(x) _getDriver(x,fprintf(OUTFILE, "%s::%s(%d) getting driver %d\n", __FILE__, __FUNCTION__, __LINE__,x)); TRACE("got driver %d\n",x);
+jack_driver_t *
+_getDriver(int deviceID, int ignored)
+{
+  fflush(OUTFILE);
+#else
+jack_driver_t *
+getDriver(int deviceID)
+{
+#endif
+  jack_driver_t *drv = &outDev[deviceID];
+
+  if(pthread_mutex_lock(&drv->mutex) != 0)
+    ERR("lock returned an error\n");
+
+  /* should we try to restart the jack server? */
+  if(drv->jackd_died && drv->client == 0)
+  {
+    struct timeval now;
+    gettimeofday(&now, 0);
+
+    /* wait 250ms before trying again */
+    if(TimeValDifference(&drv->last_reconnect_attempt, &now) >= 250)
+    {
+      JACK_OpenDevice(drv);
+      drv->last_reconnect_attempt = now;
+    }
+  }
+
+  return drv;
+}
+
+/* release a device's mutex */
+/* */
+/* This macro is similar to the one for getDriver above, only simpler since we only
+   really need to know when the lock was release for the sake of debugging.
+*/
+#if TRACE_getReleaseDevice
+#define releaseDriver(x) TRACE("releasing driver %d\n",x->deviceID); _releaseDriver(x);
+void
+_releaseDriver(jack_driver_t * drv)
+#else
+void
+releaseDriver(jack_driver_t * drv)
+#endif
+{
+  /*
+     #if TRACE_getReleaseDevice
+     TRACE("deviceID == %d\n", drv->deviceID);
+     #endif
+   */
+  if(pthread_mutex_unlock(&drv->mutex) != 0)
+    ERR("lock returned an error\n");
+}
+
+
+/* Return a string corresponding to the input state */
+char *
+DEBUGSTATE(enum status_enum state)
+{
+  if(state == PLAYING)
+    return "PLAYING";
+  else if(state == PAUSED)
+    return "PAUSED";
+  else if(state == STOPPED)
+    return "STOPPED";
+  else if(state == CLOSED)
+    return "CLOSED";
+  else if(state == RESET)
+    return "RESET";
+  else
+    return "unknown state";
+}
+
+
+#define SAMPLE_MAX_16BIT  32767.0f
+#define SAMPLE_MAX_8BIT   127.0f
+
+/* floating point volume routine */
+/* volume should be a value between 0.0 and 1.0 */
+static void
+float_volume_effect(sample_t * buf, unsigned long nsamples, float volume,
+                    int skip)
+{
+  if(volume < 0)
+    volume = 0;
+  if(volume > 1.0)
+    volume = 1.0;
+
+  while(nsamples--)
+  {
+    *buf = (*buf) * volume;
+    buf += skip;
+  }
+}
+
+/* place one channel into a multi-channel stream */
+static inline void
+mux(sample_t * dst, sample_t * src, unsigned long nsamples,
+    unsigned long dst_skip)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  while(nsamples--)
+  {
+    *dst = *src;
+    dst += dst_skip;
+    src++;
+  }
+}
+
+/* pull one channel out of a multi-channel stream */
+static void
+demux(sample_t * dst, sample_t * src, unsigned long nsamples,
+      unsigned long src_skip)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  while(nsamples--)
+  {
+    *dst = *src;
+    dst++;
+    src += src_skip;
+  }
+}
+
+/* convert from 16 bit to floating point */
+static inline void
+sample_move_short_float(sample_t * dst, short *src, unsigned long nsamples)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  unsigned long i;
+  for(i = 0; i < nsamples; i++)
+    dst[i] = (sample_t) (src[i]) / SAMPLE_MAX_16BIT;
+}
+
+/* convert from floating point to 16 bit */
+static inline void
+sample_move_float_short(short *dst, sample_t * src, unsigned long nsamples)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  unsigned long i;
+  for(i = 0; i < nsamples; i++)
+    dst[i] = (short) ((src[i]) * SAMPLE_MAX_16BIT);
+}
+
+/* convert from 8 bit to floating point */
+static inline void
+sample_move_char_float(sample_t * dst, char *src, unsigned long nsamples)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  unsigned long i;
+  for(i = 0; i < nsamples; i++)
+    dst[i] = (sample_t) (src[i]) / SAMPLE_MAX_8BIT;
+}
+
+/* convert from floating point to 8 bit */
+static inline void
+sample_move_float_char(char *dst, sample_t * src, unsigned long nsamples)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  unsigned long i;
+  for(i = 0; i < nsamples; i++)
+    dst[i] = (char) ((src[i]) * SAMPLE_MAX_8BIT);
+}
+
+/* fill dst buffer with nsamples worth of silence */
+static void inline
+sample_silence_float(sample_t * dst, unsigned long nsamples)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  while(nsamples--)
+  {
+    *dst = 0;
+    dst++;
+  }
+}
+
+static bool inline
+ensure_buffer_size(char **buffer, unsigned long *cur_size,
+                   unsigned long needed_size)
+{
+  DEBUG("current size = %lu, needed size = %lu\n", *cur_size, needed_size);
+  if(*cur_size >= needed_size)
+    return TRUE;
+  DEBUG("reallocing\n");
+  char *tmp = realloc(*buffer, needed_size);
+  if(tmp)
+  {
+    *cur_size = needed_size;
+    *buffer = tmp;
+    return TRUE;
+  }
+  DEBUG("reallocing failed\n");
+  return FALSE;
+}
+
+/******************************************************************
+ *    JACK_callback
+ *
+ * every time the jack server wants something from us it calls this
+ * function, so we either deliver it some sound to play or deliver it nothing
+ * to play
+ */
+static int
+JACK_callback(nframes_t nframes, void *arg)
+{
+  jack_driver_t *drv = (jack_driver_t *) arg;
+
+  unsigned int i;
+  int src_error = 0;
+
+  TIMER("start\n");
+  gettimeofday(&drv->previousTime, 0);  /* record the current time */
+
+  CALLBACK_TRACE("nframes %ld, sizeof(sample_t) == %d\n", (long) nframes,
+                 sizeof(sample_t));
+
+  if(!drv->client)
+    ERR("client is closed, this is weird...\n");
+
+  sample_t *out_buffer[MAX_OUTPUT_PORTS];
+  /* retrieve the buffers for the output ports */
+  for(i = 0; i < drv->num_output_channels; i++)
+    out_buffer[i] = (sample_t *) jack_port_get_buffer(drv->output_port[i], nframes);
+
+  sample_t *in_buffer[MAX_INPUT_PORTS];
+  /* retrieve the buffers for the input ports */
+  for(i = 0; i < drv->num_input_channels; i++)
+    in_buffer[i] = (sample_t *) jack_port_get_buffer(drv->input_port[i], nframes);
+
+  /* handle playing state */
+  if(drv->state == PLAYING)
+  {
+    /* handle playback data, if any */
+    if(drv->num_output_channels > 0)
+    {
+      unsigned long jackFramesAvailable = nframes;      /* frames we have left to write to jack */
+      unsigned long numFramesToWrite;   /* num frames we are writing */
+      size_t inputBytesAvailable = jack_ringbuffer_read_space(drv->pPlayPtr);
+      unsigned long inputFramesAvailable;       /* frames we have available */
+
+      inputFramesAvailable = inputBytesAvailable / drv->bytes_per_jack_output_frame;
+      size_t jackBytesAvailable = jackFramesAvailable * drv->bytes_per_jack_output_frame;
+
+      long read = 0;
+
+      CALLBACK_TRACE("playing... jackFramesAvailable = %ld inputFramesAvailable = %ld\n",
+         jackFramesAvailable, inputFramesAvailable);
+
+#if JACK_CLOSE_HACK
+      if(drv->in_use == FALSE)
+      {
+        /* output silence if nothing is being outputted */
+        for(i = 0; i < drv->num_output_channels; i++)
+          sample_silence_float(out_buffer[i], nframes);
+
+        return -1;
+      }
+#endif
+
+      /* make sure our buffer is large enough for the data we are writing */
+      /* ie. callback_buffer1_size < (bytes we already wrote + bytes we are going to write in this loop) */
+      if(!ensure_buffer_size
+         (&drv->callback_buffer1, &drv->callback_buffer1_size,
+          jackBytesAvailable))
+      {
+        ERR("allocated %lu bytes, need %u bytes\n",
+            drv->callback_buffer1_size, jackBytesAvailable);
+        return -1;
+      }
+
+      /* do sample rate conversion if needed & requested */
+      if(drv->output_src && drv->output_sample_rate_ratio != 1.0)
+      {
+        long bytes_needed_write = nframes * drv->bytes_per_jack_output_frame;
+
+        /* make a very good guess at how many raw bytes we'll need to satisfy jack's request after conversion */
+        long bytes_needed_read = min(inputBytesAvailable,
+                                     (double) (bytes_needed_write +
+                                               drv->
+                                               output_sample_rate_ratio
+                                               *
+                                               drv->
+                                               bytes_per_jack_output_frame)
+                                     / drv->output_sample_rate_ratio);
+        DEBUG("guessing that we need %ld bytes in and %ld out for rate conversion ratio = %f\n",
+           bytes_needed_read, bytes_needed_write,
+           drv->output_sample_rate_ratio);
+
+        if(!ensure_buffer_size(&drv->callback_buffer1,
+                               &drv->callback_buffer1_size,
+                               bytes_needed_read))
+        {
+          ERR("could not realloc callback_buffer2!\n");
+          return 1;
+        }
+        if(!ensure_buffer_size(&drv->callback_buffer2,
+                               &drv->callback_buffer2_size,
+                               bytes_needed_write))
+        {
+          ERR("could not realloc callback_buffer2!\n");
+          return 1;
+        }
+
+        if(jackFramesAvailable && inputBytesAvailable > 0)
+        {
+          /* read in the data, but don't move the read pointer until we know how much SRC used */
+          jack_ringbuffer_peek(drv->pPlayPtr, drv->callback_buffer1,
+                               bytes_needed_read);
+
+          SRC_DATA srcdata;
+          srcdata.data_in = (sample_t *) drv->callback_buffer1;
+          srcdata.input_frames = bytes_needed_read / drv->bytes_per_jack_output_frame;
+          srcdata.src_ratio = drv->output_sample_rate_ratio;
+          srcdata.data_out = (sample_t *) drv->callback_buffer2;
+          srcdata.output_frames = nframes;
+          srcdata.end_of_input = 0;     // it's a stream, it never ends
+          DEBUG("input_frames = %ld, output_frames = %ld\n",
+                srcdata.input_frames, srcdata.output_frames);
+          /* convert the sample rate */
+          src_error = src_process(drv->output_src, &srcdata);
+          DEBUG("used = %ld, generated = %ld, error = %d: %s.\n",
+                srcdata.input_frames_used, srcdata.output_frames_gen,
+                src_error, src_strerror(src_error));
+
+          if(src_error == 0)
+          {
+            /* now we can move the read pointer */
+            jack_ringbuffer_read_advance(drv->pPlayPtr,
+                                         srcdata.
+                                         input_frames_used *
+                                         drv->bytes_per_jack_output_frame);
+            /* add on what we wrote */
+            read = srcdata.input_frames_used * drv->bytes_per_output_frame;
+            jackFramesAvailable -= srcdata.output_frames_gen;   /* take away what was used */
+          }
+        }
+
+        if(src_error == 0)
+        {
+          /* demux the stream: we skip over the number of samples we have output channels as the channel data */
+          /* is encoded like chan1,chan2,chan3,chan1,chan2,chan3... */
+          for(i = 0; i < drv->num_output_channels; i++)
+          {
+            demux(out_buffer[i],
+                  (sample_t *) drv->callback_buffer2 + i,
+                  (nframes - jackFramesAvailable), drv->num_output_channels);
+          }
+        }
+      }
+      else                      /* no resampling needed or requested */
+      {
+        /* read as much data from the buffer as is available */
+        if(jackFramesAvailable && inputBytesAvailable > 0)
+        {
+          /* write as many bytes as we have space remaining, or as much as we have data to write */
+          numFramesToWrite = min(jackFramesAvailable, inputFramesAvailable);
+          jack_ringbuffer_read(drv->pPlayPtr, drv->callback_buffer1,
+                               jackBytesAvailable);
+          /* add on what we wrote */
+          read = numFramesToWrite * drv->bytes_per_output_frame;
+          jackFramesAvailable -= numFramesToWrite;      /* take away what was written */
+        }
+        /* demux the stream: we skip over the number of samples we have output channels as the channel data */
+        /* is encoded like chan1,chan2,chan3,chan1,chan2,chan3... */
+        for(i = 0; i < drv->num_output_channels; i++)
+        {
+          demux(out_buffer[i],
+                (sample_t *) drv->callback_buffer1 + i,
+                (nframes - jackFramesAvailable), drv->num_output_channels);
+        }
+      }
+
+      drv->written_client_bytes += read;
+      drv->played_client_bytes += drv->clientBytesInJack;       /* move forward by the previous bytes we wrote since those must have finished by now */
+      drv->clientBytesInJack = read;    /* record the input bytes we wrote to jack */
+
+      /* see if we still have jackBytesLeft here, if we do that means that we
+         ran out of wave data to play and had a buffer underrun, fill in
+         the rest of the space with zero bytes so at least there is silence */
+      if(jackFramesAvailable)
+      {
+        WARN("buffer underrun of %ld frames\n", jackFramesAvailable);
+        for(i = 0; i < drv->num_output_channels; i++)
+          sample_silence_float(out_buffer[i] +
+                               (nframes - jackFramesAvailable),
+                               jackFramesAvailable);
+      }
+    }
+
+    /* handle record data, if any */
+    if(drv->num_input_channels > 0)
+    {
+      long jack_bytes = nframes * drv->bytes_per_jack_input_frame;      /* how many bytes jack is feeding us */
+
+      if(!ensure_buffer_size(&drv->callback_buffer1, &drv->callback_buffer1_size, jack_bytes))
+      {
+        ERR("allocated %lu bytes, need %lu bytes\n",
+            drv->callback_buffer1_size, jack_bytes);
+        return -1;
+      }
+
+      /* mux the invividual channels into one stream */
+      for(i = 0; i < drv->num_input_channels; i++)
+      {
+        mux((sample_t *) drv->callback_buffer1 + i, in_buffer[i],
+            nframes, drv->num_input_channels);
+      }
+
+      /* do sample rate conversion if needed & requested */
+      if(drv->input_src && drv->input_sample_rate_ratio != 1.0)
+      {
+        /* make a very good guess at how many raw bytes we'll need to read all the data jack gave us */
+        long bytes_needed_write = (double) (jack_bytes +
+                                            drv->input_sample_rate_ratio *
+                                            drv->bytes_per_jack_input_frame) *
+          drv->input_sample_rate_ratio;
+        DEBUG("guessing that we need %ld bytes in and %ld out for rate conversion ratio = %f\n",
+              nframes * drv->bytes_per_jack_input_frame,
+              bytes_needed_write, drv->input_sample_rate_ratio);
+
+        if(!ensure_buffer_size(&drv->callback_buffer2,
+                               &drv->callback_buffer2_size,
+                               bytes_needed_write))
+        {
+          ERR("could not realloc callback_buffer2!\n");
+          return 1;
+        }
+
+        SRC_DATA srcdata;
+        srcdata.data_in = (sample_t *) drv->callback_buffer1;
+        srcdata.input_frames = nframes;
+        srcdata.src_ratio = drv->input_sample_rate_ratio;
+        srcdata.data_out = (sample_t *) drv->callback_buffer2;
+        srcdata.output_frames = drv->callback_buffer2_size / drv->bytes_per_jack_input_frame;
+        srcdata.end_of_input = 0;       // it's a stream, it never ends
+        DEBUG("input_frames = %ld, output_frames = %ld\n",
+              srcdata.input_frames, srcdata.output_frames);
+        /* convert the sample rate */
+        src_error = src_process(drv->input_src, &srcdata);
+        DEBUG("used = %ld, generated = %ld, error = %d: %s.\n",
+              srcdata.input_frames_used, srcdata.output_frames_gen,
+              src_error, src_strerror(src_error));
+
+        if(src_error == 0)
+        {
+          long write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+          long bytes_used =  srcdata.output_frames_gen * drv->bytes_per_jack_input_frame;
+          /* if there isn't enough room, make some.  sure this discards data, but when dealing with input sources
+             it seems like it's better to throw away old data than new */
+          if(write_space < bytes_used)
+          {
+            /* the ringbuffer is designed such that only one thread should ever access each pointer.
+               since calling read_advance here will be touching the read pointer which is also accessed
+               by JACK_Read, we need to lock the mutex first for safety */
+            getDriver(drv->deviceID);
+
+            /* double check the write space after we've gained the lock, just in case JACK_Read was being called
+               before we gained it */
+            write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+            if(write_space < bytes_used)
+            {
+              /* hey, we warn about underruns, we might as well warn about overruns as well */
+              WARN("buffer overrun of %ld bytes\n", jack_bytes - write_space);
+              jack_ringbuffer_read_advance(drv->pRecPtr,
+                                           bytes_used - write_space);
+            }
+            releaseDriver(drv);
+          }
+
+          jack_ringbuffer_write(drv->pRecPtr, drv->callback_buffer2,
+                                bytes_used);
+        }
+      }
+      else                      /* no resampling needed */
+      {
+        long write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+        /* if there isn't enough room, make some.  sure this discards data, but when dealing with input sources
+           it seems like it's better to throw away old data than new */
+        if(write_space < jack_bytes)
+        {
+          /* the ringbuffer is designed such that only one thread should ever access each pointer.
+             since calling read_advance here will be touching the read pointer which is also accessed
+             by JACK_Read, we need to lock the mutex first for safety */
+          getDriver(drv->deviceID);
+
+          /* double check the write space after we've gained the lock, just in case JACK_Read was being called
+             before we gained it */
+          write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+          if(write_space < jack_bytes)
+          {
+            ERR("buffer overrun of %ld bytes\n", jack_bytes - write_space);
+            jack_ringbuffer_read_advance(drv->pRecPtr,
+                                         jack_bytes - write_space);
+          }
+          releaseDriver(drv);
+        }
+
+        jack_ringbuffer_write(drv->pRecPtr, drv->callback_buffer1,
+                              jack_bytes);
+      }
+    }
+  }
+  else if(drv->state == PAUSED  ||
+          drv->state == STOPPED ||
+          drv->state == CLOSED  || drv->state == RESET)
+  {
+    CALLBACK_TRACE("%s, outputting silence\n", DEBUGSTATE(drv->state));
+
+    /* output silence if nothing is being outputted */
+    for(i = 0; i < drv->num_output_channels; i++)
+      sample_silence_float(out_buffer[i], nframes);
+
+    /* if we were told to reset then zero out some variables */
+    /* and transition to STOPPED */
+    if(drv->state == RESET)
+    {
+      drv->written_client_bytes = 0;
+      drv->played_client_bytes = 0;     /* number of the clients bytes that jack has played */
+
+      drv->client_bytes = 0;    /* bytes that the client wrote to use */
+
+      drv->clientBytesInJack = 0;       /* number of input bytes in jack(not necessary the number of bytes written to jack) */
+
+      drv->position_byte_offset = 0;
+
+      if(drv->pPlayPtr)
+        jack_ringbuffer_reset(drv->pPlayPtr);
+
+      if(drv->pRecPtr)
+        jack_ringbuffer_reset(drv->pRecPtr);
+
+      drv->state = STOPPED;     /* transition to STOPPED */
+    }
+  }
+
+  CALLBACK_TRACE("done\n");
+  TIMER("finish\n");
+
+  return 0;
+}
+
+
+/******************************************************************
+ *             JACK_bufsize
+ *
+ *             Called whenever the jack server changes the the max number
+ *             of frames passed to JACK_callback
+ */
+static int
+JACK_bufsize(nframes_t nframes, void *arg)
+{
+  jack_driver_t *drv = (jack_driver_t *) arg;
+  TRACE("the maximum buffer size is now %lu frames\n", (long) nframes);
+
+  drv->jack_buffer_size = nframes;
+
+  return 0;
+}
+
+/******************************************************************
+ *		JACK_srate
+ */
+int
+JACK_srate(nframes_t nframes, void *arg)
+{
+  jack_driver_t *drv = (jack_driver_t *) arg;
+
+  drv->jack_sample_rate = (long) nframes;
+
+  /* make sure to recalculate the ratios needed for proper sample rate conversion */
+  drv->output_sample_rate_ratio = (double) drv->jack_sample_rate / (double) drv->client_sample_rate;
+  if(drv->output_src) src_set_ratio(drv->output_src, drv->output_sample_rate_ratio);
+
+  drv->input_sample_rate_ratio = (double) drv->client_sample_rate / (double) drv->jack_sample_rate;
+  if(drv->input_src) src_set_ratio(drv->input_src, drv->input_sample_rate_ratio);
+
+  TRACE("the sample rate is now %lu/sec\n", (long) nframes);
+  return 0;
+}
+
+
+/******************************************************************
+ *		JACK_shutdown
+ *
+ * if this is called then jack shut down... handle this appropriately */
+void
+JACK_shutdown(void *arg)
+{
+  jack_driver_t *drv = (jack_driver_t *) arg;
+
+  TRACE("\n");
+
+  getDriver(drv->deviceID);
+
+  drv->client = 0;              /* reset client */
+  drv->jackd_died = TRUE;
+
+  TRACE("jack shutdown, setting client to 0 and jackd_died to true, closing device\n");
+
+#if JACK_CLOSE_HACK
+  JACK_CloseDevice(drv, TRUE);
+#else
+  JACK_CloseDevice(drv);
+#endif
+
+  TRACE("trying to reconnect right now\n");
+  /* lets see if we can't reestablish the connection */
+  if(JACK_OpenDevice(drv) != ERR_SUCCESS)
+  {
+    ERR("unable to reconnect with jack\n");
+  }
+
+  releaseDriver(drv);
+}
+
+
+/******************************************************************
+ *		JACK_Error
+ *
+ * Callback for jack errors
+ */
+static void
+JACK_Error(const char *desc)
+{
+  ERR("%s\n", desc);
+}
+
+
+/******************************************************************
+ *		JACK_OpenDevice
+ *
+ *  RETURNS: ERR_SUCCESS upon success
+ */
+static int
+JACK_OpenDevice(jack_driver_t * drv)
+{
+  const char **ports;
+  char *our_client_name = 0;
+  unsigned int i;
+  int failed = 0;
+
+  TRACE("creating jack client and setting up callbacks\n");
+
+#if JACK_CLOSE_HACK
+  /* see if this device is already open */
+  if(drv->client)
+  {
+    /* if this device is already in use then it is bad for us to be in here */
+    if(drv->in_use)
+      return ERR_OPENING_JACK;
+
+    TRACE("using existing client\n");
+    drv->in_use = TRUE;
+    return ERR_SUCCESS;
+  }
+#endif
+
+  /* set up an error handler */
+  jack_set_error_function(JACK_Error);
+
+
+  /* build the client name */
+  our_client_name = (char *) malloc(snprintf
+                                    (our_client_name, 0, "%s_%d_%d%02d", client_name, getpid(),
+                                     drv->deviceID, drv->clientCtr + 1) + 1);
+  sprintf(our_client_name, "%s_%d_%d%02d", client_name, getpid(),
+          drv->deviceID, drv->clientCtr++);
+
+  /* try to become a client of the JACK server */
+  TRACE("client name '%s'\n", our_client_name);
+  if((drv->client = jack_client_new(our_client_name)) == 0)
+  {
+    /* try once more */
+    TRACE("trying once more to jack_client_new");
+    if((drv->client = jack_client_new(our_client_name)) == 0)
+    {
+      ERR("jack server not running?\n");
+      free(our_client_name);
+      return ERR_OPENING_JACK;
+    }
+  }
+
+  free(our_client_name);
+
+  TRACE("setting up jack callbacks\n");
+
+  /* JACK server to call `JACK_callback()' whenever
+     there is work to be done. */
+  jack_set_process_callback(drv->client, JACK_callback, drv);
+
+  /* setup a buffer size callback */
+  jack_set_buffer_size_callback(drv->client, JACK_bufsize, drv);
+
+  /* tell the JACK server to call `srate()' whenever
+     the sample rate of the system changes. */
+  jack_set_sample_rate_callback(drv->client, JACK_srate, drv);
+
+  /* tell the JACK server to call `jack_shutdown()' if
+     it ever shuts down, either entirely, or if it
+     just decides to stop calling us. */
+  jack_on_shutdown(drv->client, JACK_shutdown, drv);
+
+  /* display the current sample rate. once the client is activated
+     (see below), you should rely on your own sample rate
+     callback (see above) for this value. */
+  drv->jack_sample_rate = jack_get_sample_rate(drv->client);
+  drv->output_sample_rate_ratio = (double) drv->jack_sample_rate / (double) drv->client_sample_rate;
+  drv->input_sample_rate_ratio = (double) drv->client_sample_rate / (double) drv->jack_sample_rate;
+  TRACE("client sample rate: %lu, jack sample rate: %lu, output ratio = %f, input ratio = %f\n",
+        drv->client_sample_rate, drv->jack_sample_rate,
+        drv->output_sample_rate_ratio, drv->input_sample_rate_ratio);
+
+  drv->jack_buffer_size = jack_get_buffer_size(drv->client);
+
+  /* create the output ports */
+  TRACE("creating output ports\n");
+  for(i = 0; i < drv->num_output_channels; i++)
+  {
+    char portname[32];
+    sprintf(portname, "out_%d", i);
+    TRACE("port %d is named '%s'\n", i, portname);
+    /* NOTE: Yes, this is supposed to be JackPortIsOutput since this is an output */
+    /* port FROM bio2jack */
+    drv->output_port[i] = jack_port_register(drv->client, portname,
+                                             JACK_DEFAULT_AUDIO_TYPE,
+                                             JackPortIsOutput, 0);
+  }
+
+  /* create the input ports */
+  TRACE("creating input ports\n");
+  for(i = 0; i < drv->num_input_channels; i++)
+  {
+    char portname[32];
+    sprintf(portname, "in_%d", i);
+    TRACE("port %d is named '%s'\n", i, portname);
+    /* NOTE: Yes, this is supposed to be JackPortIsInput since this is an input */
+    /* port TO bio2jack */
+    drv->input_port[i] = jack_port_register(drv->client, portname,
+                                            JACK_DEFAULT_AUDIO_TYPE,
+                                            JackPortIsInput, 0);
+  }
+
+#if JACK_CLOSE_HACK
+  drv->in_use = TRUE;
+#endif
+
+  /* tell the JACK server that we are ready to roll */
+  TRACE("calling jack_activate()\n");
+  if(jack_activate(drv->client))
+  {
+    ERR("cannot activate client\n");
+    return ERR_OPENING_JACK;
+  }
+
+  /* if we have output channels and the port connection mode isn't CONNECT_NONE */
+  /* then we should connect up some ports */
+  if((drv->num_output_channels > 0) && (port_connection_mode != CONNECT_NONE))
+  {
+    /* determine how we are to acquire output port names */
+    if((drv->jack_port_name_count == 0) || (drv->jack_port_name_count == 1))
+    {
+      if(drv->jack_port_name_count == 0)
+      {
+        TRACE("jack_get_ports() passing in NULL/NULL\n");
+        ports = jack_get_ports(drv->client, NULL, NULL,
+                               drv->jack_output_port_flags);
+      }
+      else
+      {
+        TRACE("jack_get_ports() passing in port of '%s'\n",
+              drv->jack_port_name[0]);
+        ports = jack_get_ports(drv->client, drv->jack_port_name[0], NULL,
+                               drv->jack_output_port_flags);
+      }
+
+      /* display a trace of the output ports we found */
+      int num_ports = 0;
+      if(ports)
+      {
+        for(i = 0; ports[i]; i++)
+        {
+          TRACE("ports[%d] = '%s'\n", i, ports[i]);
+          num_ports++;
+        }
+      }
+
+      /* ensure that we found enough ports */
+      if(!ports || (i < drv->num_output_channels))
+      {
+        TRACE("ERR: jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+              drv->jack_output_port_flags);
+#if JACK_CLOSE_HACK
+        JACK_CloseDevice(drv, TRUE);
+#else
+        JACK_CloseDevice(drv);
+#endif
+        return ERR_PORT_NOT_FOUND;
+      }
+
+      /* connect a port for each output channel. Note: you can't do this before
+         the client is activated (this may change in the future). */
+      for(i = 0; i < drv->num_output_channels; i++)
+      {
+        TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[i]);
+        if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[i]))
+        {
+          ERR("cannot connect to output port %d('%s')\n", i, ports[i]);
+          failed = 1;
+        }
+      }
+
+      /* only if we are in CONNECT_ALL mode should we keep connecting ports up beyond */
+      /* the minimum number of ports required for each output channel coming into bio2jack */
+      if(port_connection_mode == CONNECT_ALL)
+      {
+          /* It's much cheaper and easier to let JACK do the processing required to
+             connect 2 channels to 4 or 4 channels to 2 or any other combinations.
+             This effectively eliminates the need for sample_move_d16_d16() */
+          if(drv->num_output_channels < num_ports)
+          {
+              for(i = drv->num_output_channels; ports[i]; i++)
+              {
+                  int n = i % drv->num_output_channels;
+                  TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[n]);
+                  if(jack_connect(drv->client, jack_port_name(drv->output_port[n]), ports[i]))
+                  {
+                      // non fatal
+                      ERR("cannot connect to output port %d('%s')\n", n, ports[i]);
+                  }
+              }
+          }
+          else if(drv->num_output_channels > num_ports)
+          {
+              for(i = num_ports; i < drv->num_output_channels; i++)
+              {
+                  int n = i % num_ports;
+                  TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[n]);
+                  if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[n]))
+                  {
+                      // non fatal
+                      ERR("cannot connect to output port %d('%s')\n", i, ports[n]);
+                  }
+              }
+          }
+      }
+
+      free(ports);              /* free the returned array of ports */
+    }
+    else
+    {
+      for(i = 0; i < drv->jack_port_name_count; i++)
+      {
+        TRACE("jack_get_ports() portname %d of '%s\n", i,
+              drv->jack_port_name[i]);
+        ports = jack_get_ports(drv->client, drv->jack_port_name[i], NULL,
+                               drv->jack_output_port_flags);
+
+        if(!ports)
+        {
+          ERR("jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+              drv->jack_output_port_flags);
+          return ERR_PORT_NOT_FOUND;
+        }
+
+        TRACE("ports[%d] = '%s'\n", 0, ports[0]);       /* display a trace of the output port we found */
+
+        /* connect the port */
+        TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[i]);
+        if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[0]))
+        {
+          ERR("cannot connect to output port %d('%s')\n", 0, ports[0]);
+          failed = 1;
+        }
+        free(ports);            /* free the returned array of ports */
+      }
+    }
+  }                             /* if( drv->num_output_channels > 0 ) */
+
+
+  if(drv->num_input_channels > 0)
+  {
+    /* determine how we are to acquire input port names */
+    if((drv->jack_port_name_count == 0) || (drv->jack_port_name_count == 1))
+    {
+      if(drv->jack_port_name_count == 0)
+      {
+        TRACE("jack_get_ports() passing in NULL/NULL\n");
+        ports = jack_get_ports(drv->client, NULL, NULL, drv->jack_input_port_flags);
+      }
+      else
+      {
+        TRACE("jack_get_ports() passing in port of '%s'\n",
+              drv->jack_port_name[0]);
+        ports = jack_get_ports(drv->client, drv->jack_port_name[0], NULL,
+                               drv->jack_input_port_flags);
+      }
+
+      /* display a trace of the input ports we found */
+      int num_ports = 0;
+      if(ports)
+      {
+        for(i = 0; ports[i]; i++)
+        {
+          TRACE("ports[%d] = '%s'\n", i, ports[i]);
+          num_ports++;
+        }
+      }
+
+      /* ensure that we found enough ports */
+      if(!ports || (i < drv->num_input_channels))
+      {
+        TRACE("ERR: jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+              drv->jack_input_port_flags);
+#if JACK_CLOSE_HACK
+        JACK_CloseDevice(drv, TRUE);
+#else
+        JACK_CloseDevice(drv);
+#endif
+        return ERR_PORT_NOT_FOUND;
+      }
+
+      /* connect the ports. Note: you can't do this before
+         the client is activated (this may change in the future). */
+      for(i = 0; i < drv->num_input_channels; i++)
+      {
+        TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[i]);
+        if(jack_connect(drv->client, ports[i], jack_port_name(drv->input_port[i])))
+        {
+          ERR("cannot connect to input port %d('%s')\n", i, ports[i]);
+          failed = 1;
+        }
+      }
+
+      /* It's much cheaper and easier to let JACK do the processing required to
+         connect 2 channels to 4 or 4 channels to 2 or any other combinations.
+         This effectively eliminates the need for sample_move_d16_d16() */
+      if(drv->num_input_channels < num_ports)
+      {
+        for(i = drv->num_input_channels; ports[i]; i++)
+        {
+          int n = i % drv->num_input_channels;
+          TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[n]);
+          if(jack_connect(drv->client, ports[i], jack_port_name(drv->input_port[n])))
+          {
+            // non fatal
+            ERR("cannot connect to input port %d('%s')\n", n, ports[i]);
+          }
+        }
+      }
+      else if(drv->num_input_channels > num_ports)
+      {
+        for(i = num_ports; i < drv->num_input_channels; i++)
+        {
+          int n = i % num_ports;
+          TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[n]);
+          if(jack_connect(drv->client, ports[n], jack_port_name(drv->input_port[i])))
+          {
+            // non fatal
+            ERR("cannot connect to input port %d('%s')\n", i, ports[n]);
+          }
+        }
+      }
+
+      free(ports);              /* free the returned array of ports */
+    }
+    else
+    {
+      for(i = 0; i < drv->jack_port_name_count; i++)
+      {
+        TRACE("jack_get_ports() portname %d of '%s\n", i,
+              drv->jack_port_name[i]);
+        ports = jack_get_ports(drv->client, drv->jack_port_name[i], NULL,
+                               drv->jack_input_port_flags);
+
+        if(!ports)
+        {
+          ERR("jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+              drv->jack_input_port_flags);
+          return ERR_PORT_NOT_FOUND;
+        }
+
+        TRACE("ports[%d] = '%s'\n", 0, ports[0]);       /* display a trace of the input port we found */
+
+        /* connect the port */
+        TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[i]);
+        if(jack_connect(drv->client, jack_port_name(drv->input_port[i]), ports[0]))
+        {
+          ERR("cannot connect to input port %d('%s')\n", 0, ports[0]);
+          failed = 1;
+        }
+        free(ports);            /* free the returned array of ports */
+      }
+    }
+  }                             /* if( drv->num_input_channels > 0 ) */
+
+  /* if something failed we need to shut the client down and return 0 */
+  if(failed)
+  {
+    TRACE("failed, closing and returning error\n");
+#if JACK_CLOSE_HACK
+    JACK_CloseDevice(drv, TRUE);
+#else
+    JACK_CloseDevice(drv);
+#endif
+    return ERR_OPENING_JACK;
+  }
+
+  TRACE("success\n");
+
+  drv->jackd_died = FALSE;      /* clear out this flag so we don't keep attempting to restart things */
+  drv->state = PLAYING;         /* clients seem to behave much better with this on from the start, especially when recording */
+
+  return ERR_SUCCESS;           /* return success */
+}
+
+
+/******************************************************************
+ *		JACK_CloseDevice
+ *
+ *	Close the connection to the server cleanly.
+ *  If close_client is TRUE we close the client for this device instead of
+ *    just marking the device as in_use(JACK_CLOSE_HACK only)
+ */
+#if JACK_CLOSE_HACK
+static void
+JACK_CloseDevice(jack_driver_t * drv, bool close_client)
+#else
+static void
+JACK_CloseDevice(jack_driver_t * drv)
+#endif
+{
+  unsigned int i;
+
+#if JACK_CLOSE_HACK
+  if(close_client)
+  {
+#endif
+
+    TRACE("closing the jack client thread\n");
+    if(drv->client)
+    {
+      TRACE("after jack_deactivate()\n");
+      int errorCode = jack_client_close(drv->client);
+      if(errorCode)
+        ERR("jack_client_close() failed returning an error code of %d\n",
+            errorCode);
+    }
+
+    /* reset client */
+    drv->client = 0;
+
+    /* free up the port strings */
+    TRACE("freeing up %d port strings\n", drv->jack_port_name_count);
+    if(drv->jack_port_name_count > 1)
+    {
+      for(i = 0; i < drv->jack_port_name_count; i++)
+        free(drv->jack_port_name[i]);
+      free(drv->jack_port_name);
+    }
+    JACK_CleanupDriver(drv);
+
+    JACK_ResetFromDriver(drv);
+
+#if JACK_CLOSE_HACK
+  } else
+  {
+    TRACE("setting in_use to FALSE\n");
+    drv->in_use = FALSE;
+
+    if(!drv->client)
+    {
+      TRACE("critical error, closing a device that has no client\n");
+    }
+  }
+#endif
+}
+
+
+
+
+/**************************************/
+/* External interface functions below */
+/**************************************/
+
+/* Clear out any buffered data, stop playing, zero out some variables */
+static void
+JACK_ResetFromDriver(jack_driver_t * drv)
+{
+  TRACE("resetting drv->deviceID(%d)\n", drv->deviceID);
+
+  /* NOTE: we use the RESET state so we don't need to worry about clearing out */
+  /* variables that the callback modifies while the callback is running */
+  /* we set the state to RESET and the callback clears the variables out for us */
+  drv->state = RESET;           /* tell the callback that we are to reset, the callback will transition this to STOPPED */
+}
+
+/* Clear out any buffered data, stop playing, zero out some variables */
+void
+JACK_Reset(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  TRACE("resetting deviceID(%d)\n", deviceID);
+  JACK_ResetFromDriver(drv);
+  releaseDriver(drv);
+}
+
+
+/*
+ * open the audio device for writing to
+ *
+ * deviceID is set to the opened device
+ * if client is non-zero and in_use is FALSE then just set in_use to TRUE
+ *
+ * return value is zero upon success, non-zero upon failure 
+ *
+ * if ERR_RATE_MISMATCH (*rate) will be updated with the jack servers rate
+ */
+int
+JACK_Open(int *deviceID, unsigned int bits_per_channel, unsigned long *rate,
+          int channels)
+{
+  /* we call through to JACK_OpenEx(), but default the input channels to 0 for better backwards
+     compatibility with clients written before recording was available */
+  return JACK_OpenEx(deviceID, bits_per_channel,
+                     rate,
+                     0, channels,
+                     NULL, 0, JackPortIsPhysical);
+}
+
+/*
+ * see JACK_Open() for comments
+ * NOTE: jack_port_name has three ways of being used:
+ *       - NULL - finds all ports with the given flags
+ *       - A single regex string used to retrieve all port names
+ *       - A series of port names, one for each output channel
+ *
+ * we set *deviceID
+ */
+int
+JACK_OpenEx(int *deviceID, unsigned int bits_per_channel,
+            unsigned long *rate,
+            unsigned int input_channels, unsigned int output_channels,
+            const char **jack_port_name,
+            unsigned int jack_port_name_count, unsigned long jack_port_flags)
+{
+  jack_driver_t *drv = 0;
+  unsigned int i;
+  int retval;
+
+  if(input_channels < 1 && output_channels < 1)
+  {
+    ERR("no input OR output channels, nothing to do\n");
+    return ERR_OPENING_JACK;
+  }
+
+  switch (bits_per_channel)
+  {
+  case 8:
+  case 16:
+    break;
+  default:
+    ERR("invalid bits_per_channel\n");
+    return ERR_OPENING_JACK;
+  }
+
+  /* Lock the device_mutex and find one that's not allocated already.
+     We'll keep this lock until we've either made use of it, or given up. */
+  pthread_mutex_lock(&device_mutex);
+
+  for(i = 0; i < MAX_OUTDEVICES; i++)
+  {
+    if(!outDev[i].allocated)
+    {
+      drv = &outDev[i];
+      break;
+    }
+  }
+
+  if(!drv)
+  {
+    ERR("no more devices available\n");
+    return ERR_OPENING_JACK;
+  }
+
+  /* We found an unallocated device, now lock it for extra saftey */
+  getDriver(drv->deviceID);
+
+  TRACE("bits_per_channel=%d rate=%ld, input_channels=%d, output_channels=%d\n",
+     bits_per_channel, *rate, input_channels, output_channels);
+
+  if(output_channels > MAX_OUTPUT_PORTS)
+  {
+    ERR("output_channels == %d, MAX_OUTPUT_PORTS == %d\n", output_channels,
+        MAX_OUTPUT_PORTS);
+    releaseDriver(drv);
+    pthread_mutex_unlock(&device_mutex);
+    return ERR_TOO_MANY_OUTPUT_CHANNELS;
+  }
+
+  if(input_channels > MAX_INPUT_PORTS)
+  {
+    ERR("input_channels == %d, MAX_INPUT_PORTS == %d\n", input_channels,
+        MAX_INPUT_PORTS);
+    releaseDriver(drv);
+    pthread_mutex_unlock(&device_mutex);
+    return ERR_TOO_MANY_INPUT_CHANNELS;
+  }
+
+  drv->jack_output_port_flags = jack_port_flags | JackPortIsInput;      /* port must be input(ie we can put data into it), so mask this in */
+  drv->jack_input_port_flags = jack_port_flags | JackPortIsOutput;      /* port must be output(ie we can get data from it), so mask this in */
+
+  /* check that we have the correct number of port names 
+     FIXME?: not sure how we should handle output ports vs input ports....
+   */
+  if((jack_port_name_count > 1)
+     && ((jack_port_name_count < output_channels)
+         || (jack_port_name_count < input_channels)))
+  {
+    ERR("specified individual port names but not enough, gave %d names, need %d\n",
+       jack_port_name_count, output_channels);
+    releaseDriver(drv);
+    pthread_mutex_unlock(&device_mutex);
+    return ERR_PORT_NAME_OUTPUT_CHANNEL_MISMATCH;
+  } else
+  {
+    /* copy this data into the device information */
+    drv->jack_port_name_count = jack_port_name_count;
+
+    if(drv->jack_port_name_count != 0)
+    {
+      drv->jack_port_name =
+        (char **) malloc(sizeof(char *) * drv->jack_port_name_count);
+      for(i = 0; i < drv->jack_port_name_count; i++)
+      {
+        drv->jack_port_name[i] = strdup(jack_port_name[i]);
+        TRACE("jack_port_name[%d] == '%s'\n", i, jack_port_name[i]);
+      }
+    } else
+    {
+      drv->jack_port_name = NULL;
+      TRACE("jack_port_name = NULL\n");
+    }
+  }
+
+  /* initialize some variables */
+  drv->in_use = FALSE;
+
+  JACK_ResetFromDriver(drv);    /* flushes all queued buffers, sets status to STOPPED and resets some variables */
+
+  /* drv->jack_sample_rate is set by JACK_OpenDevice() */
+  drv->client_sample_rate = *rate;
+  drv->bits_per_channel = bits_per_channel;
+  drv->num_input_channels = input_channels;
+  drv->num_output_channels = output_channels;
+  drv->bytes_per_input_frame = (drv->bits_per_channel * drv->num_input_channels) / 8;
+  drv->bytes_per_output_frame = (drv->bits_per_channel * drv->num_output_channels) / 8;
+  drv->bytes_per_jack_output_frame = sizeof(sample_t) * drv->num_output_channels;
+  drv->bytes_per_jack_input_frame = sizeof(sample_t) * drv->num_input_channels;
+
+  if(drv->num_output_channels > 0)
+  {
+    drv->pPlayPtr = jack_ringbuffer_create(drv->num_output_channels *
+                                           drv->bytes_per_jack_output_frame *
+                                           DEFAULT_RB_SIZE);
+  }
+
+  if(drv->num_input_channels > 0)
+  {
+      drv->pRecPtr = jack_ringbuffer_create(drv->num_input_channels *
+                                            drv->bytes_per_jack_input_frame *
+                                            DEFAULT_RB_SIZE);
+  }
+
+  DEBUG("bytes_per_output_frame == %ld\n", drv->bytes_per_output_frame);
+  DEBUG("bytes_per_input_frame  == %ld\n", drv->bytes_per_input_frame);
+  DEBUG("bytes_per_jack_output_frame == %ld\n",
+        drv->bytes_per_jack_output_frame);
+  DEBUG("bytes_per_jack_input_frame == %ld\n",
+        drv->bytes_per_jack_input_frame);
+
+  /* go and open up the device */
+  retval = JACK_OpenDevice(drv);
+  if(retval != ERR_SUCCESS)
+  {
+    TRACE("error opening jack device\n");
+    releaseDriver(drv);
+    pthread_mutex_unlock(&device_mutex);
+    return retval;
+  }
+  else
+  {
+    TRACE("succeeded opening jack device\n");
+  }
+
+  /* setup SRC objects just in case they'll be needed but only if requested */
+  if(do_sample_rate_conversion)
+  {
+    int error;
+    if(drv->num_output_channels > 0)
+    {
+      drv->output_src = src_new(preferred_src_converter, drv->num_output_channels, &error);
+      if(error != 0)
+      {
+        src_delete(drv->output_src);
+        drv->output_src = 0;
+        ERR("Could not created SRC object for output stream %d: %s\n",
+            error, src_strerror(error));
+      }
+    }
+    if(drv->num_input_channels > 0)
+    {
+      drv->input_src = src_new(preferred_src_converter, drv->num_input_channels, &error);
+      if(error != 0)
+      {
+        src_delete(drv->input_src);
+        drv->input_src = 0;
+        ERR("Could not created SRC object for input stream %d: %s\n",
+            error, src_strerror(error));
+      }
+    }
+  }
+  else if((long) (*rate) != drv->jack_sample_rate)
+  {
+    TRACE("rate of %ld doesn't match jack sample rate of %ld, returning error\n",
+          *rate, drv->jack_sample_rate);
+    *rate = drv->jack_sample_rate;
+#if JACK_CLOSE_HACK
+    JACK_CloseDevice(drv, TRUE);
+#else
+    JACK_CloseDevice(drv);
+#endif
+    releaseDriver(drv);
+    pthread_mutex_unlock(&device_mutex);
+    return ERR_RATE_MISMATCH;
+  }
+
+  drv->allocated = TRUE;        /* record that we opened this device */
+
+  DEBUG("sizeof(sample_t) == %d\n", sizeof(sample_t));
+
+  int periodSize = jack_get_buffer_size(drv->client);
+  int periods = 0;
+  /* FIXME: maybe we should keep different latency values for input vs output? */
+  if(drv->num_output_channels > 0)
+  {
+    periods = jack_port_get_total_latency(drv->client,
+                                          drv->output_port[0]) / periodSize;
+    drv->latencyMS = periodSize * periods * 1000 / (drv->jack_sample_rate *
+                                                    (drv->bits_per_channel / 8 *
+                                                     drv->num_output_channels));
+  }
+  else if(drv->num_input_channels > 0)
+  {
+    periods = jack_port_get_total_latency(drv->client,
+                                          drv->input_port[0]) / periodSize;
+    drv->latencyMS =
+      periodSize * periods * 1000 / (drv->jack_sample_rate *
+                                     (drv->bits_per_channel / 8 *
+                                      drv->num_input_channels));
+  }
+
+  TRACE("drv->latencyMS == %ldms\n", drv->latencyMS);
+
+  *deviceID = drv->deviceID;    /* set the deviceID for the caller */
+  releaseDriver(drv);
+  pthread_mutex_unlock(&device_mutex);
+  return ERR_SUCCESS;           /* success */
+}
+
+/* Close the jack device */
+//FIXME: add error handling in here at some point...
+/* NOTE: return 0 for success, non-zero for failure */
+int
+JACK_Close(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+
+  TRACE("deviceID(%d)\n", deviceID);
+
+#if JACK_CLOSE_HACK
+  JACK_CloseDevice(drv, TRUE);
+#else
+  JACK_CloseDevice(drv);
+#endif
+
+  JACK_ResetFromDriver(drv);    /* reset this device to a normal starting state */
+
+  pthread_mutex_lock(&device_mutex);
+
+  /* free buffer memory */
+  drv->callback_buffer1_size = 0;
+  if(drv->callback_buffer1) free(drv->callback_buffer1);
+  drv->callback_buffer1 = 0;
+
+  drv->callback_buffer2_size = 0;
+  if(drv->callback_buffer2) free(drv->callback_buffer2);
+  drv->callback_buffer2 = 0;
+
+  drv->rw_buffer1_size = 0;
+  if(drv->rw_buffer1) free(drv->rw_buffer1);
+  drv->rw_buffer1 = 0;
+
+  if(drv->pPlayPtr) jack_ringbuffer_free(drv->pPlayPtr);
+  drv->pPlayPtr = 0;
+
+  if(drv->pRecPtr) jack_ringbuffer_free(drv->pRecPtr);
+  drv->pRecPtr = 0;
+
+  /* free the SRC objects */
+  if(drv->output_src) src_delete(drv->output_src);
+  drv->output_src = 0;
+
+  if(drv->input_src) src_delete(drv->input_src);
+  drv->input_src = 0;
+
+  drv->allocated = FALSE;       /* release this device */
+
+  pthread_mutex_unlock(&device_mutex);
+
+  releaseDriver(drv);
+
+  return 0;
+}
+
+/* If we haven't already taken in the max allowed data then create a wave header */
+/* to package the audio data and attach the wave header to the end of the */
+/* linked list of wave headers */
+/* These wave headers will be peeled off as they are played by the callback routine */
+/* Return value is the number of bytes written */
+/* NOTE: this function takes the length of data to be written bytes */
+long
+JACK_Write(int deviceID, unsigned char *data, unsigned long bytes)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+
+  long frames_free, frames;
+
+  TIMER("start\n");
+
+  TRACE("deviceID(%d), bytes == %ld\n", deviceID, bytes);
+
+  /* check and see that we have enough space for this audio */
+  frames_free =
+    jack_ringbuffer_write_space(drv->pPlayPtr) /
+    drv->bytes_per_jack_output_frame;
+  frames = bytes / drv->bytes_per_output_frame;
+  TRACE("frames free == %ld, bytes = %lu\n", frames_free, bytes);
+
+  TRACE("state = '%s'\n", DEBUGSTATE(drv->state));
+  /* if we are currently STOPPED we should start playing now... 
+     do this before the check for bytes == 0 since some clients like
+     to write 0 bytes the first time out */
+  if(drv->state == STOPPED)
+  {
+    TRACE("currently STOPPED, transitioning to PLAYING\n");
+    drv->state = PLAYING;
+  }
+
+  /* handle the case where the user calls this routine with 0 bytes */
+  if(bytes == 0 || frames_free < 1)
+  {
+    TRACE("no room left\n");
+    TIMER("finish (nothing to do, buffer is full)\n");
+    releaseDriver(drv);
+    return 0;                   /* indicate that we couldn't write any bytes */
+  }
+
+  frames = min(frames, frames_free);
+  long jack_bytes = frames * drv->bytes_per_jack_output_frame;
+  if(!ensure_buffer_size(&drv->rw_buffer1, &drv->rw_buffer1_size, jack_bytes))
+  {
+    ERR("couldn't allocate enough space for the buffer\n");
+    releaseDriver(drv);
+    return 0;
+  }
+  /* adjust bytes to be how many client bytes we're actually writing */
+  bytes = frames * drv->bytes_per_output_frame;
+
+  /* convert from client samples to jack samples
+     we have to tell it how many samples there are, which is frames * channels */
+  switch (drv->bits_per_channel)
+  {
+  case 8:
+    sample_move_char_float((sample_t *) drv->rw_buffer1, (char *) data,
+                           frames * drv->num_output_channels);
+  case 16:
+    sample_move_short_float((sample_t *) drv->rw_buffer1, (short *) data,
+                            frames * drv->num_output_channels);
+    break;
+  }
+
+  int i;
+  for(i = 0; i < drv->num_output_channels; i++)
+  {
+    /* apply volume to the floating value */
+    if(drv->volumeEffectType == dbAttenuation)
+    {
+      /* assume the volume setting is dB of attenuation, a volume of 0 */
+      /* is 0dB attenuation */
+      float volume = powf(10.0, -((float) drv->volume[i]) / 20.0);
+      float_volume_effect((sample_t *) drv->rw_buffer1 + i,
+                          frames, volume, drv->num_output_channels);
+    } else
+    {
+      float_volume_effect((sample_t *) drv->rw_buffer1 + i, frames,
+                          ((float) drv->volume[i] / 100.0),
+                          drv->num_output_channels);
+    }
+  }
+
+  DEBUG("ringbuffer read space = %d, write space = %d\n",
+        jack_ringbuffer_read_space(drv->pPlayPtr),
+        jack_ringbuffer_write_space(drv->pPlayPtr));
+
+  jack_ringbuffer_write(drv->pPlayPtr, drv->rw_buffer1, jack_bytes);
+  DEBUG("wrote %lu bytes, %lu jack_bytes\n", bytes, jack_bytes);
+
+  DEBUG("ringbuffer read space = %d, write space = %d\n",
+        jack_ringbuffer_read_space(drv->pPlayPtr),
+        jack_ringbuffer_write_space(drv->pPlayPtr));
+
+  drv->client_bytes += bytes;   /* update client_bytes */
+
+  TIMER("finish\n");
+
+  DEBUG("returning bytes written of %ld\n", bytes);
+
+  releaseDriver(drv);
+  return bytes;                 /* return the number of bytes we wrote out */
+}
+
+long
+JACK_Read(int deviceID, unsigned char *data, unsigned long bytes)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+
+  long frames_available, frames;
+
+  TIMER("start\n");
+
+  TRACE("deviceID(%d), bytes == %ld\n", deviceID, bytes);
+
+  /* find out if there's any room to write this data */
+  frames_available =
+    jack_ringbuffer_read_space(drv->pRecPtr) /
+    drv->bytes_per_jack_input_frame;
+  frames = bytes / drv->bytes_per_input_frame;
+  DEBUG("frames available = %ld, bytes = %lu\n", frames_available, bytes);
+
+  TRACE("state = '%s'\n", DEBUGSTATE(drv->state));
+  /* if we are currently STOPPED we should start recording now... */
+  if(drv->state == STOPPED)
+  {
+    TRACE("currently STOPPED, transitioning to PLAYING\n");
+    drv->state = PLAYING;
+  }
+
+  /* handle the case where the user calls this routine with 0 bytes */
+  if(bytes == 0 || frames_available < 1)
+  {
+    TRACE("no bytes in buffer\n");
+
+    TIMER("finish (nothing to do)\n");
+    releaseDriver(drv);
+    return 0;
+  }
+
+  frames = min(frames, frames_available);
+  long jack_bytes = frames * drv->bytes_per_jack_input_frame;
+  if(!ensure_buffer_size(&drv->rw_buffer1, &drv->rw_buffer1_size, jack_bytes))
+  {
+    ERR("couldn't allocate enough space for the buffer\n");
+    releaseDriver(drv);
+    return 0;
+  }
+
+  DEBUG("ringbuffer read space = %d, write space = %d\n",
+        jack_ringbuffer_read_space(drv->pRecPtr),
+        jack_ringbuffer_write_space(drv->pRecPtr));
+
+  jack_ringbuffer_read(drv->pRecPtr, drv->rw_buffer1,
+                       frames * drv->bytes_per_jack_input_frame);
+
+  DEBUG("ringbuffer read space = %d, write space = %d\n",
+        jack_ringbuffer_read_space(drv->pRecPtr),
+        jack_ringbuffer_write_space(drv->pRecPtr));
+
+  int i;
+  for(i = 0; i < drv->num_output_channels; i++)
+  {
+    /* apply volume to the floating value */
+    if(drv->volumeEffectType == dbAttenuation)
+    {
+      /* assume the volume setting is dB of attenuation, a volume of 0 */
+      /* is 0dB attenuation */
+      float volume = powf(10.0, -((float) drv->volume[i]) / 20.0);
+      float_volume_effect((sample_t *) drv->rw_buffer1 + i,
+                          frames, volume, drv->num_output_channels);
+    } else
+    {
+      float_volume_effect((sample_t *) drv->rw_buffer1 + i, frames,
+                          ((float) drv->volume[i] / 100.0),
+                          drv->num_output_channels);
+    }
+  }
+
+  /* convert from jack samples to client samples
+     we have to tell it how many samples there are, which is frames * channels */
+  switch (drv->bits_per_channel)
+  {
+  case 8:
+    sample_move_float_char((char *) data, (sample_t *) drv->rw_buffer1,
+                           frames * drv->num_input_channels);
+    break;
+  case 16:
+    sample_move_float_short((short *) data, (sample_t *) drv->rw_buffer1,
+                            frames * drv->num_input_channels);
+    break;
+  }
+
+  TIMER("finish\n");
+
+  long read_bytes = frames * drv->bytes_per_input_frame;
+
+  DEBUG("returning bytes read of %ld\n", bytes);
+
+  releaseDriver(drv);
+  return read_bytes;
+}
+
+/* return ERR_SUCCESS for success */
+static int
+JACK_SetVolumeForChannelFromDriver(jack_driver_t * drv,
+                                   unsigned int channel, unsigned int volume)
+{
+  /* TODO?: maybe we should have different volume levels for input & output */
+  /* ensure that we have the channel we are setting volume for */
+  if(channel > (drv->num_output_channels - 1))
+    return 1;
+
+  if(volume > 100)
+    volume = 100;               /* check for values in excess of max */
+
+  drv->volume[channel] = volume;
+  return ERR_SUCCESS;
+}
+
+/* return ERR_SUCCESS for success */
+int
+JACK_SetVolumeForChannel(int deviceID, unsigned int channel,
+                         unsigned int volume)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  int retval = JACK_SetVolumeForChannelFromDriver(drv, channel, volume);
+  releaseDriver(drv);
+  return retval;
+}
+
+/* Set the volume */
+/* return 0 for success */
+/* NOTE: we check for invalid volume values */
+int
+JACK_SetAllVolume(int deviceID, unsigned int volume)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  unsigned int i;
+
+  TRACE("deviceID(%d), setting volume of %d\n", deviceID, volume);
+
+  for(i = 0; i < drv->num_output_channels; i++)
+  {
+    if(JACK_SetVolumeForChannelFromDriver(drv, i, volume) != ERR_SUCCESS)
+    {
+      releaseDriver(drv);
+      return 1;
+    }
+  }
+
+  releaseDriver(drv);
+  return ERR_SUCCESS;
+}
+
+/* Return the current volume in the inputted pointers */
+/* NOTE: we check for null pointers being passed in just in case */
+void
+JACK_GetVolumeForChannel(int deviceID, unsigned int channel,
+                         unsigned int *volume)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+
+  /* ensure that we have the channel we are getting volume for */
+  if(channel > (drv->num_output_channels - 1))
+  {
+    ERR("asking for channel index %d but we only have %ld channels\n", channel, drv->num_output_channels);
+    releaseDriver(drv);
+    return;
+  }
+
+  if(volume)
+    *volume = drv->volume[channel];
+
+#if VERBOSE_OUTPUT
+  if(volume)
+  {
+    TRACE("deviceID(%d), returning volume of %d for channel %d\n",
+          deviceID, *volume, channel);
+  }
+  else
+  {
+    TRACE("volume is null, can't dereference it\n");
+  }
+#endif
+
+  releaseDriver(drv);
+}
+
+
+/* linear means 0 volume is silence, 100 is full volume */
+/* dbAttenuation means 0 volume is 0dB attenuation */
+/* Bio2jack defaults to linear */
+enum JACK_VOLUME_TYPE
+JACK_SetVolumeEffectType(int deviceID, enum JACK_VOLUME_TYPE type)
+{
+  enum JACK_VOLUME_TYPE retval;
+  jack_driver_t *drv = getDriver(deviceID);
+
+  TRACE("setting type of '%s'\n",
+        (type == dbAttenuation ? "dbAttenuation" : "linear"));
+
+  retval = drv->volumeEffectType;
+  drv->volumeEffectType = type;
+
+  releaseDriver(drv);
+  return retval;
+}
+
+
+/* Controls the state of the playback(playing, paused, ...) */
+int
+JACK_SetState(int deviceID, enum status_enum state)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+
+  switch (state)
+  {
+  case PAUSED:
+    drv->state = PAUSED;
+    break;
+  case PLAYING:
+    drv->state = PLAYING;
+    break;
+  case STOPPED:
+    drv->state = STOPPED;
+    break;
+  default:
+    TRACE("unknown state of %d\n", state);
+  }
+
+  TRACE("%s\n", DEBUGSTATE(drv->state));
+
+  releaseDriver(drv);
+  return 0;
+}
+
+/* Retrieve the current state of the device */
+enum status_enum
+JACK_GetState(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  enum status_enum return_val;
+
+  return_val = drv->state;
+  releaseDriver(drv);
+
+  TRACE("deviceID(%d), returning current state of %s\n", deviceID,
+        DEBUGSTATE(return_val));
+  return return_val;
+}
+
+/* Retrieve the number of bytes per second we are outputting */
+unsigned long
+JACK_GetOutputBytesPerSecondFromDriver(jack_driver_t * drv)
+{
+  unsigned long return_val;
+
+  return_val = drv->bytes_per_output_frame * drv->client_sample_rate;
+
+#if VERBOSE_OUTPUT
+  TRACE("deviceID(%d), return_val = %ld\n", drv->deviceID, return_val);
+#endif
+
+  return return_val;
+}
+
+/* Retrieve the number of bytes per second we are outputting */
+unsigned long
+JACK_GetOutputBytesPerSecond(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  unsigned long return_val;
+
+  return_val = JACK_GetOutputBytesPerSecondFromDriver(drv);
+  releaseDriver(drv);
+
+  return return_val;
+}
+
+/* Retrieve the number of input bytes(from jack) per second we are outputting
+   to the user of bio2jack */
+static long
+JACK_GetInputBytesPerSecondFromDriver(jack_driver_t * drv)
+{
+  long return_val;
+
+  return_val = drv->bytes_per_input_frame * drv->client_sample_rate;
+#if VERBOSE_OUTPUT
+  TRACE("drv->deviceID(%d), return_val = %ld\n", drv->deviceID, return_val);
+#endif
+
+  return return_val;
+}
+
+/* Retrieve the number of input bytes(from jack) per second we are outputting
+   to the user of bio2jack */
+unsigned long
+JACK_GetInputBytesPerSecond(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val = JACK_GetInputBytesPerSecondFromDriver(drv);
+  releaseDriver(drv);
+
+#if VERBOSE_OUTPUT
+  TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val);
+#endif
+
+  return return_val;
+}
+
+/* Return the number of bytes we have buffered thus far for output */
+/* NOTE: convert from output bytes to input bytes in here */
+static long
+JACK_GetBytesStoredFromDriver(jack_driver_t * drv)
+{
+  if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0)
+    return 0;
+
+  /* leave at least one frame in the buffer at all times to prevent underruns */
+  long return_val =
+    jack_ringbuffer_read_space(drv->pPlayPtr) - drv->jack_buffer_size;
+  if(return_val <= 0)
+  {
+    return_val = 0;
+  } else
+  {
+    /* adjust from jack bytes to client bytes */
+    return_val =
+      return_val / drv->bytes_per_jack_output_frame *
+      drv->bytes_per_output_frame;
+  }
+
+  return return_val;
+}
+
+/* An approximation of how many bytes we have to send out to jack */
+/* that is computed as if we were sending jack a continuous stream of */
+/* bytes rather than chunks during discrete callbacks.  */
+/* Return the number of bytes we have buffered thus far for output */
+/* NOTE: convert from output bytes to input bytes in here */
+unsigned long
+JACK_GetBytesStored(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long retval = JACK_GetBytesStoredFromDriver(drv);
+  releaseDriver(drv);
+  TRACE("deviceID(%d), retval = %ld\n", deviceID, retval);
+  return retval;
+}
+
+static unsigned long
+JACK_GetBytesFreeSpaceFromDriver(jack_driver_t * drv)
+{
+  if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0)
+    return 0;
+
+  /* leave at least one frame in the buffer at all times to prevent underruns */
+  long return_val = jack_ringbuffer_write_space(drv->pPlayPtr) - drv->jack_buffer_size;
+  if(return_val <= 0)
+  {
+    return_val = 0;
+  } else
+  {
+    /* adjust from jack bytes to client bytes */
+    return_val =
+      return_val / drv->bytes_per_jack_output_frame *
+      drv->bytes_per_output_frame;
+  }
+
+  return return_val;
+}
+
+/* Return the number of bytes we can write to the device */
+unsigned long
+JACK_GetBytesFreeSpace(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  unsigned long return_val;
+
+  return_val = JACK_GetBytesFreeSpaceFromDriver(drv);
+  releaseDriver(drv);
+
+  if(return_val < 0) return_val = 0;
+
+  TRACE("deviceID(%d), retval == %ld\n", deviceID, return_val);
+
+  return return_val;
+}
+
+/* bytes of space used in the input buffer */
+unsigned long
+JACK_GetBytesUsedSpace(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val;
+
+  if(drv->pRecPtr == 0 || drv->bytes_per_jack_input_frame == 0)
+  {
+    return_val = 0;
+  } else
+  {
+    /* adjust from jack bytes to client bytes */
+    return_val =
+      jack_ringbuffer_read_space(drv->pRecPtr) /
+      drv->bytes_per_jack_input_frame * drv->bytes_per_input_frame;
+  }
+
+  releaseDriver(drv);
+
+  if(return_val < 0)
+    return_val = 0;
+  TRACE("deviceID(%d), retval == %ld\n", deviceID, return_val);
+
+  return return_val;
+}
+
+/* Get the current position of the driver, either in bytes or */
+/* in milliseconds */
+/* NOTE: this is position relative to input bytes, output bytes may differ greatly due to
+   input vs. output channel count */
+static long
+JACK_GetPositionFromDriver(jack_driver_t * drv, enum pos_enum position,
+                           int type)
+{
+  long return_val = 0;
+  struct timeval now;
+  long elapsedMS;
+  double sec2msFactor = 1000;
+
+  char *type_str = "UNKNOWN type";
+
+  /* if we are reset we should return a position of 0 */
+  if(drv->state == RESET)
+  {
+    TRACE("we are currently RESET, returning 0\n");
+    return 0;
+  }
+
+  if(type == WRITTEN)
+  {
+    type_str = "WRITTEN";
+    return_val = drv->client_bytes;
+  } else if(type == WRITTEN_TO_JACK)
+  {
+    type_str = "WRITTEN_TO_JACK";
+    return_val = drv->written_client_bytes;
+  } else if(type == PLAYED)       /* account for the elapsed time for the played_bytes */
+  {
+    type_str = "PLAYED";
+    return_val = drv->played_client_bytes;
+    gettimeofday(&now, 0);
+
+    elapsedMS = TimeValDifference(&drv->previousTime, &now);    /* find the elapsed milliseconds since last JACK_Callback() */
+
+    TRACE("elapsedMS since last callback is '%ld'\n", elapsedMS);
+
+    /* account for the bytes played since the last JACK_Callback() */
+    /* NOTE: [Xms * (Bytes/Sec)] * (1 sec/1,000ms) */
+    /* NOTE: don't do any compensation if no data has been sent to jack since the last callback */
+    /* as this would result a bogus computed result */
+    if(drv->clientBytesInJack != 0)
+    {
+      return_val += (long) ((double) elapsedMS *
+                            ((double) JACK_GetOutputBytesPerSecondFromDriver(drv) /
+                             sec2msFactor));
+    } else
+    {
+      TRACE("clientBytesInJack == 0\n");
+    }
+  }
+
+  /* add on the offset */
+  return_val += drv->position_byte_offset;
+
+  /* convert byte position to milliseconds value if necessary */
+  if(position == MILLISECONDS)
+  {
+    if(JACK_GetOutputBytesPerSecondFromDriver(drv) != 0)
+    {
+      return_val = (long) (((double) return_val /
+                            (double) JACK_GetOutputBytesPerSecondFromDriver(drv)) *
+                           (double) sec2msFactor);
+    } else
+    {
+      return_val = 0;
+    }
+  }
+
+  TRACE("drv->deviceID(%d), type(%s), return_val = %ld\n", drv->deviceID,
+        type_str, return_val);
+
+  return return_val;
+}
+
+/* Get the current position of the driver, either in bytes or */
+/* in milliseconds */
+/* NOTE: this is position relative to input bytes, output bytes may differ greatly due to input vs. output channel count */
+long
+JACK_GetPosition(int deviceID, enum pos_enum position, int type)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long retval = JACK_GetPositionFromDriver(drv, position, type);
+  releaseDriver(drv);
+  TRACE("retval == %ld\n", retval);
+  return retval;
+}
+
+// Set position always applies to written bytes
+// NOTE: we must apply this instantly because if we pass this as a message
+//   to the callback we risk the user sending us audio data in the mean time
+//   and there is no need to send this as a message, we don't modify any
+//   internal variables
+void
+JACK_SetPositionFromDriver(jack_driver_t * drv, enum pos_enum position,
+                           long value)
+{
+  double sec2msFactor = 1000;
+#if TRACE_ENABLE
+  long input_value = value;
+#endif
+
+  /* convert the incoming value from milliseconds into bytes */
+  if(position == MILLISECONDS)
+  {
+    value = (long) (((double) value *
+                     (double) JACK_GetOutputBytesPerSecondFromDriver(drv)) /
+                    sec2msFactor);
+  }
+
+  /* ensure that if the user asks for the position */
+  /* they will at this instant get the correct position */
+  drv->position_byte_offset = value - drv->client_bytes;
+
+  TRACE("deviceID(%d) input_value of %ld %s, new value of %ld, setting position_byte_offset to %ld\n",
+        drv->deviceID, input_value, (position == MILLISECONDS) ? "ms" : "bytes",
+        value, drv->position_byte_offset);
+}
+
+// Set position always applies to written bytes
+// NOTE: we must apply this instantly because if we pass this as a message
+//   to the callback we risk the user sending us audio data in the mean time
+//   and there is no need to send this as a message, we don't modify any
+//   internal variables
+void
+JACK_SetPosition(int deviceID, enum pos_enum position, long value)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  JACK_SetPositionFromDriver(drv, position, value);
+  releaseDriver(drv);
+
+  TRACE("deviceID(%d) value of %ld\n", drv->deviceID, value);
+}
+
+/* Return the number of bytes per frame, or (output_channels * bits_per_channel) / 8 */
+unsigned long
+JACK_GetBytesPerOutputFrame(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val = drv->bytes_per_output_frame;
+  releaseDriver(drv);
+  TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val);
+  return return_val;
+}
+
+/* Return the number of bytes per frame, or (input_channels * bits_per_channel) / 8 */
+unsigned long
+JACK_GetBytesPerInputFrame(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val = drv->bytes_per_input_frame;
+  releaseDriver(drv);
+  TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val);
+  return return_val;
+}
+
+/* Return the number of output bytes we buffer max */
+long
+JACK_GetMaxOutputBufferedBytes(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val;
+
+  if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0) return_val = 0;
+
+  /* adjust from jack bytes to client bytes */
+  return_val =
+    (jack_ringbuffer_read_space(drv->pPlayPtr) +
+     jack_ringbuffer_write_space(drv->pPlayPtr)) /
+    drv->bytes_per_jack_output_frame * drv->bytes_per_output_frame;
+
+  releaseDriver(drv);
+
+  TRACE("return_val = %ld\n", return_val);
+
+  return return_val;
+}
+
+/* Return the number of input bytes we buffer max */
+long
+JACK_GetMaxInputBufferedBytes(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val;
+
+  if(drv->pRecPtr == 0 || drv->bytes_per_jack_input_frame == 0) return_val = 0;
+
+  /* adjust from jack bytes to client bytes */
+  return_val =
+    (jack_ringbuffer_read_space(drv->pRecPtr) +
+     jack_ringbuffer_write_space(drv->pRecPtr)) /
+    drv->bytes_per_jack_input_frame * drv->bytes_per_input_frame;
+
+  releaseDriver(drv);
+
+  TRACE("return_val = %ld\n", return_val);
+
+  return return_val;
+}
+
+/* Get the number of output channels */
+int
+JACK_GetNumOutputChannels(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  int return_val = drv->num_output_channels;
+  releaseDriver(drv);
+  TRACE("getting num_output_channels of %d\n", return_val);
+  return return_val;
+}
+
+/* Get the number of input channels */
+int
+JACK_GetNumInputChannels(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  int return_val = drv->num_input_channels;
+  releaseDriver(drv);
+  TRACE("getting num_input_channels of %d\n", return_val);
+  return return_val;
+}
+
+/* Get the number of samples per second, the sample rate */
+long
+JACK_GetSampleRate(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  int return_val = drv->client_sample_rate;
+  releaseDriver(drv);
+  TRACE("getting sample_rate of %d\n", return_val);
+  return return_val;
+}
+
+void
+JACK_CleanupDriver(jack_driver_t * drv)
+{
+  TRACE("\n");
+  /* things that need to be reset both in JACK_Init & JACK_CloseDevice */
+  drv->client = 0;
+  drv->in_use = FALSE;
+  drv->state = CLOSED;
+  drv->jack_sample_rate = 0;
+  drv->output_sample_rate_ratio = 1.0;
+  drv->input_sample_rate_ratio = 1.0;
+  drv->jackd_died = FALSE;
+  gettimeofday(&drv->previousTime, 0);  /* record the current time */
+  gettimeofday(&drv->last_reconnect_attempt, 0);
+}
+
+/* Initialize the jack porting library to a clean state */
+void
+JACK_Init(void)
+{
+  jack_driver_t *drv;
+  int x, y;
+
+  if(init_done)
+  {
+    TRACE("not initing twice\n");
+    return;
+  }
+
+  init_done = 1;
+
+  TRACE("\n");
+
+  pthread_mutex_lock(&device_mutex);
+
+  /* initialize the device structures */
+  for(x = 0; x < MAX_OUTDEVICES; x++)
+  {
+    drv = &outDev[x];
+
+    pthread_mutex_init(&drv->mutex, NULL);
+
+    getDriver(x);
+
+    memset(drv, 0, sizeof(jack_driver_t));
+    drv->volumeEffectType = linear;
+    drv->deviceID = x;
+
+    for(y = 0; y < MAX_OUTPUT_PORTS; y++)       /* make all volume 25% as a default */
+      drv->volume[y] = 25;
+
+    JACK_CleanupDriver(drv);
+    JACK_ResetFromDriver(drv);
+    releaseDriver(drv);
+  }
+
+  client_name = 0;              /* initialize the name to null */
+  do_sample_rate_conversion = TRUE;     /* default to on */
+  JACK_SetClientName("bio2jack");
+
+  pthread_mutex_unlock(&device_mutex);
+
+  TRACE("finished\n");
+}
+
+/* Get the latency, in frames, of jack */
+long
+JACK_GetJackOutputLatency(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val = 0;
+
+  if(drv->client && drv->num_output_channels)
+    return_val = jack_port_get_total_latency(drv->client, drv->output_port[0]);
+
+  TRACE("got latency of %ld frames\n", return_val);
+
+  releaseDriver(drv);
+  return return_val;
+}
+
+/* Get the latency, in frames, of jack */
+long
+JACK_GetJackInputLatency(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val = 0;
+
+  if(drv->client && drv->num_input_channels)
+    return_val = jack_port_get_total_latency(drv->client, drv->input_port[0]);
+
+  TRACE("got latency of %ld frames\n", return_val);
+
+  releaseDriver(drv);
+  return return_val;
+}
+
+/* bytes that jack requests during each callback */
+unsigned long
+JACK_GetJackBufferedBytes(int deviceID)
+{
+  jack_driver_t *drv = getDriver(deviceID);
+  long return_val;
+
+  if(drv->bytes_per_jack_output_frame == 0)
+  {
+    return_val = 0;
+  } else
+  {
+    /* adjust from jack bytes to client bytes */
+    return_val =
+      drv->jack_buffer_size / drv->bytes_per_jack_output_frame *
+      drv->bytes_per_output_frame * drv->num_output_channels;
+  }
+
+  releaseDriver(drv);
+  return return_val;
+}
+
+/* value = TRUE, perform sample rate conversion */
+void
+JACK_DoSampleRateConversion(bool value)
+{
+  do_sample_rate_conversion = value;
+}
+
+/* FIXME: put the filename of the resample library header file with the decoders in here */
+/* consider mapping them in the bio2jack.h header file since its useless to the user unless */
+/* they can figure out wtf the settings on */
+void
+JACK_SetSampleRateConversionFunction(int converter)
+{
+  preferred_src_converter = converter;
+}
+
+/* set the client name that will be reported to jack when we open a */
+/* connection via JACK_OpenDevice() */
+void
+JACK_SetClientName(char *name)
+{
+  if(name)
+  {
+    if(client_name) free(client_name);
+
+    /* jack_client_name_size() is the max length of a client name, including
+       the terminating null. */
+    int size = strlen(name) + 1;        /* take into account the terminating null */
+    if(size > jack_client_name_size())
+      size = jack_client_name_size();
+
+    client_name = malloc(size);
+    if(client_name)
+      snprintf(client_name, size, "%s", name);
+    else
+      ERR("unable to allocate %d bytes for client_name\n", size);
+  }
+}
+
+void
+JACK_SetPortConnectionMode(enum JACK_PORT_CONNECTION_MODE mode)
+{
+    port_connection_mode = mode;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/jack/bio2jack.h	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2003-2004 Chris Morgan <cmorgan@alum.wpi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _H_JACK_OUT_H
+#define _H_JACK_OUT_H
+
+#include <jack/jack.h>
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#define bool long
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define ERR_SUCCESS                           0
+#define ERR_OPENING_JACK                      1
+#define ERR_RATE_MISMATCH                     2
+#define ERR_BYTES_PER_OUTPUT_FRAME_INVALID    3
+#define ERR_BYTES_PER_INPUT_FRAME_INVALID     4
+#define ERR_TOO_MANY_OUTPUT_CHANNELS          5
+#define ERR_PORT_NAME_OUTPUT_CHANNEL_MISMATCH 6
+#define ERR_PORT_NOT_FOUND                    7
+#define ERR_TOO_MANY_INPUT_CHANNELS           8
+#define ERR_PORT_NAME_INPUT_CHANNEL_MISMATCH  9
+
+enum status_enum { PLAYING, PAUSED, STOPPED, CLOSED, RESET };
+enum pos_enum    { BYTES, MILLISECONDS };
+
+#define PLAYED          1 /* played out of the speakers(estimated value but should be close */
+#define WRITTEN_TO_JACK 2 /* amount written out to jack */
+#define WRITTEN         3 /* amount written to the bio2jack device */
+
+/**********************/
+/* External functions */
+void JACK_Init(void); /* call this before any other bio2jack calls */
+void JACK_DoSampleRateConversion(bool value); /* whether the next device that's Open()d should do
+                                                 sample rate conversion if necessary */
+void JACK_SetSampleRateConversionFunction(int converter); /* which SRC converter function should be used
+                                                             for the next Open()d device */
+int  JACK_Open(int *deviceID, unsigned int bits_per_sample, unsigned long *rate, int channels); /* Note: defaults to 0 input channels
+												   if you need input (record) use OpenEx
+												   instead */
+int  JACK_OpenEx(int *deviceID, unsigned int bits_per_channel,
+                 unsigned long *rate,
+                 unsigned int input_channels, unsigned int output_channels,
+                 const char **jack_port_name, unsigned int jack_port_name_count,
+                 unsigned long jack_port_flags);
+int  JACK_Close(int deviceID); /* return 0 for success */
+void JACK_Reset(int deviceID); /* free all buffered data and reset several values in the device */
+long JACK_Write(int deviceID, unsigned char *data, unsigned long bytes); /* returns the number of bytes written */
+long JACK_Read(int deviceID, unsigned char *data, unsigned long bytes); /* returns the number of bytes read */
+
+/* state setting values */
+/* set/get the written/played/buffered value based on a byte or millisecond input value */
+long JACK_GetPosition(int deviceID, enum pos_enum position, int type);
+void JACK_SetPosition(int deviceID, enum pos_enum position, long value);
+
+long JACK_GetJackLatency(int deviceID); /* deprectated, you probably want JACK_GetJackOutputLatency */
+long JACK_GetJackOutputLatency(int deviceID); /* return the output latency in frames */
+long JACK_GetJackInputLatency(int deviceID); /* return the input latency in frames */
+
+int JACK_SetState(int deviceID, enum status_enum state); /* playing, paused, stopped */
+enum status_enum JACK_GetState(int deviceID);
+
+long JACK_GetMaxOutputBufferedBytes(int deviceID);
+long JACK_GetMaxInputBufferedBytes(int deviceID);
+
+/* bytes that jack requests during each callback */
+unsigned long JACK_GetJackBufferedBytes(int deviceID);
+
+/* Properties of the jack driver */
+
+/* linear means 0 volume is silence, 100 is full volume */
+/* dbAttenuation means 0 volume is 0dB attenuation */
+/* Bio2jack defaults to linear */
+/* Note: volume controls only effect output channels for now */
+enum JACK_VOLUME_TYPE { linear, dbAttenuation };
+enum JACK_VOLUME_TYPE JACK_SetVolumeEffectType(int deviceID,
+                                               enum JACK_VOLUME_TYPE type);
+
+int  JACK_SetAllVolume(int deviceID, unsigned int volume); /* returns 0 on success */
+int  JACK_SetVolumeForChannel(int deviceID, unsigned int channel, unsigned int volume);
+void JACK_GetVolumeForChannel(int deviceID, unsigned int channel, unsigned int *volume);
+
+
+unsigned long JACK_GetOutputBytesPerSecond(int deviceID); /* bytes_per_output_frame * sample_rate */
+unsigned long JACK_GetInputBytesPerSecond(int deviceID);  /* bytes_per_input_frame * sample_rate */
+unsigned long JACK_GetBytesStored(int deviceID);          /* bytes currently buffered in the output buffer */
+unsigned long JACK_GetBytesFreeSpace(int deviceID);       /* bytes of free space in the output buffer */
+unsigned long JACK_GetBytesUsedSpace(int deviceID);       /* bytes of space used in the input buffer */
+unsigned long JACK_GetBytesPerOutputFrame(int deviceID);
+unsigned long JACK_GetBytesPerInputFrame(int deviceID);
+
+/* Note: these will probably be removed in a future release */
+int  JACK_GetNumInputChannels(int deviceID);
+int  JACK_GetNumOutputChannels(int deviceID);
+
+long JACK_GetSampleRate(int deviceID); /* samples per second */
+
+void JACK_SetClientName(char *name); /* sets the name that bio2jack will use when
+                                        creating a new jack client.  name_%pid%_%deviceID%%counter%
+                                        will be used
+                                        NOTE: this defaults to name = bio2jack
+                                        NOTE: we limit the size of the client name to
+                                           jack_client_name_size() */
+
+enum JACK_PORT_CONNECTION_MODE
+{
+    CONNECT_ALL,    /* connect to all avaliable ports */
+    CONNECT_OUTPUT, /* connect only to the ports we need for output */
+    CONNECT_NONE    /* don't connect to any ports */
+};
+
+/* set the mode for port connections */
+/* defaults to CONNECT_ALL */ 
+void JACK_SetPortConnectionMode(enum JACK_PORT_CONNECTION_MODE mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef JACK_OUT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/jack/configure.c	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,163 @@
+/*      xmms - jack output plugin
+ *    Copyright (C) 2004      Chris Morgan
+ *
+ *
+ *  audacious port (2005) by Giacomo Lozito from develia.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.
+ *
+ *  Contains code Copyright (C) 1998-2000 Mikael Alm, Olle Hallnas,
+ *  Thomas Nillson, 4Front Technologies and Galex Yen
+ */
+
+#include "jack.h"
+
+#include "libaudacious/configfile.h"
+
+#include <gtk/gtk.h>
+
+extern jackconfig jack_cfg;
+
+static GtkWidget *configure_win = NULL, *vbox;
+
+static GtkWidget *GTK_isTraceEnabled;
+static GtkWidget *bbox, *ok, *cancel;
+
+static GtkWidget *option_frame;
+static GtkWidget *port_connection_mode_box;
+static GtkWidget *port_connection_mode_combo;
+
+#define GET_CHARS(edit) gtk_editable_get_chars(GTK_EDITABLE(edit), 0, -1)
+
+static void configure_win_ok_cb(GtkWidget * w, gpointer data)
+{
+	ConfigFile *cfgfile;
+	gchar *filename;
+
+	jack_cfg.isTraceEnabled = (gint) GTK_CHECK_BUTTON(GTK_isTraceEnabled)->toggle_button.active;
+  jack_cfg.port_connection_mode = GET_CHARS(GTK_COMBO(port_connection_mode_combo)->entry);
+
+  jack_set_port_connection_mode(); /* update the connection mode */
+
+	filename = g_strconcat(g_get_home_dir(), "/.audacious/config", NULL);
+	
+	cfgfile = xmms_cfg_open_file(filename);
+	if (!cfgfile)
+		cfgfile = xmms_cfg_new();
+
+	xmms_cfg_write_boolean(cfgfile, "jack", "isTraceEnabled", jack_cfg.isTraceEnabled);
+  xmms_cfg_write_string(cfgfile, "jack", "port_connection_mode", jack_cfg.port_connection_mode);
+	xmms_cfg_write_file(cfgfile, filename);
+	xmms_cfg_free(cfgfile);
+
+	g_free(filename);
+
+	gtk_widget_destroy(configure_win);
+}
+
+static void get_port_connection_modes(GtkCombo *combo)
+{
+    GtkWidget *item;
+    char *descr;
+
+    descr = g_strdup("Connect to all available jack ports");
+    item = gtk_list_item_new_with_label(descr);
+    gtk_widget_show(item);
+    g_free(descr);
+    gtk_combo_set_item_string(combo, GTK_ITEM(item), "CONNECT_ALL");
+    gtk_container_add(GTK_CONTAINER(combo->list), item);
+
+    descr = g_strdup("Connect only the output ports");
+    item = gtk_list_item_new_with_label(descr);
+    gtk_widget_show(item);
+    g_free(descr);
+    gtk_combo_set_item_string(combo, GTK_ITEM(item), "CONNECT_OUTPUT");
+    gtk_container_add(GTK_CONTAINER(combo->list), item);
+
+    descr = g_strdup("Connect to no ports");
+    item = gtk_list_item_new_with_label(descr);
+    gtk_widget_show(item);
+    g_free(descr);
+    gtk_combo_set_item_string(combo, GTK_ITEM(item), "CONNECT_NONE");
+    gtk_container_add(GTK_CONTAINER(combo->list), item);
+}
+
+void jack_configure(void)
+{
+	if (!configure_win)
+	{	
+		configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+		gtk_window_set_type_hint ( GTK_WINDOW(configure_win), GDK_WINDOW_TYPE_HINT_DIALOG);
+		gtk_signal_connect(GTK_OBJECT(configure_win), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &configure_win);
+		gtk_window_set_title(GTK_WINDOW(configure_win), ("jack Plugin configuration"));
+		gtk_window_set_policy(GTK_WINDOW(configure_win), FALSE, FALSE, FALSE);
+		gtk_window_set_position(GTK_WINDOW(configure_win), GTK_WIN_POS_MOUSE);
+		gtk_container_border_width(GTK_CONTAINER(configure_win), 10);
+
+    vbox = gtk_vbox_new(FALSE, 10);
+    gtk_container_add(GTK_CONTAINER(configure_win), vbox);
+
+    /* add a frame for other xmms-jack options */
+    option_frame = gtk_frame_new("Options:");
+    gtk_box_pack_start(GTK_BOX(vbox), option_frame, FALSE, FALSE, 0);
+
+    /* add a hbox that will contain a label for a dropdown and the dropdown itself */
+    port_connection_mode_box = gtk_hbox_new(FALSE, 5);
+    gtk_container_set_border_width(GTK_CONTAINER(port_connection_mode_box), 5);
+    gtk_container_add(GTK_CONTAINER(option_frame), port_connection_mode_box);
+
+    /* add the label */
+    gtk_box_pack_start(GTK_BOX(port_connection_mode_box), gtk_label_new("Connection mode:"),
+                       FALSE, FALSE, 0);
+
+    /* add the dropdown */
+    port_connection_mode_combo = gtk_combo_new();
+    get_port_connection_modes(GTK_COMBO(port_connection_mode_combo));
+    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(port_connection_mode_combo)->entry),
+                       jack_cfg.port_connection_mode);
+    gtk_box_pack_start(GTK_BOX(port_connection_mode_box), port_connection_mode_combo,
+                       TRUE, TRUE, 0);
+
+    /* create a check_button for debug output */
+    GTK_isTraceEnabled = gtk_check_button_new_with_label("Enable debug printing");
+    gtk_box_pack_start(GTK_BOX(vbox), GTK_isTraceEnabled, FALSE, FALSE, 0);
+    gtk_widget_show(GTK_isTraceEnabled);
+
+    /* set the state of the check_button based upon the value of */
+    /* isTracingEnabled */
+    GTK_CHECK_BUTTON(GTK_isTraceEnabled)->toggle_button.active = jack_cfg.isTraceEnabled;
+
+    /* add the box for the ok/canceled buttons at the bottom */
+    bbox = gtk_hbox_new(FALSE, 10);
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+		ok = gtk_button_new_with_label(("Ok"));
+		gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(configure_win_ok_cb), NULL);
+		GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
+		gtk_widget_show(ok);
+		gtk_widget_grab_default(ok);
+
+		cancel = gtk_button_new_with_label(("Cancel"));
+		gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(configure_win));
+		GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+		gtk_widget_show(cancel);
+
+		gtk_widget_show_all(configure_win);
+	}
+	else
+		gdk_window_raise(configure_win->window);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/jack/jack.c	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,617 @@
+/*      xmms - jack output plugin
+ *	Copyright 2002 Chris Morgan<cmorgan@alum.wpi.edu>
+ *
+ *      audacious port (2005) by Giacomo Lozito from develia.org
+ *
+ *	This code maps xmms calls into the jack translation library
+ */
+
+#include "libaudacious/configfile.h"
+#include <dlfcn.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include "config.h"
+#include "bio2jack.h" /* includes for the bio2jack library */
+#include "jack.h"
+#include "xconvert.h" /* xmms rate conversion header file */
+#include <string.h>
+
+
+
+/* set to 1 for verbose output */
+#define VERBOSE_OUTPUT          0
+
+jackconfig jack_cfg;
+
+#define OUTFILE stderr
+
+#define TRACE(...)                                      \
+    if(jack_cfg.isTraceEnabled) {                       \
+        fprintf(OUTFILE, "%s:", __FUNCTION__),          \
+        fprintf(OUTFILE, __VA_ARGS__),				    \
+        fflush(OUTFILE);                                \
+    }
+
+#define ERR(...)                                        \
+    if(jack_cfg.isTraceEnabled) {                       \
+        fprintf(OUTFILE, "ERR: %s:", __FUNCTION__),     \
+        fprintf(OUTFILE, __VA_ARGS__),				    \
+        fflush(OUTFILE);                                \
+    }
+
+
+static int driver = 0; /* handle to the jack output device */
+
+typedef struct format_info {
+  AFormat format;
+  long    frequency;
+  int     channels;
+  long    bps;
+} format_info_t;
+
+static format_info_t input;
+static format_info_t effect;
+static format_info_t output;
+
+static convert_freq_func_t freq_convert; /* rate convert function */
+static struct xmms_convert_buffers *convertb; /* convert buffer */
+
+#define MAKE_FUNCPTR(f) static typeof(f) * fp_##f = NULL;
+MAKE_FUNCPTR(xmms_convert_buffers_new);
+MAKE_FUNCPTR(xmms_convert_buffers_destroy);
+MAKE_FUNCPTR(xmms_convert_get_frequency_func);
+void *xmmslibhandle; /* handle to the dlopen'ed libxmms.so */
+
+static int isXmmsFrequencyAvailable = 0;
+
+static gboolean output_opened; /* true if we have a connection to jack */
+
+static GtkWidget *dialog, *button, *label;
+
+
+/* Giacomo's note: removed the destructor from the original xmms-jack, cause
+   destructors + thread join + NPTL currently leads to problems; solved this
+   by adding a cleanup function callback for output plugins in Audacious, this
+   is used to close the JACK connection and to perform a correct shutdown */
+void jack_cleanup(void)
+{
+  int errval;
+  TRACE("cleanup\n");
+
+  if((errval = JACK_Close(driver)))
+    ERR("error closing device, errval of %d\n", errval);
+
+  /* only clean this up if we have the function to call */
+  if(isXmmsFrequencyAvailable)
+  {
+    fp_xmms_convert_buffers_destroy(convertb); /* clean up the rate conversion buffers */
+    dlclose(xmmslibhandle);
+  }
+
+  return;
+}
+
+
+void jack_sample_rate_error(void)
+{
+	dialog = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(dialog), ("Sample rate mismatch"));
+	gtk_container_border_width(GTK_CONTAINER(dialog), 5);
+	label = gtk_label_new((
+		"Xmms is asking for a sample rate that differs from\n "
+		"that of the jack server.  Xmms 1.2.8 or later\n"
+		"contains resampling routines that xmms-jack will\n"
+		"dynamically load and use to perform resampling.\n"
+		"Or you can restart the jack server\n"
+		"with a sample rate that matches the one that\n"
+		"xmms desires.  -r is the option for the jack\n"
+		"alsa driver so -r 44100 or -r 48000 should do\n\n"
+		"Chris Morgan <cmorgan@alum.wpi.edu>\n"));
+
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
+	gtk_widget_show(label);
+
+	button = gtk_button_new_with_label((" Close "));
+	gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(dialog));
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
+	gtk_widget_show(button);
+
+	gtk_widget_show(dialog);
+	gtk_widget_grab_focus(button);
+}
+
+
+/* Return the number of milliseconds of audio data that has been */
+/* written out to the device */
+gint jack_get_written_time(void)
+{
+  long return_val;
+  return_val = JACK_GetPosition(driver, MILLISECONDS, WRITTEN);
+
+  TRACE("returning %ld milliseconds\n", return_val);
+  return return_val;
+}
+
+
+/* Return the current number of milliseconds of audio data that has */
+/* been played out of the audio device, not including the buffer */
+gint jack_get_output_time(void)
+{
+  gint return_val;
+  
+  /* don't try to get any values if the device is still closed */
+  if(JACK_GetState(driver) == CLOSED)
+    return_val = 0;
+  else
+    return_val = JACK_GetPosition(driver, MILLISECONDS, PLAYED); /* get played position in terms of milliseconds */
+
+  TRACE("returning %d milliseconds\n", return_val);
+  return return_val;
+}
+
+
+/* returns TRUE if we are currently playing */
+/* NOTE: this was confusing at first BUT, if the device is open and there */
+/* is no more audio to be played, then the device is NOT PLAYING */
+gint jack_playing(void)
+{
+  gint return_val;
+
+  /* If we are playing see if we ACTUALLY have something to play */
+  if(JACK_GetState(driver) == PLAYING)
+  {
+    /* If we have zero bytes stored, we are done playing */
+    if(JACK_GetBytesStored(driver) == 0)
+      return_val = FALSE;
+    else
+      return_val = TRUE;
+  }
+  else
+    return_val = FALSE;
+
+  TRACE("returning %d\n", return_val);
+  return return_val;
+}
+
+
+void jack_set_port_connection_mode()
+{
+  /* setup the port connection mode that determines how bio2jack will connect ports */
+  enum JACK_PORT_CONNECTION_MODE mode;
+  
+  if(strcmp(jack_cfg.port_connection_mode, "CONNECT_ALL") == 0)
+      mode = CONNECT_ALL;
+  else if(strcmp(jack_cfg.port_connection_mode, "CONNECT_OUTPUT") == 0)
+      mode = CONNECT_OUTPUT;
+  else if(strcmp(jack_cfg.port_connection_mode, "CONNECT_NONE") == 0)
+      mode = CONNECT_NONE;
+  else
+  {
+      TRACE("Defaulting to CONNECT_ALL");
+      mode = CONNECT_ALL;
+  }
+  JACK_SetPortConnectionMode(mode);
+}
+
+/* Initialize necessary things */
+void jack_init(void)
+{
+  /* read the isTraceEnabled setting from the config file */
+  ConfigFile *cfgfile;
+  gchar *filename;
+
+  filename = g_strconcat(g_get_home_dir(), "/.audacious/config", NULL);
+  cfgfile = xmms_cfg_open_file(filename);
+  if (!cfgfile)
+  {
+      jack_cfg.isTraceEnabled = FALSE;
+      jack_cfg.port_connection_mode = "CONNECT_ALL"; /* default to connect all */
+  } else
+  {
+      xmms_cfg_read_boolean(cfgfile, "jack", "isTraceEnabled", &jack_cfg.isTraceEnabled);
+      if(!xmms_cfg_read_string(cfgfile, "jack", "port_connection_mode", &jack_cfg.port_connection_mode))
+          jack_cfg.port_connection_mode = "CONNECT_ALL";
+  }
+
+  xmms_cfg_free(cfgfile);
+  g_free(filename);
+
+  TRACE("initializing\n");
+  JACK_Init(); /* initialize the driver */
+
+  /* set the bio2jack name so users will see xmms-jack in their */
+  /* jack client list */
+  JACK_SetClientName("audacious-jack");
+
+  /* set the port connection mode */
+  jack_set_port_connection_mode();
+
+  xmmslibhandle = dlopen("libaudacious.so", RTLD_NOW);
+  if(xmmslibhandle)
+  {
+    fp_xmms_convert_buffers_new = dlsym(xmmslibhandle, "xmms_convert_buffers_new");
+    fp_xmms_convert_buffers_destroy = dlsym(xmmslibhandle, "xmms_convert_buffers_destroy");
+    fp_xmms_convert_get_frequency_func = dlsym(xmmslibhandle, "xmms_convert_get_frequency_func");
+
+    if(!fp_xmms_convert_buffers_new)
+    {
+      TRACE("fp_xmms_convert_buffers_new couldn't be dlsym'ed\n");
+      TRACE("dlerror: %s\n", dlerror());
+    }
+
+    if(!fp_xmms_convert_buffers_destroy)
+    {
+      TRACE("fp_xmms_convert_buffers_destroy couldn't be dlsym'ed\n");
+      TRACE("dlerror: %s\n", dlerror());
+    }
+
+    if(!fp_xmms_convert_get_frequency_func)
+    {
+      TRACE("fp_xmms_get_frequency_func couldn't be dlsym'ed\n");
+      TRACE("dlerror: %s\n", dlerror());
+    }
+
+    if(!fp_xmms_convert_buffers_new || !fp_xmms_convert_buffers_destroy ||
+       !fp_xmms_convert_get_frequency_func)
+    {
+      dlclose(xmmslibhandle); /* close the library, no need to keep it open */
+      TRACE("One or more frequency convertion functions are missing, upgrade to xmms >=1.2.8\n");
+    } else
+    {
+      TRACE("Found frequency convertion functions, setting isXmmsFrequencyAvailable to 1\n");
+      isXmmsFrequencyAvailable = 1;
+    }
+  } else
+  {
+    TRACE("unable to dlopen '%s'\n", "libaudacious.so");
+  }
+
+  /* only initialize this stuff if we have the functions available */
+  if(isXmmsFrequencyAvailable)
+  {
+    convertb = fp_xmms_convert_buffers_new ();
+    freq_convert = fp_xmms_convert_get_frequency_func(FMT_S16_LE, 2);
+  }
+
+  output_opened = FALSE;
+}
+
+
+/* Return the amount of data that can be written to the device */
+gint jack_free(void)
+{
+  unsigned long return_val = JACK_GetBytesFreeSpace(driver);
+  unsigned long tmp;
+
+  /* adjust for frequency differences, otherwise xmms could send us */
+  /* as much data as we have free, then we go to convert this to */
+  /* the output frequency and won't have enough space, so adjust */
+  /* by the ratio of the two */
+  if(effect.frequency != output.frequency)
+  {
+    tmp = return_val;
+    return_val = (return_val * effect.frequency) / output.frequency;
+    TRACE("adjusting from %ld to %ld free bytes to compensate for frequency differences\n", tmp, return_val);
+  }
+
+  if(return_val > G_MAXINT)
+  {
+      TRACE("Warning: return_val > G_MAXINT\n");
+      return_val = G_MAXINT;
+  }
+
+  TRACE("free space of %ld bytes\n", return_val);
+
+  return return_val;
+}
+
+
+/* Close the device */
+void jack_close(void)
+{
+  TRACE("\n");
+
+  JACK_Reset(driver); /* flush buffers, reset position and set state to STOPPED */
+  TRACE("resetting driver, not closing now, destructor will close for us\n");
+}
+
+
+/* Open the device up */
+gint jack_open(AFormat fmt, gint sample_rate, gint num_channels)
+{
+  int bits_per_sample;
+  int retval;
+  unsigned long rate;
+
+  TRACE("fmt == %d, sample_rate == %d, num_channels == %d\n",
+	fmt, sample_rate, num_channels);
+
+  if((fmt == FMT_U8) || (fmt == FMT_S8))
+  {
+    bits_per_sample = 8;
+  } else
+  {
+    bits_per_sample = 16;
+  }
+
+  /* record some useful information */
+  input.format    = fmt;
+  input.frequency = sample_rate;
+  input.bps       = bits_per_sample * sample_rate * num_channels;
+  input.channels  = num_channels;
+
+  /* setup the effect as matching the input format */
+  effect.format    = input.format;
+  effect.frequency = input.frequency;
+  effect.channels  = input.channels;
+  effect.bps       = input.bps;
+
+  /* if we are already opened then don't open again */
+  if(output_opened)
+  {
+    /* if something has changed we should close and re-open the connect to jack */
+    if((output.channels != input.channels) ||
+       (output.frequency != input.frequency) ||
+       (output.format != input.format))
+    {
+      TRACE("output.channels is %d, jack_open called with %d channels\n", output.channels, input.channels);
+      TRACE("output.frequency is %ld, jack_open called with %ld\n", output.frequency, input.frequency);
+      TRACE("output.format is %d, jack_open called with %d\n", output.format, input.format);
+      jack_close();
+    } else
+    {
+        TRACE("output_opened is TRUE and no options changed, not reopening\n");
+        return 1;
+    }
+  }
+
+  /* try to open the jack device with the requested rate at first */
+  output.frequency = input.frequency;
+  output.bps       = input.bps;
+  output.channels  = input.channels;
+  output.format    = input.format;
+
+  rate = output.frequency;
+  retval = JACK_Open(&driver, bits_per_sample, &rate, output.channels);
+  output.frequency = rate; /* avoid compile warning as output.frequency differs in type
+                              from what JACK_Open() wants for the type of the rate parameter */
+  if((retval == ERR_RATE_MISMATCH) && isXmmsFrequencyAvailable)
+  {
+    TRACE("xmms(input) wants rate of '%ld', jacks rate(output) is '%ld', opening at jack rate\n", input.frequency, output.frequency);
+
+    /* open the jack device with true jack's rate, return 0 upon failure */
+    retval = JACK_Open(&driver, bits_per_sample, &rate, output.channels);
+    output.frequency = rate; /* avoid compile warning as output.frequency differs in type
+                                from what JACK_Open() wants for the type of the rate parameter */
+    if(retval)
+    {
+      TRACE("failed to open jack with JACK_Open(), error %d\n", retval);
+      return 0;
+    }
+    TRACE("success!!\n");
+  } else if((retval == ERR_RATE_MISMATCH) && !isXmmsFrequencyAvailable)
+  {
+    TRACE("JACK_Open(), sample rate mismatch with no resampling routines available\n");
+
+    jack_sample_rate_error(); /* notify the user that we can't resample */
+
+    return 0;
+  } else if(retval != ERR_SUCCESS)
+  {
+    TRACE("failed to open jack with JACK_Open(), error %d\n", retval);
+    return 0;
+  }
+
+  output_opened = TRUE;
+
+  return 1;
+}
+
+
+/* write some audio out to the device */
+void jack_write(gpointer ptr, gint length)
+{
+  long written;
+  EffectPlugin *ep;
+  AFormat new_format;
+  int new_frequency, new_channels;
+  long positionMS;
+
+  TRACE("starting length of %d\n", length);
+
+  /* copy the current values into temporary values */
+  new_format = input.format;
+  new_frequency = input.frequency;
+  new_channels = input.channels;
+
+  /* query xmms for the current plugin */
+  ep = get_current_effect_plugin();
+  if(effects_enabled() && ep && ep->query_format)
+  {
+    ep->query_format(&new_format, &new_frequency, &new_channels);
+  }
+
+  /* if the format has changed take this into account by modifying */
+  /* the time offset and reopening the device with the new format settings */
+  if (new_format != effect.format ||
+      new_frequency != effect.frequency ||
+      new_channels != effect.channels)
+  {
+    TRACE("format changed, storing new values and opening/closing jack\n");
+    TRACE("effect.format == %d, new_format == %d, effect.frequency == %ld, new_frequency == %d, effect.channels == %d, new_channels = %d\n",
+	  effect.format, new_format, effect.frequency, new_frequency, effect.channels, new_channels);
+ 
+    positionMS = JACK_GetPosition(driver, MILLISECONDS, PLAYED);
+
+    jack_close();
+    jack_open(new_format, new_frequency, new_channels);
+ 
+    /* restore the position after the open and close */
+    JACK_SetState(driver, PAUSED);
+    JACK_SetPosition(driver, MILLISECONDS, positionMS);
+    JACK_SetState(driver, PLAYING);
+  }
+
+  /* if effects are enabled and we have a plugin, run the current */
+  /* samples through the plugin */
+  if (effects_enabled() && ep && ep->mod_samples)
+  {
+    length = ep->mod_samples(&ptr, length,
+                             input.format,
+                             input.frequency,
+                             input.channels);
+    TRACE("effects_enabled(), length is now %d\n", length);
+  }
+
+  TRACE("effect.frequency == %ld, input.frequency == %ld, output.frequency == %ld\n",
+        effect.frequency, input.frequency, output.frequency);
+
+  /* if we need rate conversion, perform it here */
+  if((effect.frequency != output.frequency) && isXmmsFrequencyAvailable)
+  {
+    TRACE("performing rate conversion from '%ld'(effect) to '%ld'(output)\n", effect.frequency, output.frequency);
+    length = freq_convert (convertb, &ptr, length, effect.frequency, output.frequency);
+  }
+
+  TRACE("length = %d\n", length);
+  /* loop until we have written all the data out to the jack device */
+  /* this is due to xmms' audio driver api */
+  char *buf = (char*)ptr;
+  while(length > 0)
+  {
+    TRACE("writing %d bytes\n", length);
+    written = JACK_Write(driver, (unsigned char*)buf, length);
+    length-=written;
+    buf+=written;
+  }
+  TRACE("finished\n");
+}
+
+
+/* Flush any output currently buffered */
+/* and set the number of bytes written based on ms_offset_time, */
+/* the number of milliseconds of offset passed in */
+/* This is done so the driver itself keeps track of */
+/* current playing position of the mp3 */
+void jack_flush(gint ms_offset_time)
+{
+  TRACE("setting values for ms_offset_time of %d\n", ms_offset_time);
+
+  JACK_Reset(driver); /* flush buffers and set state to STOPPED */
+
+  /* update the internal driver values to correspond to the input time given */
+  JACK_SetPosition(driver, MILLISECONDS, ms_offset_time);
+
+  JACK_SetState(driver, PLAYING);
+}
+
+
+/* Pause the jack device */
+void jack_pause(short p)
+{
+  TRACE("p == %d\n", p);
+
+  /* pause the device if p is non-zero, unpause the device if p is zero and */
+  /* we are currently paused */
+  if(p)
+    JACK_SetState(driver, PAUSED);
+  else if(JACK_GetState(driver) == PAUSED)
+    JACK_SetState(driver, PLAYING);
+}
+
+
+/* Set the volume */
+void jack_set_volume(int l, int r)
+{
+  if(output.channels == 1)
+  {
+    TRACE("l(%d)\n", l);
+  } else if(output.channels > 1)
+  {
+    TRACE("l(%d), r(%d)\n", l, r);
+  }
+
+  if(output.channels > 0)
+      JACK_SetVolumeForChannel(driver, 0, l);
+  if(output.channels > 1)
+      JACK_SetVolumeForChannel(driver, 1, r);
+}
+
+
+/* Get the current volume setting */
+void jack_get_volume(int *l, int *r)
+{
+  unsigned int _l, _r;
+
+  if(output.channels > 0)
+  {
+      JACK_GetVolumeForChannel(driver, 0, &_l);
+      (*l) = _l;
+  }
+  if(output.channels > 1)
+  {
+      JACK_GetVolumeForChannel(driver, 1, &_r);
+      (*r) = _r;
+  }
+
+#if VERBOSE_OUTPUT
+  if(output.channels == 1)
+      TRACE("l(%d)\n", *l);
+  else if(output.channels > 1)
+      TRACE("l(%d), r(%d)\n", *l, *r);
+#endif
+}
+
+
+void jack_about(void)
+{
+	dialog = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(dialog), ("About JACK Output Plugin 0.15"));
+	gtk_container_border_width(GTK_CONTAINER(dialog), 5);
+	label = gtk_label_new((
+		"XMMS jack Driver 0.15\n\n "
+		"xmms-jack.sf.net\n"
+		"Chris Morgan<cmorgan@alum.wpi.edu>\n"
+		"\nAudacious port by\n"
+		"Giacomo Lozito from develia.org\n"));
+
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
+	gtk_widget_show(label);
+
+	button = gtk_button_new_with_label((" Close "));
+	gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(dialog));
+	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
+	gtk_widget_show(button);
+
+	gtk_widget_show(dialog);
+	gtk_widget_grab_focus(button);
+}
+
+
+OutputPlugin jack_op =
+{
+	NULL,
+	NULL,
+	"JACK Output Plugin 0.15",
+	jack_init,
+	jack_cleanup,
+	jack_about,
+	jack_configure,
+	jack_get_volume,
+	jack_set_volume,
+	jack_open,
+	jack_write,
+	jack_close,
+	jack_flush,
+	jack_pause,
+	jack_free,
+	jack_playing,
+	jack_get_output_time,
+	jack_get_written_time,
+};
+
+
+OutputPlugin *get_oplugin_info(void)
+{
+	return &jack_op;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/jack/jack.h	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,16 @@
+#ifndef _JACK_H
+#define _JACK_H
+
+#include <glib.h>
+
+void jack_configure(void);
+
+typedef struct
+{
+    gboolean isTraceEnabled; /* if true we will print debug information to the console */
+    char *port_connection_mode; /* the port connection mode setting */
+} jackconfig;
+
+void jack_set_port_connection_mode(); /* called by jack_init() and the 'ok' handler in configure.c */
+
+#endif /* #ifndef _JACK_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/jack/xconvert.h	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,31 @@
+/*
+ *  Copyright (C) 2003  Haavard Kvaalen <havardk@xmms.org>
+ *
+ *  Licensed under GNU LGPL version 2.
+ */
+
+
+
+#include "audacious/plugin.h"
+
+void convert_free_buffer(void);
+
+struct xmms_convert_buffers;
+
+struct xmms_convert_buffers* xmms_convert_buffers_new(void);
+/*
+ * Free the data assosiated with the buffers, without destroying the
+ * context.  The context can be reused.
+ */
+void xmms_convert_buffers_free(struct xmms_convert_buffers* buf);
+void xmms_convert_buffers_destroy(struct xmms_convert_buffers* buf);
+
+
+typedef int (*convert_func_t)(struct xmms_convert_buffers* buf, void **data, int length);
+typedef int (*convert_channel_func_t)(struct xmms_convert_buffers* buf, void **data, int length);
+typedef int (*convert_freq_func_t)(struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq);
+
+
+convert_func_t xmms_convert_get_func(AFormat output, AFormat input);
+convert_channel_func_t xmms_convert_get_channel_func(AFormat fmt, int output, int input);
+convert_freq_func_t xmms_convert_get_frequency_func(AFormat fmt, int channels);
--- a/configure.ac	Mon Dec 19 08:45:45 2005 -0800
+++ b/configure.ac	Mon Dec 19 08:58:27 2005 -0800
@@ -442,6 +442,19 @@
 AM_CONDITIONAL(ENABLE_CROSSFADE, test "$enable_crossfade" = "yes")
 AM_CONDITIONAL(HAVE_LIBSAMPLERATE, test "$have_libsamplerate" = "yes")
 
+dnl *** jack output plugin
+AC_ARG_ENABLE( jack,
+[  --disable-jack          disable jack output plugin [default=enabled]],,
+                enable_jack="yes")
+
+if test "x$enable_jack" = xyes; then
+        AM_PATH_JACK(have_jack=yes, have_jack=no)
+else
+        AC_MSG_RESULT([*** jack plugin disabled per user request ***])
+        have_jack=no
+fi
+AM_CONDITIONAL(HAVE_JACK,test "x$have_jack" = xyes)
+
 dnl *** sid
 AC_ARG_ENABLE( sid,
 [  --disable-sid           disable sid input plugin [default=enabled]],,
@@ -764,6 +777,7 @@
         Plugins/Output/OSS/Makefile
         Plugins/Output/esd/Makefile
         Plugins/Output/alsa/Makefile
+	Plugins/Output/jack/Makefile
 	Plugins/Output/crossfade/Makefile
 	Plugins/Output/disk_writer/Makefile
         Plugins/Input/Makefile
@@ -834,6 +848,7 @@
 echo "  Open Sound System (oss):                $have_oss"
 echo "  Advanced Linux Sound Arch. (alsa):      $have_alsa"
 echo "  Enlightenment Sound Daemon (esd):       $have_esd"
+echo "  Jack Audio Connection Kit (jack):       $have_jack"
 echo "  Crossfading (crossfade):                $enable_crossfade"
 echo "    + libsamplerate support               $have_libsamplerate"
 echo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/m4/jack.m4	Mon Dec 19 08:58:27 2005 -0800
@@ -0,0 +1,56 @@
+# Configure paths for JACK
+
+dnl AM_PATH_JACK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for JACK, and define JACK_CFLAGS and JACK_LIBS
+dnl
+AC_DEFUN([AM_PATH_JACK],
+[dnl 
+dnl **** Check for Jack sound server ****
+dnl
+JACK_LIBS=
+JACK_CFLAGS=
+JACK_EVERYTHINGOK=yes
+
+AC_CHECK_HEADERS(jack/jack.h)
+if test "${ac_cv_header_jack_jack_h}" = "no"
+then
+  AC_MSG_WARN([Could not find jack/jack.h  Install jack headers to build bio2jack])
+  JACK_EVERYTHINGOK=no
+else
+  JACK_CFLAGS="-lpthread -ljack -ldl"
+fi
+
+AC_CHECK_LIB(jack, jack_activate, JACK_LIBS="-ljack -ldl")
+if test "${ac_cv_lib_jack_jack_activate}" = "no"
+then
+  AC_MSG_WARN([Could not find jack_activate in libjack.  Ensure that you have libjack installed and that it a current version.])
+  JACK_EVERYTHINGOK=no
+fi
+
+AC_SUBST(JACK_CFLAGS)
+AC_SUBST(JACK_LIBS)
+
+dnl **** Check for libsamplerate necessary for bio2jack ****
+PKG_CHECK_MODULES(SAMPLERATE, samplerate >= 0.0.15,
+            ac_cv_samplerate=1, ac_cv_samplerate=0)
+
+AC_DEFINE_UNQUOTED([HAVE_SAMPLERATE],${ac_cv_samplerate},
+            [Set to 1 if you have libsamplerate.])
+
+dnl Make sure libsamplerate is found, we can't compile without it
+if test "${ac_cv_samplerate}" = 0
+then
+  AC_MSG_WARN([Could not find libsamplerate, necessary for jack output plugin.])
+  JACK_EVERYTHINGOK=no
+fi
+
+AC_SUBST(SAMPLERATE_CFLAGS)
+AC_SUBST(SAMPLERATE_LIBS)
+
+if test "x$JACK_EVERYTHINGOK" = xno; then
+  ifelse([$2], , :, [$2])
+else
+  ifelse([$1], , :, [$1])
+fi
+
+])