summaryrefslogtreecommitdiff
path: root/src/libs/sound/mixer
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/sound/mixer')
-rw-r--r--src/libs/sound/mixer/Makeinfo3
-rw-r--r--src/libs/sound/mixer/mixer.c1760
-rw-r--r--src/libs/sound/mixer/mixer.h274
-rw-r--r--src/libs/sound/mixer/mixerint.h110
-rw-r--r--src/libs/sound/mixer/nosound/Makeinfo2
-rw-r--r--src/libs/sound/mixer/nosound/audiodrv_nosound.c410
-rw-r--r--src/libs/sound/mixer/nosound/audiodrv_nosound.h69
-rw-r--r--src/libs/sound/mixer/sdl/Makeinfo2
-rw-r--r--src/libs/sound/mixer/sdl/audiodrv_sdl.c486
-rw-r--r--src/libs/sound/mixer/sdl/audiodrv_sdl.h66
10 files changed, 3182 insertions, 0 deletions
diff --git a/src/libs/sound/mixer/Makeinfo b/src/libs/sound/mixer/Makeinfo
new file mode 100644
index 0000000..66f960d
--- /dev/null
+++ b/src/libs/sound/mixer/Makeinfo
@@ -0,0 +1,3 @@
+uqm_SUBDIRS="sdl nosound"
+uqm_CFILES="mixer.c"
+uqm_HFILES="mixer.h mixerint.h"
diff --git a/src/libs/sound/mixer/mixer.c b/src/libs/sound/mixer/mixer.c
new file mode 100644
index 0000000..3e14ddd
--- /dev/null
+++ b/src/libs/sound/mixer/mixer.c
@@ -0,0 +1,1760 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Mixer for low-level sound output drivers
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "mixer.h"
+#include "mixerint.h"
+#include "libs/misc.h"
+#include "libs/threadlib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+static uint32 mixer_initialized = 0;
+static uint32 mixer_format;
+static uint32 mixer_chansize;
+static uint32 mixer_sampsize;
+static uint32 mixer_freq;
+static uint32 mixer_channels;
+static uint32 last_error = MIX_NO_ERROR;
+static mixer_Quality mixer_quality;
+static mixer_Resampling mixer_resampling;
+static mixer_Flags mixer_flags;
+
+/* when locking more than one mutex
+ * you must lock them in this order
+ */
+static RecursiveMutex src_mutex;
+static RecursiveMutex buf_mutex;
+static RecursiveMutex act_mutex;
+
+#define MAX_SOURCES 8
+mixer_Source *active_sources[MAX_SOURCES];
+
+
+/*************************************************
+ * Internals
+ */
+
+static void
+mixer_SetError (uint32 error)
+{
+ last_error = error;
+}
+
+
+/*************************************************
+ * General interface
+ */
+
+uint32
+mixer_GetError (void)
+{
+ uint32 error = last_error;
+ last_error = MIX_NO_ERROR;
+ return error;
+}
+
+/* Initialize the mixer with a certain audio format */
+bool
+mixer_Init (uint32 frequency, uint32 format, mixer_Quality quality,
+ mixer_Flags flags)
+{
+ if (mixer_initialized)
+ mixer_Uninit ();
+
+ last_error = MIX_NO_ERROR;
+ memset (active_sources, 0, sizeof(mixer_Source*) * MAX_SOURCES);
+
+ mixer_chansize = MIX_FORMAT_BPC (format);
+ mixer_channels = MIX_FORMAT_CHANS (format);
+ mixer_sampsize = MIX_FORMAT_SAMPSIZE (format);
+ mixer_freq = frequency;
+ mixer_quality = quality;
+ mixer_format = format;
+ mixer_flags = flags;
+
+ mixer_resampling.None = mixer_ResampleNone;
+ mixer_resampling.Downsample = mixer_ResampleNearest;
+ if (mixer_quality == MIX_QUALITY_DEFAULT)
+ mixer_resampling.Upsample = mixer_UpsampleLinear;
+ else if (mixer_quality == MIX_QUALITY_HIGH)
+ mixer_resampling.Upsample = mixer_UpsampleCubic;
+ else
+ mixer_resampling.Upsample = mixer_ResampleNearest;
+
+ src_mutex = CreateRecursiveMutex("mixer_SourceMutex", SYNC_CLASS_AUDIO);
+ buf_mutex = CreateRecursiveMutex("mixer_BufferMutex", SYNC_CLASS_AUDIO);
+ act_mutex = CreateRecursiveMutex("mixer_ActiveMutex", SYNC_CLASS_AUDIO);
+
+ mixer_initialized = 1;
+
+ return true;
+}
+
+/* Uninitialize the mixer */
+void
+mixer_Uninit (void)
+{
+ if (mixer_initialized)
+ {
+ DestroyRecursiveMutex (src_mutex);
+ DestroyRecursiveMutex (buf_mutex);
+ DestroyRecursiveMutex (act_mutex);
+ mixer_initialized = 0;
+ }
+}
+
+
+/**********************************************************
+ * THE mixer
+ *
+ */
+
+void
+mixer_MixChannels (void *userdata, uint8 *stream, sint32 len)
+{
+ uint8 *end_stream = stream + len;
+ bool left = true;
+
+ /* keep this order or die */
+ LockRecursiveMutex (src_mutex);
+ LockRecursiveMutex (buf_mutex);
+ LockRecursiveMutex (act_mutex);
+
+ for (; stream < end_stream; stream += mixer_chansize)
+ {
+ uint32 i;
+ float fullsamp = 0;
+
+ for (i = 0; i < MAX_SOURCES; i++)
+ {
+ mixer_Source *src;
+ float samp = 0;
+
+ /* find next source */
+ for (; i < MAX_SOURCES && (
+ (src = active_sources[i]) == 0
+ || src->state != MIX_PLAYING
+ || !mixer_SourceGetNextSample (src, &samp, left));
+ i++)
+ ;
+
+ if (i < MAX_SOURCES)
+ {
+ /* sample acquired */
+ fullsamp += samp;
+ }
+ }
+
+ /* clip the sample */
+ if (mixer_chansize == 2)
+ {
+ /* check S16 clipping */
+ if (fullsamp > SINT16_MAX)
+ fullsamp = SINT16_MAX;
+ else if (fullsamp < SINT16_MIN)
+ fullsamp = SINT16_MIN;
+ }
+ else
+ {
+ /* check S8 clipping */
+ if (fullsamp > SINT8_MAX)
+ fullsamp = SINT8_MAX;
+ else if (fullsamp < SINT8_MIN)
+ fullsamp = SINT8_MIN;
+ }
+
+ mixer_PutSampleExt (stream, mixer_chansize, (sint32)fullsamp);
+ if (mixer_channels == 2)
+ left = !left;
+ }
+
+ /* keep this order or die */
+ UnlockRecursiveMutex (act_mutex);
+ UnlockRecursiveMutex (buf_mutex);
+ UnlockRecursiveMutex (src_mutex);
+
+ (void) userdata; // satisfying compiler - unused arg
+}
+
+/* fake mixer -- only process buffer and source states */
+void
+mixer_MixFake (void *userdata, uint8 *stream, sint32 len)
+{
+ uint8 *end_stream = stream + len;
+ bool left = true;
+
+ /* keep this order or die */
+ LockRecursiveMutex (src_mutex);
+ LockRecursiveMutex (buf_mutex);
+ LockRecursiveMutex (act_mutex);
+
+ for (; stream < end_stream; stream += mixer_chansize)
+ {
+ uint32 i;
+
+ for (i = 0; i < MAX_SOURCES; i++)
+ {
+ mixer_Source *src;
+ float samp;
+
+ /* find next source */
+ for (; i < MAX_SOURCES && (
+ (src = active_sources[i]) == 0
+ || src->state != MIX_PLAYING
+ || !mixer_SourceGetFakeSample (src, &samp, left));
+ i++)
+ ;
+ }
+ if (mixer_channels == 2)
+ left = !left;
+ }
+
+ /* keep this order or die */
+ UnlockRecursiveMutex (act_mutex);
+ UnlockRecursiveMutex (buf_mutex);
+ UnlockRecursiveMutex (src_mutex);
+
+ (void) userdata; // satisfying compiler - unused arg
+}
+
+
+/*************************************************
+ * Sources interface
+ */
+
+/* generate n sources */
+void
+mixer_GenSources (uint32 n, mixer_Object *psrcobj)
+{
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!psrcobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GenSources() called with null ptr");
+ return;
+ }
+ for (; n; n--, psrcobj++)
+ {
+ mixer_Source *src;
+
+ src = (mixer_Source *) HMalloc (sizeof (mixer_Source));
+ src->magic = mixer_srcMagic;
+ src->locked = false;
+ src->state = MIX_INITIAL;
+ src->looping = false;
+ src->gain = MIX_GAIN_ADJ;
+ src->cqueued = 0;
+ src->cprocessed = 0;
+ src->firstqueued = 0;
+ src->nextqueued = 0;
+ src->prevqueued = 0;
+ src->lastqueued = 0;
+ src->pos = 0;
+ src->count = 0;
+
+ *psrcobj = (mixer_Object) src;
+ }
+}
+
+/* delete n sources */
+void
+mixer_DeleteSources (uint32 n, mixer_Object *psrcobj)
+{
+ uint32 i;
+ mixer_Object *pcurobj;
+
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!psrcobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteSources() called with null ptr");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ /* check to make sure we can delete all sources */
+ for (i = n, pcurobj = psrcobj; i && pcurobj; i--, pcurobj++)
+ {
+ mixer_Source *src = (mixer_Source *) *pcurobj;
+
+ if (!src)
+ continue;
+
+ if (src->magic != mixer_srcMagic)
+ break;
+ }
+
+ if (i)
+ { /* some source failed */
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteSources(): not a source");
+ }
+ else
+ { /* all sources checked out */
+ for (; n; n--, psrcobj++)
+ {
+ mixer_Source *src = (mixer_Source *) *psrcobj;
+
+ if (!src)
+ continue;
+
+ /* stopping should not be necessary
+ * under ideal circumstances
+ */
+ if (src->state != MIX_INITIAL)
+ mixer_SourceStop_internal (src);
+
+ /* unqueueing should not be necessary
+ * under ideal circumstances
+ */
+ mixer_SourceUnqueueAll (src);
+ HFree (src);
+ *psrcobj = 0;
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* check if really is a source */
+bool
+mixer_IsSource (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+ bool ret;
+
+ if (!src)
+ return false;
+
+ LockRecursiveMutex (src_mutex);
+ ret = src->magic == mixer_srcMagic;
+ UnlockRecursiveMutex (src_mutex);
+
+ return ret;
+}
+
+/* set source integer property */
+void
+mixer_Sourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcei() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcei(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_LOOPING:
+ src->looping = value;
+ break;
+ case MIX_BUFFER:
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) value;
+
+ if (src->cqueued > 0)
+ mixer_SourceUnqueueAll (src);
+
+ if (buf && !mixer_CheckBufferState (buf, "mixer_Sourcei"))
+ break;
+
+ src->firstqueued = buf;
+ src->nextqueued = src->firstqueued;
+ src->prevqueued = 0;
+ src->lastqueued = src->nextqueued;
+ if (src->lastqueued)
+ src->lastqueued->next = 0;
+ src->cqueued = 1;
+ }
+ break;
+ case MIX_SOURCE_STATE:
+ if (value == MIX_INITIAL)
+ {
+ mixer_SourceRewind_internal (src);
+ }
+ else
+ {
+ log_add (log_Debug, "mixer_Sourcei(MIX_SOURCE_STATE): "
+ "unsupported state, call ignored");
+ }
+ break;
+ default:
+ mixer_SetError (MIX_INVALID_ENUM);
+ log_add (log_Debug, "mixer_Sourcei() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* set source float property */
+void
+mixer_Sourcef (mixer_Object srcobj, mixer_SourceProp pname, float value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcef() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcef(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_GAIN:
+ src->gain = value * MIX_GAIN_ADJ;
+ break;
+ default:
+ log_add (log_Debug, "mixer_Sourcei() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* set source float array property (CURRENTLY NOT IMPLEMENTED) */
+void mixer_Sourcefv (mixer_Object srcobj, mixer_SourceProp pname, float *value)
+{
+ (void)srcobj;
+ (void)pname;
+ (void)value;
+}
+
+
+/* get source integer property */
+void
+mixer_GetSourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal *value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src || !value)
+ {
+ mixer_SetError (src ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcei() called with null param");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcei(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_LOOPING:
+ *value = src->looping;
+ break;
+ case MIX_BUFFER:
+ *value = (mixer_IntVal) src->firstqueued;
+ break;
+ case MIX_SOURCE_STATE:
+ *value = src->state;
+ break;
+ case MIX_BUFFERS_QUEUED:
+ *value = src->cqueued;
+ break;
+ case MIX_BUFFERS_PROCESSED:
+ *value = src->cprocessed;
+ break;
+ default:
+ mixer_SetError (MIX_INVALID_ENUM);
+ log_add (log_Debug, "mixer_GetSourcei() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* get source float property */
+void
+mixer_GetSourcef (mixer_Object srcobj, mixer_SourceProp pname,
+ float *value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src || !value)
+ {
+ mixer_SetError (src ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcef() called with null param");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcef(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_GAIN:
+ *value = src->gain / MIX_GAIN_ADJ;
+ break;
+ default:
+ log_add (log_Debug, "mixer_GetSourcef() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* start the source; add it to active array */
+void
+mixer_SourcePlay (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePlay() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePlay(): not a source");
+ }
+ else /* should make the source active */
+ {
+ if (src->state < MIX_PLAYING)
+ {
+ if (src->firstqueued && !src->nextqueued)
+ mixer_SourceRewind_internal (src);
+ mixer_SourceActivate (src);
+ }
+ src->state = MIX_PLAYING;
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* stop the source; remove it from active array and requeue buffers */
+void
+mixer_SourceRewind (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceRewind() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePlay(): not a source");
+ }
+ else
+ {
+ mixer_SourceRewind_internal (src);
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* pause the source; keep in active array */
+void
+mixer_SourcePause (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePause() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePause(): not a source");
+ }
+ else /* should keep all buffers and offsets */
+ {
+ if (src->state < MIX_PLAYING)
+ mixer_SourceActivate (src);
+ src->state = MIX_PAUSED;
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* stop the source; remove it from active array
+ * and unqueue 'queued' buffers
+ */
+void
+mixer_SourceStop (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceStop() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceStop(): not a source");
+ }
+ else /* should remove queued buffers */
+ {
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+ mixer_SourceStop_internal (src);
+ src->state = MIX_STOPPED;
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* queue buffers on the source */
+void
+mixer_SourceQueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj)
+{
+ uint32 i;
+ mixer_Object* pobj;
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src || !pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceQueueBuffers() called "
+ "with null param");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+ /* check to make sure we can safely queue all buffers */
+ for (i = n, pobj = pbufobj; i; i--, pobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pobj;
+ if (!buf || !mixer_CheckBufferState (buf,
+ "mixer_SourceQueueBuffers"))
+ {
+ break;
+ }
+ }
+ UnlockRecursiveMutex (buf_mutex);
+
+ if (i == 0)
+ { /* all buffers checked out */
+ LockRecursiveMutex (src_mutex);
+ LockRecursiveMutex (buf_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceQueueBuffers(): not a source");
+ }
+ else
+ {
+ for (i = n, pobj = pbufobj; i; i--, pobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pobj;
+
+ /* add buffer to the chain */
+ if (src->lastqueued)
+ src->lastqueued->next = buf;
+ src->lastqueued = buf;
+
+ if (!src->firstqueued)
+ {
+ src->firstqueued = buf;
+ src->nextqueued = buf;
+ src->prevqueued = 0;
+ }
+ src->cqueued++;
+ buf->state = MIX_BUF_QUEUED;
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+ UnlockRecursiveMutex (src_mutex);
+ }
+}
+
+/* unqueue buffers from the source */
+void
+mixer_SourceUnqueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj)
+{
+ uint32 i;
+ mixer_Source *src = (mixer_Source *) srcobj;
+ mixer_Buffer *curbuf = 0;
+
+ if (!src || !pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceUnqueueBuffers() called "
+ "with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceUnqueueBuffers(): not a source");
+ }
+ else if (n > src->cqueued)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ }
+ else
+ {
+ LockRecursiveMutex (buf_mutex);
+
+ /* check to make sure we can unqueue all buffers */
+ for (i = n, curbuf = src->firstqueued;
+ i && curbuf && curbuf->state != MIX_BUF_PLAYING;
+ i--, curbuf = curbuf->next)
+ ;
+
+ if (i)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_SourceUnqueueBuffers(): "
+ "active buffer attempted");
+ }
+ else
+ { /* all buffers checked out */
+ for (i = n; i; i--, pbufobj++)
+ {
+ mixer_Buffer *buf = src->firstqueued;
+
+ /* remove buffer from the chain */
+ if (src->nextqueued == buf)
+ src->nextqueued = buf->next;
+ if (src->prevqueued == buf)
+ src->prevqueued = 0;
+ if (src->lastqueued == buf)
+ src->lastqueued = 0;
+ src->firstqueued = buf->next;
+ src->cqueued--;
+
+ if (buf->state == MIX_BUF_PROCESSED)
+ src->cprocessed--;
+
+ buf->state = MIX_BUF_FILLED;
+ buf->next = 0;
+ *pbufobj = (mixer_Object) buf;
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/*************************************************
+ * Sources internals
+ */
+
+static void
+mixer_SourceUnqueueAll (mixer_Source *src)
+{
+ mixer_Buffer *buf;
+ mixer_Buffer *nextbuf;
+
+ if (!src)
+ {
+ log_add (log_Debug, "mixer_SourceUnqueueAll() called "
+ "with null source");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ for (buf = src->firstqueued; buf; buf = nextbuf)
+ {
+ if (buf->state == MIX_BUF_PLAYING)
+ {
+ log_add (log_Debug, "mixer_SourceUnqueueAll(): "
+ "attempted on active buffer");
+ }
+ nextbuf = buf->next;
+ buf->state = MIX_BUF_FILLED;
+ buf->next = 0;
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+
+ src->firstqueued = 0;
+ src->nextqueued = 0;
+ src->prevqueued = 0;
+ src->lastqueued = 0;
+ src->cqueued = 0;
+ src->cprocessed = 0;
+ src->pos = 0;
+ src->count = 0;
+}
+
+/* add the source to the active array */
+static void
+mixer_SourceActivate (mixer_Source* src)
+{
+ uint32 i;
+
+ LockRecursiveMutex (act_mutex);
+
+ /* check active sources, see if this source is there already */
+ for (i = 0; i < MAX_SOURCES && active_sources[i] != src; i++)
+ ;
+ if (i < MAX_SOURCES)
+ { /* source found */
+ log_add (log_Debug, "mixer_SourceActivate(): "
+ "source already active in slot %u", i);
+ UnlockRecursiveMutex (act_mutex);
+ return;
+ }
+
+ /* find an empty slot */
+ for (i = 0; i < MAX_SOURCES && active_sources[i] != 0; i++)
+ ;
+ if (i < MAX_SOURCES)
+ { /* slot found */
+ active_sources[i] = src;
+ }
+ else
+ {
+ log_add (log_Debug, "mixer_SourceActivate(): "
+ "no more slots available (max=%d)", MAX_SOURCES);
+ }
+
+ UnlockRecursiveMutex (act_mutex);
+}
+
+/* remove the source from the active array */
+static void
+mixer_SourceDeactivate (mixer_Source* src)
+{
+ uint32 i;
+
+ LockRecursiveMutex (act_mutex);
+
+ /* check active sources, see if this source is there */
+ for (i = 0; i < MAX_SOURCES && active_sources[i] != src; i++)
+ ;
+ if (i < MAX_SOURCES)
+ { /* source found */
+ active_sources[i] = 0;
+ }
+ else
+ { /* source not found */
+ log_add (log_Debug, "mixer_SourceDeactivate(): source not active");
+ }
+
+ UnlockRecursiveMutex (act_mutex);
+}
+
+static void
+mixer_SourceStop_internal (mixer_Source *src)
+{
+ mixer_Buffer *buf;
+ mixer_Buffer *nextbuf;
+
+ if (!src->firstqueued)
+ return;
+
+ /* assert the source buffers state */
+ if (!src->lastqueued)
+ {
+ log_add (log_Debug, "mixer_SourceStop_internal(): "
+ "desynced source state");
+#ifdef DEBUG
+ explode ();
+#endif
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ /* find last 'processed' buffer */
+ for (buf = src->firstqueued;
+ buf && buf->next && buf->next != src->nextqueued;
+ buf = buf->next)
+ ;
+ src->lastqueued = buf;
+ if (buf)
+ buf->next = 0; /* break the chain */
+
+ /* unqueue all 'queued' buffers */
+ for (buf = src->nextqueued; buf; buf = nextbuf)
+ {
+ nextbuf = buf->next;
+ buf->state = MIX_BUF_FILLED;
+ buf->next = 0;
+ src->cqueued--;
+ }
+
+ if (src->cqueued == 0)
+ { /* all buffers were removed */
+ src->firstqueued = 0;
+ src->lastqueued = 0;
+ }
+ src->nextqueued = 0;
+ src->prevqueued = 0;
+ src->pos = 0;
+ src->count = 0;
+
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+static void
+mixer_SourceRewind_internal (mixer_Source *src)
+{
+ /* should change the processed buffers to queued */
+ mixer_Buffer *buf;
+
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+
+ LockRecursiveMutex (buf_mutex);
+
+ for (buf = src->firstqueued;
+ buf && buf->state != MIX_BUF_QUEUED;
+ buf = buf->next)
+ {
+ buf->state = MIX_BUF_QUEUED;
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+
+ src->pos = 0;
+ src->count = 0;
+ src->cprocessed = 0;
+ src->nextqueued = src->firstqueued;
+ src->prevqueued = 0;
+ src->state = MIX_INITIAL;
+}
+
+/* get the sample next in queue in internal format */
+static inline bool
+mixer_SourceGetNextSample (mixer_Source *src, float *psamp, bool left)
+{
+ /* fake the data if requested */
+ if (mixer_flags & MIX_FAKE_DATA)
+ return mixer_SourceGetFakeSample (src, psamp, left);
+
+ while (src->nextqueued)
+ {
+ mixer_Buffer *buf = src->nextqueued;
+
+ if (!buf->data || buf->size < mixer_sampsize)
+ {
+ /* buffer invalid, go next */
+ buf->state = MIX_BUF_PROCESSED;
+ src->pos = 0;
+ src->nextqueued = src->nextqueued->next;
+ src->cprocessed++;
+ continue;
+ }
+
+ if (!left && buf->orgchannels == 1)
+ {
+ /* mono source so we can copy left channel to right */
+ *psamp = src->samplecache;
+ }
+ else
+ {
+ *psamp = src->samplecache = buf->Resample(src, left) * src->gain;
+ }
+
+ if (src->pos < buf->size ||
+ (left && buf->sampsize != mixer_sampsize))
+ {
+ buf->state = MIX_BUF_PLAYING;
+ }
+ else
+ {
+ /* buffer exhausted, go next */
+ buf->state = MIX_BUF_PROCESSED;
+ src->pos = 0;
+ src->prevqueued = src->nextqueued;
+ src->nextqueued = src->nextqueued->next;
+ src->cprocessed++;
+ }
+
+ return true;
+ }
+
+ /* no more playable buffers */
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+
+ src->state = MIX_STOPPED;
+
+ return false;
+}
+
+/* fake the next sample, but process buffers and states */
+static inline bool
+mixer_SourceGetFakeSample (mixer_Source *src, float *psamp, bool left)
+{
+ while (src->nextqueued)
+ {
+ mixer_Buffer *buf = src->nextqueued;
+
+ if (left || buf->orgchannels != 1)
+ {
+ if (mixer_freq == buf->orgfreq)
+ src->pos += mixer_chansize;
+ else
+ mixer_SourceAdvance(src, left);
+ }
+ *psamp = 0;
+
+ if (src->pos < buf->size ||
+ (left && buf->sampsize != mixer_sampsize))
+ {
+ buf->state = MIX_BUF_PLAYING;
+ }
+ else
+ {
+ /* buffer exhausted, go next */
+ buf->state = MIX_BUF_PROCESSED;
+ src->pos = 0;
+ src->prevqueued = src->nextqueued;
+ src->nextqueued = src->nextqueued->next;
+ src->cprocessed++;
+ }
+
+ return true;
+ }
+
+ /* no more playable buffers */
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+
+ src->state = MIX_STOPPED;
+
+ return false;
+}
+
+/* advance position in currently queued buffer */
+static inline uint32
+mixer_SourceAdvance (mixer_Source *src, bool left)
+{
+ mixer_Buffer *curr = src->nextqueued;
+ if (curr->orgchannels == 2 && mixer_channels == 2)
+ {
+ if (!left)
+ {
+ src->pos += curr->high;
+ src->count += curr->low;
+ if (src->count > UINT16_MAX)
+ {
+ src->count -= UINT16_MAX;
+ src->pos += curr->sampsize;
+ }
+ return mixer_chansize;
+ }
+ }
+ else
+ {
+ src->pos += curr->high;
+ src->count += curr->low;
+ if (src->count > UINT16_MAX)
+ {
+ src->count -= UINT16_MAX;
+ src->pos += curr->sampsize;
+ }
+ }
+ return 0;
+}
+
+
+/*************************************************
+ * Buffers interface
+ */
+
+/* generate n buffer objects */
+void
+mixer_GenBuffers (uint32 n, mixer_Object *pbufobj)
+{
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_VALUE);
+ log_add (log_Debug, "mixer_GenBuffers() called with null ptr");
+ return;
+ }
+ for (; n; n--, pbufobj++)
+ {
+ mixer_Buffer *buf;
+
+ buf = (mixer_Buffer *) HMalloc (sizeof (mixer_Buffer));
+ buf->magic = mixer_bufMagic;
+ buf->locked = false;
+ buf->state = MIX_BUF_INITIAL;
+ buf->data = 0;
+ buf->size = 0;
+ buf->next = 0;
+ buf->orgdata = 0;
+ buf->orgfreq = 0;
+ buf->orgsize = 0;
+ buf->orgchannels = 0;
+ buf->orgchansize = 0;
+
+ *pbufobj = (mixer_Object) buf;
+ }
+}
+
+/* delete n buffer objects */
+void
+mixer_DeleteBuffers (uint32 n, mixer_Object *pbufobj)
+{
+ uint32 i;
+ mixer_Object *pcurobj;
+
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteBuffers() called with null ptr");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ /* check to make sure we can delete all buffers */
+ for (i = n, pcurobj = pbufobj; i && pcurobj; i--, pcurobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pcurobj;
+
+ if (!buf)
+ continue;
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteBuffers(): not a buffer");
+ break;
+ }
+ else if (buf->locked)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_DeleteBuffers(): locked buffer");
+ break;
+ }
+ else if (buf->state >= MIX_BUF_QUEUED)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_DeleteBuffers(): "
+ "attempted on queued/active buffer");
+ break;
+ }
+ }
+
+ if (i == 0)
+ {
+ /* all buffers check out */
+ for (; n; n--, pbufobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pbufobj;
+
+ if (!buf)
+ continue;
+
+ if (buf->data)
+ HFree (buf->data);
+ HFree (buf);
+
+ *pbufobj = 0;
+ }
+ }
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+/* check if really a buffer object */
+bool
+mixer_IsBuffer (mixer_Object bufobj)
+{
+ mixer_Buffer *buf = (mixer_Buffer *) bufobj;
+ bool ret;
+
+ if (!buf)
+ return false;
+
+ LockRecursiveMutex (buf_mutex);
+ ret = buf->magic == mixer_bufMagic;
+ UnlockRecursiveMutex (buf_mutex);
+
+ return ret;
+}
+
+/* get buffer property */
+void
+mixer_GetBufferi (mixer_Object bufobj, mixer_BufferProp pname,
+ mixer_IntVal *value)
+{
+ mixer_Buffer *buf = (mixer_Buffer *) bufobj;
+
+ if (!buf || !value)
+ {
+ mixer_SetError (buf ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetBufferi() called with null param");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ if (buf->locked)
+ {
+ UnlockRecursiveMutex (buf_mutex);
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_GetBufferi() called with locked buffer");
+ return;
+ }
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetBufferi(): not a buffer");
+ }
+ else
+ {
+ /* Return original buffer values
+ */
+ switch (pname)
+ {
+ case MIX_FREQUENCY:
+ *value = buf->orgfreq;
+ break;
+ case MIX_BITS:
+ *value = buf->orgchansize << 3;
+ break;
+ case MIX_CHANNELS:
+ *value = buf->orgchannels;
+ break;
+ case MIX_SIZE:
+ *value = buf->orgsize;
+ break;
+ case MIX_DATA:
+ *value = (mixer_IntVal) buf->orgdata;
+ break;
+ default:
+ mixer_SetError (MIX_INVALID_ENUM);
+ log_add (log_Debug, "mixer_GetBufferi() called "
+ "with invalid property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+/* fill buffer with external data */
+void
+mixer_BufferData (mixer_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ mixer_Buffer *buf = (mixer_Buffer *) bufobj;
+ mixer_Convertion conv;
+ uint32 dstsize;
+
+ if (!buf || !data || !size)
+ {
+ mixer_SetError (buf ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_BufferData() called with bad param");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ if (buf->locked)
+ {
+ UnlockRecursiveMutex (buf_mutex);
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_BufferData() called "
+ "with locked buffer");
+ return;
+ }
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_BufferData(): not a buffer");
+ }
+ else if (buf->state > MIX_BUF_FILLED)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_BufferData() attempted "
+ "on in-use buffer");
+ }
+ else
+ {
+ if (buf->data)
+ HFree (buf->data);
+ buf->data = 0;
+ buf->size = 0;
+
+ /* Store original buffer values for OpenAL compatibility */
+ buf->orgdata = data;
+ buf->orgfreq = freq;
+ buf->orgsize = size;
+ buf->orgchannels = MIX_FORMAT_CHANS (format);
+ buf->orgchansize = MIX_FORMAT_BPC (format);
+
+ conv.srcsamples = conv.dstsamples =
+ size / MIX_FORMAT_SAMPSIZE (format);
+
+ if (conv.dstsamples >
+ UINT32_MAX / MIX_FORMAT_SAMPSIZE (format))
+ {
+ mixer_SetError (MIX_INVALID_VALUE);
+ }
+ else
+ {
+ if (MIX_FORMAT_CHANS (format) < MIX_FORMAT_CHANS (mixer_format))
+ buf->sampsize = MIX_FORMAT_BPC (mixer_format) *
+ MIX_FORMAT_CHANS (format);
+ else
+ buf->sampsize = MIX_FORMAT_SAMPSIZE (mixer_format);
+ buf->size = dstsize = conv.dstsamples * buf->sampsize;
+
+ /* only copy/convert the data if not faking */
+ if (! (mixer_flags & MIX_FAKE_DATA))
+ {
+ buf->data = HMalloc (dstsize);
+
+ if (MIX_FORMAT_BPC (format) == MIX_FORMAT_BPC (mixer_format) &&
+ MIX_FORMAT_CHANS (format) <= MIX_FORMAT_CHANS (mixer_format))
+ {
+ /* format is compatible with internal */
+ buf->locked = true;
+ UnlockRecursiveMutex (buf_mutex);
+
+ memcpy (buf->data, data, size);
+ if (MIX_FORMAT_BPC (format) == 1)
+ {
+ /* convert buffer to S8 format internally */
+ uint8* dst;
+ for (dst = buf->data; dstsize; dstsize--, dst++)
+ *dst ^= 0x80;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+ buf->locked = false;
+ }
+ else
+ {
+ /* needs convertion */
+ conv.srcfmt = format;
+ conv.srcdata = data;
+ conv.srcsize = size;
+
+ if (MIX_FORMAT_CHANS (format) < MIX_FORMAT_CHANS (mixer_format))
+ conv.dstfmt = MIX_FORMAT_MAKE (mixer_chansize,
+ MIX_FORMAT_CHANS (format));
+ else
+ conv.dstfmt = mixer_format;
+ conv.dstdata = buf->data;
+ conv.dstsize = dstsize;
+
+ buf->locked = true;
+ UnlockRecursiveMutex (buf_mutex);
+
+ mixer_ConvertBuffer_internal (&conv);
+
+ LockRecursiveMutex (buf_mutex);
+ buf->locked = false;
+ }
+ }
+
+ buf->state = MIX_BUF_FILLED;
+ buf->high = (buf->orgfreq / mixer_freq) * buf->sampsize;
+ buf->low = (((buf->orgfreq % mixer_freq) << 16) / mixer_freq);
+ if (mixer_freq == buf->orgfreq)
+ buf->Resample = mixer_resampling.None;
+ else if (mixer_freq > buf->orgfreq)
+ buf->Resample = mixer_resampling.Upsample;
+ else
+ buf->Resample = mixer_resampling.Downsample;
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+
+/*************************************************
+ * Buffer internals
+ */
+
+static inline bool
+mixer_CheckBufferState (mixer_Buffer *buf, const char* FuncName)
+{
+ if (!buf)
+ return false;
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "%s(): not a buffer", FuncName);
+ return false;
+ }
+
+ if (buf->locked)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "%s(): locked buffer attempted", FuncName);
+ return false;
+ }
+
+ if (buf->state != MIX_BUF_FILLED)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "%s: invalid buffer attempted", FuncName);
+ return false;
+ }
+ return true;
+}
+
+static void
+mixer_ConvertBuffer_internal (mixer_Convertion *conv)
+{
+ conv->srcbpc = MIX_FORMAT_BPC (conv->srcfmt);
+ conv->srcchans = MIX_FORMAT_CHANS (conv->srcfmt);
+ conv->dstbpc = MIX_FORMAT_BPC (conv->dstfmt);
+ conv->dstchans = MIX_FORMAT_CHANS (conv->dstfmt);
+
+ conv->flags = 0;
+ if (conv->srcbpc > conv->dstbpc)
+ conv->flags |= mixConvSizeDown;
+ else if (conv->srcbpc < conv->dstbpc)
+ conv->flags |= mixConvSizeUp;
+ if (conv->srcchans > conv->dstchans)
+ conv->flags |= mixConvStereoDown;
+ else if (conv->srcchans < conv->dstchans)
+ conv->flags |= mixConvStereoUp;
+
+ mixer_ResampleFlat (conv);
+}
+
+/*************************************************
+ * Resampling routines
+ */
+
+/* get a sample from external buffer
+ * in internal format
+ */
+static inline sint32
+mixer_GetSampleExt (void *src, uint32 bpc)
+{
+ if (bpc == 2)
+ return *(sint16 *)src;
+ else
+ return (*(uint8 *)src) - 128;
+}
+
+/* get a sample from internal buffer */
+static inline sint32
+mixer_GetSampleInt (void *src, uint32 bpc)
+{
+ if (bpc == 2)
+ return *(sint16 *)src;
+ else
+ return *(sint8 *)src;
+}
+
+/* put a sample into an external buffer
+ * from internal format
+ */
+static inline void
+mixer_PutSampleExt (void *dst, uint32 bpc, sint32 samp)
+{
+ if (bpc == 2)
+ *(sint16 *)dst = samp;
+ else
+ *(uint8 *)dst = samp ^ 0x80;
+}
+
+/* put a sample into an internal buffer
+ * in internal format
+ */
+static inline void
+mixer_PutSampleInt (void *dst, uint32 bpc, sint32 samp)
+{
+ if (bpc == 2)
+ *(sint16 *)dst = samp;
+ else
+ *(sint8 *)dst = samp;
+}
+
+/* get a sample from source */
+static float
+mixer_ResampleNone (mixer_Source *src, bool left)
+{
+ uint8 *d0 = src->nextqueued->data + src->pos;
+ src->pos += mixer_chansize;
+ (void) left; // satisfying compiler - unused arg
+ return mixer_GetSampleInt (d0, mixer_chansize);
+}
+
+/* get a resampled (up/down) sample from source (nearest neighbor) */
+static float
+mixer_ResampleNearest (mixer_Source *src, bool left)
+{
+ uint8 *d0 = src->nextqueued->data + src->pos;
+ d0 += mixer_SourceAdvance (src, left);
+ return mixer_GetSampleInt (d0, mixer_chansize);
+}
+
+/* get an upsampled sample from source (linear interpolation) */
+static float
+mixer_UpsampleLinear (mixer_Source *src, bool left)
+{
+ mixer_Buffer *curr = src->nextqueued;
+ mixer_Buffer *next = src->nextqueued->next;
+ uint8 *d0, *d1;
+ float s0, s1, t;
+
+ t = src->count / 65536.0f;
+ d0 = curr->data + src->pos;
+ d0 += mixer_SourceAdvance (src, left);
+
+ if (d0 + curr->sampsize >= curr->data + curr->size)
+ {
+ if (next && next->data && next->size >= curr->sampsize)
+ {
+ d1 = next->data;
+ if (!left)
+ d1 += mixer_chansize;
+ }
+ else
+ d1 = d0;
+ }
+ else
+ d1 = d0 + curr->sampsize;
+
+ s0 = mixer_GetSampleInt (d0, mixer_chansize);
+ s1 = mixer_GetSampleInt (d1, mixer_chansize);
+ return s0 + t * (s1 - s0);
+}
+
+/* get an upsampled sample from source (cubic interpolation) */
+static float
+mixer_UpsampleCubic (mixer_Source *src, bool left)
+{
+ mixer_Buffer *prev = src->prevqueued;
+ mixer_Buffer *curr = src->nextqueued;
+ mixer_Buffer *next = src->nextqueued->next;
+ uint8 *d0, *d1, *d2, *d3; /* prev, curr, next, next + 1 */
+ float t, t2, a, b, c, s0, s1, s2, s3;
+
+ t = src->count / 65536.0f;
+ t2 = t * t;
+ d1 = curr->data + src->pos;
+ d1 += mixer_SourceAdvance (src, left);
+
+ if (d1 - curr->sampsize < curr->data)
+ {
+ if (prev && prev->data && prev->size >= curr->sampsize)
+ {
+ d0 = prev->data + prev->size - curr->sampsize;
+ if (!left)
+ d0 += mixer_chansize;
+ }
+ else
+ d0 = d1;
+ }
+ else
+ d0 = d1 - curr->sampsize;
+
+ if (d1 + curr->sampsize >= curr->data + curr->size)
+ {
+ if (next && next->data && next->size >= curr->sampsize * 2)
+ {
+ d2 = next->data;
+ if (!left)
+ d2 += mixer_chansize;
+ d3 = d2 + curr->sampsize;
+ }
+ else
+ d2 = d3 = d1;
+ }
+ else
+ {
+ d2 = d1 + curr->sampsize;
+ if (d2 + curr->sampsize >= curr->data + curr->size)
+ {
+ if (next && next->data && next->size >= curr->sampsize)
+ {
+ d3 = next->data;
+ if (!left)
+ d3 += mixer_chansize;
+ }
+ else
+ d3 = d2;
+ }
+ else
+ d3 = d2 + curr->sampsize;
+ }
+
+ s0 = mixer_GetSampleInt (d0, mixer_chansize);
+ s1 = mixer_GetSampleInt (d1, mixer_chansize);
+ s2 = mixer_GetSampleInt (d2, mixer_chansize);
+ s3 = mixer_GetSampleInt (d3, mixer_chansize);
+
+ a = (3.0f * (s1 - s2) - s0 + s3) * 0.5f;
+ b = 2.0f * s2 + s0 - ((5.0f * s1 + s3) * 0.5f);
+ c = (s2 - s0) * 0.5f;
+
+ return a * t2 * t + b * t2 + c * t + s1;
+}
+
+/* get next sample from external buffer
+ * in internal format, while performing
+ * convertion if necessary
+ */
+static inline sint32
+mixer_GetConvSample (uint8 **psrc, uint32 bpc, uint32 flags)
+{
+ sint32 samp;
+
+ samp = mixer_GetSampleExt (*psrc, bpc);
+ *psrc += bpc;
+ if (flags & mixConvStereoDown)
+ {
+ /* downmix to mono - average up channels */
+ samp = (samp + mixer_GetSampleExt (*psrc, bpc)) / 2;
+ *psrc += bpc;
+ }
+
+ if (flags & mixConvSizeUp)
+ {
+ /* convert S8 to S16 */
+ samp <<= 8;
+ }
+ else if (flags & mixConvSizeDown)
+ {
+ /* convert S16 to S8
+ * if arithmetic shift is available to the compiler
+ * it will use it to optimize this
+ */
+ samp /= 0x100;
+ }
+
+ return samp;
+}
+
+/* put next sample into an internal buffer
+ * in internal format, while performing
+ * convertion if necessary
+ */
+static inline void
+mixer_PutConvSample (uint8 **pdst, uint32 bpc, uint32 flags, sint32 samp)
+{
+ mixer_PutSampleInt (*pdst, bpc, samp);
+ *pdst += bpc;
+ if (flags & mixConvStereoUp)
+ {
+ mixer_PutSampleInt (*pdst, bpc, samp);
+ *pdst += bpc;
+ }
+}
+
+/* resampling with respect to sample size only */
+static void
+mixer_ResampleFlat (mixer_Convertion *conv)
+{
+ mixer_ConvFlags flags = conv->flags;
+ uint8 *src = conv->srcdata;
+ uint8 *dst = conv->dstdata;
+ uint32 srcbpc = conv->srcbpc;
+ uint32 dstbpc = conv->dstbpc;
+ uint32 samples;
+
+ samples = conv->srcsamples;
+ if ( !(conv->flags & (mixConvStereoUp | mixConvStereoDown)))
+ samples *= conv->srcchans;
+
+ for (; samples; samples--)
+ {
+ sint32 samp;
+
+ samp = mixer_GetConvSample (&src, srcbpc, flags);
+ mixer_PutConvSample (&dst, dstbpc, flags, samp);
+ }
+}
diff --git a/src/libs/sound/mixer/mixer.h b/src/libs/sound/mixer/mixer.h
new file mode 100644
index 0000000..71e7878
--- /dev/null
+++ b/src/libs/sound/mixer/mixer.h
@@ -0,0 +1,274 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Mixer for low-level sound output drivers
+ */
+
+#ifndef LIBS_SOUND_MIXER_MIXER_H_
+#define LIBS_SOUND_MIXER_MIXER_H_
+
+#include "config.h"
+#include "types.h"
+#include "endian_uqm.h"
+
+/**
+ * The interface heavily influenced by OpenAL
+ * to the point where you should use OpenAL's
+ * documentation when programming the mixer.
+ * (some source properties are not supported)
+ *
+ * EXCEPTION: You may not queue the same buffer
+ * on more than one source
+ */
+
+#ifdef WORDS_BIGENDIAN
+# define MIX_IS_BIG_ENDIAN true
+# define MIX_WANT_BIG_ENDIAN true
+#else
+# define MIX_IS_BIG_ENDIAN false
+# define MIX_WANT_BIG_ENDIAN false
+#endif
+
+/**
+ * Mixer errors (see OpenAL errors)
+ */
+enum
+{
+ MIX_NO_ERROR = 0,
+ MIX_INVALID_NAME = 0xA001U,
+ MIX_INVALID_ENUM = 0xA002U,
+ MIX_INVALID_VALUE = 0xA003U,
+ MIX_INVALID_OPERATION = 0xA004U,
+ MIX_OUT_OF_MEMORY = 0xA005U,
+
+ MIX_DRIVER_FAILURE = 0xA101U
+};
+
+/**
+ * Source properties (see OpenAL)
+ */
+typedef enum
+{
+ MIX_POSITION = 0x1004,
+ MIX_LOOPING = 0x1007,
+ MIX_BUFFER = 0x1009,
+ MIX_GAIN = 0x100A,
+ MIX_SOURCE_STATE = 0x1010,
+
+ MIX_BUFFERS_QUEUED = 0x1015,
+ MIX_BUFFERS_PROCESSED = 0x1016
+
+} mixer_SourceProp;
+
+/**
+ * Source state information
+ */
+typedef enum
+{
+ MIX_INITIAL = 0,
+ MIX_STOPPED,
+ MIX_PLAYING,
+ MIX_PAUSED,
+
+} mixer_SourceState;
+
+/**
+ * Sound buffer properties
+ */
+typedef enum
+{
+ MIX_FREQUENCY = 0x2001,
+ MIX_BITS = 0x2002,
+ MIX_CHANNELS = 0x2003,
+ MIX_SIZE = 0x2004,
+ MIX_DATA = 0x2005
+
+} mixer_BufferProp;
+
+/**
+ * Buffer states: semi-private
+ */
+typedef enum
+{
+ MIX_BUF_INITIAL = 0,
+ MIX_BUF_FILLED,
+ MIX_BUF_QUEUED,
+ MIX_BUF_PLAYING,
+ MIX_BUF_PROCESSED
+
+} mixer_BufferState;
+
+/** Sound buffers: format specifier.
+ * bits 00..07: bytes per sample
+ * bits 08..15: channels
+ * bits 15..31: meaningless
+ */
+#define MIX_FORMAT_DUMMYID 0x00170000
+#define MIX_FORMAT_BPC(f) ((f) & 0xff)
+#define MIX_FORMAT_CHANS(f) (((f) >> 8) & 0xff)
+#define MIX_FORMAT_BPC_MAX 2
+#define MIX_FORMAT_CHANS_MAX 2
+#define MIX_FORMAT_MAKE(b, c) \
+ ( MIX_FORMAT_DUMMYID | ((b) & 0xff) | (((c) & 0xff) << 8) )
+
+#define MIX_FORMAT_SAMPSIZE(f) \
+ ( MIX_FORMAT_BPC(f) * MIX_FORMAT_CHANS(f) )
+
+typedef enum
+{
+ MIX_FORMAT_MONO8 = MIX_FORMAT_MAKE (1, 1),
+ MIX_FORMAT_STEREO8 = MIX_FORMAT_MAKE (1, 2),
+ MIX_FORMAT_MONO16 = MIX_FORMAT_MAKE (2, 1),
+ MIX_FORMAT_STEREO16 = MIX_FORMAT_MAKE (2, 2)
+
+} mixer_Format;
+
+typedef enum
+{
+ MIX_QUALITY_LOW = 0,
+ MIX_QUALITY_MEDIUM,
+ MIX_QUALITY_HIGH,
+ MIX_QUALITY_DEFAULT = MIX_QUALITY_MEDIUM,
+ MIX_QUALITY_COUNT
+
+} mixer_Quality;
+
+typedef enum
+{
+ MIX_NOFLAGS = 0,
+ MIX_FAKE_DATA = 1
+} mixer_Flags;
+
+/*************************************************
+ * Interface Types
+ */
+
+typedef intptr_t mixer_Object;
+typedef intptr_t mixer_IntVal;
+
+typedef struct _mixer_Source mixer_Source;
+
+typedef struct _mixer_Buffer
+{
+ uint32 magic;
+ bool locked;
+ mixer_BufferState state;
+ uint8 *data;
+ uint32 size;
+ uint32 sampsize;
+ uint32 high;
+ uint32 low;
+ float (* Resample) (mixer_Source *src, bool left);
+ /* original buffer values for OpenAL compat */
+ void* orgdata;
+ uint32 orgfreq;
+ uint32 orgsize;
+ uint32 orgchannels;
+ uint32 orgchansize;
+ /* next buffer in chain */
+ struct _mixer_Buffer *next;
+
+} mixer_Buffer;
+
+#define mixer_bufMagic 0x4258494DU /* MIXB in LSB */
+
+struct _mixer_Source
+{
+ uint32 magic;
+ bool locked;
+ mixer_SourceState state;
+ bool looping;
+ float gain;
+ uint32 cqueued;
+ uint32 cprocessed;
+ mixer_Buffer *firstqueued; /* first buf in the queue */
+ mixer_Buffer *nextqueued; /* next to play, or 0 */
+ mixer_Buffer *prevqueued; /* previously played */
+ mixer_Buffer *lastqueued; /* last in queue */
+ uint32 pos; /* position in current buffer */
+ uint32 count; /* fractional part of pos */
+
+ float samplecache;
+
+};
+
+#define mixer_srcMagic 0x5358494DU /* MIXS in LSB */
+
+/*************************************************
+ * General interface
+ */
+uint32 mixer_GetError (void);
+
+bool mixer_Init (uint32 frequency, uint32 format, mixer_Quality quality,
+ mixer_Flags flags);
+void mixer_Uninit (void);
+void mixer_MixChannels (void *userdata, uint8 *stream, sint32 len);
+void mixer_MixFake (void *userdata, uint8 *stream, sint32 len);
+
+/*************************************************
+ * Sources
+ */
+void mixer_GenSources (uint32 n, mixer_Object *psrcobj);
+void mixer_DeleteSources (uint32 n, mixer_Object *psrcobj);
+bool mixer_IsSource (mixer_Object srcobj);
+void mixer_Sourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal value);
+void mixer_Sourcef (mixer_Object srcobj, mixer_SourceProp pname,
+ float value);
+void mixer_Sourcefv (mixer_Object srcobj, mixer_SourceProp pname,
+ float *value);
+void mixer_GetSourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal *value);
+void mixer_GetSourcef (mixer_Object srcobj, mixer_SourceProp pname,
+ float *value);
+void mixer_SourceRewind (mixer_Object srcobj);
+void mixer_SourcePlay (mixer_Object srcobj);
+void mixer_SourcePause (mixer_Object srcobj);
+void mixer_SourceStop (mixer_Object srcobj);
+void mixer_SourceQueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj);
+void mixer_SourceUnqueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj);
+
+/*************************************************
+ * Buffers
+ */
+void mixer_GenBuffers (uint32 n, mixer_Object *pbufobj);
+void mixer_DeleteBuffers (uint32 n, mixer_Object *pbufobj);
+bool mixer_IsBuffer (mixer_Object bufobj);
+void mixer_GetBufferi (mixer_Object bufobj, mixer_BufferProp pname,
+ mixer_IntVal *value);
+void mixer_BufferData (mixer_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+
+/* Make sure the prop-value type is of suitable size
+ * it must be able to store both int and void*
+ * Adapted from SDL
+ * This will generate "negative subscript or subscript is too large"
+ * error during compile, if the actual size of a type is wrong
+ */
+#define MIX_COMPILE_TIME_ASSERT(name, x) \
+ typedef int mixer_dummy_##name [(x) * 2 - 1]
+
+MIX_COMPILE_TIME_ASSERT (mixer_Object,
+ sizeof(mixer_Object) >= sizeof(void*));
+MIX_COMPILE_TIME_ASSERT (mixer_IntVal,
+ sizeof(mixer_IntVal) >= sizeof(mixer_Object));
+
+#undef MIX_COMPILE_TIME_ASSERT
+
+#endif /* LIBS_SOUND_MIXER_MIXER_H_ */
diff --git a/src/libs/sound/mixer/mixerint.h b/src/libs/sound/mixer/mixerint.h
new file mode 100644
index 0000000..0605161
--- /dev/null
+++ b/src/libs/sound/mixer/mixerint.h
@@ -0,0 +1,110 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Mixer for low-level sound output drivers
+ * Internals
+ */
+
+#ifndef LIBS_SOUND_MIXER_MIXERINT_H_
+#define LIBS_SOUND_MIXER_MIXERINT_H_
+
+#include "port.h"
+#include "types.h"
+
+/*************************************************
+ * Internals
+ */
+
+/* Conversion info types and funcs */
+typedef enum
+{
+ mixConvNone = 0,
+ mixConvStereoUp = 1,
+ mixConvStereoDown = 2,
+ mixConvSizeUp = 4,
+ mixConvSizeDown = 8
+
+} mixer_ConvFlags;
+
+typedef struct
+{
+ uint32 srcfmt;
+ void *srcdata;
+ uint32 srcsize;
+ uint32 srcbpc; /* bytes/sample for 1 chan */
+ uint32 srcchans;
+ uint32 srcsamples;
+
+ uint32 dstfmt;
+ void *dstdata;
+ uint32 dstsize;
+ uint32 dstbpc; /* bytes/sample for 1 chan */
+ uint32 dstchans;
+ uint32 dstsamples;
+
+ mixer_ConvFlags flags;
+
+} mixer_Convertion;
+
+typedef struct
+{
+ float (* Upsample) (mixer_Source *src, bool left);
+ float (* Downsample) (mixer_Source *src, bool left);
+ float (* None) (mixer_Source *src, bool left);
+} mixer_Resampling;
+
+static void mixer_ConvertBuffer_internal (mixer_Convertion *conv);
+static void mixer_ResampleFlat (mixer_Convertion *conv);
+
+static inline sint32 mixer_GetSampleExt (void *src, uint32 bpc);
+static inline sint32 mixer_GetSampleInt (void *src, uint32 bpc);
+static inline void mixer_PutSampleInt (void *dst, uint32 bpc,
+ sint32 samp);
+static inline void mixer_PutSampleExt (void *dst, uint32 bpc,
+ sint32 samp);
+
+static float mixer_ResampleNone (mixer_Source *src, bool left);
+static float mixer_ResampleNearest (mixer_Source *src, bool left);
+static float mixer_UpsampleLinear (mixer_Source *src, bool left);
+static float mixer_UpsampleCubic (mixer_Source *src, bool left);
+
+/* Source manipulation */
+static void mixer_SourceUnqueueAll (mixer_Source *src);
+static void mixer_SourceStop_internal (mixer_Source *src);
+static void mixer_SourceRewind_internal (mixer_Source *src);
+static void mixer_SourceActivate (mixer_Source* src);
+static void mixer_SourceDeactivate (mixer_Source* src);
+
+static inline bool mixer_CheckBufferState (mixer_Buffer *buf,
+ const char* FuncName);
+
+/* Clipping boundaries */
+#define MIX_S16_MAX ((float) SINT16_MAX)
+#define MIX_S16_MIN ((float) SINT16_MIN)
+#define MIX_S8_MAX ((float) SINT8_MAX)
+#define MIX_S8_MIN ((float) SINT8_MIN)
+
+/* Channel gain adjustment for clipping reduction */
+#define MIX_GAIN_ADJ (0.75f)
+
+/* The Mixer */
+static inline bool mixer_SourceGetNextSample (mixer_Source *src,
+ float *psamp, bool left);
+static inline bool mixer_SourceGetFakeSample (mixer_Source *src,
+ float *psamp, bool left);
+static inline uint32 mixer_SourceAdvance (mixer_Source *src, bool left);
+
+#endif /* LIBS_SOUND_MIXER_MIXERINT_H_ */
diff --git a/src/libs/sound/mixer/nosound/Makeinfo b/src/libs/sound/mixer/nosound/Makeinfo
new file mode 100644
index 0000000..17f0c34
--- /dev/null
+++ b/src/libs/sound/mixer/nosound/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="audiodrv_nosound.c"
+uqm_HFILES="audiodrv_nosound.h"
diff --git a/src/libs/sound/mixer/nosound/audiodrv_nosound.c b/src/libs/sound/mixer/nosound/audiodrv_nosound.c
new file mode 100644
index 0000000..005bb44
--- /dev/null
+++ b/src/libs/sound/mixer/nosound/audiodrv_nosound.c
@@ -0,0 +1,410 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Nosound audio driver
+ */
+
+#include "audiodrv_nosound.h"
+#include "../../sndintrn.h"
+#include "libs/tasklib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include <stdlib.h>
+
+
+static Task PlaybackTask;
+static uint32 nosound_freq = 22050;
+
+static const audio_Driver noSound_Driver =
+{
+ noSound_Uninit,
+ noSound_GetError,
+ audio_DRIVER_NOSOUND,
+ {
+ /* Errors */
+ MIX_NO_ERROR,
+ MIX_INVALID_NAME,
+ MIX_INVALID_ENUM,
+ MIX_INVALID_VALUE,
+ MIX_INVALID_OPERATION,
+ MIX_OUT_OF_MEMORY,
+ MIX_DRIVER_FAILURE,
+
+ /* Source properties */
+ MIX_POSITION,
+ MIX_LOOPING,
+ MIX_BUFFER,
+ MIX_GAIN,
+ MIX_SOURCE_STATE,
+ MIX_BUFFERS_QUEUED,
+ MIX_BUFFERS_PROCESSED,
+
+ /* Source state information */
+ MIX_INITIAL,
+ MIX_STOPPED,
+ MIX_PLAYING,
+ MIX_PAUSED,
+
+ /* Sound buffer properties */
+ MIX_FREQUENCY,
+ MIX_BITS,
+ MIX_CHANNELS,
+ MIX_SIZE,
+ MIX_FORMAT_MONO16,
+ MIX_FORMAT_STEREO16,
+ MIX_FORMAT_MONO8,
+ MIX_FORMAT_STEREO8
+ },
+
+ /* Sources */
+ noSound_GenSources,
+ noSound_DeleteSources,
+ noSound_IsSource,
+ noSound_Sourcei,
+ noSound_Sourcef,
+ noSound_Sourcefv,
+ noSound_GetSourcei,
+ noSound_GetSourcef,
+ noSound_SourceRewind,
+ noSound_SourcePlay,
+ noSound_SourcePause,
+ noSound_SourceStop,
+ noSound_SourceQueueBuffers,
+ noSound_SourceUnqueueBuffers,
+
+ /* Buffers */
+ noSound_GenBuffers,
+ noSound_DeleteBuffers,
+ noSound_IsBuffer,
+ noSound_GetBufferi,
+ noSound_BufferData
+};
+
+
+/*
+ * Initialization
+ */
+
+sint32
+noSound_Init (audio_Driver *driver, sint32 flags)
+{
+ int i;
+ TFB_DecoderFormats formats =
+ {
+ 0, 0,
+ audio_FORMAT_MONO8, audio_FORMAT_STEREO8,
+ audio_FORMAT_MONO16, audio_FORMAT_STEREO16
+ };
+
+ log_add (log_Info, "Using nosound audio driver.");
+ log_add (log_Info, "Initializing mixer.");
+
+ if (!mixer_Init (nosound_freq, MIX_FORMAT_MAKE (1, 1),
+ MIX_QUALITY_LOW, MIX_FAKE_DATA))
+ {
+ log_add (log_Error, "Mixer initialization failed: %x",
+ mixer_GetError ());
+ return -1;
+ }
+ log_add (log_Info, "Mixer initialized.");
+
+ log_add (log_Info, "Initializing sound decoders.");
+ if (SoundDecoder_Init (flags, &formats))
+ {
+ log_add (log_Error, "Sound decoders initialization failed.");
+ mixer_Uninit ();
+ return -1;
+ }
+ log_add (log_Info, "Sound decoders initialized.");
+
+ *driver = noSound_Driver;
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ audio_GenSources (1, &soundSource[i].handle);
+ soundSource[i].stream_mutex = CreateMutex ("Nosound stream mutex", SYNC_CLASS_AUDIO);
+ }
+
+ if (InitStreamDecoder ())
+ {
+ log_add (log_Error, "Stream decoder initialization failed.");
+ // TODO: cleanup source mutexes [or is it "muti"? :) ]
+ SoundDecoder_Uninit ();
+ mixer_Uninit ();
+ return -1;
+ }
+
+ PlaybackTask = AssignTask (PlaybackTaskFunc, 1024,
+ "nosound audio playback");
+
+ return 0;
+}
+
+void
+noSound_Uninit (void)
+{
+ int i;
+
+ UninitStreamDecoder ();
+
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ if (soundSource[i].sample && soundSource[i].sample->decoder)
+ {
+ StopStream (i);
+ }
+ if (soundSource[i].sbuffer)
+ {
+ void *sbuffer = soundSource[i].sbuffer;
+ soundSource[i].sbuffer = NULL;
+ HFree (sbuffer);
+ }
+ DestroyMutex (soundSource[i].stream_mutex);
+
+ noSound_DeleteSources (1, &soundSource[i].handle);
+ }
+
+ if (PlaybackTask)
+ {
+ ConcludeTask (PlaybackTask);
+ PlaybackTask = 0;
+ }
+
+ mixer_Uninit ();
+ SoundDecoder_Uninit ();
+}
+
+
+/*
+ * Playback task
+ */
+
+int
+PlaybackTaskFunc (void *data)
+{
+ Task task = (Task)data;
+ uint8 *stream;
+ uint32 entryTime;
+ sint32 period, delay;
+ uint32 len = 2048;
+
+ stream = (uint8 *) HMalloc (len);
+ period = (sint32)((len / (double)nosound_freq) * ONE_SECOND);
+
+ while (!Task_ReadState (task, TASK_EXIT))
+ {
+ entryTime = GetTimeCounter ();
+ mixer_MixFake (NULL, stream, len);
+ delay = period - (GetTimeCounter () - entryTime);
+ if (delay > 0)
+ HibernateThread (delay);
+ }
+
+ HFree (stream);
+ FinishTask (task);
+ return 0;
+}
+
+
+/*
+ * General
+ */
+
+sint32
+noSound_GetError (void)
+{
+ sint32 value = mixer_GetError ();
+ switch (value)
+ {
+ case MIX_NO_ERROR:
+ return audio_NO_ERROR;
+ case MIX_INVALID_NAME:
+ return audio_INVALID_NAME;
+ case MIX_INVALID_ENUM:
+ return audio_INVALID_ENUM;
+ case MIX_INVALID_VALUE:
+ return audio_INVALID_VALUE;
+ case MIX_INVALID_OPERATION:
+ return audio_INVALID_OPERATION;
+ case MIX_OUT_OF_MEMORY:
+ return audio_OUT_OF_MEMORY;
+ default:
+ log_add (log_Debug, "noSound_GetError: unknown value %x",
+ value);
+ return audio_DRIVER_FAILURE;
+ break;
+ }
+}
+
+
+/*
+ * Sources
+ */
+
+void
+noSound_GenSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_GenSources (n, (mixer_Object *) psrcobj);
+}
+
+void
+noSound_DeleteSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_DeleteSources (n, (mixer_Object *) psrcobj);
+}
+
+bool
+noSound_IsSource (audio_Object srcobj)
+{
+ return mixer_IsSource ((mixer_Object) srcobj);
+}
+
+void
+noSound_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value)
+
+{
+ mixer_Sourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal) value);
+}
+
+void
+noSound_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value)
+{
+ mixer_Sourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+noSound_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_Sourcefv ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+noSound_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetSourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal *) value);
+ if (pname == MIX_SOURCE_STATE)
+ {
+ switch (*value)
+ {
+ case MIX_INITIAL:
+ *value = audio_INITIAL;
+ break;
+ case MIX_STOPPED:
+ *value = audio_STOPPED;
+ break;
+ case MIX_PLAYING:
+ *value = audio_PLAYING;
+ break;
+ case MIX_PAUSED:
+ *value = audio_PAUSED;
+ break;
+ default:
+ log_add (log_Debug, "noSound_GetSourcei(): unknown value %lx",
+ (long int) *value);
+ *value = audio_DRIVER_FAILURE;
+ }
+ }
+}
+
+void
+noSound_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_GetSourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+noSound_SourceRewind (audio_Object srcobj)
+{
+ mixer_SourceRewind ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourcePlay (audio_Object srcobj)
+{
+ mixer_SourcePlay ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourcePause (audio_Object srcobj)
+{
+ mixer_SourcePause ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourceStop (audio_Object srcobj)
+{
+ mixer_SourceStop ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceQueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+void
+noSound_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceUnqueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+
+/*
+ * Buffers
+ */
+
+void
+noSound_GenBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_GenBuffers (n, (mixer_Object *) pbufobj);
+}
+
+void
+noSound_DeleteBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_DeleteBuffers (n, (mixer_Object *) pbufobj);
+}
+
+bool
+noSound_IsBuffer (audio_Object bufobj)
+{
+ return mixer_IsBuffer ((mixer_Object) bufobj);
+}
+
+void
+noSound_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetBufferi ((mixer_Object) bufobj, (mixer_BufferProp) pname,
+ (mixer_IntVal *) value);
+}
+
+void
+noSound_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ mixer_BufferData ((mixer_Object) bufobj, format, data, size, freq);
+}
diff --git a/src/libs/sound/mixer/nosound/audiodrv_nosound.h b/src/libs/sound/mixer/nosound/audiodrv_nosound.h
new file mode 100644
index 0000000..173b706
--- /dev/null
+++ b/src/libs/sound/mixer/nosound/audiodrv_nosound.h
@@ -0,0 +1,69 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Nosound audio driver
+ */
+
+#ifndef LIBS_SOUND_MIXER_NOSOUND_AUDIODRV_NOSOUND_H_
+#define LIBS_SOUND_MIXER_NOSOUND_AUDIODRV_NOSOUND_H_
+
+#include "config.h"
+#include "libs/sound/sound.h"
+#include "libs/sound/mixer/mixer.h"
+
+
+/* Playback task */
+int PlaybackTaskFunc (void *data);
+
+/* General */
+sint32 noSound_Init (audio_Driver *driver, sint32 flags);
+void noSound_Uninit (void);
+sint32 noSound_GetError (void);
+
+/* Sources */
+void noSound_GenSources (uint32 n, audio_Object *psrcobj);
+void noSound_DeleteSources (uint32 n, audio_Object *psrcobj);
+bool noSound_IsSource (audio_Object srcobj);
+void noSound_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value);
+void noSound_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value);
+void noSound_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void noSound_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value);
+void noSound_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void noSound_SourceRewind (audio_Object srcobj);
+void noSound_SourcePlay (audio_Object srcobj);
+void noSound_SourcePause (audio_Object srcobj);
+void noSound_SourceStop (audio_Object srcobj);
+void noSound_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+void noSound_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+
+/* Buffers */
+void noSound_GenBuffers (uint32 n, audio_Object *pbufobj);
+void noSound_DeleteBuffers (uint32 n, audio_Object *pbufobj);
+bool noSound_IsBuffer (audio_Object bufobj);
+void noSound_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value);
+void noSound_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+
+#endif /* LIBS_SOUND_MIXER_NOSOUND_AUDIODRV_NOSOUND_H_ */
diff --git a/src/libs/sound/mixer/sdl/Makeinfo b/src/libs/sound/mixer/sdl/Makeinfo
new file mode 100644
index 0000000..64c22c0
--- /dev/null
+++ b/src/libs/sound/mixer/sdl/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="audiodrv_sdl.c"
+uqm_HFILES="audiodrv_sdl.h"
diff --git a/src/libs/sound/mixer/sdl/audiodrv_sdl.c b/src/libs/sound/mixer/sdl/audiodrv_sdl.c
new file mode 100644
index 0000000..7ef522e
--- /dev/null
+++ b/src/libs/sound/mixer/sdl/audiodrv_sdl.c
@@ -0,0 +1,486 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* SDL audio driver
+ */
+
+#include "audiodrv_sdl.h"
+#include "../../sndintrn.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include <stdlib.h>
+
+
+/* SDL2 wants to talk to a specific device. We'll let SDL1 use the same
+ * function names and just throw the device argument away. */
+#if SDL_MAJOR_VERSION > 1
+static SDL_AudioDeviceID dev;
+#else
+#define SDL_CloseAudioDevice(x) SDL_CloseAudio ()
+#define SDL_PauseAudioDevice(x, y) SDL_PauseAudio (y)
+#endif
+static const audio_Driver mixSDL_Driver =
+{
+ mixSDL_Uninit,
+ mixSDL_GetError,
+ audio_DRIVER_MIXSDL,
+ {
+ /* Errors */
+ MIX_NO_ERROR,
+ MIX_INVALID_NAME,
+ MIX_INVALID_ENUM,
+ MIX_INVALID_VALUE,
+ MIX_INVALID_OPERATION,
+ MIX_OUT_OF_MEMORY,
+ MIX_DRIVER_FAILURE,
+
+ /* Source properties */
+ MIX_POSITION,
+ MIX_LOOPING,
+ MIX_BUFFER,
+ MIX_GAIN,
+ MIX_SOURCE_STATE,
+ MIX_BUFFERS_QUEUED,
+ MIX_BUFFERS_PROCESSED,
+
+ /* Source state information */
+ MIX_INITIAL,
+ MIX_STOPPED,
+ MIX_PLAYING,
+ MIX_PAUSED,
+
+ /* Sound buffer properties */
+ MIX_FREQUENCY,
+ MIX_BITS,
+ MIX_CHANNELS,
+ MIX_SIZE,
+ MIX_FORMAT_MONO16,
+ MIX_FORMAT_STEREO16,
+ MIX_FORMAT_MONO8,
+ MIX_FORMAT_STEREO8
+ },
+
+ /* Sources */
+ mixSDL_GenSources,
+ mixSDL_DeleteSources,
+ mixSDL_IsSource,
+ mixSDL_Sourcei,
+ mixSDL_Sourcef,
+ mixSDL_Sourcefv,
+ mixSDL_GetSourcei,
+ mixSDL_GetSourcef,
+ mixSDL_SourceRewind,
+ mixSDL_SourcePlay,
+ mixSDL_SourcePause,
+ mixSDL_SourceStop,
+ mixSDL_SourceQueueBuffers,
+ mixSDL_SourceUnqueueBuffers,
+
+ /* Buffers */
+ mixSDL_GenBuffers,
+ mixSDL_DeleteBuffers,
+ mixSDL_IsBuffer,
+ mixSDL_GetBufferi,
+ mixSDL_BufferData
+};
+
+
+static void audioCallback (void *userdata, Uint8 *stream, int len);
+
+/*
+ * Initialization
+ */
+
+sint32
+mixSDL_Init (audio_Driver *driver, sint32 flags)
+{
+ int i;
+ SDL_AudioSpec desired, obtained;
+ mixer_Quality quality;
+ TFB_DecoderFormats formats =
+ {
+ MIX_IS_BIG_ENDIAN, MIX_WANT_BIG_ENDIAN,
+ audio_FORMAT_MONO8, audio_FORMAT_STEREO8,
+ audio_FORMAT_MONO16, audio_FORMAT_STEREO16
+ };
+
+ log_add (log_Info, "Initializing SDL audio subsystem.");
+ if ((SDL_InitSubSystem(SDL_INIT_AUDIO)) == -1)
+ {
+ log_add (log_Error, "Couldn't initialize audio subsystem: %s",
+ SDL_GetError());
+ return -1;
+ }
+ log_add (log_Info, "SDL audio subsystem initialized.");
+
+ if (flags & audio_QUALITY_HIGH)
+ {
+ quality = MIX_QUALITY_HIGH;
+ desired.freq = 44100;
+ desired.samples = 4096;
+ }
+ else if (flags & audio_QUALITY_LOW)
+ {
+ quality = MIX_QUALITY_LOW;
+#ifdef __SYMBIAN32__
+ desired.freq = 11025;
+ desired.samples = 4096;
+#else
+ desired.freq = 22050;
+ desired.samples = 2048;
+#endif
+ }
+ else
+ {
+ quality = MIX_QUALITY_DEFAULT;
+ desired.freq = 44100;
+ desired.samples = 4096;
+ }
+
+ desired.format = AUDIO_S16SYS;
+ desired.channels = 2;
+ desired.callback = audioCallback;
+
+ log_add (log_Info, "Opening SDL audio device.");
+#if SDL_MAJOR_VERSION > 1
+ dev = SDL_OpenAudioDevice (NULL, 0, &desired, &obtained,
+ SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE
+#ifdef SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+ | SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+#endif
+ );
+ if (dev != 0 && obtained.channels != 1 && obtained.channels != 2)
+ {
+ /* Try again without SDL_AUDIO_ALLOW_CHANNELS_CHANGE
+ * in case the device only supports >2 channels for some
+ * reason */
+ SDL_CloseAudioDevice (dev);
+ dev = SDL_OpenAudioDevice (NULL, 0, &desired, &obtained,
+ SDL_AUDIO_ALLOW_FREQUENCY_CHANGE
+#ifdef SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+ | SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+#endif
+ );
+ }
+ if (dev == 0)
+#else
+ if (SDL_OpenAudio (&desired, &obtained) < 0)
+#endif
+ {
+ log_add (log_Error, "Unable to open audio device: %s",
+ SDL_GetError ());
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+ if (obtained.format != desired.format ||
+ (obtained.channels != 1 && obtained.channels != 2))
+ {
+ log_add (log_Error, "Unable to obtain desired audio format.");
+ SDL_CloseAudio ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+
+ {
+#if SDL_MAJOR_VERSION == 1
+ char devicename[256];
+ SDL_AudioDriverName (devicename, sizeof (devicename));
+#else
+ const char *devicename = SDL_GetCurrentAudioDriver ();
+#endif
+ log_add (log_Info, " using %s at %d Hz 16 bit %s, "
+ "%d samples audio buffer",
+ devicename, obtained.freq,
+ obtained.channels > 1 ? "stereo" : "mono",
+ obtained.samples);
+ }
+
+ log_add (log_Info, "Initializing mixer.");
+ if (!mixer_Init (obtained.freq, MIX_FORMAT_MAKE (2, obtained.channels),
+ quality, 0))
+ {
+ log_add (log_Error, "Mixer initialization failed: %x",
+ mixer_GetError ());
+ SDL_CloseAudioDevice (dev);
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+ log_add (log_Info, "Mixer initialized.");
+
+ log_add (log_Info, "Initializing sound decoders.");
+ if (SoundDecoder_Init (flags, &formats))
+ {
+ log_add (log_Error, "Sound decoders initialization failed.");
+ SDL_CloseAudioDevice (dev);
+ mixer_Uninit ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+ log_add (log_Info, "Sound decoders initialized.");
+
+ *driver = mixSDL_Driver;
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ audio_GenSources (1, &soundSource[i].handle);
+ soundSource[i].stream_mutex = CreateMutex ("MixSDL stream mutex", SYNC_CLASS_AUDIO);
+ }
+
+ if (InitStreamDecoder ())
+ {
+ log_add (log_Error, "Stream decoder initialization failed.");
+ // TODO: cleanup source mutexes [or is it "muti"? :) ]
+ SDL_CloseAudioDevice (dev);
+ SoundDecoder_Uninit ();
+ mixer_Uninit ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+
+ atexit (unInitAudio);
+
+ SetSFXVolume (sfxVolumeScale);
+ SetSpeechVolume (speechVolumeScale);
+ SetMusicVolume ((COUNT)musicVolume);
+
+ SDL_PauseAudioDevice (dev, 0);
+
+ return 0;
+}
+
+void
+mixSDL_Uninit (void)
+{
+ int i;
+
+ UninitStreamDecoder ();
+
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ if (soundSource[i].sample && soundSource[i].sample->decoder)
+ {
+ StopStream (i);
+ }
+ if (soundSource[i].sbuffer)
+ {
+ void *sbuffer = soundSource[i].sbuffer;
+ soundSource[i].sbuffer = NULL;
+ HFree (sbuffer);
+ }
+ DestroyMutex (soundSource[i].stream_mutex);
+ soundSource[i].stream_mutex = 0;
+
+ mixSDL_DeleteSources (1, &soundSource[i].handle);
+ }
+
+ SDL_CloseAudioDevice (dev);
+ mixer_Uninit ();
+ SoundDecoder_Uninit ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+}
+
+static void
+audioCallback (void *userdata, Uint8 *stream, int len)
+{
+ mixer_MixChannels (userdata, stream, len);
+}
+
+/*
+ * General
+ */
+
+sint32
+mixSDL_GetError (void)
+{
+ sint32 value = mixer_GetError ();
+ switch (value)
+ {
+ case MIX_NO_ERROR:
+ return audio_NO_ERROR;
+ case MIX_INVALID_NAME:
+ return audio_INVALID_NAME;
+ case MIX_INVALID_ENUM:
+ return audio_INVALID_ENUM;
+ case MIX_INVALID_VALUE:
+ return audio_INVALID_VALUE;
+ case MIX_INVALID_OPERATION:
+ return audio_INVALID_OPERATION;
+ case MIX_OUT_OF_MEMORY:
+ return audio_OUT_OF_MEMORY;
+ default:
+ log_add (log_Debug, "mixSDL_GetError: unknown value %x", value);
+ return audio_DRIVER_FAILURE;
+ break;
+ }
+}
+
+
+/*
+ * Sources
+ */
+
+void
+mixSDL_GenSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_GenSources (n, (mixer_Object *) psrcobj);
+}
+
+void
+mixSDL_DeleteSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_DeleteSources (n, (mixer_Object *) psrcobj);
+}
+
+bool
+mixSDL_IsSource (audio_Object srcobj)
+{
+ return mixer_IsSource ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value)
+
+{
+ mixer_Sourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal) value);
+}
+
+void
+mixSDL_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value)
+{
+ mixer_Sourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+mixSDL_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_Sourcefv ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+mixSDL_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetSourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal *) value);
+ if (pname == MIX_SOURCE_STATE)
+ {
+ switch (*value)
+ {
+ case MIX_INITIAL:
+ *value = audio_INITIAL;
+ break;
+ case MIX_STOPPED:
+ *value = audio_STOPPED;
+ break;
+ case MIX_PLAYING:
+ *value = audio_PLAYING;
+ break;
+ case MIX_PAUSED:
+ *value = audio_PAUSED;
+ break;
+ default:
+ *value = audio_DRIVER_FAILURE;
+ }
+ }
+}
+
+void
+mixSDL_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_GetSourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+mixSDL_SourceRewind (audio_Object srcobj)
+{
+ mixer_SourceRewind ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourcePlay (audio_Object srcobj)
+{
+ mixer_SourcePlay ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourcePause (audio_Object srcobj)
+{
+ mixer_SourcePause ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourceStop (audio_Object srcobj)
+{
+ mixer_SourceStop ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceQueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+void
+mixSDL_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceUnqueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+
+/*
+ * Buffers
+ */
+
+void
+mixSDL_GenBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_GenBuffers (n, (mixer_Object *) pbufobj);
+}
+
+void
+mixSDL_DeleteBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_DeleteBuffers (n, (mixer_Object *) pbufobj);
+}
+
+bool
+mixSDL_IsBuffer (audio_Object bufobj)
+{
+ return mixer_IsBuffer ((mixer_Object) bufobj);
+}
+
+void
+mixSDL_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetBufferi ((mixer_Object) bufobj, (mixer_BufferProp) pname,
+ (mixer_IntVal *) value);
+}
+
+void
+mixSDL_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ mixer_BufferData ((mixer_Object) bufobj, format, data, size, freq);
+}
diff --git a/src/libs/sound/mixer/sdl/audiodrv_sdl.h b/src/libs/sound/mixer/sdl/audiodrv_sdl.h
new file mode 100644
index 0000000..d74301b
--- /dev/null
+++ b/src/libs/sound/mixer/sdl/audiodrv_sdl.h
@@ -0,0 +1,66 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* SDL audio driver
+ */
+
+#ifndef LIBS_SOUND_MIXER_SDL_AUDIODRV_SDL_H_
+#define LIBS_SOUND_MIXER_SDL_AUDIODRV_SDL_H_
+
+#include "port.h"
+#include "libs/sound/sound.h"
+#include "libs/sound/mixer/mixer.h"
+#include SDL_INCLUDE(SDL.h)
+
+/* General */
+sint32 mixSDL_Init (audio_Driver *driver, sint32 flags);
+void mixSDL_Uninit (void);
+sint32 mixSDL_GetError (void);
+
+/* Sources */
+void mixSDL_GenSources (uint32 n, audio_Object *psrcobj);
+void mixSDL_DeleteSources (uint32 n, audio_Object *psrcobj);
+bool mixSDL_IsSource (audio_Object srcobj);
+void mixSDL_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value);
+void mixSDL_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value);
+void mixSDL_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void mixSDL_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value);
+void mixSDL_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void mixSDL_SourceRewind (audio_Object srcobj);
+void mixSDL_SourcePlay (audio_Object srcobj);
+void mixSDL_SourcePause (audio_Object srcobj);
+void mixSDL_SourceStop (audio_Object srcobj);
+void mixSDL_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+void mixSDL_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+
+/* Buffers */
+void mixSDL_GenBuffers (uint32 n, audio_Object *pbufobj);
+void mixSDL_DeleteBuffers (uint32 n, audio_Object *pbufobj);
+bool mixSDL_IsBuffer (audio_Object bufobj);
+void mixSDL_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value);
+void mixSDL_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+
+#endif /* LIBS_SOUND_MIXER_SDL_AUDIODRV_SDL_H_ */