##### BSD Make specific definitions #####
-MAKE:=$(MAKE)
+MAKE:=$(MAKE) -f BSDmakefile
DO_LD=$(CC) -o $@ $> $(LDFLAGS)
+ (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;
+}
+
+
/*-----------------------------------------------------------------*/
#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))
/*
- 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
}
#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>
#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)
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
}
===========================================================================
*/
-// snd_coreaudio.c
-// all other sound mixing is portable
-
#include <limits.h>
#include <CoreAudio/AudioHardware.h>
#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;
}
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;
Con_Printf("AudioDeviceStop: returned %d\n", status);
return;
}
-
s_isRunning = false;
+
+ pthread_mutex_destroy(&coreaudio_mutex);
status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc);
if (status)
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);
}
#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
{
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},
+ }
+ }
+};
// =======================================================================
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" };
// ====================================================================
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;
}
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;
// 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");
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);
}
-// =======================================================================
-// Load a sound
-// =======================================================================
-
/*
==================
S_FindName
-
==================
*/
sfx_t *S_FindName (const char *name)
/*
==================
S_FreeSfx
-
==================
*/
void S_FreeSfx (sfx_t *sfx, qboolean force)
/*
==================
S_ServerSounds
-
==================
*/
void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds)
/*
==================
S_PrecacheSound
-
==================
*/
sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean lock)
}
-//=============================================================================
+/*
+==================
+S_BlockSound
+==================
+*/
+void S_BlockSound (void)
+{
+ snd_blocked++;
+}
+
+
+/*
+==================
+S_UnblockSound
+==================
+*/
+void S_UnblockSound (void)
+{
+ snd_blocked--;
+}
+
/*
=================
{
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];
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;
}
}
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
int ch_idx;
int skip;
- if (!shm || !sfx || nosound.integer)
+ if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer)
return -1;
if (!sfx->fetcher)
{
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)
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++)
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 ();
}
}
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);
}
{
channel_t *target_chan;
- if (!shm || !sfx || nosound.integer)
+ if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer)
return;
if (!sfx->fetcher)
{
}
-//=============================================================================
-
/*
===================
S_UpdateAmbientSounds
-
===================
*/
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();
+}
/*
============
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++)
{
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)
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;
#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
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
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
/*
-================
-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
# 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;
}
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++;
// 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;
}
}
// 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++;
// 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;
}
// 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;
}
+
//=============================================================================
/*
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;
}
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];
#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;
}
===============================================================================
*/
-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])
// 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;
{
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)
}
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;
}
}
- // 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;
}
-
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)
Cvar_RegisterVariable(&bgmvolume);
Cvar_RegisterVariable(&volume);
Cvar_RegisterVariable(&snd_staticvolume);
- Cvar_RegisterVariable(&_snd_mixahead);
- Cvar_RegisterVariable(&snd_swapstereo);
Cvar_RegisterVariable(&snd_initialized);
}
{
return false;
}
+
+void S_BlockSound (void)
+{
+}
+
+void S_UnblockSound (void)
+{
+}
// Per-sfx data structure
typedef struct
{
- unsigned char *file;
+ unsigned char *file;
size_t filesize;
snd_format_t format;
} ogg_stream_persfx_t;
{
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;
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;
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
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;
}
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;
}
}
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 };
/*
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;
return false;
// Already loaded?
- if (s->fetcher != NULL)
+ if (sfx->fetcher != NULL)
return true;
// Load the file
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;
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
{
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);
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);
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
// 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
}
-
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 ) ) {
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();
}
-
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;
}
/*
*/
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 };
/*
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
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;
}
#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;
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
*/
#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
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;
static int prev_painted;
static unsigned int paintpot;
+static unsigned int dsound_time;
+
/*
* Global variables. Must be visible to window-procedure function
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;
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)
{
}
}
- while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
+ while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
{
if (hresult != DSERR_ALLOCATED)
{
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");
}
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;
}
// 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");
}
// 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);
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;
}
}
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;
}
// 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;
/*
==================
-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,
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);
if (hWaveHdr == NULL)
{
Con_Print("Sound: Failed to Alloc header.\n");
- FreeSound ();
+ SndSys_Shutdown ();
return false;
}
if (lpWaveHdr == NULL)
{
Con_Print("Sound: Failed to lock header.\n");
- FreeSound ();
+ SndSys_Shutdown ();
return false;
}
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;
// Init DirectSound
if (!wavonly)
{
- stat = SNDDMA_InitDirect ();
+ stat = SndSys_InitDirectSound (requested, suggested);
if (stat == SIS_SUCCESS)
Con_Print("DirectSound initialized\n");
// 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;
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)
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;
}
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;
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)
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);
}
-
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
#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;
static qboolean vid_isfullscreen = false;
static qboolean vid_usingvsync = false;
+static qboolean sound_active = true;
+
static int scr_width, scr_height;
static AGLContext context;
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)
vid_usingmouse = false;
vid_hidden = false;
vid_activewindow = true;
+ sound_active = true;
GL_Init();
SelectWindow(window);
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);
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();
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>
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;
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();
}
void Sys_SendKeyEvents( void )
{
+ static qboolean sound_active = true;
SDL_Event event;
while( SDL_PollEvent( &event ) )
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;
+ }
}
/////////////////
#include <commctrl.h>
#include <dinput.h>
-extern void S_BlockSound (void);
-extern void S_UnblockSound (void);
extern HINSTANCE global_hInstance;
*
****************************************************************************/
{
- static BOOL sound_active;
+ static qboolean sound_active = false; // initially blocked by Sys_InitConsole()
vid_activewindow = fActive;
vid_hidden = minimize;