From: molivier Date: Wed, 13 Oct 2004 13:42:10 +0000 (+0000) Subject: snd_dma.c -> snd_main.c X-Git-Tag: xonotic-v0.1.0preview~5494 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=5e1173baaa37a1665ccd20d65d516a6662d6d23f;p=xonotic%2Fdarkplaces.git snd_dma.c -> snd_main.c git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@4625 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/darkplaces.dev b/darkplaces.dev index 5d51b189..1c3f6b6a 100644 --- a/darkplaces.dev +++ b/darkplaces.dev @@ -1410,7 +1410,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit139] -FileName=snd_dma.c +FileName=snd_main.c CompileCpp=0 Folder=Source Files Compile=1 diff --git a/darkplaces.dsp b/darkplaces.dsp index 7dd00d4d..d6482d8c 100644 --- a/darkplaces.dsp +++ b/darkplaces.dsp @@ -360,7 +360,7 @@ SOURCE=.\sbar.c # End Source File # Begin Source File -SOURCE=.\snd_dma.c +SOURCE=.\snd_main.c # End Source File # Begin Source File diff --git a/makefile.inc b/makefile.inc index 1da3a0c6..319ae4c1 100644 --- a/makefile.inc +++ b/makefile.inc @@ -18,7 +18,7 @@ CPUOPTIMIZATIONS= ##### Variables that you shouldn't care about ##### # Sound objects -OBJ_COMMONSOUND=snd_dma.o snd_mem.o snd_mix.o snd_ogg.o snd_wav.o +OBJ_COMMONSOUND=snd_main.o snd_mem.o snd_mix.o snd_ogg.o snd_wav.o OBJ_NOSOUND=snd_null.o # CD objects diff --git a/snd_dma.c b/snd_dma.c deleted file mode 100644 index 73c55534..00000000 --- a/snd_dma.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ -// snd_dma.c -- main control for any streaming sound output device - -#include "quakedef.h" - -#include "snd_main.h" -#include "snd_ogg.h" - -// TODO: find a better solution instead of using a define -#if defined( _WIN32 ) && !defined( USE_SDL ) -# define USE_DSOUND -#endif - -#ifdef USE_DSOUND -#include -#include -extern DWORD gSndBufSize; -extern LPDIRECTSOUND pDS; -extern LPDIRECTSOUNDBUFFER pDSBuf; -#endif - - -void S_Play(void); -void S_PlayVol(void); -void S_Play2(void); -void S_SoundList(void); -void S_Update_(); - -void S_ClearBuffer (void); - - -// ======================================================================= -// Internal sound data & structures -// ======================================================================= - -channel_t channels[MAX_CHANNELS]; -unsigned int total_channels; - -int snd_blocked = 0; -cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0"}; -cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1"}; - -// pointer should go away -volatile dma_t *shm = 0; -volatile dma_t sn; - -vec3_t listener_origin; -matrix4x4_t listener_matrix; -vec_t sound_nominal_clip_dist=1000.0; -mempool_t *snd_mempool; - -// sample PAIRS -int soundtime; -int paintedtime; - - -// Linked list of known sfx -sfx_t *known_sfx = NULL; - -int sound_started = 0; - -cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1"}; -cvar_t volume = {CVAR_SAVE, "volume", "0.7"}; -cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1"}; - -cvar_t nosound = {0, "nosound", "0"}; -cvar_t snd_precache = {0, "snd_precache", "1"}; -cvar_t bgmbuffer = {0, "bgmbuffer", "4096"}; -cvar_t ambient_level = {0, "ambient_level", "0.3"}; -cvar_t ambient_fade = {0, "ambient_fade", "100"}; -cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0"}; -cvar_t snd_show = {0, "snd_show", "0"}; -cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1"}; -cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0"}; - - -// ==================================================================== -// User-setable variables -// ==================================================================== - - -// -// Fake dma is a synchronous faking of the DMA progress used for -// isolating performance in the renderer. -// - -qboolean fakedma = false; - - -void S_SoundInfo_f(void) -{ - if (!sound_started || !shm) - { - Con_Print("sound system not started\n"); - return; - } - - Con_Printf("%5d stereo\n", shm->format.channels - 1); - 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("%5u total_channels\n", total_channels); -} - - -void S_Startup(void) -{ - if (!snd_initialized.integer) - return; - - shm = &sn; - memset((void *)shm, 0, sizeof(*shm)); - - // create a piece of DMA memory - if (fakedma) - { - shm->format.width = 2; - shm->format.speed = 22050; - shm->format.channels = 2; - shm->samples = 32768; - shm->samplepos = 0; - shm->buffer = Mem_Alloc(snd_mempool, shm->format.channels * shm->samples * shm->format.width); - } - else - { - if (!SNDDMA_Init()) - { - Con_Print("S_Startup: SNDDMA_Init failed.\n"); - sound_started = 0; - shm = NULL; - return; - } - } - - sound_started = 1; - - Con_DPrintf("Sound sampling rate: %i\n", shm->format.speed); - - S_StopAllSounds (); -} - -void S_Shutdown(void) -{ - if (!sound_started) - return; - - if (fakedma) - Mem_Free(shm->buffer); - else - SNDDMA_Shutdown(); - - shm = NULL; - sound_started = 0; -} - -void S_Restart_f(void) -{ - S_Shutdown(); - S_Startup(); -} - -/* -================ -S_Init -================ -*/ -void S_Init(void) -{ - Con_DPrint("\nSound Initialization\n"); - - Cvar_RegisterVariable(&volume); - Cvar_RegisterVariable(&bgmvolume); - Cvar_RegisterVariable(&snd_staticvolume); - -// COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio) - if (COM_CheckParm("-nosound") || COM_CheckParm("-safe")) - return; - - snd_mempool = Mem_AllocPool("sound", 0, NULL); - -// COMMANDLINEOPTION: Sound: -simsound runs sound mixing but with no output - if (COM_CheckParm("-simsound")) - fakedma = true; - - Cmd_AddCommand("play", S_Play); - Cmd_AddCommand("play2", S_Play2); - Cmd_AddCommand("playvol", S_PlayVol); - Cmd_AddCommand("stopsound", S_StopAllSounds); - Cmd_AddCommand("soundlist", S_SoundList); - Cmd_AddCommand("soundinfo", S_SoundInfo_f); - Cmd_AddCommand("snd_restart", S_Restart_f); - - Cvar_RegisterVariable(&nosound); - 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); - Cvar_RegisterVariable(&snd_show); - Cvar_RegisterVariable(&_snd_mixahead); - Cvar_RegisterVariable(&snd_swapstereo); // for people with backwards sound wiring - - Cvar_SetValueQuick(&snd_initialized, true); - - known_sfx = NULL; - - total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics - memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); - - OGG_OpenLibrary (); -} - - -// ======================================================================= -// Load a sound -// ======================================================================= - -/* -================== -S_FindName - -================== -*/ -sfx_t *S_FindName (const char *name, qboolean stdpath) -{ - sfx_t *sfx; - size_t len; - char namebuffer [MAX_QPATH]; - - if (!snd_initialized.integer) - return NULL; - - // Add the default sound directory to the path - len = snprintf (namebuffer, sizeof (namebuffer), stdpath ? "sound/%s" : "%s", name); - if (len >= sizeof (namebuffer)) - Host_Error ("S_FindName: sound name too long (%s)", name); - - // Look for this sound in the list of known sfx - for (sfx = known_sfx; sfx != NULL; sfx = sfx->next) - if(!strcmp (sfx->name, namebuffer)) - return sfx; - - // Add a sfx_t struct for this sound - sfx = Mem_Alloc (snd_mempool, sizeof (*sfx)); - memset (sfx, 0, sizeof(*sfx)); - strlcpy (sfx->name, namebuffer, sizeof (sfx->name)); - sfx->next = known_sfx; - known_sfx = sfx; - - return sfx; -} - - -/* -================== -S_FreeSfx - -================== -*/ -void S_FreeSfx (sfx_t *sfx) -{ - // Never free a locked sfx - if (sfx->locks > 0) - return; - - Con_DPrintf ("S_FreeSfx: freeing %s\n", sfx->name); - - // Remove it from the list of known sfx - if (sfx == known_sfx) - known_sfx = known_sfx->next; - else - { - sfx_t *prev_sfx; - - for (prev_sfx = known_sfx; prev_sfx != NULL; prev_sfx = prev_sfx->next) - if (prev_sfx->next == sfx) - { - prev_sfx->next = sfx->next; - break; - } - if (prev_sfx == NULL) - Sys_Error ("S_FreeSfx: Can't find SFX %s in the list!\n", sfx->name); - } - - // Free it - Mem_FreePool (&sfx->mempool); - Mem_Free (sfx); -} - - -/* -================== -S_ServerSounds - -================== -*/ -void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds) -{ - sfx_t *sfx; - unsigned int i; - - // Start the ambient sounds and make them loop - channels[AMBIENT_WATER].sfx = S_PrecacheSound ("ambience/water1.wav", false, true, true); - channels[AMBIENT_SKY].sfx = S_PrecacheSound ("ambience/wind2.wav", false, true, true); - for (i = 0; i < NUM_AMBIENTS; i++) - channels[i].flags |= CHANNELFLAG_FORCELOOP; - - // Remove 1 lock from all sfx with the SFXFLAG_SERVERSOUND flag, and remove the flag - for (sfx = known_sfx; sfx != NULL; sfx = sfx->next) - if (sfx->flags & SFXFLAG_SERVERSOUND) - { - sfx->locks--; - sfx->flags &= ~SFXFLAG_SERVERSOUND; - } - - // Add 1 lock and the SFXFLAG_SERVERSOUND flag to each sfx in "serversound" - for (i = 1; i < numsounds; i++) - { - sfx = S_FindName (serversound[i], true); - if (sfx != NULL) - { - sfx->locks++; - sfx->flags |= SFXFLAG_SERVERSOUND; - } - } - - // Free all unlocked sfx - sfx = known_sfx; - while (sfx != NULL) - { - sfx_t* crtsfx; - - // We may lose the "next" pointer after S_FreeSfx - crtsfx = sfx; - sfx = sfx->next; - - S_FreeSfx (crtsfx); - } -} - - -/* -================== -S_PrecacheSound - -================== -*/ -sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean stdpath, qboolean lock) -{ - sfx_t *sfx; - - if (!snd_initialized.integer) - return NULL; - - sfx = S_FindName (name, stdpath); - if (sfx == NULL) - return NULL; - - if (lock) - sfx->locks++; - - if (!nosound.integer && snd_precache.integer) - S_LoadSound(sfx, complain); - - return sfx; -} - -/* -================== -S_UnlockSfx - -Remove a lock from a SFX and freed it if possible -================== -*/ -void S_UnlockSfx (sfx_t *sfx) -{ - sfx->locks--; - S_FreeSfx (sfx); -} - - -//============================================================================= - -/* -================= -SND_PickChannel - -Picks a channel based on priorities, empty slots, number of channels -================= -*/ -channel_t *SND_PickChannel(int entnum, int entchannel) -{ - int ch_idx; - int first_to_die; - int life_left; - channel_t* ch; - -// Check for replacement sound, or find the best one to replace - first_to_die = -1; - life_left = 0x7fffffff; - for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) - { - ch = &channels[ch_idx]; - if (entchannel != 0 // channel 0 never overrides - && ch->entnum == entnum - && (ch->entchannel == entchannel || entchannel == -1) ) - { // always override sound from same entity - first_to_die = ch_idx; - break; - } - - // don't let monster sounds override player sounds - if (ch->entnum == cl.viewentity && entnum != cl.viewentity && ch->sfx) - continue; - - // don't override looped sounds - if ((ch->flags & CHANNELFLAG_FORCELOOP) != 0 || - (ch->sfx != NULL && ch->sfx->loopstart >= 0)) - continue; - - if (ch->end - paintedtime < life_left) - { - life_left = ch->end - paintedtime; - first_to_die = ch_idx; - } - } - - if (first_to_die == -1) - return NULL; - - S_StopChannel (first_to_die); - - return &channels[first_to_die]; -} - -/* -================= -SND_Spatialize - -Spatializes a channel -================= -*/ -void SND_Spatialize(channel_t *ch, qboolean isstatic) -{ - vec_t dist, scale, pan; - vec3_t source_vec; - - // anything coming from the view entity will always be full volume - // LordHavoc: make sounds with ATTN_NONE have no spatialization - if (ch->entnum == cl.viewentity || ch->dist_mult == 0) - { - ch->leftvol = ch->master_vol; - ch->rightvol = ch->master_vol; - } - else - { - // update sound origin if we know about the entity - if (ch->entnum > 0 && cls.state == ca_connected && cl_entities[ch->entnum].state_current.active) - { - //Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl_entities[ch->entnum].state_current.origin[0], cl_entities[ch->entnum].state_current.origin[1], cl_entities[ch->entnum].state_current.origin[2]); - VectorCopy(cl_entities[ch->entnum].state_current.origin, ch->origin); - if (cl_entities[ch->entnum].state_current.modelindex && cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->soundfromcenter) - VectorMAMAM(1.0f, ch->origin, 0.5f, cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->normalmins, 0.5f, cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->normalmaxs, ch->origin); - } - - // calculate stereo seperation and distance attenuation - Matrix4x4_Transform(&listener_matrix, ch->origin, source_vec); - dist = VectorNormalizeLength(source_vec); - // distance - scale = ch->master_vol * (1.0 - (dist * ch->dist_mult)); - // panning - pan = scale * source_vec[1]; - // calculate the volumes - ch->leftvol = (int) (scale + pan); - ch->rightvol = (int) (scale - pan); - } - - // Adjust volume of static sounds - if (isstatic) - { - ch->leftvol *= snd_staticvolume.value; - ch->rightvol *= snd_staticvolume.value; - } - - // clamp volumes - ch->leftvol = bound(0, ch->leftvol, 255); - ch->rightvol = bound(0, ch->rightvol, 255); -} - - -// ======================================================================= -// Start a sound effect -// ======================================================================= - -void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, vec3_t origin, float fvol, float attenuation, qboolean isstatic) -{ - // Initialize the channel - memset (target_chan, 0, sizeof (*target_chan)); - VectorCopy (origin, target_chan->origin); - target_chan->master_vol = fvol * 255; - target_chan->sfx = sfx; - target_chan->end = paintedtime + sfx->total_length; - target_chan->lastptime = paintedtime; - target_chan->flags = flags; - - // If it's a static sound - if (isstatic) - { - if (sfx->loopstart == -1) - Con_DPrintf("Quake compatibility warning: Static sound \"%s\" is not looped\n", sfx->name); - target_chan->dist_mult = attenuation / (64.0f * sound_nominal_clip_dist); - } - else - target_chan->dist_mult = attenuation / sound_nominal_clip_dist; - - // Lock the SFX during play - sfx->locks++; -} - - -int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) -{ - channel_t *target_chan, *check; - int ch_idx; - size_t skip; - - if (!sound_started || !sfx || !sfx->fetcher || nosound.integer) - return -1; - - // Pick a channel to play on - target_chan = SND_PickChannel(entnum, entchannel); - if (!target_chan) - return -1; - - if (!S_LoadSound (sfx, true)) - return -1; // couldn't load the sound's data - - S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_NONE, origin, fvol, attenuation, false); - target_chan->entnum = entnum; - target_chan->entchannel = entchannel; - - SND_Spatialize(target_chan, false); - - // if an identical sound has also been started this frame, offset the pos - // a bit to keep it from just making the first one louder - check = &channels[NUM_AMBIENTS]; - for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++) - { - if (check == target_chan) - continue; - if (check->sfx == sfx && !check->pos) - { - skip = 0.1 * sfx->format.speed; - if (skip > sfx->total_length) - skip = sfx->total_length; - if (skip > 0) - skip = rand() % skip; - target_chan->pos += skip; - target_chan->end -= skip; - break; - } - } - - return (target_chan - channels); -} - -void S_StopChannel (unsigned int channel_ind) -{ - channel_t *ch; - - if (channel_ind >= total_channels) - return; - - ch = &channels[channel_ind]; - if (ch->sfx != NULL) - { - sfx_t *sfx = ch->sfx; - - if (sfx->fetcher != NULL) - { - snd_fetcher_end_t fetcher_end = sfx->fetcher->end; - if (fetcher_end != NULL) - fetcher_end (ch); - } - - // Remove the lock it holds - S_UnlockSfx (sfx); - - ch->sfx = NULL; - } - ch->end = 0; -} - - -qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value) -{ - if (ch_ind >= total_channels) - return false; - - if (flag != CHANNELFLAG_FORCELOOP && - flag != CHANNELFLAG_PAUSED && - flag != CHANNELFLAG_FULLVOLUME) - return false; - - if (value) - channels[ch_ind].flags |= flag; - else - channels[ch_ind].flags &= ~flag; - - return true; -} - -void S_StopSound(int entnum, int entchannel) -{ - unsigned int i; - - for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) - if (channels[i].entnum == entnum && channels[i].entchannel == entchannel) - { - S_StopChannel (i); - return; - } -} - -void S_StopAllSounds (void) -{ - unsigned int i; - - for (i = 0; i < total_channels; i++) - S_StopChannel (i); - - total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics - memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); - - S_ClearBuffer (); -} - -void S_PauseGameSounds (qboolean toggle) -{ - unsigned int i; - - for (i = 0; i < total_channels; i++) - { - channel_t *ch; - - ch = &channels[i]; - if (ch->sfx != NULL && ! (ch->flags & CHANNELFLAG_LOCALSOUND)) - S_SetChannelFlag (i, CHANNELFLAG_PAUSED, toggle); - } -} - -void S_SetChannelVolume (unsigned int ch_ind, float fvol) -{ - channels[ch_ind].master_vol = fvol * 255; -} - - -void S_ClearBuffer(void) -{ - int clear; - - if (!sound_started || !shm) - return; - - if (shm->format.width == 1) - clear = 0x80; - else - clear = 0; - -#ifdef USE_DSOUND - if (pDSBuf) - { - DWORD dwSize; - DWORD *pData; - int reps; - HRESULT hresult; - - reps = 0; - - while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&pData, &dwSize, NULL, NULL, 0)) != DS_OK) - { - if (hresult != DSERR_BUFFERLOST) - { - Con_Print("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); - S_Shutdown (); - return; - } - - if (++reps > 10000) - { - Con_Print("S_ClearBuffer: DS: couldn't restore buffer\n"); - S_Shutdown (); - return; - } - } - - memset(pData, clear, shm->samples * shm->format.width); - - pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); - - } - else -#endif - if (shm->buffer) - { - int setsize = shm->samples * shm->format.width; - unsigned char *buf = shm->buffer; - - while (setsize--) - *buf++ = clear; - - // 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); - } -} - - -/* -================= -S_StaticSound -================= -*/ -void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation) -{ - channel_t *target_chan; - - if (!sfx) - return; - - if (total_channels == MAX_CHANNELS) - { - Con_Print("total_channels == MAX_CHANNELS\n"); - return; - } - - if (!S_LoadSound (sfx, true)) - return; - - target_chan = &channels[total_channels++]; - S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_FORCELOOP, origin, fvol, attenuation, true); - - SND_Spatialize (target_chan, true); -} - - -//============================================================================= - -/* -=================== -S_UpdateAmbientSounds - -=================== -*/ -void S_UpdateAmbientSounds (void) -{ - float vol; - int ambient_channel; - channel_t *chan; - qbyte ambientlevels[NUM_AMBIENTS]; - - // Mute ambient sounds until proven otherwise - for (ambient_channel = 0 ; ambient_channel < NUM_AMBIENTS;ambient_channel++) - channels[ambient_channel].master_vol = 0; - - if (ambient_level.value <= 0 || !cl.worldmodel || !cl.worldmodel->brush.AmbientSoundLevelsForPoint) - return; - - cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels)); - - // Calc ambient sound levels - for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) - { - chan = &channels[ambient_channel]; - if (chan->sfx == NULL || (chan->sfx->flags & SFXFLAG_FILEMISSING)) - continue; - - vol = ambient_level.value * ambientlevels[ambient_channel]; - if (vol < 8) - vol = 0; - - // Don't adjust volume too fast - if (chan->master_vol < vol) - { - chan->master_vol += host_realframetime * ambient_fade.value; - if (chan->master_vol > vol) - chan->master_vol = vol; - } - else if (chan->master_vol > vol) - { - chan->master_vol -= host_realframetime * ambient_fade.value; - if (chan->master_vol < vol) - chan->master_vol = vol; - } - - chan->leftvol = chan->rightvol = chan->master_vol; - } -} - - -/* -============ -S_Update - -Called once each time through the main loop -============ -*/ -void S_Update(const matrix4x4_t *listenermatrix) -{ - unsigned int i, j, total; - channel_t *ch, *combine; - - if (!snd_initialized.integer || (snd_blocked > 0)) - return; - - Matrix4x4_Invert_Simple(&listener_matrix, listenermatrix); - Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin); - -// update general area ambient sound sources - S_UpdateAmbientSounds (); - - combine = NULL; - -// update spatialization for static and dynamic sounds - ch = channels+NUM_AMBIENTS; - for (i=NUM_AMBIENTS ; isfx) - continue; - SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS); // respatialize channel - if (!ch->leftvol && !ch->rightvol) - continue; - - // try to combine static sounds with a previous channel of the same - // sound effect so we don't mix five torches every frame - - if (i > MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) - { - // see if it can just use the last one - if (combine && combine->sfx == ch->sfx) - { - combine->leftvol += ch->leftvol; - combine->rightvol += ch->rightvol; - ch->leftvol = ch->rightvol = 0; - continue; - } - // search for one - combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; - for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; jsfx == ch->sfx) - break; - - if (j == total_channels) - combine = NULL; - else - { - if (combine != ch) - { - combine->leftvol += ch->leftvol; - combine->rightvol += ch->rightvol; - ch->leftvol = ch->rightvol = 0; - } - continue; - } - } - } - -// -// debugging output -// - if (snd_show.integer) - { - total = 0; - ch = channels; - for (i=0 ; isfx && (ch->leftvol || ch->rightvol) ) - total++; - - Con_Printf("----(%u)----\n", total); - } - -// mix some sound - S_Update_(); -} - -void GetSoundtime(void) -{ - int samplepos; - static int buffers; - static int oldsamplepos; - int fullsamples; - - fullsamples = shm->samples / shm->format.channels; - -// it is possible to miscount buffers if it has wrapped twice between -// calls to S_Update. Oh well. - 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; -} - -void S_ExtraUpdate (void) -{ - if (snd_noextraupdate.integer) - return; // don't pollute timings - S_Update_(); -} - -void S_Update_(void) -{ - unsigned endtime; - int samps; - - if (!sound_started || (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 + _snd_mixahead.value * shm->format.speed; - samps = shm->samples >> (shm->format.channels - 1); - if (endtime > (unsigned int)(soundtime + samps)) - endtime = soundtime + samps; - -#ifdef USE_DSOUND -// if the buffer was lost or stopped, restore it and/or restart it - { - DWORD dwStatus; - - if (pDSBuf) - { - if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK) - Con_Print("Couldn't get sound buffer status\n"); - - if (dwStatus & DSBSTATUS_BUFFERLOST) - pDSBuf->lpVtbl->Restore (pDSBuf); - - if (!(dwStatus & DSBSTATUS_PLAYING)) - pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); - } - } -#endif - - S_PaintChannels (endtime); - - SNDDMA_Submit (); -} - -/* -=============================================================================== - -console functions - -=============================================================================== -*/ - -static void S_Play_Common(float fvol, float attenuation) -{ - int i, ch_ind; - char name[256]; - sfx_t *sfx; - - i = 1; - while (inext, i++) - { - if (sfx->fetcher != NULL) - { - size = sfx->mempool->totalsize; - total += size; - Con_Printf ("%c%c(%2db, %6s) %8i : %s\n", - (sfx->loopstart >= 0) ? 'L' : ' ', - (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ', - sfx->format.width * 8, - (sfx->format.channels == 1) ? "mono" : "stereo", - size, - sfx->name); - } - } - Con_Printf("Total resident: %i\n", total); -} - - -qboolean S_LocalSound (const char *sound, qboolean stdpath) -{ - sfx_t *sfx; - int ch_ind; - - if (!snd_initialized.integer || nosound.integer) - return true; - - sfx = S_PrecacheSound (sound, true, stdpath, false); - if (!sfx) - { - Con_Printf("S_LocalSound: can't precache %s\n", sound); - return false; - } - - ch_ind = S_StartSound (cl.viewentity, 0, sfx, vec3_origin, 1, 1); - if (ch_ind < 0) - return false; - - channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND; - return true; -} diff --git a/snd_main.c b/snd_main.c new file mode 100644 index 00000000..9e585899 --- /dev/null +++ b/snd_main.c @@ -0,0 +1,1094 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// snd_main.c -- main control for any streaming sound output device + +#include "quakedef.h" + +#include "snd_main.h" +#include "snd_ogg.h" + +// TODO: find a better solution instead of using a define +#if defined( _WIN32 ) && !defined( USE_SDL ) +# define USE_DSOUND +#endif + +#ifdef USE_DSOUND +#include +#include +extern DWORD gSndBufSize; +extern LPDIRECTSOUND pDS; +extern LPDIRECTSOUNDBUFFER pDSBuf; +#endif + + +void S_Play(void); +void S_PlayVol(void); +void S_Play2(void); +void S_SoundList(void); +void S_Update_(); + +void S_ClearBuffer (void); + + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +channel_t channels[MAX_CHANNELS]; +unsigned int total_channels; + +int snd_blocked = 0; +cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0"}; +cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1"}; + +// pointer should go away +volatile dma_t *shm = 0; +volatile dma_t sn; + +vec3_t listener_origin; +matrix4x4_t listener_matrix; +vec_t sound_nominal_clip_dist=1000.0; +mempool_t *snd_mempool; + +// sample PAIRS +int soundtime; +int paintedtime; + + +// Linked list of known sfx +sfx_t *known_sfx = NULL; + +int sound_started = 0; + +cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1"}; +cvar_t volume = {CVAR_SAVE, "volume", "0.7"}; +cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1"}; + +cvar_t nosound = {0, "nosound", "0"}; +cvar_t snd_precache = {0, "snd_precache", "1"}; +cvar_t bgmbuffer = {0, "bgmbuffer", "4096"}; +cvar_t ambient_level = {0, "ambient_level", "0.3"}; +cvar_t ambient_fade = {0, "ambient_fade", "100"}; +cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0"}; +cvar_t snd_show = {0, "snd_show", "0"}; +cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1"}; +cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0"}; + + +// ==================================================================== +// User-setable variables +// ==================================================================== + + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. +// + +qboolean fakedma = false; + + +void S_SoundInfo_f(void) +{ + if (!sound_started || !shm) + { + Con_Print("sound system not started\n"); + return; + } + + Con_Printf("%5d stereo\n", shm->format.channels - 1); + 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("%5u total_channels\n", total_channels); +} + + +void S_Startup(void) +{ + if (!snd_initialized.integer) + return; + + shm = &sn; + memset((void *)shm, 0, sizeof(*shm)); + + // create a piece of DMA memory + if (fakedma) + { + shm->format.width = 2; + shm->format.speed = 22050; + shm->format.channels = 2; + shm->samples = 32768; + shm->samplepos = 0; + shm->buffer = Mem_Alloc(snd_mempool, shm->format.channels * shm->samples * shm->format.width); + } + else + { + if (!SNDDMA_Init()) + { + Con_Print("S_Startup: SNDDMA_Init failed.\n"); + sound_started = 0; + shm = NULL; + return; + } + } + + sound_started = 1; + + Con_DPrintf("Sound sampling rate: %i\n", shm->format.speed); + + S_StopAllSounds (); +} + +void S_Shutdown(void) +{ + if (!sound_started) + return; + + if (fakedma) + Mem_Free(shm->buffer); + else + SNDDMA_Shutdown(); + + shm = NULL; + sound_started = 0; +} + +void S_Restart_f(void) +{ + S_Shutdown(); + S_Startup(); +} + +/* +================ +S_Init +================ +*/ +void S_Init(void) +{ + Con_DPrint("\nSound Initialization\n"); + + Cvar_RegisterVariable(&volume); + Cvar_RegisterVariable(&bgmvolume); + Cvar_RegisterVariable(&snd_staticvolume); + +// COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio) + if (COM_CheckParm("-nosound") || COM_CheckParm("-safe")) + return; + + snd_mempool = Mem_AllocPool("sound", 0, NULL); + +// COMMANDLINEOPTION: Sound: -simsound runs sound mixing but with no output + if (COM_CheckParm("-simsound")) + fakedma = true; + + Cmd_AddCommand("play", S_Play); + Cmd_AddCommand("play2", S_Play2); + Cmd_AddCommand("playvol", S_PlayVol); + Cmd_AddCommand("stopsound", S_StopAllSounds); + Cmd_AddCommand("soundlist", S_SoundList); + Cmd_AddCommand("soundinfo", S_SoundInfo_f); + Cmd_AddCommand("snd_restart", S_Restart_f); + + Cvar_RegisterVariable(&nosound); + 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); + Cvar_RegisterVariable(&snd_show); + Cvar_RegisterVariable(&_snd_mixahead); + Cvar_RegisterVariable(&snd_swapstereo); // for people with backwards sound wiring + + Cvar_SetValueQuick(&snd_initialized, true); + + known_sfx = NULL; + + total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics + memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); + + OGG_OpenLibrary (); +} + + +// ======================================================================= +// Load a sound +// ======================================================================= + +/* +================== +S_FindName + +================== +*/ +sfx_t *S_FindName (const char *name, qboolean stdpath) +{ + sfx_t *sfx; + size_t len; + char namebuffer [MAX_QPATH]; + + if (!snd_initialized.integer) + return NULL; + + // Add the default sound directory to the path + len = snprintf (namebuffer, sizeof (namebuffer), stdpath ? "sound/%s" : "%s", name); + if (len >= sizeof (namebuffer)) + Host_Error ("S_FindName: sound name too long (%s)", name); + + // Look for this sound in the list of known sfx + for (sfx = known_sfx; sfx != NULL; sfx = sfx->next) + if(!strcmp (sfx->name, namebuffer)) + return sfx; + + // Add a sfx_t struct for this sound + sfx = Mem_Alloc (snd_mempool, sizeof (*sfx)); + memset (sfx, 0, sizeof(*sfx)); + strlcpy (sfx->name, namebuffer, sizeof (sfx->name)); + sfx->next = known_sfx; + known_sfx = sfx; + + return sfx; +} + + +/* +================== +S_FreeSfx + +================== +*/ +void S_FreeSfx (sfx_t *sfx) +{ + // Never free a locked sfx + if (sfx->locks > 0) + return; + + Con_DPrintf ("S_FreeSfx: freeing %s\n", sfx->name); + + // Remove it from the list of known sfx + if (sfx == known_sfx) + known_sfx = known_sfx->next; + else + { + sfx_t *prev_sfx; + + for (prev_sfx = known_sfx; prev_sfx != NULL; prev_sfx = prev_sfx->next) + if (prev_sfx->next == sfx) + { + prev_sfx->next = sfx->next; + break; + } + if (prev_sfx == NULL) + Sys_Error ("S_FreeSfx: Can't find SFX %s in the list!\n", sfx->name); + } + + // Free it + Mem_FreePool (&sfx->mempool); + Mem_Free (sfx); +} + + +/* +================== +S_ServerSounds + +================== +*/ +void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds) +{ + sfx_t *sfx; + unsigned int i; + + // Start the ambient sounds and make them loop + channels[AMBIENT_WATER].sfx = S_PrecacheSound ("ambience/water1.wav", false, true, true); + channels[AMBIENT_SKY].sfx = S_PrecacheSound ("ambience/wind2.wav", false, true, true); + for (i = 0; i < NUM_AMBIENTS; i++) + channels[i].flags |= CHANNELFLAG_FORCELOOP; + + // Remove 1 lock from all sfx with the SFXFLAG_SERVERSOUND flag, and remove the flag + for (sfx = known_sfx; sfx != NULL; sfx = sfx->next) + if (sfx->flags & SFXFLAG_SERVERSOUND) + { + sfx->locks--; + sfx->flags &= ~SFXFLAG_SERVERSOUND; + } + + // Add 1 lock and the SFXFLAG_SERVERSOUND flag to each sfx in "serversound" + for (i = 1; i < numsounds; i++) + { + sfx = S_FindName (serversound[i], true); + if (sfx != NULL) + { + sfx->locks++; + sfx->flags |= SFXFLAG_SERVERSOUND; + } + } + + // Free all unlocked sfx + sfx = known_sfx; + while (sfx != NULL) + { + sfx_t* crtsfx; + + // We may lose the "next" pointer after S_FreeSfx + crtsfx = sfx; + sfx = sfx->next; + + S_FreeSfx (crtsfx); + } +} + + +/* +================== +S_PrecacheSound + +================== +*/ +sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean stdpath, qboolean lock) +{ + sfx_t *sfx; + + if (!snd_initialized.integer) + return NULL; + + sfx = S_FindName (name, stdpath); + if (sfx == NULL) + return NULL; + + if (lock) + sfx->locks++; + + if (!nosound.integer && snd_precache.integer) + S_LoadSound(sfx, complain); + + return sfx; +} + +/* +================== +S_UnlockSfx + +Remove a lock from a SFX and freed it if possible +================== +*/ +void S_UnlockSfx (sfx_t *sfx) +{ + sfx->locks--; + S_FreeSfx (sfx); +} + + +//============================================================================= + +/* +================= +SND_PickChannel + +Picks a channel based on priorities, empty slots, number of channels +================= +*/ +channel_t *SND_PickChannel(int entnum, int entchannel) +{ + int ch_idx; + int first_to_die; + int life_left; + channel_t* ch; + +// Check for replacement sound, or find the best one to replace + first_to_die = -1; + life_left = 0x7fffffff; + for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) + { + ch = &channels[ch_idx]; + if (entchannel != 0 // channel 0 never overrides + && ch->entnum == entnum + && (ch->entchannel == entchannel || entchannel == -1) ) + { // always override sound from same entity + first_to_die = ch_idx; + break; + } + + // don't let monster sounds override player sounds + if (ch->entnum == cl.viewentity && entnum != cl.viewentity && ch->sfx) + continue; + + // don't override looped sounds + if ((ch->flags & CHANNELFLAG_FORCELOOP) != 0 || + (ch->sfx != NULL && ch->sfx->loopstart >= 0)) + continue; + + if (ch->end - paintedtime < life_left) + { + life_left = ch->end - paintedtime; + first_to_die = ch_idx; + } + } + + if (first_to_die == -1) + return NULL; + + S_StopChannel (first_to_die); + + return &channels[first_to_die]; +} + +/* +================= +SND_Spatialize + +Spatializes a channel +================= +*/ +void SND_Spatialize(channel_t *ch, qboolean isstatic) +{ + vec_t dist, scale, pan; + vec3_t source_vec; + + // anything coming from the view entity will always be full volume + // LordHavoc: make sounds with ATTN_NONE have no spatialization + if (ch->entnum == cl.viewentity || ch->dist_mult == 0) + { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + } + else + { + // update sound origin if we know about the entity + if (ch->entnum > 0 && cls.state == ca_connected && cl_entities[ch->entnum].state_current.active) + { + //Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl_entities[ch->entnum].state_current.origin[0], cl_entities[ch->entnum].state_current.origin[1], cl_entities[ch->entnum].state_current.origin[2]); + VectorCopy(cl_entities[ch->entnum].state_current.origin, ch->origin); + if (cl_entities[ch->entnum].state_current.modelindex && cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->soundfromcenter) + VectorMAMAM(1.0f, ch->origin, 0.5f, cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->normalmins, 0.5f, cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->normalmaxs, ch->origin); + } + + // calculate stereo seperation and distance attenuation + Matrix4x4_Transform(&listener_matrix, ch->origin, source_vec); + dist = VectorNormalizeLength(source_vec); + // distance + scale = ch->master_vol * (1.0 - (dist * ch->dist_mult)); + // panning + pan = scale * source_vec[1]; + // calculate the volumes + ch->leftvol = (int) (scale + pan); + ch->rightvol = (int) (scale - pan); + } + + // Adjust volume of static sounds + if (isstatic) + { + ch->leftvol *= snd_staticvolume.value; + ch->rightvol *= snd_staticvolume.value; + } + + // clamp volumes + ch->leftvol = bound(0, ch->leftvol, 255); + ch->rightvol = bound(0, ch->rightvol, 255); +} + + +// ======================================================================= +// Start a sound effect +// ======================================================================= + +void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, vec3_t origin, float fvol, float attenuation, qboolean isstatic) +{ + // Initialize the channel + memset (target_chan, 0, sizeof (*target_chan)); + VectorCopy (origin, target_chan->origin); + target_chan->master_vol = fvol * 255; + target_chan->sfx = sfx; + target_chan->end = paintedtime + sfx->total_length; + target_chan->lastptime = paintedtime; + target_chan->flags = flags; + + // If it's a static sound + if (isstatic) + { + if (sfx->loopstart == -1) + Con_DPrintf("Quake compatibility warning: Static sound \"%s\" is not looped\n", sfx->name); + target_chan->dist_mult = attenuation / (64.0f * sound_nominal_clip_dist); + } + else + target_chan->dist_mult = attenuation / sound_nominal_clip_dist; + + // Lock the SFX during play + sfx->locks++; +} + + +int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) +{ + channel_t *target_chan, *check; + int ch_idx; + size_t skip; + + if (!sound_started || !sfx || !sfx->fetcher || nosound.integer) + return -1; + + // Pick a channel to play on + target_chan = SND_PickChannel(entnum, entchannel); + if (!target_chan) + return -1; + + if (!S_LoadSound (sfx, true)) + return -1; // couldn't load the sound's data + + S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_NONE, origin, fvol, attenuation, false); + target_chan->entnum = entnum; + target_chan->entchannel = entchannel; + + SND_Spatialize(target_chan, false); + + // if an identical sound has also been started this frame, offset the pos + // a bit to keep it from just making the first one louder + check = &channels[NUM_AMBIENTS]; + for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++) + { + if (check == target_chan) + continue; + if (check->sfx == sfx && !check->pos) + { + skip = 0.1 * sfx->format.speed; + if (skip > sfx->total_length) + skip = sfx->total_length; + if (skip > 0) + skip = rand() % skip; + target_chan->pos += skip; + target_chan->end -= skip; + break; + } + } + + return (target_chan - channels); +} + +void S_StopChannel (unsigned int channel_ind) +{ + channel_t *ch; + + if (channel_ind >= total_channels) + return; + + ch = &channels[channel_ind]; + if (ch->sfx != NULL) + { + sfx_t *sfx = ch->sfx; + + if (sfx->fetcher != NULL) + { + snd_fetcher_end_t fetcher_end = sfx->fetcher->end; + if (fetcher_end != NULL) + fetcher_end (ch); + } + + // Remove the lock it holds + S_UnlockSfx (sfx); + + ch->sfx = NULL; + } + ch->end = 0; +} + + +qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value) +{ + if (ch_ind >= total_channels) + return false; + + if (flag != CHANNELFLAG_FORCELOOP && + flag != CHANNELFLAG_PAUSED && + flag != CHANNELFLAG_FULLVOLUME) + return false; + + if (value) + channels[ch_ind].flags |= flag; + else + channels[ch_ind].flags &= ~flag; + + return true; +} + +void S_StopSound(int entnum, int entchannel) +{ + unsigned int i; + + for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) + if (channels[i].entnum == entnum && channels[i].entchannel == entchannel) + { + S_StopChannel (i); + return; + } +} + +void S_StopAllSounds (void) +{ + unsigned int i; + + for (i = 0; i < total_channels; i++) + S_StopChannel (i); + + total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics + memset(channels, 0, MAX_CHANNELS * sizeof(channel_t)); + + S_ClearBuffer (); +} + +void S_PauseGameSounds (qboolean toggle) +{ + unsigned int i; + + for (i = 0; i < total_channels; i++) + { + channel_t *ch; + + ch = &channels[i]; + if (ch->sfx != NULL && ! (ch->flags & CHANNELFLAG_LOCALSOUND)) + S_SetChannelFlag (i, CHANNELFLAG_PAUSED, toggle); + } +} + +void S_SetChannelVolume (unsigned int ch_ind, float fvol) +{ + channels[ch_ind].master_vol = fvol * 255; +} + + +void S_ClearBuffer(void) +{ + int clear; + + if (!sound_started || !shm) + return; + + if (shm->format.width == 1) + clear = 0x80; + else + clear = 0; + +#ifdef USE_DSOUND + if (pDSBuf) + { + DWORD dwSize; + DWORD *pData; + int reps; + HRESULT hresult; + + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&pData, &dwSize, NULL, NULL, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_Print("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + return; + } + + if (++reps > 10000) + { + Con_Print("S_ClearBuffer: DS: couldn't restore buffer\n"); + S_Shutdown (); + return; + } + } + + memset(pData, clear, shm->samples * shm->format.width); + + pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); + + } + else +#endif + if (shm->buffer) + { + int setsize = shm->samples * shm->format.width; + unsigned char *buf = shm->buffer; + + while (setsize--) + *buf++ = clear; + + // 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); + } +} + + +/* +================= +S_StaticSound +================= +*/ +void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation) +{ + channel_t *target_chan; + + if (!sfx) + return; + + if (total_channels == MAX_CHANNELS) + { + Con_Print("total_channels == MAX_CHANNELS\n"); + return; + } + + if (!S_LoadSound (sfx, true)) + return; + + target_chan = &channels[total_channels++]; + S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_FORCELOOP, origin, fvol, attenuation, true); + + SND_Spatialize (target_chan, true); +} + + +//============================================================================= + +/* +=================== +S_UpdateAmbientSounds + +=================== +*/ +void S_UpdateAmbientSounds (void) +{ + float vol; + int ambient_channel; + channel_t *chan; + qbyte ambientlevels[NUM_AMBIENTS]; + + // Mute ambient sounds until proven otherwise + for (ambient_channel = 0 ; ambient_channel < NUM_AMBIENTS;ambient_channel++) + channels[ambient_channel].master_vol = 0; + + if (ambient_level.value <= 0 || !cl.worldmodel || !cl.worldmodel->brush.AmbientSoundLevelsForPoint) + return; + + cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels)); + + // Calc ambient sound levels + for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) + { + chan = &channels[ambient_channel]; + if (chan->sfx == NULL || (chan->sfx->flags & SFXFLAG_FILEMISSING)) + continue; + + vol = ambient_level.value * ambientlevels[ambient_channel]; + if (vol < 8) + vol = 0; + + // Don't adjust volume too fast + if (chan->master_vol < vol) + { + chan->master_vol += host_realframetime * ambient_fade.value; + if (chan->master_vol > vol) + chan->master_vol = vol; + } + else if (chan->master_vol > vol) + { + chan->master_vol -= host_realframetime * ambient_fade.value; + if (chan->master_vol < vol) + chan->master_vol = vol; + } + + chan->leftvol = chan->rightvol = chan->master_vol; + } +} + + +/* +============ +S_Update + +Called once each time through the main loop +============ +*/ +void S_Update(const matrix4x4_t *listenermatrix) +{ + unsigned int i, j, total; + channel_t *ch, *combine; + + if (!snd_initialized.integer || (snd_blocked > 0)) + return; + + Matrix4x4_Invert_Simple(&listener_matrix, listenermatrix); + Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin); + +// update general area ambient sound sources + S_UpdateAmbientSounds (); + + combine = NULL; + +// update spatialization for static and dynamic sounds + ch = channels+NUM_AMBIENTS; + for (i=NUM_AMBIENTS ; isfx) + continue; + SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS); // respatialize channel + if (!ch->leftvol && !ch->rightvol) + continue; + + // try to combine static sounds with a previous channel of the same + // sound effect so we don't mix five torches every frame + + if (i > MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) + { + // see if it can just use the last one + if (combine && combine->sfx == ch->sfx) + { + combine->leftvol += ch->leftvol; + combine->rightvol += ch->rightvol; + ch->leftvol = ch->rightvol = 0; + continue; + } + // search for one + combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; + for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; jsfx == ch->sfx) + break; + + if (j == total_channels) + combine = NULL; + else + { + if (combine != ch) + { + combine->leftvol += ch->leftvol; + combine->rightvol += ch->rightvol; + ch->leftvol = ch->rightvol = 0; + } + continue; + } + } + } + +// +// debugging output +// + if (snd_show.integer) + { + total = 0; + ch = channels; + for (i=0 ; isfx && (ch->leftvol || ch->rightvol) ) + total++; + + Con_Printf("----(%u)----\n", total); + } + +// mix some sound + S_Update_(); +} + +void GetSoundtime(void) +{ + int samplepos; + static int buffers; + static int oldsamplepos; + int fullsamples; + + fullsamples = shm->samples / shm->format.channels; + +// it is possible to miscount buffers if it has wrapped twice between +// calls to S_Update. Oh well. + 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; +} + +void S_ExtraUpdate (void) +{ + if (snd_noextraupdate.integer) + return; // don't pollute timings + S_Update_(); +} + +void S_Update_(void) +{ + unsigned endtime; + int samps; + + if (!sound_started || (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 + _snd_mixahead.value * shm->format.speed; + samps = shm->samples >> (shm->format.channels - 1); + if (endtime > (unsigned int)(soundtime + samps)) + endtime = soundtime + samps; + +#ifdef USE_DSOUND +// if the buffer was lost or stopped, restore it and/or restart it + { + DWORD dwStatus; + + if (pDSBuf) + { + if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK) + Con_Print("Couldn't get sound buffer status\n"); + + if (dwStatus & DSBSTATUS_BUFFERLOST) + pDSBuf->lpVtbl->Restore (pDSBuf); + + if (!(dwStatus & DSBSTATUS_PLAYING)) + pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + } + } +#endif + + S_PaintChannels (endtime); + + SNDDMA_Submit (); +} + +/* +=============================================================================== + +console functions + +=============================================================================== +*/ + +static void S_Play_Common(float fvol, float attenuation) +{ + int i, ch_ind; + char name[256]; + sfx_t *sfx; + + i = 1; + while (inext, i++) + { + if (sfx->fetcher != NULL) + { + size = sfx->mempool->totalsize; + total += size; + Con_Printf ("%c%c(%2db, %6s) %8i : %s\n", + (sfx->loopstart >= 0) ? 'L' : ' ', + (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ', + sfx->format.width * 8, + (sfx->format.channels == 1) ? "mono" : "stereo", + size, + sfx->name); + } + } + Con_Printf("Total resident: %i\n", total); +} + + +qboolean S_LocalSound (const char *sound, qboolean stdpath) +{ + sfx_t *sfx; + int ch_ind; + + if (!snd_initialized.integer || nosound.integer) + return true; + + sfx = S_PrecacheSound (sound, true, stdpath, false); + if (!sfx) + { + Con_Printf("S_LocalSound: can't precache %s\n", sound); + return false; + } + + ch_ind = S_StartSound (cl.viewentity, 0, sfx, vec3_origin, 1, 1); + if (ch_ind < 0) + return false; + + channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND; + return true; +}