diff options
author | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
---|---|---|
committer | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
commit | 6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch) | |
tree | e3eda937a05d7db42de725b7013bd0344b987f34 /code/client/snd_dma.c | |
parent | 872d4d7f55af706737ffb361bb76ad13e7496770 (diff) | |
download | ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip |
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/client/snd_dma.c')
-rwxr-xr-x | code/client/snd_dma.c | 3272 |
1 files changed, 1636 insertions, 1636 deletions
diff --git a/code/client/snd_dma.c b/code/client/snd_dma.c index 6ecff18..aae87ec 100755 --- a/code/client/snd_dma.c +++ b/code/client/snd_dma.c @@ -1,1636 +1,1636 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code 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.
-
-Quake III Arena source code 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 Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: snd_dma.c
- *
- * desc: main control for any streaming sound output device
- *
- * $Archive: /MissionPack/code/client/snd_dma.c $
- *
- *****************************************************************************/
-
-#include "snd_local.h"
-#include "client.h"
-
-void S_Play_f(void);
-void S_SoundList_f(void);
-void S_Music_f(void);
-
-void S_Update_();
-void S_StopAllSounds(void);
-void S_UpdateBackgroundTrack( void );
-
-static fileHandle_t s_backgroundFile;
-static wavinfo_t s_backgroundInfo;
-//int s_nextWavChunk;
-static int s_backgroundSamples;
-static char s_backgroundLoop[MAX_QPATH];
-//static char s_backgroundMusic[MAX_QPATH]; //TTimo: unused
-
-
-// =======================================================================
-// Internal sound data & structures
-// =======================================================================
-
-// only begin attenuating sound volumes when outside the FULLVOLUME range
-#define SOUND_FULLVOLUME 80
-
-#define SOUND_ATTENUATE 0.0008f
-
-channel_t s_channels[MAX_CHANNELS];
-channel_t loop_channels[MAX_CHANNELS];
-int numLoopChannels;
-
-static int s_soundStarted;
-static qboolean s_soundMuted;
-
-dma_t dma;
-
-static int listener_number;
-static vec3_t listener_origin;
-static vec3_t listener_axis[3];
-
-int s_soundtime; // sample PAIRS
-int s_paintedtime; // sample PAIRS
-
-// MAX_SFX may be larger than MAX_SOUNDS because
-// of custom player sounds
-#define MAX_SFX 4096
-sfx_t s_knownSfx[MAX_SFX];
-int s_numSfx = 0;
-
-#define LOOP_HASH 128
-static sfx_t *sfxHash[LOOP_HASH];
-
-cvar_t *s_volume;
-cvar_t *s_testsound;
-cvar_t *s_khz;
-cvar_t *s_show;
-cvar_t *s_mixahead;
-cvar_t *s_mixPreStep;
-cvar_t *s_musicVolume;
-cvar_t *s_separation;
-cvar_t *s_doppler;
-
-static loopSound_t loopSounds[MAX_GENTITIES];
-static channel_t *freelist = NULL;
-
-int s_rawend;
-portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
-
-
-// ====================================================================
-// User-setable variables
-// ====================================================================
-
-
-void S_SoundInfo_f(void) {
- Com_Printf("----- Sound Info -----\n" );
- if (!s_soundStarted) {
- Com_Printf ("sound system not started\n");
- } else {
- if ( s_soundMuted ) {
- Com_Printf ("sound system is muted\n");
- }
-
- Com_Printf("%5d stereo\n", dma.channels - 1);
- Com_Printf("%5d samples\n", dma.samples);
- Com_Printf("%5d samplebits\n", dma.samplebits);
- Com_Printf("%5d submission_chunk\n", dma.submission_chunk);
- Com_Printf("%5d speed\n", dma.speed);
- Com_Printf("0x%x dma buffer\n", dma.buffer);
- if ( s_backgroundFile ) {
- Com_Printf("Background file: %s\n", s_backgroundLoop );
- } else {
- Com_Printf("No background file.\n" );
- }
-
- }
- Com_Printf("----------------------\n" );
-}
-
-
-
-/*
-================
-S_Init
-================
-*/
-void S_Init( void ) {
- cvar_t *cv;
- qboolean r;
-
- Com_Printf("\n------- sound initialization -------\n");
-
- s_volume = Cvar_Get ("s_volume", "0.8", CVAR_ARCHIVE);
- s_musicVolume = Cvar_Get ("s_musicvolume", "0.25", CVAR_ARCHIVE);
- s_separation = Cvar_Get ("s_separation", "0.5", CVAR_ARCHIVE);
- s_doppler = Cvar_Get ("s_doppler", "1", CVAR_ARCHIVE);
- s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE);
- s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE);
-
- s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE);
- s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT);
- s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT);
-
- cv = Cvar_Get ("s_initsound", "1", 0);
- if ( !cv->integer ) {
- Com_Printf ("not initializing.\n");
- Com_Printf("------------------------------------\n");
- return;
- }
-
- Cmd_AddCommand("play", S_Play_f);
- Cmd_AddCommand("music", S_Music_f);
- Cmd_AddCommand("s_list", S_SoundList_f);
- Cmd_AddCommand("s_info", S_SoundInfo_f);
- Cmd_AddCommand("s_stop", S_StopAllSounds);
-
- r = SNDDMA_Init();
- Com_Printf("------------------------------------\n");
-
- if ( r ) {
- s_soundStarted = 1;
- s_soundMuted = 1;
-// s_numSfx = 0;
-
- Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
-
- s_soundtime = 0;
- s_paintedtime = 0;
-
- S_StopAllSounds ();
-
- S_SoundInfo_f();
- }
-
-}
-
-
-void S_ChannelFree(channel_t *v) {
- v->thesfx = NULL;
- *(channel_t **)v = freelist;
- freelist = (channel_t*)v;
-}
-
-channel_t* S_ChannelMalloc() {
- channel_t *v;
- if (freelist == NULL) {
- return NULL;
- }
- v = freelist;
- freelist = *(channel_t **)freelist;
- v->allocTime = Com_Milliseconds();
- return v;
-}
-
-void S_ChannelSetup() {
- channel_t *p, *q;
-
- // clear all the sounds so they don't
- Com_Memset( s_channels, 0, sizeof( s_channels ) );
-
- p = s_channels;;
- q = p + MAX_CHANNELS;
- while (--q > p) {
- *(channel_t **)q = q-1;
- }
-
- *(channel_t **)q = NULL;
- freelist = p + MAX_CHANNELS - 1;
- Com_DPrintf("Channel memory manager started\n");
-}
-
-// =======================================================================
-// Shutdown sound engine
-// =======================================================================
-
-void S_Shutdown( void ) {
- if ( !s_soundStarted ) {
- return;
- }
-
- SNDDMA_Shutdown();
-
- s_soundStarted = 0;
-
- Cmd_RemoveCommand("play");
- Cmd_RemoveCommand("music");
- Cmd_RemoveCommand("stopsound");
- Cmd_RemoveCommand("soundlist");
- Cmd_RemoveCommand("soundinfo");
-}
-
-
-// =======================================================================
-// Load a sound
-// =======================================================================
-
-/*
-================
-return a hash value for the sfx name
-================
-*/
-static long S_HashSFXName(const char *name) {
- int i;
- long hash;
- char letter;
-
- hash = 0;
- i = 0;
- while (name[i] != '\0') {
- letter = tolower(name[i]);
- if (letter =='.') break; // don't include extension
- if (letter =='\\') letter = '/'; // damn path names
- hash+=(long)(letter)*(i+119);
- i++;
- }
- hash &= (LOOP_HASH-1);
- return hash;
-}
-
-/*
-==================
-S_FindName
-
-Will allocate a new sfx if it isn't found
-==================
-*/
-static sfx_t *S_FindName( const char *name ) {
- int i;
- int hash;
-
- sfx_t *sfx;
-
- if (!name) {
- Com_Error (ERR_FATAL, "S_FindName: NULL\n");
- }
- if (!name[0]) {
- Com_Error (ERR_FATAL, "S_FindName: empty name\n");
- }
-
- if (strlen(name) >= MAX_QPATH) {
- Com_Error (ERR_FATAL, "Sound name too long: %s", name);
- }
-
- hash = S_HashSFXName(name);
-
- sfx = sfxHash[hash];
- // see if already loaded
- while (sfx) {
- if (!Q_stricmp(sfx->soundName, name) ) {
- return sfx;
- }
- sfx = sfx->next;
- }
-
- // find a free sfx
- for (i=0 ; i < s_numSfx ; i++) {
- if (!s_knownSfx[i].soundName[0]) {
- break;
- }
- }
-
- if (i == s_numSfx) {
- if (s_numSfx == MAX_SFX) {
- Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
- }
- s_numSfx++;
- }
-
- sfx = &s_knownSfx[i];
- Com_Memset (sfx, 0, sizeof(*sfx));
- strcpy (sfx->soundName, name);
-
- sfx->next = sfxHash[hash];
- sfxHash[hash] = sfx;
-
- return sfx;
-}
-
-/*
-=================
-S_DefaultSound
-=================
-*/
-void S_DefaultSound( sfx_t *sfx ) {
-
- int i;
-
- sfx->soundLength = 512;
- sfx->soundData = SND_malloc();
- sfx->soundData->next = NULL;
-
-
- for ( i = 0 ; i < sfx->soundLength ; i++ ) {
- sfx->soundData->sndChunk[i] = i;
- }
-}
-
-/*
-===================
-S_DisableSounds
-
-Disables sounds until the next S_BeginRegistration.
-This is called when the hunk is cleared and the sounds
-are no longer valid.
-===================
-*/
-void S_DisableSounds( void ) {
- S_StopAllSounds();
- s_soundMuted = qtrue;
-}
-
-/*
-=====================
-S_BeginRegistration
-
-=====================
-*/
-void S_BeginRegistration( void ) {
- s_soundMuted = qfalse; // we can play again
-
- if (s_numSfx == 0) {
- SND_setup();
-
- s_numSfx = 0;
- Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) );
- Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
-
- S_RegisterSound("sound/feedback/hit.wav", qfalse); // changed to a sound in baseq3
- }
-}
-
-
-/*
-==================
-S_RegisterSound
-
-Creates a default buzz sound if the file can't be loaded
-==================
-*/
-sfxHandle_t S_RegisterSound( const char *name, qboolean compressed ) {
- sfx_t *sfx;
-
- compressed = qfalse;
- if (!s_soundStarted) {
- return 0;
- }
-
- if ( strlen( name ) >= MAX_QPATH ) {
- Com_Printf( "Sound name exceeds MAX_QPATH\n" );
- return 0;
- }
-
- sfx = S_FindName( name );
- if ( sfx->soundData ) {
- if ( sfx->defaultSound ) {
- Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName );
- return 0;
- }
- return sfx - s_knownSfx;
- }
-
- sfx->inMemory = qfalse;
- sfx->soundCompressed = compressed;
-
- S_memoryLoad(sfx);
-
- if ( sfx->defaultSound ) {
- Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName );
- return 0;
- }
-
- return sfx - s_knownSfx;
-}
-
-void S_memoryLoad(sfx_t *sfx) {
- // load the sound file
- if ( !S_LoadSound ( sfx ) ) {
-// Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->soundName );
- sfx->defaultSound = qtrue;
- }
- sfx->inMemory = qtrue;
-}
-
-//=============================================================================
-
-/*
-=================
-S_SpatializeOrigin
-
-Used for spatializing s_channels
-=================
-*/
-void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *right_vol)
-{
- vec_t dot;
- vec_t dist;
- vec_t lscale, rscale, scale;
- vec3_t source_vec;
- vec3_t vec;
-
- const float dist_mult = SOUND_ATTENUATE;
-
- // calculate stereo seperation and distance attenuation
- VectorSubtract(origin, listener_origin, source_vec);
-
- dist = VectorNormalize(source_vec);
- dist -= SOUND_FULLVOLUME;
- if (dist < 0)
- dist = 0; // close enough to be at full volume
- dist *= dist_mult; // different attenuation levels
-
- VectorRotate( source_vec, listener_axis, vec );
-
- dot = -vec[1];
-
- if (dma.channels == 1)
- { // no attenuation = no spatialization
- rscale = 1.0;
- lscale = 1.0;
- }
- else
- {
- rscale = 0.5 * (1.0 + dot);
- lscale = 0.5 * (1.0 - dot);
- //rscale = s_separation->value + ( 1.0 - s_separation->value ) * dot;
- //lscale = s_separation->value - ( 1.0 - s_separation->value ) * dot;
- if ( rscale < 0 ) {
- rscale = 0;
- }
- if ( lscale < 0 ) {
- lscale = 0;
- }
- }
-
- // add in distance effect
- scale = (1.0 - dist) * rscale;
- *right_vol = (master_vol * scale);
- if (*right_vol < 0)
- *right_vol = 0;
-
- scale = (1.0 - dist) * lscale;
- *left_vol = (master_vol * scale);
- if (*left_vol < 0)
- *left_vol = 0;
-}
-
-// =======================================================================
-// Start a sound effect
-// =======================================================================
-
-/*
-====================
-S_StartSound
-
-Validates the parms and ques the sound up
-if pos is NULL, the sound will be dynamically sourced from the entity
-Entchannel 0 will never override a playing sound
-====================
-*/
-void S_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) {
- channel_t *ch;
- sfx_t *sfx;
- int i, oldest, chosen, time;
- int inplay, allowed;
-
- if ( !s_soundStarted || s_soundMuted ) {
- return;
- }
-
- if ( !origin && ( entityNum < 0 || entityNum > MAX_GENTITIES ) ) {
- Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum );
- }
-
- if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
- Com_Printf( S_COLOR_YELLOW, "S_StartSound: handle %i out of range\n", sfxHandle );
- return;
- }
-
- sfx = &s_knownSfx[ sfxHandle ];
-
- if (sfx->inMemory == qfalse) {
- S_memoryLoad(sfx);
- }
-
- if ( s_show->integer == 1 ) {
- Com_Printf( "%i : %s\n", s_paintedtime, sfx->soundName );
- }
-
- time = Com_Milliseconds();
-
-// Com_Printf("playing %s\n", sfx->soundName);
- // pick a channel to play on
-
- allowed = 4;
- if (entityNum == listener_number) {
- allowed = 8;
- }
-
- ch = s_channels;
- inplay = 0;
- for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {
- if (ch[i].entnum == entityNum && ch[i].thesfx == sfx) {
- if (time - ch[i].allocTime < 50) {
-// if (Cvar_VariableValue( "cg_showmiss" )) {
-// Com_Printf("double sound start\n");
-// }
- return;
- }
- inplay++;
- }
- }
-
- if (inplay>allowed) {
- return;
- }
-
- sfx->lastTimeUsed = time;
-
- ch = S_ChannelMalloc(); // entityNum, entchannel);
- if (!ch) {
- ch = s_channels;
-
- oldest = sfx->lastTimeUsed;
- chosen = -1;
- for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
- if (ch->entnum != listener_number && ch->entnum == entityNum && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) {
- oldest = ch->allocTime;
- chosen = i;
- }
- }
- if (chosen == -1) {
- ch = s_channels;
- for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
- if (ch->entnum != listener_number && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) {
- oldest = ch->allocTime;
- chosen = i;
- }
- }
- if (chosen == -1) {
- if (ch->entnum == listener_number) {
- for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
- if (ch->allocTime<oldest) {
- oldest = ch->allocTime;
- chosen = i;
- }
- }
- }
- if (chosen == -1) {
- Com_Printf("dropping sound\n");
- return;
- }
- }
- }
- ch = &s_channels[chosen];
- ch->allocTime = sfx->lastTimeUsed;
- }
-
- if (origin) {
- VectorCopy (origin, ch->origin);
- ch->fixed_origin = qtrue;
- } else {
- ch->fixed_origin = qfalse;
- }
-
- ch->master_vol = 127;
- ch->entnum = entityNum;
- ch->thesfx = sfx;
- ch->startSample = START_SAMPLE_IMMEDIATE;
- ch->entchannel = entchannel;
- ch->leftvol = ch->master_vol; // these will get calced at next spatialize
- ch->rightvol = ch->master_vol; // unless the game isn't running
- ch->doppler = qfalse;
-}
-
-
-/*
-==================
-S_StartLocalSound
-==================
-*/
-void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) {
- if ( !s_soundStarted || s_soundMuted ) {
- return;
- }
-
- if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
- Com_Printf( S_COLOR_YELLOW, "S_StartLocalSound: handle %i out of range\n", sfxHandle );
- return;
- }
-
- S_StartSound (NULL, listener_number, channelNum, sfxHandle );
-}
-
-
-/*
-==================
-S_ClearSoundBuffer
-
-If we are about to perform file access, clear the buffer
-so sound doesn't stutter.
-==================
-*/
-void S_ClearSoundBuffer( void ) {
- int clear;
-
- if (!s_soundStarted)
- return;
-
- // stop looping sounds
- Com_Memset(loopSounds, 0, MAX_GENTITIES*sizeof(loopSound_t));
- Com_Memset(loop_channels, 0, MAX_CHANNELS*sizeof(channel_t));
- numLoopChannels = 0;
-
- S_ChannelSetup();
-
- s_rawend = 0;
-
- if (dma.samplebits == 8)
- clear = 0x80;
- else
- clear = 0;
-
- SNDDMA_BeginPainting ();
- if (dma.buffer)
- // TTimo: due to a particular bug workaround in linux sound code,
- // have to optionally use a custom C implementation of Com_Memset
- // not affecting win32, we have #define Snd_Memset Com_Memset
- // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
- Snd_Memset(dma.buffer, clear, dma.samples * dma.samplebits/8);
- SNDDMA_Submit ();
-}
-
-/*
-==================
-S_StopAllSounds
-==================
-*/
-void S_StopAllSounds(void) {
- if ( !s_soundStarted ) {
- return;
- }
-
- // stop the background music
- S_StopBackgroundTrack();
-
- S_ClearSoundBuffer ();
-}
-
-/*
-==============================================================
-
-continuous looping sounds are added each frame
-
-==============================================================
-*/
-
-void S_StopLoopingSound(int entityNum) {
- loopSounds[entityNum].active = qfalse;
-// loopSounds[entityNum].sfx = 0;
- loopSounds[entityNum].kill = qfalse;
-}
-
-/*
-==================
-S_ClearLoopingSounds
-
-==================
-*/
-void S_ClearLoopingSounds( qboolean killall ) {
- int i;
- for ( i = 0 ; i < MAX_GENTITIES ; i++) {
- if (killall || loopSounds[i].kill == qtrue || (loopSounds[i].sfx && loopSounds[i].sfx->soundLength == 0)) {
- loopSounds[i].kill = qfalse;
- S_StopLoopingSound(i);
- }
- }
- numLoopChannels = 0;
-}
-
-/*
-==================
-S_AddLoopingSound
-
-Called during entity generation for a frame
-Include velocity in case I get around to doing doppler...
-==================
-*/
-void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
- sfx_t *sfx;
-
- if ( !s_soundStarted || s_soundMuted ) {
- return;
- }
-
- if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
- Com_Printf( S_COLOR_YELLOW, "S_AddLoopingSound: handle %i out of range\n", sfxHandle );
- return;
- }
-
- sfx = &s_knownSfx[ sfxHandle ];
-
- if (sfx->inMemory == qfalse) {
- S_memoryLoad(sfx);
- }
-
- if ( !sfx->soundLength ) {
- Com_Error( ERR_DROP, "%s has length 0", sfx->soundName );
- }
-
- VectorCopy( origin, loopSounds[entityNum].origin );
- VectorCopy( velocity, loopSounds[entityNum].velocity );
- loopSounds[entityNum].active = qtrue;
- loopSounds[entityNum].kill = qtrue;
- loopSounds[entityNum].doppler = qfalse;
- loopSounds[entityNum].oldDopplerScale = 1.0;
- loopSounds[entityNum].dopplerScale = 1.0;
- loopSounds[entityNum].sfx = sfx;
-
- if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) {
- vec3_t out;
- float lena, lenb;
-
- loopSounds[entityNum].doppler = qtrue;
- lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin);
- VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out);
- lenb = DistanceSquared(loopSounds[listener_number].origin, out);
- if ((loopSounds[entityNum].framenum+1) != cls.framecount) {
- loopSounds[entityNum].oldDopplerScale = 1.0;
- } else {
- loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale;
- }
- loopSounds[entityNum].dopplerScale = lenb/(lena*100);
- if (loopSounds[entityNum].dopplerScale<=1.0) {
- loopSounds[entityNum].doppler = qfalse; // don't bother doing the math
- }
- }
-
- loopSounds[entityNum].framenum = cls.framecount;
-}
-
-/*
-==================
-S_AddLoopingSound
-
-Called during entity generation for a frame
-Include velocity in case I get around to doing doppler...
-==================
-*/
-void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
- sfx_t *sfx;
-
- if ( !s_soundStarted || s_soundMuted ) {
- return;
- }
-
- if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
- Com_Printf( S_COLOR_YELLOW, "S_AddRealLoopingSound: handle %i out of range\n", sfxHandle );
- return;
- }
-
- sfx = &s_knownSfx[ sfxHandle ];
-
- if (sfx->inMemory == qfalse) {
- S_memoryLoad(sfx);
- }
-
- if ( !sfx->soundLength ) {
- Com_Error( ERR_DROP, "%s has length 0", sfx->soundName );
- }
- VectorCopy( origin, loopSounds[entityNum].origin );
- VectorCopy( velocity, loopSounds[entityNum].velocity );
- loopSounds[entityNum].sfx = sfx;
- loopSounds[entityNum].active = qtrue;
- loopSounds[entityNum].kill = qfalse;
- loopSounds[entityNum].doppler = qfalse;
-}
-
-
-
-/*
-==================
-S_AddLoopSounds
-
-Spatialize all of the looping sounds.
-All sounds are on the same cycle, so any duplicates can just
-sum up the channel multipliers.
-==================
-*/
-void S_AddLoopSounds (void) {
- int i, j, time;
- int left_total, right_total, left, right;
- channel_t *ch;
- loopSound_t *loop, *loop2;
- static int loopFrame;
-
-
- numLoopChannels = 0;
-
- time = Com_Milliseconds();
-
- loopFrame++;
- for ( i = 0 ; i < MAX_GENTITIES ; i++) {
- loop = &loopSounds[i];
- if ( !loop->active || loop->mergeFrame == loopFrame ) {
- continue; // already merged into an earlier sound
- }
-
- if (loop->kill) {
- S_SpatializeOrigin( loop->origin, 127, &left_total, &right_total); // 3d
- } else {
- S_SpatializeOrigin( loop->origin, 90, &left_total, &right_total); // sphere
- }
-
- loop->sfx->lastTimeUsed = time;
-
- for (j=(i+1); j< MAX_GENTITIES ; j++) {
- loop2 = &loopSounds[j];
- if ( !loop2->active || loop2->doppler || loop2->sfx != loop->sfx) {
- continue;
- }
- loop2->mergeFrame = loopFrame;
-
- if (loop2->kill) {
- S_SpatializeOrigin( loop2->origin, 127, &left, &right); // 3d
- } else {
- S_SpatializeOrigin( loop2->origin, 90, &left, &right); // sphere
- }
-
- loop2->sfx->lastTimeUsed = time;
- left_total += left;
- right_total += right;
- }
- if (left_total == 0 && right_total == 0) {
- continue; // not audible
- }
-
- // allocate a channel
- ch = &loop_channels[numLoopChannels];
-
- if (left_total > 255) {
- left_total = 255;
- }
- if (right_total > 255) {
- right_total = 255;
- }
-
- ch->master_vol = 127;
- ch->leftvol = left_total;
- ch->rightvol = right_total;
- ch->thesfx = loop->sfx;
- ch->doppler = loop->doppler;
- ch->dopplerScale = loop->dopplerScale;
- ch->oldDopplerScale = loop->oldDopplerScale;
- numLoopChannels++;
- if (numLoopChannels == MAX_CHANNELS) {
- return;
- }
- }
-}
-
-//=============================================================================
-
-/*
-=================
-S_ByteSwapRawSamples
-
-If raw data has been loaded in little endien binary form, this must be done.
-If raw data was calculated, as with ADPCM, this should not be called.
-=================
-*/
-void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
- int i;
-
- if ( width != 2 ) {
- return;
- }
- if ( LittleShort( 256 ) == 256 ) {
- return;
- }
-
- if ( s_channels == 2 ) {
- samples <<= 1;
- }
- for ( i = 0 ; i < samples ; i++ ) {
- ((short *)data)[i] = LittleShort( ((short *)data)[i] );
- }
-}
-
-portable_samplepair_t *S_GetRawSamplePointer() {
- return s_rawsamples;
-}
-
-/*
-============
-S_RawSamples
-
-Music streaming
-============
-*/
-void S_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
- int i;
- int src, dst;
- float scale;
- int intVolume;
-
- if ( !s_soundStarted || s_soundMuted ) {
- return;
- }
-
- intVolume = 256 * volume;
-
- if ( s_rawend < s_soundtime ) {
- Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend, s_soundtime );
- s_rawend = s_soundtime;
- }
-
- scale = (float)rate / dma.speed;
-
-//Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend);
- if (s_channels == 2 && width == 2)
- {
- if (scale == 1.0)
- { // optimized case
- for (i=0 ; i<samples ; i++)
- {
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
- s_rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
- }
- }
- else
- {
- for (i=0 ; ; i++)
- {
- src = i*scale;
- if (src >= samples)
- break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
- s_rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
- }
- }
- }
- else if (s_channels == 1 && width == 2)
- {
- for (i=0 ; ; i++)
- {
- src = i*scale;
- if (src >= samples)
- break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((short *)data)[src] * intVolume;
- s_rawsamples[dst].right = ((short *)data)[src] * intVolume;
- }
- }
- else if (s_channels == 2 && width == 1)
- {
- intVolume *= 256;
-
- for (i=0 ; ; i++)
- {
- src = i*scale;
- if (src >= samples)
- break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
- s_rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
- }
- }
- else if (s_channels == 1 && width == 1)
- {
- intVolume *= 256;
-
- for (i=0 ; ; i++)
- {
- src = i*scale;
- if (src >= samples)
- break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
- s_rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
- }
- }
-
- if ( s_rawend > s_soundtime + MAX_RAW_SAMPLES ) {
- Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend, s_soundtime );
- }
-}
-
-//=============================================================================
-
-/*
-=====================
-S_UpdateEntityPosition
-
-let the sound system know where an entity currently is
-======================
-*/
-void S_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
- if ( entityNum < 0 || entityNum > MAX_GENTITIES ) {
- Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
- }
- VectorCopy( origin, loopSounds[entityNum].origin );
-}
-
-
-/*
-============
-S_Respatialize
-
-Change the volumes of all the playing sounds for changes in their positions
-============
-*/
-void S_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) {
- int i;
- channel_t *ch;
- vec3_t origin;
-
- if ( !s_soundStarted || s_soundMuted ) {
- return;
- }
-
- listener_number = entityNum;
- VectorCopy(head, listener_origin);
- VectorCopy(axis[0], listener_axis[0]);
- VectorCopy(axis[1], listener_axis[1]);
- VectorCopy(axis[2], listener_axis[2]);
-
- // update spatialization for dynamic sounds
- ch = s_channels;
- for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
- if ( !ch->thesfx ) {
- continue;
- }
- // anything coming from the view entity will always be full volume
- if (ch->entnum == listener_number) {
- ch->leftvol = ch->master_vol;
- ch->rightvol = ch->master_vol;
- } else {
- if (ch->fixed_origin) {
- VectorCopy( ch->origin, origin );
- } else {
- VectorCopy( loopSounds[ ch->entnum ].origin, origin );
- }
-
- S_SpatializeOrigin (origin, ch->master_vol, &ch->leftvol, &ch->rightvol);
- }
- }
-
- // add loopsounds
- S_AddLoopSounds ();
-}
-
-
-/*
-========================
-S_ScanChannelStarts
-
-Returns qtrue if any new sounds were started since the last mix
-========================
-*/
-qboolean S_ScanChannelStarts( void ) {
- channel_t *ch;
- int i;
- qboolean newSamples;
-
- newSamples = qfalse;
- ch = s_channels;
-
- for (i=0; i<MAX_CHANNELS ; i++, ch++) {
- if ( !ch->thesfx ) {
- continue;
- }
- // if this channel was just started this frame,
- // set the sample count to it begins mixing
- // into the very first sample
- if ( ch->startSample == START_SAMPLE_IMMEDIATE ) {
- ch->startSample = s_paintedtime;
- newSamples = qtrue;
- continue;
- }
-
- // if it is completely finished by now, clear it
- if ( ch->startSample + (ch->thesfx->soundLength) <= s_paintedtime ) {
- S_ChannelFree(ch);
- }
- }
-
- return newSamples;
-}
-
-/*
-============
-S_Update
-
-Called once each time through the main loop
-============
-*/
-void S_Update( void ) {
- int i;
- int total;
- channel_t *ch;
-
- if ( !s_soundStarted || s_soundMuted ) {
- Com_DPrintf ("not started or muted\n");
- return;
- }
-
- //
- // debugging output
- //
- if ( s_show->integer == 2 ) {
- total = 0;
- ch = s_channels;
- for (i=0 ; i<MAX_CHANNELS; i++, ch++) {
- if (ch->thesfx && (ch->leftvol || ch->rightvol) ) {
- Com_Printf ("%f %f %s\n", ch->leftvol, ch->rightvol, ch->thesfx->soundName);
- total++;
- }
- }
-
- Com_Printf ("----(%i)---- painted: %i\n", total, s_paintedtime);
- }
-
- // add raw data from streamed samples
- S_UpdateBackgroundTrack();
-
- // mix some sound
- S_Update_();
-}
-
-void S_GetSoundtime(void)
-{
- int samplepos;
- static int buffers;
- static int oldsamplepos;
- int fullsamples;
-
- fullsamples = dma.samples / dma.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 (s_paintedtime > 0x40000000)
- { // time to chop things off to avoid 32 bit limits
- buffers = 0;
- s_paintedtime = fullsamples;
- S_StopAllSounds ();
- }
- }
- oldsamplepos = samplepos;
-
- s_soundtime = buffers*fullsamples + samplepos/dma.channels;
-
-#if 0
-// check to make sure that we haven't overshot
- if (s_paintedtime < s_soundtime)
- {
- Com_DPrintf ("S_Update_ : overflow\n");
- s_paintedtime = s_soundtime;
- }
-#endif
-
- if ( dma.submission_chunk < 256 ) {
- s_paintedtime = s_soundtime + s_mixPreStep->value * dma.speed;
- } else {
- s_paintedtime = s_soundtime + dma.submission_chunk;
- }
-}
-
-
-void S_Update_(void) {
- unsigned endtime;
- int samps;
- static float lastTime = 0.0f;
- float ma, op;
- float thisTime, sane;
- static int ot = -1;
-
- if ( !s_soundStarted || s_soundMuted ) {
- return;
- }
-
- thisTime = Com_Milliseconds();
-
- // Updates s_soundtime
- S_GetSoundtime();
-
- if (s_soundtime == ot) {
- return;
- }
- ot = s_soundtime;
-
- // clear any sound effects that end before the current time,
- // and start any new sounds
- S_ScanChannelStarts();
-
- sane = thisTime - lastTime;
- if (sane<11) {
- sane = 11; // 85hz
- }
-
- ma = s_mixahead->value * dma.speed;
- op = s_mixPreStep->value + sane*dma.speed*0.01;
-
- if (op < ma) {
- ma = op;
- }
-
- // mix ahead of current position
- endtime = s_soundtime + ma;
-
- // mix to an even submission block size
- endtime = (endtime + dma.submission_chunk-1)
- & ~(dma.submission_chunk-1);
-
- // never mix more than the complete buffer
- samps = dma.samples >> (dma.channels-1);
- if (endtime - s_soundtime > samps)
- endtime = s_soundtime + samps;
-
-
-
- SNDDMA_BeginPainting ();
-
- S_PaintChannels (endtime);
-
- SNDDMA_Submit ();
-
- lastTime = thisTime;
-}
-
-/*
-===============================================================================
-
-console functions
-
-===============================================================================
-*/
-
-void S_Play_f( void ) {
- int i;
- sfxHandle_t h;
- char name[256];
-
- i = 1;
- while ( i<Cmd_Argc() ) {
- if ( !Q_strrchr(Cmd_Argv(i), '.') ) {
- Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
- } else {
- Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
- }
- h = S_RegisterSound( name, qfalse );
- if( h ) {
- S_StartLocalSound( h, CHAN_LOCAL_SOUND );
- }
- i++;
- }
-}
-
-void S_Music_f( void ) {
- int c;
-
- c = Cmd_Argc();
-
- if ( c == 2 ) {
- S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(1) );
- s_backgroundLoop[0] = 0;
- } else if ( c == 3 ) {
- S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) );
- } else {
- Com_Printf ("music <musicfile> [loopfile]\n");
- return;
- }
-
-}
-
-void S_SoundList_f( void ) {
- int i;
- sfx_t *sfx;
- int size, total;
- char type[4][16];
- char mem[2][16];
-
- strcpy(type[0], "16bit");
- strcpy(type[1], "adpcm");
- strcpy(type[2], "daub4");
- strcpy(type[3], "mulaw");
- strcpy(mem[0], "paged out");
- strcpy(mem[1], "resident ");
- total = 0;
- for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++) {
- size = sfx->soundLength;
- total += size;
- Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], sfx->soundName, mem[sfx->inMemory] );
- }
- Com_Printf ("Total resident: %i\n", total);
- S_DisplayFreeMemory();
-}
-
-
-/*
-===============================================================================
-
-background music functions
-
-===============================================================================
-*/
-
-int FGetLittleLong( fileHandle_t f ) {
- int v;
-
- FS_Read( &v, sizeof(v), f );
-
- return LittleLong( v);
-}
-
-int FGetLittleShort( fileHandle_t f ) {
- short v;
-
- FS_Read( &v, sizeof(v), f );
-
- return LittleShort( v);
-}
-
-// returns the length of the data in the chunk, or 0 if not found
-int S_FindWavChunk( fileHandle_t f, char *chunk ) {
- char name[5];
- int len;
- int r;
-
- name[4] = 0;
- len = 0;
- r = FS_Read( name, 4, f );
- if ( r != 4 ) {
- return 0;
- }
- len = FGetLittleLong( f );
- if ( len < 0 || len > 0xfffffff ) {
- len = 0;
- return 0;
- }
- len = (len + 1 ) & ~1; // pad to word boundary
-// s_nextWavChunk += len + 8;
-
- if ( strcmp( name, chunk ) ) {
- return 0;
- }
-
- return len;
-}
-
-/*
-======================
-S_StopBackgroundTrack
-======================
-*/
-void S_StopBackgroundTrack( void ) {
- if ( !s_backgroundFile ) {
- return;
- }
- Sys_EndStreamedFile( s_backgroundFile );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- s_rawend = 0;
-}
-
-/*
-======================
-S_StartBackgroundTrack
-======================
-*/
-void S_StartBackgroundTrack( const char *intro, const char *loop ){
- int len;
- char dump[16];
- char name[MAX_QPATH];
-
- if ( !intro ) {
- intro = "";
- }
- if ( !loop || !loop[0] ) {
- loop = intro;
- }
- Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop );
-
- Q_strncpyz( name, intro, sizeof( name ) - 4 );
- COM_DefaultExtension( name, sizeof( name ), ".wav" );
-
- if ( !intro[0] ) {
- return;
- }
-
- Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
-
- // close the background track, but DON'T reset s_rawend
- // if restarting the same back ground track
- if ( s_backgroundFile ) {
- Sys_EndStreamedFile( s_backgroundFile );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- }
-
- //
- // open up a wav file and get all the info
- //
- FS_FOpenFileRead( name, &s_backgroundFile, qtrue );
- if ( !s_backgroundFile ) {
- Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", name );
- return;
- }
-
- // skip the riff wav header
-
- FS_Read(dump, 12, s_backgroundFile);
-
- if ( !S_FindWavChunk( s_backgroundFile, "fmt " ) ) {
- Com_Printf( "No fmt chunk in %s\n", name );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- return;
- }
-
- // save name for soundinfo
- s_backgroundInfo.format = FGetLittleShort( s_backgroundFile );
- s_backgroundInfo.channels = FGetLittleShort( s_backgroundFile );
- s_backgroundInfo.rate = FGetLittleLong( s_backgroundFile );
- FGetLittleLong( s_backgroundFile );
- FGetLittleShort( s_backgroundFile );
- s_backgroundInfo.width = FGetLittleShort( s_backgroundFile ) / 8;
-
- if ( s_backgroundInfo.format != WAV_FORMAT_PCM ) {
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- Com_Printf("Not a microsoft PCM format wav: %s\n", name);
- return;
- }
-
- if ( s_backgroundInfo.channels != 2 || s_backgroundInfo.rate != 22050 ) {
- Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", name );
- }
-
- if ( ( len = S_FindWavChunk( s_backgroundFile, "data" ) ) == 0 ) {
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- Com_Printf("No data chunk in %s\n", name);
- return;
- }
-
- s_backgroundInfo.samples = len / (s_backgroundInfo.width * s_backgroundInfo.channels);
-
- s_backgroundSamples = s_backgroundInfo.samples;
-
- //
- // start the background streaming
- //
- Sys_BeginStreamedFile( s_backgroundFile, 0x10000 );
-}
-
-/*
-======================
-S_UpdateBackgroundTrack
-======================
-*/
-void S_UpdateBackgroundTrack( void ) {
- int bufferSamples;
- int fileSamples;
- byte raw[30000]; // just enough to fit in a mac stack frame
- int fileBytes;
- int r;
- static float musicVolume = 0.5f;
-
- if ( !s_backgroundFile ) {
- return;
- }
-
- // graeme see if this is OK
- musicVolume = (musicVolume + (s_musicVolume->value * 2))/4.0f;
-
- // don't bother playing anything if musicvolume is 0
- if ( musicVolume <= 0 ) {
- return;
- }
-
- // see how many samples should be copied into the raw buffer
- if ( s_rawend < s_soundtime ) {
- s_rawend = s_soundtime;
- }
-
- while ( s_rawend < s_soundtime + MAX_RAW_SAMPLES ) {
- bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime);
-
- // decide how much data needs to be read from the file
- fileSamples = bufferSamples * s_backgroundInfo.rate / dma.speed;
-
- // don't try and read past the end of the file
- if ( fileSamples > s_backgroundSamples ) {
- fileSamples = s_backgroundSamples;
- }
-
- // our max buffer size
- fileBytes = fileSamples * (s_backgroundInfo.width * s_backgroundInfo.channels);
- if ( fileBytes > sizeof(raw) ) {
- fileBytes = sizeof(raw);
- fileSamples = fileBytes / (s_backgroundInfo.width * s_backgroundInfo.channels);
- }
-
- r = Sys_StreamedRead( raw, 1, fileBytes, s_backgroundFile );
- if ( r != fileBytes ) {
- Com_Printf("StreamedRead failure on music track\n");
- S_StopBackgroundTrack();
- return;
- }
-
- // byte swap if needed
- S_ByteSwapRawSamples( fileSamples, s_backgroundInfo.width, s_backgroundInfo.channels, raw );
-
- // add to raw buffer
- S_RawSamples( fileSamples, s_backgroundInfo.rate,
- s_backgroundInfo.width, s_backgroundInfo.channels, raw, musicVolume );
-
- s_backgroundSamples -= fileSamples;
- if ( !s_backgroundSamples ) {
- // loop
- if (s_backgroundLoop[0]) {
- Sys_EndStreamedFile( s_backgroundFile );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- S_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop );
- if ( !s_backgroundFile ) {
- return; // loop failed to restart
- }
- } else {
- s_backgroundFile = 0;
- return;
- }
- }
- }
-}
-
-
-/*
-======================
-S_FreeOldestSound
-======================
-*/
-
-void S_FreeOldestSound() {
- int i, oldest, used;
- sfx_t *sfx;
- sndBuffer *buffer, *nbuffer;
-
- oldest = Com_Milliseconds();
- used = 0;
-
- for (i=1 ; i < s_numSfx ; i++) {
- sfx = &s_knownSfx[i];
- if (sfx->inMemory && sfx->lastTimeUsed<oldest) {
- used = i;
- oldest = sfx->lastTimeUsed;
- }
- }
-
- sfx = &s_knownSfx[used];
-
- Com_DPrintf("S_FreeOldestSound: freeing sound %s\n", sfx->soundName);
-
- buffer = sfx->soundData;
- while(buffer != NULL) {
- nbuffer = buffer->next;
- SND_free(buffer);
- buffer = nbuffer;
- }
- sfx->inMemory = qfalse;
- sfx->soundData = NULL;
-}
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code 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. + +Quake III Arena source code 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: snd_dma.c + * + * desc: main control for any streaming sound output device + * + * $Archive: /MissionPack/code/client/snd_dma.c $ + * + *****************************************************************************/ + +#include "snd_local.h" +#include "client.h" + +void S_Play_f(void); +void S_SoundList_f(void); +void S_Music_f(void); + +void S_Update_(); +void S_StopAllSounds(void); +void S_UpdateBackgroundTrack( void ); + +static fileHandle_t s_backgroundFile; +static wavinfo_t s_backgroundInfo; +//int s_nextWavChunk; +static int s_backgroundSamples; +static char s_backgroundLoop[MAX_QPATH]; +//static char s_backgroundMusic[MAX_QPATH]; //TTimo: unused + + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +// only begin attenuating sound volumes when outside the FULLVOLUME range +#define SOUND_FULLVOLUME 80 + +#define SOUND_ATTENUATE 0.0008f + +channel_t s_channels[MAX_CHANNELS]; +channel_t loop_channels[MAX_CHANNELS]; +int numLoopChannels; + +static int s_soundStarted; +static qboolean s_soundMuted; + +dma_t dma; + +static int listener_number; +static vec3_t listener_origin; +static vec3_t listener_axis[3]; + +int s_soundtime; // sample PAIRS +int s_paintedtime; // sample PAIRS + +// MAX_SFX may be larger than MAX_SOUNDS because +// of custom player sounds +#define MAX_SFX 4096 +sfx_t s_knownSfx[MAX_SFX]; +int s_numSfx = 0; + +#define LOOP_HASH 128 +static sfx_t *sfxHash[LOOP_HASH]; + +cvar_t *s_volume; +cvar_t *s_testsound; +cvar_t *s_khz; +cvar_t *s_show; +cvar_t *s_mixahead; +cvar_t *s_mixPreStep; +cvar_t *s_musicVolume; +cvar_t *s_separation; +cvar_t *s_doppler; + +static loopSound_t loopSounds[MAX_GENTITIES]; +static channel_t *freelist = NULL; + +int s_rawend; +portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; + + +// ==================================================================== +// User-setable variables +// ==================================================================== + + +void S_SoundInfo_f(void) { + Com_Printf("----- Sound Info -----\n" ); + if (!s_soundStarted) { + Com_Printf ("sound system not started\n"); + } else { + if ( s_soundMuted ) { + Com_Printf ("sound system is muted\n"); + } + + Com_Printf("%5d stereo\n", dma.channels - 1); + Com_Printf("%5d samples\n", dma.samples); + Com_Printf("%5d samplebits\n", dma.samplebits); + Com_Printf("%5d submission_chunk\n", dma.submission_chunk); + Com_Printf("%5d speed\n", dma.speed); + Com_Printf("0x%x dma buffer\n", dma.buffer); + if ( s_backgroundFile ) { + Com_Printf("Background file: %s\n", s_backgroundLoop ); + } else { + Com_Printf("No background file.\n" ); + } + + } + Com_Printf("----------------------\n" ); +} + + + +/* +================ +S_Init +================ +*/ +void S_Init( void ) { + cvar_t *cv; + qboolean r; + + Com_Printf("\n------- sound initialization -------\n"); + + s_volume = Cvar_Get ("s_volume", "0.8", CVAR_ARCHIVE); + s_musicVolume = Cvar_Get ("s_musicvolume", "0.25", CVAR_ARCHIVE); + s_separation = Cvar_Get ("s_separation", "0.5", CVAR_ARCHIVE); + s_doppler = Cvar_Get ("s_doppler", "1", CVAR_ARCHIVE); + s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE); + s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE); + + s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE); + s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT); + s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT); + + cv = Cvar_Get ("s_initsound", "1", 0); + if ( !cv->integer ) { + Com_Printf ("not initializing.\n"); + Com_Printf("------------------------------------\n"); + return; + } + + Cmd_AddCommand("play", S_Play_f); + Cmd_AddCommand("music", S_Music_f); + Cmd_AddCommand("s_list", S_SoundList_f); + Cmd_AddCommand("s_info", S_SoundInfo_f); + Cmd_AddCommand("s_stop", S_StopAllSounds); + + r = SNDDMA_Init(); + Com_Printf("------------------------------------\n"); + + if ( r ) { + s_soundStarted = 1; + s_soundMuted = 1; +// s_numSfx = 0; + + Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH); + + s_soundtime = 0; + s_paintedtime = 0; + + S_StopAllSounds (); + + S_SoundInfo_f(); + } + +} + + +void S_ChannelFree(channel_t *v) { + v->thesfx = NULL; + *(channel_t **)v = freelist; + freelist = (channel_t*)v; +} + +channel_t* S_ChannelMalloc() { + channel_t *v; + if (freelist == NULL) { + return NULL; + } + v = freelist; + freelist = *(channel_t **)freelist; + v->allocTime = Com_Milliseconds(); + return v; +} + +void S_ChannelSetup() { + channel_t *p, *q; + + // clear all the sounds so they don't + Com_Memset( s_channels, 0, sizeof( s_channels ) ); + + p = s_channels;; + q = p + MAX_CHANNELS; + while (--q > p) { + *(channel_t **)q = q-1; + } + + *(channel_t **)q = NULL; + freelist = p + MAX_CHANNELS - 1; + Com_DPrintf("Channel memory manager started\n"); +} + +// ======================================================================= +// Shutdown sound engine +// ======================================================================= + +void S_Shutdown( void ) { + if ( !s_soundStarted ) { + return; + } + + SNDDMA_Shutdown(); + + s_soundStarted = 0; + + Cmd_RemoveCommand("play"); + Cmd_RemoveCommand("music"); + Cmd_RemoveCommand("stopsound"); + Cmd_RemoveCommand("soundlist"); + Cmd_RemoveCommand("soundinfo"); +} + + +// ======================================================================= +// Load a sound +// ======================================================================= + +/* +================ +return a hash value for the sfx name +================ +*/ +static long S_HashSFXName(const char *name) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while (name[i] != '\0') { + letter = tolower(name[i]); + if (letter =='.') break; // don't include extension + if (letter =='\\') letter = '/'; // damn path names + hash+=(long)(letter)*(i+119); + i++; + } + hash &= (LOOP_HASH-1); + return hash; +} + +/* +================== +S_FindName + +Will allocate a new sfx if it isn't found +================== +*/ +static sfx_t *S_FindName( const char *name ) { + int i; + int hash; + + sfx_t *sfx; + + if (!name) { + Com_Error (ERR_FATAL, "S_FindName: NULL\n"); + } + if (!name[0]) { + Com_Error (ERR_FATAL, "S_FindName: empty name\n"); + } + + if (strlen(name) >= MAX_QPATH) { + Com_Error (ERR_FATAL, "Sound name too long: %s", name); + } + + hash = S_HashSFXName(name); + + sfx = sfxHash[hash]; + // see if already loaded + while (sfx) { + if (!Q_stricmp(sfx->soundName, name) ) { + return sfx; + } + sfx = sfx->next; + } + + // find a free sfx + for (i=0 ; i < s_numSfx ; i++) { + if (!s_knownSfx[i].soundName[0]) { + break; + } + } + + if (i == s_numSfx) { + if (s_numSfx == MAX_SFX) { + Com_Error (ERR_FATAL, "S_FindName: out of sfx_t"); + } + s_numSfx++; + } + + sfx = &s_knownSfx[i]; + Com_Memset (sfx, 0, sizeof(*sfx)); + strcpy (sfx->soundName, name); + + sfx->next = sfxHash[hash]; + sfxHash[hash] = sfx; + + return sfx; +} + +/* +================= +S_DefaultSound +================= +*/ +void S_DefaultSound( sfx_t *sfx ) { + + int i; + + sfx->soundLength = 512; + sfx->soundData = SND_malloc(); + sfx->soundData->next = NULL; + + + for ( i = 0 ; i < sfx->soundLength ; i++ ) { + sfx->soundData->sndChunk[i] = i; + } +} + +/* +=================== +S_DisableSounds + +Disables sounds until the next S_BeginRegistration. +This is called when the hunk is cleared and the sounds +are no longer valid. +=================== +*/ +void S_DisableSounds( void ) { + S_StopAllSounds(); + s_soundMuted = qtrue; +} + +/* +===================== +S_BeginRegistration + +===================== +*/ +void S_BeginRegistration( void ) { + s_soundMuted = qfalse; // we can play again + + if (s_numSfx == 0) { + SND_setup(); + + s_numSfx = 0; + Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) ); + Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH); + + S_RegisterSound("sound/feedback/hit.wav", qfalse); // changed to a sound in baseq3 + } +} + + +/* +================== +S_RegisterSound + +Creates a default buzz sound if the file can't be loaded +================== +*/ +sfxHandle_t S_RegisterSound( const char *name, qboolean compressed ) { + sfx_t *sfx; + + compressed = qfalse; + if (!s_soundStarted) { + return 0; + } + + if ( strlen( name ) >= MAX_QPATH ) { + Com_Printf( "Sound name exceeds MAX_QPATH\n" ); + return 0; + } + + sfx = S_FindName( name ); + if ( sfx->soundData ) { + if ( sfx->defaultSound ) { + Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); + return 0; + } + return sfx - s_knownSfx; + } + + sfx->inMemory = qfalse; + sfx->soundCompressed = compressed; + + S_memoryLoad(sfx); + + if ( sfx->defaultSound ) { + Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); + return 0; + } + + return sfx - s_knownSfx; +} + +void S_memoryLoad(sfx_t *sfx) { + // load the sound file + if ( !S_LoadSound ( sfx ) ) { +// Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->soundName ); + sfx->defaultSound = qtrue; + } + sfx->inMemory = qtrue; +} + +//============================================================================= + +/* +================= +S_SpatializeOrigin + +Used for spatializing s_channels +================= +*/ +void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *right_vol) +{ + vec_t dot; + vec_t dist; + vec_t lscale, rscale, scale; + vec3_t source_vec; + vec3_t vec; + + const float dist_mult = SOUND_ATTENUATE; + + // calculate stereo seperation and distance attenuation + VectorSubtract(origin, listener_origin, source_vec); + + dist = VectorNormalize(source_vec); + dist -= SOUND_FULLVOLUME; + if (dist < 0) + dist = 0; // close enough to be at full volume + dist *= dist_mult; // different attenuation levels + + VectorRotate( source_vec, listener_axis, vec ); + + dot = -vec[1]; + + if (dma.channels == 1) + { // no attenuation = no spatialization + rscale = 1.0; + lscale = 1.0; + } + else + { + rscale = 0.5 * (1.0 + dot); + lscale = 0.5 * (1.0 - dot); + //rscale = s_separation->value + ( 1.0 - s_separation->value ) * dot; + //lscale = s_separation->value - ( 1.0 - s_separation->value ) * dot; + if ( rscale < 0 ) { + rscale = 0; + } + if ( lscale < 0 ) { + lscale = 0; + } + } + + // add in distance effect + scale = (1.0 - dist) * rscale; + *right_vol = (master_vol * scale); + if (*right_vol < 0) + *right_vol = 0; + + scale = (1.0 - dist) * lscale; + *left_vol = (master_vol * scale); + if (*left_vol < 0) + *left_vol = 0; +} + +// ======================================================================= +// Start a sound effect +// ======================================================================= + +/* +==================== +S_StartSound + +Validates the parms and ques the sound up +if pos is NULL, the sound will be dynamically sourced from the entity +Entchannel 0 will never override a playing sound +==================== +*/ +void S_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) { + channel_t *ch; + sfx_t *sfx; + int i, oldest, chosen, time; + int inplay, allowed; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( !origin && ( entityNum < 0 || entityNum > MAX_GENTITIES ) ) { + Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum ); + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW, "S_StartSound: handle %i out of range\n", sfxHandle ); + return; + } + + sfx = &s_knownSfx[ sfxHandle ]; + + if (sfx->inMemory == qfalse) { + S_memoryLoad(sfx); + } + + if ( s_show->integer == 1 ) { + Com_Printf( "%i : %s\n", s_paintedtime, sfx->soundName ); + } + + time = Com_Milliseconds(); + +// Com_Printf("playing %s\n", sfx->soundName); + // pick a channel to play on + + allowed = 4; + if (entityNum == listener_number) { + allowed = 8; + } + + ch = s_channels; + inplay = 0; + for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch[i].entnum == entityNum && ch[i].thesfx == sfx) { + if (time - ch[i].allocTime < 50) { +// if (Cvar_VariableValue( "cg_showmiss" )) { +// Com_Printf("double sound start\n"); +// } + return; + } + inplay++; + } + } + + if (inplay>allowed) { + return; + } + + sfx->lastTimeUsed = time; + + ch = S_ChannelMalloc(); // entityNum, entchannel); + if (!ch) { + ch = s_channels; + + oldest = sfx->lastTimeUsed; + chosen = -1; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch->entnum != listener_number && ch->entnum == entityNum && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) { + oldest = ch->allocTime; + chosen = i; + } + } + if (chosen == -1) { + ch = s_channels; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch->entnum != listener_number && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) { + oldest = ch->allocTime; + chosen = i; + } + } + if (chosen == -1) { + if (ch->entnum == listener_number) { + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if (ch->allocTime<oldest) { + oldest = ch->allocTime; + chosen = i; + } + } + } + if (chosen == -1) { + Com_Printf("dropping sound\n"); + return; + } + } + } + ch = &s_channels[chosen]; + ch->allocTime = sfx->lastTimeUsed; + } + + if (origin) { + VectorCopy (origin, ch->origin); + ch->fixed_origin = qtrue; + } else { + ch->fixed_origin = qfalse; + } + + ch->master_vol = 127; + ch->entnum = entityNum; + ch->thesfx = sfx; + ch->startSample = START_SAMPLE_IMMEDIATE; + ch->entchannel = entchannel; + ch->leftvol = ch->master_vol; // these will get calced at next spatialize + ch->rightvol = ch->master_vol; // unless the game isn't running + ch->doppler = qfalse; +} + + +/* +================== +S_StartLocalSound +================== +*/ +void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) { + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW, "S_StartLocalSound: handle %i out of range\n", sfxHandle ); + return; + } + + S_StartSound (NULL, listener_number, channelNum, sfxHandle ); +} + + +/* +================== +S_ClearSoundBuffer + +If we are about to perform file access, clear the buffer +so sound doesn't stutter. +================== +*/ +void S_ClearSoundBuffer( void ) { + int clear; + + if (!s_soundStarted) + return; + + // stop looping sounds + Com_Memset(loopSounds, 0, MAX_GENTITIES*sizeof(loopSound_t)); + Com_Memset(loop_channels, 0, MAX_CHANNELS*sizeof(channel_t)); + numLoopChannels = 0; + + S_ChannelSetup(); + + s_rawend = 0; + + if (dma.samplebits == 8) + clear = 0x80; + else + clear = 0; + + SNDDMA_BeginPainting (); + if (dma.buffer) + // TTimo: due to a particular bug workaround in linux sound code, + // have to optionally use a custom C implementation of Com_Memset + // not affecting win32, we have #define Snd_Memset Com_Memset + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 + Snd_Memset(dma.buffer, clear, dma.samples * dma.samplebits/8); + SNDDMA_Submit (); +} + +/* +================== +S_StopAllSounds +================== +*/ +void S_StopAllSounds(void) { + if ( !s_soundStarted ) { + return; + } + + // stop the background music + S_StopBackgroundTrack(); + + S_ClearSoundBuffer (); +} + +/* +============================================================== + +continuous looping sounds are added each frame + +============================================================== +*/ + +void S_StopLoopingSound(int entityNum) { + loopSounds[entityNum].active = qfalse; +// loopSounds[entityNum].sfx = 0; + loopSounds[entityNum].kill = qfalse; +} + +/* +================== +S_ClearLoopingSounds + +================== +*/ +void S_ClearLoopingSounds( qboolean killall ) { + int i; + for ( i = 0 ; i < MAX_GENTITIES ; i++) { + if (killall || loopSounds[i].kill == qtrue || (loopSounds[i].sfx && loopSounds[i].sfx->soundLength == 0)) { + loopSounds[i].kill = qfalse; + S_StopLoopingSound(i); + } + } + numLoopChannels = 0; +} + +/* +================== +S_AddLoopingSound + +Called during entity generation for a frame +Include velocity in case I get around to doing doppler... +================== +*/ +void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { + sfx_t *sfx; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW, "S_AddLoopingSound: handle %i out of range\n", sfxHandle ); + return; + } + + sfx = &s_knownSfx[ sfxHandle ]; + + if (sfx->inMemory == qfalse) { + S_memoryLoad(sfx); + } + + if ( !sfx->soundLength ) { + Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); + } + + VectorCopy( origin, loopSounds[entityNum].origin ); + VectorCopy( velocity, loopSounds[entityNum].velocity ); + loopSounds[entityNum].active = qtrue; + loopSounds[entityNum].kill = qtrue; + loopSounds[entityNum].doppler = qfalse; + loopSounds[entityNum].oldDopplerScale = 1.0; + loopSounds[entityNum].dopplerScale = 1.0; + loopSounds[entityNum].sfx = sfx; + + if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) { + vec3_t out; + float lena, lenb; + + loopSounds[entityNum].doppler = qtrue; + lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin); + VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out); + lenb = DistanceSquared(loopSounds[listener_number].origin, out); + if ((loopSounds[entityNum].framenum+1) != cls.framecount) { + loopSounds[entityNum].oldDopplerScale = 1.0; + } else { + loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale; + } + loopSounds[entityNum].dopplerScale = lenb/(lena*100); + if (loopSounds[entityNum].dopplerScale<=1.0) { + loopSounds[entityNum].doppler = qfalse; // don't bother doing the math + } + } + + loopSounds[entityNum].framenum = cls.framecount; +} + +/* +================== +S_AddLoopingSound + +Called during entity generation for a frame +Include velocity in case I get around to doing doppler... +================== +*/ +void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { + sfx_t *sfx; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { + Com_Printf( S_COLOR_YELLOW, "S_AddRealLoopingSound: handle %i out of range\n", sfxHandle ); + return; + } + + sfx = &s_knownSfx[ sfxHandle ]; + + if (sfx->inMemory == qfalse) { + S_memoryLoad(sfx); + } + + if ( !sfx->soundLength ) { + Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); + } + VectorCopy( origin, loopSounds[entityNum].origin ); + VectorCopy( velocity, loopSounds[entityNum].velocity ); + loopSounds[entityNum].sfx = sfx; + loopSounds[entityNum].active = qtrue; + loopSounds[entityNum].kill = qfalse; + loopSounds[entityNum].doppler = qfalse; +} + + + +/* +================== +S_AddLoopSounds + +Spatialize all of the looping sounds. +All sounds are on the same cycle, so any duplicates can just +sum up the channel multipliers. +================== +*/ +void S_AddLoopSounds (void) { + int i, j, time; + int left_total, right_total, left, right; + channel_t *ch; + loopSound_t *loop, *loop2; + static int loopFrame; + + + numLoopChannels = 0; + + time = Com_Milliseconds(); + + loopFrame++; + for ( i = 0 ; i < MAX_GENTITIES ; i++) { + loop = &loopSounds[i]; + if ( !loop->active || loop->mergeFrame == loopFrame ) { + continue; // already merged into an earlier sound + } + + if (loop->kill) { + S_SpatializeOrigin( loop->origin, 127, &left_total, &right_total); // 3d + } else { + S_SpatializeOrigin( loop->origin, 90, &left_total, &right_total); // sphere + } + + loop->sfx->lastTimeUsed = time; + + for (j=(i+1); j< MAX_GENTITIES ; j++) { + loop2 = &loopSounds[j]; + if ( !loop2->active || loop2->doppler || loop2->sfx != loop->sfx) { + continue; + } + loop2->mergeFrame = loopFrame; + + if (loop2->kill) { + S_SpatializeOrigin( loop2->origin, 127, &left, &right); // 3d + } else { + S_SpatializeOrigin( loop2->origin, 90, &left, &right); // sphere + } + + loop2->sfx->lastTimeUsed = time; + left_total += left; + right_total += right; + } + if (left_total == 0 && right_total == 0) { + continue; // not audible + } + + // allocate a channel + ch = &loop_channels[numLoopChannels]; + + if (left_total > 255) { + left_total = 255; + } + if (right_total > 255) { + right_total = 255; + } + + ch->master_vol = 127; + ch->leftvol = left_total; + ch->rightvol = right_total; + ch->thesfx = loop->sfx; + ch->doppler = loop->doppler; + ch->dopplerScale = loop->dopplerScale; + ch->oldDopplerScale = loop->oldDopplerScale; + numLoopChannels++; + if (numLoopChannels == MAX_CHANNELS) { + return; + } + } +} + +//============================================================================= + +/* +================= +S_ByteSwapRawSamples + +If raw data has been loaded in little endien binary form, this must be done. +If raw data was calculated, as with ADPCM, this should not be called. +================= +*/ +void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) { + int i; + + if ( width != 2 ) { + return; + } + if ( LittleShort( 256 ) == 256 ) { + return; + } + + if ( s_channels == 2 ) { + samples <<= 1; + } + for ( i = 0 ; i < samples ; i++ ) { + ((short *)data)[i] = LittleShort( ((short *)data)[i] ); + } +} + +portable_samplepair_t *S_GetRawSamplePointer() { + return s_rawsamples; +} + +/* +============ +S_RawSamples + +Music streaming +============ +*/ +void S_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float volume ) { + int i; + int src, dst; + float scale; + int intVolume; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + intVolume = 256 * volume; + + if ( s_rawend < s_soundtime ) { + Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend, s_soundtime ); + s_rawend = s_soundtime; + } + + scale = (float)rate / dma.speed; + +//Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend); + if (s_channels == 2 && width == 2) + { + if (scale == 1.0) + { // optimized case + for (i=0 ; i<samples ; i++) + { + dst = s_rawend&(MAX_RAW_SAMPLES-1); + s_rawend++; + s_rawsamples[dst].left = ((short *)data)[i*2] * intVolume; + s_rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume; + } + } + else + { + for (i=0 ; ; i++) + { + src = i*scale; + if (src >= samples) + break; + dst = s_rawend&(MAX_RAW_SAMPLES-1); + s_rawend++; + s_rawsamples[dst].left = ((short *)data)[src*2] * intVolume; + s_rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume; + } + } + } + else if (s_channels == 1 && width == 2) + { + for (i=0 ; ; i++) + { + src = i*scale; + if (src >= samples) + break; + dst = s_rawend&(MAX_RAW_SAMPLES-1); + s_rawend++; + s_rawsamples[dst].left = ((short *)data)[src] * intVolume; + s_rawsamples[dst].right = ((short *)data)[src] * intVolume; + } + } + else if (s_channels == 2 && width == 1) + { + intVolume *= 256; + + for (i=0 ; ; i++) + { + src = i*scale; + if (src >= samples) + break; + dst = s_rawend&(MAX_RAW_SAMPLES-1); + s_rawend++; + s_rawsamples[dst].left = ((char *)data)[src*2] * intVolume; + s_rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume; + } + } + else if (s_channels == 1 && width == 1) + { + intVolume *= 256; + + for (i=0 ; ; i++) + { + src = i*scale; + if (src >= samples) + break; + dst = s_rawend&(MAX_RAW_SAMPLES-1); + s_rawend++; + s_rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume; + s_rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume; + } + } + + if ( s_rawend > s_soundtime + MAX_RAW_SAMPLES ) { + Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend, s_soundtime ); + } +} + +//============================================================================= + +/* +===================== +S_UpdateEntityPosition + +let the sound system know where an entity currently is +====================== +*/ +void S_UpdateEntityPosition( int entityNum, const vec3_t origin ) { + if ( entityNum < 0 || entityNum > MAX_GENTITIES ) { + Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum ); + } + VectorCopy( origin, loopSounds[entityNum].origin ); +} + + +/* +============ +S_Respatialize + +Change the volumes of all the playing sounds for changes in their positions +============ +*/ +void S_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) { + int i; + channel_t *ch; + vec3_t origin; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + listener_number = entityNum; + VectorCopy(head, listener_origin); + VectorCopy(axis[0], listener_axis[0]); + VectorCopy(axis[1], listener_axis[1]); + VectorCopy(axis[2], listener_axis[2]); + + // update spatialization for dynamic sounds + ch = s_channels; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if ( !ch->thesfx ) { + continue; + } + // anything coming from the view entity will always be full volume + if (ch->entnum == listener_number) { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + } else { + if (ch->fixed_origin) { + VectorCopy( ch->origin, origin ); + } else { + VectorCopy( loopSounds[ ch->entnum ].origin, origin ); + } + + S_SpatializeOrigin (origin, ch->master_vol, &ch->leftvol, &ch->rightvol); + } + } + + // add loopsounds + S_AddLoopSounds (); +} + + +/* +======================== +S_ScanChannelStarts + +Returns qtrue if any new sounds were started since the last mix +======================== +*/ +qboolean S_ScanChannelStarts( void ) { + channel_t *ch; + int i; + qboolean newSamples; + + newSamples = qfalse; + ch = s_channels; + + for (i=0; i<MAX_CHANNELS ; i++, ch++) { + if ( !ch->thesfx ) { + continue; + } + // if this channel was just started this frame, + // set the sample count to it begins mixing + // into the very first sample + if ( ch->startSample == START_SAMPLE_IMMEDIATE ) { + ch->startSample = s_paintedtime; + newSamples = qtrue; + continue; + } + + // if it is completely finished by now, clear it + if ( ch->startSample + (ch->thesfx->soundLength) <= s_paintedtime ) { + S_ChannelFree(ch); + } + } + + return newSamples; +} + +/* +============ +S_Update + +Called once each time through the main loop +============ +*/ +void S_Update( void ) { + int i; + int total; + channel_t *ch; + + if ( !s_soundStarted || s_soundMuted ) { + Com_DPrintf ("not started or muted\n"); + return; + } + + // + // debugging output + // + if ( s_show->integer == 2 ) { + total = 0; + ch = s_channels; + for (i=0 ; i<MAX_CHANNELS; i++, ch++) { + if (ch->thesfx && (ch->leftvol || ch->rightvol) ) { + Com_Printf ("%f %f %s\n", ch->leftvol, ch->rightvol, ch->thesfx->soundName); + total++; + } + } + + Com_Printf ("----(%i)---- painted: %i\n", total, s_paintedtime); + } + + // add raw data from streamed samples + S_UpdateBackgroundTrack(); + + // mix some sound + S_Update_(); +} + +void S_GetSoundtime(void) +{ + int samplepos; + static int buffers; + static int oldsamplepos; + int fullsamples; + + fullsamples = dma.samples / dma.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 (s_paintedtime > 0x40000000) + { // time to chop things off to avoid 32 bit limits + buffers = 0; + s_paintedtime = fullsamples; + S_StopAllSounds (); + } + } + oldsamplepos = samplepos; + + s_soundtime = buffers*fullsamples + samplepos/dma.channels; + +#if 0 +// check to make sure that we haven't overshot + if (s_paintedtime < s_soundtime) + { + Com_DPrintf ("S_Update_ : overflow\n"); + s_paintedtime = s_soundtime; + } +#endif + + if ( dma.submission_chunk < 256 ) { + s_paintedtime = s_soundtime + s_mixPreStep->value * dma.speed; + } else { + s_paintedtime = s_soundtime + dma.submission_chunk; + } +} + + +void S_Update_(void) { + unsigned endtime; + int samps; + static float lastTime = 0.0f; + float ma, op; + float thisTime, sane; + static int ot = -1; + + if ( !s_soundStarted || s_soundMuted ) { + return; + } + + thisTime = Com_Milliseconds(); + + // Updates s_soundtime + S_GetSoundtime(); + + if (s_soundtime == ot) { + return; + } + ot = s_soundtime; + + // clear any sound effects that end before the current time, + // and start any new sounds + S_ScanChannelStarts(); + + sane = thisTime - lastTime; + if (sane<11) { + sane = 11; // 85hz + } + + ma = s_mixahead->value * dma.speed; + op = s_mixPreStep->value + sane*dma.speed*0.01; + + if (op < ma) { + ma = op; + } + + // mix ahead of current position + endtime = s_soundtime + ma; + + // mix to an even submission block size + endtime = (endtime + dma.submission_chunk-1) + & ~(dma.submission_chunk-1); + + // never mix more than the complete buffer + samps = dma.samples >> (dma.channels-1); + if (endtime - s_soundtime > samps) + endtime = s_soundtime + samps; + + + + SNDDMA_BeginPainting (); + + S_PaintChannels (endtime); + + SNDDMA_Submit (); + + lastTime = thisTime; +} + +/* +=============================================================================== + +console functions + +=============================================================================== +*/ + +void S_Play_f( void ) { + int i; + sfxHandle_t h; + char name[256]; + + i = 1; + while ( i<Cmd_Argc() ) { + if ( !Q_strrchr(Cmd_Argv(i), '.') ) { + Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) ); + } else { + Q_strncpyz( name, Cmd_Argv(i), sizeof(name) ); + } + h = S_RegisterSound( name, qfalse ); + if( h ) { + S_StartLocalSound( h, CHAN_LOCAL_SOUND ); + } + i++; + } +} + +void S_Music_f( void ) { + int c; + + c = Cmd_Argc(); + + if ( c == 2 ) { + S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(1) ); + s_backgroundLoop[0] = 0; + } else if ( c == 3 ) { + S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) ); + } else { + Com_Printf ("music <musicfile> [loopfile]\n"); + return; + } + +} + +void S_SoundList_f( void ) { + int i; + sfx_t *sfx; + int size, total; + char type[4][16]; + char mem[2][16]; + + strcpy(type[0], "16bit"); + strcpy(type[1], "adpcm"); + strcpy(type[2], "daub4"); + strcpy(type[3], "mulaw"); + strcpy(mem[0], "paged out"); + strcpy(mem[1], "resident "); + total = 0; + for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++) { + size = sfx->soundLength; + total += size; + Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], sfx->soundName, mem[sfx->inMemory] ); + } + Com_Printf ("Total resident: %i\n", total); + S_DisplayFreeMemory(); +} + + +/* +=============================================================================== + +background music functions + +=============================================================================== +*/ + +int FGetLittleLong( fileHandle_t f ) { + int v; + + FS_Read( &v, sizeof(v), f ); + + return LittleLong( v); +} + +int FGetLittleShort( fileHandle_t f ) { + short v; + + FS_Read( &v, sizeof(v), f ); + + return LittleShort( v); +} + +// returns the length of the data in the chunk, or 0 if not found +int S_FindWavChunk( fileHandle_t f, char *chunk ) { + char name[5]; + int len; + int r; + + name[4] = 0; + len = 0; + r = FS_Read( name, 4, f ); + if ( r != 4 ) { + return 0; + } + len = FGetLittleLong( f ); + if ( len < 0 || len > 0xfffffff ) { + len = 0; + return 0; + } + len = (len + 1 ) & ~1; // pad to word boundary +// s_nextWavChunk += len + 8; + + if ( strcmp( name, chunk ) ) { + return 0; + } + + return len; +} + +/* +====================== +S_StopBackgroundTrack +====================== +*/ +void S_StopBackgroundTrack( void ) { + if ( !s_backgroundFile ) { + return; + } + Sys_EndStreamedFile( s_backgroundFile ); + FS_FCloseFile( s_backgroundFile ); + s_backgroundFile = 0; + s_rawend = 0; +} + +/* +====================== +S_StartBackgroundTrack +====================== +*/ +void S_StartBackgroundTrack( const char *intro, const char *loop ){ + int len; + char dump[16]; + char name[MAX_QPATH]; + + if ( !intro ) { + intro = ""; + } + if ( !loop || !loop[0] ) { + loop = intro; + } + Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop ); + + Q_strncpyz( name, intro, sizeof( name ) - 4 ); + COM_DefaultExtension( name, sizeof( name ), ".wav" ); + + if ( !intro[0] ) { + return; + } + + Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) ); + + // close the background track, but DON'T reset s_rawend + // if restarting the same back ground track + if ( s_backgroundFile ) { + Sys_EndStreamedFile( s_backgroundFile ); + FS_FCloseFile( s_backgroundFile ); + s_backgroundFile = 0; + } + + // + // open up a wav file and get all the info + // + FS_FOpenFileRead( name, &s_backgroundFile, qtrue ); + if ( !s_backgroundFile ) { + Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", name ); + return; + } + + // skip the riff wav header + + FS_Read(dump, 12, s_backgroundFile); + + if ( !S_FindWavChunk( s_backgroundFile, "fmt " ) ) { + Com_Printf( "No fmt chunk in %s\n", name ); + FS_FCloseFile( s_backgroundFile ); + s_backgroundFile = 0; + return; + } + + // save name for soundinfo + s_backgroundInfo.format = FGetLittleShort( s_backgroundFile ); + s_backgroundInfo.channels = FGetLittleShort( s_backgroundFile ); + s_backgroundInfo.rate = FGetLittleLong( s_backgroundFile ); + FGetLittleLong( s_backgroundFile ); + FGetLittleShort( s_backgroundFile ); + s_backgroundInfo.width = FGetLittleShort( s_backgroundFile ) / 8; + + if ( s_backgroundInfo.format != WAV_FORMAT_PCM ) { + FS_FCloseFile( s_backgroundFile ); + s_backgroundFile = 0; + Com_Printf("Not a microsoft PCM format wav: %s\n", name); + return; + } + + if ( s_backgroundInfo.channels != 2 || s_backgroundInfo.rate != 22050 ) { + Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", name ); + } + + if ( ( len = S_FindWavChunk( s_backgroundFile, "data" ) ) == 0 ) { + FS_FCloseFile( s_backgroundFile ); + s_backgroundFile = 0; + Com_Printf("No data chunk in %s\n", name); + return; + } + + s_backgroundInfo.samples = len / (s_backgroundInfo.width * s_backgroundInfo.channels); + + s_backgroundSamples = s_backgroundInfo.samples; + + // + // start the background streaming + // + Sys_BeginStreamedFile( s_backgroundFile, 0x10000 ); +} + +/* +====================== +S_UpdateBackgroundTrack +====================== +*/ +void S_UpdateBackgroundTrack( void ) { + int bufferSamples; + int fileSamples; + byte raw[30000]; // just enough to fit in a mac stack frame + int fileBytes; + int r; + static float musicVolume = 0.5f; + + if ( !s_backgroundFile ) { + return; + } + + // graeme see if this is OK + musicVolume = (musicVolume + (s_musicVolume->value * 2))/4.0f; + + // don't bother playing anything if musicvolume is 0 + if ( musicVolume <= 0 ) { + return; + } + + // see how many samples should be copied into the raw buffer + if ( s_rawend < s_soundtime ) { + s_rawend = s_soundtime; + } + + while ( s_rawend < s_soundtime + MAX_RAW_SAMPLES ) { + bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime); + + // decide how much data needs to be read from the file + fileSamples = bufferSamples * s_backgroundInfo.rate / dma.speed; + + // don't try and read past the end of the file + if ( fileSamples > s_backgroundSamples ) { + fileSamples = s_backgroundSamples; + } + + // our max buffer size + fileBytes = fileSamples * (s_backgroundInfo.width * s_backgroundInfo.channels); + if ( fileBytes > sizeof(raw) ) { + fileBytes = sizeof(raw); + fileSamples = fileBytes / (s_backgroundInfo.width * s_backgroundInfo.channels); + } + + r = Sys_StreamedRead( raw, 1, fileBytes, s_backgroundFile ); + if ( r != fileBytes ) { + Com_Printf("StreamedRead failure on music track\n"); + S_StopBackgroundTrack(); + return; + } + + // byte swap if needed + S_ByteSwapRawSamples( fileSamples, s_backgroundInfo.width, s_backgroundInfo.channels, raw ); + + // add to raw buffer + S_RawSamples( fileSamples, s_backgroundInfo.rate, + s_backgroundInfo.width, s_backgroundInfo.channels, raw, musicVolume ); + + s_backgroundSamples -= fileSamples; + if ( !s_backgroundSamples ) { + // loop + if (s_backgroundLoop[0]) { + Sys_EndStreamedFile( s_backgroundFile ); + FS_FCloseFile( s_backgroundFile ); + s_backgroundFile = 0; + S_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop ); + if ( !s_backgroundFile ) { + return; // loop failed to restart + } + } else { + s_backgroundFile = 0; + return; + } + } + } +} + + +/* +====================== +S_FreeOldestSound +====================== +*/ + +void S_FreeOldestSound() { + int i, oldest, used; + sfx_t *sfx; + sndBuffer *buffer, *nbuffer; + + oldest = Com_Milliseconds(); + used = 0; + + for (i=1 ; i < s_numSfx ; i++) { + sfx = &s_knownSfx[i]; + if (sfx->inMemory && sfx->lastTimeUsed<oldest) { + used = i; + oldest = sfx->lastTimeUsed; + } + } + + sfx = &s_knownSfx[used]; + + Com_DPrintf("S_FreeOldestSound: freeing sound %s\n", sfx->soundName); + + buffer = sfx->soundData; + while(buffer != NULL) { + nbuffer = buffer->next; + SND_free(buffer); + buffer = nbuffer; + } + sfx->inMemory = qfalse; + sfx->soundData = NULL; +} |