[AD] Mixer diff #3

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Another diff for mixer.c, to be applied to 4.1.13's source. This version includes two new functions: get_mixer_quality and set_mixer_quality. They can be used and the changes will occur in real-time (not useful for the DirectX mixer).

The mixer streams are probably complete. They've been modified slightly to deal with indices instead of pointers, due to switching from a linked list to a resizeable array. All the new functions have a, however small, description.

There's about two things left to do. The first.. given being able to see the code between the low quality and hq1 mixers, as well as the profiling provided previously, would anyone object to removing the low quality mixer? The information I have strongly suggests there is no longer a need for it, as well it would haelp to trim down the code some more. If I don't hear any sustainable arguments to keep it, I'll remove it for the next version.

As for the second, and this is beyond my current scope to do, the DirectX mixer will need to be either changed to use Allegro's mixer as well as other DX voices or just removed completely, for the new functionality to be of any practical use. The more I think about it, the more I realize that the DX mixer created a much bigger change in functionality than even the recent change of yield_timeslice. I've ranted about it before, so I'll save it this time, but it is my strong opinion the DX mixer driver should be removed, and the Allegro mixer left to handle all the mixing.

- Kitty Cat (who just realized the latest mixer diff is bigger than the current mixer source) :o
--- mixer.c.orig	2004-03-21 02:08:54.000000000 -0800
+++ mixer.c	2004-04-19 22:26:18.000000000 -0700
@@ -20,6 +20,10 @@
  *
  *      Synchronization added by Sam Hocevar.
  *
+ *      Chris Robinson included functions to report the mixer's settings,
+ *      switched to signed 24-bit mixing, cleaned up some of the mess the
+ *      code had gathered, and added the mixer stream functionality.
+ *
  *      See readme.txt for copyright information.
  */
 
@@ -34,9 +38,13 @@
 typedef struct MIXER_VOICE
 {
    int playing;               /* are we active? */
-   int stereo;                /* mono or stereo input data? */
-   unsigned char *data8;      /* data for 8 bit samples */
-   unsigned short *data16;    /* data for 16 bit samples */
+   int channels;              /* # of chaanels for input data? */
+   int bits;                  /* sample bit-depth */
+   union {
+      unsigned char *u8;      /* data for 8 bit samples */
+      unsigned short *u16;    /* data for 16 bit samples */
+      void *buffer;           /* generic data pointer */
+   } data;
    long pos;                  /* fixed point position in sample */
    long diff;                 /* fixed point speed of play */
    long len;                  /* fixed point sample length */
@@ -47,7 +55,45 @@
 } MIXER_VOICE;
 
 
-#define MIX_VOLUME_LEVELS     32
+typedef struct MIXER_STREAM
+{
+   /* These make it easier to update in real time */
+   int freq;
+   int pan;
+   int vol;
+
+   /* Stuff used by the mixer */
+   int bits;
+   int channels;
+   int lvol, rvol;
+   int step;
+
+   /* Current data info */
+   union {
+      unsigned char *u8;
+      unsigned short *u16;
+      void *buffer;
+   } data;
+   unsigned int pos;
+   unsigned int end_pos;
+
+   /* Next stream */
+   struct {
+      void *buffer;
+      unsigned int buffer_len;
+   } stored;
+
+   /* Flag to tell the mixer to stop the stream or kill it when it's done,
+      or to tell if it's alrady dead */
+   int status;
+} MIXER_STREAM;
+static MIXER_STREAM *mixer_stream;
+static int mixer_stream_count;
+
+#define AUTOKILL (-1)
+#define DEAD     (-2)
+
+
 #define MIX_FIX_SHIFT         8
 #define MIX_FIX_SCALE         (1<<MIX_FIX_SHIFT)
 
@@ -59,41 +105,22 @@
 static MIXER_VOICE mixer_voice[MIXER_MAX_SFX];
 
 /* temporary sample mixing buffer */
-static unsigned short *mix_buffer = NULL; 
+static signed int *mix_buffer = NULL; 
 
 /* lookup table for converting sample volumes */
-typedef signed short MIXER_VOL_TABLE[256];
+#define MIX_VOLUME_LEVELS     32
+typedef signed int MIXER_VOL_TABLE[256];
 static MIXER_VOL_TABLE *mix_vol_table = NULL;
 
-/* lookup table for amplifying and clipping samples */
-static unsigned short *mix_clip_table = NULL;
-
-#define MIX_RES_16            14
-#define MIX_RES_8             10
-
-/* alternative table system for high-quality sample mixing */
-#define BITS_PAN              7 
-#define BITS_VOL              7 
-#define BITS_MIXER_CORE       32
-#define BITS_SAMPLES          16
-
-typedef unsigned short VOLUME_T;
-
-#define BITS_TOT (BITS_PAN+BITS_VOL) 
-#define ENTRIES_VOL_TABLE (1<<BITS_TOT)
-#define SIZE_VOLUME_TABLE (sizeof(VOLUME_T)*ENTRIES_VOL_TABLE)
-
-static VOLUME_T *volume_table = NULL;
-
 /* flags for the mixing code */
 static int mix_voices;
 static int mix_size;
 static int mix_freq;
-static int mix_stereo;
-static int mix_16bit;
+static int mix_channels;
+static int mix_bits;
 
 /* shift factor for volume per voice */
-static int voice_volume_scale = -1;
+static int voice_volume_scale = 0;
 
 static void mixer_lock_mem(void);
 
@@ -103,6 +130,370 @@
 #endif
 
 
