[MPlayer-dev-eng] [PATCH] JACK audio output module

Kamil Strzelecki esack at o2.pl
Sat Jun 5 23:28:46 CEST 2004


Hi,
I've written JACK support module for libao2. It uses bio2jack - nice,
small, statically linked library that makes porting i/o blocking apps
to JACK trivial, it's also used in xmms-jack output module.
Patch attached. It works just fine for me.

cheers
-- 
              ..:: Kamil Strzelecki = esack at dharma.one.pl ::..
 
    "There are two major products that come out of Berkeley: LSD and UNIX.
       We don't believe this to be a coincidence." --Jeremy S. Anderson

-------------- next part --------------
#
# JACK audio output driver for MPlayer
#
diff -Nur mplayer-cvs/main/configure mplayer-jack/main/configure
--- mplayer-cvs/main/configure	2004-05-31 17:07:20.000000000 +0200
+++ mplayer-jack/main/configure	2004-06-05 20:55:32.000000000 +0200
@@ -254,6 +254,7 @@
   --disable-ossaudio     disable OSS sound support [autodetect]
   --disable-arts         disable aRts sound support [autodetect]
   --disable-esd          disable esd sound support [autodetect]
+  --disable-jack         disable JACK sound support [autodetect]
   --disable-nas          disable NAS sound support [autodetect]
   --disable-sgiaudio     disable SGI sound support [autodetect]
   --disable-sunaudio     disable Sun sound support [autodetect]
@@ -1170,6 +1171,7 @@
 _ossaudio=auto
 _arts=auto
 _esd=auto
+_jack=auto
 _liblzo=auto
 _mad=auto
 _vorbis=auto
@@ -1335,6 +1337,8 @@
   --disable-arts)	_arts=no	;;
   --enable-esd)		_esd=yes	;;
   --disable-esd)	_esd=no		;;
+  --enable-jack)	_jack=yes	;;
+  --disable-jack)	_jack=no	;;
   --enable-mad)		_mad=yes	;;
   --disable-mad)	_mad=no		;;
   --enable-liblzo)	_liblzo=yes	;;
@@ -4013,6 +4017,33 @@
   _noaomodules="esd $_noaomodules"
 fi
 
+
+echocheck "JACK"
+if test "$_jack" = auto ; then
+  _jack=no
+  if ( jackd --version | grep version | awk '{ print $3 }' ) >> "$TMPLOG" 2>&1 ; then
+
+cat > $TMPC << EOF
+#include <jack/jack.h>
+int main(void) { return 0; }
+EOF
+cc_check `pkg-config --libs --cflags jack` && ( "$TMPO" >> "$TMPLOG" 2>&1 ) && _jack=yes
+
+  fi
+fi
+
+if test "$_jack" = yes ; then
+  _def_jack='#define USE_JACK 1'
+  _aosrc="$_aosrc bio2jack.c ao_jack.c"
+  _aomodules="jack $_aomodules"
+  _ld_jack=`pkg-config --libs jack`
+  _inc_jack=`pkg-config --cflags jack`
+else
+  _noaomodules="jack $_noaomodules"
+fi
+echores "$_jack"
+
+
 echocheck "ALSA audio"
 if test "$_alsa" != no ; then
   _alsa=no
@@ -6032,6 +6063,8 @@
 ARTS_INC = $_inc_arts
 ESD_LIB = $_ld_esd
 ESD_INC = $_inc_esd
+JACK_LIB = $_ld_jack
+JACK_INC = $_inc_jack
 SGIAUDIO_LIB = $_ld_sgiaudio
 
 # input/demuxer/codecs
@@ -6418,6 +6451,7 @@
 $_def_arts
 $_def_esd
 $_def_esd_latency
+$_def_jack
 $_def_sys_asoundlib_h
 $_def_alsa_asoundlib_h
 $_def_sunaudio
diff -Nur mplayer-cvs/main/libao2/ao_jack.c mplayer-jack/main/libao2/ao_jack.c
--- mplayer-cvs/main/libao2/ao_jack.c	1970-01-01 01:00:00.000000000 +0100
+++ mplayer-jack/main/libao2/ao_jack.c	2004-06-05 20:55:32.000000000 +0200
@@ -0,0 +1,178 @@
+/*
+ * ao_jack - JACK audio output driver for MPlayer
+ *
+ * Kamil Strzelecki <esack at browarek.net>
+ *
+ * This driver is distribuited under terms of GPL
+ *
+ * It uses bio2jack (http://bio2jack.sf.net/).
+ *
+ */
+
+#include <stdio.h>
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "afmt.h"
+#include "../config.h"
+#include "../mp_msg.h"
+
+#include "bio2jack.h"
+
+static int driver = 0;
+
+static ao_info_t info =
+{
+    "JACK audio output",
+    "jack",
+    "Kamil Strzelecki <esack at browarek.net>",
+    ""
+};
+
+LIBAO_EXTERN(jack)
+
+
+static int control(int cmd, void *arg)
+{
+	switch (cmd) {
+    	case AOCONTROL_GET_VOLUME:	
+			{
+				ao_control_vol_t *vol = (ao_control_vol_t *)arg;
+				int *l, *r;
+				
+				JACK_GetVolume(driver, l, r);
+				vol->left = (float )*l;
+				vol->right = (float )*r;
+				
+				return CONTROL_OK;
+			}
+		case AOCONTROL_SET_VOLUME:
+			{
+				ao_control_vol_t *vol = (ao_control_vol_t *)arg;
+				int l = (int )vol->left,
+					r = (int )vol->right,
+					err = 0;
+
+				if((err = JACK_SetVolume(driver, l, r))) {
+					mp_msg(MSGT_AO, MSGL_ERR, 
+							"AO: [Jack] Setting volume failed, error %d\n",err);
+					return CONTROL_ERROR;
+				}
+				
+				return CONTROL_OK;				  
+			}
+	}
+
+	return(CONTROL_UNKNOWN);
+}
+
+
+static int init(int rate_hz, int channels, int format, int flags)
+{
+	int err, m, frag_spec;
+	unsigned long *rate = NULL;
+	unsigned int bits_per_sample;
+	
+	mp_msg(MSGT_AO, MSGL_INFO, "AO: [Jack] Initialising library.\n");
+	JACK_Init(); //initialize JACK
+
+	switch (format) {
+		case AFMT_U8:
+		case AFMT_S8:
+		    format = AFMT_U8;
+			bits_per_sample = 8;
+			m = 0;
+		    break;
+		default:
+		    format = AFMT_S16_LE;
+			bits_per_sample = 16;
+			m = 2;
+		    break;
+	}
+
+	ao_data.format = format;
+	ao_data.channels = channels;
+	ao_data.samplerate = rate_hz;
+	ao_data.bps = (rate_hz*channels*m);
+	
+	rate = (unsigned long *)&rate_hz;
+	
+	err = JACK_Open(&driver, bits_per_sample, rate,	channels);
+	
+	/* if sample rates doesn't match try to open device with jack's rate and
+	 * let mplayer convert it */
+	if(err == ERR_RATE_MISMATCH) {
+		mp_msg(MSGT_AO, MSGL_INFO, 
+				"AO: [Jack] Sample rate mismatch, trying to resample.\n");
+		
+		ao_data.samplerate = *rate; // mplayer's filter will handle this
+		
+		if((err = JACK_Open(&driver, bits_per_sample, rate, channels))) {
+			mp_msg(MSGT_AO, MSGL_ERR,
+					"AO: [Jack] JACK_Open() failed, error %d\n", err);
+			return 0;
+		}
+    }
+	/* any other error */
+	else if(err != ERR_SUCCESS) {
+	  mp_msg(MSGT_AO, MSGL_ERR, 
+			  "AO: [Jack] JACK_Open() failed, error %d\n", err);
+      return 0;
+	}
+	
+	if(err == ERR_SUCCESS) 
+		mp_msg(MSGT_AO, MSGL_INFO, 
+				"AO: [Jack] OK. I'm ready to go (%d Hz/%d channels/%d bit)\n"
+				, ao_data.samplerate, ao_data.channels, bits_per_sample);
+
+	return 1;
+}
+
+
+static void uninit(int immed)
+{
+	int errval = 0;
+	
+	JACK_Reset(driver);
+	
+	if((errval = JACK_Close(driver)))
+		mp_msg(MSGT_AO, MSGL_ERR, 
+				"AO: [Jack] error closing device, error %d\n", errval);
+}
+
+
+static int play(void* data,int len,int flags)
+{
+	return JACK_Write(driver, data, len);
+}
+
+
+static void audio_pause()
+{
+	JACK_SetState(driver, PAUSED);
+}
+
+
+static void audio_resume()
+{
+	JACK_SetState(driver, PLAYING);
+}
+
+
+static void reset()
+{
+	JACK_Reset(driver);
+}
+
+
+static int get_space()
+{
+	return JACK_GetBytesFreeSpace(driver);
+}
+
+
+static float get_delay()
+{
+	return (float )JACK_GetJackLatency(driver);
+}
+
diff -Nur mplayer-cvs/main/libao2/audio_out.c mplayer-jack/main/libao2/audio_out.c
--- mplayer-cvs/main/libao2/audio_out.c	2004-05-13 00:48:15.000000000 +0200
+++ mplayer-jack/main/libao2/audio_out.c	2004-06-05 20:55:32.000000000 +0200
@@ -22,6 +22,9 @@
 #ifdef USE_ESD
 extern ao_functions_t audio_out_esd;
 #endif
