- the Linux sound modules (ALSA and OSS) are now write-based, instead of
authormolivier <molivier@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 4 Jun 2006 10:57:24 +0000 (10:57 +0000)
committermolivier <molivier@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 4 Jun 2006 10:57:24 +0000 (10:57 +0000)
mmap-based. It should fix the problems with inboard sound cards
- added 3 cvars controlling the sound output format: snd_speed,
snd_channels, and snd_width (default values: 48000, 2, and 2
respectively). They're saved in the config file.
- the checks for command line options and environment variables
modifying the sound output format are now common to all modules. The
command line options supported are: -sndmono, -sndstereo, -sndquad,
-sndspeed, and -sndbits. The environment variables supported are:
QUAKE_SOUND_CHANNELS, QUAKE_SOUND_SPEED, and QUAKE_SOUND_SAMPLEBITS.
- added a (still dumb) function to figure out a better sound format when
the initialization of the sound card failed
- sound modules can now suggest a sound format if the initialization of
the sound card failed
- you can now do a "snd_restart" while having modified snd_width and/or
snd_channel. Doing a snd_restart with a modified snd_speed isn't yet
supported, it will fall back to the previous speed
- The WGL video module was blocking the sound output when the
application window lost the focus. Now, this code is shared and the AGL,
GLX and SDL video modules implement such a mecanism
- A lot of minor fixes, changes, factorizations and rewrites all over
the sound engine code and its modules
- added CeilPowerOf2() in the math library
- fixed BSDmakefile for NetBSD

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6415 d7cf8633-e32d-0410-b094-e92efae38249

24 files changed:
BSDmakefile
mathlib.c
mathlib.h
snd_alsa.c
snd_bsd.c
snd_coreaudio.c
snd_main.c
snd_main.h
snd_mem.c
snd_mix.c
snd_null.c
snd_ogg.c
snd_ogg.h
snd_oss.c
snd_sdl.c
snd_wav.c
snd_wav.h
snd_win.c
sound.h
sys_win.c
vid_agl.c
vid_glx.c
vid_sdl.c
vid_wgl.c

index fb60cb9..6d86a29 100644 (file)
@@ -69,7 +69,7 @@ LIB_SOUND=$(LIB_SND_BSD)
 
 ##### BSD Make specific definitions #####
 
-MAKE:=$(MAKE)
+MAKE:=$(MAKE) -f BSDmakefile
 
 DO_LD=$(CC) -o $@ $> $(LDFLAGS)
 
index 36e858d..eaa1a76 100644 (file)
--- a/mathlib.c
+++ b/mathlib.c
@@ -254,6 +254,24 @@ void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point,
               + (t0 * vr[2] + t1 * vu[2] + vf[2] * vf[2]) * point[2];
 }
 
+/*-----------------------------------------------------------------*/
+
+// returns the smallest integer greater than or equal to "value", or 0 if "value" is too big
+unsigned int CeilPowerOf2(unsigned int value)
+{
+       unsigned int ceilvalue;
+
+       if (value > (1U << (sizeof(int) * 8 - 1)))
+               return 0;
+
+       ceilvalue = 1;
+       while (ceilvalue < value)
+               ceilvalue <<= 1;
+
+       return ceilvalue;
+}
+
+
 /*-----------------------------------------------------------------*/
 
 
index c56df89..61ada99 100644 (file)
--- a/mathlib.h
+++ b/mathlib.h
@@ -53,9 +53,16 @@ extern vec3_t vec3_origin;
 #define lhrandom(MIN,MAX) (((double)rand() / RAND_MAX) * ((MAX)-(MIN)) + (MIN))
 
 #define invpow(base,number) (log(number) / log(base))
+
+// returns log base 2 of "n" (WARNING: "n" MUST be a power of 2!)
 #define log2i(n) ((((n) & 0xAAAAAAAA) != 0 ? 1 : 0) | (((n) & 0xCCCCCCCC) != 0 ? 2 : 0) | (((n) & 0xF0F0F0F0) != 0 ? 4 : 0) | (((n) & 0xFF00FF00) != 0 ? 8 : 0) | (((n) & 0xFFFF0000) != 0 ? 16 : 0))
+
+// TOCHECK: what is this function supposed to do?
 #define bit2i(n) log2i((n) << 1)
 
+// returns the smallest integer greater than or equal to "value", or 0 if "value" is too big
+unsigned int CeilPowerOf2(unsigned int value);
+
 #define DEG2RAD(a) ((a) * ((float) M_PI / 180.0f))
 #define RAD2DEG(a) ((a) * (180.0f / (float) M_PI))
 #define ANGLEMOD(a) (((int) ((a) * (65536.0f / 360.0f)) & 65535) * (360.0f / 65536.0f))
index 31f5c1e..2e6b8f3 100644 (file)
@@ -1,10 +1,5 @@
 /*
-       snd_alsa.c
-
-       Support for the ALSA 1.0.1 sound driver
-
-       Copyright (C) 1999,2000  contributors of the QuakeForge project
-       Please see the file "AUTHORS" for a list of contributors
+       Copyright (C) 2006  Mathieu Olivier
 
        This program is free software; you can redistribute it and/or
        modify it under the terms of the GNU General Public License
 
 */
 
+// ALSA module, used by Linux
+
+
 #include <alsa/asoundlib.h>
 
 #include "quakedef.h"
 #include "snd_main.h"
 
-static snd_pcm_uframes_t buffer_size;
 
-static const char  *pcmname = NULL;
-static snd_pcm_t   *pcm;
+#define NB_PERIODS 2
+
+static snd_pcm_t* pcm_handle = NULL;
+static snd_pcm_sframes_t expected_delay = 0;
+static unsigned int alsasoundtime;
+
+
+/*
+====================
+SndSys_Init
 
-qboolean SNDDMA_Init (void)
+Create "snd_renderbuffer" with the proper sound format if the call is successful
+May return a suggested format if the requested format isn't available
+====================
+*/
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
 {
-       int                                     err, i, j;
-       int                                     width;
-       int                                     channels;
-       unsigned int            rate;
-       snd_pcm_hw_params_t     *hw;
-       snd_pcm_sw_params_t     *sw;
-       snd_pcm_uframes_t       frag_size;
-
-       snd_pcm_hw_params_alloca (&hw);
-       snd_pcm_sw_params_alloca (&sw);
-
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndbits <number> sets sound precision to 8 or 16 bit (email me if you want others added)
-       width = 2;
-       if ((i=COM_CheckParm("-sndbits")) != 0)
+       const char* pcm_name;
+       int i, err;
+       snd_pcm_hw_params_t* hw_params = NULL;
+       snd_pcm_format_t snd_pcm_format;
+       snd_pcm_uframes_t buffer_size;
+
+       Con_Print ("SndSys_Init: using the ALSA module\n");
+
+       // Check the requested sound format
+       if (requested->width < 1 || requested->width > 2)
+       {
+               Con_Printf ("SndSys_Init: invalid sound width (%hu)\n",
+                                       requested->width);
+
+               if (suggested != NULL)
+               {
+                       memcpy (suggested, requested, sizeof (suggested));
+
+                       if (requested->width < 1)
+                               suggested->width = 1;
+                       else
+                               suggested->width = 2;
+
+                       Con_Printf ("SndSys_Init: suggesting sound width = %hu\n",
+                                               suggested->width);
+               }
+               
+               return false;
+    }
+       
+       if (pcm_handle != NULL)
+       {
+               Con_Print ("SndSys_Init: WARNING: Init called before Shutdown!\n");
+               SndSys_Shutdown ();
+       }
+       
+       // Determine the name of the PCM handle we'll use
+       switch (requested->channels)
+       {
+               case 4:
+                       pcm_name = "surround40";
+                       break;
+               case 6:
+                       pcm_name = "surround51";
+                       break;
+               case 8:
+                       pcm_name = "surround71";
+                       break;
+               default:
+                       pcm_name = "default";
+                       break;
+       }
+// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
+       i = COM_CheckParm ("-sndpcm");
+       if (i != 0 && i < com_argc - 1)
+               pcm_name = com_argv[i + 1];
+
+       // Open the audio device
+       Con_DPrintf ("SndSys_Init: PCM device is \"%s\"\n", pcm_name);
+       err = snd_pcm_open (&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+       if (err != 0)
+       {
+               Con_Printf ("SndSys_Init: can't open audio device \"%s\" (%s)\n",
+                                       pcm_name, snd_strerror (err));
+               return false;
+       }
+       
+       // Allocate the hardware parameters
+       err = snd_pcm_hw_params_malloc (&hw_params);
+       if (err != 0)
+       {
+               Con_Printf ("SndSys_Init: can't allocate hardware parameters (%s)\n",
+                                       snd_strerror (err));
+               goto init_error;
+       }
+       err = snd_pcm_hw_params_any (pcm_handle, hw_params);
+       if (err != 0)
        {
-               j = atoi(com_argv[i+1]);
-               if (j == 16 || j == 8)
-                       width = j / 8;
-               else
-                       Con_Printf("Error: invalid sample bits: %d\n", j);
+               Con_Printf ("SndSys_Init: can't initialize hardware parameters (%s)\n",
+                                       snd_strerror (err));
+               goto init_error;
        }
 
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
-       rate = 44100;
-       if ((i=COM_CheckParm("-sndspeed")) != 0)
+       // Set the access type
+       err = snd_pcm_hw_params_set_access (pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
+       if (err != 0)
        {
-               j = atoi(com_argv[i+1]);
-               if (j >= 1)
-                       rate = j;
-               else
-                       Con_Printf("Error: invalid sample rate: %d\n", rate);
+               Con_Printf ("SndSys_Init: can't set access type (%s)\n",
+                                       snd_strerror (err));
+               goto init_error;
        }
 
-       for (channels = 8;channels >= 1;channels--)
+       // Set the sound width
+       if (requested->width == 1)
+               snd_pcm_format = SND_PCM_FORMAT_U8;
+       else
+               snd_pcm_format = SND_PCM_FORMAT_S16;
+       err = snd_pcm_hw_params_set_format (pcm_handle, hw_params, snd_pcm_format);
+       if (err != 0)
        {
-               if ((channels & 1) && channels != 1)
-                       continue;
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndmono sets sound output to mono
-               if ((i=COM_CheckParm("-sndmono")) != 0)
-                       if (channels != 1)
-                               continue;
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndstereo sets sound output to stereo
-               if ((i=COM_CheckParm("-sndstereo")) != 0)
-                       if (channels != 2)
-                               continue;
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndquad sets sound output to 4 channel surround
-               if ((i=COM_CheckParm("-sndquad")) != 0)
-                       if (channels != 4)
-                               continue;
+               Con_Printf ("SndSys_Init: can't set sound width to %hu (%s)\n",
+                                       requested->width, snd_strerror (err));
+               goto init_error;
+       }
 
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
-               if (channels == 8)
-                       pcmname = "surround71";
-               else if (channels == 6)
-                       pcmname = "surround51";
-               else if (channels == 4)
-                       pcmname = "surround40";
-               else
-                       pcmname = "default";
-               if ((i=COM_CheckParm("-sndpcm"))!=0)
-                       pcmname = com_argv[i+1];
-
-               Con_Printf ("ALSA: Trying PCM %s.\n", pcmname);
-
-               err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
-               if (0 > err)
-               {
-                       Con_Printf ("Error: audio open error: %s\n", snd_strerror (err));
-                       continue;
-               }
+       // Set the sound channels
+       err = snd_pcm_hw_params_set_channels (pcm_handle, hw_params, requested->channels);
+       if (err != 0)
+       {
+               Con_Printf ("SndSys_Init: can't set sound channels to %hu (%s)\n",
+                                       requested->channels, snd_strerror (err));
+               goto init_error;
+       }
 
-               err = snd_pcm_hw_params_any (pcm, hw);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: error setting hw_params_any. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
+       // Set the sound speed
+       err = snd_pcm_hw_params_set_rate (pcm_handle, hw_params, requested->speed, 0);
+       if (err != 0)
+       {
+               Con_Printf ("SndSys_Init: can't set sound speed to %u (%s)\n",
+                                       requested->speed, snd_strerror (err));
+               goto init_error;
+       }
 
-               err = snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: Failure to set interleaved mmap PCM access. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
+       buffer_size = requested->speed / 5;
+       err = snd_pcm_hw_params_set_buffer_size_near (pcm_handle, hw_params, &buffer_size);
+       if (err != 0)
+       {
+               Con_Printf ("SndSys_Init: can't set sound buffer size to %lu (%s)\n",
+                                       buffer_size, snd_strerror (err));
+               goto init_error;
+       }
 
-               err = snd_pcm_hw_params_set_format (pcm, hw, width == 1 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: desired bits %i not supported by driver.  %s\n", width * 8, snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
+       buffer_size /= NB_PERIODS;
+       err = snd_pcm_hw_params_set_period_size_near(pcm_handle, hw_params, &buffer_size, 0);
+       if (err != 0)
+       {
+               Con_Printf ("SndSys_Init: can't set sound period size to %lu (%s)\n",
+                                       buffer_size, snd_strerror (err));
+               goto init_error;
+       }
 
-               err = snd_pcm_hw_params_set_channels (pcm, hw, channels);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: no usable channels. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
+       err = snd_pcm_hw_params (pcm_handle, hw_params);
+       if (err != 0)
+       {
+               Con_Printf ("SndSys_Init: can't set hardware parameters (%s)\n",
+                                       snd_strerror (err));
+               goto init_error;
+       }
 
-               err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: desired rate %i not supported by driver. %s\n", rate, snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
+       snd_pcm_hw_params_free (hw_params);
 
-               frag_size = 64 * width * rate / 11025;
-               err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: unable to set period size near %i. %s\n", (int) frag_size, snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
-               err = snd_pcm_hw_params (pcm, hw);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: unable to install hw params: %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
-               err = snd_pcm_sw_params_current (pcm, sw);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: unable to determine current sw params. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
-               err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: unable to set playback threshold. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
-               err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: unable to set playback stop threshold. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
-               err = snd_pcm_sw_params (pcm, sw);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: unable to install sw params. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
+       expected_delay = 0;
+       alsasoundtime = 0;
+       
+       return true;
 
-               err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
-               if (0 > err)
-               {
-                       Con_Printf ("ALSA: unable to get buffer size. %s\n", snd_strerror (err));
-                       snd_pcm_close (pcm);
-                       continue;
-               }
 
-               memset( (void*) shm, 0, sizeof(*shm) );
-               shm->format.channels = channels;
-               shm->format.width = width;
-               shm->format.speed = rate;
-               shm->samplepos = 0;
-               shm->sampleframes = buffer_size;
-               shm->samples = shm->sampleframes * shm->format.channels;
-               SNDDMA_GetDMAPos ();            // sets shm->buffer
+// It's not very clean, but it avoids a lot of duplicated code.
+init_error:
+       
+       if (hw_params != NULL)
+               snd_pcm_hw_params_free (hw_params);
+       SndSys_Shutdown ();
+       return false;
+}
+
 
-               return true;
+/*
+====================
+SndSys_Shutdown
+
+Stop the sound card, delete "snd_renderbuffer" and free its other resources
+====================
+*/
+void SndSys_Shutdown (void)
+{
+       if (pcm_handle != NULL)
+       {
+               snd_pcm_close(pcm_handle);
+               pcm_handle = NULL;
+       }
+
+       if (snd_renderbuffer != NULL)
+       {
+               Mem_Free(snd_renderbuffer->ring);
+               Mem_Free(snd_renderbuffer);
+               snd_renderbuffer = NULL;
        }
-       return false;
 }
 
-int SNDDMA_GetDMAPos (void)
+
+/*
+====================
+SndSys_Recover
+
+Try to recover from errors
+====================
+*/
+static qboolean SndSys_Recover (int err_num)
 {
-       const snd_pcm_channel_area_t *areas;
-       snd_pcm_uframes_t offset;
-       snd_pcm_uframes_t nframes = shm->sampleframes;
+       int err;
+
+       // We can only do something on underrun ("broken pipe") errors
+       if (err_num != -EPIPE)
+               return false;
+                       
+       err = snd_pcm_prepare (pcm_handle);
+       if (err != 0)
+       {
+               Con_DPrintf ("SndSys_Recover: unable to recover (%s)\n",
+                                        snd_strerror (err));
 
-       if (!shm)
-               return 0;
+               // TOCHECK: should we stop the playback ?
+
+               return false;
+       }
+       
+       Con_DPrint ("SndSys_Recover: recovered successfully\n");
 
-       snd_pcm_avail_update (pcm);
-       snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
-       offset *= shm->format.channels;
-       nframes *= shm->format.channels;
-       shm->samplepos = offset;
-       shm->buffer = (unsigned char *)areas->addr;
-       return shm->samplepos;
+       return true;
 }
 
-void SNDDMA_Shutdown (void)
+
+/*
+====================
+SndSys_Write
+====================
+*/
+static snd_pcm_sframes_t SndSys_Write (const unsigned char* buffer, unsigned int nbframes)
 {
-       snd_pcm_close (pcm);
+       snd_pcm_sframes_t written;
+
+       written = snd_pcm_writei (pcm_handle, buffer, nbframes);
+       if (written < 0)
+       {
+               Con_Printf ("SndSys_Write: audio write returned %ld (%s)!\n",
+                                       written, snd_strerror (written));
+
+               if (SndSys_Recover (written))
+               {
+                       written = snd_pcm_writei (pcm_handle, buffer, nbframes);
+                       if (written < 0)
+                               Con_Printf ("SndSys_Write: audio write failed again (error %ld: %s)!\n",
+                                                       written, snd_strerror (written));
+               }
+       }
+       
+       return written;
 }
 
+
 /*
-       SNDDMA_Submit
+====================
+SndSys_Submit
 
-       Send sound to device if buffer isn't really the dma buffer
+Submit the contents of "snd_renderbuffer" to the sound card
+====================
 */
-void SNDDMA_Submit (void)
+void SndSys_Submit (void)
 {
-       int                     state;
-       int                     count = paintedtime - soundtime;
-       const snd_pcm_channel_area_t *areas;
-       snd_pcm_uframes_t nframes;
-       snd_pcm_uframes_t offset;
+       unsigned int startoffset, factor;
+       snd_pcm_uframes_t limit, nbframes;
+       snd_pcm_sframes_t written;
+       
+       if (pcm_handle == NULL ||
+               snd_renderbuffer->startframe == snd_renderbuffer->endframe)
+               return;
+
+       startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
+       factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+       limit = snd_renderbuffer->maxframes - startoffset;
+       nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+//Con_DPrintf(">> SndSys_Submit: startframe=%u, endframe=%u (%u frames), maxframes=%u, startoffset=%u\n",
+//                     snd_renderbuffer->startframe, snd_renderbuffer->endframe,
+//                     nbframes, snd_renderbuffer->maxframes, startoffset);
+
+       if (nbframes > limit)
+       {
+//Con_DPrintf(">> SndSys_Submit: 2 phases-submit\n");
+               written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit);
+               if (written < 0)
+                       return;
+               snd_renderbuffer->startframe += written;
+               expected_delay += written;
+
+//Con_DPrintf(">> SndSys_Submit: %ld/%ld frames written\n", written, limit);
+               if ((snd_pcm_uframes_t)written != limit)
+                       return;
+               
+               nbframes -= limit;
+               startoffset = 0;
+       }
+//else Con_DPrintf(">> SndSys_Submit: 1 phase-submit\n");
+
+       written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes);
+       if (written < 0)
+               return;
+//Con_DPrintf(">> SndSys_Submit: %ld/%ld frames written\n", written, nbframes);
+       snd_renderbuffer->startframe += written;
+       expected_delay += written;
+}
 
-       nframes = count / shm->format.channels;
 
-       snd_pcm_avail_update (pcm);
-       snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
+/*
+====================
+SndSys_GetSoundTime
+
+Returns the number of sample frames consumed since the sound started
+====================
+*/
+unsigned int SndSys_GetSoundTime (void)
+{
+       snd_pcm_sframes_t delay, timediff;
+       int err;
 
-       state = snd_pcm_state (pcm);
+       if (pcm_handle == NULL)
+               return 0;
 
-       switch (state) {
-               case SND_PCM_STATE_PREPARED:
-                       snd_pcm_mmap_commit (pcm, offset, nframes);
-                       snd_pcm_start (pcm);
-                       break;
-               case SND_PCM_STATE_RUNNING:
-                       snd_pcm_mmap_commit (pcm, offset, nframes);
-                       break;
-               default:
-                       break;
+       err = snd_pcm_delay (pcm_handle, &delay);
+       if (err != 0)
+       {
+               Con_DPrintf ("SndSys_GetSoundTime: can't get playback delay (%s)\n",
+                                        snd_strerror (err));
+
+               if (! SndSys_Recover (err))
+                       return 0;
+
+               err = snd_pcm_delay (pcm_handle, &delay);
+               if (err != 0)
+               {
+                       Con_DPrintf ("SndSys_GetSoundTime: can't get playback delay, again (%s)\n",
+                                                snd_strerror (err));
+                       return 0;
+               }
        }
+
+//Con_DPrintf(">> SndSys_GetSoundTime: expected_delay=%ld, delay=%ld\n",
+//                     expected_delay, delay);
+       if (expected_delay < delay)
+       {
+               Con_Printf ("SndSys_GetSoundTime: expected_delay(%ld) < delay(%ld)\n",
+                                       expected_delay, delay);
+               timediff = 0;
+       }
+       else
+               timediff = expected_delay - delay;
+       expected_delay = delay;
+       
+       alsasoundtime += (unsigned int)timediff;
+       
+       return alsasoundtime;
 }
 
-void *S_LockBuffer(void)
+
+/*
+====================
+SndSys_LockRenderBuffer
+
+Get the exclusive lock on "snd_renderbuffer"
+====================
+*/
+qboolean SndSys_LockRenderBuffer (void)
 {
-       return shm->buffer;
+       // Nothing to do
+       return true;
 }
 
-void S_UnlockBuffer(void)
+
+/*
+====================
+SndSys_UnlockRenderBuffer
+
+Release the exclusive lock on "snd_renderbuffer"
+====================
+*/
+void SndSys_UnlockRenderBuffer (void)
 {
+       // Nothing to do
 }
index 923d6fc..adb28d8 100644 (file)
--- a/snd_bsd.c
+++ b/snd_bsd.c
@@ -21,13 +21,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include <sys/param.h>
 #include <sys/audioio.h>
 #ifndef SUNOS
-#include <sys/endian.h>
+#      include <sys/endian.h>
 #endif
 #include <sys/ioctl.h>
 
 #include <fcntl.h>
 #ifndef SUNOS
-#include <paths.h>
+#      include <paths.h>
 #endif
 #include <unistd.h>
 
@@ -35,34 +35,30 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "snd_main.h"
 
 
-static const int tryrates[] = {44100, 22050, 11025, 8000};
-
 static int audio_fd = -1;
 
-// TODO: allocate them in SNDDMA_Init, with a size depending on
-// the sound format (enough for 0.5 sec of sound for instance)
-#define SND_BUFF_SIZE 65536
-static unsigned char dma_buffer [SND_BUFF_SIZE];
-static unsigned char writebuf [SND_BUFF_SIZE];
 
+/*
+====================
+SndSys_Init
 
-qboolean SNDDMA_Init (void)
+Create "snd_renderbuffer" with the proper sound format if the call is successful
+May return a suggested format if the requested format isn't available
+====================
+*/
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
 {
        unsigned int i;
        const char *snddev;
        audio_info_t info;
 
-       memset ((void*)shm, 0, sizeof (*shm));
-
        // Open the audio device
 #ifdef _PATH_SOUND
        snddev = _PATH_SOUND;
+#elif defined(SUNOS)
+       snddev = "/dev/audio";
 #else
-#ifndef SUNOS
        snddev = "/dev/sound";
-#else
-       snddev = "/dev/audio";
-#endif
 #endif
        audio_fd = open (snddev, O_WRONLY | O_NDELAY | O_NONBLOCK);
        if (audio_fd < 0)
@@ -71,131 +67,163 @@ qboolean SNDDMA_Init (void)
                return false;
        }
 
-       // Look for an appropriate sound format
-       // TODO: we should also test mono/stereo and bits
-       // TODO: support "-sndspeed", "-sndbits", "-sndmono" and "-sndstereo"
-       shm->format.channels = 2;
-       shm->format.width = 2;
-       for (i = 0; i < sizeof (tryrates) / sizeof (tryrates[0]); i++)
-       {
-               shm->format.speed = tryrates[i];
-
-               AUDIO_INITINFO (&info);
-               info.play.sample_rate = shm->format.speed;
-               info.play.channels = shm->format.channels;
-               info.play.precision = shm->format.width * 8;
-// We only handle sound cards of the same endianess than the CPU
-#if BYTE_ORDER == BIG_ENDIAN
-               info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
-#else
-#ifndef SUNOS
-               info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
+       AUDIO_INITINFO (&info);
+#ifdef AUMODE_PLAY     // NetBSD / OpenBSD 
+       info.mode = AUMODE_PLAY;
+#endif
+       info.play.sample_rate = requested->speed;
+       info.play.channels = requested->channels;
+       info.play.precision = requested->width * 8;
+       if (requested->width == 1)
+#ifdef SUNOS
+               info.play.encoding = AUDIO_ENCODING_LINEAR8;
 #else
-               info.play.encoding = AUDIO_ENCODING_LINEAR;
+               info.play.encoding = AUDIO_ENCODING_ULINEAR;
 #endif
+       else
+#ifdef SUNOS
+               info.play.encoding = AUDIO_ENCODING_LINEAR;
+#else
+#      if BYTE_ORDER == BIG_ENDIAN
+               info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
+#      else
+               info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
+#      endif
 #endif
-               if (ioctl (audio_fd, AUDIO_SETINFO, &info) == 0)
-                       break;
-       }
-       if (i == sizeof (tryrates) / sizeof (tryrates[0]))
-       {
-               Con_Print("Can't select an appropriate sound output format\n");
-               close (audio_fd);
-               return false;
-       }
 
-       // Print some information
-       Con_Printf("%d bit %s sound initialized (rate: %dHz)\n",
-                               info.play.precision,
-                               (info.play.channels == 2) ? "stereo" : "mono",
-                               info.play.sample_rate);
+       if (ioctl (audio_fd, AUDIO_SETINFO, &info) == 0)
+               break;
 
-       shm->sampleframes = sizeof (dma_buffer) / shm->format.width / shm->format.channels;
-       shm->samples = shm->sampleframes * shm->format.channels;
-       shm->samplepos = 0;
-       shm->buffer = dma_buffer;
+       // TODO: check the parameters with AUDIO_GETINFO
+       // TODO: check AUDIO_ENCODINGFLAG_EMULATED with AUDIO_GETENC
 
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
        return true;
 }
 
-int SNDDMA_GetDMAPos (void)
-{
-       audio_info_t info;
 
-       if (!shm)
-               return 0;
+/*
+====================
+SndSys_Shutdown
 
-       if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0)
+Stop the sound card, delete "snd_renderbuffer" and free its other resources
+====================
+*/
+void SndSys_Shutdown (void)
+{
+       if (audio_fd >= 0)
        {
-               Con_Print("Error: can't get audio info\n");
-               SNDDMA_Shutdown ();
-               return 0;
+               close(audio_fd);
+               audio_fd = -1;
        }
 
-       return ((info.play.samples * shm->format.channels) % shm->samples);
+       if (snd_renderbuffer != NULL)
+       {
+               Mem_Free(snd_renderbuffer->ring);
+               Mem_Free(snd_renderbuffer);
+               snd_renderbuffer = NULL;
+       }
 }
 
-void SNDDMA_Shutdown (void)
-{
-       close (audio_fd);
-       audio_fd = -1;
-}
 
 /*
-==============
-SNDDMA_Submit
+====================
+SndSys_Submit
 
-Send sound to device if buffer isn't really the dma buffer
-===============
+Submit the contents of "snd_renderbuffer" to the sound card
+====================
 */
-void SNDDMA_Submit (void)
+void SndSys_Submit (void)
 {
-       int bsize;
-       int bytes, b;
-       static int wbufp = 0;
-       unsigned char *p;
-       int idx;
-       int stop = paintedtime;
-
-       if (!shm)
-               return;
-
-       if (paintedtime < wbufp)
-               wbufp = 0; // reset
-
-       bsize = shm->format.channels * shm->format.width;
-       bytes = (paintedtime - wbufp) * bsize;
-
-       if (!bytes)
+       unsigned int startoffset, factor, limit, nbframes;
+       int written;
+       
+       if (audio_fd < 0 ||
+               snd_renderbuffer->startframe == snd_renderbuffer->endframe)
                return;
 
-       if (bytes > sizeof (writebuf))
+       startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
+       factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+       limit = snd_renderbuffer->maxframes - startoffset;
+       nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+       if (nbframes > limit)
        {
-               bytes = sizeof (writebuf);
-               stop = wbufp + bytes / bsize;
+               written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], limit * factor);
+               if (written < 0)
+               {
+                       Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
+                       return;
+               }
+
+               if (written % factor != 0)
+                       Sys_Error("SndSys_Submit: nb of bytes written (%d) isn't aligned to a frame sample!\n", written);
+
+               snd_renderbuffer->startframe += written / factor;
+
+               if ((unsigned int)written < nbframes * factor)
+               {
+                       Con_Printf("SndSys_Submit: audio can't keep up! (%d < %u)\n", written, nbframes * factor);
+                       return;
+               }
+               
+               nbframes -= limit;
+               startoffset = 0;
        }
 
-       // Transfert the sound data from the circular dma_buffer to writebuf
-       // TODO: using 2 memcpys instead of this loop should be faster
-       p = writebuf;
-       idx = (wbufp*bsize) & (sizeof (dma_buffer) - 1);
-       for (b = bytes; b; b--)
+       written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
+       if (written < 0)
        {
-               *p++ = dma_buffer[idx];
-               idx = (idx + 1) & (sizeof (dma_buffer) - 1);
+               Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
+               return;
        }
+       snd_renderbuffer->startframe += written / factor;
+}
+
+
+/*
+====================
+SndSys_GetSoundTime
 
-       if (write (audio_fd, writebuf, bytes) < bytes)
-               Con_Print("audio can't keep up!\n");
+Returns the number of sample frames consumed since the sound started
+====================
+*/
+unsigned int SndSys_GetSoundTime (void)
+{
+       audio_info_t info;
 
-       wbufp = stop;
+       if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0)
+       {
+               Con_Print("Error: can't get audio info\n");
+               SNDDMA_Shutdown ();
+               return 0;
+       }
+
+       return info.play.samples;
 }
 