+/* clamp_volume:
+ *  Clamps an integer between two values.
+ */
+static INLINE int clamp_val(int i, int min, int max)
+{
+   /* Clamp to min */
+   i -= min;
+   i &= (~i) >> 31;
+   i += min;
+
+   /* Clamp to max */
+   i -= max;
+   i &= i >> 31;
+   i += max;
+
+   return i;
+}
+
+
+/* set_mixer_quality:
+ *  Sets the resampling quality of the mixer. Valid values are the same as
+ *  the 'quality' config variable.
+ */
+void set_mixer_quality(int quality)
+{
+   if(mix_channels == 1)
+      quality = 0;
+   else
+      quality = clamp_val(quality, 0, 2);
+
+   _sound_hq = quality;
+}
+END_OF_FUNCTION(set_mixer_quality);
+
+/* get_mixer_quality:
+ *  Returns the current mixing quality, as loaded by the 'quality' config
+ *  variable, or a previous call to set_mixer_quality.
+ */
+int get_mixer_quality(void)
+{
+   return _sound_hq;
+}
+END_OF_FUNCTION(get_mixer_quality);
+
+
+/* get_mixer_frequency:
+ *  Returns the mixer frequency, in Hz.
+ */
+int get_mixer_frequency(void)
+{
+   return mix_freq;
+}
+END_OF_FUNCTION(get_mixer_frequency);
+
+/* get_mixer_bits:
+ *  Returns the mixer bitdepth.
+ */
+int get_mixer_bits(void)
+{
+   return mix_bits;
+}
+END_OF_FUNCTION(get_mixer_bits);
+
+/* get_mixer_channels:
+ *  Returns the number of output channels.
+ */
+int get_mixer_channels(void)
+{
+   return mix_channels;
+}
+END_OF_FUNCTION(get_mixer_channels);
+
+/* get_mixer_voices:
+ *  Returns the number of voices allocated to the mixer.
+ */
+int get_mixer_voices(void)
+{
+   return mix_voices;
+}
+END_OF_FUNCTION(get_mixer_voices);
+
+/* get_mixer_buffer_length:
+ *  Returns the number of samples per channel in the mixer buffer.
+ */
+int get_mixer_buffer_length(void)
+{
+   ASSERT(mix_channels);
+   return mix_size / mix_channels;
+}
+END_OF_FUNCTION(get_mixer_buffer_length);
+
+
+/* allocate_mixer_stream:
+ *  Allocates a mixer stream to pass pre-formatted audio data into the mixer
+ *  buffer. The audio data must always be unsigned, with the bitdepth and
+ *  channel count specified. An index to the mixer stream is returned, or -1
+ *  on error.
+ */
+int allocate_mixer_stream(int bits, int channels, int freq, int vol, int pan)
+{
+   MIXER_STREAM *tmp;
+   int i;
+
+   /* Check valid values */
+   if(((bits != 8)&&(bits != 16)) || ((channels != 1)&&(channels != 2)))
+      return -1;
+
+   if(freq <= 0)
+      return -1;
+
+   vol = clamp_val(vol, 0, 255);
+   pan = clamp_val(pan, 0, 255);
+
+   /* See if there's any dead streams we can reuse */
+   for(i = 0;i < mixer_stream_count;++i) {
+      /* If the mixer stream is dead, hijack it */
+      if(mixer_stream[i].status == DEAD) {
+	 mixer_stream[i].bits = bits;
+	 mixer_stream[i].channels = channels;
+
+	 mixer_stream[i].lvol = vol * (255-pan);
+	 mixer_stream[i].rvol = vol * pan;
+	 mixer_stream[i].lvol += mixer_stream[i].lvol >> 7;
+	 mixer_stream[i].rvol += mixer_stream[i].rvol >> 7;
+	 mixer_stream[i].step = freq * MIX_FIX_SCALE / mix_freq;
+
+	 mixer_stream[i].freq = freq;
+	 mixer_stream[i].pan = pan;
+	 mixer_stream[i].vol = vol;
+
+	 /* Revive! */
+	 mixer_stream[i].status = 0;
+	 return i;
+      }
+   }
+
+   i = -1;
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->lock_mutex(mixer_mutex);
+#endif
+   UNLOCK_DATA(mixer_stream, mixer_stream_count * sizeof(MIXER_STREAM));
+   tmp = realloc(mixer_stream, (mixer_stream_count+1) * sizeof(MIXER_STREAM));
+   if(tmp) {
+      mixer_stream = tmp;
+
+      i = mixer_stream_count;
+      ++mixer_stream_count;
+
+      mixer_stream[i].bits = bits;
+      mixer_stream[i].channels = channels;
+
+      mixer_stream[i].lvol = vol * (255-pan);
+      mixer_stream[i].rvol = vol * pan;
+      mixer_stream[i].lvol += mixer_stream[i].lvol >> 7;
+      mixer_stream[i].rvol += mixer_stream[i].rvol >> 7;
+      mixer_stream[i].step = freq * MIX_FIX_SCALE / mix_freq;
+
+      mixer_stream[i].freq = freq;
+      mixer_stream[i].pan = pan;
+      mixer_stream[i].vol = vol;
+   }
+   LOCK_DATA(mixer_stream, sizeof(MIXER_STREAM));
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->unlock_mutex(mixer_mutex);
+#endif
+
+   return i;
+}
+END_OF_FUNCTION(allocate_mixer_stream);
+
+/* deallocate_mixer_stream:
+ *  Stops and kills off a mixer stream.
+ */
+void deallocate_mixer_stream(int stream)
+{
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->lock_mutex(mixer_mutex);
+#endif
+   mixer_stream[stream].stored.buffer = NULL;
+   mixer_stream[stream].stored.buffer_len = 0;
+   mixer_stream[stream].data.buffer = NULL;
+   mixer_stream[stream].end_pos = 0;
+   mixer_stream[stream].pos = 0;
+   mixer_stream[stream].status = DEAD;
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->unlock_mutex(mixer_mutex);
+#endif
+}
+END_OF_FUNCTION(deallocate_mixer_stream);
+
+/* release_mixer_stream:
+ *  Marks a mixer stream to deactivate after the current lot of audio is done
+ *  playing. Since the stream may be killed at any time, the pointer should
+ *  not be used after this function.
+ */
+void release_mixer_stream(int stream)
+{
+   mixer_stream[stream].status = AUTOKILL;
+}
+END_OF_FUNCTION(release_mixer_stream);
+
+/* stop_mixer_stream:
+ *  Stops and flushes the mixer stream.
+ */
+void stop_mixer_stream(int stream)
+{
+   if(mixer_stream[stream].data.buffer) {
+#ifdef ALLEGRO_MULTITHREADED
+      system_driver->lock_mutex(mixer_mutex);
+#endif
+      mixer_stream[stream].stored.buffer = NULL;
+      mixer_stream[stream].stored.buffer_len = 0;
+      mixer_stream[stream].data.buffer = NULL;
+      mixer_stream[stream].end_pos = 0;
+      mixer_stream[stream].pos = 0;
+#ifdef ALLEGRO_MULTITHREADED
+      system_driver->unlock_mutex(mixer_mutex);
+#endif
+   }
+}
+END_OF_FUNCTION(stop_mixer_stream);
+
+/* get_mixer_stream_position:
+ *  Returns the current sample position of the mixer stream, or -1 if
+ *  inactive.
+ */
+int get_mixer_stream_position(int stream)
+{
+   if(!mixer_stream[stream].data.buffer)
+      return -1;
+
+   return mixer_stream[stream].pos >> MIX_FIX_SHIFT;
+}
+END_OF_FUNCTION(get_mixer_stream_position);
+
+/* get_mixer_stream_freq:
+ *  Returns the current playback frequency of the mixer stream.
+ */
+int get_mixer_stream_freq(int stream)
+{
+   return mixer_stream[stream].freq;
+}
+END_OF_FUNCTION(get_mixer_stream_freq);
+
+/* get_mixer_stream_volume:
+ *  Returns the current playback volume of the mixer stream.
+ */
+int get_mixer_stream_volume(int stream)
+{
+   return mixer_stream[stream].vol;
+}
+END_OF_FUNCTION(get_mixer_stream_volume);
+
+/* get_mixer_stream_pan:
+ *  Returns the current panning of the mixer stream.
+ */
+int get_mixer_stream_pan(int stream, int pan)
+{
+   return mixer_stream[stream].pan;
+}
+END_OF_FUNCTION(get_mixer_stream_pan);
+
+/* set_mixer_stream_freq:
+ *  Changes the playback frequency of the mixer_stream.
+ */
+void set_mixer_stream_freq(int stream, int freq)
+{
+   ASSERT(freq > 0);
+
+   mixer_stream[stream].freq = freq;
+   freq = freq * MIX_FIX_SCALE / mix_freq;
+
+   mixer_stream[stream].step = freq;
+}
+END_OF_FUNCTION(set_mixer_stream_freq);
+
+/* set_mixer_stream_volume:
+ *  Changes the playback volume of the mixer_stream.
+ */
+void set_mixer_stream_volume(int stream, int vol)
+{
+   vol = clamp_val(vol, 0, 255);
+   mixer_stream[stream].vol = vol;
+
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->lock_mutex(mixer_mutex);
+#endif
+   mixer_stream[stream].lvol = vol * (255-mixer_stream[stream].pan);
+   mixer_stream[stream].rvol = vol * mixer_stream[stream].pan;
+   mixer_stream[stream].lvol += mixer_stream[stream].lvol >> 7;
+   mixer_stream[stream].rvol += mixer_stream[stream].rvol >> 7;
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->unlock_mutex(mixer_mutex);
+#endif
+}
+END_OF_FUNCTION(set_mixer_stream_volume);
+
+/* set_mixer_stream_pan:
+ *  Changes the panning of the mixer_stream.
+ */
+void set_mixer_stream_pan(int stream, int pan)
+{
+   pan = clamp_val(pan, 0, 255);
+   mixer_stream[stream].pan = pan;
+
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->lock_mutex(mixer_mutex);
+#endif
+   mixer_stream[stream].lvol = mixer_stream[stream].vol * (255-pan);
+   mixer_stream[stream].rvol = mixer_stream[stream].vol * pan;
+   mixer_stream[stream].lvol += mixer_stream[stream].lvol >> 7;
+   mixer_stream[stream].rvol += mixer_stream[stream].rvol >> 7;
+#ifdef ALLEGRO_MULTITHREADED
+   system_driver->unlock_mutex(mixer_mutex);
+#endif
+}
+END_OF_FUNCTION(set_mixer_stream_pan);
+
+/* mixer_stream_is_ready:
+ *  Reports if the mixer stream is ready to accept more audio data.
+ */
+int mixer_stream_is_ready(int stream)
+{
+   return (mixer_stream[stream].status > AUTOKILL &&
+	   mixer_stream[stream].stored.buffer == NULL);
+}
+
+/* set_mixer_stream_buffer:
+ *  Sets the active mixer stream buffer and buffer length, storing it if
+ *  there's already a buffer playing, or returning FALSE if there's already a
+ *  buffer stored. The length specified is the number of samples per channel
+ *  to play in the buffer. Important: Do NOT change the buffer data once it
+ *  has been commited to the stream. You must wait until the buffer stops
+ *  playing (by checking get_mixer_stream_position) or is cleared out (by
+ *  checking mixer_stream_is_ready after supplying a different buffer).
+ */
+int set_mixer_stream_buffer(int stream, void *buf, int len)
+{
+   ASSERT(buf);
+   ASSERT(len > 0);
+
+   if(mixer_stream[stream].status <= AUTOKILL)
+      return FALSE;
+
+   mixer_stream[stream].status = 0;
+
+   if(!mixer_stream[stream].data.buffer) {
+      mixer_stream[stream].data.buffer = buf;
+      mixer_stream[stream].end_pos = len * MIX_FIX_SCALE;
+      mixer_stream[stream].pos = 0;
+
+      return TRUE;
+   }
+
+   if(mixer_stream[stream].stored.buffer)
+      return FALSE;
+
+   mixer_stream[stream].stored.buffer_len = len * MIX_FIX_SCALE;
+   mixer_stream[stream].stored.buffer = buf;
+
+   return TRUE;
+}
+END_OF_FUNCTION(set_mixer_stream_buffer);
+
 
 /* set_volume_per_voice:
  *  Enables the programmer (not the end-user) to alter the maximum volume of
@@ -114,41 +505,43 @@
  *  - pass 1 if you want to pan a full-volume sample to one side without
  *    distortion,
  *  - each time the scale parameter increases by 1, the volume halves.
- *
- *  This must be called _before_ install_sound().
  */
+static void update_mixer_volume(MIXER_VOICE *mv, PHYS_VOICE *pv);
 void set_volume_per_voice(int scale)
 {
-   voice_volume_scale = scale;
-}
-
-
-/* create_volume_table:
- *  Builds a volume table for the high quality 16 bit mixing mode.
- */
-static int create_volume_table(int vol_scale)
-{
-   double step;
-   double acum = 0;
    int i;
 
-   if (!volume_table) {
-      volume_table = (VOLUME_T *)malloc(SIZE_VOLUME_TABLE);
-      if (!volume_table)
-	 return 1;
-      LOCK_DATA(volume_table, SIZE_VOLUME_TABLE);
-   }
-
-   step = (double)(32768 >> vol_scale) / ENTRIES_VOL_TABLE;
+   if(scale < 0) {
+      /* Work out the # of voices and the needed scale */
+      scale = 1;
+      for(i = 1;i < mix_voices;i <<= 1)
+	 scale++;
+
+      /* Backwards compatiblity with 3.12 */
+      if(scale < 2)
+	 scale = 2;
+   }
+
+   /* Don't allow over-sampling */
+   if(scale < 1)
+      scale = 1;
 
-   for (i=0; i<ENTRIES_VOL_TABLE; i++, acum+=step)
-      volume_table[i] = acum;
+   /* Update the mixer voices' volumes */
+#ifdef ALLEGRO_MULTITHREADED
+   if(mixer_mutex)
+      system_driver->lock_mutex(mixer_mutex);
+#endif
+   voice_volume_scale = scale-1;
 
-   return 0;
+   for(i = 0;i < MIXER_MAX_SFX;++i)
+      update_mixer_volume(mixer_voice+i, _phys_voice+i);
+#ifdef ALLEGRO_MULTITHREADED
+   if(mixer_mutex)
+      system_driver->unlock_mutex(mixer_mutex);
+#endif
 }
 
 
-
 /* _mixer_init:
  *  Initialises the sample mixing code, returning 0 on success. You should
  *  pass it the number of samples you want it to mix each time the refill
@@ -162,112 +555,62 @@
 int _mixer_init(int bufsize, int freq, int stereo, int is16bit, int *voices)
 {
    int i, j;
-   int clip_size;
-   int clip_scale;
-   int clip_max;
-   int mix_vol_scale;
 
-   mix_voices = 1;
-   mix_vol_scale = -1;
-
-   while ((mix_voices < MIXER_MAX_SFX) && (mix_voices < *voices)) {
-      mix_voices <<= 1;
-      mix_vol_scale++;
-   }
-
-   if (voice_volume_scale >= 0)
-      mix_vol_scale = voice_volume_scale;
-   else {
-      /* backward compatibility with 3.12 version */
-      if (mix_vol_scale < 2)
-         mix_vol_scale = 2;
-   }
-
-   *voices = mix_voices;
+   mix_voices = *voices;
+   if(mix_voices > MIXER_MAX_SFX)
+      *voices = mix_voices = MIXER_MAX_SFX;
 
    mix_size = bufsize;
    mix_freq = freq;