+#ifdef USE_JACK
+extern ao_functions_t audio_out_jack;
+#endif
 extern ao_functions_t audio_out_null;
 #ifdef HAVE_ALSA5
  extern ao_functions_t audio_out_alsa5;
@@ -97,6 +100,9 @@
 #ifdef USE_ESD
         &audio_out_esd,
 #endif
+#ifdef USE_JACK
+        &audio_out_jack,
+#endif
 #ifdef HAVE_NAS
 	&audio_out_nas,
 #endif
diff -Nur mplayer-cvs/main/libao2/bio2jack.c mplayer-jack/main/libao2/bio2jack.c
--- mplayer-cvs/main/libao2/bio2jack.c	1970-01-01 01:00:00.000000000 +0100
+++ mplayer-jack/main/libao2/bio2jack.c	2004-06-05 20:55:32.000000000 +0200
@@ -0,0 +1,1334 @@
+/*
+ * Copyright 2003 Chris Morgan <cmorgan at 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
+ */
+
+#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 <pthread.h>
+#include <sys/time.h>
+
+#include "bio2jack.h"
+
+//FIXME: this is only true if we actually have messages
+//FIXME: probably want to send all state changing messages like:
+// setting reset, stopped, paused etc through messages
+// if we don't do this we run the risk of setting reset, then
+// sending a message and having things execute not in the order that
+// the user sent them
+
+/* enable/disable TRACING through the JACK_Callback() function */
+/* this can sometimes be too much information */
+/* NOTE: need to enable the general tracing as well */
+#define CALLBACK_TRACE 0
+
+#define OUTFILE stderr
+
+#define TRACE_ENABLE   0
+#if TRACE_ENABLE
+    #define TRACE(...) fprintf(OUTFILE, "%s:", __FUNCTION__),	\
+         fprintf(OUTFILE, __VA_ARGS__),				\
+         fflush(OUTFILE);
+#else
+    #define TRACE(...) do{}while(0)
+#endif
+
+    #define ERR(...) fprintf(OUTFILE, "ERR: %s:", __FUNCTION__),\
+         fprintf(OUTFILE, __VA_ARGS__),				\
+         fflush(OUTFILE);
+
+#define min(a,b)   (((a) < (b)) ? (a) : (b))
+#define max(a,b)   (((a) < (b)) ? (b) : (a))
+
+
+/* set to 1 for verbose output */
+#define VERBOSE_OUTPUT          0
+
+/* Structure that holds a packet of wave output data from the client */
+typedef struct wave_header_s
+{
+  char* pData;                 /* pointer to the audio data */
+  long  size;                  /* number of bytes pointed to by pData */
+
+  struct wave_header_s *pNext; /* pointer to the next wave_header_s */
+} wave_header_t;
+
+
+enum cmd_enum { CMD_SET_POSITION };
+
+/* message passing structure */
+typedef struct message_s
+{
+  enum cmd_enum    command;
+  long             data;
+  
+  struct message_s *pNext;     /* pointer to the next message */
+} message_t;
+
+
+typedef struct jack_driver_s
+{
+  int              deviceID;             /* id of this device */
+  long             sample_rate;          /* samples(frames) per second */
+  unsigned long    num_channels;         /* number of output channels(1 is mono, 2 stereo etc..) */
+  unsigned long    bits_per_channel;
+  unsigned long    bytes_per_frame;      /* (num_channels * bits_per_channel) / 8 */
+
+  long             bytesInJack;          /* bytes that we wrote during the previous JACK_Callback() */
+  unsigned long    buffer_size;          /* number of bytes in the buffer allocated for processing data in JACK_Callback */
+
+  char*            sound_buffer;
+  struct timeval   previousTime;         /* time of last JACK_Callback() write to jack, allows for MS accurate bytes played  */
+
+  unsigned long    written_jack_bytes;   /* total number of bytes we have written to jack */
+  unsigned long    played_bytes;         /* total number of bytes jack has played */
+  unsigned long    written_bytes;        /* total bytes written in via JACK_Write() */
+
+  unsigned long    latencyMS;            /* latency in ms between writing and actual audio output of the written data */
+
+  jack_port_t*     out_port_l;           /* ports for left and right channels */
+  jack_port_t*     out_port_r;
+  jack_client_t*   client;               /* pointer to jack client */
+
+  bool             in_use;               /* true if this device is currently in use */
+
+  wave_header_t*   pPlayPtr;             /* pointer to the current wave header */
+  long             playptr_offset;       /* offset to not yet written bytes in pPlayPtr */
+
+  enum status_enum state;                /* one of PLAYING, PAUSED, STOPPED, CLOSED, RESET etc*/
+
+  int              left_volume;          /* percentage of sample value to preserve */
+  int              right_volume;         /* 100 would be no attenuation and 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 */
+
+
+  message_t        *pMessages;           /* linked list of messages we are sending to the callback process */
+} jack_driver_t;
+
+
+/* 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 */
+static int first_free_device = 0;
+#define MAX_OUTDEVICES 10
+static jack_driver_t outDev[MAX_OUTDEVICES];
+
+/* default audio buffer size */
+#define SECONDS .25
+static long MAX_BUFFERED_BYTES = (16 * 2 * (44100/(1/SECONDS))) / 8; /* 16 bits, 2 channels .25 seconds */
+
+#if JACK_CLOSE_HACK
+static void	JACK_CloseDevice(jack_driver_t* this, bool close_client);
+#else
+static void	JACK_CloseDevice(jack_driver_t* this);
+#endif
+
+static int JACK_OpenDevice(jack_driver_t* this);
+
+/* 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
+
+/* Alsaplayer function that applies volume changes to a buffer */
+/* Length is in terms of 32 bit samples */
+static void volume_effect32(void *buffer, int length, int left, int right)
+{
+  short *data = (short *)buffer;
+  int i, v;
+
+  if (right == -1) right = left;
+
+  for(i = 0; i < length; i++)
+  {
+    v = (int) ((*(data) * left) / 100);
+    *(data++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
+    v = (int) ((*(data) * right) / 100);
+    *(data++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
+  }
+}
+
+/* move 16 bit mono/stereo to 16 bit stereo */
+static void sample_move_d16_d16(short *dst, short *src,
+                  unsigned long nsamples, int nChannels)
+{
+  while(nsamples--)
+  {
+    *dst = *src;
+    dst++;
+
+    if(nChannels == 2) src++;
+
+    *dst = *src;
+    dst++;
+
+    src++;
+  }
+}
+
+/* convert from 16 bit to floating point */
+/* allow for copying of stereo data with alternating left/right */
+/* channels to a buffer that will hold a single channel stream */
+/* nsamples is in terms of 16bit samples */
+/* src_skip is in terms of 16bit samples */
+static void sample_move_d16_s16 (sample_t *dst, short *src,
+                        unsigned long nsamples, unsigned long src_skip)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  while (nsamples--)
+  {
+    *dst = (*src) / SAMPLE_MAX_16BIT;
+    dst++;
+    src += src_skip;
+  }
+}
+
+/* fill dst buffer with nsamples worth of silence */
+void sample_silence_dS (sample_t *dst, unsigned long nsamples)
+{
+  /* ALERT: signed sign-extension portability !!! */
+  while (nsamples--)
+  {
+    *dst = 0;
+    dst++;
+  }
+}
+
+/******************************************************************
+ *    JACK_callback
+ *
+ * everytime 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)
+{
+  sample_t* out_l;
+  sample_t* out_r;
+  jack_driver_t* this = (jack_driver_t*)arg;
+
+#if CALLBACK_TRACE
+  TRACE("nframes %ld, sizeof(sample_t) == %d\n", nframes, sizeof(sample_t));
+#endif
+
+  if(!this->client)
+    ERR("client is closed, this is weird...\n");
+
+  out_l = (sample_t *) jack_port_get_buffer(this->out_port_l, nframes);
+  out_r = (sample_t *) jack_port_get_buffer(this->out_port_r, nframes);
+
+#if 0
+  /* process one message */
+  if(this->pMessages)
+  {
+    message_t *msg = this->pMessages;
+
+    /* process a set position command */
+    if(msg->command == CMD_SET_POSITION)
+    {
+      /* set the position byte offset based on the written_bytes and the desired */
+      /* offset */
+      this->position_byte_offset = msg->data - this->written_bytes;
+
+      /* make all positions the same location */
+      this->written_jack_bytes = this->played_bytes = this->written_bytes;
+
+      this->bytesInJack = 0; /* no bytes left in jack else we will get bad byte counts */
+
+      if(this->pPlayPtr != 0)
+      {
+	TRACE("ERROR, setting position but pPlayPtr != 0\n");
+	TRACE("state == %s\n", DEBUGSTATE(this->state));
+      }
+      
+      TRACE("deviceID(%d), setting position to byte offset of %ld\n", this->deviceID, msg->data);
+    }
+
+#if 0
+    if(msg->command == WRITTEN)
+    {
+      this->written_bytes = msg->data;
+      TRACE("deviceID(%d), setting written_bytes to %d\n", this->deviceID, this->written_bytes);
+    } else if(msg->command == WRITTEN_TO_JACK)
+    {
+      this->written_jack_bytes = msg->data;
+      TRACE("deviceID(%d), setting written_jack_bytes to %d\n", this->deviceID, this->written_jack_bytes);
+    }
+    else if(msg->command == PLAYED) /* type is PLAYED */
+    {
+      this->played_bytes = msg->data;
+      TRACE("deviceID(%d), setting played_bytes to %d\n", this->deviceID, this->played_bytes);
+    } else
+    {
+      ERR("unknown type for this->setType\n");
+    }
+#endif
+    this->pMessages = msg->pNext;    /* take this message off of the queue */
+    free(msg);                 /* free up its memory */
+  }  
+#endif
+
+  /* handle playing state */
+  if(this->state == PLAYING)
+  {
+    unsigned long jackFramesAvailable = nframes; /* frames we have left to write to jack */
+    unsigned long outputFramesAvailable;         /* frames we have available this loop */
+    unsigned long numFramesToWrite;              /* num frames we are writing this loop */
+
+    long written = 0;
+    char* buffer;
+
+#if CALLBACK_TRACE
+    TRACE("playing... jackFramesAvailable = %d\n", jackFramesAvailable);
+#endif
+
+#if JACK_CLOSE_HACK
+    if(this->in_use == FALSE)
+    {
+      /* output silence if nothing is being outputted */
+      sample_silence_dS(out_l, nframes);
+      sample_silence_dS(out_r, nframes);
+
+      return 0;
+    }
+#endif
+
+    /* see if our buffer is large enough for the data we are writing */
+    /* ie. Buffer_size < (bytes we already wrote + bytes we are going to write in this loop) */
+    /* Note: sound_buffer is always filled with 16-bit stereo data, even for mono */
+    /* so frame * 2 bytes(16 bits) * 2 output channels */
+    if(this->buffer_size < (jackFramesAvailable * sizeof(short) * 2))
+    {
+      ERR("our buffer must have changed size\n");
+      ERR("allocated %ld bytes, need %ld bytes\n", this->buffer_size,
+	  jackFramesAvailable * sizeof(short) * 2);
+      return 0;
+    }
+
+    /* while we have jackBytesLeft and a wave header to be played */
+    while(jackFramesAvailable && this->pPlayPtr)
+    {
+      /* (bytes of data) / (2 bytes(16 bits) * 2 channels) == frames */
+      outputFramesAvailable = (this->pPlayPtr->size - this->playptr_offset) / (sizeof(short) * 2);
+
+#if CALLBACK_TRACE
+      TRACE("outputFramesAvailable == %ld, jackFramesAvailable == %ld\n", outputFramesAvailable, jackFramesAvailable);
+#endif
+
+      buffer = this->pPlayPtr->pData + this->playptr_offset;
+
+      /* convert to actual frames based on the format */
+      if(this->bits_per_channel == 8)
+        outputFramesAvailable<<=1; /* multiply by 2 to get actual frames after conversion from 8 to 16 bits */
+      if(this->num_channels == 1)
+        outputFramesAvailable<<=1; /* multiply by 2 to get the actual frames available after conversion from 1 to 2 channels */
+
+      numFramesToWrite = min(jackFramesAvailable, outputFramesAvailable); /* write as many bytes as we have space remaining, or as much as we have data to write */
+
+#if CALLBACK_TRACE
+      TRACE("outputFramesAvailable after conversion %d\n", outputFramesAvailable);
+#endif
+
+      /* convert from 8 bit to 16 bit and mono to stereo if necessary */
+      /* otherwise just memcpy to the output buffer */
+      //      if(this->bits_per_channel == 8)
+      //      {
+      //        sample_move_d8_d16 ((short*)this->sound_buffer + ((jackBytesAvailableThisCallback - jackBytesLeft) / sizeof(short)),
+      //                  buffer, jackBytesToWrite, this->num_channels);
+      //      } else if(this->num_channels == 1)
+      
+      /* 16 bit input samples */
+      if(this->num_channels == 1)
+      {
+        sample_move_d16_d16((short*)this->sound_buffer + ((nframes - jackFramesAvailable) * this->bits_per_channel * this->num_channels) / 8,
+                 (short*)buffer, numFramesToWrite, this->num_channels);
+      } else /* just copy the memory over */
+      {
+#if CALLBACK_TRACE
+	TRACE("nframes == %d, jackFramesAvailable == %d, sizeof(sample_t) == %d, this->num_channels\n",
+	      nframes, jackFramesAvailable, sizeof(sample_t), this->num_channels);
+#endif
+        memcpy(this->sound_buffer + ((nframes - jackFramesAvailable) * this->bits_per_channel * this->num_channels) / 8,
+	       buffer, (numFramesToWrite * this->bits_per_channel * this->num_channels) / 8);
+      }
+
+      /* advance to the next wave header if possible, or advance pointer */
+      /* inside of the current header if we haven't completed it */
+      if(numFramesToWrite == outputFramesAvailable)
+      {
+        wave_header_t* pOldHeader;
+
+        free(this->pPlayPtr->pData); /* free the wave data so we don't leak like crazy */
+        this->playptr_offset = 0;
+        pOldHeader = this->pPlayPtr;
+        this->pPlayPtr = this->pPlayPtr->pNext;
+        free(pOldHeader); /* free the wave header structure that we just finished playing */
+      }
+      else
+      {
+	/* else advance by the bytes we took in to write */
+        this->playptr_offset+=((numFramesToWrite * this->bits_per_channel * this->num_channels) / 8);
+      }
+
+      /* add on what we wrote */
+      written+=((numFramesToWrite * this->bits_per_channel * this->num_channels) / 8);
+
+      jackFramesAvailable-=numFramesToWrite; /* take away what was written */
+
+#if CALLBACK_TRACE
+      TRACE("jackFramesAvailable == %d\n", jackFramesAvailable);
+#endif
+    } /* while(jackFramesAvailable && this->pPlayPtr) */
+
+
+    gettimeofday(&this->previousTime, 0); /* record the current time */
+    this->written_jack_bytes+=written; /* update states on wave device */
+    this->played_bytes+=this->bytesInJack; /* move forward by the previous bytes we wrote since those must have finished by now */
+    this->bytesInJack = written; /* record the bytes inside of jack */
+
+    /* Now that we have finished filling the buffer either until it is full or until */
+    /* we have run out of application sound data to process, apply volume and output */
+    /* the audio to the jack server */
+
+    /* apply volume to the buffer */
+    /* NOTE: buffer_size >> 2 to convert from bytes to 16 bit stereo(32bit) samples */
+    volume_effect32(this->sound_buffer, (nframes - jackFramesAvailable), this->left_volume,
+        this->right_volume);
+
+    /* convert from stereo 16 bit to single channel 32 bit float */
+    /* for each jack server channel */
+    /* NOTE: we skip over two sample since we want to only get either the left or right channel */
+    sample_move_d16_s16(out_l, (short*)this->sound_buffer, 
+			(nframes - jackFramesAvailable), 2);
+    sample_move_d16_s16(out_r, (short*)this->sound_buffer + 1,
+			(nframes - jackFramesAvailable), 2);
+
+    /* 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)
+    {
+#if CALLBACK_TRACE
+      TRACE("buffer underrun of %ld frames\n", jackFramesAvailable);
+#endif
+      sample_silence_dS(out_l + (nframes - jackFramesAvailable), jackFramesAvailable);
+      sample_silence_dS(out_r + (nframes - jackFramesAvailable), jackFramesAvailable);
+    }
+  }
+  else if(this->state == PAUSED ||
+    this->state == STOPPED ||
+    this->state == CLOSED || this->state == RESET)
+  {
+#if CALLBACK_TRACE
+      TRACE("PAUSED or STOPPED or CLOSED, outputting silence\n");
+#endif
+      /* output silence if nothing is being outputted */
+      sample_silence_dS(out_l, nframes);
+      sample_silence_dS(out_r, nframes);
+
+      /* if we were told to reset then zero out some variables */
+      /* and transition to STOPPED */
+      if(this->state == RESET)
+      {
+	this->written_jack_bytes   = 0;
+	this->played_bytes         = 0;
+	this->written_bytes        = 0;
+	this->bytesInJack          = 0;
+
+	this->pPlayPtr = 0;
+	this->playptr_offset = 0;
+	this->position_byte_offset = 0;
+
+	/* free up all of the buffers of audio that are queued */
+	/* NOTE: this needs to be done inside of the callback because */
+	/*  the callback could be using any of these buffers */
+	wave_header_t *wh = this->pPlayPtr;
+	while(wh)
+	{
+	  wh = wh->pNext;
+	  free(this->pPlayPtr->pData); /* free up the app data */
+	  free(this->pPlayPtr); /* free the structure itself */
+	  this->pPlayPtr = wh;
+	}
+
+	this->state                = STOPPED; /* transition to STOPPED */
+      }
+  }
+
+#if CALLBACK_TRACE
+  TRACE("done\n");
+#endif
+
+  return 0;
+}
+
+
+/******************************************************************
+ *             JACK_bufsize
+ *
+ *             Called whenever the jack server changes the the max number
+ *             of frames passed to JACK_callback
+ */
+//TODO: probably pull this function out until we find a need for it again
+#if 0
+static int JACK_bufsize (nframes_t nframes, void *arg)
+{
+  jack_driver_t* this = (jack_driver_t*)arg;
+  unsigned long buffer_required;
+  TRACE("the maximum buffer size is now %lu frames\n", nframes);
+
+  /* make sure the callback routine has adequate memory */
+  /* see if our buffer is large enough for the data we are writing */
+  /* ie. Buffer_size < (bytes we already wrote + bytes we are going to write in this loop) */
+  /* frames * 2 bytes in 16 bits * 2 channels of output */
+  buffer_required = nframes * sizeof(short) * 2;
+  if(this->buffer_size < buffer_required)
+  {
+    TRACE("expanding buffer from this->buffer_size == %ld, to %ld\n",
+	 this->buffer_size, buffer_required);
+    this->buffer_size = buffer_required;
+    this->sound_buffer = realloc(this->sound_buffer, this->buffer_size);
+
+    /* if we don't have a buffer then error out */
+    if(!this->sound_buffer)
+    {
+      ERR("error allocating sound_buffer memory\n");
+      return 0;
+    }
+  }
+
+  TRACE("called\n");
+  return 0;
+}
+#endif
+
+/******************************************************************
+ *		JACK_srate
+ */
+int JACK_srate (nframes_t nframes, void *arg)
+{
+  TRACE("the sample rate is now %lu/sec\n", nframes);
+  return 0;
+}
+
+
+/******************************************************************
+ *		JACK_shutdown
+ *
+ * if this is called then jack shut down... handle this appropriately */
+void JACK_shutdown(void* arg)
+{
+  jack_driver_t* this = (jack_driver_t*)arg;
+
+  this->client = 0; /* reset client */
+
+  TRACE("trying to reconnect after sleeping for a short while...\n");
+
+  /* lets see if we can't reestablish the connection */
+  if(!JACK_OpenDevice(this))
+  {
+    ERR("unable to reconnect with jack...\n");
+  }
+}
+
+
+/******************************************************************
+ *		JACK_Error
+ *
+ * Callback for jack errors
+ */
+static void JACK_Error(const char *desc)
+{
+  ERR("%s\n", desc);
+}
+
+
+/******************************************************************
+ *		JACK_SendMessage
+ *
+/* put a message on the message queue of a driver */
+static bool JACK_SendMessage(jack_driver_t* this, enum cmd_enum command, long data)
+{
+  message_t *newMessage;
+  message_t **m;
+
+  newMessage = (message_t*)malloc(sizeof(message_t));
+  if(!newMessage)
+  {
+    ERR("error allocating new message\n");
+    return FALSE;
+  }
+
+  newMessage->command = command;
+  newMessage->data = data;
+  newMessage->pNext = 0; /* setup the next pointer to point to null */
+
+  /* now setup the last pointer in the existing array to point to this header */
+  /* we use a pointer to a pointer here just to make this code more elegant */
+  /* and in case pMessages is null it makes that condition clean */
+  for(m = &(this->pMessages); *m; m = &((*m)->pNext));
+  *m = newMessage; /* point it to this new message */
+
+  return TRUE;
+}
+
+
+/******************************************************************
+ *		JACK_OpenDevice
+ */
+static int JACK_OpenDevice(jack_driver_t* this)
+{
+  const char** ports;
+  int i;
+  char client_name[64];
+  int failed = 0;
+
+  TRACE("creating jack client and setting up callbacks\n");
+
+#if JACK_CLOSE_HACK
+        /* see if this device is already open */
+        if(this->client)
+        {
+          /* if this device is already in use then it is bad for us to be in here */
+          if(this->in_use)
+            return 0;
+
+          TRACE("using existing client\n");
+          this->in_use = TRUE;
+          return 1;
+        }
+#endif	
+
+        /* zero out the buffer pointer and the size of the buffer */
+        this->sound_buffer = 0;
+        this->buffer_size = 0;
+        this->playptr_offset = 0;
+
+	/* set up an error handler */
+	jack_set_error_function(JACK_Error);
+
+        /* try to become a client of the JACK server */
+        snprintf(client_name, sizeof(client_name), "bio2jack_%d_%d", 0, getpid());
+        TRACE("client name '%s'\n", client_name);
+        if ((this->client = jack_client_new (client_name)) == 0)
+        {
+                /* jack has problems with shutting down clients, so lets */
+                /* wait a short while and try once more before we give up */
+                if ((this->client = jack_client_new (client_name)) == 0)
+                {
+                  ERR("jack server not running?\n");
+                  return 0;
+                }
+        }
+
+        /* JACK server to call `JACK_callback()' whenever
+           there is work to be done. */
+        jack_set_process_callback (this->client, JACK_callback, this);
+
+	/* allocate a buffer */
+	/* frames * 2 bytes(16 bits) * 2 channels(left and right) */
+	this->buffer_size = jack_get_buffer_size(this->client) * sizeof(short) * 2;
+	this->sound_buffer = realloc(this->sound_buffer, this->buffer_size);
+
+        /* tell the JACK server to call `srate()' whenever
+           the sample rate of the system changes. */
+        jack_set_sample_rate_callback (this->client, JACK_srate, this);
+
+        /* 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 (this->client, JACK_shutdown, this);
+
+        /* 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. */
+        this->sample_rate = jack_get_sample_rate(this->client);
+        TRACE("engine sample rate: %lu\n", this->sample_rate);
+
+        /* create the left and right channel output ports */
+        /* jack's ports are all mono so for stereo you need two */
+        this->out_port_l = jack_port_register (this->client, "out_l",
+                         JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+        this->out_port_r = jack_port_register (this->client, "out_r",
+                         JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+
+#if JACK_CLOSE_HACK
+        this->in_use = TRUE;
+#endif
+
+        /* tell the JACK server that we are ready to roll */
+        if (jack_activate (this->client))
+        {
+          ERR( "cannot activate client\n");
+          return 0;
+        }
+
+        /* figure out what the ports that we want to output on are */
+        /* NOTE: we do this instead of using stuff like "alsa_pcm:playback_X" because */
+        /*   this way works if names are changed */
+        ports = jack_get_ports(this->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
+
+        /* display a trace of the output ports we found */
+        for(i = 0; ports[i]; i++)
+        {
+          TRACE("ports[%d] = '%s'\n", i, ports[i]);
+        }
+
+        if(!ports)
+        {
+          ERR("jack_get_ports() failed to find 'JackPortIsPhysical|JackPortIsInput'\n");
+        }
+
+        /* connect the ports. Note: you can't do this before
+           the client is activated (this may change in the future).
+        */
+        /* we want to connect to two ports so we have stereo output ;-) */
+
+        if(jack_connect(this->client, jack_port_name(this->out_port_l), ports[0]))
+        {
+          ERR ("cannot connect to output port %d('%s')\n", 0, ports[0]);
+          failed = 1;
+        }
+
+        if(jack_connect(this->client, jack_port_name(this->out_port_r), ports[1]))
+        {
+          ERR ("cannot connect to output port %d('%s')\n", 1, ports[1]);
+          failed = 1;
+        }
+
+        free(ports); /* free the returned array of ports */
+
+        /* if something failed we need to shut the client down and return 0 */
+        if(failed)
+        {
+          JACK_CloseDevice(this, TRUE);
+          return 0;
+        }
+
+        return 1; /* 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* this, bool close_client)
+#else
+static void JACK_CloseDevice(jack_driver_t* this)
+#endif
+{
+#if JACK_CLOSE_HACK
+    if(close_client)
+    {
+#endif
+      TRACE("closing the jack client thread\n");
+      if(this->client)
+      {
+	//        jack_deactivate(this->client); /* supposed to help the jack_client_close() to succeed */
+        jack_client_close (this->client);
+      }
+
+      /* wait here for just a bit */
+#define usecInSec 1000000
+      usleep(usecInSec/5);
+
+      JACK_Reset(this->deviceID);
+      this->client = 0; /* reset client */
+      free(this->sound_buffer); /* free buffer memory */
+      this->sound_buffer = 0;
+      this->buffer_size = 0; /* zero out size of the buffer */
+#if JACK_CLOSE_HACK
+    } else
+    {
+      TRACE("setting in_use to FALSE\n");
+      this->in_use = FALSE;
+
+      if(!this->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 */
+void JACK_Reset(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+
+  TRACE("resetting deviceID(%d)\n", 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 */
+  this->state = RESET; /* tell the callback that we are to reset, the callback will transition this to STOPPED */
+}
+
+
+/*
+ * 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)
+{
+  jack_driver_t *this = &outDev[first_free_device];
+
+  TRACE("bits_per_channel=%d rate=%d, channels=%d\n", bits_per_channel, *rate, channels);
+  //TRACE("bits_per_channel=%d rate=, channels=%d\n", bits_per_channel, channels);
+
+  /* initialize some variables */
+  this->in_use = FALSE;
+
+  JACK_Reset(this->deviceID); /* flushes all queued buffers, sets status to STOPPED and resets some variables */
+
+  /* this->sample_rate is set by JACK_OpenDevice() */
+  this->bits_per_channel       = bits_per_channel;
+  this->num_channels           = channels;
+  this->bytes_per_frame        = (this->bits_per_channel*this->num_channels)/8;
+
+  TRACE("bytes_per_frame == %d\n", this->bytes_per_frame);
+
+  /* make sure bytes_per_frame is valid and non-zerp */
+  if(!this->bytes_per_frame)
+  {
+    ERR("bytes_per_frame is zero\n");
+    return ERR_BYTES_PER_FRAME_INVALID;
+  }
+
+  /* go and open up the device */
+  if(!JACK_OpenDevice(this))
+  {
+    TRACE("error opening jack device\n");
+    return ERR_OPENING_JACK;
+  } else
+  {
+    TRACE("succeeded opening jack device\n");
+  }
+
+  /* make sure the sample rate of the jack server matches that of the client */
+  if((long)(*rate) != this->sample_rate)
+  {
+    TRACE("rate of %d doesn't match jack sample rate of %d\n", *rate, this->sample_rate);
+    *rate = this->sample_rate;
+    JACK_CloseDevice(this, TRUE);
+    return ERR_RATE_MISMATCH;
+  }
+
+  first_free_device++; /* record that we opened this device */
+
+
+  TRACE("sizeof(sample_t) == %d\n", sizeof(sample_t));
+
+  this->latencyMS = 10;
+
+  TRACE("this->latencyMS == %dms\n", this->latencyMS);
+
+  *deviceID = this->deviceID; /* set the deviceID for the caller */
+  return ERR_SUCCESS; /* success */
+}
+
+/* Close the jack device */
+/* FIXME: not sure what to do here if we have been given the choice of closing */
+/* the client or not.. */
+//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* this = &outDev[deviceID];
+
+  TRACE("deviceID(%d)\n", deviceID);
+
+#if JACK_CLOSE_HACK
+  JACK_CloseDevice(this, TRUE);
+#else
+  JACK_CloseDevice(this);
+#endif
+
+  JACK_Reset(this->deviceID); /* reset this device to a normal starting state */
+
+  first_free_device--; /* decrement device count */
+
+  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, char *data, unsigned long bytes)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  wave_header_t *newWaveHeader;
+  wave_header_t **wh;
+  struct timeval now;
+  long bytes_stored;
+
+  TRACE("deviceID(%d), bytes == %ld\n", deviceID, bytes);
+
+  gettimeofday(&now, 0);
+  TRACE("Starting Time = %ld.%ld\n", now.tv_sec, now.tv_usec);
+
+  /* check and see that we have enough space for this audio */
+  TRACE("bytes stored == %ld\n", this->written_bytes - this->played_bytes);
+  if((this->written_bytes - this->played_bytes) + bytes > (long)MAX_BUFFERED_BYTES) /* hackish check for now */
+  {
+    TRACE("bytes stored(%ld) + bytes(%ld) > MAX_BUFFERED_BYTES(%ld), returning 0\n", this->written_bytes - this->played_bytes, bytes, MAX_BUFFERED_BYTES);
+    return 0; /* indicate that we couldn't write any bytes */
+  }
+
+  newWaveHeader = (wave_header_t*)malloc(sizeof(wave_header_t));   /* create a wave header for this data */
+  if(!newWaveHeader)
+  {
+    ERR("error allocating memory for newWaveHeader\n");
+  }
+
+  newWaveHeader->pData = (char*)malloc(sizeof(char) * bytes); /* allocate memory for the data */
+  memcpy(newWaveHeader->pData, data, sizeof(char) * bytes);   /* copy in the data */
+  newWaveHeader->size = bytes;   /* update the size */
+  newWaveHeader->pNext = 0;   /* setup the next pointer to point to null */
+
+  /* now setup the last pointer in the existing array to point to this header */
+  /* we use a pointer to a pointer here just to make this code more elegant */
+  /* and in case pQueuePtr is null it makes that condition clean */
+  for(wh = &(this->pPlayPtr); *wh; wh = &((*wh)->pNext));
+  *wh = newWaveHeader; /* point it to this new header */
+
+  this->written_bytes += bytes; /* update written_bytes */
+
+  if (!this->pPlayPtr) /* if we have no header being played then use this one */
+  {
+    this->pPlayPtr = newWaveHeader;
+    this->playptr_offset = 0;
+  }
+
+  /* if we are currently STOPPED we should start playing now... */
+  if (this->state == STOPPED)
+  {
+    TRACE("currently STOPPED, transitioning to PLAYING\n");
+    this->state = PLAYING;
+  }
+
+  bytes_stored = JACK_GetBytesStored(deviceID);
+  TRACE("bytes stored == %ld\n", bytes_stored);
+
+//  gettimeofday(&now, 0);
+//  TRACE("Ending Time = %ld.%ld\n", now.tv_sec, now.tv_usec);
+
+  TRACE("returning bytes written of %d\n", bytes);
+  return bytes; /* return the number of bytes we wrote out */
+}
+
+#if 0
+/* the client is exiting, close the audio device and the corresponding client */
+static void JACK_exit(int deviceID)
+{
+  jack_driver_t* this = &outDev[deviceID];
+
+  TRACE("deviceID(%d)\n", deviceID);
+
+  JACK_CloseDevice(this, TRUE); /* close the device, FORCE the client to close */
+
+  free (this);
+}
+#endif
+
+/* Set the volume */
+/* return 0 for success */
+/* NOTE: we check for invalid volume values */
+int JACK_SetVolume(int deviceID, int left, int right)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  int return_val = 0;
+
+  /* check for and correct invalid volume values */
+  if((left > 100) || (right > 100))
+  {
+    left = max(100, left);
+    right = max(100, right);
+    return_val = 1;
+  }
+
+  TRACE("deviceID(%d), setting volume of l(%d), r(%d)\n", deviceID, left, right);
+
+  this->left_volume = left;
+  this->right_volume = right;
+
+  return return_val;
+}
+
+/* Return the current volume in the inputted pointers */
+/* NOTE: we check for null pointers being passed in just in case */
+void JACK_GetVolume(int deviceID, int *left, int *right)
+{
+  jack_driver_t *this = &outDev[deviceID];
+
+  if(left) *left = this->left_volume;
+  if(right) *right = this->right_volume;
+
+#if VERBOSE_OUTPUT
+  TRACE("deviceID(%d), returning volume of l(%d), r(%d)\n", deviceID, *left, *right);
+#endif
+}
+
+/* Controls the state of the playback(playing, paused, ...) */
+int JACK_SetState(int deviceID, enum status_enum state)
+{
+  jack_driver_t *this = &outDev[deviceID];
+
+  switch (state) {
+  case PAUSED:
+    this->state = PAUSED;
+    break;
+
+  case PLAYING:
+    this->state = PLAYING;
+    break;
+
+  case STOPPED:
+    this->state = STOPPED;
+    break;
+  default:
+    TRACE("unknown state of %d\n", state);
+  }
+
+  TRACE("%s\n", DEBUGSTATE(this->state));
+
+  return 0;
+}
+
+/* Retrieve the current state of the device */
+enum status_enum JACK_GetState(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  enum status_enum return_val;
+
+  return_val = this->state;
+  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 */
+long JACK_GetBytesPerSecond(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  long return_val;
+
+  return_val =  this->bytes_per_frame * this->sample_rate;
+#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 */
+long JACK_GetBytesStored(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  long return_val;
+
+  return_val =  this->written_bytes - this->played_bytes;
+
+  if(return_val < 0)
+    ERR("written_bytes == %ld < played_bytes == %ld\n", this->written_bytes, this->played_bytes);
+
+  TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val);
+
+  return return_val;
+}
+
+/* Return the number of bytes we can write to the device */
+long JACK_GetBytesFreeSpace(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  long return_val;
+
+  return_val = MAX_BUFFERED_BYTES - JACK_GetBytesStored(deviceID);
+  TRACE("deviceID(%d), MAX_BUFFERED_BYTES - bytes stored == %ld\n", deviceID, return_val);
+
+  return return_val;
+}
+
+/* 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 the current position of the driver, either in bytes or */
+/* in milliseconds */
+long JACK_GetPosition(int deviceID, enum pos_enum position, int type)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  long return_val = 0;  
+  struct timeval now;
+  long elapsedMS;
+  double sec2msFactor = 1000;
+
+  char *type_str;
+
+  /* if we are reset we should return a position of 0 */
+  if(this->state == RESET)
+  {
+    TRACE("we are currently RESET, returning 0\n");
+    return 0;
+  }
+
+  if(type == WRITTEN)
+  {
+    type_str = "WRITTEN";
+    return_val = this->written_bytes;
+  }
+  else if(type == WRITTEN_TO_JACK)
+  {
+    type_str = "WRITTEN_TO_JACK";
+    return_val = this->written_jack_bytes;
+  }
+  else if(type == PLAYED)  /* account for the elapsed time for the played_bytes */
+  {
+    type_str = "PLAYED";
+    return_val = this->played_bytes;
+    gettimeofday(&now, 0);
+
+    elapsedMS = TimeValDifference(&this->previousTime, &now); /* find the elapsed milliseconds since last JACK_Callback() */
+
+    TRACE("elapsedMS since last callback is '%d'\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(this->bytesInJack != 0)
+    {
+      return_val+=((double)elapsedMS * ((double)JACK_GetBytesPerSecond(this->deviceID) / sec2msFactor));
+    } else
+    {
+      TRACE("bytesInJack == 0\n");
+    }
+  }
+
+  /* add on the offset */
+  return_val+=this->position_byte_offset;
+
+  /* convert byte position to milliseconds value if necessary */
+  if(position == MILLISECONDS)
+    return_val = ((double)return_val / (double)JACK_GetBytesPerSecond(this->deviceID)) * (double)sec2msFactor;
+
+  TRACE("deviceID(%d), type(%s), return_val = %d\n", deviceID, type_str, return_val);
+
+  return return_val;
+}
+
+// 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 *this = &outDev[deviceID];
+  double sec2msFactor = 1000;
+  long input_value = value;
+  /* convert the incoming value from milliseconds into bytes */
+  if(position == MILLISECONDS)
+    value = ((double)value*(double)JACK_GetBytesPerSecond(this->deviceID)) / sec2msFactor;
+
+  TRACE("deviceID(%d) input_value of %ld, new value of %ld, setting position_byte_offset to %ld\n", this->deviceID, input_value, value, value - this->written_bytes);
+
+  /* ensure that if the user asks for the position */
+  /* they will at this instant get the correct position */
+  this->position_byte_offset = value - this->written_bytes;
+}
+
+/* Return the number of bytes per frame, or (channels * bits_per_channel) / 8 */
+long JACK_GetBytesPerFrame(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  long return_val;
+
+  return_val = this->bytes_per_frame;
+  TRACE("deviceID(%d), return_val = %d\n", deviceID, return_val);
+  
+  return return_val;
+}
+
+/* Return the number of bytes we buffer max */
+long JACK_GetMaxBufferedBytes(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  long return_val;
+
+  return_val = MAX_BUFFERED_BYTES;
+  TRACE("getting MAX_BUFFERED_BYTES of %d\n", return_val);
+
+  return return_val;
+}
+
+/* Set the max number of bytes the jack driver should buffer */
+void JACK_SetMaxBufferedBytes(int deviceID, long max_buffered_bytes)
+{
+  jack_driver_t *this = &outDev[deviceID];
+
+  TRACE("setting MAX_BUFFERED_BYTES to %d, from %d\n", max_buffered_bytes, MAX_BUFFERED_BYTES);
+  MAX_BUFFERED_BYTES = max_buffered_bytes;
+}
+
+/* Get the number of channels */
+int JACK_GetNumChannels(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  int return_val = this->num_channels;
+
+  TRACE("getting num_channels of %d\n", return_val);
+
+  return return_val;
+}
+ 
+/* Set the number of channels */
+int JACK_SetNumChannels(int deviceID, int channels)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  int return_val = this->num_channels;
+  int bpf = this->bytes_per_frame;
+
+  long positionMS = JACK_GetPosition(deviceID, MILLISECONDS, PLAYED);
+
+  this->num_channels = channels;
+  this->bytes_per_frame = (this->bits_per_channel*this->num_channels)/8;
+
+  JACK_SetPosition(deviceID, MILLISECONDS, positionMS);
+
+  TRACE("changing num_channels from '%d' to '%d'\n", return_val, channels);
+  TRACE("bytes_per_frame changed from '%d' to '%d'\n", bpf, this->bytes_per_frame);
+
+  return return_val;
+}
+ 
+/* Get the number of samples per second, the sample rate */
+long JACK_GetSampleRate(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  int return_val = this->sample_rate;
+
+  TRACE("getting sample_rate of %d\n", return_val);
+
+  return return_val;
+}
+
+/* Initialize the jack porting libarary to a clean state */
+void JACK_Init(void)
+{
+  jack_driver_t *this;
+  int x;
+
+  TRACE("\n");
+
+  for(x = 0; x < MAX_OUTDEVICES; x++)
+  {
+    this = &outDev[x];
+
+    JACK_Reset(x);
+
+    this->deviceID        = x; /* makes it easy to convert a pointer back into a deviceID */
+    this->client          = 0;
+    this->in_use          = FALSE;
+    this->left_volume     = 25; /* default to 25% volume at driver init */
+    this->right_volume    = 25;
+    this->state           = CLOSED;
+    this->bytes_per_frame = 0;
+    this->sample_rate     = 0;
+    this->pMessages       = 0; /* no messages */
+    this->position_byte_offset = 0; /* no offset applied now */
+    gettimeofday(&this->previousTime, 0); /* record the current time */
+  }
+
+  TRACE("finished\n");
+}
+
+/* Get the latency, in ms, of jack */
+long JACK_GetJackLatency(int deviceID)
+{
+  jack_driver_t *this = &outDev[deviceID];
+  long return_val;
+
+  return_val = jack_port_get_latency(this->out_port_l);
+  TRACE("got latency of %dms\n", return_val);
+
+  return return_val;
+}
diff -Nur mplayer-cvs/main/libao2/bio2jack.h mplayer-jack/main/libao2/bio2jack.h
--- mplayer-cvs/main/libao2/bio2jack.h	1970-01-01 01:00:00.000000000 +0100
+++ mplayer-jack/main/libao2/bio2jack.h	2004-06-05 20:55:32.000000000 +0200
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003 Chris Morgan <cmorgan at 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>
+
+#define bool long
+
+#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_FRAME_INVALID    3
+
+
+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);          /* initialize the jack library, this needs to be called before all other functions */
+int  JACK_Open(int* deviceID, unsigned int bits_per_sample, unsigned long *rate, int channels);
+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, char *data, unsigned long bytes); /* returns the number of bytes written */
+
+/* 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); /* return the latency in milliseconds of jack */
+
+int JACK_SetState(int deviceID, enum status_enum state); /* playing, paused, stopped */
+enum status_enum JACK_GetState(int deviceID);
+
+long JACK_GetMaxBufferedBytes(int deviceID);
+void JACK_SetMaxBufferedBytes(int deviceID, long max_buffered_bytes); /* set the max number of bytes the jack driver should buffer */
+
+
+/* Properties of the jack driver */
+int  JACK_SetVolume(int deviceID, int left, int right); /* returns 0 on success */
+void JACK_GetVolume(int deviceID, int *left, int *right);
+
+long JACK_GetBytesPerSecond(int deviceID); /* bytes_per_frame * sample_rate */
+long JACK_GetBytesStored(int deviceID); /* bytes currently buffered in the device */
+long JACK_GetBytesFreeSpace(int deviceID); /* bytes of free space in the buffers */
+long JACK_GetBytesPerFrame(int deviceID);
+int  JACK_GetNumChannels(int deviceID);
+int  JACK_SetNumChannels(int deviceID, int channels);
+long JACK_GetSampleRate(int deviceID); /* samples per second */
+
+#endif /* #ifndef JACK_OUT_H */
+
diff -Nur mplayer-cvs/main/libao2/Makefile mplayer-jack/main/libao2/Makefile
--- mplayer-cvs/main/libao2/Makefile	2003-05-23 14:37:58.000000000 +0200
+++ mplayer-jack/main/libao2/Makefile	2004-06-05 20:55:32.000000000 +0200
@@ -6,7 +6,7 @@
 
 OBJS=$(SRCS:.c=.o)
 