-void *S_LockBuffer (void)
+
+/*
+====================
+SndSys_LockRenderBuffer
+
+Get the exclusive lock on "snd_renderbuffer"
+====================
+*/
+qboolean SndSys_LockRenderBuffer (void)
 {
-       return shm->buffer;
+       // Nothing to do
+       return true;
 }
 
-void S_UnlockBuffer (void)
+
+/*
+====================
+SndSys_UnlockRenderBuffer
+
+Release the exclusive lock on "snd_renderbuffer"
+====================
+*/
+void SndSys_UnlockRenderBuffer (void)
 {
+       // Nothing to do
 }
index 0938930..a7f711e 100644 (file)
@@ -20,9 +20,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ===========================================================================
 */
 
-// snd_coreaudio.c
-// all other sound mixing is portable
-
 #include <limits.h>
 
 #include <CoreAudio/AudioHardware.h>
@@ -30,101 +27,149 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #include "quakedef.h"
 #include "snd_main.h"
 
-// BUFFER_SIZE must be an even multiple of CHUNK_SIZE
-#define CHUNK_SIZE 2048
-#define BUFFER_SIZE 16384
 
-static unsigned int submissionChunk;
-static unsigned int maxMixedSamples;
-static short *s_mixedSamples;
-static int s_chunkCount;  // number of chunks submitted
-static qboolean s_isRunning;
+#define CHUNK_SIZE 1024
 
-static AudioDeviceID outputDeviceID;
-static AudioStreamBasicDescription outputStreamBasicDescription;
+static unsigned int submissionChunk = 0;  // in sample frames
+static unsigned int coreaudiotime = 0;  // based on the number of chunks submitted so far
+static qboolean s_isRunning = false;
+static pthread_mutex_t coreaudio_mutex;
+static AudioDeviceID outputDeviceID = kAudioDeviceUnknown;
 
 
 /*
-===============
+====================
 audioDeviceIOProc
-===============
+====================
 */
-
-OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
-                                                  const AudioTimeStamp *inNow,
-                                                  const AudioBufferList *inInputData,
-                                                  const AudioTimeStamp *inInputTime,
-                                                  AudioBufferList *outOutputData,
-                                                  const AudioTimeStamp *inOutputTime,
-                                                  void *inClientData)
+static OSStatus audioDeviceIOProc(AudioDeviceID inDevice,
+                                                                 const AudioTimeStamp *inNow,
+                                                                 const AudioBufferList *inInputData,
+                                                                 const AudioTimeStamp *inInputTime,
+                                                                 AudioBufferList *outOutputData,
+                                                                 const AudioTimeStamp *inOutputTime,
+                                                                 void *inClientData)
 {
-       int     offset;
-       short *samples;
-       unsigned int sampleIndex;
        float *outBuffer;
-       float scale;
-
-       offset = (s_chunkCount * submissionChunk) % maxMixedSamples;
-       samples = s_mixedSamples + offset;
-
-       outBuffer = (float *)outOutputData->mBuffers[0].mData;
-
-       // If we have run out of samples, return silence
-       if (s_chunkCount * submissionChunk > shm->format.channels * paintedtime)
-       {
-               memset(outBuffer, 0, sizeof(*outBuffer) * submissionChunk);
-       }
-       else
+       unsigned int frameCount, factor;
+
+       outBuffer = (float*)outOutputData->mBuffers[0].mData;
+       factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
+       frameCount = 0;
+       
+       // Lock the snd_renderbuffer
+       if (SndSys_LockRenderBuffer())
        {
+               unsigned int maxFrames, sampleIndex, sampleCount;
+               unsigned int startOffset, endOffset;
+               const short *samples;
+               const float scale = 1.0f / SHRT_MAX;
+
+               // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer
+               maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+               if (maxFrames >= submissionChunk)
+                       frameCount = submissionChunk;
+               else
+                       frameCount = maxFrames;
+
                // Convert the samples from shorts to floats.  Scale the floats to be [-1..1].
-               scale = (1.0f / SHRT_MAX);
-               for (sampleIndex = 0; sampleIndex < submissionChunk; sampleIndex++)
-                       outBuffer[sampleIndex] = samples[sampleIndex] * scale;
+               startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
+               endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes;
+               if (startOffset > endOffset)  // if the buffer wraps
+               {
+                       sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels;
+                       samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
+                       for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
+                               outBuffer[sampleIndex] = samples[sampleIndex] * scale;
+                       
+                       outBuffer = &outBuffer[sampleCount];
+                       sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount;
+                       samples = (const short*)(&snd_renderbuffer->ring[0]);
+                       for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
+                               outBuffer[sampleIndex] = samples[sampleIndex] * scale;
+               }
+               else
+               {
+                       sampleCount = frameCount * snd_renderbuffer->format.channels;
+                       samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]);
+                       for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
+                               outBuffer[sampleIndex] = samples[sampleIndex] * scale;
+               }
+
+               snd_renderbuffer->startframe += frameCount;
+
+               // Unlock the snd_renderbuffer
+               SndSys_UnlockRenderBuffer();
+       }
 
-               s_chunkCount++; // this is the next buffer we will submit
+       // If there was not enough samples, complete with silence samples
+       if (frameCount < submissionChunk)
+       {
+               unsigned int missingFrames;
+               
+               missingFrames = submissionChunk - frameCount;
+               Con_DPrintf("audioDeviceIOProc: %u sample frames missing\n", missingFrames);
+               memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0]));
        }
 
+       coreaudiotime += submissionChunk;
        return 0;
 }
 
+
 /*
-===============
-SNDDMA_Init
-===============
+====================
+SndSys_Init
+
+Create "snd_renderbuffer" with the proper sound format if the call is successful
+May return a suggested format if the requested format isn't available
+====================
 */
-qboolean SNDDMA_Init(void)
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
 {
        OSStatus status;
        UInt32 propertySize, bufferByteCount;
+       AudioStreamBasicDescription streamDesc;
 
        if (s_isRunning)
                return true;
 
        Con_Printf("Initializing CoreAudio...\n");
+       
+       // We only accept 16-bit samples for the moment
+       if (requested->width != 2)
+       {
+               // Suggest a 16-bit format instead
+               if (suggested != NULL)
+               {
+                       memcpy (suggested, requested, sizeof (suggested));
+                       suggested->width = 2;
+               }
 
+               return false;
+       }
+       
        // Get the output device
        propertySize = sizeof(outputDeviceID);
        status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID);
        if (status)
        {
-               Con_Printf("AudioHardwareGetProperty returned %d\n", status);
+               Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", status);
                return false;
        }
-
        if (outputDeviceID == kAudioDeviceUnknown)
        {
-               Con_Printf("AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n");
+               Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n");
                return false;
        }
 
        // Configure the output device
-       // TODO: support "-sndspeed", "-sndmono" and "-sndstereo"
        propertySize = sizeof(bufferByteCount);
-       bufferByteCount = CHUNK_SIZE * sizeof(float);
+       bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels;
        status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount);
        if (status)
        {
-               Con_Printf("AudioDeviceSetProperty: returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE);
+               Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE);
                return false;
        }
 
@@ -132,101 +177,90 @@ qboolean SNDDMA_Init(void)
        status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount);
        if (status)
        {
-               Con_Printf("AudioDeviceGetProperty: returned %d when setting kAudioDevicePropertyBufferSize\n", status);
+               Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", status);
                return false;
        }
+
        submissionChunk = bufferByteCount / sizeof(float);
-       Con_DPrintf("   Chunk size = %d samples\n", submissionChunk);
+       if (submissionChunk % requested->channels != 0)
+       {
+               Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n");
+               return false;
+       }
+       submissionChunk /= requested->channels;
+       Con_DPrintf("   Chunk size = %d sample frames\n", submissionChunk);
 
        // Print out the device status
-       propertySize = sizeof(outputStreamBasicDescription);
-       status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &outputStreamBasicDescription);
+       propertySize = sizeof(streamDesc);
+       status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc);
        if (status)
        {
-               Con_Printf("AudioDeviceGetProperty: returned %d when getting kAudioDevicePropertyStreamFormat\n", status);
+               Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", status);
                return false;
        }
-       Con_DPrintf("   Hardware format:\n");
-       Con_DPrintf("    %5d mSampleRate\n", (unsigned int)outputStreamBasicDescription.mSampleRate);
+       Con_DPrint ("   Hardware format:\n");
+       Con_DPrintf("    %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate);
        Con_DPrintf("     %c%c%c%c mFormatID\n",
-                               (outputStreamBasicDescription.mFormatID & 0xff000000) >> 24,
-                               (outputStreamBasicDescription.mFormatID & 0x00ff0000) >> 16,
-                               (outputStreamBasicDescription.mFormatID & 0x0000ff00) >>  8,
-                               (outputStreamBasicDescription.mFormatID & 0x000000ff) >>  0);
-       Con_DPrintf("    %5d mBytesPerPacket\n", outputStreamBasicDescription.mBytesPerPacket);
-       Con_DPrintf("    %5d mFramesPerPacket\n", outputStreamBasicDescription.mFramesPerPacket);
-       Con_DPrintf("    %5d mBytesPerFrame\n", outputStreamBasicDescription.mBytesPerFrame);
-       Con_DPrintf("    %5d mChannelsPerFrame\n", outputStreamBasicDescription.mChannelsPerFrame);
-       Con_DPrintf("    %5d mBitsPerChannel\n", outputStreamBasicDescription.mBitsPerChannel);
-
-       if(outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM)
+                               (streamDesc.mFormatID & 0xff000000) >> 24,
+                               (streamDesc.mFormatID & 0x00ff0000) >> 16,
+                               (streamDesc.mFormatID & 0x0000ff00) >>  8,
+                               (streamDesc.mFormatID & 0x000000ff) >>  0);
+       Con_DPrintf("    %5d mBytesPerPacket\n", streamDesc.mBytesPerPacket);
+       Con_DPrintf("    %5d mFramesPerPacket\n", streamDesc.mFramesPerPacket);
+       Con_DPrintf("    %5d mBytesPerFrame\n", streamDesc.mBytesPerFrame);
+       Con_DPrintf("    %5d mChannelsPerFrame\n", streamDesc.mChannelsPerFrame);
+       Con_DPrintf("    %5d mBitsPerChannel\n", streamDesc.mBitsPerChannel);
+
+       if(streamDesc.mFormatID != kAudioFormatLinearPCM)
        {
-               Con_Printf("Default Audio Device doesn't support Linear PCM!\n");
+               Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n");
                return false;
        }
 
-       // Start sound running
+       // Add the callback function
        status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL);
        if (status)
        {
-               Con_Printf("AudioDeviceAddIOProc: returned %d\n", status);
+               Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", status);
                return false;
        }
 
-       maxMixedSamples = BUFFER_SIZE;
-       s_mixedSamples = (short *)Mem_Alloc (snd_mempool, sizeof(*s_mixedSamples) * maxMixedSamples);
-       Con_DPrintf("   Buffer size = %d samples (%d chunks)\n",
-                               maxMixedSamples, (maxMixedSamples / submissionChunk));
+       // We haven't sent any sample frames yet
+       coreaudiotime = 0;
 
-       // Tell the main app what we expect from it
-       memset ((void*)shm, 0, sizeof (*shm));
-       shm->format.speed = outputStreamBasicDescription.mSampleRate;
-       shm->format.channels = outputStreamBasicDescription.mChannelsPerFrame;
-       shm->format.width = 2;
-       shm->sampleframes = maxMixedSamples / shm->format.channels;
-       shm->samples = maxMixedSamples;
-       shm->buffer = (unsigned char *)s_mixedSamples;
-       shm->samplepos = 0;
+       if (pthread_mutex_init(&coreaudio_mutex, NULL) != 0)
+       {
+               Con_Print("CoreAudio: can't create pthread mutex\n");
+               AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
+               return false;
+       }
 
-       // We haven't enqueued anything yet
-       s_chunkCount = 0;
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
 
+       // Start sound running
        status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc);
        if (status)
        {
-               Con_Printf("AudioDeviceStart: returned %d\n", status);
+               Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", status);
+               pthread_mutex_destroy(&coreaudio_mutex);
+               AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
                return false;
        }
-
        s_isRunning = true;
 
-       Con_Printf("   Initialization successful\n");
-
+       Con_Print("   Initialization successful\n");
        return true;
 }
 
-/*
-===============
-SNDDMA_GetDMAPos
-
-return the current sample position (in mono samples read)
-inside the recirculating dma buffer, so the mixing code will know
-how many sample are required to fill it up.
-===============
-*/
-int SNDDMA_GetDMAPos(void)
-{
-       return (s_chunkCount * submissionChunk) % shm->samples;
-}
 
 /*
-===============
-SNDDMA_Shutdown
+====================
+SndSys_Shutdown
 
-Reset the sound device for exiting
-===============
+Stop the sound card, delete "snd_renderbuffer" and free its other resources
+====================
 */
-void SNDDMA_Shutdown(void)
+void SndSys_Shutdown(void)
 {
        OSStatus status;
 
@@ -239,8 +273,9 @@ void SNDDMA_Shutdown(void)
                Con_Printf("AudioDeviceStop: returned %d\n", status);
                return;
        }
-
        s_isRunning = false;
+       
+       pthread_mutex_destroy(&coreaudio_mutex);
 
        status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
        if (status)
@@ -249,38 +284,62 @@ void SNDDMA_Shutdown(void)
                return;
        }
 
-       Mem_Free(s_mixedSamples);
-       s_mixedSamples = NULL;
-       shm->buffer = NULL;
+       if (snd_renderbuffer != NULL)
+       {
+               Mem_Free(snd_renderbuffer->ring);
+               Mem_Free(snd_renderbuffer);
+               snd_renderbuffer = NULL;
+       }
+}
+
+
+/*
+====================
+SndSys_Submit
+
+Submit the contents of "snd_renderbuffer" to the sound card
+====================
+*/
+void SndSys_Submit (void)
+{
+       // Nothing to do here (this sound module is callback-based)
 }
 
+
 /*
-===============
-SNDDMA_Submit
-===============
+====================
+SndSys_GetSoundTime
+
+Returns the number of sample frames consumed since the sound started
+====================
 */
-void SNDDMA_Submit(void)
+unsigned int SndSys_GetSoundTime (void)
 {
-       // nothing to do (CoreAudio is callback-based)
+       return coreaudiotime;
 }
 
+
 /*
-===============
-S_LockBuffer
-===============
+====================
+SndSys_LockRenderBuffer
+
+Get the exclusive lock on "snd_renderbuffer"
+====================
 */
-void *S_LockBuffer(void)
+qboolean SndSys_LockRenderBuffer (void)
 {
-       // not necessary (just return the buffer)
-       return shm->buffer;
+       return (pthread_mutex_lock(&coreaudio_mutex) == 0);
 }
 
+
 /*
-===============
-S_UnlockBuffer
-===============
+====================
+SndSys_UnlockRenderBuffer
+
+Release the exclusive lock on "snd_renderbuffer"
+====================
 */
-void S_UnlockBuffer(void)
+void SndSys_UnlockRenderBuffer (void)
 {
-       // not necessary
+    pthread_mutex_unlock(&coreaudio_mutex);
 }
index e5066ad..18cc555 100644 (file)
@@ -24,8 +24,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "snd_main.h"
 #include "snd_ogg.h"
 
+
+#define SND_MIN_SPEED 8000
+#define SND_MAX_SPEED 96000
+#define SND_MIN_WIDTH 1
+#define SND_MAX_WIDTH 2
+#define SND_MIN_CHANNELS 1
+#define SND_MAX_CHANNELS 8
+
 #if SND_LISTENERS != 8