-   mix_stereo = stereo;
-   mix_16bit = is16bit;
+   mix_channels = (stereo ? 2 : 1);
+   mix_bits = (is16bit ? 16 : 8);
 
    for (i=0; i<MIXER_MAX_SFX; i++) {
       mixer_voice[i].playing = FALSE;
-      mixer_voice[i].data8 = NULL;
-      mixer_voice[i].data16 = NULL;
+      mixer_voice[i].data.buffer = NULL;
    }
 
    /* temporary buffer for sample mixing */
-   mix_buffer = malloc(mix_size*sizeof(short));
+   mix_buffer = malloc(mix_size * sizeof(*mix_buffer));
    if (!mix_buffer)
       return -1;
 
-   LOCK_DATA(mix_buffer, mix_size*sizeof(short));
-
-   /* volume table for mixing samples into the temporary buffer */
-   mix_vol_table = malloc(sizeof(MIXER_VOL_TABLE) * MIX_VOLUME_LEVELS);
-   if (!mix_vol_table) {
-      free(mix_buffer);
-      mix_buffer = NULL;
-      return -1;
-   }
+   LOCK_DATA(mix_buffer, mix_size * sizeof(*mix_buffer));
 
-   LOCK_DATA(mix_vol_table, sizeof(MIXER_VOL_TABLE) * MIX_VOLUME_LEVELS);
-
-   for (j=0; j<MIX_VOLUME_LEVELS; j++)
-      for (i=0; i<256; i++)
-         mix_vol_table[j][i] = ((i-128) * j * 128 / MIX_VOLUME_LEVELS) >> mix_vol_scale;
-
-   if ((_sound_hq) && (mix_stereo) && (mix_16bit)) {
-      /* make high quality table if requested and output is 16 bit stereo */
-      if (create_volume_table(mix_vol_scale) != 0)
-	 return -1;
-   }
-   else
+   /* 16 bit output isn't required for the high quality mixers */
+   if ((!_sound_hq) || (mix_channels == 1)) {
+      /* no high quality mixer available */
       _sound_hq = 0;
 
-   /* lookup table for amplifying and clipping sample buffers */
-   if (mix_16bit) {
-      clip_size = 1 << MIX_RES_16;
-      clip_scale = 18 - MIX_RES_16;
-      clip_max = 0xFFFF;
-   }
-   else {
-      clip_size = 1 << MIX_RES_8;
-      clip_scale = 10 - MIX_RES_8;
-      clip_max = 0xFF;
-   }
-
-   /* We now always use a clip table, owing to the new set_volume_per_voice()
-    * functionality. It is not a big loss in performance.
-    */
-   mix_clip_table = malloc(sizeof(short) * clip_size);
-   if (!mix_clip_table) {
-      free(mix_buffer);
-      mix_buffer = NULL;
-      free(mix_vol_table);
-      mix_vol_table = NULL;
-      free(volume_table);
-      volume_table = NULL;
-      return -1;
-   }
+      /* volume table for mixing samples into the temporary buffer */
+      mix_vol_table = malloc(sizeof(MIXER_VOL_TABLE) * MIX_VOLUME_LEVELS);
+      if (!mix_vol_table) {
+	 free(mix_buffer);
+	 mix_buffer = NULL;
+	 return -1;
+      }
 
-   LOCK_DATA(mix_clip_table, sizeof(short) * clip_size);
+      LOCK_DATA(mix_vol_table, sizeof(MIXER_VOL_TABLE) * MIX_VOLUME_LEVELS);
 
-   /* clip extremes of the sample range */
-   for (i=0; i<clip_size*3/8; i++) {
-      mix_clip_table[i] = 0;
-      mix_clip_table[clip_size-1-i] = clip_max;
+      for (j=0; j<MIX_VOLUME_LEVELS; j++)
+	 for (i=0; i<256; i++)
+	    mix_vol_table[j][i] = ((i-128) * 256 * j / MIX_VOLUME_LEVELS) << 8;
    }
-
-   for (i=0; i<clip_size/4; i++)
-      mix_clip_table[clip_size*3/8 + i] = i<<clip_scale;
+   /* We no longer need to use prebuilt stuff for the high quality mixers */
 
    mixer_lock_mem();
 
 #ifdef ALLEGRO_MULTITHREADED
+   /* Woops. Forgot to clean up incase this fails. :) */
    mixer_mutex = system_driver->create_mutex();
-   if (!mixer_mutex)
+   if (!mixer_mutex) {
+      if(mix_vol_table)
+	 free(mix_vol_table);
+      mix_vol_table = NULL;
+      free(mix_buffer);
+      mix_buffer = NULL;
       return -1;
+   }
 #endif
 
    return 0;
@@ -285,29 +628,26 @@
    mixer_mutex = NULL;
 #endif
 
-   if (mix_buffer) {
+   if (mix_buffer)
       free(mix_buffer);
-      mix_buffer = NULL;
-   }
+   mix_buffer = NULL;
 
-   if (mix_vol_table) {
+   if (mix_vol_table)
       free(mix_vol_table);
-      mix_vol_table = NULL;
-   }
+   mix_vol_table = NULL;
 
-   if (mix_clip_table) {
-      free(mix_clip_table);
-      mix_clip_table = NULL;
-   }
-
-   if (volume_table) {
-      free(volume_table);
-      volume_table = NULL;
-   }
+   UNLOCK_DATA(mixer_stream, mixer_stream_count * sizeof(MIXER_STREAM));
+   if(mixer_stream)
+      free(mixer_stream);
+   mixer_stream = NULL;
+
+   mix_size = 0;
+   mix_freq = 0;
+   mix_channels = 0;
+   mix_bits = 0;
 }
 
 
-
 /* update_mixer_volume:
  *  Called whenever the voice volume or pan changes, to update the mixer 
  *  amplification table indexes.
@@ -317,37 +657,41 @@
    int vol, pan, lvol, rvol;
 
    if (_sound_hq) {
-      vol = pv->vol>>13;
-      pan = pv->pan>>13;
+      /* now use full 16 bit volume ranges */
+      vol = pv->vol>>12;
+      pan = pv->pan>>12;
 
-      /* no need to check for mix_stereo if we're using hq */
-      lvol = vol*(127-pan);
+      /* no need to check for mix_channels if we're using hq */
+      lvol = vol*(255-pan);
       rvol = vol*pan;
 
