]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
- 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 fb60cb96b02b0b740dc9f25095cfe8fde565345f..6d86a291f161ae5c3b48c743d7baa5b62a306586 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 36e858dee589dca6c48541c8730375e5a6340db1..eaa1a765d8e05bc5c65328a808591ad5053941e1 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 c56df89e33d5a373505bb2249e5d6dd53458c77e..61ada9963822ba6fc7cd576c6120083fe5c315f3 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 31f5c1e4e113587e8be8493c50942727c2a90af4..2e6b8f3755857c07de02b4cfe6946138aa63bbfa 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 923d6fc8c119b3d6c859c75ce411bf2b92139ccd..adb28d8fb6c8eb3d78867a5f6471157fddeee240 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 093893016ab4486fb85c2312142c22243fc362d9..a7f711e7f3915f63b7933375f8882e0dff0c4223 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 e5066ad8c2835e353f3a846aa5e6214566ea0208..18cc555a93d4da94791385b282f94087f631dcc5 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 ec097d22d795e7bb92db06f41fd77dce70616804..47b622d089213eabc63aba784d260dda7f80d18b 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 37781bfa05d22cd24ee2f4bc35cfd0505afe864e..777dd6c6652b3c9769c4d99d1d374ba4601fc98c 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 03ba042a6a71a027edabf171fa6031aba7bff7e9..0e4dada06e3248d9f37a835ca326d6a907389f2c 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 3da10596c4638b1eb896c0e084cf2e9b2e66db36..43619e924b30f3dc70db408ee508f67fdbb7fd7e 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 b0073d263f6a9b9b5d9efe98379654b8c70fd203..0a6f7fdd8d98b89628841e93358db6f0c38194cd 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 4d200b79f6bba8f545ed6d1c32ad3e35eec56c89..f8c5fe78ffdb61900d58bde97d6bbd834a75b544 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 f50c321c58c03d2cd2aa7d6145091c72ce7575bc..8980069181404aea89782a2669dd2c83b43fd51a 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 cfeeb1b49905c5259e990d14e40d1662e81d22fa..6dccba7371fae2146d18286b2e7f1a909a168fb2 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 89bd6c6e4cd9f5ba45406507929aa235c883345d..d96c8199ee69cf307709312150d767f360528532 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 ae471620a6fbef6e913cb57261ce64cad5afe1ac..89c7ee518102c940f332e473c9782392296e3fb1 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 490edd292d92ba1163a9719fa28586701d133292..89b333d54b2c18b2e75baf6428e919eca4bea007 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);
+       if (snd_renderbuffer != NULL)
+       {
+               Mem_Free(snd_renderbuffer);
+               snd_renderbuffer = NULL;
+       }
+
+       pDS = NULL;
+       pDSBuf = NULL;
+       pDSPBuf = NULL;
+       hWaveOut = 0;
+       hData = 0;
+       hWaveHdr = 0;
+       lpData = NULL;
+       lpWaveHdr = NULL;
+       dsound_init = false;
+       wav_init = false;
 }
 