-#error this data only supports up to 8 channel, update it!
+#      error this data only supports up to 8 channel, update it!
 #endif
 typedef struct listener_s
 {
@@ -45,11 +53,79 @@ speakerlayout_t;
 
 static speakerlayout_t snd_speakerlayout;
 
-void S_Play(void);
-void S_PlayVol(void);
-void S_Play2(void);
-void S_SoundList(void);
-void S_Update_();
+#define SND_SPEAKERLAYOUTS (sizeof(snd_speakerlayouts) / sizeof(snd_speakerlayouts[0]))
+static const speakerlayout_t snd_speakerlayouts[] =
+{
+       {
+               "surround71", 8,
+               {
+                       {45, 0.2, 0.2, 0.5}, // front left
+                       {315, 0.2, 0.2, 0.5}, // front right
+                       {135, 0.2, 0.2, 0.5}, // rear left
+                       {225, 0.2, 0.2, 0.5}, // rear right
+                       {0, 0.2, 0.2, 0.5}, // front center
+                       {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so...  no lfe)
+                       {90, 0.2, 0.2, 0.5}, // side left
+                       {180, 0.2, 0.2, 0.5}, // side right
+               }
+       },
+       {
+               "surround51", 6,
+               {
+                       {45, 0.2, 0.2, 0.5}, // front left
+                       {315, 0.2, 0.2, 0.5}, // front right
+                       {135, 0.2, 0.2, 0.5}, // rear left
+                       {225, 0.2, 0.2, 0.5}, // rear right
+                       {0, 0.2, 0.2, 0.5}, // front center
+                       {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so...  no lfe)
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+               }
+       },
+       {
+               // these systems sometimes have a subwoofer as well, but it has no
+               // channel of its own
+               "surround40", 4,
+               {
+                       {45, 0.3, 0.3, 0.8}, // front left
+                       {315, 0.3, 0.3, 0.8}, // front right
+                       {135, 0.3, 0.3, 0.8}, // rear left
+                       {225, 0.3, 0.3, 0.8}, // rear right
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+               }
+       },
+       {
+               // these systems sometimes have a subwoofer as well, but it has no
+               // channel of its own
+               "stereo", 2,
+               {
+                       {90, 0.5, 0.5, 1}, // side left
+                       {270, 0.5, 0.5, 1}, // side right
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+               }
+       },
+       {
+               "mono", 1,
+               {
+                       {0, 0, 1, 1}, // center
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+                       {0, 0, 0, 0},
+               }
+       }
+};
 
 
 // =======================================================================
@@ -59,47 +135,54 @@ void S_Update_();
 channel_t channels[MAX_CHANNELS];
 unsigned int total_channels;
 
-int snd_blocked = 0;
-cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"};
-cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1", "enables keeping compressed ogg sound files compressed, decompressing them only as needed, otherwise they will be decompressed completely at load (may use a lot of memory)"};
-
-volatile dma_t *shm = 0;
-volatile dma_t sn;
+snd_ringbuffer_t *snd_renderbuffer = NULL;
+unsigned int soundtime = 0;
+static unsigned int oldpaintedtime = 0;
+unsigned int extrasoundtime = 0;
+static double snd_starttime = 0.0;
 
 vec3_t listener_origin;
 matrix4x4_t listener_matrix[SND_LISTENERS];
 vec_t sound_nominal_clip_dist=1000.0;
 mempool_t *snd_mempool;
 
-int soundtime;
-int paintedtime;
-
 // Linked list of known sfx
-sfx_t *known_sfx = NULL;
+static sfx_t *known_sfx = NULL;
+
+static qboolean sound_spatialized = false;
 
-qboolean sound_spatialized = false;
+qboolean simsound = false;
 
-// Fake dma is a synchronous faking of the DMA progress used for
-// isolating performance in the renderer.
-qboolean fakedma = false;
+int snd_blocked = 0;
 
+// Cvars declared in sound.h (part of the sound API)
 cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1", "volume of background music (such as CD music or replacement files such as sound/cdtracks/track002.ogg)"};
 cvar_t volume = {CVAR_SAVE, "volume", "0.7", "volume of sound effects"};
+cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"};
 cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1", "volume of ambient sound effects (such as swampy sounds at the start of e1m2)"};
 
-cvar_t nosound = {0, "nosound", "0", "disables sound"};
-cvar_t snd_precache = {0, "snd_precache", "1", "loads sounds before they are used"};
-//cvar_t bgmbuffer = {0, "bgmbuffer", "4096", "unused quake cvar"};
-cvar_t ambient_level = {0, "ambient_level", "0.3", "volume of environment noises (water and wind)"};
-cvar_t ambient_fade = {0, "ambient_fade", "100", "rate of volume fading when moving from one environment to another"};
-cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0", "disables extra sound mixer calls that are meant to reduce the chance of sound breakup at very low framerates"};
-cvar_t snd_show = {0, "snd_show", "0", "shows some statistics about sound mixing"};
+// Cvars declared in snd_main.h (shared with other snd_*.c files)
 cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1", "how much sound to mix ahead of time"};
+cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1", "enables keeping compressed ogg sound files compressed, decompressing them only as needed, otherwise they will be decompressed completely at load (may use a lot of memory)"};
 cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0", "swaps left/right speakers for old ISA soundblaster cards"};
 
+// Local cvars
+static cvar_t nosound = {0, "nosound", "0", "disables sound"};
+static cvar_t snd_precache = {0, "snd_precache", "1", "loads sounds before they are used"};
+static cvar_t ambient_level = {0, "ambient_level", "0.3", "volume of environment noises (water and wind)"};
+static cvar_t ambient_fade = {0, "ambient_fade", "100", "rate of volume fading when moving from one environment to another"};
+static cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0", "disables extra sound mixer calls that are meant to reduce the chance of sound breakup at very low framerates"};
+static cvar_t snd_show = {0, "snd_show", "0", "shows some statistics about sound mixing"};
+
+// Default sound format is 48KHz, 16-bit, stereo
+// (48KHz because a lot of onboard sound cards sucks at any other speed)
+static cvar_t snd_speed = {CVAR_SAVE, "snd_speed", "48000", "sound output frequency, in hertz"};
+static cvar_t snd_width = {CVAR_SAVE, "snd_width", "2", "sound output precision, in bytes (1 and 2 supported)"};
+static cvar_t snd_channels = {CVAR_SAVE, "snd_channels", "2", "number of channels for the sound ouput (2 for stereo; up to 8 supported for 3D sound)"};
+
 // Ambient sounds
-sfx_t* ambient_sfxs [2] = { NULL, NULL };
-const char* ambient_names [2] = { "sound/ambience/water1.wav", "sound/ambience/wind2.wav" };
+static sfx_t* ambient_sfxs [2] = { NULL, NULL };
+static const char* ambient_names [2] = { "sound/ambience/water1.wav", "sound/ambience/wind2.wav" };
 
 
 // ====================================================================
@@ -108,71 +191,386 @@ const char* ambient_names [2] = { "sound/ambience/water1.wav", "sound/ambience/w
 
 void S_FreeSfx (sfx_t *sfx, qboolean force);
 
+static void S_Play_Common (float fvol, float attenuation)
+{
+       int i, ch_ind;
+       char name [MAX_QPATH];
+       sfx_t *sfx;
+
+       i = 1;
+       while (i < Cmd_Argc ())
+       {
+               // Get the name, and appends ".wav" as an extension if there's none
+               strlcpy (name, Cmd_Argv (i), sizeof (name));
+               if (!strrchr (name, '.'))
+                       strlcat (name, ".wav", sizeof (name));
+               i++;
+
+               // If we need to get the volume from the command line
+               if (fvol == -1.0f)
+               {
+                       fvol = atof (Cmd_Argv (i));
+                       i++;
+               }
+
+               sfx = S_PrecacheSound (name, true, false);
+               if (sfx)
+               {
+                       ch_ind = S_StartSound (-1, 0, sfx, listener_origin, fvol, attenuation);
+
+                       // Free the sfx if the file didn't exist
+                       if (ch_ind < 0)
+                               S_FreeSfx (sfx, false);
+                       else
+                               channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND;
+               }
+       }
+}
+
+static void S_Play_f(void)
+{
+       S_Play_Common (1.0f, 1.0f);
+}
+
+static void S_Play2_f(void)
+{
+       S_Play_Common (1.0f, 0.0f);
+}
+
+static void S_PlayVol_f(void)
+{
+       S_Play_Common (-1.0f, 0.0f);
+}
+
+static void S_SoundList_f (void)
+{
+       unsigned int i;
+       sfx_t *sfx;
+       unsigned int total;
+
+       total = 0;
+       for (sfx = known_sfx, i = 0; sfx != NULL; sfx = sfx->next, i++)
+       {
+               if (sfx->fetcher != NULL)
+               {
+                       unsigned int size;
+                       const snd_format_t* format;
+                       
+                       size = sfx->memsize;
+                       format = sfx->fetcher->getfmt(sfx);
+                       Con_Printf ("%c%c%c%c(%2db, %6s) %8i : %s\n",
+                                               (sfx->loopstart >= 0) ? 'L' : ' ',
+                                               (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ',
+                                               (sfx->locks > 0) ? 'K' : ' ',
+                                               (sfx->flags & SFXFLAG_PERMANENTLOCK) ? 'P' : ' ',
+                                               format->width * 8,
+                                               (format->channels == 1) ? "mono" : "stereo",
+                                               size,
+                                               sfx->name);
+                       total += size;
+               }
+               else
+                       Con_Printf ("    (  unknown  ) unloaded : %s\n", sfx->name);
+       }
+       Con_Printf("Total resident: %i\n", total);
+}
+
 
 void S_SoundInfo_f(void)
 {
-       if (!shm)
+       if (snd_renderbuffer == NULL)
        {
                Con_Print("sound system not started\n");
                return;
        }
 
-       Con_Printf("%5d speakers\n", shm->format.channels);
-       Con_Printf("%5d frames\n", shm->sampleframes);
-       Con_Printf("%5d samples\n", shm->samples);
-       Con_Printf("%5d samplepos\n", shm->samplepos);
-       Con_Printf("%5d samplebits\n", shm->format.width * 8);
-       Con_Printf("%5d speed\n", shm->format.speed);
-       Con_Printf("%p dma buffer\n", shm->buffer);
+       Con_Printf("%5d speakers\n", snd_renderbuffer->format.channels);
+       Con_Printf("%5d frames\n", snd_renderbuffer->maxframes);
+       Con_Printf("%5d samplebits\n", snd_renderbuffer->format.width * 8);
+       Con_Printf("%5d speed\n", snd_renderbuffer->format.speed);
        Con_Printf("%5u total_channels\n", total_channels);
 }
 
 
-void S_Startup(void)
+// TODO: make this function smarter...
+static qboolean S_ChooseCheaperFormat (snd_format_t* format, qboolean fixed_speed, qboolean fixed_width, qboolean fixed_channels)
+{
+       // Can we decrease the number of channels?
+       if (!fixed_channels && format->channels > 1)
+       {
+               unsigned short channels = format->channels;
+
+               // If it has an odd number of channels(?!), make it even
+               if (channels & 1)
+                       channels--;
+               else
+               {
+                       // Remove 2 speakers, unless it's a stereo format
+                       if (channels != 2)
+                               channels -= 2;
+                       else
+                               channels = 1;
+               }
+
+               format->channels = channels;
+               return true;
+       }
+
+       // Can we decrease the speed?
+       if (!fixed_speed)
+       {
+               unsigned int suggest_speeds [] = { 44100, 22050, 11025 };
+               unsigned int i;
+               
+               for (i = 0; i < sizeof(suggest_speeds) / sizeof(suggest_speeds[0]); i++)
+                       if (format->speed > suggest_speeds[i])
+                       {
+                               format->speed = suggest_speeds[i];
+                               return true;
+                       }
+
+               // the speed is already low
+       }
+
+       // Can we decrease the number of bits per sample?
+       if (!fixed_width && format->width > 1)
+       {
+               format->width = 1;
+               return true;
+       }
+
+       return false;
+}
+
+
+void S_Startup (void)
 {
+       qboolean fixed_speed, fixed_width, fixed_channels;
+       snd_format_t chosen_fmt;
+       static snd_format_t prev_render_format = {0, 0, 0};
+       const char* env;
+       int i;
+       unsigned int layout_id;
+
        if (!snd_initialized.integer)
                return;
 
-       shm = &sn;
-       memset((void *)shm, 0, sizeof(*shm));
+       fixed_speed = false;
+       fixed_width = false;
+       fixed_channels = false;
+
+       // Get the starting sound format from the cvars
+       chosen_fmt.speed = snd_speed.integer;
+       chosen_fmt.width = snd_width.integer;
+       chosen_fmt.channels = snd_channels.integer;
 
-       // create a piece of DMA memory
-       if (fakedma)
+       // Check the environment variables to see if the player wants a particular sound format
+       env = getenv("QUAKE_SOUND_CHANNELS");
+       if (env != NULL)
        {
-               shm->format.width = 2;
-               shm->format.speed = 22050;
-               shm->format.channels = 2;
-               shm->sampleframes = 16384;
-               shm->samples = shm->sampleframes * shm->format.channels;
-               shm->samplepos = 0;
-               shm->buffer = (unsigned char *)Mem_Alloc(snd_mempool, shm->samples * shm->format.width);
+               chosen_fmt.channels = atoi (env);
+               fixed_channels = true;
        }
-       else
+       env = getenv("QUAKE_SOUND_SPEED");
+       if (env != NULL)
+       {
+               chosen_fmt.speed = atoi (env);
+               fixed_speed = true;
+       }
+       env = getenv("QUAKE_SOUND_SAMPLEBITS");
+       if (env != NULL)
+       {
+               chosen_fmt.width = atoi (env) / 8;
+               fixed_width = true;
+       }
+
+       // Parse the command line to see if the player wants a particular sound format
+// COMMANDLINEOPTION: Sound: -sndquad sets sound output to 4 channel surround
+       if (COM_CheckParm ("-sndquad") != 0)
+       {
+               chosen_fmt.channels = 4;
+               fixed_channels = true;
+       }
+// COMMANDLINEOPTION: Sound: -sndstereo sets sound output to stereo
+       else if (COM_CheckParm ("-sndstereo") != 0)
+       {
+               chosen_fmt.channels = 2;
+               fixed_channels = true;
+       }
+// COMMANDLINEOPTION: Sound: -sndmono sets sound output to mono
+       else if (COM_CheckParm ("-sndmono") != 0)
+       {
+               chosen_fmt.channels = 1;
+               fixed_channels = true;
+       }
+// COMMANDLINEOPTION: Sound: -sndspeed <hz> chooses sound output rate (supported values are 48000, 44100, 32000, 24000, 22050, 16000, 11025 (quake), 8000)
+       i = COM_CheckParm ("-sndspeed");
+       if (0 < i && i < com_argc - 1)
+       {
+               chosen_fmt.speed = atoi (com_argv[i + 1]);
+               fixed_speed = true;
+       }
+// COMMANDLINEOPTION: Sound: -sndbits <bits> chooses 8 bit or 16 bit sound output
+       i = COM_CheckParm ("-sndbits");
+       if (0 < i && i < com_argc - 1)
        {
-               if (!SNDDMA_Init())
+               chosen_fmt.width = atoi (com_argv[i + 1]) / 8;
+               fixed_width = true;
+       }
+
+       // You can't change sound speed after start time (not yet supported)
+       if (prev_render_format.speed != 0)
+       {
+               fixed_speed = true;
+               if (chosen_fmt.speed != prev_render_format.speed)
                {
-                       Con_Print("S_Startup: SNDDMA_Init failed.\n");
-                       shm = NULL;
+                       Con_Printf("S_Startup: sound speed has changed! This is NOT supported yet. Falling back to previous speed (%u Hz)\n",
+                                          prev_render_format.speed);
+                       chosen_fmt.speed = prev_render_format.speed;
+               }
+       }
+       
+       // Sanity checks
+       if (chosen_fmt.speed < SND_MIN_SPEED)
+       {
+               chosen_fmt.speed = SND_MIN_SPEED;
+               fixed_speed = false;
+       }
+       else if (chosen_fmt.speed > SND_MAX_SPEED)
+       {
+               chosen_fmt.speed = SND_MAX_SPEED;
+               fixed_speed = false;
+       }
+
+       if (chosen_fmt.width < SND_MIN_WIDTH)
+       {
+               chosen_fmt.width = SND_MIN_WIDTH;
+               fixed_width = false;
+       }
+       else if (chosen_fmt.width > SND_MAX_WIDTH)
+       {
+               chosen_fmt.width = SND_MAX_WIDTH;
+               fixed_width = false;
+       }
+
+       if (chosen_fmt.channels < SND_MIN_CHANNELS)
+       {
+               chosen_fmt.channels = SND_MIN_CHANNELS;
+               fixed_channels = false;
+       }
+       else if (chosen_fmt.channels > SND_MAX_CHANNELS)
+       {
+               chosen_fmt.channels = SND_MAX_CHANNELS;
+               fixed_channels = false;
+       }
+       
+       // create the sound buffer used for sumitting the samples to the plaform-dependent module
+       if (!simsound)
+       {
+               snd_format_t suggest_fmt;
+               qboolean accepted;
+
+               accepted = false;
+               do
+               {
+                       Con_DPrintf("S_Startup: initializing sound output format: %dHz, %d bit, %d channels...\n",
+                                               chosen_fmt.speed, chosen_fmt.width * 8,
+                                               chosen_fmt.channels);
+
+                       memset(&suggest_fmt, 0, sizeof(suggest_fmt));
+                       accepted = SndSys_Init(&chosen_fmt, &suggest_fmt);
+
+                       if (!accepted)
+                       {
+                               Con_DPrintf("S_Startup: sound output initialization FAILED\n");
+
+                               // If the module is suggesting another one
+                               if (suggest_fmt.speed != 0)
+                               {
+                                       memcpy(&chosen_fmt, &suggest_fmt, sizeof(chosen_fmt));
+                                       Con_Printf ("           Driver has suggested %dHz, %d bit, %d channels. Retrying...\n",
+                                                               suggest_fmt.speed, suggest_fmt.width * 8,
+                                                               suggest_fmt.channels);
+                               }
+                               // Else, try to find a less resource-demanding format
+                               else if (!S_ChooseCheaperFormat (&chosen_fmt, fixed_speed, fixed_width, fixed_channels))
+                                       break;
+                       }
+               } while (!accepted);
+
+               // If we haven't found a suitable format
+               if (!accepted)
+               {
+                       Con_Print("S_Startup: SndSys_Init failed.\n");
                        sound_spatialized = false;
                        return;
                }
        }
+       else
+       {
+               snd_renderbuffer = Snd_CreateRingBuffer(&chosen_fmt, 0, NULL);
+               Con_Print ("S_Startup: simulating sound output\n");
+       }
+
+       memcpy(&prev_render_format, &snd_renderbuffer->format, sizeof(prev_render_format));
+       Con_Printf("Sound format: %dHz, %d channels, %d bits per sample\n",
+                          chosen_fmt.speed, chosen_fmt.channels, chosen_fmt.width * 8);
+
+       // Update the cvars
+       snd_speed.integer = chosen_fmt.speed;
+       snd_width.integer = chosen_fmt.width;
+       snd_channels.integer = chosen_fmt.channels;
+
+       snd_starttime = realtime;
+
+       // If the sound module has already run, add an extra time to make sure
+       // the sound time doesn't decrease, to not confuse playing SFXs
+       if (oldpaintedtime != 0)
+       {
+               // The extra time must be a multiple of the render buffer size
+               // to avoid modifying the current position in the buffer,
+               // some modules write directly to a shared (DMA) buffer
+               extrasoundtime = oldpaintedtime + snd_renderbuffer->maxframes - 1;
+               extrasoundtime -= extrasoundtime % snd_renderbuffer->maxframes;
+               Con_DPrintf("S_Startup: extra sound time = %u\n", extrasoundtime);
+
+               soundtime = extrasoundtime;
+       }
+       else
+               extrasoundtime = 0;
+       snd_renderbuffer->startframe = soundtime;
+       snd_renderbuffer->endframe = soundtime;
 
-       Con_Printf("Sound format: %dHz, %d bit, %d channels\n", shm->format.speed,
-                          shm->format.width * 8, shm->format.channels);
+       // select speaker layout
+       for (layout_id = 0; layout_id < SND_SPEAKERLAYOUTS; layout_id++)
+               if (snd_speakerlayouts[layout_id].channels == snd_renderbuffer->format.channels)
+                       break;
+       if (layout_id >= SND_SPEAKERLAYOUTS)
+       {
+               Con_Printf("S_Startup: invalid number of channels (%hu). Can't find the corresponding speaker layout.\n"
+                                  "Defaulting to mono output\n",
+                                  snd_renderbuffer->format.channels);
+               layout_id = SND_SPEAKERLAYOUTS - 1;
+       }
+       snd_speakerlayout = snd_speakerlayouts[layout_id];
 }
 
 void S_Shutdown(void)
 {
-       if (!shm)
+       if (snd_renderbuffer == NULL)
                return;
 
-       if (fakedma)
-               Mem_Free(shm->buffer);
+       oldpaintedtime = snd_renderbuffer->endframe;
+
+       if (simsound)
+       {
+               Mem_Free(snd_renderbuffer->ring);
+               Mem_Free(snd_renderbuffer);
+               snd_renderbuffer = NULL;
+       }
        else
-               SNDDMA_Shutdown();
+               SndSys_Shutdown();
 
-       shm = NULL;
        sound_spatialized = false;
 }
 
@@ -195,6 +593,10 @@ void S_Init(void)
        Cvar_RegisterVariable(&bgmvolume);
        Cvar_RegisterVariable(&snd_staticvolume);
 
+       Cvar_RegisterVariable(&snd_speed);
+       Cvar_RegisterVariable(&snd_width);
+       Cvar_RegisterVariable(&snd_channels);
+
 // COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio)
        if (COM_CheckParm("-nosound") || COM_CheckParm("-safe"))
                return;
@@ -203,13 +605,13 @@ void S_Init(void)
 
 // COMMANDLINEOPTION: Sound: -simsound runs sound mixing but with no output
        if (COM_CheckParm("-simsound"))
-               fakedma = true;
+               simsound = true;
 
-       Cmd_AddCommand("play", S_Play, "play a sound at your current location (not heard by anyone else)");
-       Cmd_AddCommand("play2", S_Play2, "play a sound globally throughout the level (not heard by anyone else)");
-       Cmd_AddCommand("playvol", S_PlayVol, "play a sound at the specified volume level at your current location (not heard by anyone else)");
+       Cmd_AddCommand("play", S_Play_f, "play a sound at your current location (not heard by anyone else)");
+       Cmd_AddCommand("play2", S_Play2_f, "play a sound globally throughout the level (not heard by anyone else)");
+       Cmd_AddCommand("playvol", S_PlayVol_f, "play a sound at the specified volume level at your current location (not heard by anyone else)");
        Cmd_AddCommand("stopsound", S_StopAllSounds, "silence");
-       Cmd_AddCommand("soundlist", S_SoundList, "list loaded sounds");
+       Cmd_AddCommand("soundlist", S_SoundList_f, "list loaded sounds");
        Cmd_AddCommand("soundinfo", S_SoundInfo_f, "print sound system information (such as channels and speed)");
        Cmd_AddCommand("snd_restart", S_Restart_f, "restart sound system");
 
@@ -217,7 +619,6 @@ void S_Init(void)
        Cvar_RegisterVariable(&snd_precache);
        Cvar_RegisterVariable(&snd_initialized);
        Cvar_RegisterVariable(&snd_streaming);
-       //Cvar_RegisterVariable(&bgmbuffer);
        Cvar_RegisterVariable(&ambient_level);
        Cvar_RegisterVariable(&ambient_fade);
        Cvar_RegisterVariable(&snd_noextraupdate);
@@ -257,14 +658,9 @@ void S_Terminate (void)
 }
 
 
-// =======================================================================
-// Load a sound
-// =======================================================================
-
 /*
 ==================
 S_FindName
-
 ==================
 */
 sfx_t *S_FindName (const char *name)
@@ -300,7 +696,6 @@ sfx_t *S_FindName (const char *name)
 /*
 ==================
 S_FreeSfx
-
 ==================
 */
 void S_FreeSfx (sfx_t *sfx, qboolean force)
@@ -348,7 +743,6 @@ void S_FreeSfx (sfx_t *sfx, qboolean force)
 /*
 ==================
 S_ServerSounds
-
 ==================
 */
 void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds)
@@ -406,7 +800,6 @@ void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds)
 /*
 ==================
 S_PrecacheSound
-
 ==================
 */
 sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean lock)
@@ -467,7 +860,27 @@ void S_UnlockSfx (sfx_t *sfx)
 }
 
 
