From: molivier Date: Sun, 4 Jun 2006 10:57:24 +0000 (+0000) Subject: - the Linux sound modules (ALSA and OSS) are now write-based, instead of X-Git-Tag: xonotic-v0.1.0preview~3958 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=commitdiff_plain;h=da29a8beeb35293e2fd38b51883c91b5cf4cf4ad - the Linux sound modules (ALSA and OSS) are now write-based, instead of mmap-based. It should fix the problems with inboard sound cards - added 3 cvars controlling the sound output format: snd_speed, snd_channels, and snd_width (default values: 48000, 2, and 2 respectively). They're saved in the config file. - the checks for command line options and environment variables modifying the sound output format are now common to all modules. The command line options supported are: -sndmono, -sndstereo, -sndquad, -sndspeed, and -sndbits. The environment variables supported are: QUAKE_SOUND_CHANNELS, QUAKE_SOUND_SPEED, and QUAKE_SOUND_SAMPLEBITS. - added a (still dumb) function to figure out a better sound format when the initialization of the sound card failed - sound modules can now suggest a sound format if the initialization of the sound card failed - you can now do a "snd_restart" while having modified snd_width and/or snd_channel. Doing a snd_restart with a modified snd_speed isn't yet supported, it will fall back to the previous speed - The WGL video module was blocking the sound output when the application window lost the focus. Now, this code is shared and the AGL, GLX and SDL video modules implement such a mecanism - A lot of minor fixes, changes, factorizations and rewrites all over the sound engine code and its modules - added CeilPowerOf2() in the math library - fixed BSDmakefile for NetBSD git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6415 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/BSDmakefile b/BSDmakefile index fb60cb96..6d86a291 100644 --- a/BSDmakefile +++ b/BSDmakefile @@ -69,7 +69,7 @@ LIB_SOUND=$(LIB_SND_BSD) ##### BSD Make specific definitions ##### -MAKE:=$(MAKE) +MAKE:=$(MAKE) -f BSDmakefile DO_LD=$(CC) -o $@ $> $(LDFLAGS) diff --git a/mathlib.c b/mathlib.c index 36e858de..eaa1a765 100644 --- a/mathlib.c +++ b/mathlib.c @@ -254,6 +254,24 @@ void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, + (t0 * vr[2] + t1 * vu[2] + vf[2] * vf[2]) * point[2]; } +/*-----------------------------------------------------------------*/ + +// returns the smallest integer greater than or equal to "value", or 0 if "value" is too big +unsigned int CeilPowerOf2(unsigned int value) +{ + unsigned int ceilvalue; + + if (value > (1U << (sizeof(int) * 8 - 1))) + return 0; + + ceilvalue = 1; + while (ceilvalue < value) + ceilvalue <<= 1; + + return ceilvalue; +} + + /*-----------------------------------------------------------------*/ diff --git a/mathlib.h b/mathlib.h index c56df89e..61ada996 100644 --- a/mathlib.h +++ b/mathlib.h @@ -53,9 +53,16 @@ extern vec3_t vec3_origin; #define lhrandom(MIN,MAX) (((double)rand() / RAND_MAX) * ((MAX)-(MIN)) + (MIN)) #define invpow(base,number) (log(number) / log(base)) + +// returns log base 2 of "n" (WARNING: "n" MUST be a power of 2!) #define log2i(n) ((((n) & 0xAAAAAAAA) != 0 ? 1 : 0) | (((n) & 0xCCCCCCCC) != 0 ? 2 : 0) | (((n) & 0xF0F0F0F0) != 0 ? 4 : 0) | (((n) & 0xFF00FF00) != 0 ? 8 : 0) | (((n) & 0xFFFF0000) != 0 ? 16 : 0)) + +// TOCHECK: what is this function supposed to do? #define bit2i(n) log2i((n) << 1) +// returns the smallest integer greater than or equal to "value", or 0 if "value" is too big +unsigned int CeilPowerOf2(unsigned int value); + #define DEG2RAD(a) ((a) * ((float) M_PI / 180.0f)) #define RAD2DEG(a) ((a) * (180.0f / (float) M_PI)) #define ANGLEMOD(a) (((int) ((a) * (65536.0f / 360.0f)) & 65535) * (360.0f / 65536.0f)) diff --git a/snd_alsa.c b/snd_alsa.c index 31f5c1e4..2e6b8f37 100644 --- a/snd_alsa.c +++ b/snd_alsa.c @@ -1,10 +1,5 @@ /* - snd_alsa.c - - Support for the ALSA 1.0.1 sound driver - - Copyright (C) 1999,2000 contributors of the QuakeForge project - Please see the file "AUTHORS" for a list of contributors + Copyright (C) 2006 Mathieu Olivier This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -26,256 +21,409 @@ */ +// ALSA module, used by Linux + + #include #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 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 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 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 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 } diff --git a/snd_bsd.c b/snd_bsd.c index 923d6fc8..adb28d8f 100644 --- a/snd_bsd.c +++ b/snd_bsd.c @@ -21,13 +21,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #ifndef SUNOS -#include +# include #endif #include #include #ifndef SUNOS -#include +# include #endif #include @@ -35,34 +35,30 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "snd_main.h" -static const int tryrates[] = {44100, 22050, 11025, 8000}; - static int audio_fd = -1; -// TODO: allocate them in SNDDMA_Init, with a size depending on -// the sound format (enough for 0.5 sec of sound for instance) -#define SND_BUFF_SIZE 65536 -static unsigned char dma_buffer [SND_BUFF_SIZE]; -static unsigned char writebuf [SND_BUFF_SIZE]; +/* +==================== +SndSys_Init -qboolean SNDDMA_Init (void) +Create "snd_renderbuffer" with the proper sound format if the call is successful +May return a suggested format if the requested format isn't available +==================== +*/ +qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) { unsigned int i; const char *snddev; audio_info_t info; - memset ((void*)shm, 0, sizeof (*shm)); - // Open the audio device #ifdef _PATH_SOUND snddev = _PATH_SOUND; +#elif defined(SUNOS) + snddev = "/dev/audio"; #else -#ifndef SUNOS snddev = "/dev/sound"; -#else - snddev = "/dev/audio"; -#endif #endif audio_fd = open (snddev, O_WRONLY | O_NDELAY | O_NONBLOCK); if (audio_fd < 0) @@ -71,131 +67,163 @@ qboolean SNDDMA_Init (void) return false; } - // Look for an appropriate sound format - // TODO: we should also test mono/stereo and bits - // TODO: support "-sndspeed", "-sndbits", "-sndmono" and "-sndstereo" - shm->format.channels = 2; - shm->format.width = 2; - for (i = 0; i < sizeof (tryrates) / sizeof (tryrates[0]); i++) - { - shm->format.speed = tryrates[i]; - - AUDIO_INITINFO (&info); - info.play.sample_rate = shm->format.speed; - info.play.channels = shm->format.channels; - info.play.precision = shm->format.width * 8; -// We only handle sound cards of the same endianess than the CPU -#if BYTE_ORDER == BIG_ENDIAN - info.play.encoding = AUDIO_ENCODING_SLINEAR_BE; -#else -#ifndef SUNOS - info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + AUDIO_INITINFO (&info); +#ifdef AUMODE_PLAY // NetBSD / OpenBSD + info.mode = AUMODE_PLAY; +#endif + info.play.sample_rate = requested->speed; + info.play.channels = requested->channels; + info.play.precision = requested->width * 8; + if (requested->width == 1) +#ifdef SUNOS + info.play.encoding = AUDIO_ENCODING_LINEAR8; #else - info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.encoding = AUDIO_ENCODING_ULINEAR; #endif + else +#ifdef SUNOS + info.play.encoding = AUDIO_ENCODING_LINEAR; +#else +# if BYTE_ORDER == BIG_ENDIAN + info.play.encoding = AUDIO_ENCODING_SLINEAR_BE; +# else + info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; +# endif #endif - if (ioctl (audio_fd, AUDIO_SETINFO, &info) == 0) - break; - } - if (i == sizeof (tryrates) / sizeof (tryrates[0])) - { - Con_Print("Can't select an appropriate sound output format\n"); - close (audio_fd); - return false; - } - // Print some information - Con_Printf("%d bit %s sound initialized (rate: %dHz)\n", - info.play.precision, - (info.play.channels == 2) ? "stereo" : "mono", - info.play.sample_rate); + if (ioctl (audio_fd, AUDIO_SETINFO, &info) == 0) + break; - shm->sampleframes = sizeof (dma_buffer) / shm->format.width / shm->format.channels; - shm->samples = shm->sampleframes * shm->format.channels; - shm->samplepos = 0; - shm->buffer = dma_buffer; + // TODO: check the parameters with AUDIO_GETINFO + // TODO: check AUDIO_ENCODINGFLAG_EMULATED with AUDIO_GETENC + snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL); return true; } -int SNDDMA_GetDMAPos (void) -{ - audio_info_t info; - if (!shm) - return 0; +/* +==================== +SndSys_Shutdown - if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) +Stop the sound card, delete "snd_renderbuffer" and free its other resources +==================== +*/ +void SndSys_Shutdown (void) +{ + if (audio_fd >= 0) { - Con_Print("Error: can't get audio info\n"); - SNDDMA_Shutdown (); - return 0; + close(audio_fd); + audio_fd = -1; } - return ((info.play.samples * shm->format.channels) % shm->samples); + if (snd_renderbuffer != NULL) + { + Mem_Free(snd_renderbuffer->ring); + Mem_Free(snd_renderbuffer); + snd_renderbuffer = NULL; + } } -void SNDDMA_Shutdown (void) -{ - close (audio_fd); - audio_fd = -1; -} /* -============== -SNDDMA_Submit +==================== +SndSys_Submit -Send sound to device if buffer isn't really the dma buffer -=============== +Submit the contents of "snd_renderbuffer" to the sound card +==================== */ -void SNDDMA_Submit (void) +void SndSys_Submit (void) { - int bsize; - int bytes, b; - static int wbufp = 0; - unsigned char *p; - int idx; - int stop = paintedtime; - - if (!shm) - return; - - if (paintedtime < wbufp) - wbufp = 0; // reset - - bsize = shm->format.channels * shm->format.width; - bytes = (paintedtime - wbufp) * bsize; - - if (!bytes) + unsigned int startoffset, factor, limit, nbframes; + int written; + + if (audio_fd < 0 || + snd_renderbuffer->startframe == snd_renderbuffer->endframe) return; - if (bytes > sizeof (writebuf)) + startoffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes; + factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels; + limit = snd_renderbuffer->maxframes - startoffset; + nbframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe; + if (nbframes > limit) { - bytes = sizeof (writebuf); - stop = wbufp + bytes / bsize; + written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], limit * factor); + if (written < 0) + { + Con_Printf("SndSys_Submit: audio write returned %d!\n", written); + return; + } + + if (written % factor != 0) + Sys_Error("SndSys_Submit: nb of bytes written (%d) isn't aligned to a frame sample!\n", written); + + snd_renderbuffer->startframe += written / factor; + + if ((unsigned int)written < nbframes * factor) + { + Con_Printf("SndSys_Submit: audio can't keep up! (%d < %u)\n", written, nbframes * factor); + return; + } + + nbframes -= limit; + startoffset = 0; } - // Transfert the sound data from the circular dma_buffer to writebuf - // TODO: using 2 memcpys instead of this loop should be faster - p = writebuf; - idx = (wbufp*bsize) & (sizeof (dma_buffer) - 1); - for (b = bytes; b; b--) + written = write (audio_fd, &snd_renderbuffer->ring[startoffset * factor], nbframes * factor); + if (written < 0) { - *p++ = dma_buffer[idx]; - idx = (idx + 1) & (sizeof (dma_buffer) - 1); + Con_Printf("SndSys_Submit: audio write returned %d!\n", written); + return; } + snd_renderbuffer->startframe += written / factor; +} + + +/* +==================== +SndSys_GetSoundTime - if (write (audio_fd, writebuf, bytes) < bytes) - Con_Print("audio can't keep up!\n"); +Returns the number of sample frames consumed since the sound started +==================== +*/ +unsigned int SndSys_GetSoundTime (void) +{ + audio_info_t info; - wbufp = stop; + if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) + { + Con_Print("Error: can't get audio info\n"); + SNDDMA_Shutdown (); + return 0; + } + + return info.play.samples; } -void *S_LockBuffer (void) + +/* +==================== +SndSys_LockRenderBuffer + +Get the exclusive lock on "snd_renderbuffer" +==================== +*/ +qboolean SndSys_LockRenderBuffer (void) { - return shm->buffer; + // Nothing to do + return true; } -void S_UnlockBuffer (void) + +/* +==================== +SndSys_UnlockRenderBuffer + +Release the exclusive lock on "snd_renderbuffer" +==================== +*/ +void SndSys_UnlockRenderBuffer (void) { + // Nothing to do } diff --git a/snd_coreaudio.c b/snd_coreaudio.c index 09389301..a7f711e7 100644 --- a/snd_coreaudio.c +++ b/snd_coreaudio.c @@ -20,9 +20,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ -// snd_coreaudio.c -// all other sound mixing is portable - #include #include @@ -30,101 +27,149 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "quakedef.h" #include "snd_main.h" -// BUFFER_SIZE must be an even multiple of CHUNK_SIZE -#define CHUNK_SIZE 2048 -#define BUFFER_SIZE 16384 -static unsigned int submissionChunk; -static unsigned int maxMixedSamples; -static short *s_mixedSamples; -static int s_chunkCount; // number of chunks submitted -static qboolean s_isRunning; +#define CHUNK_SIZE 1024 -static AudioDeviceID outputDeviceID; -static AudioStreamBasicDescription outputStreamBasicDescription; +static unsigned int submissionChunk = 0; // in sample frames +static unsigned int coreaudiotime = 0; // based on the number of chunks submitted so far +static qboolean s_isRunning = false; +static pthread_mutex_t coreaudio_mutex; +static AudioDeviceID outputDeviceID = kAudioDeviceUnknown; /* -=============== +==================== audioDeviceIOProc -=============== +==================== */ - -OSStatus audioDeviceIOProc(AudioDeviceID inDevice, - const AudioTimeStamp *inNow, - const AudioBufferList *inInputData, - const AudioTimeStamp *inInputTime, - AudioBufferList *outOutputData, - const AudioTimeStamp *inOutputTime, - void *inClientData) +static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, + const AudioTimeStamp *inNow, + const AudioBufferList *inInputData, + const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, + const AudioTimeStamp *inOutputTime, + void *inClientData) { - int offset; - short *samples; - unsigned int sampleIndex; float *outBuffer; - float scale; - - offset = (s_chunkCount * submissionChunk) % maxMixedSamples; - samples = s_mixedSamples + offset; - - outBuffer = (float *)outOutputData->mBuffers[0].mData; - - // If we have run out of samples, return silence - if (s_chunkCount * submissionChunk > shm->format.channels * paintedtime) - { - memset(outBuffer, 0, sizeof(*outBuffer) * submissionChunk); - } - else + unsigned int frameCount, factor; + + outBuffer = (float*)outOutputData->mBuffers[0].mData; + factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width; + frameCount = 0; + + // Lock the snd_renderbuffer + if (SndSys_LockRenderBuffer()) { + unsigned int maxFrames, sampleIndex, sampleCount; + unsigned int startOffset, endOffset; + const short *samples; + const float scale = 1.0f / SHRT_MAX; + + // Transfert up to a chunk of sample frames from snd_renderbuffer to outBuffer + maxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe; + if (maxFrames >= submissionChunk) + frameCount = submissionChunk; + else + frameCount = maxFrames; + // Convert the samples from shorts to floats. Scale the floats to be [-1..1]. - scale = (1.0f / SHRT_MAX); - for (sampleIndex = 0; sampleIndex < submissionChunk; sampleIndex++) - outBuffer[sampleIndex] = samples[sampleIndex] * scale; + startOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes; + endOffset = (snd_renderbuffer->startframe + frameCount) % snd_renderbuffer->maxframes; + if (startOffset > endOffset) // if the buffer wraps + { + sampleCount = (snd_renderbuffer->maxframes - startOffset) * snd_renderbuffer->format.channels; + samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]); + for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) + outBuffer[sampleIndex] = samples[sampleIndex] * scale; + + outBuffer = &outBuffer[sampleCount]; + sampleCount = frameCount * snd_renderbuffer->format.channels - sampleCount; + samples = (const short*)(&snd_renderbuffer->ring[0]); + for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) + outBuffer[sampleIndex] = samples[sampleIndex] * scale; + } + else + { + sampleCount = frameCount * snd_renderbuffer->format.channels; + samples = (const short*)(&snd_renderbuffer->ring[startOffset * factor]); + for (sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) + outBuffer[sampleIndex] = samples[sampleIndex] * scale; + } + + snd_renderbuffer->startframe += frameCount; + + // Unlock the snd_renderbuffer + SndSys_UnlockRenderBuffer(); + } - s_chunkCount++; // this is the next buffer we will submit + // If there was not enough samples, complete with silence samples + if (frameCount < submissionChunk) + { + unsigned int missingFrames; + + missingFrames = submissionChunk - frameCount; + Con_DPrintf("audioDeviceIOProc: %u sample frames missing\n", missingFrames); + memset(&outBuffer[frameCount * snd_renderbuffer->format.channels], 0, missingFrames * sizeof(outBuffer[0])); } + coreaudiotime += submissionChunk; return 0; } + /* -=============== -SNDDMA_Init -=============== +==================== +SndSys_Init + +Create "snd_renderbuffer" with the proper sound format if the call is successful +May return a suggested format if the requested format isn't available +==================== */ -qboolean SNDDMA_Init(void) +qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) { OSStatus status; UInt32 propertySize, bufferByteCount; + AudioStreamBasicDescription streamDesc; if (s_isRunning) return true; Con_Printf("Initializing CoreAudio...\n"); + + // We only accept 16-bit samples for the moment + if (requested->width != 2) + { + // Suggest a 16-bit format instead + if (suggested != NULL) + { + memcpy (suggested, requested, sizeof (suggested)); + suggested->width = 2; + } + return false; + } + // Get the output device propertySize = sizeof(outputDeviceID); status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &outputDeviceID); if (status) { - Con_Printf("AudioHardwareGetProperty returned %d\n", status); + Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioHardwarePropertyDefaultOutputDevice\n", status); return false; } - if (outputDeviceID == kAudioDeviceUnknown) { - Con_Printf("AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n"); + Con_Printf("CoreAudio: outputDeviceID is kAudioDeviceUnknown\n"); return false; } // Configure the output device - // TODO: support "-sndspeed", "-sndmono" and "-sndstereo" propertySize = sizeof(bufferByteCount); - bufferByteCount = CHUNK_SIZE * sizeof(float); + bufferByteCount = CHUNK_SIZE * sizeof(float) * requested->channels; status = AudioDeviceSetProperty(outputDeviceID, NULL, 0, false, kAudioDevicePropertyBufferSize, propertySize, &bufferByteCount); if (status) { - Con_Printf("AudioDeviceSetProperty: returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE); + Con_Printf("CoreAudio: AudioDeviceSetProperty() returned %d when setting kAudioDevicePropertyBufferSize to %d\n", status, CHUNK_SIZE); return false; } @@ -132,101 +177,90 @@ qboolean SNDDMA_Init(void) status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &bufferByteCount); if (status) { - Con_Printf("AudioDeviceGetProperty: returned %d when setting kAudioDevicePropertyBufferSize\n", status); + Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when setting kAudioDevicePropertyBufferSize\n", status); return false; } + submissionChunk = bufferByteCount / sizeof(float); - Con_DPrintf(" Chunk size = %d samples\n", submissionChunk); + if (submissionChunk % requested->channels != 0) + { + Con_Print("CoreAudio: chunk size is NOT a multiple of the number of channels\n"); + return false; + } + submissionChunk /= requested->channels; + Con_DPrintf(" Chunk size = %d sample frames\n", submissionChunk); // Print out the device status - propertySize = sizeof(outputStreamBasicDescription); - status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &outputStreamBasicDescription); + propertySize = sizeof(streamDesc); + status = AudioDeviceGetProperty(outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &streamDesc); if (status) { - Con_Printf("AudioDeviceGetProperty: returned %d when getting kAudioDevicePropertyStreamFormat\n", status); + Con_Printf("CoreAudio: AudioDeviceGetProperty() returned %d when getting kAudioDevicePropertyStreamFormat\n", status); return false; } - Con_DPrintf(" Hardware format:\n"); - Con_DPrintf(" %5d mSampleRate\n", (unsigned int)outputStreamBasicDescription.mSampleRate); + Con_DPrint (" Hardware format:\n"); + Con_DPrintf(" %5d mSampleRate\n", (unsigned int)streamDesc.mSampleRate); Con_DPrintf(" %c%c%c%c mFormatID\n", - (outputStreamBasicDescription.mFormatID & 0xff000000) >> 24, - (outputStreamBasicDescription.mFormatID & 0x00ff0000) >> 16, - (outputStreamBasicDescription.mFormatID & 0x0000ff00) >> 8, - (outputStreamBasicDescription.mFormatID & 0x000000ff) >> 0); - Con_DPrintf(" %5d mBytesPerPacket\n", outputStreamBasicDescription.mBytesPerPacket); - Con_DPrintf(" %5d mFramesPerPacket\n", outputStreamBasicDescription.mFramesPerPacket); - Con_DPrintf(" %5d mBytesPerFrame\n", outputStreamBasicDescription.mBytesPerFrame); - Con_DPrintf(" %5d mChannelsPerFrame\n", outputStreamBasicDescription.mChannelsPerFrame); - Con_DPrintf(" %5d mBitsPerChannel\n", outputStreamBasicDescription.mBitsPerChannel); - - if(outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM) + (streamDesc.mFormatID & 0xff000000) >> 24, + (streamDesc.mFormatID & 0x00ff0000) >> 16, + (streamDesc.mFormatID & 0x0000ff00) >> 8, + (streamDesc.mFormatID & 0x000000ff) >> 0); + Con_DPrintf(" %5d mBytesPerPacket\n", streamDesc.mBytesPerPacket); + Con_DPrintf(" %5d mFramesPerPacket\n", streamDesc.mFramesPerPacket); + Con_DPrintf(" %5d mBytesPerFrame\n", streamDesc.mBytesPerFrame); + Con_DPrintf(" %5d mChannelsPerFrame\n", streamDesc.mChannelsPerFrame); + Con_DPrintf(" %5d mBitsPerChannel\n", streamDesc.mBitsPerChannel); + + if(streamDesc.mFormatID != kAudioFormatLinearPCM) { - Con_Printf("Default Audio Device doesn't support Linear PCM!\n"); + Con_Print("CoreAudio: Default audio device doesn't support linear PCM!\n"); return false; } - // Start sound running + // Add the callback function status = AudioDeviceAddIOProc(outputDeviceID, audioDeviceIOProc, NULL); if (status) { - Con_Printf("AudioDeviceAddIOProc: returned %d\n", status); + Con_Printf("CoreAudio: AudioDeviceAddIOProc() returned %d\n", status); return false; } - maxMixedSamples = BUFFER_SIZE; - s_mixedSamples = (short *)Mem_Alloc (snd_mempool, sizeof(*s_mixedSamples) * maxMixedSamples); - Con_DPrintf(" Buffer size = %d samples (%d chunks)\n", - maxMixedSamples, (maxMixedSamples / submissionChunk)); + // We haven't sent any sample frames yet + coreaudiotime = 0; - // Tell the main app what we expect from it - memset ((void*)shm, 0, sizeof (*shm)); - shm->format.speed = outputStreamBasicDescription.mSampleRate; - shm->format.channels = outputStreamBasicDescription.mChannelsPerFrame; - shm->format.width = 2; - shm->sampleframes = maxMixedSamples / shm->format.channels; - shm->samples = maxMixedSamples; - shm->buffer = (unsigned char *)s_mixedSamples; - shm->samplepos = 0; + if (pthread_mutex_init(&coreaudio_mutex, NULL) != 0) + { + Con_Print("CoreAudio: can't create pthread mutex\n"); + AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc); + return false; + } - // We haven't enqueued anything yet - s_chunkCount = 0; + snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL); + // Start sound running status = AudioDeviceStart(outputDeviceID, audioDeviceIOProc); if (status) { - Con_Printf("AudioDeviceStart: returned %d\n", status); + Con_Printf("CoreAudio: AudioDeviceStart() returned %d\n", status); + pthread_mutex_destroy(&coreaudio_mutex); + AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc); return false; } - s_isRunning = true; - Con_Printf(" Initialization successful\n"); - + Con_Print(" Initialization successful\n"); return true; } -/* -=============== -SNDDMA_GetDMAPos - -return the current sample position (in mono samples read) -inside the recirculating dma buffer, so the mixing code will know -how many sample are required to fill it up. -=============== -*/ -int SNDDMA_GetDMAPos(void) -{ - return (s_chunkCount * submissionChunk) % shm->samples; -} /* -=============== -SNDDMA_Shutdown +==================== +SndSys_Shutdown -Reset the sound device for exiting -=============== +Stop the sound card, delete "snd_renderbuffer" and free its other resources +==================== */ -void SNDDMA_Shutdown(void) +void SndSys_Shutdown(void) { OSStatus status; @@ -239,8 +273,9 @@ void SNDDMA_Shutdown(void) Con_Printf("AudioDeviceStop: returned %d\n", status); return; } - s_isRunning = false; + + pthread_mutex_destroy(&coreaudio_mutex); status = AudioDeviceRemoveIOProc(outputDeviceID, audioDeviceIOProc); if (status) @@ -249,38 +284,62 @@ void SNDDMA_Shutdown(void) return; } - Mem_Free(s_mixedSamples); - s_mixedSamples = NULL; - shm->buffer = NULL; + if (snd_renderbuffer != NULL) + { + Mem_Free(snd_renderbuffer->ring); + Mem_Free(snd_renderbuffer); + snd_renderbuffer = NULL; + } +} + + +/* +==================== +SndSys_Submit + +Submit the contents of "snd_renderbuffer" to the sound card +==================== +*/ +void SndSys_Submit (void) +{ + // Nothing to do here (this sound module is callback-based) } + /* -=============== -SNDDMA_Submit -=============== +==================== +SndSys_GetSoundTime + +Returns the number of sample frames consumed since the sound started +==================== */ -void SNDDMA_Submit(void) +unsigned int SndSys_GetSoundTime (void) { - // nothing to do (CoreAudio is callback-based) + return coreaudiotime; } + /* -=============== -S_LockBuffer -=============== +==================== +SndSys_LockRenderBuffer + +Get the exclusive lock on "snd_renderbuffer" +==================== */ -void *S_LockBuffer(void) +qboolean SndSys_LockRenderBuffer (void) { - // not necessary (just return the buffer) - return shm->buffer; + return (pthread_mutex_lock(&coreaudio_mutex) == 0); } + /* -=============== -S_UnlockBuffer -=============== +==================== +SndSys_UnlockRenderBuffer + +Release the exclusive lock on "snd_renderbuffer" +==================== */ -void S_UnlockBuffer(void) +void SndSys_UnlockRenderBuffer (void) { - // not necessary + pthread_mutex_unlock(&coreaudio_mutex); } diff --git a/snd_main.c b/snd_main.c index e5066ad8..18cc555a 100644 --- a/snd_main.c +++ b/snd_main.c @@ -24,8 +24,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "snd_main.h" #include "snd_ogg.h" + +#define SND_MIN_SPEED 8000 +#define SND_MAX_SPEED 96000 +#define SND_MIN_WIDTH 1 +#define SND_MAX_WIDTH 2 +#define SND_MIN_CHANNELS 1 +#define SND_MAX_CHANNELS 8 + #if SND_LISTENERS != 8 -#error this data only supports up to 8 channel, update it! +# error this data only supports up to 8 channel, update it! #endif typedef struct listener_s { @@ -45,11 +53,79 @@ speakerlayout_t; static speakerlayout_t snd_speakerlayout; -void S_Play(void); -void S_PlayVol(void); -void S_Play2(void); -void S_SoundList(void); -void S_Update_(); +#define SND_SPEAKERLAYOUTS (sizeof(snd_speakerlayouts) / sizeof(snd_speakerlayouts[0])) +static const speakerlayout_t snd_speakerlayouts[] = +{ + { + "surround71", 8, + { + {45, 0.2, 0.2, 0.5}, // front left + {315, 0.2, 0.2, 0.5}, // front right + {135, 0.2, 0.2, 0.5}, // rear left + {225, 0.2, 0.2, 0.5}, // rear right + {0, 0.2, 0.2, 0.5}, // front center + {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so... no lfe) + {90, 0.2, 0.2, 0.5}, // side left + {180, 0.2, 0.2, 0.5}, // side right + } + }, + { + "surround51", 6, + { + {45, 0.2, 0.2, 0.5}, // front left + {315, 0.2, 0.2, 0.5}, // front right + {135, 0.2, 0.2, 0.5}, // rear left + {225, 0.2, 0.2, 0.5}, // rear right + {0, 0.2, 0.2, 0.5}, // front center + {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so... no lfe) + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + }, + { + // these systems sometimes have a subwoofer as well, but it has no + // channel of its own + "surround40", 4, + { + {45, 0.3, 0.3, 0.8}, // front left + {315, 0.3, 0.3, 0.8}, // front right + {135, 0.3, 0.3, 0.8}, // rear left + {225, 0.3, 0.3, 0.8}, // rear right + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + }, + { + // these systems sometimes have a subwoofer as well, but it has no + // channel of its own + "stereo", 2, + { + {90, 0.5, 0.5, 1}, // side left + {270, 0.5, 0.5, 1}, // side right + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + }, + { + "mono", 1, + { + {0, 0, 1, 1}, // center + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + } + } +}; // ======================================================================= @@ -59,47 +135,54 @@ void S_Update_(); channel_t channels[MAX_CHANNELS]; unsigned int total_channels; -int snd_blocked = 0; -cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"}; -cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1", "enables keeping compressed ogg sound files compressed, decompressing them only as needed, otherwise they will be decompressed completely at load (may use a lot of memory)"}; - -volatile dma_t *shm = 0; -volatile dma_t sn; +snd_ringbuffer_t *snd_renderbuffer = NULL; +unsigned int soundtime = 0; +static unsigned int oldpaintedtime = 0; +unsigned int extrasoundtime = 0; +static double snd_starttime = 0.0; vec3_t listener_origin; matrix4x4_t listener_matrix[SND_LISTENERS]; vec_t sound_nominal_clip_dist=1000.0; mempool_t *snd_mempool; -int soundtime; -int paintedtime; - // Linked list of known sfx -sfx_t *known_sfx = NULL; +static sfx_t *known_sfx = NULL; + +static qboolean sound_spatialized = false; -qboolean sound_spatialized = false; +qboolean simsound = false; -// Fake dma is a synchronous faking of the DMA progress used for -// isolating performance in the renderer. -qboolean fakedma = false; +int snd_blocked = 0; +// Cvars declared in sound.h (part of the sound API) cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1", "volume of background music (such as CD music or replacement files such as sound/cdtracks/track002.ogg)"}; cvar_t volume = {CVAR_SAVE, "volume", "0.7", "volume of sound effects"}; +cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"}; cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1", "volume of ambient sound effects (such as swampy sounds at the start of e1m2)"}; -cvar_t nosound = {0, "nosound", "0", "disables sound"}; -cvar_t snd_precache = {0, "snd_precache", "1", "loads sounds before they are used"}; -//cvar_t bgmbuffer = {0, "bgmbuffer", "4096", "unused quake cvar"}; -cvar_t ambient_level = {0, "ambient_level", "0.3", "volume of environment noises (water and wind)"}; -cvar_t ambient_fade = {0, "ambient_fade", "100", "rate of volume fading when moving from one environment to another"}; -cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0", "disables extra sound mixer calls that are meant to reduce the chance of sound breakup at very low framerates"}; -cvar_t snd_show = {0, "snd_show", "0", "shows some statistics about sound mixing"}; +// Cvars declared in snd_main.h (shared with other snd_*.c files) cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1", "how much sound to mix ahead of time"}; +cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1", "enables keeping compressed ogg sound files compressed, decompressing them only as needed, otherwise they will be decompressed completely at load (may use a lot of memory)"}; cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0", "swaps left/right speakers for old ISA soundblaster cards"}; +// Local cvars +static cvar_t nosound = {0, "nosound", "0", "disables sound"}; +static cvar_t snd_precache = {0, "snd_precache", "1", "loads sounds before they are used"}; +static cvar_t ambient_level = {0, "ambient_level", "0.3", "volume of environment noises (water and wind)"}; +static cvar_t ambient_fade = {0, "ambient_fade", "100", "rate of volume fading when moving from one environment to another"}; +static cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0", "disables extra sound mixer calls that are meant to reduce the chance of sound breakup at very low framerates"}; +static cvar_t snd_show = {0, "snd_show", "0", "shows some statistics about sound mixing"}; + +// Default sound format is 48KHz, 16-bit, stereo +// (48KHz because a lot of onboard sound cards sucks at any other speed) +static cvar_t snd_speed = {CVAR_SAVE, "snd_speed", "48000", "sound output frequency, in hertz"}; +static cvar_t snd_width = {CVAR_SAVE, "snd_width", "2", "sound output precision, in bytes (1 and 2 supported)"}; +static cvar_t snd_channels = {CVAR_SAVE, "snd_channels", "2", "number of channels for the sound ouput (2 for stereo; up to 8 supported for 3D sound)"}; + // Ambient sounds -sfx_t* ambient_sfxs [2] = { NULL, NULL }; -const char* ambient_names [2] = { "sound/ambience/water1.wav", "sound/ambience/wind2.wav" }; +static sfx_t* ambient_sfxs [2] = { NULL, NULL }; +static const char* ambient_names [2] = { "sound/ambience/water1.wav", "sound/ambience/wind2.wav" }; // ==================================================================== @@ -108,71 +191,386 @@ const char* ambient_names [2] = { "sound/ambience/water1.wav", "sound/ambience/w void S_FreeSfx (sfx_t *sfx, qboolean force); +static void S_Play_Common (float fvol, float attenuation) +{ + int i, ch_ind; + char name [MAX_QPATH]; + sfx_t *sfx; + + i = 1; + while (i < Cmd_Argc ()) + { + // Get the name, and appends ".wav" as an extension if there's none + strlcpy (name, Cmd_Argv (i), sizeof (name)); + if (!strrchr (name, '.')) + strlcat (name, ".wav", sizeof (name)); + i++; + + // If we need to get the volume from the command line + if (fvol == -1.0f) + { + fvol = atof (Cmd_Argv (i)); + i++; + } + + sfx = S_PrecacheSound (name, true, false); + if (sfx) + { + ch_ind = S_StartSound (-1, 0, sfx, listener_origin, fvol, attenuation); + + // Free the sfx if the file didn't exist + if (ch_ind < 0) + S_FreeSfx (sfx, false); + else + channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND; + } + } +} + +static void S_Play_f(void) +{ + S_Play_Common (1.0f, 1.0f); +} + +static void S_Play2_f(void) +{ + S_Play_Common (1.0f, 0.0f); +} + +static void S_PlayVol_f(void) +{ + S_Play_Common (-1.0f, 0.0f); +} + +static void S_SoundList_f (void) +{ + unsigned int i; + sfx_t *sfx; + unsigned int total; + + total = 0; + for (sfx = known_sfx, i = 0; sfx != NULL; sfx = sfx->next, i++) + { + if (sfx->fetcher != NULL) + { + unsigned int size; + const snd_format_t* format; + + size = sfx->memsize; + format = sfx->fetcher->getfmt(sfx); + Con_Printf ("%c%c%c%c(%2db, %6s) %8i : %s\n", + (sfx->loopstart >= 0) ? 'L' : ' ', + (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ', + (sfx->locks > 0) ? 'K' : ' ', + (sfx->flags & SFXFLAG_PERMANENTLOCK) ? 'P' : ' ', + format->width * 8, + (format->channels == 1) ? "mono" : "stereo", + size, + sfx->name); + total += size; + } + else + Con_Printf (" ( unknown ) unloaded : %s\n", sfx->name); + } + Con_Printf("Total resident: %i\n", total); +} + void S_SoundInfo_f(void) { - if (!shm) + if (snd_renderbuffer == NULL) { Con_Print("sound system not started\n"); return; } - Con_Printf("%5d speakers\n", shm->format.channels); - Con_Printf("%5d frames\n", shm->sampleframes); - Con_Printf("%5d samples\n", shm->samples); - Con_Printf("%5d samplepos\n", shm->samplepos); - Con_Printf("%5d samplebits\n", shm->format.width * 8); - Con_Printf("%5d speed\n", shm->format.speed); - Con_Printf("%p dma buffer\n", shm->buffer); + Con_Printf("%5d speakers\n", snd_renderbuffer->format.channels); + Con_Printf("%5d frames\n", snd_renderbuffer->maxframes); + Con_Printf("%5d samplebits\n", snd_renderbuffer->format.width * 8); + Con_Printf("%5d speed\n", snd_renderbuffer->format.speed); Con_Printf("%5u total_channels\n", total_channels); } -void S_Startup(void) +// TODO: make this function smarter... +static qboolean S_ChooseCheaperFormat (snd_format_t* format, qboolean fixed_speed, qboolean fixed_width, qboolean fixed_channels) +{ + // Can we decrease the number of channels? + if (!fixed_channels && format->channels > 1) + { + unsigned short channels = format->channels; + + // If it has an odd number of channels(?!), make it even + if (channels & 1) + channels--; + else + { + // Remove 2 speakers, unless it's a stereo format + if (channels != 2) + channels -= 2; + else + channels = 1; + } + + format->channels = channels; + return true; + } + + // Can we decrease the speed? + if (!fixed_speed) + { + unsigned int suggest_speeds [] = { 44100, 22050, 11025 }; + unsigned int i; + + for (i = 0; i < sizeof(suggest_speeds) / sizeof(suggest_speeds[0]); i++) + if (format->speed > suggest_speeds[i]) + { + format->speed = suggest_speeds[i]; + return true; + } + + // the speed is already low + } + + // Can we decrease the number of bits per sample? + if (!fixed_width && format->width > 1) + { + format->width = 1; + return true; + } + + return false; +} + + +void S_Startup (void) { + qboolean fixed_speed, fixed_width, fixed_channels; + snd_format_t chosen_fmt; + static snd_format_t prev_render_format = {0, 0, 0}; + const char* env; + int i; + unsigned int layout_id; + if (!snd_initialized.integer) return; - shm = &sn; - memset((void *)shm, 0, sizeof(*shm)); + fixed_speed = false; + fixed_width = false; + fixed_channels = false; + + // Get the starting sound format from the cvars + chosen_fmt.speed = snd_speed.integer; + chosen_fmt.width = snd_width.integer; + chosen_fmt.channels = snd_channels.integer; - // create a piece of DMA memory - if (fakedma) + // Check the environment variables to see if the player wants a particular sound format + env = getenv("QUAKE_SOUND_CHANNELS"); + if (env != NULL) { - shm->format.width = 2; - shm->format.speed = 22050; - shm->format.channels = 2; - shm->sampleframes = 16384; - shm->samples = shm->sampleframes * shm->format.channels; - shm->samplepos = 0; - shm->buffer = (unsigned char *)Mem_Alloc(snd_mempool, shm->samples * shm->format.width); + chosen_fmt.channels = atoi (env); + fixed_channels = true; } - else + env = getenv("QUAKE_SOUND_SPEED"); + if (env != NULL) + { + chosen_fmt.speed = atoi (env); + fixed_speed = true; + } + env = getenv("QUAKE_SOUND_SAMPLEBITS"); + if (env != NULL) + { + chosen_fmt.width = atoi (env) / 8; + fixed_width = true; + } + + // Parse the command line to see if the player wants a particular sound format +// COMMANDLINEOPTION: Sound: -sndquad sets sound output to 4 channel surround + if (COM_CheckParm ("-sndquad") != 0) + { + chosen_fmt.channels = 4; + fixed_channels = true; + } +// COMMANDLINEOPTION: Sound: -sndstereo sets sound output to stereo + else if (COM_CheckParm ("-sndstereo") != 0) + { + chosen_fmt.channels = 2; + fixed_channels = true; + } +// COMMANDLINEOPTION: Sound: -sndmono sets sound output to mono + else if (COM_CheckParm ("-sndmono") != 0) + { + chosen_fmt.channels = 1; + fixed_channels = true; + } +// COMMANDLINEOPTION: Sound: -sndspeed 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 chooses 8 bit or 16 bit sound output + i = COM_CheckParm ("-sndbits"); + if (0 < i && i < com_argc - 1) { - if (!SNDDMA_Init()) + chosen_fmt.width = atoi (com_argv[i + 1]) / 8; + fixed_width = true; + } + + // You can't change sound speed after start time (not yet supported) + if (prev_render_format.speed != 0) + { + fixed_speed = true; + if (chosen_fmt.speed != prev_render_format.speed) { - Con_Print("S_Startup: SNDDMA_Init failed.\n"); - shm = NULL; + Con_Printf("S_Startup: sound speed has changed! This is NOT supported yet. Falling back to previous speed (%u Hz)\n", + prev_render_format.speed); + chosen_fmt.speed = prev_render_format.speed; + } + } + + // Sanity checks + if (chosen_fmt.speed < SND_MIN_SPEED) + { + chosen_fmt.speed = SND_MIN_SPEED; + fixed_speed = false; + } + else if (chosen_fmt.speed > SND_MAX_SPEED) + { + chosen_fmt.speed = SND_MAX_SPEED; + fixed_speed = false; + } + + if (chosen_fmt.width < SND_MIN_WIDTH) + { + chosen_fmt.width = SND_MIN_WIDTH; + fixed_width = false; + } + else if (chosen_fmt.width > SND_MAX_WIDTH) + { + chosen_fmt.width = SND_MAX_WIDTH; + fixed_width = false; + } + + if (chosen_fmt.channels < SND_MIN_CHANNELS) + { + chosen_fmt.channels = SND_MIN_CHANNELS; + fixed_channels = false; + } + else if (chosen_fmt.channels > SND_MAX_CHANNELS) + { + chosen_fmt.channels = SND_MAX_CHANNELS; + fixed_channels = false; + } + + // create the sound buffer used for sumitting the samples to the plaform-dependent module + if (!simsound) + { + snd_format_t suggest_fmt; + qboolean accepted; + + accepted = false; + do + { + Con_DPrintf("S_Startup: initializing sound output format: %dHz, %d bit, %d channels...\n", + chosen_fmt.speed, chosen_fmt.width * 8, + chosen_fmt.channels); + + memset(&suggest_fmt, 0, sizeof(suggest_fmt)); + accepted = SndSys_Init(&chosen_fmt, &suggest_fmt); + + if (!accepted) + { + Con_DPrintf("S_Startup: sound output initialization FAILED\n"); + + // If the module is suggesting another one + if (suggest_fmt.speed != 0) + { + memcpy(&chosen_fmt, &suggest_fmt, sizeof(chosen_fmt)); + Con_Printf (" Driver has suggested %dHz, %d bit, %d channels. Retrying...\n", + suggest_fmt.speed, suggest_fmt.width * 8, + suggest_fmt.channels); + } + // Else, try to find a less resource-demanding format + else if (!S_ChooseCheaperFormat (&chosen_fmt, fixed_speed, fixed_width, fixed_channels)) + break; + } + } while (!accepted); + + // If we haven't found a suitable format + if (!accepted) + { + Con_Print("S_Startup: SndSys_Init failed.\n"); sound_spatialized = false; return; } } + else + { + snd_renderbuffer = Snd_CreateRingBuffer(&chosen_fmt, 0, NULL); + Con_Print ("S_Startup: simulating sound output\n"); + } + + memcpy(&prev_render_format, &snd_renderbuffer->format, sizeof(prev_render_format)); + Con_Printf("Sound format: %dHz, %d channels, %d bits per sample\n", + chosen_fmt.speed, chosen_fmt.channels, chosen_fmt.width * 8); + + // Update the cvars + snd_speed.integer = chosen_fmt.speed; + snd_width.integer = chosen_fmt.width; + snd_channels.integer = chosen_fmt.channels; + + snd_starttime = realtime; + + // If the sound module has already run, add an extra time to make sure + // the sound time doesn't decrease, to not confuse playing SFXs + if (oldpaintedtime != 0) + { + // The extra time must be a multiple of the render buffer size + // to avoid modifying the current position in the buffer, + // some modules write directly to a shared (DMA) buffer + extrasoundtime = oldpaintedtime + snd_renderbuffer->maxframes - 1; + extrasoundtime -= extrasoundtime % snd_renderbuffer->maxframes; + Con_DPrintf("S_Startup: extra sound time = %u\n", extrasoundtime); + + soundtime = extrasoundtime; + } + else + extrasoundtime = 0; + snd_renderbuffer->startframe = soundtime; + snd_renderbuffer->endframe = soundtime; - Con_Printf("Sound format: %dHz, %d bit, %d channels\n", shm->format.speed, - shm->format.width * 8, shm->format.channels); + // select speaker layout + for (layout_id = 0; layout_id < SND_SPEAKERLAYOUTS; layout_id++) + if (snd_speakerlayouts[layout_id].channels == snd_renderbuffer->format.channels) + break; + if (layout_id >= SND_SPEAKERLAYOUTS) + { + Con_Printf("S_Startup: invalid number of channels (%hu). Can't find the corresponding speaker layout.\n" + "Defaulting to mono output\n", + snd_renderbuffer->format.channels); + layout_id = SND_SPEAKERLAYOUTS - 1; + } + snd_speakerlayout = snd_speakerlayouts[layout_id]; } void S_Shutdown(void) { - if (!shm) + if (snd_renderbuffer == NULL) return; - if (fakedma) - Mem_Free(shm->buffer); + oldpaintedtime = snd_renderbuffer->endframe; + + if (simsound) + { + Mem_Free(snd_renderbuffer->ring); + Mem_Free(snd_renderbuffer); + snd_renderbuffer = NULL; + } else - SNDDMA_Shutdown(); + SndSys_Shutdown(); - shm = NULL; sound_spatialized = false; } @@ -195,6 +593,10 @@ void S_Init(void) Cvar_RegisterVariable(&bgmvolume); Cvar_RegisterVariable(&snd_staticvolume); + Cvar_RegisterVariable(&snd_speed); + Cvar_RegisterVariable(&snd_width); + Cvar_RegisterVariable(&snd_channels); + // COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio) if (COM_CheckParm("-nosound") || COM_CheckParm("-safe")) return; @@ -203,13 +605,13 @@ void S_Init(void) // COMMANDLINEOPTION: Sound: -simsound runs sound mixing but with no output if (COM_CheckParm("-simsound")) - fakedma = true; + simsound = true; - Cmd_AddCommand("play", S_Play, "play a sound at your current location (not heard by anyone else)"); - Cmd_AddCommand("play2", S_Play2, "play a sound globally throughout the level (not heard by anyone else)"); - Cmd_AddCommand("playvol", S_PlayVol, "play a sound at the specified volume level at your current location (not heard by anyone else)"); + Cmd_AddCommand("play", S_Play_f, "play a sound at your current location (not heard by anyone else)"); + Cmd_AddCommand("play2", S_Play2_f, "play a sound globally throughout the level (not heard by anyone else)"); + Cmd_AddCommand("playvol", S_PlayVol_f, "play a sound at the specified volume level at your current location (not heard by anyone else)"); Cmd_AddCommand("stopsound", S_StopAllSounds, "silence"); - Cmd_AddCommand("soundlist", S_SoundList, "list loaded sounds"); + Cmd_AddCommand("soundlist", S_SoundList_f, "list loaded sounds"); Cmd_AddCommand("soundinfo", S_SoundInfo_f, "print sound system information (such as channels and speed)"); Cmd_AddCommand("snd_restart", S_Restart_f, "restart sound system"); @@ -217,7 +619,6 @@ void S_Init(void) Cvar_RegisterVariable(&snd_precache); Cvar_RegisterVariable(&snd_initialized); Cvar_RegisterVariable(&snd_streaming); - //Cvar_RegisterVariable(&bgmbuffer); Cvar_RegisterVariable(&ambient_level); Cvar_RegisterVariable(&ambient_fade); Cvar_RegisterVariable(&snd_noextraupdate); @@ -257,14 +658,9 @@ void S_Terminate (void) } -// ======================================================================= -// Load a sound -// ======================================================================= - /* ================== S_FindName - ================== */ sfx_t *S_FindName (const char *name) @@ -300,7 +696,6 @@ sfx_t *S_FindName (const char *name) /* ================== S_FreeSfx - ================== */ void S_FreeSfx (sfx_t *sfx, qboolean force) @@ -348,7 +743,6 @@ void S_FreeSfx (sfx_t *sfx, qboolean force) /* ================== S_ServerSounds - ================== */ void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds) @@ -406,7 +800,6 @@ void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds) /* ================== S_PrecacheSound - ================== */ sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean lock) @@ -467,7 +860,27 @@ void S_UnlockSfx (sfx_t *sfx) } -//============================================================================= +/* +================== +S_BlockSound +================== +*/ +void S_BlockSound (void) +{ + snd_blocked++; +} + + +/* +================== +S_UnblockSound +================== +*/ +void S_UnblockSound (void) +{ + snd_blocked--; +} + /* ================= @@ -480,12 +893,12 @@ channel_t *SND_PickChannel(int entnum, int entchannel) { int ch_idx; int first_to_die; - int life_left; + int first_life_left, life_left; channel_t* ch; // Check for replacement sound, or find the best one to replace first_to_die = -1; - life_left = 0x7fffffff; + first_life_left = 0x7fffffff; for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) { ch = &channels[ch_idx]; @@ -520,9 +933,10 @@ channel_t *SND_PickChannel(int entnum, int entchannel) continue; } - if (ch->end - paintedtime < life_left) + life_left = (int)(ch->end - snd_renderbuffer->endframe); + if (life_left < first_life_left) { - life_left = ch->end - paintedtime; + first_life_left = life_left; first_to_die = ch_idx; } } @@ -606,8 +1020,8 @@ void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, VectorCopy (origin, target_chan->origin); target_chan->master_vol = (int)(fvol * 255); target_chan->sfx = sfx; - target_chan->end = paintedtime + sfx->total_length; - target_chan->lastptime = paintedtime; + target_chan->end = snd_renderbuffer->endframe + sfx->total_length; + target_chan->lastptime = snd_renderbuffer->endframe; target_chan->flags = flags; // If it's a static sound @@ -631,7 +1045,7 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f int ch_idx; int skip; - if (!shm || !sfx || nosound.integer) + if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer) return -1; if (!sfx->fetcher) { @@ -662,7 +1076,7 @@ int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float f continue; if (check->sfx == sfx && !check->pos) { - skip = (int)(0.1 * sfx->format.speed); + skip = (int)(0.1 * snd_renderbuffer->format.speed); if (skip > (int)sfx->total_length) skip = (int)sfx->total_length; if (skip > 0) @@ -737,9 +1151,9 @@ void S_StopSound(int entnum, int entchannel) void S_StopAllSounds (void) { unsigned int i; - unsigned char *pbuf; - if (!shm) + // TOCHECK: is this test necessary? + if (snd_renderbuffer == NULL) return; for (i = 0; i < total_channels; i++) @@ -748,22 +1162,18 @@ void S_StopAllSounds (void) total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); - // Clear sound buffer - pbuf = (unsigned char *)S_LockBuffer(); - if (pbuf != NULL) + // Mute the contents of the submittion buffer + if (simsound || SndSys_LockRenderBuffer ()) { - int setsize = shm->samples * shm->format.width; - int clear = (shm->format.width == 1) ? 0x80 : 0; - - // FIXME: is it (still) true? (check with OSS and ALSA) - // on i586/i686 optimized versions of glibc, glibc *wrongly* IMO, - // reads the memory area before writing to it causing a seg fault - // since the memory is PROT_WRITE only and not PROT_READ|PROT_WRITE - //memset(shm->buffer, clear, shm->samples * shm->format.width); - while (setsize--) - *pbuf++ = clear; - - S_UnlockBuffer (); + int clear; + size_t memsize; + + clear = (snd_renderbuffer->format.width == 1) ? 0x80 : 0; + memsize = snd_renderbuffer->maxframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels; + memset(snd_renderbuffer->ring, clear, memsize); + + if (!simsound) + SndSys_UnlockRenderBuffer (); } } @@ -783,7 +1193,7 @@ void S_PauseGameSounds (qboolean toggle) void S_SetChannelVolume (unsigned int ch_ind, float fvol) { - channels[ch_ind].master_vol = (int)(fvol * 255); + channels[ch_ind].master_vol = (int)(fvol * 255.0f); } @@ -796,7 +1206,7 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation) { channel_t *target_chan; - if (!shm || !sfx || nosound.integer) + if (snd_renderbuffer == NULL || sfx == NULL || nosound.integer) return; if (!sfx->fetcher) { @@ -817,12 +1227,9 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation) } -//============================================================================= - /* =================== S_UpdateAmbientSounds - =================== */ void S_UpdateAmbientSounds (void) @@ -868,79 +1275,45 @@ void S_UpdateAmbientSounds (void) } } -#define SND_SPEAKERLAYOUTS 5 -static speakerlayout_t snd_speakerlayouts[SND_SPEAKERLAYOUTS] = +static void S_PaintAndSubmit (void) { - { - "surround71", 8, - { - {45, 0.2, 0.2, 0.5}, // front left - {315, 0.2, 0.2, 0.5}, // front right - {135, 0.2, 0.2, 0.5}, // rear left - {225, 0.2, 0.2, 0.5}, // rear right - {0, 0.2, 0.2, 0.5}, // front center - {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so... no lfe) - {90, 0.2, 0.2, 0.5}, // side left - {180, 0.2, 0.2, 0.5}, // side right - } - }, - { - "surround51", 6, - { - {45, 0.2, 0.2, 0.5}, // front left - {315, 0.2, 0.2, 0.5}, // front right - {135, 0.2, 0.2, 0.5}, // rear left - {225, 0.2, 0.2, 0.5}, // rear right - {0, 0.2, 0.2, 0.5}, // front center - {0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so... no lfe) - {0, 0, 0, 0}, - {0, 0, 0, 0}, - } - }, - { - // these systems sometimes have a subwoofer as well, but it has no - // channel of its own - "surround40", 4, - { - {45, 0.3, 0.3, 0.8}, // front left - {315, 0.3, 0.3, 0.8}, // front right - {135, 0.3, 0.3, 0.8}, // rear left - {225, 0.3, 0.3, 0.8}, // rear right - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - } - }, - { - // these systems sometimes have a subwoofer as well, but it has no - // channel of its own - "stereo", 2, - { - {90, 0.5, 0.5, 1}, // side left - {270, 0.5, 0.5, 1}, // side right - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - } - }, - { - "mono", 1, - { - {0, 0, 1, 1}, // center - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - } - } -}; + unsigned int newsoundtime, paintedtime, endtime, maxtime, usedframes; + + if (snd_renderbuffer == NULL || snd_blocked > 0) + return; + + // Update sound time + if (cls.capturevideo_soundfile) // SUPER NASTY HACK to record non-realtime sound + newsoundtime = (unsigned int)((double)cls.capturevideo_frame * (double)snd_renderbuffer->format.speed / (double)cls.capturevideo_framerate); + else if (simsound) + newsoundtime = (unsigned int)((realtime - snd_starttime) * (double)snd_renderbuffer->format.speed); + else + newsoundtime = SndSys_GetSoundTime(); + + newsoundtime += extrasoundtime; + if (newsoundtime < soundtime) + Con_Printf("S_PaintAndSubmit: WARNING: newsoundtime < soundtime (%u < %u)\n", + newsoundtime, soundtime); + soundtime = newsoundtime; + + // Check to make sure that we haven't overshot + paintedtime = snd_renderbuffer->endframe; + if (paintedtime < soundtime) + paintedtime = soundtime; + + // mix ahead of current position + endtime = soundtime + (unsigned int)(_snd_mixahead.value * (float)snd_renderbuffer->format.speed); + usedframes = snd_renderbuffer->endframe - snd_renderbuffer->startframe; + maxtime = paintedtime + snd_renderbuffer->maxframes - usedframes; + endtime = min(endtime, maxtime); + + S_PaintChannels(snd_renderbuffer, paintedtime, endtime); + + if (simsound) + snd_renderbuffer->startframe = snd_renderbuffer->endframe; + else + SndSys_Submit(); +} /* ============ @@ -955,18 +1328,12 @@ void S_Update(const matrix4x4_t *listenermatrix) channel_t *ch, *combine; matrix4x4_t basematrix, rotatematrix; - if (!snd_initialized.integer || (snd_blocked > 0) || !shm) + if (!snd_initialized.integer || snd_blocked > 0 || snd_renderbuffer == NULL) return; Matrix4x4_Invert_Simple(&basematrix, listenermatrix); Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin); - // select speaker layout - for (i = 0;i < SND_SPEAKERLAYOUTS - 1;i++) - if (snd_speakerlayouts[i].channels == shm->format.channels) - break; - snd_speakerlayout = snd_speakerlayouts[i]; - // calculate the current matrices for (j = 0;j < SND_LISTENERS;j++) { @@ -1056,39 +1423,7 @@ void S_Update(const matrix4x4_t *listenermatrix) Con_Printf("----(%u)----\n", total); } - S_Update_(); -} - -void GetSoundtime(void) -{ - int samplepos; - static int buffers; - static int oldsamplepos; - int fullsamples; - - fullsamples = shm->sampleframes; - - // it is possible to miscount buffers if it has wrapped twice between - // calls to S_Update. Oh well. - if (cls.capturevideo_soundfile) // SUPER NASTY HACK to record non-realtime sound - samplepos = (int)((((unsigned int)((double)cls.capturevideo_frame * (double)shm->format.speed / (double)cls.capturevideo_framerate)) * (unsigned int)shm->format.channels) % (unsigned int)shm->samples); - else - samplepos = SNDDMA_GetDMAPos(); - - if (samplepos < oldsamplepos) - { - buffers++; // buffer wrapped - - if (paintedtime > 0x40000000) - { // time to chop things off to avoid 32 bit limits - buffers = 0; - paintedtime = fullsamples; - S_StopAllSounds (); - } - } - oldsamplepos = samplepos; - - soundtime = buffers * fullsamples + samplepos / shm->format.channels; + S_PaintAndSubmit(); } void S_ExtraUpdate (void) @@ -1096,121 +1431,9 @@ void S_ExtraUpdate (void) if (snd_noextraupdate.integer || !sound_spatialized) return; - S_Update_(); -} - -void S_Update_(void) -{ - unsigned endtime; - - if (!shm || (snd_blocked > 0)) - return; - - // Updates DMA time - GetSoundtime(); - - // check to make sure that we haven't overshot - if (paintedtime < soundtime) - paintedtime = soundtime; - - // mix ahead of current position - endtime = soundtime + (unsigned int)(_snd_mixahead.value * shm->format.speed); - endtime = min(endtime, (unsigned int)(soundtime + shm->sampleframes)); - - S_PaintChannels (endtime); - - SNDDMA_Submit (); -} - -/* -=============================================================================== - -console functions - -=============================================================================== -*/ - -static void S_Play_Common(float fvol, float attenuation) -{ - int i, ch_ind; - char name[MAX_QPATH]; - sfx_t *sfx; - - i = 1; - while (inext, 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; diff --git a/snd_main.h b/snd_main.h index ec097d22..47b622d0 100644 --- a/snd_main.h +++ b/snd_main.h @@ -24,20 +24,32 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sound.h" -typedef struct sfxbuffer_s -{ - unsigned int length; - unsigned int offset; - unsigned char data[4]; // variable sized -} sfxbuffer_t; - typedef struct snd_format_s { unsigned int speed; - unsigned int width; - unsigned int channels; + unsigned short width; + unsigned short channels; } snd_format_t; +typedef struct snd_buffer_s +{ + snd_format_t format; + unsigned int nbframes; // current size, in sample frames + unsigned int maxframes; // max size (buffer size), in sample frames + unsigned char samples[4]; // variable sized +} snd_buffer_t; + +typedef struct snd_ringbuffer_s +{ + snd_format_t format; + unsigned char* ring; + unsigned int maxframes; // max size (buffer size), in sample frames + unsigned int startframe; // index of the first frame in the buffer + // if startframe == endframe, the bufffer is empty + unsigned int endframe; // index of the first EMPTY frame in the "ring" buffer + // may be smaller than startframe if the "ring" buffer has wrapped +} snd_ringbuffer_t; + // sfx_t flags #define SFXFLAG_NONE 0 #define SFXFLAG_FILEMISSING (1 << 0) // wasn't able to load the associated sound file @@ -51,43 +63,32 @@ struct sfx_s char name[MAX_QPATH]; sfx_t *next; size_t memsize; // total memory used (including sfx_t and fetcher data) - int locks; // One lock is automatically granted while the sfx is + + // One lock is automatically granted while the sfx is // playing (and removed when stopped). Locks can also be - // added by S_PrecacheSound and S_ServerSounds. + int locks; // added by S_PrecacheSound and S_ServerSounds. // A SFX with no lock and no SFXFLAG_PERMANENTLOCK is // freed at level change by S_ServerSounds. + unsigned int flags; // cf SFXFLAG_* defines - snd_format_t format; - int loopstart; - unsigned int total_length; + int loopstart; // in sample frames. -1 if not looped + unsigned int total_length; // in sample frames const snd_fetcher_t *fetcher; void *fetcher_data; // Per-sfx data for the sound fetching functions }; -typedef struct dma_s -{ - snd_format_t format; - int sampleframes; // frames in buffer (frame = samples for all speakers) - int samples; // mono samples in buffer - int samplepos; // in mono samples - unsigned char *buffer; - int bufferlength; // used only by certain drivers -} dma_t; - // maximum supported speakers constant #define SND_LISTENERS 8 typedef struct channel_s { - int pad[8]; + short listener_volume [SND_LISTENERS]; // 0-255 volume per speaker + int master_vol; // 0-255 master volume sfx_t *sfx; // sfx number - int pad2[8]; unsigned int flags; // cf CHANNELFLAG_* defines - int master_vol; // 0-255 master volume - short listener_volume[SND_LISTENERS]; // 0-255 volume per speaker - int end; // end time in global paintsamples - int lastptime; // last time this channel was painted - int pos; // sample position in sfx + unsigned int end; // end time in global paintsamples + unsigned int lastptime; // last time this channel was painted + unsigned int pos; // sample position in sfx int entnum; // to allow overriding a specific sound int entchannel; vec3_t origin; // origin of sound effect @@ -95,61 +96,87 @@ typedef struct channel_s void *fetcher_data; // Per-channel data for the sound fetching function } channel_t; -typedef const sfxbuffer_t* (*snd_fetcher_getsb_t) (channel_t* ch, unsigned int start, unsigned int nbsamples); +// Sound fetching functions +// "start" is both an input and output parameter: it returns the actual start time of the sound buffer +typedef const snd_buffer_t* (*snd_fetcher_getsb_t) (channel_t* ch, unsigned int *start, unsigned int nbsampleframes); typedef void (*snd_fetcher_endsb_t) (channel_t* ch); typedef void (*snd_fetcher_free_t) (sfx_t* sfx); +typedef const snd_format_t* (*snd_fetcher_getfmt_t) (sfx_t* sfx); struct snd_fetcher_s { snd_fetcher_getsb_t getsb; snd_fetcher_endsb_t endsb; snd_fetcher_free_t free; + snd_fetcher_getfmt_t getfmt; }; -void S_PaintChannels(int endtime); +// 0 to NUM_AMBIENTS - 1 = water, etc +// NUM_AMBIENTS to NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS - 1 = normal entity sounds +// NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS to total_channels = static sounds +#define MAX_DYNAMIC_CHANNELS 128 +#define MAX_CHANNELS 516 -// initializes cycling through a DMA buffer and returns information on it -qboolean SNDDMA_Init(void); +extern unsigned int total_channels; +extern channel_t channels[MAX_CHANNELS]; -// gets the current DMA position -int SNDDMA_GetDMAPos(void); +extern snd_ringbuffer_t *snd_renderbuffer; +extern unsigned int soundtime; // WARNING: sound modules must NOT use it -void SNDDMA_Submit(void); +extern cvar_t _snd_mixahead; +extern cvar_t snd_swapstereo; +extern cvar_t snd_streaming; + +extern int snd_blocked; // counter. When > 0, we stop submitting sound to the audio device + +extern mempool_t *snd_mempool; -// shutdown the DMA xfer. -void SNDDMA_Shutdown(void); +// If simsound is true, the sound card is not initialized and no sound is submitted to it. +// More generally, all arch-dependent operations are skipped or emulated. +// Used for isolating performance in the renderer. +extern qboolean simsound; -qboolean S_LoadSound (sfx_t *s, qboolean complain); + +// ==================================================================== +// Architecture-independent functions +// ==================================================================== + +void S_PaintChannels (snd_ringbuffer_t* rb, unsigned int starttime, unsigned int endtime); + +qboolean S_LoadSound (sfx_t *sfx, qboolean complain); void S_LockSfx (sfx_t *sfx); void S_UnlockSfx (sfx_t *sfx); -void *S_LockBuffer(void); -void S_UnlockBuffer(void); +snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed); +qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format); -extern size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_format_t* in_format, unsigned char *out_data, const char* sfxname); +// If "buffer" is NULL, the function allocates one buffer of "sampleframes" sample frames itself +// (if "sampleframes" is 0, the function chooses the size). +snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer); -// ==================================================================== -// 0 to NUM_AMBIENTS - 1 = water, etc -// NUM_AMBIENTS to NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS - 1 = normal entity sounds -// NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS to total_channels = static sounds -#define MAX_CHANNELS 516 -#define MAX_DYNAMIC_CHANNELS 128 +// ==================================================================== +// Architecture-dependent functions +// ==================================================================== -extern channel_t channels[MAX_CHANNELS]; +// Create "snd_renderbuffer" with the proper sound format if the call is successful +// May return a suggested format if the requested format isn't available +qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested); -extern unsigned int total_channels; +// Stop the sound card, delete "snd_renderbuffer" and free its other resources +void SndSys_Shutdown (void); -extern int paintedtime; -extern int soundtime; -extern volatile dma_t *shm; +// Submit the contents of "snd_renderbuffer" to the sound card +void SndSys_Submit (void); -extern cvar_t snd_swapstereo; -extern cvar_t snd_streaming; +// Returns the number of sample frames consumed since the sound started +unsigned int SndSys_GetSoundTime (void); -extern int snd_blocked; +// Get the exclusive lock on "snd_renderbuffer" +qboolean SndSys_LockRenderBuffer (void); -extern mempool_t *snd_mempool; +// Release the exclusive lock on "snd_renderbuffer" +void SndSys_UnlockRenderBuffer (void); #endif diff --git a/snd_mem.c b/snd_mem.c index 37781bfa..777dd6c6 100644 --- a/snd_mem.c +++ b/snd_mem.c @@ -27,32 +27,126 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /* -================ -ResampleSfx -================ +==================== +Snd_CreateRingBuffer + +If "buffer" is NULL, the function allocates one buffer of "sampleframes" sample frames itself +(if "sampleframes" is 0, the function chooses the size). +==================== +*/ +snd_ringbuffer_t *Snd_CreateRingBuffer (const snd_format_t* format, unsigned int sampleframes, void* buffer) +{ + snd_ringbuffer_t *ringbuffer; + + // If the caller provides a buffer, it must give us its size + if (sampleframes == 0 && buffer != NULL) + return NULL; + + ringbuffer = (snd_ringbuffer_t*)Mem_Alloc(snd_mempool, sizeof (*ringbuffer)); + memset(ringbuffer, 0, sizeof(*ringbuffer)); + memcpy(&ringbuffer->format, format, sizeof(ringbuffer->format)); + + // If we haven't been given a buffer + if (buffer == NULL) + { + unsigned int maxframes; + size_t memsize; + + if (sampleframes == 0) + maxframes = (format->speed + 1) / 2; // Make the sound buffer large enough for containing 0.5 sec of sound + else + maxframes = sampleframes; + + memsize = maxframes * format->width * format->channels; + ringbuffer->ring = Mem_Alloc(snd_mempool, memsize); + ringbuffer->maxframes = maxframes; + } + else + { + ringbuffer->ring = buffer; + ringbuffer->maxframes = sampleframes; + } + + return ringbuffer; +} + + +/* +==================== +Snd_CreateSndBuffer +==================== */ -size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_format_t* in_format, unsigned char *out_data, const char* sfxname) +snd_buffer_t *Snd_CreateSndBuffer (const unsigned char *samples, unsigned int sampleframes, const snd_format_t* in_format, unsigned int sb_speed) +{ + size_t newsampleframes, memsize; + snd_buffer_t* sb; + + newsampleframes = (double)sampleframes * (double)sb_speed / (double)in_format->speed; + + memsize = newsampleframes * in_format->channels * in_format->width; + memsize += sizeof (*sb) - sizeof (sb->samples); + + sb = (snd_buffer_t*)Mem_Alloc (snd_mempool, memsize); + sb->format.channels = in_format->channels; + sb->format.width = in_format->width; + sb->format.speed = sb_speed; + sb->maxframes = newsampleframes; + sb->nbframes = 0; + + if (!Snd_AppendToSndBuffer (sb, samples, sampleframes, in_format)) + { + Mem_Free (sb); + return NULL; + } + + return sb; +} + + +/* +==================== +Snd_AppendToSndBuffer +==================== +*/ +qboolean Snd_AppendToSndBuffer (snd_buffer_t* sb, const unsigned char *samples, unsigned int sampleframes, const snd_format_t* format) { size_t srclength, outcount; + unsigned char *out_data; - srclength = in_length * in_format->channels; - outcount = (int)((double)in_length * shm->format.speed / in_format->speed); + //Con_DPrintf("ResampleSfx: %d samples @ %dHz -> %d samples @ %dHz\n", + // sampleframes, format->speed, outcount, sb->format.speed); - //Con_DPrintf("ResampleSfx(%s): %d samples @ %dHz -> %d samples @ %dHz\n", - // sfxname, in_length, in_format->speed, outcount, shm->format.speed); + // If the formats are incompatible + if (sb->format.channels != format->channels || sb->format.width != format->width) + { + Con_Print("AppendToSndBuffer: incompatible sound formats!\n"); + return false; + } + + outcount = (double)sampleframes * (double)sb->format.speed / (double)format->speed; + + // If the sound buffer is too short + if (outcount > sb->maxframes - sb->nbframes) + { + Con_Print("AppendToSndBuffer: sound buffer too short!\n"); + return false; + } + + out_data = &sb->samples[sb->nbframes * sb->format.width * sb->format.channels]; + srclength = sampleframes * format->channels; // Trivial case (direct transfer) - if (in_format->speed == shm->format.speed) + if (format->speed == sb->format.speed) { - if (in_format->width == 1) + if (format->width == 1) { size_t i; for (i = 0; i < srclength; i++) - ((signed char*)out_data)[i] = in_data[i] - 128; + ((signed char*)out_data)[i] = samples[i] - 128; } - else // if (in_format->width == 2) - memcpy (out_data, in_data, srclength * in_format->width); + else // if (format->width == 2) + memcpy (out_data, samples, srclength * format->width); } // General case (linear interpolation with a fixed-point fractional @@ -63,17 +157,17 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo # define INTEGER_BITS (sizeof(samplefrac)*8 - FRACTIONAL_BITS) else { - const unsigned int fracstep = (unsigned int)((double)in_format->speed / shm->format.speed * (1 << FRACTIONAL_BITS)); + const unsigned int fracstep = (unsigned int)((double)format->speed / sb->format.speed * (1 << FRACTIONAL_BITS)); size_t remain_in = srclength, total_out = 0; unsigned int samplefrac; - const unsigned char *in_ptr = in_data; + const unsigned char *in_ptr = samples; unsigned char *out_ptr = out_data; // Check that we can handle one second of that sound - if (in_format->speed * in_format->channels > (1 << INTEGER_BITS)) + if (format->speed * format->channels > (1 << INTEGER_BITS)) { Con_Printf ("ResampleSfx: sound quality too high for resampling (%uHz, %u channel(s))\n", - in_format->speed, in_format->channels); + format->speed, format->channels); return 0; } @@ -88,36 +182,36 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo samplefrac = 0; // If more than 1 sec of sound remains to be converted - if (outcount - total_out > shm->format.speed) + if (outcount - total_out > sb->format.speed) { - tmpcount = shm->format.speed; + tmpcount = sb->format.speed; interpolation_limit = tmpcount; // all samples can be interpolated } else { tmpcount = outcount - total_out; - interpolation_limit = (int)ceil((double)(((remain_in / in_format->channels) - 1) << FRACTIONAL_BITS) / fracstep); + interpolation_limit = (int)ceil((double)(((remain_in / format->channels) - 1) << FRACTIONAL_BITS) / fracstep); if (interpolation_limit > tmpcount) interpolation_limit = tmpcount; } // 16 bit samples - if (in_format->width == 2) + if (format->width == 2) { const short* in_ptr_short; // Interpolated part for (i = 0; i < interpolation_limit; i++) { - srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels; + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; in_ptr_short = &((const short*)in_ptr)[srcsample]; - for (j = 0; j < in_format->channels; j++) + for (j = 0; j < format->channels; j++) { int a, b; a = *in_ptr_short; - b = *(in_ptr_short + in_format->channels); + b = *(in_ptr_short + format->channels); *((short*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a; in_ptr_short++; @@ -130,10 +224,10 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo // Non-interpolated part for (/* nothing */; i < tmpcount; i++) { - srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels; + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; in_ptr_short = &((const short*)in_ptr)[srcsample]; - for (j = 0; j < in_format->channels; j++) + for (j = 0; j < format->channels; j++) { *((short*)out_ptr) = *in_ptr_short; @@ -145,22 +239,22 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo } } // 8 bit samples - else // if (in_format->width == 1) + else // if (format->width == 1) { const unsigned char* in_ptr_byte; // Convert up to 1 sec of sound for (i = 0; i < interpolation_limit; i++) { - srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels; + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample]; - for (j = 0; j < in_format->channels; j++) + for (j = 0; j < format->channels; j++) { int a, b; a = *in_ptr_byte - 128; - b = *(in_ptr_byte + in_format->channels) - 128; + b = *(in_ptr_byte + format->channels) - 128; *((signed char*)out_ptr) = (((b - a) * (samplefrac & FRACTIONAL_MASK)) >> FRACTIONAL_BITS) + a; in_ptr_byte++; @@ -173,10 +267,10 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo // Non-interpolated part for (/* nothing */; i < tmpcount; i++) { - srcsample = (samplefrac >> FRACTIONAL_BITS) * in_format->channels; + srcsample = (samplefrac >> FRACTIONAL_BITS) * format->channels; in_ptr_byte = &((const unsigned char*)in_ptr)[srcsample]; - for (j = 0; j < in_format->channels; j++) + for (j = 0; j < format->channels; j++) { *((signed char*)out_ptr) = *in_ptr_byte - 128; @@ -189,15 +283,17 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo } // Update the counters and the buffer position - remain_in -= in_format->speed * in_format->channels; - in_ptr += in_format->speed * in_format->channels * in_format->width; + remain_in -= format->speed * format->channels; + in_ptr += format->speed * format->channels * format->width; total_out += tmpcount; } } - return outcount; + sb->nbframes += outcount; + return true; } + //============================================================================= /* @@ -205,62 +301,59 @@ size_t ResampleSfx (const unsigned char *in_data, size_t in_length, const snd_fo S_LoadSound ============== */ -qboolean S_LoadSound (sfx_t *s, qboolean complain) +qboolean S_LoadSound (sfx_t *sfx, qboolean complain) { char namebuffer[MAX_QPATH + 16]; size_t len; - if (!shm || !shm->format.speed) - return false; + // See if already loaded + if (sfx->fetcher != NULL) + return true; // If we weren't able to load it previously, no need to retry - if (s->flags & SFXFLAG_FILEMISSING) + if (sfx->flags & SFXFLAG_FILEMISSING) return false; - // See if in memory - if (s->fetcher != NULL) - { - if (s->format.speed != shm->format.speed) - Con_Printf ("S_LoadSound: sound %s hasn't been resampled (%uHz instead of %uHz)\n", s->name); - return true; - } + // No sound? + if (snd_renderbuffer == NULL) + return false; // LordHavoc: if the sound filename does not begin with sound/, try adding it - if (strncasecmp(s->name, "sound/", 6)) + if (strncasecmp(sfx->name, "sound/", 6)) { - len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", s->name); + len = dpsnprintf (namebuffer, sizeof(namebuffer), "sound/%s", sfx->name); if (len < 0) { // name too long - Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name); + Con_Printf("S_LoadSound: name \"%s\" is too long\n", sfx->name); return false; } - if (S_LoadWavFile (namebuffer, s)) + if (S_LoadWavFile (namebuffer, sfx)) return true; if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) strcpy (namebuffer + len - 3, "ogg"); - if (OGG_LoadVorbisFile (namebuffer, s)) + if (OGG_LoadVorbisFile (namebuffer, sfx)) return true; } // LordHavoc: then try without the added sound/ as wav and ogg - len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", s->name); + len = dpsnprintf (namebuffer, sizeof(namebuffer), "%s", sfx->name); if (len < 0) { // name too long - Con_Printf("S_LoadSound: name \"%s\" is too long\n", s->name); + Con_Printf("S_LoadSound: name \"%s\" is too long\n", sfx->name); return false; } - if (S_LoadWavFile (namebuffer, s)) + if (S_LoadWavFile (namebuffer, sfx)) return true; if (len >= 4 && !strcasecmp (namebuffer + len - 4, ".wav")) strcpy (namebuffer + len - 3, "ogg"); - if (OGG_LoadVorbisFile (namebuffer, s)) + if (OGG_LoadVorbisFile (namebuffer, sfx)) return true; // Can't load the sound! - s->flags |= SFXFLAG_FILEMISSING; + sfx->flags |= SFXFLAG_FILEMISSING; if (complain) - Con_Printf("S_LoadSound: Couldn't load \"%s\"\n", s->name); + Con_Printf("S_LoadSound: Couldn't load \"%s\"\n", sfx->name); return false; } diff --git a/snd_mix.c b/snd_mix.c index 03ba042a..0e4dada0 100644 --- a/snd_mix.c +++ b/snd_mix.c @@ -17,11 +17,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// snd_mix.c -- portable code to mix sounds #include "quakedef.h" #include "snd_main.h" + typedef struct portable_samplepair_s { int sample[SND_LISTENERS]; @@ -31,285 +31,299 @@ typedef struct portable_samplepair_s #define PAINTBUFFER_SIZE 2048 portable_sampleframe_t paintbuffer[PAINTBUFFER_SIZE]; + // FIXME: this desyncs with the video too easily extern void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate); -void S_CaptureAVISound(portable_sampleframe_t *buf, size_t length) +static void S_CaptureAVISound(size_t length) { - int n; size_t i; unsigned char out[PAINTBUFFER_SIZE * 4]; + unsigned char* out_ptr; + if (!cls.capturevideo_active) return; + // write the sound buffer as little endian 16bit interleaved stereo - for(i = 0;i < length;i++) + for(i = 0, out_ptr = out; i < length; i++, out_ptr += 4) { - n = buf[i].sample[0]; - n = bound(-32768, n, 32767); - out[i*4+0] = n & 0xFF; - out[i*4+1] = (n >> 8) & 0xFF; - n = buf[i].sample[1]; - n = bound(-32768, n, 32767); - out[i*4+2] = n & 0xFF; - out[i*4+3] = (n >> 8) & 0xFF; + int n0, n1; + + n0 = paintbuffer[i].sample[0]; + n0 = bound(-32768, n0, 32767); + out_ptr[0] = (unsigned char)n0; + out_ptr[1] = (unsigned char)(n0 >> 8); + + n1 = paintbuffer[i].sample[1]; + n1 = bound(-32768, n1, 32767); + out_ptr[2] = (unsigned char)n1; + out_ptr[3] = (unsigned char)(n1 >> 8); } - SCR_CaptureVideo_SoundFrame(out, length, shm->format.speed); + SCR_CaptureVideo_SoundFrame(out, length, snd_renderbuffer->format.speed); } -// TODO: rewrite this function -void S_TransferPaintBuffer(int endtime) +static unsigned int S_TransferPaintBuffer(snd_ringbuffer_t* rb, unsigned int starttime, unsigned int endtime) { - void *pbuf; - int i; - portable_sampleframe_t *snd_p; - int lpaintedtime; - int snd_frames; - int val; - if ((pbuf = S_LockBuffer())) + unsigned int partialend; + + // Lock submitbuffer + if (!simsound && !SndSys_LockRenderBuffer()) + return 0; + + partialend = starttime; + while (partialend < endtime) // handle recirculating buffer issues { - snd_p = paintbuffer; - lpaintedtime = paintedtime; - for (lpaintedtime = paintedtime;lpaintedtime < endtime;lpaintedtime += snd_frames) + unsigned int startoffset, maxframes, nbframes, i; + void *rb_ptr; + portable_sampleframe_t *painted_ptr; + int val; + + startoffset = partialend % rb->maxframes; + maxframes = rb->maxframes - startoffset; + nbframes = endtime - partialend; + if (nbframes > maxframes) + nbframes = maxframes; + + rb_ptr = &rb->ring[startoffset * rb->format.width * rb->format.channels]; + painted_ptr = &paintbuffer[partialend - starttime]; + + if (rb->format.width == 2) // 16bit { - // handle recirculating buffer issues - i = lpaintedtime & (shm->sampleframes - 1); - snd_frames = shm->sampleframes - i; - if (snd_frames > endtime - lpaintedtime) - snd_frames = endtime - lpaintedtime; - if (shm->format.width == 2) + short *snd_out = (short*)rb_ptr; + if (rb->format.channels == 8) // 7.1 surround { - // 16bit - short *snd_out = (short *) pbuf + i * shm->format.channels; - if (shm->format.channels == 8) - { - // 7.1 surround - if (snd_swapstereo.value) - { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - *snd_out++ = bound(-32768, snd_p->sample[3], 32767); - *snd_out++ = bound(-32768, snd_p->sample[2], 32767); - *snd_out++ = bound(-32768, snd_p->sample[4], 32767); - *snd_out++ = bound(-32768, snd_p->sample[5], 32767); - *snd_out++ = bound(-32768, snd_p->sample[6], 32767); - *snd_out++ = bound(-32768, snd_p->sample[7], 32767); - } + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) + { + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[6], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[7], 32767); } - else + } + else + { + for (i = 0;i < nbframes;i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - *snd_out++ = bound(-32768, snd_p->sample[2], 32767); - *snd_out++ = bound(-32768, snd_p->sample[3], 32767); - *snd_out++ = bound(-32768, snd_p->sample[4], 32767); - *snd_out++ = bound(-32768, snd_p->sample[5], 32767); - *snd_out++ = bound(-32768, snd_p->sample[6], 32767); - *snd_out++ = bound(-32768, snd_p->sample[7], 32767); - } - } - } - else if (shm->format.channels == 6) - { - // 5.1 surround - if (snd_swapstereo.value) - { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - *snd_out++ = bound(-32768, snd_p->sample[3], 32767); - *snd_out++ = bound(-32768, snd_p->sample[2], 32767); - *snd_out++ = bound(-32768, snd_p->sample[4], 32767); - *snd_out++ = bound(-32768, snd_p->sample[5], 32767); - } + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[6], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[7], 32767); } - else + } + } + else if (rb->format.channels == 6) // 5.1 surround + { + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - *snd_out++ = bound(-32768, snd_p->sample[2], 32767); - *snd_out++ = bound(-32768, snd_p->sample[3], 32767); - *snd_out++ = bound(-32768, snd_p->sample[4], 32767); - *snd_out++ = bound(-32768, snd_p->sample[5], 32767); - } + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767); } } - else if (shm->format.channels == 4) + else { - // 4.0 surround - if (snd_swapstereo.value) + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - *snd_out++ = bound(-32768, snd_p->sample[3], 32767); - *snd_out++ = bound(-32768, snd_p->sample[2], 32767); - } + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[4], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[5], 32767); } - else + } + } + else if (rb->format.channels == 4) // 4.0 surround + { + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - *snd_out++ = bound(-32768, snd_p->sample[2], 32767); - *snd_out++ = bound(-32768, snd_p->sample[3], 32767); - } + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767); } } - else if (shm->format.channels == 2) + else { - // 2.0 stereo - if (snd_swapstereo.value) + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - } + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[2], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[3], 32767); } - else + } + } + else if (rb->format.channels == 2) // 2.0 stereo + { + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - *snd_out++ = bound(-32768, snd_p->sample[0], 32767); - *snd_out++ = bound(-32768, snd_p->sample[1], 32767); - } + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); } } - else if (shm->format.channels == 1) + else { - // 1.0 mono - for (i = 0;i < snd_frames;i++, snd_p++) - *snd_out++ = bound(-32768, (snd_p->sample[0] + snd_p->sample[1]) >> 1, 32767); + for (i = 0; i < nbframes; i++, painted_ptr++) + { + *snd_out++ = bound(-32768, painted_ptr->sample[0], 32767); + *snd_out++ = bound(-32768, painted_ptr->sample[1], 32767); + } } } - else + else if (rb->format.channels == 1) // 1.0 mono { - // 8bit - unsigned char *snd_out = (unsigned char *) pbuf + i * shm->format.channels; - if (shm->format.channels == 8) - { - // 7.1 surround - if (snd_swapstereo.value) - { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } + for (i = 0; i < nbframes; i++, painted_ptr++) + { + val = (painted_ptr->sample[0] + painted_ptr->sample[1]) >> 1; + *snd_out++ = bound(-32768, val, 32767); + } + } + } + else // 8bit + { + unsigned char *snd_out = (unsigned char*)rb_ptr; + if (rb->format.channels == 8) // 7.1 surround + { + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) + { + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[6] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[7] >> 8) + 128; *snd_out++ = bound(0, val, 255); } - else + } + else + { + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } - } - } - else if (shm->format.channels == 6) - { - // 5.1 surround - if (snd_swapstereo.value) - { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[6] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[7] >> 8) + 128; *snd_out++ = bound(0, val, 255); } - else + } + } + else if (rb->format.channels == 6) // 5.1 surround + { + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255); } } - else if (shm->format.channels == 4) + else { - // 4.0 surround - if (snd_swapstereo.value) + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[4] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[5] >> 8) + 128; *snd_out++ = bound(0, val, 255); } - else + } + } + else if (rb->format.channels == 4) // 4.0 surround + { + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255); } } - else if (shm->format.channels == 2) + else { - // 2.0 stereo - if (snd_swapstereo.value) + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[2] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[3] >> 8) + 128; *snd_out++ = bound(0, val, 255); } - else + } + } + else if (rb->format.channels == 2) // 2.0 stereo + { + if (snd_swapstereo.value) + { + for (i = 0; i < nbframes; i++, painted_ptr++) { - for (i = 0;i < snd_frames;i++, snd_p++) - { - val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255); - val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255); - } + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); } } - else if (shm->format.channels == 1) + else { - // 1.0 mono - for (i = 0;i < snd_frames;i++, snd_p++) + for (i = 0; i < nbframes; i++, painted_ptr++) { - val = ((snd_p->sample[0]+snd_p->sample[1]) >> 9) + 128;*snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[0] >> 8) + 128; *snd_out++ = bound(0, val, 255); + val = (painted_ptr->sample[1] >> 8) + 128; *snd_out++ = bound(0, val, 255); } } } + else if (rb->format.channels == 1) // 1.0 mono + { + for (i = 0;i < nbframes;i++, painted_ptr++) + { + val = ((painted_ptr->sample[0] + painted_ptr->sample[1]) >> 9) + 128; + *snd_out++ = bound(0, val, 255); + } + } } - S_UnlockBuffer(); + + partialend += nbframes; } + + rb->endframe = endtime; + + // Remove outdated samples from the ring buffer, if any + if (rb->startframe < soundtime) + rb->startframe = soundtime; + + if (!simsound) + SndSys_UnlockRenderBuffer(); + + return endtime - starttime; } @@ -321,32 +335,287 @@ CHANNEL MIXING =============================================================================== */ -qboolean SND_PaintChannel (channel_t *ch, int endtime); +static qboolean SND_PaintChannel (channel_t *ch, unsigned int count) +{ + int snd_vol, vol[SND_LISTENERS]; + const snd_buffer_t *sb; + unsigned int i, sb_offset; + + // If this channel manages its own volume + if (ch->flags & CHANNELFLAG_FULLVOLUME) + snd_vol = 256; + else + snd_vol = (int)(volume.value * 256); + + for (i = 0;i < SND_LISTENERS;i++) + vol[i] = ch->listener_volume[i] * snd_vol; + + sb_offset = ch->pos; + sb = ch->sfx->fetcher->getsb (ch, &sb_offset, count); + if (sb == NULL) + { + Con_DPrintf("SND_PaintChannel: ERROR: can't get sound buffer from sfx \"%s\"\n", + ch->sfx->name, count); + } + else + { +#if SND_LISTENERS != 8 +# error the following code only supports up to 8 channels, update it +#endif + if (sb->format.width == 1) + { + const signed char *samples = (signed char*)sb->samples + (ch->pos - sb_offset) * sb->format.channels; + + // Stereo sound support + if (sb->format.channels == 2) + { + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 8; + paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9; + paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9; + paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 8; + paintbuffer[i].sample[7] += (samples[1] * vol[7]) >> 8; + samples += 2; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 8; + paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 9; + paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 9; + samples += 2; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 8; + samples += 2; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 8; + samples += 2; + } + } + } + else if (sb->format.channels == 1) + { + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 8; + paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 8; + paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 8; + paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 8; + paintbuffer[i].sample[7] += (samples[0] * vol[7]) >> 8; + samples += 1; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 8; + paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 8; + paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 8; + samples += 1; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 8; + paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 8; + samples += 1; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 8; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 8; + samples += 1; + } + } + } + else + return false; // unsupported number of channels in sound + } + else if (sb->format.width == 2) + { + const signed short *samples = (signed short*)sb->samples + (ch->pos - sb_offset) * sb->format.channels; -void S_PaintChannels(int endtime) + // Stereo sound support + if (sb->format.channels == 2) + { + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 16; + paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17; + paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17; + paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 16; + paintbuffer[i].sample[7] += (samples[1] * vol[7]) >> 16; + samples += 2; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 16; + paintbuffer[i].sample[4] += ((samples[0] + samples[1]) * vol[4]) >> 17; + paintbuffer[i].sample[5] += ((samples[0] + samples[1]) * vol[5]) >> 17; + samples += 2; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (samples[1] * vol[3]) >> 16; + samples += 2; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[1] * vol[1]) >> 16; + samples += 2; + } + } + } + else if (sb->format.channels == 1) + { + if (vol[6] + vol[7] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 16; + paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 16; + paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 16; + paintbuffer[i].sample[6] += (samples[0] * vol[6]) >> 16; + paintbuffer[i].sample[7] += (samples[0] * vol[7]) >> 16; + samples += 1; + } + } + else if (vol[4] + vol[5] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 16; + paintbuffer[i].sample[4] += (samples[0] * vol[4]) >> 16; + paintbuffer[i].sample[5] += (samples[0] * vol[5]) >> 16; + samples += 1; + } + } + else if (vol[2] + vol[3] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16; + paintbuffer[i].sample[2] += (samples[0] * vol[2]) >> 16; + paintbuffer[i].sample[3] += (samples[0] * vol[3]) >> 16; + samples += 1; + } + } + else if (vol[0] + vol[1] > 0) + { + for (i = 0;i < count;i++) + { + paintbuffer[i].sample[0] += (samples[0] * vol[0]) >> 16; + paintbuffer[i].sample[1] += (samples[0] * vol[1]) >> 16; + samples += 1; + } + } + } + else + return false; // unsupported number of channels in sound + } + } + + ch->pos += count; + return true; +} + +void S_PaintChannels (snd_ringbuffer_t* rb, unsigned int starttime, unsigned int endtime) { - unsigned int i, j; - int end; - channel_t *ch; - sfx_t *sfx; - int ltime, count; + unsigned int paintedtime; + paintedtime = starttime; while (paintedtime < endtime) { - // if paintbuffer is smaller than DMA buffer - end = endtime; - if (endtime - paintedtime > PAINTBUFFER_SIZE) - end = paintedtime + PAINTBUFFER_SIZE; + unsigned int partialend, i, framecount; + channel_t *ch; + + // if paintbuffer is too small + if (endtime > paintedtime + PAINTBUFFER_SIZE) + partialend = paintedtime + PAINTBUFFER_SIZE; + else + partialend = endtime; // clear the paint buffer - memset (&paintbuffer, 0, (end - paintedtime) * sizeof (paintbuffer[0])); + memset (paintbuffer, 0, (partialend - paintedtime) * sizeof (paintbuffer[0])); // paint in the channels. ch = channels; - for (i=0; isfx; - if (!sfx) + if (sfx == NULL) continue; for (j = 0;j < SND_LISTENERS;j++) if (ch->listener_volume[j]) @@ -359,7 +628,7 @@ void S_PaintChannels(int endtime) // if the channel is paused if (ch->flags & CHANNELFLAG_PAUSED) { - int pausedtime = end - paintedtime; + int pausedtime = partialend - paintedtime; ch->lastptime += pausedtime; ch->end += pausedtime; continue; @@ -375,12 +644,15 @@ void S_PaintChannels(int endtime) { int loopstart; - if (ch->flags & CHANNELFLAG_FORCELOOP) - loopstart = 0; - else - loopstart = -1; if (sfx->loopstart >= 0) - loopstart = sfx->loopstart; + loopstart = bound(0, sfx->loopstart, (int)sfx->total_length - 1); + else + { + if (ch->flags & CHANNELFLAG_FORCELOOP) + loopstart = 0; + else + loopstart = -1; + } // If the sound is looped if (loopstart >= 0) @@ -392,23 +664,23 @@ void S_PaintChannels(int endtime) } ltime = paintedtime; - while (ltime < end) + while (ltime < partialend) { + int count; qboolean stop_paint; // paint up to end - if (ch->end < end) - count = (int)ch->end - ltime; + if (ch->end < partialend) + count = ch->end - ltime; else - count = end - ltime; + count = partialend - ltime; if (count > 0) { - for (j = 0;j < SND_LISTENERS;j++) + for (j = 0; j < SND_LISTENERS; j++) ch->listener_volume[j] = bound(0, ch->listener_volume[j], 255); - stop_paint = !SND_PaintChannel (ch, count); - + stop_paint = !SND_PaintChannel (ch, (unsigned int)count); if (!stop_paint) { ltime += count; @@ -439,256 +711,15 @@ void S_PaintChannels(int endtime) } } - // transfer out according to DMA format - S_CaptureAVISound (paintbuffer, end - paintedtime); - S_TransferPaintBuffer(end); - paintedtime = end; - } -} - + S_CaptureAVISound (partialend - paintedtime); + framecount = S_TransferPaintBuffer (rb, paintedtime, partialend); + paintedtime += framecount; -qboolean SND_PaintChannel (channel_t *ch, int count) -{ - int snd_vol, vol[SND_LISTENERS]; - const sfxbuffer_t *sb; - int i; - - // If this channel manages its own volume - if (ch->flags & CHANNELFLAG_FULLVOLUME) - snd_vol = 256; - else - snd_vol = (int)(volume.value * 256); - - for (i = 0;i < SND_LISTENERS;i++) - vol[i] = ch->listener_volume[i] * snd_vol; - - sb = ch->sfx->fetcher->getsb (ch, ch->pos, count); - if (sb == NULL) - return false; - -#if SND_LISTENERS != 8 -#error this code only supports up to 8 channels, update it -#endif - - if (ch->sfx->format.width == 1) - { - const signed char *sfx = (signed char *)sb->data + (ch->pos - sb->offset) * ch->sfx->format.channels; - // Stereo sound support - if (ch->sfx->format.channels == 2) - { - if (vol[6] + vol[7] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; - paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8; - paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9; - paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9; - paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8; - paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 8; - sfx += 2; - } - } - else if (vol[4] + vol[5] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; - paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8; - paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9; - paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9; - sfx += 2; - } - } - else if (vol[2] + vol[3] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; - paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8; - sfx += 2; - } - } - else if (vol[0] + vol[1] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8; - sfx += 2; - } - } - } - else if (ch->sfx->format.channels == 1) - { - if (vol[6] + vol[7] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; - paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8; - paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8; - paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8; - paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8; - paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 8; - sfx += 1; - } - } - else if (vol[4] + vol[5] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; - paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8; - paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8; - paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8; - sfx += 1; - } - } - else if (vol[2] + vol[3] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8; - paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8; - sfx += 1; - } - } - else if (vol[0] + vol[1] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8; - sfx += 1; - } - } - } - else - return true; // unsupported number of channels in sound - } - else if (ch->sfx->format.width == 2) - { - const signed short *sfx = (signed short *)sb->data + (ch->pos - sb->offset) * ch->sfx->format.channels; - // Stereo sound support - if (ch->sfx->format.channels == 2) - { - if (vol[6] + vol[7] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; - paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16; - paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17; - paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17; - paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16; - paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 16; - sfx += 2; - } - } - else if (vol[4] + vol[5] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; - paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16; - paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17; - paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17; - sfx += 2; - } - } - else if (vol[2] + vol[3] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; - paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16; - sfx += 2; - } - } - else if (vol[0] + vol[1] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16; - sfx += 2; - } - } - } - else if (ch->sfx->format.channels == 1) + // If there was not enough free space in the sound buffer, stop here + if (paintedtime != partialend) { - if (vol[6] + vol[7] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; - paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16; - paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16; - paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16; - paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16; - paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 16; - sfx += 1; - } - } - else if (vol[4] + vol[5] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; - paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16; - paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16; - paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16; - sfx += 1; - } - } - else if (vol[2] + vol[3] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; - paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16; - paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16; - sfx += 1; - } - } - else if (vol[0] + vol[1] > 0) - { - for (i = 0;i < count;i++) - { - paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16; - paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16; - sfx += 1; - } - } + Con_DPrintf(">> S_PaintChannels: Not enough free space in the sound buffer ( %u != %u)\n", paintedtime, partialend); + break; } - else - return true; // unsupported number of channels in sound } - ch->pos += count; - return true; } - diff --git a/snd_null.c b/snd_null.c index 3da10596..43619e92 100755 --- a/snd_null.c +++ b/snd_null.c @@ -25,8 +25,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1", "volume of background music (such as CD music or replacement files such as sound/cdtracks/track002.ogg)"}; cvar_t volume = {CVAR_SAVE, "volume", "0.7", "volume of sound effects"}; cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1", "volume of ambient sound effects (such as swampy sounds at the start of e1m2)"}; -cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1", "how much sound to mix ahead of time"}; -cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0", "swaps left/right speakers for old ISA soundblaster cards"}; cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0", "indicates the sound subsystem is active"}; void S_Init (void) @@ -34,8 +32,6 @@ void S_Init (void) Cvar_RegisterVariable(&bgmvolume); Cvar_RegisterVariable(&volume); Cvar_RegisterVariable(&snd_staticvolume); - Cvar_RegisterVariable(&_snd_mixahead); - Cvar_RegisterVariable(&snd_swapstereo); Cvar_RegisterVariable(&snd_initialized); } @@ -116,3 +112,11 @@ qboolean S_LocalSound (const char *s) { return false; } + +void S_BlockSound (void) +{ +} + +void S_UnblockSound (void) +{ +} diff --git a/snd_ogg.c b/snd_ogg.c index b0073d26..0a6f7fdd 100644 --- a/snd_ogg.c +++ b/snd_ogg.c @@ -382,7 +382,7 @@ static unsigned char resampling_buffer [48000 * 2 * 2]; // Per-sfx data structure typedef struct { - unsigned char *file; + unsigned char *file; size_t filesize; snd_format_t format; } ogg_stream_persfx_t; @@ -392,8 +392,9 @@ typedef struct { OggVorbis_File vf; ov_decode_t ov_decode; + unsigned int sb_offset; int bs; - sfxbuffer_t sb; // must be at the end due to its dynamically allocated size + snd_buffer_t sb; // must be at the end due to its dynamically allocated size } ogg_stream_perchannel_t; @@ -404,33 +405,31 @@ static const ov_callbacks callbacks = {ovcb_read, ovcb_seek, ovcb_close, ovcb_te OGG_FetchSound ==================== */ -static const sfxbuffer_t* OGG_FetchSound (channel_t* ch, unsigned int start, unsigned int nbsamples) +static const snd_buffer_t* OGG_FetchSound (channel_t* ch, unsigned int* start, unsigned int nbsampleframes) { ogg_stream_perchannel_t* per_ch; - sfxbuffer_t* sb; sfx_t* sfx; - snd_format_t* format; ogg_stream_persfx_t* per_sfx; + snd_format_t* ogg_format; + snd_buffer_t* sb; int newlength, done, ret, bigendian; + unsigned int real_start; unsigned int factor; - size_t buff_len; per_ch = (ogg_stream_perchannel_t *)ch->fetcher_data; sfx = ch->sfx; per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data; - format = &sfx->format; - buff_len = STREAM_BUFFER_SIZE(format); + ogg_format = &per_sfx->format; // If there's no fetcher structure attached to the channel yet if (per_ch == NULL) { - size_t memsize; - ogg_stream_persfx_t* per_sfx; + size_t buff_len, memsize; - memsize = sizeof (*per_ch) - sizeof (per_ch->sb.data) + buff_len; + buff_len = STREAM_BUFFER_SIZE(ogg_format); + memsize = sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len; per_ch = (ogg_stream_perchannel_t *)Mem_Alloc (snd_mempool, memsize); sfx->memsize += memsize; - per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data; // Open it with the VorbisFile API per_ch->ov_decode.buffer = per_sfx->file; @@ -442,53 +441,90 @@ static const sfxbuffer_t* OGG_FetchSound (channel_t* ch, unsigned int start, uns Mem_Free (per_ch); return NULL; } - - per_ch->sb.offset = 0; - per_ch->sb.length = 0; per_ch->bs = 0; + per_ch->sb_offset = 0; + per_ch->sb.format.speed = snd_renderbuffer->format.speed; + per_ch->sb.format.width = ogg_format->width; + per_ch->sb.format.channels = ogg_format->channels; + per_ch->sb.nbframes = 0; + per_ch->sb.maxframes = buff_len / (per_ch->sb.format.channels * per_ch->sb.format.width); + ch->fetcher_data = per_ch; } + + real_start = *start; sb = &per_ch->sb; factor = per_sfx->format.width * per_sfx->format.channels; // If the stream buffer can't contain that much samples anyway - if (nbsamples * factor > buff_len) + if (nbsampleframes > sb->maxframes) { - Con_Printf ("OGG_FetchSound: stream buffer too small (%u bytes required)\n", nbsamples * factor); + Con_Printf ("OGG_FetchSound: stream buffer too small (%u sample frames required)\n", nbsampleframes); return NULL; } // If the data we need has already been decompressed in the sfxbuffer, just return it - if (sb->offset <= start && sb->offset + sb->length >= start + nbsamples) + if (per_ch->sb_offset <= real_start && per_ch->sb_offset + sb->nbframes >= real_start + nbsampleframes) + { + *start = per_ch->sb_offset; return sb; + } - newlength = (int)(sb->offset + sb->length) - start; + newlength = (int)(per_ch->sb_offset + sb->nbframes) - real_start; // If we need to skip some data before decompressing the rest, or if the stream has looped - if (newlength < 0 || sb->offset > start) + if (newlength < 0 || per_ch->sb_offset > real_start) { - if (qov_pcm_seek (&per_ch->vf, (ogg_int64_t)start) != 0) + unsigned int time_start; + ogg_int64_t ogg_start; + int err; + + if (real_start > sfx->total_length) + { + Con_Printf ("OGG_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n", + real_start, sfx->total_length); + return NULL; + } + + // We work with 200ms (1/5 sec) steps to avoid rounding errors + time_start = real_start * 5 / snd_renderbuffer->format.speed; + ogg_start = time_start * (per_sfx->format.speed / 5); + err = qov_pcm_seek (&per_ch->vf, ogg_start); + if (err != 0) + { + Con_Printf ("OGG_FetchSound: qov_pcm_seek(..., %d) returned %d\n", + real_start, err); return NULL; - sb->length = 0; + } + sb->nbframes = 0; + + real_start = (float)ogg_start / per_sfx->format.speed * snd_renderbuffer->format.speed; + if (*start - real_start + nbsampleframes > sb->maxframes) + { + Con_Printf ("OGG_FetchSound: stream buffer too small after seek (%u sample frames required)\n", + *start - real_start + nbsampleframes); + per_ch->sb_offset = real_start; + return NULL; + } } - // Else, move forward the samples we need to keep in the sfxbuffer + // Else, move forward the samples we need to keep in the sound buffer else { - memmove (sb->data, sb->data + (start - sb->offset) * factor, newlength * factor); - sb->length = newlength; + memmove (sb->samples, sb->samples + (real_start - per_ch->sb_offset) * factor, newlength * factor); + sb->nbframes = newlength; } - sb->offset = start; + per_ch->sb_offset = real_start; // We add exactly 1 sec of sound to the buffer: // 1- to ensure we won't lose any sample during the resampling process // 2- to force one call to OGG_FetchSound per second to regulate the workload - if ((sfx->format.speed + sb->length) * factor > buff_len) + if (sb->format.speed + sb->nbframes > sb->maxframes) { - Con_Printf ("OGG_FetchSound: stream buffer overflow (%u bytes / %u)\n", - (sfx->format.speed + sb->length) * factor, buff_len); + Con_Printf ("OGG_FetchSound: stream buffer overflow (%u sample frames / %u)\n", + sb->format.speed + sb->nbframes, sb->maxframes); return NULL; } newlength = per_sfx->format.speed * factor; // -> 1 sec of sound before resampling @@ -503,10 +539,9 @@ static const sfxbuffer_t* OGG_FetchSound (channel_t* ch, unsigned int start, uns while ((ret = qov_read (&per_ch->vf, (char *)&resampling_buffer[done], (int)(newlength - done), bigendian, 2, 1, &per_ch->bs)) > 0) done += ret; - // Resample in the sfxbuffer - newlength = (int)ResampleSfx (resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format, sb->data + sb->length * (size_t)factor, sfx->name); - sb->length += newlength; + Snd_AppendToSndBuffer (sb, resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format); + *start = per_ch->sb_offset; return sb; } @@ -524,17 +559,15 @@ static void OGG_FetchEnd (channel_t* ch) if (per_ch != NULL) { size_t buff_len; - snd_format_t* format; // Free the ogg vorbis decoder qov_clear (&per_ch->vf); + buff_len = per_ch->sb.maxframes * per_ch->sb.format.channels * per_ch->sb.format.width; + ch->sfx->memsize -= sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len; + Mem_Free (per_ch); ch->fetcher_data = NULL; - - format = &ch->sfx->format; - buff_len = STREAM_BUFFER_SIZE(format); - ch->sfx->memsize -= sizeof (*per_ch) - sizeof (per_ch->sb.data) + buff_len; } } @@ -560,7 +593,19 @@ static void OGG_FreeSfx (sfx_t* sfx) sfx->fetcher = NULL; } -static const snd_fetcher_t ogg_fetcher = { OGG_FetchSound, OGG_FetchEnd, OGG_FreeSfx }; + +/* +==================== +OGG_GetFormat +==================== +*/ +static const snd_format_t* OGG_GetFormat (sfx_t* sfx) +{ + ogg_stream_persfx_t* per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data; + return &per_sfx->format; +} + +static const snd_fetcher_t ogg_fetcher = { OGG_FetchSound, OGG_FetchEnd, OGG_FreeSfx, OGG_GetFormat }; /* @@ -570,7 +615,7 @@ OGG_LoadVorbisFile Load an Ogg Vorbis file into memory ==================== */ -qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s) +qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx) { unsigned char *data; fs_offset_t filesize; @@ -583,7 +628,7 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s) return false; // Already loaded? - if (s->fetcher != NULL) + if (sfx->fetcher != NULL) return true; // Load the file @@ -609,7 +654,7 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s) if (vi->channels < 1 || vi->channels > 2) { Con_Printf("%s has an unsupported number of channels (%i)\n", - s->name, vi->channels); + sfx->name, vi->channels); qov_clear (&vf); Mem_Free(data); return false; @@ -618,30 +663,27 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s) len = qov_pcm_total (&vf, -1) * vi->channels * 2; // 16 bits => "* 2" // Decide if we go for a stream or a simple PCM cache - buff_len = (int)ceil (STREAM_BUFFER_DURATION * (shm->format.speed * 2 * vi->channels)); + buff_len = (int)ceil (STREAM_BUFFER_DURATION * (snd_renderbuffer->format.speed * 2 * vi->channels)); if (snd_streaming.integer && len > (ogg_int64_t)filesize + 3 * buff_len) { ogg_stream_persfx_t* per_sfx; Con_DPrintf ("\"%s\" will be streamed\n", filename); per_sfx = (ogg_stream_persfx_t *)Mem_Alloc (snd_mempool, sizeof (*per_sfx)); - s->memsize += sizeof (*per_sfx); + sfx->memsize += sizeof (*per_sfx); per_sfx->file = data; per_sfx->filesize = filesize; - s->memsize += filesize; + sfx->memsize += filesize; per_sfx->format.speed = vi->rate; per_sfx->format.width = 2; // We always work with 16 bits samples per_sfx->format.channels = vi->channels; - s->format.speed = shm->format.speed; - s->format.width = per_sfx->format.width; - s->format.channels = per_sfx->format.channels; - - s->fetcher_data = per_sfx; - s->fetcher = &ogg_fetcher; - s->loopstart = -1; - s->flags |= SFXFLAG_STREAMED; - s->total_length = (int)((size_t)len / per_sfx->format.channels / 2 * ((float)s->format.speed / per_sfx->format.speed)); + + sfx->fetcher_data = per_sfx; + sfx->fetcher = &ogg_fetcher; + sfx->loopstart = -1; + sfx->flags |= SFXFLAG_STREAMED; + sfx->total_length = (int)((size_t)len / (per_sfx->format.channels * 2) * ((double)snd_renderbuffer->format.speed / per_sfx->format.speed)); } else { @@ -649,8 +691,8 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s) ogg_int64_t done; int bs, bigendian; long ret; - sfxbuffer_t *sb; - size_t memsize; + snd_buffer_t *sb; + snd_format_t ogg_format; Con_DPrintf ("\"%s\" will be cached\n", filename); @@ -658,34 +700,35 @@ qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s) buff = (char *)Mem_Alloc (snd_mempool, (int)len); done = 0; bs = 0; -#if BYTE_ORDER == LITTLE_ENDIAN - bigendian = 0; -#else +#if BYTE_ORDER == BIG_ENDIAN bigendian = 1; +#else + bigendian = 0; #endif while ((ret = qov_read (&vf, &buff[done], (int)(len - done), bigendian, 2, 1, &bs)) > 0) done += ret; - // Calculate resampled length - // FIXME: is this using the correct rounding direction? ceil may be better - len = (int)((double)done * (double)shm->format.speed / (double)vi->rate); - - // Resample it - memsize = (size_t)len + sizeof (*sb) - sizeof (sb->data); - sb = (sfxbuffer_t *)Mem_Alloc (snd_mempool, memsize); - s->memsize += memsize; - s->fetcher_data = sb; - s->fetcher = &wav_fetcher; - s->format.speed = vi->rate; - s->format.width = 2; // We always work with 16 bits samples - s->format.channels = vi->channels; - s->loopstart = -1; - s->flags &= ~SFXFLAG_STREAMED; - - sb->length = (unsigned int)ResampleSfx ((unsigned char *)buff, (size_t)done / (vi->channels * 2), &s->format, sb->data, s->name); - s->format.speed = shm->format.speed; - s->total_length = sb->length; - sb->offset = 0; + // Build the sound buffer + ogg_format.speed = vi->rate; + ogg_format.channels = vi->channels; + ogg_format.width = 2; // We always work with 16 bits samples + sb = Snd_CreateSndBuffer ((unsigned char *)buff, (size_t)done / (vi->channels * 2), &ogg_format, snd_renderbuffer->format.speed); + if (sb == NULL) + { + qov_clear (&vf); + Mem_Free (data); + Mem_Free (buff); + return false; + } + + sfx->fetcher = &wav_fetcher; + sfx->fetcher_data = sb; + + sfx->total_length = sb->nbframes; + sfx->memsize += sb->maxframes * sb->format.channels * sb->format.width + sizeof (*sb) - sizeof (sb->samples); + + sfx->loopstart = -1; + sfx->flags &= ~SFXFLAG_STREAMED; qov_clear (&vf); Mem_Free (data); diff --git a/snd_ogg.h b/snd_ogg.h index 4d200b79..f8c5fe78 100644 --- a/snd_ogg.h +++ b/snd_ogg.h @@ -27,7 +27,7 @@ qboolean OGG_OpenLibrary (void); void OGG_CloseLibrary (void); -qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *s); +qboolean OGG_LoadVorbisFile (const char *filename, sfx_t *sfx); #endif diff --git a/snd_oss.c b/snd_oss.c index f50c321c..89800691 100644 --- a/snd_oss.c +++ b/snd_oss.c @@ -20,262 +20,291 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // OSS module, used by Linux and FreeBSD -#include + #include -#include -#include #include -#include -#include -#include #include -#include +#include + #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 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 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 } - diff --git a/snd_sdl.c b/snd_sdl.c index cfeeb1b4..6dccba73 100644 --- a/snd_sdl.c +++ b/snd_sdl.c @@ -16,72 +16,74 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "quakedef.h" -#include "snd_main.h" -#include -/* -Info: -SDL samples are really frames (full set of samples for all speakers) -*/ - -#define AUDIO_SDL_SAMPLEFRAMES 4096 -#define AUDIO_LOCALFACTOR 4 +#include +#include -typedef struct AudioState_s -{ - int width; - int size; - int pos; - void *buffer; -} AudioState; +#include "quakedef.h" +#include "snd_main.h" -static AudioState as; +static unsigned int sdlaudiotime = 0; -static void Buffer_Callback(void *userdata, Uint8 *stream, int len); -/* -================== -S_BlockSound -================== -*/ -void S_BlockSound( void ) +// Note: SDL calls SDL_LockAudio() right before this function, so no need to lock the audio data here +static void Buffer_Callback (void *userdata, Uint8 *stream, int len) { - snd_blocked++; + unsigned int factor, RequestedFrames, MaxFrames, FrameCount; + unsigned int StartOffset, EndOffset; + + factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width; + if ((unsigned int)len % factor != 0) + Sys_Error("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len); + + RequestedFrames = (unsigned int)len / factor; + + // Transfert up to a chunk of samples from snd_renderbuffer to stream + MaxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe; + if (MaxFrames > RequestedFrames) + FrameCount = RequestedFrames; + else + FrameCount = MaxFrames; + StartOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes; + EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes; + if (StartOffset > EndOffset) // if the buffer wraps + { + unsigned int PartialLength1, PartialLength2; - if( snd_blocked == 1 ) - SDL_PauseAudio( true ); -} + PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor; + memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1); + + PartialLength2 = FrameCount * factor - PartialLength1; + memcpy(&stream[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2); + } + else + memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor); + snd_renderbuffer->startframe += FrameCount; -/* -================== -S_UnblockSound -================== -*/ -void S_UnblockSound( void ) -{ - snd_blocked--; - if( snd_blocked == 0 ) - SDL_PauseAudio( false ); + if (FrameCount < RequestedFrames) + Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount); + + sdlaudiotime += RequestedFrames; } /* -================== -SNDDMA_Init +==================== +SndSys_Init -Try to find a sound device to mix for. -Returns false if nothing is found. -================== +Create "snd_renderbuffer" with the proper sound format if the call is successful +May return a suggested format if the requested format isn't available +==================== */ - -qboolean SNDDMA_Init(void) +qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) { + unsigned int buffersize; SDL_AudioSpec wantspec; - int i; - int channels; + SDL_AudioSpec obtainspec; + + Con_Print ("SndSys_Init: using the SDL module\n"); // Init the SDL Audio subsystem if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) { @@ -89,138 +91,132 @@ qboolean SNDDMA_Init(void) return false; } - for (channels = 8;channels >= 1;channels--) + buffersize = (unsigned int)ceil((double)requested->speed / 20.0); + + // Init the SDL Audio subsystem + wantspec.callback = Buffer_Callback; + wantspec.userdata = NULL; + wantspec.freq = requested->speed; + wantspec.format = ((requested->width == 1) ? AUDIO_U8 : AUDIO_S16SYS); + wantspec.channels = requested->channels; + wantspec.samples = CeilPowerOf2(buffersize); // needs to be a power of 2 on some platforms. + + Con_DPrintf("Wanted audio Specification:\n" + "\tChannels : %i\n" + "\tFormat : 0x%X\n" + "\tFrequency : %i\n" + "\tSamples : %i\n", + wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples); + + if( SDL_OpenAudio( &wantspec, &obtainspec ) ) + { + Con_Printf( "Failed to open the audio device! (%s)\n", SDL_GetError() ); + return false; + } + + Con_DPrintf("Obtained audio specification:\n" + "\tChannels : %i\n" + "\tFormat : 0x%X\n" + "\tFrequency : %i\n" + "\tSamples : %i\n", + obtainspec.channels, obtainspec.format, obtainspec.freq, obtainspec.samples); + + // If we haven't obtained what we wanted + if (wantspec.freq != obtainspec.freq || + wantspec.format != obtainspec.format || + wantspec.channels != obtainspec.channels) { - if ((channels & 1) && channels != 1) - continue; -// COMMANDLINEOPTION: SDL Sound: -sndmono sets sound output to mono - if ((i=COM_CheckParm("-sndmono")) != 0) - if (channels != 1) - continue; -// COMMANDLINEOPTION: SDL Sound: -sndstereo sets sound output to stereo - if ((i=COM_CheckParm("-sndstereo")) != 0) - if (channels != 2) - continue; -// COMMANDLINEOPTION: SDL Sound: -sndquad sets sound output to 4 channel surround - if ((i=COM_CheckParm("-sndquad")) != 0) - if (channels != 4) - continue; - // Init the SDL Audio subsystem - wantspec.callback = Buffer_Callback; - wantspec.userdata = NULL; - wantspec.freq = 44100; - // COMMANDLINEOPTION: SDL Sound: -sndspeed 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(); } - diff --git a/snd_wav.c b/snd_wav.c index 89bd6c6e..d96c8199 100644 --- a/snd_wav.c +++ b/snd_wav.c @@ -223,9 +223,10 @@ static wavinfo_t GetWavinfo (char *name, unsigned char *wav, int wavlength) WAV_FetchSound ==================== */ -static const sfxbuffer_t* WAV_FetchSound (channel_t* ch, unsigned int start, unsigned int nbsamples) +static const snd_buffer_t* WAV_FetchSound (channel_t* ch, unsigned int *start, unsigned int nbsampleframes) { - return (sfxbuffer_t *)ch->sfx->fetcher_data; + *start = 0; + return (snd_buffer_t *)ch->sfx->fetcher_data; } /* @@ -235,17 +236,28 @@ WAV_FreeSfx */ static void WAV_FreeSfx (sfx_t* sfx) { - sfxbuffer_t* sb = (sfxbuffer_t *)sfx->fetcher_data; + snd_buffer_t* sb = (snd_buffer_t *)sfx->fetcher_data; // Free the sound buffer - sfx->memsize -= (sb->length * sfx->format.channels * sfx->format.width) + sizeof (*sb) - sizeof (sb->data); + sfx->memsize -= (sb->maxframes * sb->format.channels * sb->format.width) + sizeof (*sb) - sizeof (sb->samples); Mem_Free(sb); sfx->fetcher_data = NULL; sfx->fetcher = NULL; } -const snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL, WAV_FreeSfx }; +/* +==================== +WAV_GetFormat +==================== +*/ +static const snd_format_t* WAV_GetFormat (sfx_t* sfx) +{ + snd_buffer_t* sb = (snd_buffer_t *)sfx->fetcher_data; + return &sb->format; +} + +const snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL, WAV_FreeSfx, WAV_GetFormat }; /* @@ -253,17 +265,16 @@ const snd_fetcher_t wav_fetcher = { WAV_FetchSound, NULL, WAV_FreeSfx }; S_LoadWavFile ============== */ -qboolean S_LoadWavFile (const char *filename, sfx_t *s) +qboolean S_LoadWavFile (const char *filename, sfx_t *sfx) { fs_offset_t filesize; unsigned char *data; wavinfo_t info; - int len; - size_t memsize; - sfxbuffer_t* sb; + snd_format_t wav_format; + snd_buffer_t* sb; // Already loaded? - if (s->fetcher != NULL) + if (sfx->fetcher != NULL) return true; // Load the file @@ -280,48 +291,22 @@ qboolean S_LoadWavFile (const char *filename, sfx_t *s) Con_DPrintf ("Loading WAV file \"%s\"\n", filename); - info = GetWavinfo (s->name, data, (int)filesize); - // Stereo sounds are allowed (intended for music) - if (info.channels < 1 || info.channels > 2) + info = GetWavinfo (sfx->name, data, (int)filesize); + if (info.channels < 1 || info.channels > 2) // Stereo sounds are allowed (intended for music) { - Con_Printf("%s has an unsupported number of channels (%i)\n",s->name, info.channels); + Con_Printf("%s has an unsupported number of channels (%i)\n",sfx->name, info.channels); Mem_Free(data); return false; } //if (info.channels == 2) - // Log_Printf("stereosounds.log", "%s\n", s->name); - - // calculate resampled length - len = (int) ((double) info.samples * (double) shm->format.speed / (double) info.rate); - len = len * info.width * info.channels; - - memsize = len + sizeof (*sb) - sizeof (sb->data); - sb = (sfxbuffer_t *)Mem_Alloc (snd_mempool, memsize); - if (sb == NULL) - { - Con_Printf("failed to allocate memory for sound \"%s\"\n", s->name); - Mem_Free(data); - return false; - } - s->memsize += memsize; - - s->fetcher = &wav_fetcher; - s->fetcher_data = sb; - s->format.speed = info.rate; - s->format.width = info.width; - s->format.channels = info.channels; - if (info.loopstart < 0) - s->loopstart = -1; - else - s->loopstart = (int)((double)info.loopstart * (double)shm->format.speed / (double)s->format.speed); - s->flags &= ~SFXFLAG_STREAMED; + // Log_Printf("stereosounds.log", "%s\n", sfx->name); #if BYTE_ORDER != LITTLE_ENDIAN // We must convert the WAV data from little endian // to the machine endianess before resampling it if (info.width == 2) { - int i; + unsigned int len, i; short* ptr; len = info.samples * info.channels; @@ -331,10 +316,26 @@ qboolean S_LoadWavFile (const char *filename, sfx_t *s) } #endif - sb->length = (int)ResampleSfx (data + info.dataofs, info.samples, &s->format, sb->data, s->name); - s->format.speed = shm->format.speed; - s->total_length = sb->length; - sb->offset = 0; + wav_format.speed = info.rate; + wav_format.width = info.width; + wav_format.channels = info.channels; + sb = Snd_CreateSndBuffer (data + info.dataofs, info.samples, &wav_format, snd_renderbuffer->format.speed); + if (sb == NULL) + { + Mem_Free(data); + return false; + } + sfx->fetcher = &wav_fetcher; + sfx->fetcher_data = sb; + + sfx->total_length = sb->nbframes; + sfx->memsize += sb->maxframes * sb->format.channels * sb->format.width + sizeof (*sb) - sizeof (sb->samples); + + if (info.loopstart < 0) + sfx->loopstart = -1; + else + sfx->loopstart = (double)info.loopstart * (double)snd_renderbuffer->format.speed / (double)sb->format.speed; + sfx->flags &= ~SFXFLAG_STREAMED; Mem_Free (data); return true; diff --git a/snd_wav.h b/snd_wav.h index ae471620..89c7ee51 100644 --- a/snd_wav.h +++ b/snd_wav.h @@ -28,7 +28,7 @@ extern const snd_fetcher_t wav_fetcher; -qboolean S_LoadWavFile (const char *filename, sfx_t *s); +qboolean S_LoadWavFile (const char *filename, sfx_t *sfx); #endif diff --git a/snd_win.c b/snd_win.c index 490edd29..89b333d5 100644 --- a/snd_win.c +++ b/snd_win.c @@ -19,14 +19,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include "snd_main.h" + +#ifndef DIRECTSOUND_VERSION +# define DIRECTSOUND_VERSION 0x0500 /* Version 5.0 */ +#endif #include #include #include extern HWND mainwindow; -#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) - HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); // Wave output: 64KB in 64 buffers of 1KB @@ -40,7 +42,6 @@ HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; -static qboolean wavonly; static qboolean dsound_init; static qboolean wav_init; static qboolean primary_format_set; @@ -50,6 +51,8 @@ static int snd_sent, snd_completed; static int prev_painted; static unsigned int paintpot; +static unsigned int dsound_time; + /* * Global variables. Must be visible to window-procedure function @@ -78,111 +81,15 @@ HINSTANCE hInstDS; qboolean SNDDMA_InitWav (void); sndinitstat SNDDMA_InitDirect (void); -/* -================== -S_BlockSound -================== -*/ -void S_BlockSound (void) -{ - // DirectSound takes care of blocking itself - if (wav_init) - { - snd_blocked++; - - if (snd_blocked == 1) - waveOutReset (hWaveOut); - } -} - - -/* -================== -S_UnblockSound -================== -*/ -void S_UnblockSound (void) -{ - // DirectSound takes care of blocking itself - if (wav_init) - snd_blocked--; -} - - -/* -================== -FreeSound -================== -*/ -void FreeSound (void) -{ - int i; - - if (pDSBuf) - { - pDSBuf->lpVtbl->Stop(pDSBuf); - pDSBuf->lpVtbl->Release(pDSBuf); - } - - // only release primary buffer if it's not also the mixing buffer we just released - if (pDSPBuf && (pDSBuf != pDSPBuf)) - { - pDSPBuf->lpVtbl->Release(pDSPBuf); - } - - if (pDS) - { - pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); - pDS->lpVtbl->Release(pDS); - } - - if (hWaveOut) - { - waveOutReset (hWaveOut); - - if (lpWaveHdr) - { - for (i=0 ; i< WAV_BUFFERS ; i++) - waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); - } - - waveOutClose (hWaveOut); - - if (hWaveHdr) - { - GlobalUnlock(hWaveHdr); - GlobalFree(hWaveHdr); - } - - if (hData) - { - GlobalUnlock(hData); - GlobalFree(hData); - } - - } - - pDS = NULL; - pDSBuf = NULL; - pDSPBuf = NULL; - hWaveOut = 0; - hData = 0; - hWaveHdr = 0; - lpData = NULL; - lpWaveHdr = NULL; - dsound_init = false; - wav_init = false; -} - /* ================== -SNDDMA_InitDirect +SndSys_InitDirectSound -Direct-Sound support +DirectSound 5 support ================== */ -sndinitstat SNDDMA_InitDirect (void) +static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested, snd_format_t* suggested) { DSBUFFERDESC dsbuf; DSBCAPS dsbcaps; @@ -191,26 +98,15 @@ sndinitstat SNDDMA_InitDirect (void) WAVEFORMATEX format, pformat; HRESULT hresult; int reps; - int i; - - memset((void *)shm, 0, sizeof(*shm)); - shm->format.channels = 2; - shm->format.width = 2; -// COMMANDLINEOPTION: Windows Sound: -sndspeed chooses 44100 hz, 22100 hz, or 11025 hz sound output rate - i = COM_CheckParm ("-sndspeed"); - if (i && i != (com_argc - 1)) - shm->format.speed = atoi(com_argv[i+1]); - else - shm->format.speed = 44100; memset (&format, 0, sizeof(format)); format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = shm->format.channels; - format.wBitsPerSample = shm->format.width * 8; - format.nSamplesPerSec = shm->format.speed; + format.nChannels = requested->channels; + format.wBitsPerSample = requested->width * 8; + format.nSamplesPerSec = requested->speed; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; - format.cbSize = 0; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; if (!hInstDS) { @@ -231,7 +127,7 @@ sndinitstat SNDDMA_InitDirect (void) } } - while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) + while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) { if (hresult != DSERR_ALLOCATED) { @@ -252,7 +148,7 @@ sndinitstat SNDDMA_InitDirect (void) dscaps.dwSize = sizeof(dscaps); - if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps)) + if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps)) { Con_Print("Couldn't get DS caps\n"); } @@ -260,14 +156,14 @@ sndinitstat SNDDMA_InitDirect (void) if (dscaps.dwFlags & DSCAPS_EMULDRIVER) { Con_Print("No DirectSound driver installed\n"); - FreeSound (); + SndSys_Shutdown (); return SIS_FAILURE; } - if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) + if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) { Con_Print("Set coop level failed\n"); - FreeSound (); + SndSys_Shutdown (); return SIS_FAILURE; } @@ -286,11 +182,11 @@ sndinitstat SNDDMA_InitDirect (void) // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it if (!COM_CheckParm ("-snoforceformat")) { - if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) + if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) { pformat = format; - if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat)) + if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, &pformat)) { Con_Print("Set primary sound buffer format: no\n"); } @@ -306,6 +202,8 @@ sndinitstat SNDDMA_InitDirect (void) // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use if (!primary_format_set || !COM_CheckParm ("-primarysound")) { + HRESULT result; + // create the secondary buffer we'll actually work with memset (&dsbuf, 0, sizeof(dsbuf)); dsbuf.dwSize = sizeof(DSBUFFERDESC); @@ -316,21 +214,22 @@ sndinitstat SNDDMA_InitDirect (void) memset(&dsbcaps, 0, sizeof(dsbcaps)); dsbcaps.dwSize = sizeof(dsbcaps); - if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) + result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL); + if (result != DS_OK || + requested->channels != format.nChannels || + requested->width != format.wBitsPerSample / 8 || + requested->speed != format.nSamplesPerSec) { - Con_Print("DS:CreateSoundBuffer Failed\n"); - FreeSound (); + Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n", + result, format.nChannels, format.wBitsPerSample / 8, format.nSamplesPerSec); + SndSys_Shutdown (); return SIS_FAILURE; } - shm->format.channels = format.nChannels; - shm->format.width = format.wBitsPerSample / 8; - shm->format.speed = format.nSamplesPerSec; - - if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps)) + if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps)) { Con_Print("DS:GetCaps failed\n"); - FreeSound (); + SndSys_Shutdown (); return SIS_FAILURE; } @@ -338,14 +237,14 @@ sndinitstat SNDDMA_InitDirect (void) } else { - if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY)) + if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY)) { Con_Print("Set coop level failed\n"); - FreeSound (); + SndSys_Shutdown (); return SIS_FAILURE; } - if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps)) + if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps)) { Con_Print("DS:GetCaps failed\n"); return SIS_FAILURE; @@ -356,51 +255,45 @@ sndinitstat SNDDMA_InitDirect (void) } // Make sure mixer is active - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - Con_Printf(" %d channel(s)\n" - " %d bits/sample\n" - " %d samples/sec\n", - shm->format.channels, shm->format.width * 8, shm->format.speed); + Con_DPrintf(" %d channel(s)\n" + " %d bits/sample\n" + " %d samples/sec\n", + requested->channels, requested->width * 8, requested->speed); gSndBufSize = dsbcaps.dwBufferBytes; // initialize the buffer reps = 0; - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) + while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); - FreeSound (); + SndSys_Shutdown (); return SIS_FAILURE; } if (++reps > 10000) { Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); - FreeSound (); + SndSys_Shutdown (); return SIS_FAILURE; } } memset(lpData, 0, dwSize); + IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0); - pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0); - - // we don't want anyone to access the buffer directly w/o locking it first. - lpData = NULL; - - pDSBuf->lpVtbl->Stop(pDSBuf); - pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwStartTime, NULL); - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + IDirectSoundBuffer_Stop(pDSBuf); + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - shm->samples = gSndBufSize / shm->format.width; - shm->sampleframes = shm->samples / shm->format.channels; - shm->samplepos = 0; - shm->buffer = (unsigned char *) lpData; + dwStartTime = 0; + dsound_time = 0; + snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData); dsound_init = true; @@ -410,38 +303,25 @@ sndinitstat SNDDMA_InitDirect (void) /* ================== -SNDDM_InitWav +SndSys_InitMmsystem Crappy windows multimedia base ================== */ -qboolean SNDDMA_InitWav (void) +static qboolean SndSys_InitMmsystem (const snd_format_t* requested, snd_format_t* suggested) { WAVEFORMATEX format; int i; HRESULT hr; - snd_sent = 0; - snd_completed = 0; - - memset((void *)shm, 0, sizeof(*shm)); - shm->format.channels = 2; - shm->format.width = 2; -// COMMANDLINEOPTION: Windows Sound: -sndspeed chooses 44100 hz, 22100 hz, or 11025 hz sound output rate - i = COM_CheckParm ("-sndspeed"); - if (i && i != (com_argc - 1)) - shm->format.speed = atoi(com_argv[i+1]); - else - shm->format.speed = 44100; - memset (&format, 0, sizeof(format)); format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = shm->format.channels; - format.wBitsPerSample = shm->format.width * 8; - format.nSamplesPerSec = shm->format.speed; - format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; - format.cbSize = 0; - format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.nChannels = requested->channels; + format.wBitsPerSample = requested->width * 8; + format.nSamplesPerSec = requested->speed; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; // Open a waveform device for output using window callback while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, @@ -475,14 +355,14 @@ qboolean SNDDMA_InitWav (void) if (!hData) { Con_Print("Sound: Out of memory.\n"); - FreeSound (); + SndSys_Shutdown (); return false; } lpData = GlobalLock(hData); if (!lpData) { Con_Print("Sound: Failed to lock.\n"); - FreeSound (); + SndSys_Shutdown (); return false; } memset (lpData, 0, gSndBufSize); @@ -498,7 +378,7 @@ qboolean SNDDMA_InitWav (void) if (hWaveHdr == NULL) { Con_Print("Sound: Failed to Alloc header.\n"); - FreeSound (); + SndSys_Shutdown (); return false; } @@ -507,7 +387,7 @@ qboolean SNDDMA_InitWav (void) if (lpWaveHdr == NULL) { Con_Print("Sound: Failed to lock header.\n"); - FreeSound (); + SndSys_Shutdown (); return false; } @@ -523,40 +403,42 @@ qboolean SNDDMA_InitWav (void) MMSYSERR_NOERROR) { Con_Print("Sound: failed to prepare wave headers\n"); - FreeSound (); + SndSys_Shutdown (); return false; } } - shm->samples = gSndBufSize / shm->format.width; - shm->sampleframes = shm->samples / shm->format.channels; - shm->samplepos = 0; - shm->buffer = (unsigned char *) lpData; + snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData); prev_painted = 0; paintpot = 0; + snd_sent = 0; + snd_completed = 0; + wav_init = true; return true; } + /* -================== -SNDDMA_Init +==================== +SndSys_Init -Try to find a sound device to mix for. -Returns false if nothing is found. -================== +Create "snd_renderbuffer" with the proper sound format if the call is successful +May return a suggested format if the requested format isn't available +==================== */ -qboolean SNDDMA_Init(void) +qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) { + qboolean wavonly; sndinitstat stat; -// COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound - if (COM_CheckParm ("-wavonly")) - wavonly = true; + Con_Print ("SndSys_Init: using the Win32 module\n"); +// COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound + wavonly = (COM_CheckParm ("-wavonly") != 0); dsound_init = false; wav_init = false; @@ -565,7 +447,7 @@ qboolean SNDDMA_Init(void) // Init DirectSound if (!wavonly) { - stat = SNDDMA_InitDirect (); + stat = SndSys_InitDirectSound (requested, suggested); if (stat == SIS_SUCCESS) Con_Print("DirectSound initialized\n"); @@ -579,69 +461,97 @@ qboolean SNDDMA_Init(void) // to have sound) if (!dsound_init && (stat != SIS_NOTAVAIL)) { - if (SNDDMA_InitWav ()) - Con_Print("Wave sound initialized\n"); + if (SndSys_InitMmsystem (requested, suggested)) + Con_Print("Wave sound (MMSYSTEM) initialized\n"); else Con_Print("Wave sound failed to init\n"); } - if (!dsound_init && !wav_init) - return false; - - return true; + return (dsound_init || wav_init); } + /* -============== -SNDDMA_GetDMAPos +==================== +SndSys_Shutdown -return the current sample position (in mono samples read) -inside the recirculating dma buffer, so the mixing code will know -how many sample are required to fill it up. -=============== +Stop the sound card, delete "snd_renderbuffer" and free its other resources +==================== */ -int SNDDMA_GetDMAPos(void) +void SndSys_Shutdown (void) { - DWORD dwTime, s; + if (pDSBuf) + { + IDirectSoundBuffer_Stop(pDSBuf); + IDirectSoundBuffer_Release(pDSBuf); + } - if (dsound_init) + // only release primary buffer if it's not also the mixing buffer we just released + if (pDSPBuf && (pDSBuf != pDSPBuf)) + { + IDirectSoundBuffer_Release(pDSPBuf); + } + + if (pDS) { - pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwTime, NULL); - s = dwTime - dwStartTime; + IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); + IDirectSound_Release(pDS); } - else if (wav_init) + + if (hWaveOut) { - // Find which sound blocks have completed - for (;;) + waveOutReset (hWaveOut); + + if (lpWaveHdr) { - if (snd_completed == snd_sent) - { - Con_DPrint("Sound overrun\n"); - break; - } + unsigned int i; - if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE)) - break; + for (i=0 ; i< WAV_BUFFERS ; i++) + waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); + } - snd_completed++; // this buffer has been played + waveOutClose (hWaveOut); + + if (hWaveHdr) + { + GlobalUnlock(hWaveHdr); + GlobalFree(hWaveHdr); } - s = snd_completed * WAV_BUFFER_SIZE; + if (hData) + { + GlobalUnlock(hData); + GlobalFree(hData); + } } - else - return 0; - return (s >> (shm->format.width - 1)) & (shm->samples - 1); + if (snd_renderbuffer != NULL) + { + Mem_Free(snd_renderbuffer); + snd_renderbuffer = NULL; + } + + pDS = NULL; + pDSBuf = NULL; + pDSPBuf = NULL; + hWaveOut = 0; + hData = 0; + hWaveHdr = 0; + lpData = NULL; + lpWaveHdr = NULL; + dsound_init = false; + wav_init = false; } + /* -============== -SNDDMA_Submit +==================== +SndSys_Submit -Send sound to device if buffer isn't really the dma buffer -=============== +Submit the contents of "snd_renderbuffer" to the sound card +==================== */ -void SNDDMA_Submit(void) +void SndSys_Submit (void) { LPWAVEHDR h; int wResult; @@ -650,10 +560,10 @@ void SNDDMA_Submit(void) if (!wav_init) return; - paintpot += (paintedtime - prev_painted) * shm->format.channels * shm->format.width; + paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width; if (paintpot > WAV_BUFFERS * WAV_BUFFER_SIZE) paintpot = WAV_BUFFERS * WAV_BUFFER_SIZE; - prev_painted = paintedtime; + prev_painted = snd_renderbuffer->endframe; // submit new sound blocks while (paintpot > WAV_BUFFER_SIZE) @@ -671,24 +581,62 @@ void SNDDMA_Submit(void) if (wResult != MMSYSERR_NOERROR) { Con_Print("Failed to write block to device\n"); - FreeSound (); + SndSys_Shutdown (); return; } paintpot -= WAV_BUFFER_SIZE; } + } + /* -============== -SNDDMA_Shutdown +==================== +SndSys_GetSoundTime -Reset the sound device for exiting -=============== +Returns the number of sample frames consumed since the sound started +==================== */ -void SNDDMA_Shutdown(void) +unsigned int SndSys_GetSoundTime (void) { - FreeSound (); + unsigned int s; + + if (dsound_init) + { + DWORD dwTime; + + IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL); + s = (dwTime - dwStartTime) & (gSndBufSize - 1); + dwStartTime = dwTime; + + dsound_time += s / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels); + return dsound_time; + } + + if (wav_init) + { + // Find which sound blocks have completed + for (;;) + { + if (snd_completed == snd_sent) + { + Con_DPrint("Sound overrun\n"); + break; + } + + if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE)) + break; + + snd_completed++; // this buffer has been played + } + + s = snd_completed * WAV_BUFFER_SIZE; + + return s / (snd_renderbuffer->format.width * snd_renderbuffer->format.channels); + } + + return 0; } @@ -697,7 +645,14 @@ static DWORD dsound_dwSize2; static DWORD *dsound_pbuf; static DWORD *dsound_pbuf2; -void *S_LockBuffer(void) +/* +==================== +SndSys_LockRenderBuffer + +Get the exclusive lock on "snd_renderbuffer" +==================== +*/ +qboolean SndSys_LockRenderBuffer (void) { int reps; HRESULT hresult; @@ -706,25 +661,28 @@ void *S_LockBuffer(void) if (pDSBuf) { // if the buffer was lost or stopped, restore it and/or restart it - if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK) + if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK) Con_Print("Couldn't get sound buffer status\n"); if (dwStatus & DSBSTATUS_BUFFERLOST) - pDSBuf->lpVtbl->Restore (pDSBuf); + { + Con_Print("DSound buffer is lost!!\n"); + IDirectSoundBuffer_Restore (pDSBuf); + } if (!(dwStatus & DSBSTATUS_PLAYING)) - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); reps = 0; - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK) + while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n"); S_Shutdown (); S_Startup (); - return NULL; + return false; } if (++reps > 10000) @@ -732,20 +690,28 @@ void *S_LockBuffer(void) Con_Print("S_LockBuffer: DS: couldn't restore buffer\n"); S_Shutdown (); S_Startup (); - return NULL; + return false; } } - return dsound_pbuf; + + if ((void*)dsound_pbuf != snd_renderbuffer->ring) + Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n"); + return true; } - else if (wav_init) - return shm->buffer; - else - return NULL; + + return wav_init; } -void S_UnlockBuffer(void) + +/* +==================== +SndSys_UnlockRenderBuffer + +Release the exclusive lock on "snd_renderbuffer" +==================== +*/ +void SndSys_UnlockRenderBuffer (void) { if (pDSBuf) - pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2); + IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2); } - diff --git a/sound.h b/sound.h index 14d1d054..74f9ffe9 100644 --- a/sound.h +++ b/sound.h @@ -81,5 +81,8 @@ void S_StopChannel (unsigned int channel_ind); qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value); void S_SetChannelVolume (unsigned int ch_ind, float fvol); +void S_BlockSound (void); +void S_UnblockSound (void); + #endif diff --git a/sys_win.c b/sys_win.c index 27041f6d..f0f3045d 100644 --- a/sys_win.c +++ b/sys_win.c @@ -28,8 +28,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "conproc.h" #include "direct.h" -extern void S_BlockSound (void); - cvar_t sys_usetimegettime = {CVAR_SAVE, "sys_usetimegettime", "1", "use windows timeGetTime function (which has issues on some motherboards) for timing rather than QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power)"}; HANDLE hinput, houtput; diff --git a/vid_agl.c b/vid_agl.c index 68238400..8a765011 100644 --- a/vid_agl.c +++ b/vid_agl.c @@ -53,6 +53,8 @@ static float mouse_x, mouse_y; static qboolean vid_isfullscreen = false; static qboolean vid_usingvsync = false; +static qboolean sound_active = true; + static int scr_width, scr_height; static AGLContext context; @@ -325,21 +327,37 @@ static OSStatus MainWindowEventHandler (EventHandlerCallRef nextHandler, EventRe return err; } +static void VID_AppFocusChanged(qboolean windowIsActive) +{ + if (vid_activewindow != windowIsActive) + { + vid_activewindow = windowIsActive; + if (!vid_activewindow) + VID_RestoreSystemGamma(); + } + + if (sound_active != windowIsActive) + { + sound_active = windowIsActive; + if (sound_active) + S_UnblockSound (); + else + S_BlockSound (); + } +} + static void VID_ProcessPendingAsyncEvents (void) { // Collapsed / expanded if (AsyncEvent_Collapsed != vid_hidden) { vid_hidden = !vid_hidden; - vid_activewindow = false; - VID_RestoreSystemGamma(); + VID_AppFocusChanged(!vid_hidden); } // Closed if (AsyncEvent_Quitting) - { Sys_Quit(); - } } static void VID_BuildAGLAttrib(GLint *attrib, qboolean stencil, qboolean fullscreen) @@ -537,6 +555,7 @@ int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate vid_usingmouse = false; vid_hidden = false; vid_activewindow = true; + sound_active = true; GL_Init(); SelectWindow(window); @@ -547,25 +566,27 @@ int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate static void Handle_KeyMod(UInt32 keymod) { - const struct keymod_to_event_s { int keybit; keynum_t event; } keymod_events [] = + const struct keymod_to_event_s { UInt32 keybit; keynum_t event; } keymod_events [] = { - {cmdKey, K_AUX1}, - {shiftKey, K_SHIFT}, - {alphaLock, K_CAPSLOCK}, - {optionKey, K_ALT}, - {controlKey, K_CTRL}, - {kEventKeyModifierNumLockMask, K_NUMLOCK}, - {kEventKeyModifierFnMask, K_AUX2} + { cmdKey, K_AUX1 }, + { shiftKey, K_SHIFT }, + { alphaLock, K_CAPSLOCK }, + { optionKey, K_ALT }, + { controlKey, K_CTRL }, + { kEventKeyModifierNumLockMask, K_NUMLOCK }, + { kEventKeyModifierFnMask, K_AUX2 } }; static UInt32 prev_keymod = 0; unsigned int i; UInt32 modChanges; modChanges = prev_keymod ^ keymod; + if (modChanges == 0) + return; for (i = 0; i < sizeof(keymod_events) / sizeof(keymod_events[0]); i++) { - int keybit = keymod_events[i].keybit; + UInt32 keybit = keymod_events[i].keybit; if ((modChanges & keybit) != 0) Key_Event(keymod_events[i].event, '\0', (keymod & keybit) != 0); @@ -772,11 +793,10 @@ void Sys_SendKeyEvents(void) switch (eventKind) { case kEventAppActivated : - vid_activewindow = true; + VID_AppFocusChanged(true); break; case kEventAppDeactivated: - vid_activewindow = false; - VID_RestoreSystemGamma(); + VID_AppFocusChanged(false); break; case kEventAppQuit: Sys_Quit(); diff --git a/vid_glx.c b/vid_glx.c index aea7e3c1..04d6516a 100644 --- a/vid_glx.c +++ b/vid_glx.c @@ -17,12 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -//#include -//#include -//#include -//#include -//#include -//#include #include #include @@ -574,7 +568,7 @@ void VID_Finish (qboolean allowmousegrab) Con_Print("glXSwapIntervalSGI didn't accept the vid_vsync change, it will take effect on next vid_restart (GLX_SGI_swap_control does not allow turning off vsync)\n"); } -// handle the mouse state when windowed if that's changed + // handle the mouse state when windowed if that's changed vid_usemouse = false; if (allowmousegrab && vid_mouse.integer && !key_consoleactive && (key_dest != key_game || !cls.demoplayback)) vid_usemouse = true; @@ -849,6 +843,20 @@ int VID_InitMode(int fullscreen, int width, int height, int bpp, int refreshrate void Sys_SendKeyEvents(void) { + static qboolean sound_active = true; + + // enable/disable sound on focus gain/loss + if (!vid_activewindow && sound_active) + { + S_BlockSound (); + sound_active = false; + } + else if (vid_activewindow && !sound_active) + { + S_UnblockSound (); + sound_active = true; + } + HandleEvents(); } diff --git a/vid_sdl.c b/vid_sdl.c index 60322025..c3cfda10 100644 --- a/vid_sdl.c +++ b/vid_sdl.c @@ -279,6 +279,7 @@ static keynum_t buttonremap[18] = void Sys_SendKeyEvents( void ) { + static qboolean sound_active = true; SDL_Event event; while( SDL_PollEvent( &event ) ) @@ -308,6 +309,18 @@ void Sys_SendKeyEvents( void ) Key_Event( buttonremap[event.button.button - 1], 0, false ); break; } + + // enable/disable sound on focus gain/loss + if (!vid_activewindow && sound_active) + { + S_BlockSound (); + sound_active = false; + } + else if (vid_activewindow && !sound_active) + { + S_UnblockSound (); + sound_active = true; + } } ///////////////// diff --git a/vid_wgl.c b/vid_wgl.c index c52d0d99..3c5614d6 100644 --- a/vid_wgl.c +++ b/vid_wgl.c @@ -30,8 +30,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include -extern void S_BlockSound (void); -extern void S_UnblockSound (void); extern HINSTANCE global_hInstance; @@ -414,7 +412,7 @@ void AppActivate(BOOL fActive, BOOL minimize) * ****************************************************************************/ { - static BOOL sound_active; + static qboolean sound_active = false; // initially blocked by Sys_InitConsole() vid_activewindow = fActive; vid_hidden = minimize;