+
 /*
-==============
-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)
 {
        LPWAVEHDR       h;
        int                     wResult;
@@ -650,10 +560,10 @@ void SNDDMA_Submit(void)
        if (!wav_init)
                return;
 
-       paintpot += (paintedtime - prev_painted) * shm->format.channels * shm->format.width;
+       paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
        if (paintpot > WAV_BUFFERS * WAV_BUFFER_SIZE)
                paintpot = WAV_BUFFERS * WAV_BUFFER_SIZE;
-       prev_painted = paintedtime;
+       prev_painted = snd_renderbuffer->endframe;
 
        // submit new sound blocks
        while (paintpot > WAV_BUFFER_SIZE)
@@ -671,24 +581,62 @@ void SNDDMA_Submit(void)
                if (wResult != MMSYSERR_NOERROR)
                {
                        Con_Print("Failed to write block to device\n");
-                       FreeSound ();
+                       SndSys_Shutdown ();
                        return;
                }
 
                paintpot -= WAV_BUFFER_SIZE;
        }
+
 }
 
+
 /*
-==============
-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)
 {
-       FreeSound ();
+       unsigned int s;
+
+       if (dsound_init)
+       {
+               DWORD dwTime;
+
+               IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
+               s = (dwTime - dwStartTime) & (gSndBufSize - 1);
+               dwStartTime = dwTime;
+
+               dsound_time += s / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
+               return dsound_time;
+       }
+
+       if (wav_init)
+       {
+               // Find which sound blocks have completed
+               for (;;)
+               {
+                       if (snd_completed == snd_sent)
+                       {
+                               Con_DPrint("Sound overrun\n");
+                               break;
+                       }
+
+                       if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
+                               break;
+
+                       snd_completed++;        // this buffer has been played
+               }
+
+               s = snd_completed * WAV_BUFFER_SIZE;
+
+               return s / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels);
+       }
+
+       return 0;
 }
 
 
@@ -697,7 +645,14 @@ static DWORD dsound_dwSize2;
 static DWORD *dsound_pbuf;
 static DWORD *dsound_pbuf2;
 
-void *S_LockBuffer(void)
+/*
+====================
+SndSys_LockRenderBuffer
+
+Get the exclusive lock on "snd_renderbuffer"
+====================
+*/
+qboolean SndSys_LockRenderBuffer (void)
 {
        int reps;
        HRESULT hresult;
@@ -706,25 +661,28 @@ void *S_LockBuffer(void)
        if (pDSBuf)
        {
                // if the buffer was lost or stopped, restore it and/or restart it
-               if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK)
+               if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
                        Con_Print("Couldn't get sound buffer status\n");
 
                if (dwStatus & DSBSTATUS_BUFFERLOST)
-                       pDSBuf->lpVtbl->Restore (pDSBuf);
+               {
+                       Con_Print("DSound buffer is lost!!\n");
+                       IDirectSoundBuffer_Restore (pDSBuf);
+               }
 
                if (!(dwStatus & DSBSTATUS_PLAYING))
-                       pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
+                       IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
 
                reps = 0;
 
-               while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
+               while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
                {
                        if (hresult != DSERR_BUFFERLOST)
                        {
                                Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
                                S_Shutdown ();
                                S_Startup ();
-                               return NULL;
+                               return false;
                        }
 
                        if (++reps > 10000)
@@ -732,20 +690,28 @@ void *S_LockBuffer(void)
                                Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
                                S_Shutdown ();
                                S_Startup ();
-                               return NULL;
+                               return false;
                        }
                }
-               return dsound_pbuf;
+
+               if ((void*)dsound_pbuf != snd_renderbuffer->ring)
+                       Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
+               return true;
        }
-       else if (wav_init)
-               return shm->buffer;
-       else
-               return NULL;
+
+       return wav_init;
 }
 
-void S_UnlockBuffer(void)
+
+/*
+====================
+SndSys_UnlockRenderBuffer
+
+Release the exclusive lock on "snd_renderbuffer"
+====================
+*/
+void SndSys_UnlockRenderBuffer (void)
 {
        if (pDSBuf)
-               pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
+               IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
 }
-
diff --git a/sound.h b/sound.h
index 14d1d0540c242eb92c570e2e5eaaf0e3e5574e4f..74f9ffe9b73e77274ab1ab99865c2dda8f2ae6e5 100644 (file)
--- a/sound.h
+++ b/sound.h
@@ -81,5 +81,8 @@ void S_StopChannel (unsigned int channel_ind);
 qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value);
 void S_SetChannelVolume (unsigned int ch_ind, float fvol);
 
+void S_BlockSound (void);
+void S_UnblockSound (void);
+
 
 #endif
index 27041f6d6a5d712080557fa0a711df6ff789e8c6..f0f3045dfc25437039eeea09cabdb3fee83f8ad6 100644 (file)
--- a/sys_win.c
+++ b/sys_win.c
@@ -28,8 +28,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "conproc.h"
 #include "direct.h"
 