-//=============================================================================
+/*
+==================
+S_BlockSound
+==================
+*/
+void S_BlockSound (void)
+{
+       snd_blocked++;
+}
+
+
+/*
+==================
+S_UnblockSound
+==================
+*/
+void S_UnblockSound (void)
+{
+       snd_blocked--;
+}
+
 
 /*
 =================
@@ -480,12 +893,12 @@ channel_t *SND_PickChannel(int entnum, int entchannel)
 {
        int ch_idx;
        int first_to_die;
-       int life_left;
+       int first_life_left, life_left;
        channel_t* ch;
 
 // Check for replacement sound, or find the best one to replace
        first_to_die = -1;
-       life_left = 0x7fffffff;
+       first_life_left = 0x7fffffff;
        for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++)
        {
                ch = &channels[ch_idx];
@@ -520,9 +933,10 @@ channel_t *SND_PickChannel(int entnum, int entchannel)
                                continue;
                }
 
-               if (ch->end - paintedtime < life_left)
+               life_left = (int)(ch->end - snd_renderbuffer->endframe);
+               if (life_left < first_life_left)
                {
-                       life_left = ch->end - paintedtime;
+                       first_life_left = life_left;
                        first_to_die = ch_idx;
                }
        }
@@ -606,8 +1020,8 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags,
        VectorCopy (origin, target_chan->origin);
        target_chan->master_vol = (int)(fvol * 255);
        target_chan->sfx = sfx;
-       target_chan->end = paintedtime + sfx->total_length;
-       target_chan->lastptime = paintedtime;
+       target_chan->end = snd_renderbuffer->endframe + sfx->total_length;
+       target_chan->lastptime = snd_renderbuffer->endframe;
        target_chan->flags = flags;
 
        // If it's a static sound
@@ -631,7 +1045,7 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f
        int             ch_idx;
        int             skip;
 
-       if (!shm || !sfx || nosound.integer)
+       if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer)
                return -1;
        if (!sfx->fetcher)
        {
@@ -662,7 +1076,7 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f
                        continue;
                if (check->sfx == sfx && !check->pos)
                {
-                       skip = (int)(0.1 * sfx->format.speed);
+                       skip = (int)(0.1 * snd_renderbuffer->format.speed);
                        if (skip > (int)sfx->total_length)
                                skip = (int)sfx->total_length;
                        if (skip > 0)
@@ -737,9 +1151,9 @@ void S_StopSound(int entnum, int entchannel)
 void S_StopAllSounds (void)
 {
        unsigned int i;
-       unsigned char *pbuf;
 
-       if (!shm)
+       // TOCHECK: is this test necessary?
+       if (snd_renderbuffer == NULL)
                return;
 
        for (i = 0; i < total_channels; i++)
@@ -748,22 +1162,18 @@ void S_StopAllSounds (void)
        total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;   // no statics
        memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
 
-       // Clear sound buffer
-       pbuf = (unsigned char *)S_LockBuffer();
-       if (pbuf != NULL)
+       // Mute the contents of the submittion buffer
+       if (simsound || SndSys_LockRenderBuffer ())
        {
-               int setsize = shm->samples * shm->format.width;
-               int     clear = (shm->format.width == 1) ? 0x80 : 0;
-
-               // FIXME: is it (still) true? (check with OSS and ALSA)
-               // on i586/i686 optimized versions of glibc, glibc *wrongly* IMO,
-               // reads the memory area before writing to it causing a seg fault
-               // since the memory is PROT_WRITE only and not PROT_READ|PROT_WRITE
-               //memset(shm->buffer, clear, shm->samples * shm->format.width);
-               while (setsize--)
-                       *pbuf++ = clear;
-
-               S_UnlockBuffer ();
+               int clear;
+               size_t memsize;
+               
+               clear = (snd_renderbuffer->format.width == 1) ? 0x80 : 0;
+               memsize = snd_renderbuffer->maxframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+               memset(snd_renderbuffer->ring, clear, memsize);
+
+               if (!simsound)
+                       SndSys_UnlockRenderBuffer ();
        }
 }
 
@@ -783,7 +1193,7 @@ void S_PauseGameSounds (qboolean toggle)
 
 void S_SetChannelVolume (unsigned int ch_ind, float fvol)
 {
-       channels[ch_ind].master_vol = (int)(fvol * 255);
+       channels[ch_ind].master_vol = (int)(fvol * 255.0f);
 }
 
 
@@ -796,7 +1206,7 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
 {
        channel_t       *target_chan;
 
-       if (!shm || !sfx || nosound.integer)
+       if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer)
                return;
        if (!sfx->fetcher)
        {
@@ -817,12 +1227,9 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
 }
 
 
-//=============================================================================
-
 /*
 ===================
 S_UpdateAmbientSounds
-
 ===================
 */
 void S_UpdateAmbientSounds (void)
@@ -868,79 +1275,45 @@ void S_UpdateAmbientSounds (void)
        }
 }
 
-#define SND_SPEAKERLAYOUTS 5
-static speakerlayout_t snd_speakerlayouts[SND_SPEAKERLAYOUTS] =
+static void S_PaintAndSubmit (void)
 {
-       {
-               "surround71", 8,
-               {
-                       {45, 0.2, 0.2, 0.5}, // front left
-                       {315, 0.2, 0.2, 0.5}, // front right
-                       {135, 0.2, 0.2, 0.5}, // rear left
-                       {225, 0.2, 0.2, 0.5}, // rear right
-                       {0, 0.2, 0.2, 0.5}, // front center
-                       {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so...  no lfe)
-                       {90, 0.2, 0.2, 0.5}, // side left
-                       {180, 0.2, 0.2, 0.5}, // side right
-               }
-       },
-       {
-               "surround51", 6,
-               {
-                       {45, 0.2, 0.2, 0.5}, // front left
-                       {315, 0.2, 0.2, 0.5}, // front right
-                       {135, 0.2, 0.2, 0.5}, // rear left
-                       {225, 0.2, 0.2, 0.5}, // rear right
-                       {0, 0.2, 0.2, 0.5}, // front center
-                       {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so...  no lfe)
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-               }
-       },
-       {
-               // these systems sometimes have a subwoofer as well, but it has no
-               // channel of its own
-               "surround40", 4,
-               {
-                       {45, 0.3, 0.3, 0.8}, // front left
-                       {315, 0.3, 0.3, 0.8}, // front right
-                       {135, 0.3, 0.3, 0.8}, // rear left
-                       {225, 0.3, 0.3, 0.8}, // rear right
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-               }
-       },
-       {
-               // these systems sometimes have a subwoofer as well, but it has no
-               // channel of its own
-               "stereo", 2,
-               {
-                       {90, 0.5, 0.5, 1}, // side left
-                       {270, 0.5, 0.5, 1}, // side right
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-               }
-       },
-       {
-               "mono", 1,
-               {
-                       {0, 0, 1, 1}, // center
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-                       {0, 0, 0, 0},
-               }
-       }
-};
+       unsigned int newsoundtime, paintedtime, endtime, maxtime, usedframes;
+
+       if (snd_renderbuffer == NULL || snd_blocked > 0)
+               return;
+
+       // Update sound time
+       if (cls.capturevideo_soundfile) // SUPER NASTY HACK to record non-realtime sound
+               newsoundtime = (unsigned int)((double)cls.capturevideo_frame * (double)snd_renderbuffer->format.speed / (double)cls.capturevideo_framerate);
+       else if (simsound)
+               newsoundtime = (unsigned int)((realtime - snd_starttime) * (double)snd_renderbuffer->format.speed);
+       else
+               newsoundtime = SndSys_GetSoundTime();
+       
+       newsoundtime += extrasoundtime;
+       if (newsoundtime < soundtime)
+               Con_Printf("S_PaintAndSubmit: WARNING: newsoundtime < soundtime (%u < %u)\n",
+                                  newsoundtime, soundtime);
+       soundtime = newsoundtime;
+
+       // Check to make sure that we haven't overshot
+       paintedtime = snd_renderbuffer->endframe;
+       if (paintedtime < soundtime)
+               paintedtime = soundtime;
+
+       // mix ahead of current position
+       endtime = soundtime + (unsigned int)(_snd_mixahead.value * (float)snd_renderbuffer->format.speed);
+       usedframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+       maxtime = paintedtime + snd_renderbuffer->maxframes - usedframes;
+       endtime = min(endtime, maxtime);
+
+       S_PaintChannels(snd_renderbuffer, paintedtime, endtime);
+
+       if (simsound)
+               snd_renderbuffer->startframe = snd_renderbuffer->endframe;
+       else
+               SndSys_Submit();
+}
 
 /*
 ============
@@ -955,18 +1328,12 @@ void S_Update(const matrix4x4_t *listenermatrix)
        channel_t *ch, *combine;
        matrix4x4_t basematrix, rotatematrix;
 
-       if (!snd_initialized.integer || (snd_blocked > 0) || !shm)
+       if (!snd_initialized.integer || snd_blocked > 0 || snd_renderbuffer == NULL)
                return;
 
        Matrix4x4_Invert_Simple(&basematrix, listenermatrix);
        Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin);
 
-       // select speaker layout
-       for (i = 0;i < SND_SPEAKERLAYOUTS - 1;i++)
-               if (snd_speakerlayouts[i].channels == shm->format.channels)
-                       break;
-       snd_speakerlayout = snd_speakerlayouts[i];
-
        // calculate the current matrices
        for (j = 0;j < SND_LISTENERS;j++)
        {
@@ -1056,39 +1423,7 @@ void S_Update(const matrix4x4_t *listenermatrix)
                Con_Printf("----(%u)----\n", total);
        }
 
-       S_Update_();
-}
-
-void GetSoundtime(void)
-{
-       int             samplepos;
-       static  int             buffers;
-       static  int             oldsamplepos;
-       int             fullsamples;
-
-       fullsamples = shm->sampleframes;
-
-       // it is possible to miscount buffers if it has wrapped twice between
-       // calls to S_Update.  Oh well.
-       if (cls.capturevideo_soundfile) // SUPER NASTY HACK to record non-realtime sound
-               samplepos = (int)((((unsigned int)((double)cls.capturevideo_frame * (double)shm->format.speed / (double)cls.capturevideo_framerate)) * (unsigned int)shm->format.channels) % (unsigned int)shm->samples);
-       else
-               samplepos = SNDDMA_GetDMAPos();
-
-       if (samplepos < oldsamplepos)
-       {
-               buffers++;                                      // buffer wrapped
-
-               if (paintedtime > 0x40000000)
-               {       // time to chop things off to avoid 32 bit limits
-                       buffers = 0;
-                       paintedtime = fullsamples;
-                       S_StopAllSounds ();
-               }
-       }
-       oldsamplepos = samplepos;
-
-       soundtime = buffers * fullsamples + samplepos / shm->format.channels;
+       S_PaintAndSubmit();
 }
 
 void S_ExtraUpdate (void)
@@ -1096,121 +1431,9 @@ void S_ExtraUpdate (void)
        if (snd_noextraupdate.integer || !sound_spatialized)
                return;
 
-       S_Update_();
-}
-
-void S_Update_(void)
-{
-       unsigned        endtime;
-
-       if (!shm || (snd_blocked > 0))
-               return;
-
-       // Updates DMA time
-       GetSoundtime();
-
-       // check to make sure that we haven't overshot
-       if (paintedtime < soundtime)
-               paintedtime = soundtime;
-
-       // mix ahead of current position
-       endtime = soundtime + (unsigned int)(_snd_mixahead.value * shm->format.speed);
-       endtime = min(endtime, (unsigned int)(soundtime + shm->sampleframes));
-
-       S_PaintChannels (endtime);
-
-       SNDDMA_Submit ();
-}
-
-/*
-===============================================================================
-
-console functions
-
-===============================================================================
-*/
-
-static void S_Play_Common(float fvol, float attenuation)
-{
-       int     i, ch_ind;
-       char name[MAX_QPATH];
-       sfx_t   *sfx;
-
-       i = 1;
-       while (i<Cmd_Argc())
-       {
-               // Get the name
-               strlcpy(name, Cmd_Argv(i), sizeof(name));
-               if (!strrchr(name, '.'))
-                       strlcat(name, ".wav", sizeof(name));
-               i++;
-
-               // If we need to get the volume from the command line
-               if (fvol == -1.0f)
-               {
-                       fvol = atof(Cmd_Argv(i));
-                       i++;
-               }
-
-               sfx = S_PrecacheSound (name, true, false);
-               if (sfx)
-               {
-                       ch_ind = S_StartSound(-1, 0, sfx, listener_origin, fvol, attenuation);
-
-                       // Free the sfx if the file didn't exist
-                       if (ch_ind < 0)
-                               S_FreeSfx (sfx, false);
-                       else
-                               channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND;
-               }
-       }
-}
-
-void S_Play(void)
-{
-       S_Play_Common (1.0f, 1.0f);
-}
-
-void S_Play2(void)
-{
-       S_Play_Common (1.0f, 0.0f);
+       S_PaintAndSubmit();
 }
 
-void S_PlayVol(void)
-{
-       S_Play_Common (-1.0f, 0.0f);
-}
-
-void S_SoundList(void)
-{
-       unsigned int i;
-       sfx_t   *sfx;
-       int             size, total;
-
-       total = 0;
-       for (sfx = known_sfx, i = 0; sfx != NULL; sfx = sfx->next, i++)
-       {
-               if (sfx->fetcher != NULL)
-               {
-                       size = (int)sfx->memsize;
-                       total += size;
-                       Con_Printf ("%c%c%c%c(%2db, %6s) %8i : %s\n",
-                                               (sfx->loopstart >= 0) ? 'L' : ' ',
-                                               (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ',
-                                               (sfx->locks > 0) ? 'K' : ' ',
-                                               (sfx->flags & SFXFLAG_PERMANENTLOCK) ? 'P' : ' ',
-                                               sfx->format.width * 8,
-                                               (sfx->format.channels == 1) ? "mono" : "stereo",
-                                               size,
-                                               sfx->name);
-               }
-               else
-                       Con_Printf ("    (  unknown  ) unloaded : %s\n", sfx->name);
-       }
-       Con_Printf("Total resident: %i\n", total);
-}
-
-
 qboolean S_LocalSound (const char *sound)
 {
        sfx_t   *sfx;
index ec097d2..47b622d 100644 (file)
@@ -24,20 +24,32 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "sound.h"
 
 
-typedef struct sfxbuffer_s
-{
-       unsigned int    length;
-       unsigned int    offset;
-       unsigned char   data[4];        // variable sized
-} sfxbuffer_t;
-
 typedef struct snd_format_s
 {
        unsigned int    speed;
-       unsigned int    width;
-       unsigned int    channels;
+       unsigned short  width;
+       unsigned short  channels;
 } snd_format_t;
 
+typedef struct snd_buffer_s
+{
+       snd_format_t            format;
+       unsigned int            nbframes;       // current size, in sample frames
+       unsigned int            maxframes;      // max size (buffer size), in sample frames
+       unsigned char           samples[4];     // variable sized
+} snd_buffer_t;
+
+typedef struct snd_ringbuffer_s
+{
+       snd_format_t            format;
+       unsigned char*          ring;
+       unsigned int            maxframes;      // max size (buffer size), in sample frames
+       unsigned int            startframe;     // index of the first frame in the buffer
+                                                                       // if startframe == endframe, the bufffer is empty
+       unsigned int            endframe;       // index of the first EMPTY frame in the "ring" buffer
+                                                                       // may be smaller than startframe if the "ring" buffer has wrapped
+} snd_ringbuffer_t;
+
 // sfx_t flags
 #define SFXFLAG_NONE                   0
 #define SFXFLAG_FILEMISSING            (1 << 0) // wasn't able to load the associated sound file
@@ -51,43 +63,32 @@ struct sfx_s
        char                            name[MAX_QPATH];
        sfx_t                           *next;
        size_t                          memsize;                // total memory used (including sfx_t and fetcher data)
-       int                                     locks;                  // One lock is automatically granted while the sfx is
+
+                                                                               // One lock is automatically granted while the sfx is
                                                                                // playing (and removed when stopped). Locks can also be
-                                                                               // added by S_PrecacheSound and S_ServerSounds.
+       int                                     locks;                  // added by S_PrecacheSound and S_ServerSounds.
                                                                                // A SFX with no lock and no SFXFLAG_PERMANENTLOCK is
                                                                                // freed at level change by S_ServerSounds.
+
        unsigned int            flags;                  // cf SFXFLAG_* defines
-       snd_format_t            format;
-       int                                     loopstart;
-       unsigned int            total_length;
+       int                                     loopstart;              // in sample frames. -1 if not looped
+       unsigned int            total_length;   // in sample frames
        const snd_fetcher_t     *fetcher;
        void                            *fetcher_data;  // Per-sfx data for the sound fetching functions
 };
 
-typedef struct dma_s
-{
-       snd_format_t    format;
-       int                             sampleframes;   // frames in buffer (frame = samples for all speakers)
-       int                             samples;                // mono samples in buffer
-       int                             samplepos;              // in mono samples
-       unsigned char   *buffer;
-       int                             bufferlength;   // used only by certain drivers
-} dma_t;
-
 // maximum supported speakers constant
 #define SND_LISTENERS 8
 
 typedef struct channel_s
 {
-       int pad[8];
+       short                   listener_volume [SND_LISTENERS];        // 0-255 volume per speaker
+       int                             master_vol;             // 0-255 master volume
        sfx_t                   *sfx;                   // sfx number
-       int pad2[8];
        unsigned int    flags;                  // cf CHANNELFLAG_* defines
-       int                             master_vol;             // 0-255 master volume
-       short                   listener_volume[SND_LISTENERS];         // 0-255 volume per speaker
-       int                             end;                    // end time in global paintsamples
-       int                             lastptime;              // last time this channel was painted
-       int                             pos;                    // sample position in sfx
+       unsigned int    end;                    // end time in global paintsamples
+       unsigned int    lastptime;              // last time this channel was painted
+       unsigned int    pos;                    // sample position in sfx
        int                             entnum;                 // to allow overriding a specific sound
        int                             entchannel;
        vec3_t                  origin;                 // origin of sound effect
@@ -95,61 +96,87 @@ typedef struct channel_s
        void                    *fetcher_data;  // Per-channel data for the sound fetching function
 } channel_t;
 
-typedef const sfxbuffer_t* (*snd_fetcher_getsb_t) (channel_t* ch, unsigned int start, unsigned int nbsamples);
+// Sound fetching functions
+// "start" is both an input and output parameter: it returns the actual start time of the sound buffer
+typedef const snd_buffer_t* (*snd_fetcher_getsb_t) (channel_t* ch, unsigned int *start, unsigned int nbsampleframes);
 typedef void (*snd_fetcher_endsb_t) (channel_t* ch);
 typedef void (*snd_fetcher_free_t) (sfx_t* sfx);
+typedef const snd_format_t* (*snd_fetcher_getfmt_t) (sfx_t* sfx);
 struct snd_fetcher_s
 {
        snd_fetcher_getsb_t             getsb;
        snd_fetcher_endsb_t             endsb;
        snd_fetcher_free_t              free;
+       snd_fetcher_getfmt_t    getfmt;
 };
 
-void S_PaintChannels(int endtime);
+// 0 to NUM_AMBIENTS - 1 = water, etc
+// NUM_AMBIENTS to NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS - 1 = normal entity sounds
+// NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS to total_channels = static sounds
+#define        MAX_DYNAMIC_CHANNELS    128
+#define        MAX_CHANNELS                    516
 
-// initializes cycling through a DMA buffer and returns information on it
-qboolean SNDDMA_Init(void);
+extern unsigned int total_channels;
+extern channel_t channels[MAX_CHANNELS];
 
-// gets the current DMA position
-int SNDDMA_GetDMAPos(void);
+extern snd_ringbuffer_t *snd_renderbuffer;
+extern unsigned int soundtime; // WARNING: sound modules must NOT use it
 
-void SNDDMA_Submit(void);
+extern cvar_t _snd_mixahead;
+extern cvar_t snd_swapstereo;
+extern cvar_t snd_streaming;
+
+extern int snd_blocked;                // counter. When > 0, we stop submitting sound to the audio device
+
+extern mempool_t *snd_mempool;
 
-// shutdown the DMA xfer.
-void SNDDMA_Shutdown(void);
+// If simsound is true, the sound card is not initialized and no sound is submitted to it.
+// More generally, all arch-dependent operations are skipped or emulated.
+// Used for isolating performance in the renderer.
+extern qboolean simsound;
 
-qboolean S_LoadSound (sfx_t *s, qboolean complain);
+
+// ====================================================================
+//         Architecture-independent functions
+// ====================================================================
+
+void S_PaintChannels (snd_ringbuffer_t* rb, unsigned int starttime, unsigned int endtime);
+
+qboolean S_LoadSound (sfx_t *sfx, qboolean complain);
 
 void S_LockSfx (sfx_t *sfx);
 void S_UnlockSfx (sfx_t *sfx);
 
-void *S_LockBuffer(void);
-void S_UnlockBuffer(void);
+snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed);
+qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format);
 
-extern size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_format_t* in_format, unsigned char *out_data, const char* sfxname);
+// If "buffer" is NULL, the function allocates one buffer of "sampleframes" sample frames itself
+// (if "sampleframes" is 0, the function chooses the size).
+snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer);
 
-// ====================================================================
 
-// 0 to NUM_AMBIENTS - 1 = water, etc
-// NUM_AMBIENTS to NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS - 1 = normal entity sounds
-// NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS to total_channels = static sounds
-#define        MAX_CHANNELS                    516
-#define        MAX_DYNAMIC_CHANNELS    128
+// ====================================================================
+//         Architecture-dependent functions
+// ====================================================================
 
-extern channel_t channels[MAX_CHANNELS];
+// Create "snd_renderbuffer" with the proper sound format if the call is successful
+// May return a suggested format if the requested format isn't available
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested);
 
-extern unsigned int total_channels;
+// Stop the sound card, delete "snd_renderbuffer" and free its other resources
+void SndSys_Shutdown (void);
 
-extern int paintedtime;
-extern int soundtime;
-extern volatile dma_t *shm;
+// Submit the contents of "snd_renderbuffer" to the sound card
+void SndSys_Submit (void);
 
-extern cvar_t snd_swapstereo;
-extern cvar_t snd_streaming;
+// Returns the number of sample frames consumed since the sound started
+unsigned int SndSys_GetSoundTime (void);
 
-extern int snd_blocked;
+// Get the exclusive lock on "snd_renderbuffer"
+qboolean SndSys_LockRenderBuffer (void);
 
-extern mempool_t *snd_mempool;
+// Release the exclusive lock on "snd_renderbuffer"
+void SndSys_UnlockRenderBuffer (void);
 
 
 #endif
index 37781bf..777dd6c 100644 (file)
--- a/snd_mem.c
+++ b/snd_mem.c
@@ -27,32 +27,126 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 
 /*
-================
-ResampleSfx
-================
+====================
+Snd_CreateRingBuffer
+
+If "buffer" is NULL, the function allocates one buffer of "sampleframes" sample frames itself
+(if "sampleframes" is 0, the function chooses the size).
+====================
+*/
+snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer)
+{
+       snd_ringbuffer_t *ringbuffer;
+
+       // If the caller provides a buffer, it must give us its size
+       if (sampleframes == 0 && buffer != NULL)
+               return NULL;
+
+       ringbuffer = (snd_ringbuffer_t*)Mem_Alloc(snd_mempool, sizeof (*ringbuffer));
+       memset(ringbuffer, 0, sizeof(*ringbuffer));
+       memcpy(&ringbuffer->format, format, sizeof(ringbuffer->format));
+
+       // If we haven't been given a buffer
+       if (buffer == NULL)
+       {
+               unsigned int maxframes;
+               size_t memsize;
+
+               if (sampleframes == 0)
+                       maxframes = (format->speed + 1) / 2;  // Make the sound buffer large enough for containing 0.5 sec of sound
+               else
+                       maxframes = sampleframes;
+
+               memsize = maxframes * format->width * format->channels;
+               ringbuffer->ring = Mem_Alloc(snd_mempool, memsize);
+               ringbuffer->maxframes = maxframes;
+       }
+       else
+       {
+               ringbuffer->ring = buffer;
+               ringbuffer->maxframes = sampleframes;
+       }
+
+       return ringbuffer;
+}
+
+
+/*
+====================
+Snd_CreateSndBuffer
+====================
 */