-      /* adjust for 127*127<128*128-1 */
-      lvol += lvol>>6;
-      rvol += rvol>>6;
-
-      mv->lvol = MID(0, lvol, ENTRIES_VOL_TABLE-1);
-      mv->rvol = MID(0, rvol, ENTRIES_VOL_TABLE-1);
+      /* Adjust for 255*255 < 256*256-1 */
+      lvol += lvol >> 7;
+      rvol += rvol >> 7;
+
+      /* Multiply 2 to emulate the old behavior of voice_scale=0 being
+       * 1 step louder than regular volume
+       */
+      mv->lvol = clamp_val(lvol, 0, 65535) >> voice_volume_scale;
+      mv->rvol = clamp_val(rvol, 0, 65535) >> voice_volume_scale;
    }
    else {
       vol = pv->vol >> 12;
       pan = pv->pan >> 12;
 
-      if (mix_stereo) {
-	 lvol = vol * (256-pan) * MIX_VOLUME_LEVELS / 65536;
+      if (mix_channels == 2) {
+	 lvol = vol * (255-pan) * MIX_VOLUME_LEVELS / 65536;
 	 rvol = vol * pan * MIX_VOLUME_LEVELS / 65536;
       }
-      else if (mv->stereo) {
-	 lvol = vol * (256-pan) * MIX_VOLUME_LEVELS / 131072;
+      else if (mv->channels != 1) {
+	 lvol = vol * (255-pan) * MIX_VOLUME_LEVELS / 131072;
 	 rvol = vol * pan * MIX_VOLUME_LEVELS / 131072;
       }
       else
 	 lvol = rvol = vol * MIX_VOLUME_LEVELS / 512;
 
-      mv->lvol = MID(0, lvol, MIX_VOLUME_LEVELS-1);
-      mv->rvol = MID(0, rvol, MIX_VOLUME_LEVELS-1);
+      mv->rvol = clamp_val(lvol, 0, MIX_VOLUME_LEVELS-1) >> voice_volume_scale;
+      mv->rvol = clamp_val(rvol, 0, MIX_VOLUME_LEVELS-1) >> voice_volume_scale;
    }
 }
 
@@ -440,7 +784,7 @@
       voice->freq += voice->dfreq * len;
       if (((voice->dfreq > 0) && (voice->freq >= voice->target_freq)) ||
 	  ((voice->dfreq < 0) && (voice->freq <= voice->target_freq))) {
-         voice->freq = voice->target_freq;
+	 voice->freq = voice->target_freq;
 	 voice->dfreq = 0;
       }
 
@@ -453,12 +797,12 @@
 
 
 /* helper for constructing the body of a sample mixing routine */
-#define MIXER()                                                              \
-{                                                                            \
-   if ((voice->playmode & PLAYMODE_LOOP) &&                                  \
-       (spl->loop_start < spl->loop_end)) {                                  \
+#define MIXER()                                                             \
+{									    \
+   if ((voice->playmode & PLAYMODE_LOOP) &&                                 \
+       (spl->loop_start < spl->loop_end)) {                                 \
 									     \
-      if (voice->playmode & PLAYMODE_BACKWARD) {                             \
+      if (voice->playmode & PLAYMODE_BACKWARD) {                            \
 	 /* mix a backward looping sample */                                 \
 	 while (len-- > 0) {                                                 \
 	    MIX();                                                           \
@@ -466,9 +810,9 @@
 	    if (spl->pos < spl->loop_start) {                                \
 	       if (voice->playmode & PLAYMODE_BIDIR) {                       \
 		  spl->diff = -spl->diff;                                    \
-                  /* however far the sample has overshot, move it the same */\
-                  /* distance from the loop point, within the loop section */\
-                  spl->pos = (spl->loop_start << 1) - spl->pos;              \
+		  /* however far the sample has overshot, move it the same */\
+		  /* distance from the loop point, within the loop section */\
+		  spl->pos = (spl->loop_start << 1) - spl->pos;              \
 		  voice->playmode ^= PLAYMODE_BACKWARD;                      \
 	       }                                                             \
 	       else                                                          \
@@ -476,8 +820,8 @@
 	    }                                                                \
 	    update_mixer(spl, voice, len);                                   \
 	 }                                                                   \
-      }                                                                      \
-      else {                                                                 \
+      }                                                                     \
+      else {                                                                \
 	 /* mix a forward looping sample */                                  \
 	 while (len-- > 0) {                                                 \
 	    MIX();                                                           \
@@ -485,9 +829,9 @@
 	    if (spl->pos >= spl->loop_end) {                                 \
 	       if (voice->playmode & PLAYMODE_BIDIR) {                       \
 		  spl->diff = -spl->diff;                                    \
-                  /* however far the sample has overshot, move it the same */\
-                  /* distance from the loop point, within the loop section */\
-                  spl->pos = ((spl->loop_end - 1) << 1) - spl->pos;          \
+		  /* however far the sample has overshot, move it the same */\
+		  /* distance from the loop point, within the loop section */\
+		  spl->pos = ((spl->loop_end - 1) << 1) - spl->pos;          \
 		  voice->playmode ^= PLAYMODE_BACKWARD;                      \
 	       }                                                             \
 	       else                                                          \
@@ -495,11 +839,11 @@
 	    }                                                                \
 	    update_mixer(spl, voice, len);                                   \
 	 }                                                                   \
-      }                                                                      \
-   }                                                                         \
-   else {                                                                    \
-      /* mix a non-looping sample */                                         \
-      while (len-- > 0) {                                                    \
+      }                                                                     \
+   }                                                                        \
+   else {                                                                   \
+      /* mix a non-looping sample */                                        \
+      while (len-- > 0) {                                                   \
 	 MIX();                                                              \
 	 spl->pos += spl->diff;                                              \
 	 if ((unsigned long)spl->pos >= (unsigned long)spl->len) {           \
@@ -509,8 +853,8 @@
 	    return;                                                          \
 	 }                                                                   \
 	 update_mixer(spl, voice, len);                                      \
-      }                                                                      \
-   }                                                                         \
+      }                                                                     \
+   }                                                                        \
 }
 
 
@@ -530,54 +874,55 @@
  */
 static void mix_silent_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, int len)
 {
+   len >>= 1;
    if ((voice->playmode & PLAYMODE_LOOP) &&
        (spl->loop_start < spl->loop_end)) {
 
       if (voice->playmode & PLAYMODE_BACKWARD) {
 	 /* mix a backward looping sample */
-         spl->pos += spl->diff * len;
-         if (spl->pos < spl->loop_start) {
-            if (voice->playmode & PLAYMODE_BIDIR) {
-               do {
-                  spl->diff = -spl->diff;
-                  spl->pos = (spl->loop_start << 1) - spl->pos;
-                  voice->playmode ^= PLAYMODE_BACKWARD;
-                  if (spl->pos < spl->loop_end) break;
-                  spl->diff = -spl->diff;
-                  spl->pos = ((spl->loop_end - 1) << 1) - spl->pos;
-                  voice->playmode ^= PLAYMODE_BACKWARD;
-               } while (spl->pos < spl->loop_start);
-            }
-            else {
-               do {
-                  spl->pos += (spl->loop_end - spl->loop_start);
-               } while (spl->pos < spl->loop_start);
-            }
-         }
-         update_silent_mixer(spl, voice, len);
+	 spl->pos += spl->diff * len;
+	 if (spl->pos < spl->loop_start) {
+	    if (voice->playmode & PLAYMODE_BIDIR) {
+	       do {
+		  spl->diff = -spl->diff;
+		  spl->pos = (spl->loop_start << 1) - spl->pos;
+		  voice->playmode ^= PLAYMODE_BACKWARD;
+		  if (spl->pos < spl->loop_end) break;
+		  spl->diff = -spl->diff;
+		  spl->pos = ((spl->loop_end - 1) << 1) - spl->pos;
+		  voice->playmode ^= PLAYMODE_BACKWARD;
+	       } while (spl->pos < spl->loop_start);
+	    }
+	    else {
+	       do {
+		  spl->pos += (spl->loop_end - spl->loop_start);
+	       } while (spl->pos < spl->loop_start);
+	    }
+	 }
+	 update_silent_mixer(spl, voice, len);
       }
       else {
 	 /* mix a forward looping sample */
-         spl->pos += spl->diff * len;
-         if (spl->pos >= spl->loop_end) {
-            if (voice->playmode & PLAYMODE_BIDIR) {
-               do {
-                  spl->diff = -spl->diff;
-                  spl->pos = ((spl->loop_end - 1) << 1) - spl->pos;
-                  voice->playmode ^= PLAYMODE_BACKWARD;
-                  if (spl->pos >= spl->loop_start) break;
-                  spl->diff = -spl->diff;
-                  spl->pos = (spl->loop_start << 1) - spl->pos;
-                  voice->playmode ^= PLAYMODE_BACKWARD;
-               } while (spl->pos >= spl->loop_end);
-            }
-            else {
-               do {
-                  spl->pos -= (spl->loop_end - spl->loop_start);
-               } while (spl->pos >= spl->loop_end);
-            }
+	 spl->pos += spl->diff * len;
+	 if (spl->pos >= spl->loop_end) {
+	    if (voice->playmode & PLAYMODE_BIDIR) {
+	       do {
+		  spl->diff = -spl->diff;
+		  spl->pos = ((spl->loop_end - 1) << 1) - spl->pos;
+		  voice->playmode ^= PLAYMODE_BACKWARD;
+		  if (spl->pos >= spl->loop_start) break;
+		  spl->diff = -spl->diff;
+		  spl->pos = (spl->loop_start << 1) - spl->pos;
+		  voice->playmode ^= PLAYMODE_BACKWARD;
+	       } while (spl->pos >= spl->loop_end);
+	    }
+	    else {
+	       do {
+		  spl->pos -= (spl->loop_end - spl->loop_start);
+	       } while (spl->pos >= spl->loop_end);
+	    }
 	 }
-         update_silent_mixer(spl, voice, len);
+	 update_silent_mixer(spl, voice, len);
       }
    }
    else {
@@ -601,12 +946,12 @@
  *  Mixes from an eight bit sample into a mono buffer, until either len 
  *  samples have been mixed or until the end of the sample is reached.
  */
-static void mix_mono_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_mono_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *vol = (short *)(mix_vol_table + spl->lvol);
+   signed int *vol = (int *)(mix_vol_table + spl->lvol);
 
-   #define MIX()                                                             \
-      *(buf++) += vol[spl->data8[spl->pos>>MIX_FIX_SHIFT]];
+   #define MIX()							     \
+      *(buf++) += vol[spl->data.u8[spl->pos>>MIX_FIX_SHIFT]];
 
    MIXER();
 
@@ -621,14 +966,14 @@
  *  Mixes from an eight bit stereo sample into a mono buffer, until either 
  *  len samples have been mixed or until the end of the sample is reached.
  */
-static void mix_mono_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_mono_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *lvol = (short *)(mix_vol_table + spl->lvol);
-   signed short *rvol = (short *)(mix_vol_table + spl->rvol);
+   signed int *lvol = (int *)(mix_vol_table + spl->lvol);
+   signed int *rvol = (int *)(mix_vol_table + spl->rvol);
 
-   #define MIX()                                                             \
-      *(buf)   += lvol[spl->data8[(spl->pos>>MIX_FIX_SHIFT)*2]];             \
-      *(buf++) += rvol[spl->data8[(spl->pos>>MIX_FIX_SHIFT)*2+1]];
+   #define MIX()							     \
+      *(buf)   += lvol[spl->data.u8[(spl->pos>>MIX_FIX_SHIFT)*2  ]];	 \
+      *(buf++) += rvol[spl->data.u8[(spl->pos>>MIX_FIX_SHIFT)*2+1]];
 
    MIXER();
 
@@ -643,12 +988,12 @@
  *  Mixes from a 16 bit sample into a mono buffer, until either len samples 
  *  have been mixed or until the end of the sample is reached.
  */
-static void mix_mono_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_mono_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *vol = (short *)(mix_vol_table + spl->lvol);
+   signed int *vol = (int *)(mix_vol_table + spl->lvol);
 
-   #define MIX()                                                             \
-      *(buf++) += vol[(spl->data16[spl->pos>>MIX_FIX_SHIFT])>>8];
+   #define MIX()							     \
+      *(buf++) += vol[(spl->data.u16[spl->pos>>MIX_FIX_SHIFT])>>8];
 
    MIXER();
 
@@ -663,14 +1008,14 @@
  *  Mixes from a 16 bit stereo sample into a mono buffer, until either len 
  *  samples have been mixed or until the end of the sample is reached.
  */
-static void mix_mono_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_mono_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *lvol = (short *)(mix_vol_table + spl->lvol);
-   signed short *rvol = (short *)(mix_vol_table + spl->rvol);
+   signed int *lvol = (int *)(mix_vol_table + spl->lvol);
+   signed int *rvol = (int *)(mix_vol_table + spl->rvol);
 
-   #define MIX()                                                             \
-      *(buf)   += lvol[(spl->data16[(spl->pos>>MIX_FIX_SHIFT)*2])>>8];       \
-      *(buf++) += rvol[(spl->data16[(spl->pos>>MIX_FIX_SHIFT)*2+1])>>8];
+   #define MIX()							     \
+      *(buf)   += lvol[(spl->data.u16[(spl->pos>>MIX_FIX_SHIFT)*2  ])>>8];   \
+      *(buf++) += rvol[(spl->data.u16[(spl->pos>>MIX_FIX_SHIFT)*2+1])>>8];
 
    MIXER();
 
@@ -685,16 +1030,16 @@
  *  Mixes from an eight bit sample into a stereo buffer, until either len 
  *  samples have been mixed or until the end of the sample is reached.
  */
-static void mix_stereo_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_stereo_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *lvol = (short *)(mix_vol_table + spl->lvol);
-   signed short *rvol = (short *)(mix_vol_table + spl->rvol);
+   signed int *lvol = (int *)(mix_vol_table + spl->lvol);
+   signed int *rvol = (int *)(mix_vol_table + spl->rvol);
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      *(buf++) += lvol[spl->data8[spl->pos>>MIX_FIX_SHIFT]];                 \
-      *(buf++) += rvol[spl->data8[spl->pos>>MIX_FIX_SHIFT]];
+   #define MIX()							     \
+      *(buf++) += lvol[spl->data.u8[spl->pos>>MIX_FIX_SHIFT]];	       \
+      *(buf++) += rvol[spl->data.u8[spl->pos>>MIX_FIX_SHIFT]];
 
    MIXER();
 
@@ -709,16 +1054,16 @@
  *  Mixes from an eight bit stereo sample into a stereo buffer, until either 
  *  len samples have been mixed or until the end of the sample is reached.
  */
-static void mix_stereo_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_stereo_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *lvol = (short *)(mix_vol_table + spl->lvol);
-   signed short *rvol = (short *)(mix_vol_table + spl->rvol);
+   signed int *lvol = (int *)(mix_vol_table + spl->lvol);
+   signed int *rvol = (int *)(mix_vol_table + spl->rvol);
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      *(buf++) += lvol[spl->data8[(spl->pos>>MIX_FIX_SHIFT)*2]];             \
-      *(buf++) += rvol[spl->data8[(spl->pos>>MIX_FIX_SHIFT)*2+1]];
+   #define MIX()							     \
+      *(buf++) += lvol[spl->data.u8[(spl->pos>>MIX_FIX_SHIFT)*2  ]];	 \
+      *(buf++) += rvol[spl->data.u8[(spl->pos>>MIX_FIX_SHIFT)*2+1]];
 
    MIXER();
 
@@ -733,16 +1078,16 @@
  *  Mixes from a 16 bit sample into a stereo buffer, until either len samples 
  *  have been mixed or until the end of the sample is reached.
  */
-static void mix_stereo_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_stereo_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *lvol = (short *)(mix_vol_table + spl->lvol);
-   signed short *rvol = (short *)(mix_vol_table + spl->rvol);
+   signed int *lvol = (int *)(mix_vol_table + spl->lvol);
+   signed int *rvol = (int *)(mix_vol_table + spl->rvol);
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      *(buf++) += lvol[(spl->data16[spl->pos>>MIX_FIX_SHIFT])>>8];           \
-      *(buf++) += rvol[(spl->data16[spl->pos>>MIX_FIX_SHIFT])>>8];
+   #define MIX()							     \
+      *(buf++) += lvol[(spl->data.u16[spl->pos>>MIX_FIX_SHIFT])>>8];	 \
+      *(buf++) += rvol[(spl->data.u16[spl->pos>>MIX_FIX_SHIFT])>>8];
 
    MIXER();
 
@@ -757,16 +1102,16 @@
  *  Mixes from a 16 bit stereo sample into a stereo buffer, until either len 
  *  samples have been mixed or until the end of the sample is reached.
  */
-static void mix_stereo_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_stereo_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   signed short *lvol = (short *)(mix_vol_table + spl->lvol);
-   signed short *rvol = (short *)(mix_vol_table + spl->rvol);
+   signed int *lvol = (int *)(mix_vol_table + spl->lvol);
+   signed int *rvol = (int *)(mix_vol_table + spl->rvol);
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      *(buf++) += lvol[(spl->data16[(spl->pos>>MIX_FIX_SHIFT)*2])>>8];       \
-      *(buf++) += rvol[(spl->data16[(spl->pos>>MIX_FIX_SHIFT)*2+1])>>8];
+   #define MIX()							     \
+      *(buf++) += lvol[(spl->data.u16[(spl->pos>>MIX_FIX_SHIFT)*2  ])>>8];   \
+      *(buf++) += rvol[(spl->data.u16[(spl->pos>>MIX_FIX_SHIFT)*2+1])>>8];
 
    MIXER();
 
@@ -775,23 +1120,21 @@
 
 END_OF_STATIC_FUNCTION(mix_stereo_16x2_samples);
 
-
-
 /* mix_hq1_8x1_samples:
  *  Mixes from a mono 8 bit sample into a high quality stereo buffer, 
  *  until either len samples have been mixed or until the end of the 
  *  sample is reached.
  */
-static void mix_hq1_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq1_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      *(buf++) += ((spl->data8[spl->pos>>MIX_FIX_SHIFT]-0x80)*lvol)>>8;      \
-      *(buf++) += ((spl->data8[spl->pos>>MIX_FIX_SHIFT]-0x80)*rvol)>>8;
+   #define MIX()							     \
+      *(buf++) += ((spl->data.u8[spl->pos>>MIX_FIX_SHIFT]-0x80)*lvol);       \
+      *(buf++) += ((spl->data.u8[spl->pos>>MIX_FIX_SHIFT]-0x80)*rvol);
 
    MIXER();
 
@@ -807,16 +1150,16 @@
  *  until either len samples have been mixed or until the end of the 
  *  sample is reached.
  */
-static void mix_hq1_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq1_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      *(buf++) += ((spl->data8[(spl->pos>>MIX_FIX_SHIFT)*2]-0x80)*lvol)>>8;  \
-      *(buf++) += ((spl->data8[(spl->pos>>MIX_FIX_SHIFT)*2+1]-0x80)*rvol)>>8;
+   #define MIX()							     \
+      *(buf++) += ((spl->data.u8[(spl->pos>>MIX_FIX_SHIFT)*2  ]-0x80)*lvol); \
+      *(buf++) += ((spl->data.u8[(spl->pos>>MIX_FIX_SHIFT)*2+1]-0x80)*rvol);
 
    MIXER();
 
@@ -832,16 +1175,16 @@
  *  until either len samples have been mixed or until the end of the sample 
  *  is reached.
  */
-static void mix_hq1_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq1_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      *(buf++) += ((spl->data16[spl->pos>>MIX_FIX_SHIFT]-0x8000)*lvol)>>16;  \
-      *(buf++) += ((spl->data16[spl->pos>>MIX_FIX_SHIFT]-0x8000)*rvol)>>16;
+   #define MIX()							     \
+      *(buf++) += ((spl->data.u16[spl->pos>>MIX_FIX_SHIFT]-0x8000)*lvol)>>8; \
+      *(buf++) += ((spl->data.u16[spl->pos>>MIX_FIX_SHIFT]-0x8000)*rvol)>>8;
 
    MIXER();
 
@@ -857,16 +1200,16 @@
  *  until either len samples have been mixed or until the end of the sample 
  *  is reached.
  */
-static void mix_hq1_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq1_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
 
    len >>= 1;
 
-   #define MIX()                                                                 \
-      *(buf++) += ((spl->data16[(spl->pos>>MIX_FIX_SHIFT)*2]-0x8000)*lvol)>>16;  \
-      *(buf++) += ((spl->data16[(spl->pos>>MIX_FIX_SHIFT)*2+1]-0x8000)*rvol)>>16;
+   #define MIX()								 \
+      *(buf++) += ((spl->data.u16[(spl->pos>>MIX_FIX_SHIFT)*2  ]-0x8000)*lvol)>>8;\
+      *(buf++) += ((spl->data.u16[(spl->pos>>MIX_FIX_SHIFT)*2+1]-0x8000)*rvol)>>8;
 
    MIXER();
 
@@ -876,41 +1219,43 @@
 END_OF_STATIC_FUNCTION(mix_hq1_16x2_samples);
 
 
+/* Helper to apply a 16-bit volume to a 24-bit sample */
+#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32))
 
 /* mix_hq2_8x1_samples:
  *  Mixes from a mono 8 bit sample into an interpolated stereo buffer, 
  *  until either len samples have been mixed or until the end of the 
  *  sample is reached.
  */
-static void mix_hq2_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq2_8x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
    int v, v1, v2;
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      v = spl->pos>>MIX_FIX_SHIFT;                                           \
+   #define MIX()							     \
+      v = spl->pos>>MIX_FIX_SHIFT;					   \
+									     \
+      v1 = spl->data.u8[v] << 16;					    \
 									     \
-      v1 = spl->data8[v];                                                    \
-                                                                             \
-      if (spl->pos >= spl->len-MIX_FIX_SCALE) {                              \
-         if ((voice->playmode & (PLAYMODE_LOOP |                             \
-                                 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&        \
-             spl->loop_start < spl->loop_end && spl->loop_end == spl->len)   \
-            v2 = spl->data8[spl->loop_start>>MIX_FIX_SHIFT];                 \
-         else                                                                \
-            v2 = 0x80;                                                       \
-      }                                                                      \
-      else                                                                   \
-	 v2 = spl->data8[v+1];                                               \
+      if (spl->pos >= spl->len-MIX_FIX_SCALE) {			      \
+	 if ((voice->playmode & (PLAYMODE_LOOP |			     \
+				 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&	\
+	     spl->loop_start < spl->loop_end && spl->loop_end == spl->len)   \
+	    v2 = spl->data.u8[spl->loop_start>>MIX_FIX_SHIFT] << 16;	 \
+	 else								\
+	    v2 = 0x800000;						   \
+      }								      \
+      else								   \
+	 v2 = spl->data.u8[v+1] << 16;				       \
 									     \
-      v = spl->pos & (MIX_FIX_SCALE-1);                                      \
-      v = (v1*(MIX_FIX_SCALE-v) + v2*v) / MIX_FIX_SCALE;                     \
+      v = (spl->pos & (MIX_FIX_SCALE-1)) >> 1;			       \
+      v = (((v2 - v1) * v) >> (MIX_FIX_SHIFT-1)) + v1;		       \
 									     \
-      *(buf++) += ((v-0x80)*lvol)>>8;                                        \
-      *(buf++) += ((v-0x80)*rvol)>>8;
+      *(buf++) += MULSC(v-0x800000, lvol);				   \
+      *(buf++) += MULSC(v-0x800000, rvol);
 
    MIXER();
 
@@ -926,41 +1271,41 @@
  *  until either len samples have been mixed or until the end of the 
  *  sample is reached.
  */
-static void mix_hq2_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq2_8x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
    int v, va, v1a, v2a, vb, v1b, v2b;
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      v = (spl->pos>>MIX_FIX_SHIFT) << 1; /* x2 for stereo */                \
-                                                                             \
-      v1a = spl->data8[v];                                                   \
-      v1b = spl->data8[v+1];                                                 \
+   #define MIX()							     \
+      v = (spl->pos>>MIX_FIX_SHIFT) << 1; /* x2 for stereo */		\
 									     \
-      if (spl->pos >= spl->len-MIX_FIX_SCALE) {                              \
-         if ((voice->playmode & (PLAYMODE_LOOP |                             \
-                                 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&        \
-             spl->loop_start < spl->loop_end && spl->loop_end == spl->len) { \
-            v2a = spl->data8[((spl->loop_start>>MIX_FIX_SHIFT)<<1)];         \
-            v2b = spl->data8[((spl->loop_start>>MIX_FIX_SHIFT)<<1)+1];       \
-         }                                                                   \
-         else                                                                \
-            v2a = v2b = 0x80;                                                \
-      }                                                                      \
-      else {                                                                 \
-	 v2a = spl->data8[v+2];                                              \
-	 v2b = spl->data8[v+3];                                              \
-      }                                                                      \
+      v1a = spl->data.u8[v] << 16;					   \
+      v1b = spl->data.u8[v+1] << 16;					 \
 									     \
-      v = spl->pos & (MIX_FIX_SCALE-1);                                      \
-      va = (v1a*(MIX_FIX_SCALE-v) + v2a*v) / MIX_FIX_SCALE;                  \
-      vb = (v1b*(MIX_FIX_SCALE-v) + v2b*v) / MIX_FIX_SCALE;                  \
+      if (spl->pos >= spl->len-MIX_FIX_SCALE) {			      \
+	 if ((voice->playmode & (PLAYMODE_LOOP |			     \
+				 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&	\
+	     spl->loop_start < spl->loop_end && spl->loop_end == spl->len) { \
+	    v2a = spl->data.u8[((spl->loop_start>>MIX_FIX_SHIFT)<<1)] << 16; \
+	    v2b = spl->data.u8[((spl->loop_start>>MIX_FIX_SHIFT)<<1)+1] << 16;\
+	 }								   \
+	 else								\
+	    v2a = v2b = 0x800000;					    \
+      }								      \
+      else {								 \
+	 v2a = spl->data.u8[v+2] << 16;				      \
+	 v2b = spl->data.u8[v+3] << 16;				      \
+      }								      \
 									     \
-      *(buf++) += ((va-0x80)*lvol)>>8;                                       \
-      *(buf++) += ((vb-0x80)*rvol)>>8;
+      v = (spl->pos & (MIX_FIX_SCALE-1)) >> 1;			       \
+      va = (((v2a - v1a) * v) >> (MIX_FIX_SHIFT-1)) + v1a;		   \
+      vb = (((v2b - v1b) * v) >> (MIX_FIX_SHIFT-1)) + v1b;		   \
+									     \
+      *(buf++) += MULSC(va-0x800000, lvol);				  \
+      *(buf++) += MULSC(vb-0x800000, rvol);
 
    MIXER();
 
@@ -976,35 +1321,35 @@
  *  until either len samples have been mixed or until the end of the sample 
  *  is reached.
  */
-static void mix_hq2_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq2_16x1_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
    int v, v1, v2;
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      v = spl->pos>>MIX_FIX_SHIFT;                                           \
-                                                                             \
-      v1 = spl->data16[v];                                                   \
+   #define MIX()							     \
+      v = spl->pos>>MIX_FIX_SHIFT;					   \
+									     \
+      v1 = spl->data.u16[v] << 8;					    \
 									     \
-      if (spl->pos >= spl->len-MIX_FIX_SCALE) {                              \
-         if ((voice->playmode & (PLAYMODE_LOOP |                             \
-                                 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&        \
-             spl->loop_start < spl->loop_end && spl->loop_end == spl->len)   \
-            v2 = spl->data16[spl->loop_start>>MIX_FIX_SHIFT];                \
-         else                                                                \
-            v2 = 0x8000;                                                     \
-      }                                                                      \
-      else                                                                   \
-	 v2 = spl->data16[v+1];                                              \
+      if (spl->pos >= spl->len-MIX_FIX_SCALE) {			      \
+	 if ((voice->playmode & (PLAYMODE_LOOP |			     \
+				 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&	\
+	     spl->loop_start < spl->loop_end && spl->loop_end == spl->len)   \
+	    v2 = spl->data.u16[spl->loop_start>>MIX_FIX_SHIFT] << 8;	 \
+	 else								\
+	    v2 = 0x800000;						   \
+      }								      \
+      else								   \
+	 v2 = spl->data.u16[v+1] << 8;				       \
 									     \
-      v = spl->pos & (MIX_FIX_SCALE-1);                                      \
-      v = (v1*(MIX_FIX_SCALE-v) + v2*v) / MIX_FIX_SCALE;                     \
+      v = (spl->pos & (MIX_FIX_SCALE-1)) >> 1;			       \
+      v = (((v2 - v1) * v) >> (MIX_FIX_SHIFT-1)) + v1;		       \
 									     \
-      *(buf++) += ((v-0x8000)*lvol)>>16;                                     \
-      *(buf++) += ((v-0x8000)*rvol)>>16;
+      *(buf++) += MULSC(v-0x800000, lvol);				   \
+      *(buf++) += MULSC(v-0x800000, rvol);
 
    MIXER();
 
@@ -1020,41 +1365,41 @@
  *  until either len samples have been mixed or until the end of the sample 
  *  is reached.
  */
-static void mix_hq2_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, unsigned short *buf, int len)
+static void mix_hq2_16x2_samples(MIXER_VOICE *spl, PHYS_VOICE *voice, signed int *buf, int len)
 {
-   int lvol = volume_table[spl->lvol];
-   int rvol = volume_table[spl->rvol];
+   int lvol = spl->lvol;
+   int rvol = spl->rvol;
    int v, va, v1a, v2a, vb, v1b, v2b;
 
    len >>= 1;
 
-   #define MIX()                                                             \
-      v = (spl->pos>>MIX_FIX_SHIFT) << 1; /* x2 for stereo */                \
-                                                                             \
-      v1a = spl->data16[v];                                                  \
-      v1b = spl->data16[v+1];                                                \
+   #define MIX()							     \
+      v = (spl->pos>>MIX_FIX_SHIFT) << 1; /* x2 for stereo */		\
+									     \
+      v1a = spl->data.u16[v] << 8;					   \
+      v1b = spl->data.u16[v+1] << 8;					 \
 									     \
-      if (spl->pos >= spl->len-MIX_FIX_SCALE) {                              \
-         if ((voice->playmode & (PLAYMODE_LOOP |                             \
-                                 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&        \
-             spl->loop_start < spl->loop_end && spl->loop_end == spl->len) { \
-            v2a = spl->data16[((spl->loop_start>>MIX_FIX_SHIFT)<<1)];        \
-            v2b = spl->data16[((spl->loop_start>>MIX_FIX_SHIFT)<<1)+1];      \
-         }                                                                   \
-         else                                                                \
-            v2a = v2b = 0x8000;                                              \
-      }                                                                      \
-      else {                                                                 \
-	 v2a = spl->data16[v+2];                                             \
-	 v2b = spl->data16[v+3];                                             \
-      }                                                                      \
+      if (spl->pos >= spl->len-MIX_FIX_SCALE) {			      \
+	 if ((voice->playmode & (PLAYMODE_LOOP |			     \
+				 PLAYMODE_BIDIR)) == PLAYMODE_LOOP &&	\
+	     spl->loop_start < spl->loop_end && spl->loop_end == spl->len) { \
+	    v2a = spl->data.u16[((spl->loop_start>>MIX_FIX_SHIFT)<<1)] << 8; \
+	    v2b = spl->data.u16[((spl->loop_start>>MIX_FIX_SHIFT)<<1)+1] << 8;\
+	 }								   \
+	 else								\
+	    v2a = v2b = 0x800000;					    \
+      }								      \
+      else {								 \
+	 v2a = spl->data.u16[v+2] << 8;				      \
+	 v2b = spl->data.u16[v+3] << 8;				      \
+      }								      \
 									     \
-      v = spl->pos & (MIX_FIX_SCALE-1);                                      \
-      va = (v1a*(MIX_FIX_SCALE-v) + v2a*v) / MIX_FIX_SCALE;                  \
-      vb = (v1b*(MIX_FIX_SCALE-v) + v2b*v) / MIX_FIX_SCALE;                  \
+      v = (spl->pos & (MIX_FIX_SCALE-1)) >> 1;			       \
+      va = (((v2a - v1a) * v) >> (MIX_FIX_SHIFT-1)) + v1a;		   \
+      vb = (((v2b - v1b) * v) >> (MIX_FIX_SHIFT-1)) + v1b;		   \
 									     \
-      *(buf++) += ((va-0x8000)*lvol)>>16;                                    \
-      *(buf++) += ((vb-0x8000)*rvol)>>16;
+      *(buf++) += MULSC(va-0x800000, lvol);				  \
+      *(buf++) += MULSC(vb-0x800000, rvol);
 
    MIXER();
 
@@ -1064,125 +1409,370 @@
 END_OF_STATIC_FUNCTION(mix_hq2_16x2_samples);
 
 
+static INLINE void next_mixer_buffer(MIXER_STREAM *mixer)
+{
+   mixer->pos -= mixer->end_pos;
+   mixer->end_pos = mixer->stored.buffer_len;
+   mixer->data.buffer = mixer->stored.buffer;
+
+   mixer->stored.buffer = NULL;
+   mixer->stored.buffer_len = 0;
+}
+
+void mix_8bit_to_buffer(MIXER_STREAM *mixer, signed int *buf, int len)
+{
+   int v, v1, v2;
+
+   /* Mono output, never interpolate */
+   if(mix_channels == 1) {
+      if(mixer->channels == 2) {
+	 while(len--) {
+	    *(buf)   += (mixer->data.u8[(mixer->pos>>MIX_FIX_SHIFT)*2  ]-0x80)*mixer->lvol;
+	    *(buf++) += (mixer->data.u8[(mixer->pos>>MIX_FIX_SHIFT)*2+1]-0x80)*mixer->rvol;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      else {
+	 while(len--) {
+	    *(buf++) += (mixer->data.u8[mixer->pos>>MIX_FIX_SHIFT]-0x80)*mixer->lvol;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      return;
+   }
+
+   /* Stereo output */
+   len >>= 1;
+
+   /* No interpolation */
+   if(_sound_hq < 2 || !(mixer->step & (MIX_FIX_SCALE-1))) {
+      if(mixer->channels == 2) {
+	 while(len--) {
+	    *(buf++) += (mixer->data.u8[(mixer->pos>>MIX_FIX_SHIFT)*2  ]-0x80)*mixer->lvol;
+	    *(buf++) += (mixer->data.u8[(mixer->pos>>MIX_FIX_SHIFT)*2+1]-0x80)*mixer->rvol;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      else {
+	 while(len--) {
+	    *(buf++) += (mixer->data.u8[mixer->pos>>MIX_FIX_SHIFT]-0x80)*mixer->lvol;
+	    *(buf++) += (mixer->data.u8[mixer->pos>>MIX_FIX_SHIFT]-0x80)*mixer->rvol;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      return;
+   }
+
+   /* Linear interpolation */
+   if(mixer->channels == 2) {
+      while(len--) {
+	 int v = (mixer->pos>>MIX_FIX_SHIFT) << 1;
+	 int v1a = (mixer->data.u8[v] - 0x80) << 16;
+	 int v1b = (mixer->data.u8[v+1]-0x80) << 16;
+	 int va, vb, v2a, v2b;
+
+	 if(mixer->pos+MIX_FIX_SCALE >= mixer->end_pos) {
+	    if(mixer->stored.buffer) {
+	       v2a = (((unsigned char*)mixer->stored.buffer)[0]-0x80) << 16;
+	       v2b = (((unsigned char*)mixer->stored.buffer)[1]-0x80) << 16;
+	    }
+	    else
+	       v2a = v2b = 0;
+	 }
+	 else {
+	    v2a = (mixer->data.u8[v+2]-0x80) << 16;
+	    v2b = (mixer->data.u8[v+3]-0x80) << 16;
+	 }
+
+	 v = mixer->pos & (MIX_FIX_SCALE-1);
+	 va = (((v2a - v1a) * v) >> MIX_FIX_SHIFT) + v1a;
+	 vb = (((v2b - v1b) * v) >> MIX_FIX_SHIFT) + v1b;
+
+	 *(buf++) += MULSC(va, mixer->lvol);
+	 *(buf++) += MULSC(vb, mixer->rvol);
+	 if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	    next_mixer_buffer(mixer);
+	    if(!mixer->data.buffer)
+	       break;
+	 }
+      }
+   }
+   else {
+      while(len--) {
+	 int v = mixer->pos >> MIX_FIX_SHIFT;
+	 int v1 = (mixer->data.u8[v]-0x80) << 16;
+	 int v2;
+
+	 if(mixer->pos+MIX_FIX_SCALE >= mixer->end_pos) {
+	    if(mixer->stored.buffer)
+	       v2 = (((unsigned char*)mixer->stored.buffer)[0]-0x80) << 16;
+	    else
+	       v2 = 0;
+	 }
+	 else
+	    v2 = (mixer->data.u8[v+1]-0x80) << 16;
+
+	 v = mixer->pos & (MIX_FIX_SCALE-1);
+	 v = (((v2 - v1) * v) >> MIX_FIX_SHIFT) + v1;
+
+	 *(buf++) += MULSC(v, mixer->lvol);
+	 *(buf++) += MULSC(v, mixer->rvol);
+	 if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	    next_mixer_buffer(mixer);
+	    if(!mixer->data.buffer)
+	       break;
+	 }
+      }
+   }
+}
+
+void mix_16bit_to_buffer(MIXER_STREAM *mixer, signed int *buf, int len)
+{
+   if(mix_channels == 1) {
+      if(mixer->channels == 2) {
+	 while(len--) {
+	    *(buf)   += ((mixer->data.u16[(mixer->pos>>MIX_FIX_SHIFT)*2  ]-0x8000)*mixer->lvol)>>8;
+	    *(buf++) += ((mixer->data.u16[(mixer->pos>>MIX_FIX_SHIFT)*2+1]-0x8000)*mixer->rvol)>>8;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      else {
+	 while(len--) {
+	    *(buf++) += ((mixer->data.u16[mixer->pos>>MIX_FIX_SHIFT]-0x8000)*mixer->lvol)>>8;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      return;
+   }
+
+   len >>= 1;
+
+   if(_sound_hq < 2 || !(mixer->step & (MIX_FIX_SCALE-1))) {
+      if(mixer->channels == 2) {
+	 while(len--) {
+	    *(buf++) += ((mixer->data.u16[(mixer->pos>>MIX_FIX_SHIFT)*2  ]-0x8000)*mixer->lvol)>>8;
+	    *(buf++) += ((mixer->data.u16[(mixer->pos>>MIX_FIX_SHIFT)*2+1]-0x8000)*mixer->rvol)>>8;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      else {
+	 while(len--) {
+	    *(buf++) += ((mixer->data.u16[mixer->pos>>MIX_FIX_SHIFT]-0x8000)*mixer->lvol)>>8;
+	    *(buf++) += ((mixer->data.u16[mixer->pos>>MIX_FIX_SHIFT]-0x8000)*mixer->rvol)>>8;
+	    if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	       next_mixer_buffer(mixer);
+	       if(!mixer->data.buffer)
+		  break;
+	    }
+	 }
+      }
+      return;
+   }
+
+   if(mixer->channels == 2) {
+      while(len--) {
+	 int v = (mixer->pos>>MIX_FIX_SHIFT) << 1;
+	 int v1a = (mixer->data.u16[v] - 0x8000) << 8;
+	 int v1b = (mixer->data.u16[v+1]-0x8000) << 8;
+	 int va, vb, v2a, v2b;
+
+	 if(mixer->pos+MIX_FIX_SCALE >= mixer->end_pos) {
+	    if(mixer->stored.buffer) {
+	       v2a = (((unsigned short*)mixer->stored.buffer)[0]-0x8000) << 8;
+	       v2b = (((unsigned short*)mixer->stored.buffer)[1]-0x8000) << 8;
+	    }
+	    else
+	       v2a = v2b = 0;
+	 }
+	 else {
+	    v2a = (mixer->data.u16[v+2]-0x8000) << 8;
+	    v2b = (mixer->data.u16[v+3]-0x8000) << 8;
+	 }
+
+	 v = mixer->pos & (MIX_FIX_SCALE-1);
+	 va = (((v2a - v1a) * v) >> MIX_FIX_SHIFT) + v1a;
+	 vb = (((v2b - v1b) * v) >> MIX_FIX_SHIFT) + v1b;
+
+	 *(buf++) += MULSC(va, mixer->lvol);
+	 *(buf++) += MULSC(vb, mixer->rvol);
+	 if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	    next_mixer_buffer(mixer);
+	    if(!mixer->data.buffer)
+	       break;
+	 }
+      }
+   }
+   else {
+      while(len--) {
+	 int v = mixer->pos >> MIX_FIX_SHIFT;
+	 int v1 = (mixer->data.u16[v]-0x8000) << 8;
+	 int v2;
+
+	 if(mixer->pos+MIX_FIX_SCALE >= mixer->end_pos) {
+	    if(mixer->stored.buffer)
+	       v2 = (((unsigned short*)mixer->stored.buffer)[0]-0x8000) << 8;
+	    else
+	       v2 = 0;
+	 }
+	 else
+	    v2 = (mixer->data.u16[v+1]-0x8000) << 8;
+
+	 v = mixer->pos & (MIX_FIX_SCALE-1);
+	 v = (((v2 - v1) * v) >> MIX_FIX_SHIFT) + v1;
+
+	 *(buf++) += MULSC(v, mixer->lvol);
+	 *(buf++) += MULSC(v, mixer->rvol);
+	 if((mixer->pos += mixer->step) >= mixer->end_pos) {
+	    next_mixer_buffer(mixer);
+	    if(!mixer->data.buffer)
+	       break;
+	 }
+      }
+   }
+}
+
+
+#define MIN_24 (0xFF800000)
+#define MAX_24 (0x007FFFFF)
 
 /* _mix_some_samples:
- *  Mixes samples into a buffer in conventional memory (the buf parameter
- *  should be a linear offset into the specified segment), using the buffer 
- *  size, sample frequency, etc, set when you called _mixer_init(). This 
- *  should be called by the hardware end-of-buffer interrupt routine to 
- *  get the next buffer full of samples to DMA to the card.
+ *  Mixes samples into a buffer in memory (the buf parameter should be a
+ *  linear offset into the specified segment), using the buffer size, sample
+ *  frequency, etc, set when you called _mixer_init(). This should be called
+ *  by the audio driver to get the next buffer full of samples.
  */
 void _mix_some_samples(unsigned long buf, unsigned short seg, int issigned)
 {
+   signed int *p = mix_buffer;
    int i;
-   unsigned short *p = mix_buffer;
-   unsigned long *l = (unsigned long *)p;
 
    /* clear mixing buffer */
-   for (i=0; i<mix_size/2; i++)
-      *(l++) = 0x80008000;
+   memset(p, 0, mix_size * sizeof(*p));
 
 #ifdef ALLEGRO_MULTITHREADED
    system_driver->lock_mutex(mixer_mutex);
 #endif
 
-   if (_sound_hq >= 2) {
-      /* top quality interpolated 16 bit mixing */
-      for (i=0; i<mix_voices; i++) {
-	 if (mixer_voice[i].playing) {
-            if ((_phys_voice[i].vol > 0) || (_phys_voice[i].dvol > 0)) {
-	       if (mixer_voice[i].stereo) {
-	          /* stereo input -> interpolated output */
-	          if (mixer_voice[i].data8)
+   for (i=0; i<mix_voices; i++) {
+      if (mixer_voice[i].playing) {
+	 if ((_phys_voice[i].vol > 0) || (_phys_voice[i].dvol > 0)) {
+	    /* Interpolated mixing */
+	    if (_sound_hq >= 2) {
+	       /* stereo input -> interpolated output */
+	       if (mixer_voice[i].channels != 1) {
+		  if (mixer_voice[i].bits == 8)
 		     mix_hq2_8x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_hq2_16x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
+	       /* mono input -> interpolated output */
 	       else {
-	          /* mono input -> interpolated output */
-	          if (mixer_voice[i].data8)
+		  if (mixer_voice[i].bits == 8)
 		     mix_hq2_8x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_hq2_16x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
-            }
-            else
-               mix_silent_samples(mixer_voice+i, _phys_voice+i, mix_size>>1);
-	 }
-      }
-   }
-   else if (_sound_hq) {
-      /* high quality 16 bit mixing */
-      for (i=0; i<mix_voices; i++) {
-	 if (mixer_voice[i].playing) {
-	    if ((_phys_voice[i].vol > 0) || (_phys_voice[i].dvol > 0)) {
-	       if (mixer_voice[i].stereo) {
-	          /* stereo input -> high quality output */
-	          if (mixer_voice[i].data8)
+	    }
+	    /* high quality mixing */
+	    else if (_sound_hq) {
+	       /* stereo input -> high quality output */
+	       if (mixer_voice[i].channels != 1) {
+		  if (mixer_voice[i].bits == 8)
 		     mix_hq1_8x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_hq1_16x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
+	       /* mono input -> high quality output */
 	       else {
-	          /* mono input -> high quality output */
-	          if (mixer_voice[i].data8)
+		  if (mixer_voice[i].bits == 8)
 		     mix_hq1_8x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_hq1_16x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
-            }
-            else
-               mix_silent_samples(mixer_voice+i, _phys_voice+i, mix_size>>1);
-	 }
-      }
-   }
-   else if (mix_stereo) { 
-      /* lower quality (faster) stereo mixing */
-      for (i=0; i<mix_voices; i++) {
-	 if (mixer_voice[i].playing) {
-	    if ((_phys_voice[i].vol > 0) || (_phys_voice[i].dvol > 0)) {
-	       if (mixer_voice[i].stereo) {
-	          /* stereo input -> stereo output */
-	          if (mixer_voice[i].data8)
+	    }
+	    /* low quality (fast?) stereo mixing */
+	    else if (mix_channels != 1) {
+	       /* stereo input -> stereo output */
+	       if (mixer_voice[i].channels != 1) {
+		  if (mixer_voice[i].bits == 8)
 		     mix_stereo_8x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_stereo_16x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
+	       /* mono input -> stereo output */
 	       else {
-	          /* mono input -> stereo output */
-	          if (mixer_voice[i].data8)
+		  if (mixer_voice[i].bits == 8)
 		     mix_stereo_8x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_stereo_16x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
-            }
-            else
-               mix_silent_samples(mixer_voice+i, _phys_voice+i, mix_size>>1);
-	 }
-      }
-   }
-   else {
-      /* lower quality (fast) mono mixing */
-      for (i=0; i<mix_voices; i++) {
-	 if (mixer_voice[i].playing) {
-	    if ((_phys_voice[i].vol > 0) || (_phys_voice[i].dvol > 0)) {
-	       if (mixer_voice[i].stereo) {
-	          /* stereo input -> mono output */
-	          if (mixer_voice[i].data8)
+	    }
+	    /* low quality (fast?) mono mixing */
+	    else {
+	       /* stereo input -> mono output */
+	       if (mixer_voice[i].channels != 1) {
+		  if (mixer_voice[i].bits == 8)
 		     mix_mono_8x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_mono_16x2_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
+	       /* mono input -> mono output */
 	       else {
-	          /* mono input -> mono output */
-	          if (mixer_voice[i].data8)
+		  if (mixer_voice[i].bits == 8)
 		     mix_mono_8x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
-	          else
+		  else
 		     mix_mono_16x1_samples(mixer_voice+i, _phys_voice+i, p, mix_size);
 	       }
-            }
-            else
-               mix_silent_samples(mixer_voice+i, _phys_voice+i, mix_size);
+	    }
+	 }
+	 else
+	    mix_silent_samples(mixer_voice+i, _phys_voice+i, mix_size);
+      }
+   }
+
+   for(i = 0;i < mixer_stream_count;++i) {
+      if(mixer_stream[i].status != DEAD) {
+	 if(mixer_stream[i].pos < mixer_stream[i].end_pos) {
+	    if(mixer_stream[i].bits == 16)
+	       mix_16bit_to_buffer(mixer_stream+i, p, mix_size);
+	    else
+	       mix_8bit_to_buffer(mixer_stream+i, p, mix_size);
+	 }
+	 else if(mixer_stream[i].status == AUTOKILL) {
+	    mixer_stream[i].stored.buffer = NULL;
+	    mixer_stream[i].stored.buffer_len = 0;
+	    mixer_stream[i].data.buffer = NULL;
+	    mixer_stream[i].end_pos = 0;
+	    mixer_stream[i].pos = 0;
+	    mixer_stream[i].status = DEAD;
 	 }
       }
    }
@@ -1193,28 +1783,37 @@
 
    _farsetsel(seg);
 
-   /* transfer to conventional memory buffer using a clip table */
-   if (mix_16bit) {
+   /* transfer to the audio driver's buffer */
+   if (mix_bits == 16) {
       if (issigned) {
 	 for (i=0; i<mix_size; i++) {
-	    _farnspokew(buf, mix_clip_table[*p >> (16-MIX_RES_16)] ^ 0x8000);
-	    buf += sizeof(short);
+	    _farnspokew(buf, clamp_val(*p, MIN_24, MAX_24) >> 8);
+	    buf += 2;
 	    p++;
-         }
+	 }
       }
       else {
 	 for (i=0; i<mix_size; i++) {
-	    _farnspokew(buf, mix_clip_table[*p >> (16-MIX_RES_16)]);
-            buf += sizeof(short);
-            p++;
-         }
+	    _farnspokew(buf, (signed short)(clamp_val(*p, MIN_24, MAX_24) >> 8) ^ 0x8000);
+	    buf += 2;
+	    p++;
+	 }
       }
    }
    else {
-      for (i=0; i<mix_size; i++) {
-	 _farnspokeb(buf, mix_clip_table[*p >> (16-MIX_RES_8)]);
-         buf++;
-         p++;
+      if(issigned) {
+	 for (i=0; i<mix_size; i++) {
+	    _farnspokeb(buf, clamp_val(*p, MIN_24, MAX_24) >> 16);
+	    buf++;
+	    p++;
+	 }
+      }
+      else {
+	 for (i=0; i<mix_size; i++) {
+	    _farnspokeb(buf, (signed char)(clamp_val(*p, MIN_24, MAX_24) >> 16) ^ 0x80);
+	    buf++;
+	    p++;
+	 }
       }
    }
 }
@@ -1229,20 +1828,14 @@
 void _mixer_init_voice(int voice, AL_CONST SAMPLE *sample)
 {
    mixer_voice[voice].playing = FALSE;
-   mixer_voice[voice].stereo = sample->stereo;
+   mixer_voice[voice].channels = (sample->stereo ? 2 : 1);
+   mixer_voice[voice].bits = sample->bits;
    mixer_voice[voice].pos = 0;
    mixer_voice[voice].len = sample->len << MIX_FIX_SHIFT;
    mixer_voice[voice].loop_start = sample->loop_start << MIX_FIX_SHIFT;
    mixer_voice[voice].loop_end = sample->loop_end << MIX_FIX_SHIFT;
 
-   if (sample->bits == 8) {
-      mixer_voice[voice].data8 = sample->data;
-      mixer_voice[voice].data16 = NULL;
-   }
-   else {
-      mixer_voice[voice].data8 = NULL;
-      mixer_voice[voice].data16 = sample->data;
-   }
+   mixer_voice[voice].data.buffer = sample->data;
 
    update_mixer_volume(mixer_voice+voice, _phys_voice+voice);
    update_mixer_freq(mixer_voice+voice, _phys_voice+voice);
@@ -1262,8 +1855,7 @@
 #endif
 
    mixer_voice[voice].playing = FALSE;
-   mixer_voice[voice].data8 = NULL;
-   mixer_voice[voice].data16 = NULL;
+   mixer_voice[voice].data.buffer = NULL;
 
 #ifdef ALLEGRO_MULTITHREADED
    system_driver->unlock_mutex(mixer_mutex);
@@ -1544,12 +2136,32 @@
    LOCK_VARIABLE(mixer_voice);
    LOCK_VARIABLE(mix_buffer);
    LOCK_VARIABLE(mix_vol_table);
-   LOCK_VARIABLE(mix_clip_table);
    LOCK_VARIABLE(mix_voices);
    LOCK_VARIABLE(mix_size);
    LOCK_VARIABLE(mix_freq);
-   LOCK_VARIABLE(mix_stereo);
-   LOCK_VARIABLE(mix_16bit);
+   LOCK_VARIABLE(mix_channels);
+   LOCK_VARIABLE(mix_bits);
+   LOCK_VARIABLE(mixer_stream);
+   LOCK_FUNCTION(set_mixer_quality);
+   LOCK_FUNCTION(get_mixer_quality);
+   LOCK_FUNCTION(get_mixer_buffer_length);
+   LOCK_FUNCTION(get_mixer_frequency);
+   LOCK_FUNCTION(get_mixer_bits);
+   LOCK_FUNCTION(get_mixer_channels);
+   LOCK_FUNCTION(get_mixer_voices);
+   LOCK_FUNCTION(get_mixer_stream_position);
+   LOCK_FUNCTION(get_mixer_stream_freq);
+   LOCK_FUNCTION(get_mixer_stream_volume);
+   LOCK_FUNCTION(get_mixer_stream_pan);
+   LOCK_FUNCTION(set_mixer_stream_freq);
+   LOCK_FUNCTION(set_mixer_stream_volume);
+   LOCK_FUNCTION(set_mixer_stream_pan);
+   LOCK_FUNCTION(set_mixer_stream_buffer);
+   LOCK_FUNCTION(mixer_stream_is_ready);
+   LOCK_FUNCTION(allocate_mixer_stream);
+   LOCK_FUNCTION(deallocate_mixer_stream);
+   LOCK_FUNCTION(release_mixer_stream);
+   LOCK_FUNCTION(stop_mixer_stream);
    LOCK_FUNCTION(mix_silent_samples);
    LOCK_FUNCTION(mix_mono_8x1_samples);
    LOCK_FUNCTION(mix_mono_8x2_samples);
@@ -1594,6 +2206,3 @@
    LOCK_FUNCTION(_mixer_set_tremolo);
    LOCK_FUNCTION(_mixer_set_vibrato);
 }
-
-
-


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/