-extern void S_BlockSound (void);
-
 cvar_t sys_usetimegettime = {CVAR_SAVE, "sys_usetimegettime", "1", "use windows timeGetTime function (which has issues on some motherboards) for timing rather than QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power)"};
 
 HANDLE                         hinput, houtput;
index 6823840012ec9007e97b682afe6abdc34e5164ca..8a765011f38241ef3d58e785470b8f42a0205eda 100644 (file)
--- a/vid_agl.c
+++ b/vid_agl.c
@@ -53,6 +53,8 @@ static float mouse_x, mouse_y;
 static qboolean vid_isfullscreen = false;
 static qboolean vid_usingvsync = false;
 
+static qboolean sound_active = true;
+
 static int scr_width, scr_height;
 
 static AGLContext context;
@@ -325,21 +327,37 @@ static OSStatus MainWindowEventHandler (EventHandlerCallRef nextHandler, EventRe
        return err;
 }
 
+static void VID_AppFocusChanged(qboolean windowIsActive)
+{
+       if (vid_activewindow != windowIsActive)
+       {
+               vid_activewindow = windowIsActive;
+               if (!vid_activewindow)
+                       VID_RestoreSystemGamma();
+       }
+
+       if (sound_active != windowIsActive)
+       {
+               sound_active = windowIsActive;
+               if (sound_active)
+                       S_UnblockSound ();
+               else
+                       S_BlockSound ();
+       }
+}
+
 static void VID_ProcessPendingAsyncEvents (void)
 {
        // Collapsed / expanded
        if (AsyncEvent_Collapsed != vid_hidden)
        {
                vid_hidden = !vid_hidden;
-               vid_activewindow = false;
-               VID_RestoreSystemGamma();
+               VID_AppFocusChanged(!vid_hidden);
        }
 
        // Closed
        if (AsyncEvent_Quitting)
-       {
                Sys_Quit();
-       }
 }
 
 static void VID_BuildAGLAttrib(GLint *attrib, qboolean stencil, qboolean fullscreen)
@@ -537,6 +555,7 @@ int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate
        vid_usingmouse = false;
        vid_hidden = false;
        vid_activewindow = true;
+       sound_active = true;
        GL_Init();
 
        SelectWindow(window);