-size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_format_t* in_format, unsigned char *out_data, const char* sfxname)
+snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed)
+{
+       size_t newsampleframes, memsize;
+       snd_buffer_t* sb;
+       
+       newsampleframes = (double)sampleframes * (double)sb_speed / (double)in_format->speed;
+
+       memsize = newsampleframes * in_format->channels * in_format->width;
+       memsize += sizeof (*sb) - sizeof (sb->samples);
+
+       sb = (snd_buffer_t*)Mem_Alloc (snd_mempool, memsize);
+       sb->format.channels = in_format->channels;
+       sb->format.width = in_format->width;
+       sb->format.speed = sb_speed;
+       sb->maxframes = newsampleframes;
+       sb->nbframes = 0;
+
+       if (!Snd_AppendToSndBuffer (sb, samples, sampleframes, in_format))
+       {
+               Mem_Free (sb);
+               return NULL;
+       }
+
+       return sb;
+}
+
+
+/*
+====================
+Snd_AppendToSndBuffer
+====================
+*/
+qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format)
 {
        size_t srclength, outcount;
+       unsigned char *out_data;
 
-       srclength = in_length * in_format->channels;
-       outcount = (int)((double)in_length * shm->format.speed / in_format->speed);
+       //Con_DPrintf("ResampleSfx: %d samples @ %dHz -> %d samples @ %dHz\n",
+       //                      sampleframes, format->speed, outcount, sb->format.speed);
 
-       //Con_DPrintf("ResampleSfx(%s): %d samples @ %dHz -> %d samples @ %dHz\n",
-       //                      sfxname, in_length, in_format->speed, outcount, shm->format.speed);
+       // If the formats are incompatible
+       if (sb->format.channels != format->channels || sb->format.width != format->width)
+       {
+               Con_Print("AppendToSndBuffer: incompatible sound formats!\n");
+               return false;
+       }
+
+       outcount = (double)sampleframes * (double)sb->format.speed / (double)format->speed;
+
+       // If the sound buffer is too short
+       if (outcount > sb->maxframes - sb->nbframes)
+       {
+               Con_Print("AppendToSndBuffer: sound buffer too short!\n");
+               return false;
+       }
+
+       out_data = &sb->samples[sb->nbframes * sb->format.width * sb->format.channels];
+       srclength = sampleframes * format->channels;
 
        // Trivial case (direct transfer)
-       if (in_format->speed == shm->format.speed)
+       if (format->speed == sb->format.speed)
        {
-               if (in_format->width == 1)
+               if (format->width == 1)
                {
                        size_t i;
 
                        for (i = 0; i < srclength; i++)
-                               ((signed char*)out_data)[i] = in_data[i] - 128;
+                               ((signed char*)out_data)[i] = samples[i] - 128;
                }
-               else  // if (in_format->width == 2)
-                       memcpy (out_data, in_data, srclength * in_format->width);
+               else  // if (format->width == 2)
+                       memcpy (out_data, samples, srclength * format->width);
        }
 
        // General case (linear interpolation with a fixed-point fractional
@@ -63,17 +157,17 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo
 #      define INTEGER_BITS (sizeof(samplefrac)*8 - FRACTIONAL_BITS)
        else
        {
-               const unsigned int fracstep = (unsigned int)((double)in_format->speed / shm->format.speed * (1 << FRACTIONAL_BITS));
+               const unsigned int fracstep = (unsigned int)((double)format->speed / sb->format.speed * (1 << FRACTIONAL_BITS));
                size_t remain_in = srclength, total_out = 0;
                unsigned int samplefrac;
-               const unsigned char *in_ptr = in_data;
+               const unsigned char *in_ptr = samples;
                unsigned char *out_ptr = out_data;
 
                // Check that we can handle one second of that sound
-               if (in_format->speed * in_format->channels > (1 << INTEGER_BITS))
+               if (format->speed * format->channels > (1 << INTEGER_BITS))
                {
                        Con_Printf ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))\n",
-                                          in_format->speed, in_format->channels);
+                                          format->speed, format->channels);
                        return 0;
                }
 
@@ -88,36 +182,36 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo
                        samplefrac = 0;
 
                        // If more than 1 sec of sound remains to be converted
-                       if (outcount - total_out > shm->format.speed)
+                       if (outcount - total_out > sb->format.speed)
                        {
-                               tmpcount = shm->format.speed;
+                               tmpcount = sb->format.speed;
                                interpolation_limit = tmpcount;  // all samples can be interpolated
                        }
                        else
                        {
                                tmpcount = outcount - total_out;
-                               interpolation_limit = (int)ceil((double)(((remain_in / in_format->channels) - 1) << FRACTIONAL_BITS) / fracstep);
+                               interpolation_limit = (int)ceil((double)(((remain_in / format->channels) - 1) << FRACTIONAL_BITS) / fracstep);
                                if (interpolation_limit > tmpcount)
                                        interpolation_limit = tmpcount;
                        }
 
                        // 16 bit samples
-                       if (in_format->width == 2)
+                       if (format->width == 2)
                        {
                                const short* in_ptr_short;
 
                                // Interpolated part
                                for (i = 0; i < interpolation_limit; i++)
                                {
-                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
                                        in_ptr_short = &((const short*)in_ptr)[srcsample];
 
-                                       for (j = 0; j < in_format->channels; j++)
+                                       for (j = 0; j < format->channels; j++)
                                        {
                                                int a, b;
 
                                                a = *in_ptr_short;
-                                               b = *(in_ptr_short + in_format->channels);
+                                               b = *(in_ptr_short + format->channels);
                                                *((short*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
 
                                                in_ptr_short++;
@@ -130,10 +224,10 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo
                                // Non-interpolated part
                                for (/* nothing */; i < tmpcount; i++)
                                {
-                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
                                        in_ptr_short = &((const short*)in_ptr)[srcsample];
 
-                                       for (j = 0; j < in_format->channels; j++)
+                                       for (j = 0; j < format->channels; j++)
                                        {
                                                *((short*)out_ptr) = *in_ptr_short;
 
@@ -145,22 +239,22 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo
                                }
                        }
                        // 8 bit samples
-                       else  // if (in_format->width == 1)
+                       else  // if (format->width == 1)
                        {
                                const unsigned char* in_ptr_byte;
 
                                // Convert up to 1 sec of sound
                                for (i = 0; i < interpolation_limit; i++)
                                {
-                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
                                        in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
 
-                                       for (j = 0; j < in_format->channels; j++)
+                                       for (j = 0; j < format->channels; j++)
                                        {
                                                int a, b;
 
                                                a = *in_ptr_byte - 128;
-                                               b = *(in_ptr_byte + in_format->channels) - 128;
+                                               b = *(in_ptr_byte + format->channels) - 128;
                                                *((signed char*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a;
 
                                                in_ptr_byte++;
@@ -173,10 +267,10 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo
                                // Non-interpolated part
                                for (/* nothing */; i < tmpcount; i++)
                                {
-                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels;
+                                       srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels;
                                        in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample];
 
-                                       for (j = 0; j < in_format->channels; j++)
+                                       for (j = 0; j < format->channels; j++)
                                        {
                                                *((signed char*)out_ptr) = *in_ptr_byte - 128;
 
@@ -189,15 +283,17 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo
                        }
 
                        // Update the counters and the buffer position
-                       remain_in -= in_format->speed * in_format->channels;
-                       in_ptr += in_format->speed * in_format->channels * in_format->width;
+                       remain_in -= format->speed * format->channels;
+                       in_ptr += format->speed * format->channels * format->width;
                        total_out += tmpcount;
                }
        }
 
-       return outcount;
+       sb->nbframes += outcount;
+       return true;
 }
 
+
 //=============================================================================
 
 /*
@@ -205,62 +301,59 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo
 S_LoadSound
 ==============
 */
-qboolean S_LoadSound (sfx_t *s, qboolean complain)
+qboolean S_LoadSound (sfx_t *sfx, qboolean complain)
 {
        char namebuffer[MAX_QPATH + 16];
        size_t len;
 
-       if (!shm || !shm->format.speed)
-               return false;
+       // See if already loaded
+       if (sfx->fetcher != NULL)
+               return true;
 
        // If we weren't able to load it previously, no need to retry
-       if (s->flags & SFXFLAG_FILEMISSING)
+       if (sfx->flags & SFXFLAG_FILEMISSING)
                return false;
 
-       // See if in memory
-       if (s->fetcher != NULL)
-       {
-               if (s->format.speed != shm->format.speed)
-                       Con_Printf ("S_LoadSound: sound %s hasn't been resampled (%uHz instead of %uHz)\n", s->name);
-               return true;
-       }
+       // No sound?
+       if (snd_renderbuffer == NULL)
+               return false;
 
        // LordHavoc: if the sound filename does not begin with sound/, try adding it
-       if (strncasecmp(s->name, "sound/", 6))
+       if (strncasecmp(sfx->name, "sound/", 6))
        {
-               len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", s->name);
+               len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", sfx->name);
                if (len < 0)
                {
                        // name too long
-                       Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name);
+                       Con_Printf("S_LoadSound: name \"%s\" is too long\n", sfx->name);
                        return false;
                }
-               if (S_LoadWavFile (namebuffer, s))
+               if (S_LoadWavFile (namebuffer, sfx))
                        return true;
                if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav"))
                        strcpy (namebuffer + len - 3, "ogg");
-               if (OGG_LoadVorbisFile (namebuffer, s))
+               if (OGG_LoadVorbisFile (namebuffer, sfx))
                        return true;
        }
 
        // LordHavoc: then try without the added sound/ as wav and ogg
-       len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", s->name);
+       len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", sfx->name);
        if (len < 0)
        {
                // name too long
-               Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name);
+               Con_Printf("S_LoadSound: name \"%s\" is too long\n", sfx->name);
                return false;
        }
-       if (S_LoadWavFile (namebuffer, s))
+       if (S_LoadWavFile (namebuffer, sfx))
                return true;
        if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav"))
                strcpy (namebuffer + len - 3, "ogg");
-       if (OGG_LoadVorbisFile (namebuffer, s))
+       if (OGG_LoadVorbisFile (namebuffer, sfx))
                return true;
 
        // Can't load the sound!
-       s->flags |= SFXFLAG_FILEMISSING;
+       sfx->flags |= SFXFLAG_FILEMISSING;
        if (complain)
-               Con_Printf("S_LoadSound: Couldn't load \"%s\"\n", s->name);
+               Con_Printf("S_LoadSound: Couldn't load \"%s\"\n", sfx->name);
        return false;
 }
index 03ba042..0e4dada 100644 (file)
--- a/snd_mix.c
+++ b/snd_mix.c
@@ -17,11 +17,11 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 */
-// snd_mix.c -- portable code to mix sounds
 
 #include "quakedef.h"
 #include "snd_main.h"
 
+
 typedef struct portable_samplepair_s
 {
        int sample[SND_LISTENERS];
@@ -31,285 +31,299 @@ typedef struct portable_samplepair_s
 #define        PAINTBUFFER_SIZE 2048
 portable_sampleframe_t paintbuffer[PAINTBUFFER_SIZE];
 
+
 // FIXME: this desyncs with the video too easily
 extern void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate);
-void S_CaptureAVISound(portable_sampleframe_t *buf, size_t length)
+static void S_CaptureAVISound(size_t length)
 {
-       int n;
        size_t i;
        unsigned char out[PAINTBUFFER_SIZE * 4];
+       unsigned char* out_ptr;
+
        if (!cls.capturevideo_active)
                return;
+
        // write the sound buffer as little endian 16bit interleaved stereo
-       for(i = 0;i < length;i++)
+       for(i = 0, out_ptr = out; i < length; i++, out_ptr += 4)
        {
-               n = buf[i].sample[0];
-               n = bound(-32768, n, 32767);
-               out[i*4+0] = n & 0xFF;
-               out[i*4+1] = (n >> 8) & 0xFF;
-               n = buf[i].sample[1];
-               n = bound(-32768, n, 32767);
-               out[i*4+2] = n & 0xFF;
-               out[i*4+3] = (n >> 8) & 0xFF;
+               int n0, n1;
+
+               n0 = paintbuffer[i].sample[0];
+               n0 = bound(-32768, n0, 32767);
+               out_ptr[0] = (unsigned char)n0;
+               out_ptr[1] = (unsigned char)(n0 >> 8);
+
+               n1 = paintbuffer[i].sample[1];
+               n1 = bound(-32768, n1, 32767);
+               out_ptr[2] = (unsigned char)n1;
+               out_ptr[3] = (unsigned char)(n1 >> 8);
        }
-       SCR_CaptureVideo_SoundFrame(out, length, shm->format.speed);
+       SCR_CaptureVideo_SoundFrame(out, length, snd_renderbuffer->format.speed);
 }
 
-// TODO: rewrite this function
-void S_TransferPaintBuffer(int endtime)
+static unsigned int S_TransferPaintBuffer(snd_ringbuffer_t* rb, unsigned int starttime, unsigned int endtime)
 {
-       void *pbuf;
-       int i;
-       portable_sampleframe_t *snd_p;
-       int lpaintedtime;
-       int snd_frames;
-       int val;
-       if ((pbuf = S_LockBuffer()))
+       unsigned int partialend;
+
+       // Lock submitbuffer
+       if (!simsound && !SndSys_LockRenderBuffer())
+               return 0;
+       
+       partialend = starttime;
+       while (partialend < endtime)  // handle recirculating buffer issues
        {
-               snd_p = paintbuffer;
-               lpaintedtime = paintedtime;
-               for (lpaintedtime = paintedtime;lpaintedtime < endtime;lpaintedtime += snd_frames)
+               unsigned int startoffset, maxframes, nbframes, i;
+               void *rb_ptr;
+               portable_sampleframe_t *painted_ptr;
+               int val;
+
+               startoffset = partialend % rb->maxframes;
+               maxframes = rb->maxframes - startoffset;
+               nbframes = endtime - partialend;
+               if (nbframes > maxframes)
+                       nbframes = maxframes;
+
+               rb_ptr = &rb->ring[startoffset * rb->format.width * rb->format.channels];
+               painted_ptr = &paintbuffer[partialend - starttime];
+
+               if (rb->format.width == 2)  // 16bit
                {
-                       // handle recirculating buffer issues
-                       i = lpaintedtime & (shm->sampleframes - 1);
-                       snd_frames = shm->sampleframes - i;
-                       if (snd_frames > endtime - lpaintedtime)
-                               snd_frames = endtime - lpaintedtime;
-                       if (shm->format.width == 2)
+                       short *snd_out = (short*)rb_ptr;
+                       if (rb->format.channels == 8)  // 7.1 surround
                        {
-                               // 16bit
-                               short *snd_out = (short *) pbuf + i * shm->format.channels;
-                               if (shm->format.channels == 8)
-                               {
-                                       // 7.1 surround
-                                       if (snd_swapstereo.value)
-                                       {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[3], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[2], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[4], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[5], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[6], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[7], 32767);
-                                               }
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
+                                       {
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[6], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[7], 32767);
                                        }
-                                       else
+                               }
+                               else
+                               {
+                                       for (i = 0;i < nbframes;i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[2], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[3], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[4], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[5], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[6], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[7], 32767);
-                                               }
-                                       }
-                               }
-                               else if (shm->format.channels == 6)
-                               {
-                                       // 5.1 surround
-                                       if (snd_swapstereo.value)
-                                       {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[3], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[2], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[4], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[5], 32767);
-                                               }
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[6], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[7], 32767);
                                        }
-                                       else
+                               }
+                       }
+                       else if (rb->format.channels == 6)  // 5.1 surround
+                       {
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[2], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[3], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[4], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[5], 32767);
-                                               }
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
                                        }
                                }
-                               else if (shm->format.channels == 4)
+                               else
                                {
-                                       // 4.0 surround
-                                       if (snd_swapstereo.value)
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[3], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[2], 32767);
-                                               }
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767);
                                        }
-                                       else
+                               }
+                       }
+                       else if (rb->format.channels == 4)  // 4.0 surround
+                       {
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[2], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[3], 32767);
-                                               }
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
                                        }
                                }
-                               else if (shm->format.channels == 2)
+                               else
                                {
-                                       // 2.0 stereo
-                                       if (snd_swapstereo.value)
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                               }
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767);
                                        }
-                                       else
+                               }
+                       }
+                       else if (rb->format.channels == 2)  // 2.0 stereo
+                       {
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       *snd_out++ = bound(-32768, snd_p->sample[0], 32767);
-                                                       *snd_out++ = bound(-32768, snd_p->sample[1], 32767);
-                                               }
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
                                        }
                                }
-                               else if (shm->format.channels == 1)
+                               else
                                {
-                                       // 1.0 mono
-                                       for (i = 0;i < snd_frames;i++, snd_p++)
-                                               *snd_out++ = bound(-32768, (snd_p->sample[0] + snd_p->sample[1]) >> 1, 32767);
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
+                                       {
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767);
+                                               *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767);
+                                       }
                                }
                        }
-                       else
+                       else if (rb->format.channels == 1)  // 1.0 mono
                        {
-                               // 8bit
-                               unsigned char *snd_out = (unsigned char *) pbuf + i * shm->format.channels;
-                               if (shm->format.channels == 8)
-                               {
-                                       // 7.1 surround
-                                       if (snd_swapstereo.value)
-                                       {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
+                               for (i = 0; i < nbframes; i++, painted_ptr++)
+                               {
+                                       val = (painted_ptr->sample[0] + painted_ptr->sample[1]) >> 1;
+                                       *snd_out++ = bound(-32768, val, 32767);
+                               }
+                       }
+               }
+               else  // 8bit
+               {
+                       unsigned char *snd_out = (unsigned char*)rb_ptr;
+                       if (rb->format.channels == 8)  // 7.1 surround
+                       {
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
+                                       {
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[6] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[7] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
-                                       else
+                               }
+                               else
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
-                                       }
-                               }
-                               else if (shm->format.channels == 6)
-                               {
-                                       // 5.1 surround
-                                       if (snd_swapstereo.value)
-                                       {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[6] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[7] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
-                                       else
+                               }
+                       }
+                       else if (rb->format.channels == 6)  // 5.1 surround
+                       {
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
                                }
-                               else if (shm->format.channels == 4)
+                               else
                                {
-                                       // 4.0 surround
-                                       if (snd_swapstereo.value)
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
-                                       else
+                               }
+                       }
+                       else if (rb->format.channels == 4)  // 4.0 surround
+                       {
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
                                }
-                               else if (shm->format.channels == 2)
+                               else
                                {
-                                       // 2.0 stereo
-                                       if (snd_swapstereo.value)
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
-                                       else
+                               }
+                       }
+                       else if (rb->format.channels == 2)  // 2.0 stereo
+                       {
+                               if (snd_swapstereo.value)
+                               {
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               for (i = 0;i < snd_frames;i++, snd_p++)
-                                               {
-                                                       val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                                       val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
-                                               }
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
                                }
-                               else if (shm->format.channels == 1)
+                               else
                                {
-                                       // 1.0 mono
-                                       for (i = 0;i < snd_frames;i++, snd_p++)
+                                       for (i = 0; i < nbframes; i++, painted_ptr++)
                                        {
-                                               val = ((snd_p->sample[0]+snd_p->sample[1]) >> 9) + 128;*snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255);
+                                               val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255);
                                        }
                                }
                        }
+                       else if (rb->format.channels == 1)  // 1.0 mono
+                       {
+                               for (i = 0;i < nbframes;i++, painted_ptr++)
+                               {
+                                       val = ((painted_ptr->sample[0] + painted_ptr->sample[1]) >> 9) + 128;
+                                       *snd_out++ = bound(0, val, 255);
+                               }
+                       }
                }
-               S_UnlockBuffer();
+
+               partialend += nbframes;
        }
+
+       rb->endframe = endtime;
+
+       // Remove outdated samples from the ring buffer, if any
+       if (rb->startframe < soundtime)
+               rb->startframe = soundtime;
+
+       if (!simsound)
+               SndSys_UnlockRenderBuffer();
+
+       return endtime - starttime;
 }
 
 
@@ -321,32 +335,287 @@ CHANNEL MIXING
 ===============================================================================
 */
 