-CFLAGS  = $(OPTFLAGS) -I. -I.. $(ARTS_INC) $(ESD_INC) $(SDL_INC) $(X11_INC) $(EXTRA_INC) $(DXR2_INC) $(DVB_INC)
+CFLAGS  = $(OPTFLAGS) -I. -I.. $(ARTS_INC) $(ESD_INC) $(JACK_INC) $(SDL_INC) $(X11_INC) $(EXTRA_INC) $(DXR2_INC) $(DVB_INC)
 
 .SUFFIXES: .c .o
 
diff -Nur mplayer-cvs/main/Makefile mplayer-jack/main/Makefile
--- mplayer-cvs/main/Makefile	2004-05-08 19:52:24.000000000 +0200
+++ mplayer-jack/main/Makefile	2004-06-05 20:55:32.000000000 +0200
@@ -33,7 +33,7 @@
 OBJS_MPLAYER = $(SRCS_MPLAYER:.c=.o)
 
 VO_LIBS = $(AA_LIB) $(X_LIB) $(SDL_LIB) $(GGI_LIB) $(MP1E_LIB) $(MLIB_LIB) $(SVGA_LIB) $(DIRECTFB_LIB) $(CACA_LIB)
-AO_LIBS = $(ARTS_LIB) $(ESD_LIB) $(NAS_LIB) $(SGIAUDIO_LIB)
+AO_LIBS = $(ARTS_LIB) $(ESD_LIB) $(JACK_LIB) $(NAS_LIB) $(SGIAUDIO_LIB)
 CODEC_LIBS = $(AV_LIB) $(FAME_LIB) $(MAD_LIB) $(VORBIS_LIB) $(THEORA_LIB) $(FAAD_LIB) $(LIBLZO_LIB) $(DECORE_LIB) $(XVID_LIB) $(PNG_LIB) $(Z_LIB) $(JPEG_LIB) $(ALSA_LIB) $(XMMS_LIB) $(MATROSKA_LIB) 
 COMMON_LIBS = libmpcodecs/libmpcodecs.a mp3lib/libMP3.a liba52/liba52.a libmpeg2/libmpeg2.a $(W32_LIB) $(DS_LIB) libaf/libaf.a libmpdemux/libmpdemux.a input/libinput.a postproc/libswscale.a osdep/libosdep.a $(DVDREAD_LIB) $(CODEC_LIBS) $(FREETYPE_LIB) $(TERMCAP_LIB) $(CDPARANOIA_LIB) $(MPLAYER_NETWORK_LIB) $(WIN32_LIB) $(GIF_LIB) $(MACOSX_FRAMEWORKS) $(SMBSUPPORT_LIB) $(FRIBIDI_LIB) $(FONTCONFIG_LIB) $(ENCA_LIB)
 


More information about the MPlayer-dev-eng mailing list