@@ -547,25 +566,27 @@ int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate
 
 static void Handle_KeyMod(UInt32 keymod)
 {
-       const struct keymod_to_event_s { int keybit; keynum_t event; } keymod_events [] =
+       const struct keymod_to_event_s { UInt32 keybit; keynum_t event; } keymod_events [] =
        {
-               {cmdKey,                                                K_AUX1},
-               {shiftKey,                                              K_SHIFT},
-               {alphaLock,                                             K_CAPSLOCK},
-               {optionKey,                                             K_ALT},
-               {controlKey,                                    K_CTRL},
-               {kEventKeyModifierNumLockMask,  K_NUMLOCK},
-               {kEventKeyModifierFnMask,               K_AUX2}
+               { cmdKey,                                               K_AUX1 },
+               { shiftKey,                                             K_SHIFT },
+               { alphaLock,                                    K_CAPSLOCK },
+               { optionKey,                                    K_ALT },
+               { controlKey,                                   K_CTRL },
+               { kEventKeyModifierNumLockMask, K_NUMLOCK },
+               { kEventKeyModifierFnMask,              K_AUX2 }
        };
        static UInt32 prev_keymod = 0;
        unsigned int i;
        UInt32 modChanges;
 
        modChanges = prev_keymod ^ keymod;
+       if (modChanges == 0)
+               return;
 
        for (i = 0; i < sizeof(keymod_events) / sizeof(keymod_events[0]); i++)
        {
-               int keybit = keymod_events[i].keybit;
+               UInt32 keybit = keymod_events[i].keybit;
 
                if ((modChanges & keybit) != 0)
                        Key_Event(keymod_events[i].event, '\0', (keymod & keybit) != 0);
@@ -772,11 +793,10 @@ void Sys_SendKeyEvents(void)
                                switch (eventKind)
                                {
                                        case kEventAppActivated :
-                                               vid_activewindow = true;
+                                               VID_AppFocusChanged(true);
                                                break;
                                        case kEventAppDeactivated:
-                                               vid_activewindow = false;
-                                               VID_RestoreSystemGamma();
+                                               VID_AppFocusChanged(false);
                                                break;
                                        case kEventAppQuit:
                                                Sys_Quit();
index aea7e3c1ffac8f908dfaf5c1bf34d997c68c7e01..04d6516ac50c4294fff924d9877747a822c4393f 100644 (file)
--- a/vid_glx.c
+++ b/vid_glx.c
@@ -17,12 +17,6 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
-//#include <termios.h>
-//#include <sys/ioctl.h>
-//#include <sys/stat.h>
-//#include <sys/vt.h>
-//#include <stdarg.h>
-//#include <stdio.h>
 #include <signal.h>
 
 #include <dlfcn.h>
@@ -574,7 +568,7 @@ void VID_Finish (qboolean allowmousegrab)
                        Con_Print("glXSwapIntervalSGI didn't accept the vid_vsync change, it will take effect on next vid_restart (GLX_SGI_swap_control does not allow turning off vsync)\n");
        }
 
-// handle the mouse state when windowed if that's changed
+       // handle the mouse state when windowed if that's changed
        vid_usemouse = false;
        if (allowmousegrab && vid_mouse.integer && !key_consoleactive && (key_dest != key_game || !cls.demoplayback))
                vid_usemouse = true;
@@ -849,6 +843,20 @@ int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate
 
 void Sys_SendKeyEvents(void)
 {
+       static qboolean sound_active = true;
+
+       // enable/disable sound on focus gain/loss
+       if (!vid_activewindow && sound_active)
+       {
+               S_BlockSound ();
+               sound_active = false;
+       }
+       else if (vid_activewindow && !sound_active)
+       {
+               S_UnblockSound ();
+               sound_active = true;
+       }
+
        HandleEvents();
 }
 
index 60322025dcf4f6e60c7fb1b222902e4028cd9a90..c3cfda10a498ba8b0891a70512c0477b391d26ff 100644 (file)
--- a/vid_sdl.c
+++ b/vid_sdl.c
@@ -279,6 +279,7 @@ static keynum_t buttonremap[18] =
 
 void Sys_SendKeyEvents( void )
 {
+       static qboolean sound_active = true;
        SDL_Event event;
 
        while( SDL_PollEvent( &event ) )
@@ -308,6 +309,18 @@ void Sys_SendKeyEvents( void )
                                        Key_Event( buttonremap[event.button.button - 1], 0, false );
                                break;
                }
+
+       // enable/disable sound on focus gain/loss
+       if (!vid_activewindow && sound_active)
+       {
+               S_BlockSound ();
+               sound_active = false;
+       }
+       else if (vid_activewindow && !sound_active)
+       {
+               S_UnblockSound ();
+               sound_active = true;
+       }
 }
 
 /////////////////
index c52d0d99e8bfd6bd24bfbd8a67eebe8d1478967f..3c5614d69b0faaa56dd2789c3252a126c3d32e92 100644 (file)
--- a/vid_wgl.c
+++ b/vid_wgl.c
@@ -30,8 +30,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include <commctrl.h>
 #include <dinput.h>
 
-extern void S_BlockSound (void);
-extern void S_UnblockSound (void);
 extern HINSTANCE global_hInstance;
 
 
@@ -414,7 +412,7 @@ void AppActivate(BOOL fActive, BOOL minimize)
 *
 ****************************************************************************/
 {
-       static BOOL     sound_active;
+       static qboolean sound_active = false;  // initially blocked by Sys_InitConsole()
 
        vid_activewindow = fActive;
        vid_hidden = minimize;