-qboolean SND_PaintChannel (channel_t *ch, int endtime);
+static qboolean SND_PaintChannel (channel_t *ch, unsigned int count)
+{
+       int snd_vol, vol[SND_LISTENERS];
+       const snd_buffer_t *sb;
+       unsigned int i, sb_offset;
+
+       // If this channel manages its own volume
+       if (ch->flags & CHANNELFLAG_FULLVOLUME)
+               snd_vol = 256;
+       else
+               snd_vol = (int)(volume.value * 256);
+
+       for (i = 0;i < SND_LISTENERS;i++)
+               vol[i] = ch->listener_volume[i] * snd_vol;
+
+       sb_offset = ch->pos;
+       sb = ch->sfx->fetcher->getsb (ch, &sb_offset, count);
+       if (sb == NULL)
+       {
+               Con_DPrintf("SND_PaintChannel: ERROR: can't get sound buffer from sfx \"%s\"\n",
+                                       ch->sfx->name, count);
+       }
+       else
+       {
+#if SND_LISTENERS != 8
+#              error the following code only supports up to 8 channels, update it
+#endif
+               if (sb->format.width == 1)
+               {
+                       const signed char *samples = (signed char*)sb->samples + (ch->pos - sb_offset) * sb->format.channels;
+
+                       // Stereo sound support
+                       if (sb->format.channels == 2)
+                       {
+                               if (vol[6] + vol[7] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8;
+                                               paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 8;
+                                               paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9;
+                                               paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9;
+                                               paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 8;
+                                               paintbuffer[i].sample[7] += (samples[1] * vol[7]) >> 8;
+                                               samples += 2;
+                                       }
+                               }
+                               else if (vol[4] + vol[5] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8;
+                                               paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 8;
+                                               paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9;
+                                               paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9;
+                                               samples += 2;
+                                       }
+                               }
+                               else if (vol[2] + vol[3] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8;
+                                               paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 8;
+                                               samples += 2;
+                                       }
+                               }
+                               else if (vol[0] + vol[1] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8;
+                                               samples += 2;
+                                       }
+                               }
+                       }
+                       else if (sb->format.channels == 1)
+                       {
+                               if (vol[6] + vol[7] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8;
+                                               paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 8;
+                                               paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 8;
+                                               paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 8;
+                                               paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 8;
+                                               paintbuffer[i].sample[7] += (samples[0] * vol[7]) >> 8;
+                                               samples += 1;
+                                       }
+                               }
+                               else if (vol[4] + vol[5] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8;
+                                               paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 8;
+                                               paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 8;
+                                               paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 8;
+                                               samples += 1;
+                                       }
+                               }
+                               else if (vol[2] + vol[3] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8;
+                                               paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 8;
+                                               samples += 1;
+                                       }
+                               }
+                               else if (vol[0] + vol[1] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8;
+                                               samples += 1;
+                                       }
+                               }
+                       }
+                       else
+                               return false; // unsupported number of channels in sound
+               }
+               else if (sb->format.width == 2)
+               {
+                       const signed short *samples = (signed short*)sb->samples + (ch->pos - sb_offset) * sb->format.channels;
 
-void S_PaintChannels(int endtime)
+                       // Stereo sound support
+                       if (sb->format.channels == 2)
+                       {
+                               if (vol[6] + vol[7] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16;
+                                               paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 16;
+                                               paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17;
+                                               paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17;
+                                               paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 16;
+                                               paintbuffer[i].sample[7] += (samples[1] * vol[7]) >> 16;
+                                               samples += 2;
+                                       }
+                               }
+                               else if (vol[4] + vol[5] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16;
+                                               paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 16;
+                                               paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17;
+                                               paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17;
+                                               samples += 2;
+                                       }
+                               }
+                               else if (vol[2] + vol[3] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16;
+                                               paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 16;
+                                               samples += 2;
+                                       }
+                               }
+                               else if (vol[0] + vol[1] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16;
+                                               samples += 2;
+                                       }
+                               }
+                       }
+                       else if (sb->format.channels == 1)
+                       {
+                               if (vol[6] + vol[7] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16;
+                                               paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 16;
+                                               paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 16;
+                                               paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 16;
+                                               paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 16;
+                                               paintbuffer[i].sample[7] += (samples[0] * vol[7]) >> 16;
+                                               samples += 1;
+                                       }
+                               }
+                               else if (vol[4] + vol[5] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16;
+                                               paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 16;
+                                               paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 16;
+                                               paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 16;
+                                               samples += 1;
+                                       }
+                               }
+                               else if (vol[2] + vol[3] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16;
+                                               paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16;
+                                               paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 16;
+                                               samples += 1;
+                                       }
+                               }
+                               else if (vol[0] + vol[1] > 0)
+                               {
+                                       for (i = 0;i < count;i++)
+                                       {
+                                               paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16;
+                                               paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16;
+                                               samples += 1;
+                                       }
+                               }
+                       }
+                       else
+                               return false; // unsupported number of channels in sound
+               }
+       }
+
+       ch->pos += count;
+       return true;
+}
+
+void S_PaintChannels (snd_ringbuffer_t* rb, unsigned int starttime, unsigned int endtime)
 {
-       unsigned int i, j;
-       int end;
-       channel_t *ch;
-       sfx_t *sfx;
-       int ltime, count;
+       unsigned int paintedtime;
 
+       paintedtime = starttime;
        while (paintedtime < endtime)
        {
-               // if paintbuffer is smaller than DMA buffer
-               end = endtime;
-               if (endtime - paintedtime > PAINTBUFFER_SIZE)
-                       end = paintedtime + PAINTBUFFER_SIZE;
+               unsigned int partialend, i, framecount;
+               channel_t *ch;
+
+               // if paintbuffer is too small
+               if (endtime > paintedtime + PAINTBUFFER_SIZE)
+                       partialend = paintedtime + PAINTBUFFER_SIZE;
+               else
+                       partialend = endtime;
 
                // clear the paint buffer
-               memset (&paintbuffer, 0, (end - paintedtime) * sizeof (paintbuffer[0]));
+               memset (paintbuffer, 0, (partialend - paintedtime) * sizeof (paintbuffer[0]));
 
                // paint in the channels.
                ch = channels;
-               for (i=0; i<total_channels ; i++, ch++)
+               for (i = 0; i < total_channels ; i++, ch++)
                {
+                       sfx_t *sfx;
+                       unsigned int ltime, j;
+
                        sfx = ch->sfx;
-                       if (!sfx)
+                       if (sfx == NULL)
                                continue;
                        for (j = 0;j < SND_LISTENERS;j++)
                                if (ch->listener_volume[j])
@@ -359,7 +628,7 @@ void S_PaintChannels(int endtime)
                        // if the channel is paused
                        if (ch->flags & CHANNELFLAG_PAUSED)
                        {
-                               int pausedtime = end - paintedtime;
+                               int pausedtime = partialend - paintedtime;
                                ch->lastptime += pausedtime;
                                ch->end += pausedtime;
                                continue;
@@ -375,12 +644,15 @@ void S_PaintChannels(int endtime)
                                {
                                        int loopstart;
 
-                                       if (ch->flags & CHANNELFLAG_FORCELOOP)
-                                               loopstart = 0;
-                                       else
-                                               loopstart = -1;
                                        if (sfx->loopstart >= 0)
-                                               loopstart = sfx->loopstart;
+                                               loopstart = bound(0, sfx->loopstart, (int)sfx->total_length - 1);
+                                       else
+                                       {
+                                               if (ch->flags & CHANNELFLAG_FORCELOOP)
+                                                       loopstart = 0;
+                                               else
+                                                       loopstart = -1;
+                                       }
 
                                        // If the sound is looped
                                        if (loopstart >= 0)
@@ -392,23 +664,23 @@ void S_PaintChannels(int endtime)
                        }
 
                        ltime = paintedtime;
-                       while (ltime < end)
+                       while (ltime < partialend)
                        {
+                               int count;
                                qboolean stop_paint;
 
                                // paint up to end
-                               if (ch->end < end)
-                                       count = (int)ch->end - ltime;
+                               if (ch->end < partialend)
+                                       count = ch->end - ltime;
                                else
-                                       count = end - ltime;
+                                       count = partialend - ltime;
 
                                if (count > 0)
                                {
-                                       for (j = 0;j < SND_LISTENERS;j++)
+                                       for (j = 0; j < SND_LISTENERS; j++)
                                                ch->listener_volume[j] = bound(0, ch->listener_volume[j], 255);
 
-                                       stop_paint = !SND_PaintChannel (ch, count);
-
+                                       stop_paint = !SND_PaintChannel (ch, (unsigned int)count);
                                        if (!stop_paint)
                                        {
                                                ltime += count;
@@ -439,256 +711,15 @@ void S_PaintChannels(int endtime)
                        }
                }
 
-               // transfer out according to DMA format
-               S_CaptureAVISound (paintbuffer, end - paintedtime);
-               S_TransferPaintBuffer(end);
-               paintedtime = end;
-       }
-}
-
+               S_CaptureAVISound (partialend - paintedtime);
+               framecount = S_TransferPaintBuffer (rb, paintedtime, partialend);
+               paintedtime += framecount;
 
-qboolean SND_PaintChannel (channel_t *ch, int count)
-{
-       int snd_vol, vol[SND_LISTENERS];
-       const sfxbuffer_t *sb;
-       int i;
-
-       // If this channel manages its own volume
-       if (ch->flags & CHANNELFLAG_FULLVOLUME)
-               snd_vol = 256;
-       else
-               snd_vol = (int)(volume.value * 256);
-
-       for (i = 0;i < SND_LISTENERS;i++)
-               vol[i] = ch->listener_volume[i] * snd_vol;
-
-       sb = ch->sfx->fetcher->getsb (ch, ch->pos, count);
-       if (sb == NULL)
-               return false;
-
-#if SND_LISTENERS != 8
-#error this code only supports up to 8 channels, update it
-#endif
-
-       if (ch->sfx->format.width == 1)
-       {
-               const signed char *sfx = (signed char *)sb->data + (ch->pos - sb->offset) * ch->sfx->format.channels;
-               // Stereo sound support
-               if (ch->sfx->format.channels == 2)
-               {
-                       if (vol[6] + vol[7] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
-                                       paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8;
-                                       paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9;
-                                       paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9;
-                                       paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8;
-                                       paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 8;
-                                       sfx += 2;
-                               }
-                       }
-                       else if (vol[4] + vol[5] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
-                                       paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8;
-                                       paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9;
-                                       paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9;
-                                       sfx += 2;
-                               }
-                       }
-                       else if (vol[2] + vol[3] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
-                                       paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8;
-                                       sfx += 2;
-                               }
-                       }
-                       else if (vol[0] + vol[1] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
-                                       sfx += 2;
-                               }
-                       }
-               }
-               else if (ch->sfx->format.channels == 1)
-               {
-                       if (vol[6] + vol[7] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
-                                       paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8;
-                                       paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8;
-                                       paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8;
-                                       paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8;
-                                       paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 8;
-                                       sfx += 1;
-                               }
-                       }
-                       else if (vol[4] + vol[5] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
-                                       paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8;
-                                       paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8;
-                                       paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8;
-                                       sfx += 1;
-                               }
-                       }
-                       else if (vol[2] + vol[3] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
-                                       paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8;
-                                       sfx += 1;
-                               }
-                       }
-                       else if (vol[0] + vol[1] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
-                                       sfx += 1;
-                               }
-                       }
-               }
-               else
-                       return true; // unsupported number of channels in sound
-       }
-       else if (ch->sfx->format.width == 2)
-       {
-               const signed short *sfx = (signed short *)sb->data + (ch->pos - sb->offset) * ch->sfx->format.channels;
-               // Stereo sound support
-               if (ch->sfx->format.channels == 2)
-               {
-                       if (vol[6] + vol[7] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
-                                       paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16;
-                                       paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17;
-                                       paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17;
-                                       paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16;
-                                       paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 16;
-                                       sfx += 2;
-                               }
-                       }
-                       else if (vol[4] + vol[5] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
-                                       paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16;
-                                       paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17;
-                                       paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17;
-                                       sfx += 2;
-                               }
-                       }
-                       else if (vol[2] + vol[3] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
-                                       paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16;
-                                       sfx += 2;
-                               }
-                       }
-                       else if (vol[0] + vol[1] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
-                                       sfx += 2;
-                               }
-                       }
-               }
-               else if (ch->sfx->format.channels == 1)
+               // If there was not enough free space in the sound buffer, stop here
+               if (paintedtime != partialend)
                {
-                       if (vol[6] + vol[7] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
-                                       paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16;
-                                       paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16;
-                                       paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16;
-                                       paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16;
-                                       paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 16;
-                                       sfx += 1;
-                               }
-                       }
-                       else if (vol[4] + vol[5] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
-                                       paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16;
-                                       paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16;
-                                       paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16;
-                                       sfx += 1;
-                               }
-                       }
-                       else if (vol[2] + vol[3] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
-                                       paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
-                                       paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16;
-                                       sfx += 1;
-                               }
-                       }
-                       else if (vol[0] + vol[1] > 0)
-                       {
-                               for (i = 0;i < count;i++)
-                               {
-                                       paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
-                                       paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
-                                       sfx += 1;
-                               }
-                       }
+                       Con_DPrintf(">> S_PaintChannels: Not enough free space in the sound buffer ( %u != %u)\n", paintedtime, partialend);
+                       break;
                }
-               else
-                       return true; // unsupported number of channels in sound
        }
-       ch->pos += count;
-       return true;
 }
-
index 3da1059..43619e9 100755 (executable)
@@ -25,8 +25,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1", "volume of background music (such as CD music or replacement files such as sound/cdtracks/track002.ogg)"};
 cvar_t volume = {CVAR_SAVE, "volume", "0.7", "volume of sound effects"};
 cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1", "volume of ambient sound effects (such as swampy sounds at the start of e1m2)"};
-cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1", "how much sound to mix ahead of time"};
-cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0", "swaps left/right speakers for old ISA soundblaster cards"};
 cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"};
 
 void S_Init (void)
@@ -34,8 +32,6 @@ void S_Init (void)
        Cvar_RegisterVariable(&bgmvolume);
        Cvar_RegisterVariable(&volume);
        Cvar_RegisterVariable(&snd_staticvolume);
-       Cvar_RegisterVariable(&_snd_mixahead);
-       Cvar_RegisterVariable(&snd_swapstereo);
        Cvar_RegisterVariable(&snd_initialized);
 }
 
@@ -116,3 +112,11 @@ qboolean S_LocalSound (const char *s)
 {
        return false;
 }
+
+void S_BlockSound (void)
+{
+}
+
+void S_UnblockSound (void)
+{
+}
index b0073d2..0a6f7fd 100644 (file)
--- a/snd_ogg.c
+++ b/snd_ogg.c
@@ -382,7 +382,7 @@ static unsigned char resampling_buffer [48000 * 2 * 2];
 // Per-sfx data structure
 typedef struct
 {
-       unsigned char                   *file;
+       unsigned char   *file;
        size_t                  filesize;
        snd_format_t    format;
 } ogg_stream_persfx_t;
@@ -392,8 +392,9 @@ typedef struct
 {
        OggVorbis_File  vf;
        ov_decode_t             ov_decode;
+       unsigned int    sb_offset;
        int                             bs;
-       sfxbuffer_t             sb;             // must be at the end due to its dynamically allocated size
+       snd_buffer_t    sb;             // must be at the end due to its dynamically allocated size
 } ogg_stream_perchannel_t;
 
 
@@ -404,33 +405,31 @@ static const ov_callbacks callbacks = {ovcb_read, ovcb_seek, ovcb_close, ovcb_te
 OGG_FetchSound
 ====================
 */
-static const sfxbuffer_t* OGG_FetchSound (channel_t* ch, unsigned int start, unsigned int nbsamples)
+static const snd_buffer_t* OGG_FetchSound (channel_t* ch, unsigned int* start, unsigned int nbsampleframes)
 {
        ogg_stream_perchannel_t* per_ch;
-       sfxbuffer_t* sb;
        sfx_t* sfx;
-       snd_format_t* format;
        ogg_stream_persfx_t* per_sfx;
+       snd_format_t* ogg_format;
+       snd_buffer_t* sb;
        int newlength, done, ret, bigendian;
+       unsigned int real_start;
        unsigned int factor;
-       size_t buff_len;
 
        per_ch = (ogg_stream_perchannel_t *)ch->fetcher_data;
        sfx = ch->sfx;
        per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data;
-       format = &sfx->format;
-       buff_len = STREAM_BUFFER_SIZE(format);
+       ogg_format = &per_sfx->format;
 
        // If there's no fetcher structure attached to the channel yet
        if (per_ch == NULL)
        {
-               size_t memsize;
-               ogg_stream_persfx_t* per_sfx;
+               size_t buff_len, memsize;
 
-               memsize = sizeof (*per_ch) - sizeof (per_ch->sb.data) + buff_len;
+               buff_len = STREAM_BUFFER_SIZE(ogg_format);
+               memsize = sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len;
                per_ch = (ogg_stream_perchannel_t *)Mem_Alloc (snd_mempool, memsize);
                sfx->memsize += memsize;
-               per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data;
 
                // Open it with the VorbisFile API
                per_ch->ov_decode.buffer = per_sfx->file;
@@ -442,53 +441,90 @@ static const sfxbuffer_t* OGG_FetchSound (channel_t* ch, unsigned int start, uns
                        Mem_Free (per_ch);
                        return NULL;
                }
-
-               per_ch->sb.offset = 0;
-               per_ch->sb.length = 0;
                per_ch->bs = 0;
 
+               per_ch->sb_offset = 0;
+               per_ch->sb.format.speed = snd_renderbuffer->format.speed;
+               per_ch->sb.format.width = ogg_format->width;
+               per_ch->sb.format.channels = ogg_format->channels;
+               per_ch->sb.nbframes = 0;
+               per_ch->sb.maxframes = buff_len / (per_ch->sb.format.channels * per_ch->sb.format.width);
+
                ch->fetcher_data = per_ch;
        }
+       
+       real_start = *start;
 
        sb = &per_ch->sb;
        factor = per_sfx->format.width * per_sfx->format.channels;
 
        // If the stream buffer can't contain that much samples anyway
-       if (nbsamples * factor > buff_len)
+       if (nbsampleframes > sb->maxframes)
        {
-               Con_Printf ("OGG_FetchSound: stream buffer too small (%u bytes required)\n", nbsamples * factor);
+               Con_Printf ("OGG_FetchSound: stream buffer too small (%u sample frames required)\n", nbsampleframes);
                return NULL;
        }
 
        // If the data we need has already been decompressed in the sfxbuffer, just return it
-       if (sb->offset <= start && sb->offset + sb->length >= start + nbsamples)
+       if (per_ch->sb_offset <= real_start && per_ch->sb_offset + sb->nbframes >= real_start + nbsampleframes)
+       {
+               *start = per_ch->sb_offset;
                return sb;
+       }
 
-       newlength = (int)(sb->offset + sb->length) - start;
+       newlength = (int)(per_ch->sb_offset + sb->nbframes) - real_start;
 
        // If we need to skip some data before decompressing the rest, or if the stream has looped
-       if (newlength < 0 || sb->offset > start)
+       if (newlength < 0 || per_ch->sb_offset > real_start)
        {
-               if (qov_pcm_seek (&per_ch->vf, (ogg_int64_t)start) != 0)
+               unsigned int time_start;
+               ogg_int64_t ogg_start;
+               int err;
+               
+               if (real_start > sfx->total_length)
+               {
+                       Con_Printf ("OGG_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n",
+                                               real_start, sfx->total_length);
+                       return NULL;
+               }
+
+               // We work with 200ms (1/5 sec) steps to avoid rounding errors
+               time_start = real_start * 5 / snd_renderbuffer->format.speed;
+               ogg_start = time_start * (per_sfx->format.speed / 5);
+               err = qov_pcm_seek (&per_ch->vf, ogg_start);
+               if (err != 0)
+               {
+                       Con_Printf ("OGG_FetchSound: qov_pcm_seek(..., %d) returned %d\n",
+                                               real_start, err);
                        return NULL;
-               sb->length = 0;
+               }
+               sb->nbframes = 0;
+
+               real_start = (float)ogg_start / per_sfx->format.speed * snd_renderbuffer->format.speed;
+               if (*start - real_start + nbsampleframes > sb->maxframes)
+               {
+                       Con_Printf ("OGG_FetchSound: stream buffer too small after seek (%u sample frames required)\n",
+                                               *start - real_start + nbsampleframes);
+                       per_ch->sb_offset = real_start;
+                       return NULL;
+               }
        }
-       // Else, move forward the samples we need to keep in the sfxbuffer
+       // Else, move forward the samples we need to keep in the sound buffer
        else
        {
-               memmove (sb->data, sb->data + (start - sb->offset) * factor, newlength * factor);
-               sb->length = newlength;
+               memmove (sb->samples, sb->samples + (real_start - per_ch->sb_offset) * factor, newlength * factor);
+               sb->nbframes = newlength;
        }
 
-       sb->offset = start;
+       per_ch->sb_offset = real_start;
 
        // We add exactly 1 sec of sound to the buffer:
        // 1- to ensure we won't lose any sample during the resampling process
        // 2- to force one call to OGG_FetchSound per second to regulate the workload
-       if ((sfx->format.speed + sb->length) * factor > buff_len)
+       if (sb->format.speed + sb->nbframes > sb->maxframes)
        {
-               Con_Printf ("OGG_FetchSound: stream buffer overflow (%u bytes / %u)\n",
-                                       (sfx->format.speed + sb->length) * factor, buff_len);
+               Con_Printf ("OGG_FetchSound: stream buffer overflow (%u sample frames / %u)\n",
+                                       sb->format.speed + sb->nbframes, sb->maxframes);
                return NULL;
        }
        newlength = per_sfx->format.speed * factor;  // -> 1 sec of sound before resampling
@@ -503,10 +539,9 @@ static const sfxbuffer_t* OGG_FetchSound (channel_t* ch, unsigned int start, uns
        while ((ret = qov_read (&per_ch->vf, (char *)&resampling_buffer[done], (int)(newlength - done), bigendian, 2, 1, &per_ch->bs)) > 0)
                done += ret;
 
-       // Resample in the sfxbuffer
-       newlength = (int)ResampleSfx (resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format, sb->data + sb->length * (size_t)factor, sfx->name);
-       sb->length += newlength;
+       Snd_AppendToSndBuffer (sb, resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format);
 
+       *start = per_ch->sb_offset;
        return sb;
 }
 
@@ -524,17 +559,15 @@ static void OGG_FetchEnd (channel_t* ch)
        if (per_ch != NULL)
        {
                size_t buff_len;
-               snd_format_t* format;
 
                // Free the ogg vorbis decoder
                qov_clear (&per_ch->vf);
 
+               buff_len = per_ch->sb.maxframes * per_ch->sb.format.channels * per_ch->sb.format.width;
+               ch->sfx->memsize -= sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len;
+
                Mem_Free (per_ch);
                ch->fetcher_data = NULL;
-
-               format = &ch->sfx->format;
-               buff_len = STREAM_BUFFER_SIZE(format);
-               ch->sfx->memsize -= sizeof (*per_ch) - sizeof (per_ch->sb.data) + buff_len;
        }
 }
 
@@ -560,7 +593,19 @@ static void OGG_FreeSfx (sfx_t* sfx)
        sfx->fetcher = NULL;
 }
 
-static const snd_fetcher_t ogg_fetcher = { OGG_FetchSound, OGG_FetchEnd, OGG_FreeSfx };
+
+/*
+====================
+OGG_GetFormat
+====================
+*/
+static const snd_format_t* OGG_GetFormat (sfx_t* sfx)
+{
+       ogg_stream_persfx_t* per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data;
+       return &per_sfx->format;
+}
+
+static const snd_fetcher_t ogg_fetcher = { OGG_FetchSound, OGG_FetchEnd, OGG_FreeSfx, OGG_GetFormat };
 
 
 /*
@@ -570,7 +615,7 @@ OGG_LoadVorbisFile
 Load an Ogg Vorbis file into memory
 ====================
 */
-qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s)
+qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx)
 {
        unsigned char *data;
        fs_offset_t filesize;
@@ -583,7 +628,7 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s)
                return false;
 
        // Already loaded?
-       if (s->fetcher != NULL)
+       if (sfx->fetcher != NULL)
                return true;
 
        // Load the file
@@ -609,7 +654,7 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s)
        if (vi->channels < 1 || vi->channels > 2)
        {
                Con_Printf("%s has an unsupported number of channels (%i)\n",
-                                       s->name, vi->channels);
+                                       sfx->name, vi->channels);
                qov_clear (&vf);
                Mem_Free(data);
                return false;
@@ -618,30 +663,27 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s)
        len = qov_pcm_total (&vf, -1) * vi->channels * 2;  // 16 bits => "* 2"
 
        // Decide if we go for a stream or a simple PCM cache
-       buff_len = (int)ceil (STREAM_BUFFER_DURATION * (shm->format.speed * 2 * vi->channels));
+       buff_len = (int)ceil (STREAM_BUFFER_DURATION * (snd_renderbuffer->format.speed * 2 * vi->channels));
        if (snd_streaming.integer && len > (ogg_int64_t)filesize + 3 * buff_len)
        {
                ogg_stream_persfx_t* per_sfx;
 
                Con_DPrintf ("\"%s\" will be streamed\n", filename);
                per_sfx = (ogg_stream_persfx_t *)Mem_Alloc (snd_mempool, sizeof (*per_sfx));
-               s->memsize += sizeof (*per_sfx);
+               sfx->memsize += sizeof (*per_sfx);
                per_sfx->file = data;
                per_sfx->filesize = filesize;
-               s->memsize += filesize;
+               sfx->memsize += filesize;
 
                per_sfx->format.speed = vi->rate;
                per_sfx->format.width = 2;  // We always work with 16 bits samples
                per_sfx->format.channels = vi->channels;
-               s->format.speed = shm->format.speed;
-               s->format.width = per_sfx->format.width;
-               s->format.channels = per_sfx->format.channels;
-
-               s->fetcher_data = per_sfx;
-               s->fetcher = &ogg_fetcher;
-               s->loopstart = -1;
-               s->flags |= SFXFLAG_STREAMED;
-               s->total_length = (int)((size_t)len / per_sfx->format.channels / 2 * ((float)s->format.speed / per_sfx->format.speed));
+
+               sfx->fetcher_data = per_sfx;
+               sfx->fetcher = &ogg_fetcher;
+               sfx->loopstart = -1;
+               sfx->flags |= SFXFLAG_STREAMED;
+               sfx->total_length = (int)((size_t)len / (per_sfx->format.channels * 2) * ((double)snd_renderbuffer->format.speed / per_sfx->format.speed));
        }
        else
        {
@@ -649,8 +691,8 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s)
                ogg_int64_t done;
                int bs, bigendian;
                long ret;
-               sfxbuffer_t *sb;
-               size_t memsize;
+               snd_buffer_t *sb;
+               snd_format_t ogg_format;
 
                Con_DPrintf ("\"%s\" will be cached\n", filename);
 
@@ -658,34 +700,35 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s)
                buff = (char *)Mem_Alloc (snd_mempool, (int)len);
                done = 0;
                bs = 0;
-#if BYTE_ORDER == LITTLE_ENDIAN
-               bigendian = 0;
-#else
+#if BYTE_ORDER == BIG_ENDIAN
                bigendian = 1;
+#else
+               bigendian = 0;
 #endif
                while ((ret = qov_read (&vf, &buff[done], (int)(len - done), bigendian, 2, 1, &bs)) > 0)
                        done += ret;
 
-               // Calculate resampled length
-               // FIXME: is this using the correct rounding direction?  ceil may be better
-               len = (int)((double)done * (double)shm->format.speed / (double)vi->rate);
-
-               // Resample it
-               memsize = (size_t)len + sizeof (*sb) - sizeof (sb->data);
-               sb = (sfxbuffer_t *)Mem_Alloc (snd_mempool, memsize);
-               s->memsize += memsize;
-               s->fetcher_data = sb;
-               s->fetcher = &wav_fetcher;
-               s->format.speed = vi->rate;
-               s->format.width = 2;  // We always work with 16 bits samples
-               s->format.channels = vi->channels;
-               s->loopstart = -1;
-               s->flags &= ~SFXFLAG_STREAMED;
-
-               sb->length = (unsigned int)ResampleSfx ((unsigned char *)buff, (size_t)done / (vi->channels * 2), &s->format, sb->data, s->name);
-               s->format.speed = shm->format.speed;
-               s->total_length = sb->length;
-               sb->offset = 0;
+               // Build the sound buffer
+               ogg_format.speed = vi->rate;
+               ogg_format.channels = vi->channels;
+               ogg_format.width = 2;  // We always work with 16 bits samples
+               sb = Snd_CreateSndBuffer ((unsigned char *)buff, (size_t)done / (vi->channels * 2), &ogg_format, snd_renderbuffer->format.speed);
+               if (sb == NULL)
+               {
+                       qov_clear (&vf);
+                       Mem_Free (data);
+                       Mem_Free (buff);
+                       return false;
+               }
+
+               sfx->fetcher = &wav_fetcher;
+               sfx->fetcher_data = sb;
+
+               sfx->total_length = sb->nbframes;
+               sfx->memsize += sb->maxframes * sb->format.channels * sb->format.width + sizeof (*sb) - sizeof (sb->samples);
+
+               sfx->loopstart = -1;
+               sfx->flags &= ~SFXFLAG_STREAMED;
 
                qov_clear (&vf);
                Mem_Free (data);
index 4d200b7..f8c5fe7 100644 (file)
--- a/snd_ogg.h
+++ b/snd_ogg.h
@@ -27,7 +27,7 @@
 
 qboolean OGG_OpenLibrary (void);
 void OGG_CloseLibrary (void);
-qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s);
+qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx);
 
 
 #endif
index f50c321..8980069 100644 (file)
--- a/snd_oss.c
+++ b/snd_oss.c
@@ -20,262 +20,291 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 // OSS module, used by Linux and FreeBSD
 
-#include <unistd.h>
+
 #include <fcntl.h>
-#include <stdlib.h>
-#include <sys/types.h>
 #include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/shm.h>
-#include <sys/wait.h>
 #include <sys/soundcard.h>
-#include <stdio.h>
+#include <unistd.h>
+
 #include "quakedef.h"
 #include "snd_main.h"
 
-int audio_fd;
 
-static int tryrates[] = {44100, 48000, 22050, 24000, 11025, 16000, 8000};
+#define NB_FRAGMENTS 4
+
+static int audio_fd = -1;
+static int old_osstime = 0;
+static unsigned int osssoundtime;
+
+
+/*
+====================
+SndSys_Init
 
-qboolean SNDDMA_Init(void)
+Create "snd_renderbuffer" with the proper sound format if the call is successful
+May return a suggested format if the requested format isn't available
+====================
+*/
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
 {
-       int rc;
-       int fmt;
-       int tmp;
-       int i;
-       char *s;
-       struct audio_buf_info info;
-       int caps;
-       int format16bit;
-
-#if BYTE_ORDER == BIG_ENDIAN
-       format16bit = AFMT_S16_BE;
-#else
-       format16bit = AFMT_S16_LE;
-#endif
-
-       // open /dev/dsp, confirm capability to mmap, and get size of dma buffer
-    audio_fd = open("/dev/dsp", O_RDWR);  // we have to open it O_RDWR for mmap
-       if (audio_fd < 0)
-       {
-               perror("/dev/dsp");
-               Con_Print("Could not open /dev/dsp\n");
-               return 0;
-       }
+       int flags, ioctl_param, prev_value;
+       unsigned int fragmentsize;
 
-       if (ioctl(audio_fd, SNDCTL_DSP_RESET, 0) < 0)
+       Con_DPrint("SndSys_Init: using the OSS module\n");
+
+       // Check the requested sound format
+       if (requested->width < 1 || requested->width > 2)
        {
-               perror("/dev/dsp");
-               Con_Print("Could not reset /dev/dsp\n");
-               close(audio_fd);
-               return 0;
-       }
+               Con_Printf("SndSys_Init: invalid sound width (%hu)\n",
+                                       requested->width);
+
+               if (suggested != NULL)
+               {
+                       memcpy(suggested, requested, sizeof(suggested));
 
-       if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
+                       if (requested->width < 1)
+                               suggested->width = 1;
+                       else
+                               suggested->width = 2;
+               }
+               
+               return false;
+    }
+
+       // Open /dev/dsp
+    audio_fd = open("/dev/dsp", O_WRONLY);
+       if (audio_fd < 0)
        {
                perror("/dev/dsp");
-               Con_Print("Sound driver too old\n");
-               close(audio_fd);
-               return 0;
+               Con_Print("SndSys_Init: could not open /dev/dsp\n");
+               return false;
        }
-
-       if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP))
+       
+       // Use non-blocking IOs if possible
+       flags = fcntl(audio_fd, F_GETFL);
+       if (flags != -1)
        {
-               Con_Print("Sorry but your soundcard can't do this\n");
-               close(audio_fd);
-               return 0;
+               if (fcntl(audio_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+                       Con_Print("SndSys_Init : fcntl(F_SETFL, O_NONBLOCK) failed!\n");
        }
-
-       if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
+       else
+               Con_Print("SndSys_Init: fcntl(F_GETFL) failed!\n");
+
+       // Set the fragment size (up to "NB_FRAGMENTS" fragments of "fragmentsize" bytes)
+       fragmentsize = requested->speed * requested->channels * requested->width / 5;
+       fragmentsize = (unsigned int)ceilf((float)fragmentsize / (float)NB_FRAGMENTS);
+       fragmentsize = CeilPowerOf2(fragmentsize);
+       ioctl_param = (NB_FRAGMENTS << 16) | log2i(fragmentsize);
+       if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &ioctl_param) == -1)
        {
-               perror("GETOSPACE");
-               Con_Print("Um, can't do GETOSPACE?\n");
-               close(audio_fd);
-               return 0;
+               Con_Print ("SndSys_Init: could not set the fragment size\n");
+               SndSys_Shutdown ();
+               return false;
        }
 
-       // set sample bits & speed
-       s = getenv("QUAKE_SOUND_SAMPLEBITS");
-       if (s)
-               shm->format.width = atoi(s) / 8;
-// COMMANDLINEOPTION: Linux OSS Sound: -sndbits <bits> chooses 8 bit or 16 bit sound output
-       else if ((i = COM_CheckParm("-sndbits")) != 0)
-               shm->format.width = atoi(com_argv[i+1]) / 8;
-
-       if (shm->format.width != 2 && shm->format.width != 1)
-       {
-               ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
-               if (fmt & format16bit)
-                       shm->format.width = 2;
-               else if (fmt & AFMT_U8)
-                       shm->format.width = 1;
-    }
-
-       s = getenv("QUAKE_SOUND_SPEED");
-       if (s)
-               shm->format.speed = atoi(s);
-// COMMANDLINEOPTION: Linux OSS Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
-       else if ((i = COM_CheckParm("-sndspeed")) != 0)
-               shm->format.speed = atoi(com_argv[i+1]);
+       // Set the sound width
+       if (requested->width == 1)
+               ioctl_param = AFMT_U8;
        else
+               ioctl_param = AFMT_S16_NE;
+       prev_value = ioctl_param;
+       if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &ioctl_param) == -1 ||
+               ioctl_param != prev_value)
        {
-               for (i = 0;i < (int) sizeof(tryrates) / (int) sizeof(tryrates[0]);i++)
-                       if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i]))
-                               break;
-
-               shm->format.speed = tryrates[i];
-    }
+               if (ioctl_param != prev_value && suggested != NULL)
+               {
+                       memcpy(suggested, requested, sizeof(suggested));
 
-       s = getenv("QUAKE_SOUND_CHANNELS");
-       if (s)
-               shm->format.channels = atoi(s);
-// COMMANDLINEOPTION: Linux OSS Sound: -sndmono sets sound output to mono
-       else if ((i = COM_CheckParm("-sndmono")) != 0)
-               shm->format.channels = 1;
-// COMMANDLINEOPTION: Linux OSS Sound: -sndstereo sets sound output to stereo
-       else // if ((i = COM_CheckParm("-sndstereo")) != 0)
-               shm->format.channels = 2;
-
-       tmp = (shm->format.channels == 2);
-       rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
-       if (rc < 0)
-       {
-               perror("/dev/dsp");
-               Con_Printf("Could not set /dev/dsp to stereo=%d\n", tmp);
-               close(audio_fd);
-               return 0;
-       }
+                       if (ioctl_param == AFMT_S16_NE)
+                               suggested->width = 2;
+                       else
+                               suggested->width = 1;
+               }
 
-       rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->format.speed);
-       if (rc < 0)
-       {
-               perror("/dev/dsp");
-               Con_Printf("Could not set /dev/dsp speed to %d\n", shm->format.speed);
-               close(audio_fd);
-               return 0;
+               Con_Printf("SndSys_Init: could not set the sound width to %hu\n",
+                                       requested->width);
+               SndSys_Shutdown();
+               return false;
        }
 
-       if (shm->format.width == 2)
+       // Set the sound channels
+       ioctl_param = requested->channels;
+       if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ioctl_param) == -1 ||
+               ioctl_param != requested->channels)
        {
-               rc = format16bit;
-               rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
-               if (rc < 0)
+               if (ioctl_param != requested->channels && suggested != NULL)
                {
-                       perror("/dev/dsp");
-                       Con_Print("Could not support 16-bit data.  Try 8-bit.\n");
-                       close(audio_fd);
-                       return 0;
+                       memcpy(suggested, requested, sizeof(suggested));
+                       suggested->channels = ioctl_param;
                }
+
+               Con_Printf("SndSys_Init: could not set the number of channels to %hu\n",
+                                       requested->channels);
+               SndSys_Shutdown();
+               return false;
        }
-       else if (shm->format.width == 1)
+
+       // Set the sound speed
+       ioctl_param = requested->speed;
+       if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &ioctl_param) == -1 ||
+               (unsigned int)ioctl_param != requested->speed)
        {
-               rc = AFMT_U8;
-               rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
-               if (rc < 0)
+               if ((unsigned int)ioctl_param != requested->speed && suggested != NULL)
                {
-                       perror("/dev/dsp");
-                       Con_Print("Could not support 8-bit data.\n");
-                       close(audio_fd);
-                       return 0;
+                       memcpy(suggested, requested, sizeof(suggested));
+                       suggested->speed = ioctl_param;
                }
+
+               Con_Printf("SndSys_Init: could not set the sound speed to %u\n",
+                                       requested->speed);
+               SndSys_Shutdown();
+               return false;
        }
-       else
+       
+       old_osstime = 0;
+       osssoundtime = 0;
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
+       return true;
+}
+
+
+/*
+====================
+SndSys_Shutdown
+
+Stop the sound card, delete "snd_renderbuffer" and free its other resources
+====================
+*/
+void SndSys_Shutdown (void)
+{
+       // Stop the sound and close the device
+       if (audio_fd >= 0)
        {
-               perror("/dev/dsp");
-               Con_Printf("%d-bit sound not supported.\n", shm->format.width * 8);
+               ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
                close(audio_fd);
-               return 0;
+               audio_fd = -1;
        }
 
-       shm->sampleframes = info.fragstotal * info.fragsize / shm->format.width / shm->format.channels;
-       shm->samples = shm->sampleframes * shm->format.channels;
-
-       // memory map the dma buffer
-       shm->bufferlength = info.fragstotal * info.fragsize;
-       shm->buffer = (unsigned char *) mmap(NULL, shm->bufferlength, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
-       if (!shm->buffer || shm->buffer == (unsigned char *)-1)
+       if (snd_renderbuffer != NULL)
        {
-               perror("/dev/dsp");
-               Con_Print("Could not mmap /dev/dsp\n");
-               close(audio_fd);
-               return 0;
+               Mem_Free(snd_renderbuffer->ring);
+               Mem_Free(snd_renderbuffer);
+               snd_renderbuffer = NULL;
        }
+}
+
+
+/*
+====================
+SndSys_Submit
 
-       // toggle the trigger & start her up
-       tmp = 0;
-       rc  = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
-       if (rc < 0)
+Submit the contents of "snd_renderbuffer" to the sound card
+====================
+*/
+void SndSys_Submit (void)
+{
+       unsigned int startoffset, factor, limit, nbframes;
+       int written;
+       
+       if (audio_fd < 0 ||
+               snd_renderbuffer->startframe == snd_renderbuffer->endframe)
+               return;
+
+       startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
+       factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
+       limit = snd_renderbuffer->maxframes - startoffset;
+       nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+       if (nbframes > limit)
        {
-               perror("/dev/dsp");
-               Con_Print("Could not toggle.\n");
-               close(audio_fd);
-               return 0;
+               written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], limit * factor);
+               if (written < 0)
+               {
+                       Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
+                       return;
+               }
+
+               if (written % factor != 0)
+                       Sys_Error("SndSys_Submit: nb of bytes written (%d) isn't aligned to a frame sample!\n", written);
+
+               snd_renderbuffer->startframe += written / factor;
+
+               if ((unsigned int)written < nbframes * factor)
+               {
+                       Con_Printf("SndSys_Submit: audio can't keep up! (%d < %u)\n", written, nbframes * factor);
+                       return;
+               }
+               
+               nbframes -= limit;
+               startoffset = 0;
        }
-       tmp = PCM_ENABLE_OUTPUT;
-       rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
-       if (rc < 0)
+
+       written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], nbframes * factor);
+       if (written < 0)
        {
-               perror("/dev/dsp");
-               Con_Print("Could not toggle.\n");
-               close(audio_fd);
-               return 0;
+               Con_Printf("SndSys_Submit: audio write returned %d!\n", written);
+               return;
        }
+       snd_renderbuffer->startframe += written / factor;
+}
 
-       shm->samplepos = 0;
 
-       return 1;
-}
+/*
+====================
+SndSys_GetSoundTime
 
-int SNDDMA_GetDMAPos(void)
+Returns the number of sample frames consumed since the sound started
+====================
+*/
+unsigned int SndSys_GetSoundTime (void)
 {
-
        struct count_info count;
+       int new_osstime;
+       unsigned int timediff;
 
-       if (!shm) return 0;
-
-       if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1)
+       // TODO: use SNDCTL_DSP_GETODELAY instead
+       if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1)
        {
-               perror("/dev/dsp");
-               Con_Print("Uh, sound dead.\n");
-               S_Shutdown();
+               Con_Print ("SndSys_GetSoundTimeDiff: can't ioctl (SNDCTL_DSP_GETOPTR)\n");
                return 0;
        }
-       shm->samplepos = count.ptr / shm->format.width;
+       new_osstime = count.bytes / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
 
-       return shm->samplepos;
-}
+       if (new_osstime >= old_osstime)
+               timediff = new_osstime - old_osstime;
+       else
+       {
+               Con_Print ("SndSys_GetSoundTime: osstime wrapped\n");
+               timediff = 0;
+       }
 
-void SNDDMA_Shutdown(void)
-{
-       int tmp;
-       // unmap the memory
-       munmap(shm->buffer, shm->bufferlength);
-       // stop the sound
-       tmp = 0;
-       ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
-       ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
-       // close the device
-       close(audio_fd);
-       audio_fd = -1;
+       old_osstime = new_osstime;
+       osssoundtime += timediff;
+       return osssoundtime;
 }
 
+
 /*
-==============
-SNDDMA_Submit
+====================
+SndSys_LockRenderBuffer
 
-Send sound to device if buffer isn't really the dma buffer
-===============
+Get the exclusive lock on "snd_renderbuffer"
+====================
 */
-void SNDDMA_Submit(void)
+qboolean SndSys_LockRenderBuffer (void)
 {
+       // Nothing to do
+       return true;
 }
 
-void *S_LockBuffer(void)
-{
-       return shm->buffer;
-}
 
-void S_UnlockBuffer(void)
+/*
+====================
+SndSys_UnlockRenderBuffer
+
+Release the exclusive lock on "snd_renderbuffer"
+====================
+*/
+void SndSys_UnlockRenderBuffer (void)
 {
+       // Nothing to do
 }
-
index cfeeb1b..6dccba7 100644 (file)
--- a/snd_sdl.c
+++ b/snd_sdl.c
@@ -16,72 +16,74 @@ 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.
 */
-#include "quakedef.h"
-#include "snd_main.h"
-#include <SDL.h>
 
-/*
-Info:
-SDL samples are really frames (full set of samples for all speakers)
-*/
-
-#define AUDIO_SDL_SAMPLEFRAMES         4096
-#define AUDIO_LOCALFACTOR              4
+#include <math.h>
+#include <SDL.h>
 
-typedef struct AudioState_s
-{
-       int             width;
-    int                size;
-       int             pos;
-       void    *buffer;
-} AudioState;
+#include "quakedef.h"
+#include "snd_main.h"
 
 
-static AudioState       as;
+static unsigned int sdlaudiotime = 0;
 
-static void Buffer_Callback(void *userdata, Uint8 *stream, int len);
 
-/*
-==================
-S_BlockSound
-==================
-*/
-void S_BlockSound( void )
+// Note: SDL calls SDL_LockAudio() right before this function, so no need to lock the audio data here
+static void Buffer_Callback (void *userdata, Uint8 *stream, int len)
 {
-       snd_blocked++;
+       unsigned int factor, RequestedFrames, MaxFrames, FrameCount;
+       unsigned int StartOffset, EndOffset;
+
+       factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
+       if ((unsigned int)len % factor != 0)
+               Sys_Error("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len);
+
+       RequestedFrames = (unsigned int)len / factor;
+       
+       // Transfert up to a chunk of samples from snd_renderbuffer to stream
+       MaxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
+       if (MaxFrames > RequestedFrames)
+               FrameCount = RequestedFrames;
+       else
+               FrameCount = MaxFrames;
+       StartOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
+       EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes;
+       if (StartOffset > EndOffset)  // if the buffer wraps
+       {
+               unsigned int PartialLength1, PartialLength2;
 
-       if( snd_blocked == 1 )
-               SDL_PauseAudio( true );
-}
+               PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor;
+               memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1);
+               
+               PartialLength2 = FrameCount * factor - PartialLength1;
+               memcpy(&stream[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2);
+       }
+       else
+               memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor);
 
+       snd_renderbuffer->startframe += FrameCount;
 
-/*
-==================
-S_UnblockSound
-==================
-*/
-void S_UnblockSound( void )
-{
-       snd_blocked--;
-       if( snd_blocked == 0 )
-               SDL_PauseAudio( false );
+       if (FrameCount < RequestedFrames)
+               Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount);
+
+       sdlaudiotime += RequestedFrames;
 }
 
 
 /*
-==================
-SNDDMA_Init
+====================
+SndSys_Init
 
-Try to find a sound device to mix for.
-Returns false if nothing is found.
-==================
+Create "snd_renderbuffer" with the proper sound format if the call is successful
+May return a suggested format if the requested format isn't available
+====================
 */
-
-qboolean SNDDMA_Init(void)
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
 {
+       unsigned int buffersize;
        SDL_AudioSpec wantspec;
-       int i;
-       int channels;
+       SDL_AudioSpec obtainspec;
+
+       Con_Print ("SndSys_Init: using the SDL module\n");
 
        // Init the SDL Audio subsystem
        if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) {
@@ -89,138 +91,132 @@ qboolean SNDDMA_Init(void)
                return false;
        }
 
-       for (channels = 8;channels >= 1;channels--)
+       buffersize = (unsigned int)ceil((double)requested->speed / 20.0);
+
+       // Init the SDL Audio subsystem
+       wantspec.callback = Buffer_Callback;
+       wantspec.userdata = NULL;
+       wantspec.freq = requested->speed;
+       wantspec.format = ((requested->width == 1) ? AUDIO_U8 : AUDIO_S16SYS);
+       wantspec.channels = requested->channels;
+       wantspec.samples = CeilPowerOf2(buffersize);  // needs to be a power of 2 on some platforms.
+
+       Con_DPrintf("Wanted audio Specification:\n"
+                               "\tChannels  : %i\n"
+                               "\tFormat    : 0x%X\n"
+                               "\tFrequency : %i\n"
+                               "\tSamples   : %i\n",
+                               wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples);
+
+       if( SDL_OpenAudio( &wantspec, &obtainspec ) )
+       {       
+               Con_Printf( "Failed to open the audio device! (%s)\n", SDL_GetError() );
+               return false;
+       }
+
+       Con_DPrintf("Obtained audio specification:\n"
+                               "\tChannels  : %i\n"
+                               "\tFormat    : 0x%X\n"
+                               "\tFrequency : %i\n"
+                               "\tSamples   : %i\n",
+                               obtainspec.channels, obtainspec.format, obtainspec.freq, obtainspec.samples);
+
+       // If we haven't obtained what we wanted
+       if (wantspec.freq != obtainspec.freq ||
+               wantspec.format != obtainspec.format ||
+               wantspec.channels != obtainspec.channels)
        {
-               if ((channels & 1) && channels != 1)
-                       continue;
-// COMMANDLINEOPTION: SDL Sound: -sndmono sets sound output to mono
-               if ((i=COM_CheckParm("-sndmono")) != 0)
-                       if (channels != 1)
-                               continue;
-// COMMANDLINEOPTION: SDL Sound: -sndstereo sets sound output to stereo
-               if ((i=COM_CheckParm("-sndstereo")) != 0)
-                       if (channels != 2)
-                               continue;
-// COMMANDLINEOPTION: SDL Sound: -sndquad sets sound output to 4 channel surround
-               if ((i=COM_CheckParm("-sndquad")) != 0)
-                       if (channels != 4)
-                               continue;
-               // Init the SDL Audio subsystem
-               wantspec.callback = Buffer_Callback;
-               wantspec.userdata = NULL;
-               wantspec.freq = 44100;
-               // COMMANDLINEOPTION: SDL Sound: -sndspeed <hz> chooses sound output rate (try values such as 44100, 48000, 22050, 11025 (quake), 24000, 32000, 96000, 192000, etc)
-               i = COM_CheckParm( "-sndspeed" );
-               if( i && i != ( com_argc - 1 ) )
-                       wantspec.freq = atoi( com_argv[ i+1 ] );
-               wantspec.format = AUDIO_S16SYS;
-               wantspec.channels = channels;
-               wantspec.samples = AUDIO_SDL_SAMPLEFRAMES;
-
-               if( SDL_OpenAudio( &wantspec, NULL ) )
+               SDL_CloseAudio();
+
+               // Pass the obtained format as a suggested format
+               if (suggested != NULL)
                {
-                       Con_Printf("%s\n", SDL_GetError());
-                       continue;
+                       suggested->speed = obtainspec.freq;
+                       // FIXME: check the format more carefully. There are plenty of unsupported cases
+                       suggested->width = ((obtainspec.format == AUDIO_U8) ? 1 : 2);
+                       suggested->channels = obtainspec.channels;
                }
 
-               // Init the shm structure
-               memset( (void*) shm, 0, sizeof(*shm) );
-               shm->format.channels = wantspec.channels;
-               shm->format.width = 2;
-               shm->format.speed = wantspec.freq;
-
-               shm->samplepos = 0;
-               shm->sampleframes = wantspec.samples * AUDIO_LOCALFACTOR;
-               shm->samples = shm->sampleframes * shm->format.channels;
-               shm->bufferlength = shm->samples * shm->format.width;
-               shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength );
-
-               // Init the as structure
-               as.buffer = shm->buffer;
-               as.width = shm->format.width;
-               as.pos = 0;
-               as.size = shm->bufferlength;
-               break;
-       }
-       if (channels < 1)
-       {
-               Con_Print( "Failed to open the audio device!\n" );
-               Con_DPrintf(
-                       "Audio Specification:\n"
-                       "\tChannels  : %i\n"
-                       "\tFormat    : %x\n"
-                       "\tFrequency : %i\n"
-                       "\tSamples   : %i\n",
-                       wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples );
                return false;
        }
 
-       SDL_PauseAudio( false );
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
 
+       sdlaudiotime = 0;
+       SDL_PauseAudio( false );
+       
        return true;
 }
 
+
 /*
-==============
-SNDDMA_GetDMAPos
+====================
+SndSys_Shutdown
 
-return the current sample position (in mono samples read)
-inside the recirculating dma buffer, so the mixing code will know
-how many sample are required to fill it up.
-===============
+Stop the sound card, delete "snd_renderbuffer" and free its other resources
+====================
 */
-int SNDDMA_GetDMAPos(void)
+void SndSys_Shutdown(void)
 {
-       shm->samplepos = (as.pos / as.width) % shm->samples;
-       return shm->samplepos;
+       SDL_CloseAudio();
+
+       if (snd_renderbuffer != NULL)
+       {
+               Mem_Free(snd_renderbuffer->ring);
+               Mem_Free(snd_renderbuffer);
+               snd_renderbuffer = NULL;
+       }
 }
 
+
 /*
-==============
-SNDDMA_Submit
+====================
+SndSys_Submit
 
-Send sound to device if buffer isn't really the dma buffer
-===============
+Submit the contents of "snd_renderbuffer" to the sound card
+====================
 */
-void SNDDMA_Submit(void)
+void SndSys_Submit (void)
 {
-
+       // Nothing to do here (this sound module is callback-based)
 }
 
+
 /*
-==============
-SNDDMA_Shutdown
+====================
+SndSys_GetSoundTime
 
-Reset the sound device for exiting
-===============
+Returns the number of sample frames consumed since the sound started
+====================
 */
-void SNDDMA_Shutdown(void)
+unsigned int SndSys_GetSoundTime (void)
 {
-       SDL_CloseAudio();
-       Mem_Free( as.buffer );
+       return sdlaudiotime;
 }
 
-void *S_LockBuffer(void)
+
+/*
+====================
+SndSys_LockRenderBuffer
+
+Get the exclusive lock on "snd_renderbuffer"
+====================
+*/
+qboolean SndSys_LockRenderBuffer (void)
 {
        SDL_LockAudio();
-       return shm->buffer;
+       return true;
 }
 
-void S_UnlockBuffer(void)
-{
-       SDL_UnlockAudio();
-}
 
-static void Buffer_Callback(void *userdata, Uint8 *stream, int len)
+/*
+====================
+SndSys_UnlockRenderBuffer
+
+Release the exclusive lock on "snd_renderbuffer"
+====================
+*/
+void SndSys_UnlockRenderBuffer (void)
 {
-       if( len > as.size )
-               len = as.size;
-       if( len > as.size - as.pos ) {
-               memcpy( stream, (Uint8*) as.buffer + as.pos, as.size - as.pos );
-               len -= as.size - as.pos;
-               as.pos = 0;
-       }
-       memcpy( stream, (Uint8*) as.buffer + as.pos, len );
-       as.pos = (as.pos + len) % as.size;
+       SDL_UnlockAudio();
 }
-
index 89bd6c6..d96c819 100644 (file)
--- a/snd_wav.c
+++ b/snd_wav.c
@@ -223,9 +223,10 @@ static wavinfo_t GetWavinfo (char *name, unsigned char *wav, int wavlength)
 WAV_FetchSound
 ====================
 */
-static const sfxbuffer_t* WAV_FetchSound (channel_t* ch, unsigned int start, unsigned int nbsamples)
+static const snd_buffer_t* WAV_FetchSound (channel_t* ch, unsigned int *start, unsigned int nbsampleframes)
 {
-       return (sfxbuffer_t *)ch->sfx->fetcher_data;
+       *start = 0;
+       return (snd_buffer_t *)ch->sfx->fetcher_data;
 }
 
 /*
@@ -235,17 +236,28 @@ WAV_FreeSfx
 */
 static void WAV_FreeSfx (sfx_t* sfx)
 {
-       sfxbuffer_t* sb = (sfxbuffer_t *)sfx->fetcher_data;
+       snd_buffer_t* sb = (snd_buffer_t *)sfx->fetcher_data;
 
        // Free the sound buffer
-       sfx->memsize -= (sb->length * sfx->format.channels * sfx->format.width) + sizeof (*sb) - sizeof (sb->data);
+       sfx->memsize -= (sb->maxframes * sb->format.channels * sb->format.width) + sizeof (*sb) - sizeof (sb->samples);
        Mem_Free(sb);
 
        sfx->fetcher_data = NULL;
        sfx->fetcher = NULL;
 }
 
-const snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL, WAV_FreeSfx };
+/*
+====================
+WAV_GetFormat
+====================
+*/
+static const snd_format_t* WAV_GetFormat (sfx_t* sfx)
+{
+       snd_buffer_t* sb = (snd_buffer_t *)sfx->fetcher_data;
+       return &sb->format;
+}
+
+const snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL, WAV_FreeSfx, WAV_GetFormat };
 
 
 /*
@@ -253,17 +265,16 @@ const snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL, WAV_FreeSfx };
 S_LoadWavFile
 ==============
 */
-qboolean S_LoadWavFile (const char *filename, sfx_t *s)
+qboolean S_LoadWavFile (const char *filename, sfx_t *sfx)
 {
        fs_offset_t filesize;
        unsigned char *data;
        wavinfo_t info;
-       int len;
-       size_t memsize;
-       sfxbuffer_t* sb;
+       snd_format_t wav_format;
+       snd_buffer_t* sb;
 
        // Already loaded?
-       if (s->fetcher != NULL)
+       if (sfx->fetcher != NULL)
                return true;
 
        // Load the file
@@ -280,48 +291,22 @@ qboolean S_LoadWavFile (const char *filename, sfx_t *s)
 
        Con_DPrintf ("Loading WAV file \"%s\"\n", filename);
 
-       info = GetWavinfo (s->name, data, (int)filesize);
-       // Stereo sounds are allowed (intended for music)
-       if (info.channels < 1 || info.channels > 2)
+       info = GetWavinfo (sfx->name, data, (int)filesize);
+       if (info.channels < 1 || info.channels > 2)  // Stereo sounds are allowed (intended for music)
        {
-               Con_Printf("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
+               Con_Printf("%s has an unsupported number of channels (%i)\n",sfx->name, info.channels);
                Mem_Free(data);
                return false;
        }
        //if (info.channels == 2)
-       //      Log_Printf("stereosounds.log", "%s\n", s->name);
-
-       // calculate resampled length
-       len = (int) ((double) info.samples * (double) shm->format.speed / (double) info.rate);
-       len = len * info.width * info.channels;
-
-       memsize = len + sizeof (*sb) - sizeof (sb->data);
-       sb = (sfxbuffer_t *)Mem_Alloc (snd_mempool, memsize);
-       if (sb == NULL)
-       {
-               Con_Printf("failed to allocate memory for sound \"%s\"\n", s->name);
-               Mem_Free(data);
-               return false;
-       }
-       s->memsize += memsize;
-
-       s->fetcher = &wav_fetcher;
-       s->fetcher_data = sb;
-       s->format.speed = info.rate;
-       s->format.width = info.width;
-       s->format.channels = info.channels;
-       if (info.loopstart < 0)
-               s->loopstart = -1;
-       else
-               s->loopstart = (int)((double)info.loopstart * (double)shm->format.speed / (double)s->format.speed);
-       s->flags &= ~SFXFLAG_STREAMED;
+       //      Log_Printf("stereosounds.log", "%s\n", sfx->name);
 
 #if BYTE_ORDER != LITTLE_ENDIAN
        // We must convert the WAV data from little endian
        // to the machine endianess before resampling it
        if (info.width == 2)
        {
-               int i;
+               unsigned int len, i;
                short* ptr;
 
                len = info.samples * info.channels;
@@ -331,10 +316,26 @@ qboolean S_LoadWavFile (const char *filename, sfx_t *s)
        }
 #endif
 
-       sb->length = (int)ResampleSfx (data + info.dataofs, info.samples, &s->format, sb->data, s->name);
-       s->format.speed = shm->format.speed;
-       s->total_length = sb->length;
-       sb->offset = 0;
+       wav_format.speed = info.rate;
+       wav_format.width = info.width;
+       wav_format.channels = info.channels;
+       sb = Snd_CreateSndBuffer (data + info.dataofs, info.samples, &wav_format, snd_renderbuffer->format.speed);
+       if (sb == NULL)
+       {
+               Mem_Free(data);
+               return false;
+       }
+       sfx->fetcher = &wav_fetcher;
+       sfx->fetcher_data = sb;
+
+       sfx->total_length = sb->nbframes;
+       sfx->memsize += sb->maxframes * sb->format.channels * sb->format.width + sizeof (*sb) - sizeof (sb->samples);
+
+       if (info.loopstart < 0)
+               sfx->loopstart = -1;
+       else
+               sfx->loopstart = (double)info.loopstart * (double)snd_renderbuffer->format.speed / (double)sb->format.speed;
+       sfx->flags &= ~SFXFLAG_STREAMED;
 
        Mem_Free (data);
        return true;
index ae47162..89c7ee5 100644 (file)
--- a/snd_wav.h
+++ b/snd_wav.h
@@ -28,7 +28,7 @@
 
 extern const snd_fetcher_t wav_fetcher;
 
-qboolean S_LoadWavFile (const char *filename, sfx_t *s);
+qboolean S_LoadWavFile (const char *filename, sfx_t *sfx);
 
 
 #endif
index 490edd2..89b333d 100644 (file)
--- a/snd_win.c
+++ b/snd_win.c
@@ -19,14 +19,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 #include "quakedef.h"
 #include "snd_main.h"
+
+#ifndef DIRECTSOUND_VERSION
+#      define DIRECTSOUND_VERSION 0x0500  /* Version 5.0 */
+#endif
 #include <windows.h>
 #include <mmsystem.h>
 #include <dsound.h>
 
 extern HWND mainwindow;
 
-#define iDirectSoundCreate(a,b,c)      pDirectSoundCreate(a,b,c)
-
 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
 
 // Wave output: 64KB in 64 buffers of 1KB
@@ -40,7 +42,6 @@ HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS
 
 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
 
-static qboolean        wavonly;
 static qboolean        dsound_init;
 static qboolean        wav_init;
 static qboolean        primary_format_set;
@@ -50,6 +51,8 @@ static int    snd_sent, snd_completed;
 static int prev_painted;
 static unsigned int paintpot;
 
+static unsigned int dsound_time;
+
 
 /*
  * Global variables. Must be visible to window-procedure function
@@ -78,111 +81,15 @@ HINSTANCE hInstDS;
 qboolean SNDDMA_InitWav (void);
 sndinitstat SNDDMA_InitDirect (void);
 
-/*
-==================
-S_BlockSound
-==================
-*/
-void S_BlockSound (void)
-{
-       // DirectSound takes care of blocking itself
-       if (wav_init)
-       {
-               snd_blocked++;
-
-               if (snd_blocked == 1)
-                       waveOutReset (hWaveOut);
-       }
-}
-
-
-/*
-==================
-S_UnblockSound
-==================
-*/
-void S_UnblockSound (void)
-{
-       // DirectSound takes care of blocking itself
-       if (wav_init)
-               snd_blocked--;
-}
-
-
-/*
-==================
-FreeSound
-==================
-*/
-void FreeSound (void)
-{
-       int             i;
-
-       if (pDSBuf)
-       {
-               pDSBuf->lpVtbl->Stop(pDSBuf);
-               pDSBuf->lpVtbl->Release(pDSBuf);
-       }
-
-       // only release primary buffer if it's not also the mixing buffer we just released
-       if (pDSPBuf && (pDSBuf != pDSPBuf))
-       {
-               pDSPBuf->lpVtbl->Release(pDSPBuf);
-       }
-
-       if (pDS)
-       {
-               pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
-               pDS->lpVtbl->Release(pDS);
-       }
-
-       if (hWaveOut)
-       {
-               waveOutReset (hWaveOut);
-
-               if (lpWaveHdr)
-               {
-                       for (i=0 ; i< WAV_BUFFERS ; i++)
-                               waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
-               }
-
-               waveOutClose (hWaveOut);
-
-               if (hWaveHdr)
-               {
-                       GlobalUnlock(hWaveHdr);
-                       GlobalFree(hWaveHdr);
-               }
-
-               if (hData)
-               {
-                       GlobalUnlock(hData);
-                       GlobalFree(hData);
-               }
-
-       }
-
-       pDS = NULL;
-       pDSBuf = NULL;
-       pDSPBuf = NULL;
-       hWaveOut = 0;
-       hData = 0;
-       hWaveHdr = 0;
-       lpData = NULL;
-       lpWaveHdr = NULL;
-       dsound_init = false;
-       wav_init = false;
-}
-
 
 /*
 ==================
-SNDDMA_InitDirect
+SndSys_InitDirectSound
 
-Direct-Sound support
+DirectSound 5 support
 ==================
 */
-sndinitstat SNDDMA_InitDirect (void)
+static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested, snd_format_t* suggested)
 {
        DSBUFFERDESC    dsbuf;
        DSBCAPS                 dsbcaps;
@@ -191,26 +98,15 @@ sndinitstat SNDDMA_InitDirect (void)
        WAVEFORMATEX    format, pformat;
        HRESULT                 hresult;
        int                             reps;
-       int i;
-
-       memset((void *)shm, 0, sizeof(*shm));
-       shm->format.channels = 2;
-       shm->format.width = 2;
-// COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
-       i = COM_CheckParm ("-sndspeed");
-       if (i && i != (com_argc - 1))
-               shm->format.speed = atoi(com_argv[i+1]);
-       else
-               shm->format.speed = 44100;
 
        memset (&format, 0, sizeof(format));
        format.wFormatTag = WAVE_FORMAT_PCM;
-    format.nChannels = shm->format.channels;
-    format.wBitsPerSample = shm->format.width * 8;
-    format.nSamplesPerSec = shm->format.speed;
+    format.nChannels = requested->channels;
+    format.wBitsPerSample = requested->width * 8;
+    format.nSamplesPerSec = requested->speed;
     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
-    format.cbSize = 0;
     format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
+    format.cbSize = 0;
 
        if (!hInstDS)
        {
@@ -231,7 +127,7 @@ sndinitstat SNDDMA_InitDirect (void)
                }
        }
 
-       while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
+       while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
        {
                if (hresult != DSERR_ALLOCATED)
                {
@@ -252,7 +148,7 @@ sndinitstat SNDDMA_InitDirect (void)
 
        dscaps.dwSize = sizeof(dscaps);
 
-       if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
+       if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
        {
                Con_Print("Couldn't get DS caps\n");
        }
@@ -260,14 +156,14 @@ sndinitstat SNDDMA_InitDirect (void)
        if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
        {
                Con_Print("No DirectSound driver installed\n");
-               FreeSound ();
+               SndSys_Shutdown ();
                return SIS_FAILURE;
        }
 
-       if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
+       if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
        {
                Con_Print("Set coop level failed\n");
-               FreeSound ();
+               SndSys_Shutdown ();
                return SIS_FAILURE;
        }
 
@@ -286,11 +182,11 @@ sndinitstat SNDDMA_InitDirect (void)
 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
        if (!COM_CheckParm ("-snoforceformat"))
        {
-               if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
+               if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
                {
                        pformat = format;
 
-                       if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
+                       if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, &pformat))
                        {
                                Con_Print("Set primary sound buffer format: no\n");
                        }
@@ -306,6 +202,8 @@ sndinitstat SNDDMA_InitDirect (void)
 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
        if (!primary_format_set || !COM_CheckParm ("-primarysound"))
        {
+               HRESULT result;
+
                // create the secondary buffer we'll actually work with
                memset (&dsbuf, 0, sizeof(dsbuf));
                dsbuf.dwSize = sizeof(DSBUFFERDESC);
@@ -316,21 +214,22 @@ sndinitstat SNDDMA_InitDirect (void)
                memset(&dsbcaps, 0, sizeof(dsbcaps));
                dsbcaps.dwSize = sizeof(dsbcaps);
 
-               if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
+               result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
+               if (result != DS_OK ||
+                       requested->channels != format.nChannels ||
+                       requested->width != format.wBitsPerSample / 8 ||
+                       requested->speed != format.nSamplesPerSec)
                {
-                       Con_Print("DS:CreateSoundBuffer Failed\n");
-                       FreeSound ();
+                       Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
+                                          result, format.nChannels, format.wBitsPerSample / 8, format.nSamplesPerSec);
+                       SndSys_Shutdown ();
                        return SIS_FAILURE;
                }
 
-               shm->format.channels = format.nChannels;
-               shm->format.width = format.wBitsPerSample / 8;
-               shm->format.speed = format.nSamplesPerSec;
-
-               if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
+               if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
                {
                        Con_Print("DS:GetCaps failed\n");
-                       FreeSound ();
+                       SndSys_Shutdown ();
                        return SIS_FAILURE;
                }
 
@@ -338,14 +237,14 @@ sndinitstat SNDDMA_InitDirect (void)
        }
        else
        {
-               if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
+               if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
                {
                        Con_Print("Set coop level failed\n");
-                       FreeSound ();
+                       SndSys_Shutdown ();
                        return SIS_FAILURE;
                }
 
-               if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
+               if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
                {
                        Con_Print("DS:GetCaps failed\n");
                        return SIS_FAILURE;
@@ -356,51 +255,45 @@ sndinitstat SNDDMA_InitDirect (void)
        }
 
        // Make sure mixer is active
-       pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
+       IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
 
-       Con_Printf("   %d channel(s)\n"
-                      "   %d bits/sample\n"
-                                  "   %d samples/sec\n",
-                                  shm->format.channels, shm->format.width * 8, shm->format.speed);
+       Con_DPrintf("   %d channel(s)\n"
+                               "   %d bits/sample\n"
+                               "   %d samples/sec\n",
+                               requested->channels, requested->width * 8, requested->speed);
 
        gSndBufSize = dsbcaps.dwBufferBytes;
 
        // initialize the buffer
        reps = 0;
 
-       while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
+       while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
        {
                if (hresult != DSERR_BUFFERLOST)
                {
                        Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
-                       FreeSound ();
+                       SndSys_Shutdown ();
                        return SIS_FAILURE;
                }
 
                if (++reps > 10000)
                {
                        Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
-                       FreeSound ();
+                       SndSys_Shutdown ();
                        return SIS_FAILURE;
                }
 
        }
 
        memset(lpData, 0, dwSize);
+       IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
 
-       pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
-
-       // we don't want anyone to access the buffer directly w/o locking it first.
-       lpData = NULL;
-
-       pDSBuf->lpVtbl->Stop(pDSBuf);
-       pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwStartTime, NULL);
-       pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
+       IDirectSoundBuffer_Stop(pDSBuf);
+       IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
 
-       shm->samples = gSndBufSize / shm->format.width;
-       shm->sampleframes = shm->samples / shm->format.channels;
-       shm->samplepos = 0;
-       shm->buffer = (unsigned char *) lpData;
+       dwStartTime = 0;
+       dsound_time = 0;
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
 
        dsound_init = true;
 
@@ -410,38 +303,25 @@ sndinitstat SNDDMA_InitDirect (void)
 
 /*
 ==================
-SNDDM_InitWav
+SndSys_InitMmsystem
 
 Crappy windows multimedia base
 ==================
 */
-qboolean SNDDMA_InitWav (void)
+static qboolean SndSys_InitMmsystem (const snd_format_t* requested, snd_format_t* suggested)
 {
        WAVEFORMATEX  format;
        int                             i;
        HRESULT                 hr;
 
-       snd_sent = 0;
-       snd_completed = 0;
-
-       memset((void *)shm, 0, sizeof(*shm));
-       shm->format.channels = 2;
-       shm->format.width = 2;
-// COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
-       i = COM_CheckParm ("-sndspeed");
-       if (i && i != (com_argc - 1))
-               shm->format.speed = atoi(com_argv[i+1]);
-       else
-               shm->format.speed = 44100;
-
        memset (&format, 0, sizeof(format));
        format.wFormatTag = WAVE_FORMAT_PCM;
-       format.nChannels = shm->format.channels;
-       format.wBitsPerSample = shm->format.width * 8;
-       format.nSamplesPerSec = shm->format.speed;
-       format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
-       format.cbSize = 0;
-       format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
+    format.nChannels = requested->channels;
+    format.wBitsPerSample = requested->width * 8;
+    format.nSamplesPerSec = requested->speed;
+    format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
+    format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
+    format.cbSize = 0;
 
        // Open a waveform device for output using window callback
        while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
@@ -475,14 +355,14 @@ qboolean SNDDMA_InitWav (void)
        if (!hData)
        {
                Con_Print("Sound: Out of memory.\n");
-               FreeSound ();
+               SndSys_Shutdown ();
                return false;
        }
        lpData = GlobalLock(hData);
        if (!lpData)
        {
                Con_Print("Sound: Failed to lock.\n");
-               FreeSound ();
+               SndSys_Shutdown ();
                return false;
        }
        memset (lpData, 0, gSndBufSize);
@@ -498,7 +378,7 @@ qboolean SNDDMA_InitWav (void)
        if (hWaveHdr == NULL)
        {
                Con_Print("Sound: Failed to Alloc header.\n");
-               FreeSound ();
+               SndSys_Shutdown ();
                return false;
        }
 
@@ -507,7 +387,7 @@ qboolean SNDDMA_InitWav (void)
        if (lpWaveHdr == NULL)
        {
                Con_Print("Sound: Failed to lock header.\n");
-               FreeSound ();
+               SndSys_Shutdown ();
                return false;
        }
 
@@ -523,40 +403,42 @@ qboolean SNDDMA_InitWav (void)
                                MMSYSERR_NOERROR)
                {
                        Con_Print("Sound: failed to prepare wave headers\n");
-                       FreeSound ();
+                       SndSys_Shutdown ();
                        return false;
                }
        }
 
-       shm->samples = gSndBufSize / shm->format.width;
-       shm->sampleframes = shm->samples / shm->format.channels;
-       shm->samplepos = 0;
-       shm->buffer = (unsigned char *) lpData;
+       snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
 
        prev_painted = 0;
        paintpot = 0;
 
+       snd_sent = 0;
+       snd_completed = 0;
+
        wav_init = true;
 
        return true;
 }
 
+
 /*
-==================
-SNDDMA_Init
+====================
+SndSys_Init
 
-Try to find a sound device to mix for.
-Returns false if nothing is found.
-==================
+Create "snd_renderbuffer" with the proper sound format if the call is successful
+May return a suggested format if the requested format isn't available
+====================
 */
-qboolean SNDDMA_Init(void)
+qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
 {
+       qboolean wavonly;
        sndinitstat     stat;
 
-// COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
-       if (COM_CheckParm ("-wavonly"))
-               wavonly = true;
+       Con_Print ("SndSys_Init: using the Win32 module\n");
 
+// COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
+       wavonly = (COM_CheckParm ("-wavonly") != 0);
        dsound_init = false;
        wav_init = false;
 
@@ -565,7 +447,7 @@ qboolean SNDDMA_Init(void)
        // Init DirectSound
        if (!wavonly)
        {
-               stat = SNDDMA_InitDirect ();
+               stat = SndSys_InitDirectSound (requested, suggested);
 
                if (stat == SIS_SUCCESS)
                        Con_Print("DirectSound initialized\n");
@@ -579,69 +461,97 @@ qboolean SNDDMA_Init(void)
        // to have sound)
        if (!dsound_init && (stat != SIS_NOTAVAIL))
        {
-               if (SNDDMA_InitWav ())
-                       Con_Print("Wave sound initialized\n");
+               if (SndSys_InitMmsystem (requested, suggested))
+                       Con_Print("Wave sound (MMSYSTEM) initialized\n");
                else
                        Con_Print("Wave sound failed to init\n");
        }
 
-       if (!dsound_init && !wav_init)
-               return false;
-
-       return true;
+       return (dsound_init || wav_init);
 }
 
+
 /*
-==============
-SNDDMA_GetDMAPos
+====================
+SndSys_Shutdown
 
-return the current sample position (in mono samples read)
-inside the recirculating dma buffer, so the mixing code will know
-how many sample are required to fill it up.
-===============
+Stop the sound card, delete "snd_renderbuffer" and free its other resources
+====================
 */
-int SNDDMA_GetDMAPos(void)
+void SndSys_Shutdown (void)
 {
-       DWORD dwTime, s;
+       if (pDSBuf)
+       {
+               IDirectSoundBuffer_Stop(pDSBuf);
+               IDirectSoundBuffer_Release(pDSBuf);
+       }
 
-       if (dsound_init)
+       // only release primary buffer if it's not also the mixing buffer we just released
+       if (pDSPBuf && (pDSBuf != pDSPBuf))
+       {
+               IDirectSoundBuffer_Release(pDSPBuf);
+       }
+
+       if (pDS)
        {
-               pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwTime, NULL);
-               s = dwTime - dwStartTime;
+               IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
+               IDirectSound_Release(pDS);
        }
-       else if (wav_init)
+
+       if (hWaveOut)
        {
-               // Find which sound blocks have completed
-               for (;;)
+               waveOutReset (hWaveOut);
+
+               if (lpWaveHdr)
                {
-                       if (snd_completed == snd_sent)
-                       {
-                               Con_DPrint("Sound overrun\n");
-                               break;
-                       }
+                       unsigned int i;
 
-                       if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
-                               break;
+                       for (i=0 ; i< WAV_BUFFERS ; i++)
+                               waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
+               }
 
-                       snd_completed++;        // this buffer has been played
+               waveOutClose (hWaveOut);
+
+               if (hWaveHdr)
+               {
+                       GlobalUnlock(hWaveHdr);
+                       GlobalFree(hWaveHdr);
                }
 
-               s = snd_completed * WAV_BUFFER_SIZE;
+               if (hData)
+               {
+                       GlobalUnlock(hData);
+                       GlobalFree(hData);
+               }
        }
-       else
-               return 0;
 
-       return (s >> (shm->format.width - 1)) & (shm->samples - 1);
+   &