1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 /*****************************************************************************
24  * name:		snd_dma.c
25  *
26  * desc:		main control for any streaming sound output device
27  *
28  * $Archive: /MissionPack/code/client/snd_dma.c $
29  *
30  *****************************************************************************/
31 
32 #include "snd_local.h"
33 #include "snd_codec.h"
34 #include "client.h"
35 
36 void S_Play_f(void);
37 void S_SoundList_f(void);
38 void S_Music_f(void);
39 
40 void S_Update_( void );
41 void S_UpdateBackgroundTrack( void );
42 void S_Base_StopAllSounds(void);
43 void S_Base_StopBackgroundTrack( void );
44 
45 snd_stream_t	*s_backgroundStream = NULL;
46 static char		s_backgroundLoop[MAX_QPATH];
47 //static char		s_backgroundMusic[MAX_QPATH]; //TTimo: unused
48 
49 
50 // =======================================================================
51 // Internal sound data & structures
52 // =======================================================================
53 
54 // only begin attenuating sound volumes when outside the FULLVOLUME range
55 #define		SOUND_FULLVOLUME	80
56 
57 #define		SOUND_ATTENUATE		0.0008f
58 
59 channel_t   s_channels[MAX_CHANNELS];
60 channel_t   loop_channels[MAX_CHANNELS];
61 int			numLoopChannels;
62 
63 static int	s_soundStarted;
64 static		qboolean	s_soundMuted;
65 
66 dma_t		dma;
67 
68 static int			listener_number;
69 static vec3_t		listener_origin;
70 static vec3_t		listener_axis[3];
71 
72 int			s_soundtime;		// sample PAIRS
73 int   		s_paintedtime; 		// sample PAIRS
74 
75 // MAX_SFX may be larger than MAX_SOUNDS because
76 // of custom player sounds
77 #define		MAX_SFX			4096
78 sfx_t		s_knownSfx[MAX_SFX];
79 int			s_numSfx = 0;
80 
81 #define		LOOP_HASH		128
82 static	sfx_t		*sfxHash[LOOP_HASH];
83 
84 cvar_t		*s_testsound;
85 cvar_t		*s_khz;
86 cvar_t		*s_show;
87 cvar_t		*s_mixahead;
88 cvar_t		*s_mixPreStep;
89 
90 static loopSound_t		loopSounds[MAX_GENTITIES];
91 static	channel_t		*freelist = NULL;
92 
93 int						s_rawend[MAX_RAW_STREAMS];
94 portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
95 
96 
97 // ====================================================================
98 // User-setable variables
99 // ====================================================================
100 
101 
S_Base_SoundInfo(void)102 void S_Base_SoundInfo(void) {
103 	Com_Printf("----- Sound Info -----\n" );
104 	if (!s_soundStarted) {
105 		Com_Printf ("sound system not started\n");
106 	} else {
107 		Com_Printf("%5d stereo\n", dma.channels - 1);
108 		Com_Printf("%5d samples\n", dma.samples);
109 		Com_Printf("%5d samplebits\n", dma.samplebits);
110 		Com_Printf("%5d submission_chunk\n", dma.submission_chunk);
111 		Com_Printf("%5d speed\n", dma.speed);
112 		Com_Printf("%p dma buffer\n", dma.buffer);
113 		if ( s_backgroundStream ) {
114 			Com_Printf("Background file: %s\n", s_backgroundLoop );
115 		} else {
116 			Com_Printf("No background file.\n" );
117 		}
118 
119 	}
120 	Com_Printf("----------------------\n" );
121 }
122 
123 
124 #if USE_VOIP
125 static
S_Base_StartCapture(void)126 void S_Base_StartCapture( void )
127 {
128 	// !!! FIXME: write me.
129 }
130 
131 static
S_Base_AvailableCaptureSamples(void)132 int S_Base_AvailableCaptureSamples( void )
133 {
134 	// !!! FIXME: write me.
135 	return 0;
136 }
137 
138 static
S_Base_Capture(int samples,byte * data)139 void S_Base_Capture( int samples, byte *data )
140 {
141 	// !!! FIXME: write me.
142 }
143 
144 static
S_Base_StopCapture(void)145 void S_Base_StopCapture( void )
146 {
147 	// !!! FIXME: write me.
148 }
149 
150 static
S_Base_MasterGain(float val)151 void S_Base_MasterGain( float val )
152 {
153 	// !!! FIXME: write me.
154 }
155 #endif
156 
157 
158 
159 /*
160 =================
161 S_Base_SoundList
162 =================
163 */
S_Base_SoundList(void)164 void S_Base_SoundList( void ) {
165 	int		i;
166 	sfx_t	*sfx;
167 	int		size, total;
168 	char	type[4][16];
169 	char	mem[2][16];
170 
171 	strcpy(type[0], "16bit");
172 	strcpy(type[1], "adpcm");
173 	strcpy(type[2], "daub4");
174 	strcpy(type[3], "mulaw");
175 	strcpy(mem[0], "paged out");
176 	strcpy(mem[1], "resident ");
177 	total = 0;
178 	for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++) {
179 		size = sfx->soundLength;
180 		total += size;
181 		Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod],
182 				sfx->soundName, mem[sfx->inMemory] );
183 	}
184 	Com_Printf ("Total resident: %i\n", total);
185 	S_DisplayFreeMemory();
186 }
187 
188 
189 
S_ChannelFree(channel_t * v)190 void S_ChannelFree(channel_t *v) {
191 	v->thesfx = NULL;
192 	*(channel_t **)v = freelist;
193 	freelist = (channel_t*)v;
194 }
195 
S_ChannelMalloc(void)196 channel_t*	S_ChannelMalloc( void ) {
197 	channel_t *v;
198 	if (freelist == NULL) {
199 		return NULL;
200 	}
201 	v = freelist;
202 	freelist = *(channel_t **)freelist;
203 	v->allocTime = Com_Milliseconds();
204 	return v;
205 }
206 
S_ChannelSetup(void)207 void S_ChannelSetup( void ) {
208 	channel_t *p, *q;
209 
210 	// clear all the sounds so they don't
211 	Com_Memset( s_channels, 0, sizeof( s_channels ) );
212 
213 	p = s_channels;;
214 	q = p + MAX_CHANNELS;
215 	while (--q > p) {
216 		*(channel_t **)q = q-1;
217 	}
218 
219 	*(channel_t **)q = NULL;
220 	freelist = p + MAX_CHANNELS - 1;
221 	Com_DPrintf("Channel memory manager started\n");
222 }
223 
224 
225 
226 // =======================================================================
227 // Load a sound
228 // =======================================================================
229 
230 /*
231 ================
232 return a hash value for the sfx name
233 ================
234 */
S_HashSFXName(const char * name)235 static long S_HashSFXName(const char *name) {
236 	int		i;
237 	long	hash;
238 	char	letter;
239 
240 	hash = 0;
241 	i = 0;
242 	while (name[i] != '\0') {
243 		letter = tolower(name[i]);
244 		if (letter =='.') break;				// don't include extension
245 		if (letter =='\\') letter = '/';		// damn path names
246 		hash+=(long)(letter)*(i+119);
247 		i++;
248 	}
249 	hash &= (LOOP_HASH-1);
250 	return hash;
251 }
252 
253 /*
254 ==================
255 S_FindName
256 
257 Will allocate a new sfx if it isn't found
258 ==================
259 */
S_FindName(const char * name)260 static sfx_t *S_FindName( const char *name ) {
261 	int		i;
262 	int		hash;
263 
264 	sfx_t	*sfx;
265 
266 	if (!name) {
267 		Com_Error (ERR_FATAL, "S_FindName: NULL\n");
268 	}
269 	if (!name[0]) {
270 		Com_Error (ERR_FATAL, "S_FindName: empty name\n");
271 	}
272 
273 	if (strlen(name) >= MAX_QPATH) {
274 		Com_Error (ERR_FATAL, "Sound name too long: %s", name);
275 	}
276 
277 	hash = S_HashSFXName(name);
278 
279 	sfx = sfxHash[hash];
280 	// see if already loaded
281 	while (sfx) {
282 		if (!Q_stricmp(sfx->soundName, name) ) {
283 			return sfx;
284 		}
285 		sfx = sfx->next;
286 	}
287 
288 	// find a free sfx
289 	for (i=0 ; i < s_numSfx ; i++) {
290 		if (!s_knownSfx[i].soundName[0]) {
291 			break;
292 		}
293 	}
294 
295 	if (i == s_numSfx) {
296 		if (s_numSfx == MAX_SFX) {
297 			Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
298 		}
299 		s_numSfx++;
300 	}
301 
302 	sfx = &s_knownSfx[i];
303 	Com_Memset (sfx, 0, sizeof(*sfx));
304 	strcpy (sfx->soundName, name);
305 
306 	sfx->next = sfxHash[hash];
307 	sfxHash[hash] = sfx;
308 
309 	return sfx;
310 }
311 
312 /*
313 =================
314 S_DefaultSound
315 =================
316 */
S_DefaultSound(sfx_t * sfx)317 void S_DefaultSound( sfx_t *sfx ) {
318 
319 	int		i;
320 
321 	sfx->soundLength = 512;
322 	sfx->soundData = SND_malloc();
323 	sfx->soundData->next = NULL;
324 
325 
326 	for ( i = 0 ; i < sfx->soundLength ; i++ ) {
327 		sfx->soundData->sndChunk[i] = i;
328 	}
329 }
330 
331 /*
332 ===================
333 S_DisableSounds
334 
335 Disables sounds until the next S_BeginRegistration.
336 This is called when the hunk is cleared and the sounds
337 are no longer valid.
338 ===================
339 */
S_Base_DisableSounds(void)340 void S_Base_DisableSounds( void ) {
341 	S_Base_StopAllSounds();
342 	s_soundMuted = qtrue;
343 }
344 
345 /*
346 ==================
347 S_RegisterSound
348 
349 Creates a default buzz sound if the file can't be loaded
350 ==================
351 */
S_Base_RegisterSound(const char * name,qboolean compressed)352 sfxHandle_t	S_Base_RegisterSound( const char *name, qboolean compressed ) {
353 	sfx_t	*sfx;
354 
355 	compressed = qfalse;
356 	if (!s_soundStarted) {
357 		return 0;
358 	}
359 
360 	if ( strlen( name ) >= MAX_QPATH ) {
361 		Com_Printf( "Sound name exceeds MAX_QPATH\n" );
362 		return 0;
363 	}
364 
365 	sfx = S_FindName( name );
366 	if ( sfx->soundData ) {
367 		if ( sfx->defaultSound ) {
368 			Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName );
369 			return 0;
370 		}
371 		return sfx - s_knownSfx;
372 	}
373 
374 	sfx->inMemory = qfalse;
375 	sfx->soundCompressed = compressed;
376 
377   S_memoryLoad(sfx);
378 
379 	if ( sfx->defaultSound ) {
380 		Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName );
381 		return 0;
382 	}
383 
384 	return sfx - s_knownSfx;
385 }
386 
387 /*
388 =====================
389 S_BeginRegistration
390 
391 =====================
392 */
S_Base_BeginRegistration(void)393 void S_Base_BeginRegistration( void ) {
394 	s_soundMuted = qfalse;		// we can play again
395 
396 	if (s_numSfx == 0) {
397 		SND_setup();
398 
399 		s_numSfx = 0;
400 		Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) );
401 		Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
402 
403 		S_Base_RegisterSound("sound/feedback/hit.wav", qfalse);		// changed to a sound in baseq3
404 	}
405 }
406 
S_memoryLoad(sfx_t * sfx)407 void S_memoryLoad(sfx_t	*sfx) {
408 	// load the sound file
409 	if ( !S_LoadSound ( sfx ) ) {
410 //		Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->soundName );
411 		sfx->defaultSound = qtrue;
412 	}
413 	sfx->inMemory = qtrue;
414 }
415 
416 //=============================================================================
417 
418 /*
419 =================
420 S_SpatializeOrigin
421 
422 Used for spatializing s_channels
423 =================
424 */
S_SpatializeOrigin(vec3_t origin,int master_vol,int * left_vol,int * right_vol)425 void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *right_vol)
426 {
427     vec_t		dot;
428     vec_t		dist;
429     vec_t		lscale, rscale, scale;
430     vec3_t		source_vec;
431     vec3_t		vec;
432 
433 	const float dist_mult = SOUND_ATTENUATE;
434 
435 	// calculate stereo seperation and distance attenuation
436 	VectorSubtract(origin, listener_origin, source_vec);
437 
438 	dist = VectorNormalize(source_vec);
439 	dist -= SOUND_FULLVOLUME;
440 	if (dist < 0)
441 		dist = 0;			// close enough to be at full volume
442 	dist *= dist_mult;		// different attenuation levels
443 
444 	VectorRotate( source_vec, listener_axis, vec );
445 
446 	dot = -vec[1];
447 
448 	if (dma.channels == 1)
449 	{ // no attenuation = no spatialization
450 		rscale = 1.0;
451 		lscale = 1.0;
452 	}
453 	else
454 	{
455 		rscale = 0.5 * (1.0 + dot);
456 		lscale = 0.5 * (1.0 - dot);
457 		if ( rscale < 0 ) {
458 			rscale = 0;
459 		}
460 		if ( lscale < 0 ) {
461 			lscale = 0;
462 		}
463 	}
464 
465 	// add in distance effect
466 	scale = (1.0 - dist) * rscale;
467 	*right_vol = (master_vol * scale);
468 	if (*right_vol < 0)
469 		*right_vol = 0;
470 
471 	scale = (1.0 - dist) * lscale;
472 	*left_vol = (master_vol * scale);
473 	if (*left_vol < 0)
474 		*left_vol = 0;
475 }
476 
477 // =======================================================================
478 // Start a sound effect
479 // =======================================================================
480 
481 /*
482 ====================
483 S_StartSound
484 
485 Validates the parms and ques the sound up
486 if pos is NULL, the sound will be dynamically sourced from the entity
487 Entchannel 0 will never override a playing sound
488 ====================
489 */
S_Base_StartSound(vec3_t origin,int entityNum,int entchannel,sfxHandle_t sfxHandle)490 void S_Base_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) {
491 	channel_t	*ch;
492 	sfx_t		*sfx;
493   int i, oldest, chosen, time;
494   int	inplay, allowed;
495 
496 	if ( !s_soundStarted || s_soundMuted ) {
497 		return;
498 	}
499 
500 	if ( !origin && ( entityNum < 0 || entityNum > MAX_GENTITIES ) ) {
501 		Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum );
502 	}
503 
504 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
505 		Com_Printf( S_COLOR_YELLOW "S_StartSound: handle %i out of range\n", sfxHandle );
506 		return;
507 	}
508 
509 	sfx = &s_knownSfx[ sfxHandle ];
510 
511 	if (sfx->inMemory == qfalse) {
512 		S_memoryLoad(sfx);
513 	}
514 
515 	if ( s_show->integer == 1 ) {
516 		Com_Printf( "%i : %s\n", s_paintedtime, sfx->soundName );
517 	}
518 
519 	time = Com_Milliseconds();
520 
521 //	Com_Printf("playing %s\n", sfx->soundName);
522 	// pick a channel to play on
523 
524 	allowed = 4;
525 	if (entityNum == listener_number) {
526 		allowed = 8;
527 	}
528 
529 	ch = s_channels;
530 	inplay = 0;
531 	for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {
532 		if (ch->entnum == entityNum && ch->thesfx == sfx) {
533 			if (time - ch->allocTime < 50) {
534 //				if (Cvar_VariableValue( "cg_showmiss" )) {
535 //					Com_Printf("double sound start\n");
536 //				}
537 				return;
538 			}
539 			inplay++;
540 		}
541 	}
542 
543 	if (inplay>allowed) {
544 		return;
545 	}
546 
547 	sfx->lastTimeUsed = time;
548 
549 	ch = S_ChannelMalloc();	// entityNum, entchannel);
550 	if (!ch) {
551 		ch = s_channels;
552 
553 		oldest = sfx->lastTimeUsed;
554 		chosen = -1;
555 		for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
556 			if (ch->entnum != listener_number && ch->entnum == entityNum && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) {
557 				oldest = ch->allocTime;
558 				chosen = i;
559 			}
560 		}
561 		if (chosen == -1) {
562 			ch = s_channels;
563 			for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
564 				if (ch->entnum != listener_number && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) {
565 					oldest = ch->allocTime;
566 					chosen = i;
567 				}
568 			}
569 			if (chosen == -1) {
570 				ch = s_channels;
571 				if (ch->entnum == listener_number) {
572 					for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
573 						if (ch->allocTime<oldest) {
574 							oldest = ch->allocTime;
575 							chosen = i;
576 						}
577 					}
578 				}
579 				if (chosen == -1) {
580 					Com_Printf("dropping sound\n");
581 					return;
582 				}
583 			}
584 		}
585 		ch = &s_channels[chosen];
586 		ch->allocTime = sfx->lastTimeUsed;
587 	}
588 
589 	if (origin) {
590 		VectorCopy (origin, ch->origin);
591 		ch->fixed_origin = qtrue;
592 	} else {
593 		ch->fixed_origin = qfalse;
594 	}
595 
596 	ch->master_vol = 127;
597 	ch->entnum = entityNum;
598 	ch->thesfx = sfx;
599 	ch->startSample = START_SAMPLE_IMMEDIATE;
600 	ch->entchannel = entchannel;
601 	ch->leftvol = ch->master_vol;		// these will get calced at next spatialize
602 	ch->rightvol = ch->master_vol;		// unless the game isn't running
603 	ch->doppler = qfalse;
604 }
605 
606 
607 /*
608 ==================
609 S_StartLocalSound
610 ==================
611 */
S_Base_StartLocalSound(sfxHandle_t sfxHandle,int channelNum)612 void S_Base_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) {
613 	if ( !s_soundStarted || s_soundMuted ) {
614 		return;
615 	}
616 
617 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
618 		Com_Printf( S_COLOR_YELLOW "S_StartLocalSound: handle %i out of range\n", sfxHandle );
619 		return;
620 	}
621 
622 	S_Base_StartSound (NULL, listener_number, channelNum, sfxHandle );
623 }
624 
625 
626 /*
627 ==================
628 S_ClearSoundBuffer
629 
630 If we are about to perform file access, clear the buffer
631 so sound doesn't stutter.
632 ==================
633 */
S_Base_ClearSoundBuffer(void)634 void S_Base_ClearSoundBuffer( void ) {
635 	int		clear;
636 
637 	if (!s_soundStarted)
638 		return;
639 
640 	// stop looping sounds
641 	Com_Memset(loopSounds, 0, MAX_GENTITIES*sizeof(loopSound_t));
642 	Com_Memset(loop_channels, 0, MAX_CHANNELS*sizeof(channel_t));
643 	numLoopChannels = 0;
644 
645 	S_ChannelSetup();
646 
647 	Com_Memset(s_rawend, '\0', sizeof (s_rawend));
648 
649 	if (dma.samplebits == 8)
650 		clear = 0x80;
651 	else
652 		clear = 0;
653 
654 	SNDDMA_BeginPainting ();
655 	if (dma.buffer)
656 		Com_Memset(dma.buffer, clear, dma.samples * dma.samplebits/8);
657 	SNDDMA_Submit ();
658 }
659 
660 /*
661 ==================
662 S_StopAllSounds
663 ==================
664 */
S_Base_StopAllSounds(void)665 void S_Base_StopAllSounds(void) {
666 	if ( !s_soundStarted ) {
667 		return;
668 	}
669 
670 	// stop the background music
671 	S_Base_StopBackgroundTrack();
672 
673 	S_Base_ClearSoundBuffer ();
674 }
675 
676 /*
677 ==============================================================
678 
679 continuous looping sounds are added each frame
680 
681 ==============================================================
682 */
683 
S_Base_StopLoopingSound(int entityNum)684 void S_Base_StopLoopingSound(int entityNum) {
685 	loopSounds[entityNum].active = qfalse;
686 //	loopSounds[entityNum].sfx = 0;
687 	loopSounds[entityNum].kill = qfalse;
688 }
689 
690 /*
691 ==================
692 S_ClearLoopingSounds
693 
694 ==================
695 */
S_Base_ClearLoopingSounds(qboolean killall)696 void S_Base_ClearLoopingSounds( qboolean killall ) {
697 	int i;
698 	for ( i = 0 ; i < MAX_GENTITIES ; i++) {
699 		if (killall || loopSounds[i].kill == qtrue || (loopSounds[i].sfx && loopSounds[i].sfx->soundLength == 0)) {
700 			loopSounds[i].kill = qfalse;
701 			S_Base_StopLoopingSound(i);
702 		}
703 	}
704 	numLoopChannels = 0;
705 }
706 
707 /*
708 ==================
709 S_AddLoopingSound
710 
711 Called during entity generation for a frame
712 Include velocity in case I get around to doing doppler...
713 ==================
714 */
S_Base_AddLoopingSound(int entityNum,const vec3_t origin,const vec3_t velocity,sfxHandle_t sfxHandle)715 void S_Base_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
716 	sfx_t *sfx;
717 
718 	if ( !s_soundStarted || s_soundMuted ) {
719 		return;
720 	}
721 
722 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
723 		Com_Printf( S_COLOR_YELLOW "S_AddLoopingSound: handle %i out of range\n", sfxHandle );
724 		return;
725 	}
726 
727 	sfx = &s_knownSfx[ sfxHandle ];
728 
729 	if (sfx->inMemory == qfalse) {
730 		S_memoryLoad(sfx);
731 	}
732 
733 	if ( !sfx->soundLength ) {
734 		Com_Error( ERR_DROP, "%s has length 0", sfx->soundName );
735 	}
736 
737 	VectorCopy( origin, loopSounds[entityNum].origin );
738 	VectorCopy( velocity, loopSounds[entityNum].velocity );
739 	loopSounds[entityNum].active = qtrue;
740 	loopSounds[entityNum].kill = qtrue;
741 	loopSounds[entityNum].doppler = qfalse;
742 	loopSounds[entityNum].oldDopplerScale = 1.0;
743 	loopSounds[entityNum].dopplerScale = 1.0;
744 	loopSounds[entityNum].sfx = sfx;
745 
746 	if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) {
747 		vec3_t	out;
748 		float	lena, lenb;
749 
750 		loopSounds[entityNum].doppler = qtrue;
751 		lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin);
752 		VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out);
753 		lenb = DistanceSquared(loopSounds[listener_number].origin, out);
754 		if ((loopSounds[entityNum].framenum+1) != cls.framecount) {
755 			loopSounds[entityNum].oldDopplerScale = 1.0;
756 		} else {
757 			loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale;
758 		}
759 		loopSounds[entityNum].dopplerScale = lenb/(lena*100);
760 		if (loopSounds[entityNum].dopplerScale<=1.0) {
761 			loopSounds[entityNum].doppler = qfalse;			// don't bother doing the math
762 		} else if (loopSounds[entityNum].dopplerScale>MAX_DOPPLER_SCALE) {
763 			loopSounds[entityNum].dopplerScale = MAX_DOPPLER_SCALE;
764 		}
765 	}
766 
767 	loopSounds[entityNum].framenum = cls.framecount;
768 }
769 
770 /*
771 ==================
772 S_AddLoopingSound
773 
774 Called during entity generation for a frame
775 Include velocity in case I get around to doing doppler...
776 ==================
777 */
S_Base_AddRealLoopingSound(int entityNum,const vec3_t origin,const vec3_t velocity,sfxHandle_t sfxHandle)778 void S_Base_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
779 	sfx_t *sfx;
780 
781 	if ( !s_soundStarted || s_soundMuted ) {
782 		return;
783 	}
784 
785 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
786 		Com_Printf( S_COLOR_YELLOW "S_AddRealLoopingSound: handle %i out of range\n", sfxHandle );
787 		return;
788 	}
789 
790 	sfx = &s_knownSfx[ sfxHandle ];
791 
792 	if (sfx->inMemory == qfalse) {
793 		S_memoryLoad(sfx);
794 	}
795 
796 	if ( !sfx->soundLength ) {
797 		Com_Error( ERR_DROP, "%s has length 0", sfx->soundName );
798 	}
799 	VectorCopy( origin, loopSounds[entityNum].origin );
800 	VectorCopy( velocity, loopSounds[entityNum].velocity );
801 	loopSounds[entityNum].sfx = sfx;
802 	loopSounds[entityNum].active = qtrue;
803 	loopSounds[entityNum].kill = qfalse;
804 	loopSounds[entityNum].doppler = qfalse;
805 }
806 
807 
808 
809 /*
810 ==================
811 S_AddLoopSounds
812 
813 Spatialize all of the looping sounds.
814 All sounds are on the same cycle, so any duplicates can just
815 sum up the channel multipliers.
816 ==================
817 */
S_AddLoopSounds(void)818 void S_AddLoopSounds (void) {
819 	int			i, j, time;
820 	int			left_total, right_total, left, right;
821 	channel_t	*ch;
822 	loopSound_t	*loop, *loop2;
823 	static int	loopFrame;
824 
825 
826 	numLoopChannels = 0;
827 
828 	time = Com_Milliseconds();
829 
830 	loopFrame++;
831 	for ( i = 0 ; i < MAX_GENTITIES ; i++) {
832 		loop = &loopSounds[i];
833 		if ( !loop->active || loop->mergeFrame == loopFrame ) {
834 			continue;	// already merged into an earlier sound
835 		}
836 
837 		if (loop->kill) {
838 			S_SpatializeOrigin( loop->origin, 127, &left_total, &right_total);			// 3d
839 		} else {
840 			S_SpatializeOrigin( loop->origin, 90,  &left_total, &right_total);			// sphere
841 		}
842 
843 		loop->sfx->lastTimeUsed = time;
844 
845 		for (j=(i+1); j< MAX_GENTITIES ; j++) {
846 			loop2 = &loopSounds[j];
847 			if ( !loop2->active || loop2->doppler || loop2->sfx != loop->sfx) {
848 				continue;
849 			}
850 			loop2->mergeFrame = loopFrame;
851 
852 			if (loop2->kill) {
853 				S_SpatializeOrigin( loop2->origin, 127, &left, &right);				// 3d
854 			} else {
855 				S_SpatializeOrigin( loop2->origin, 90,  &left, &right);				// sphere
856 			}
857 
858 			loop2->sfx->lastTimeUsed = time;
859 			left_total += left;
860 			right_total += right;
861 		}
862 		if (left_total == 0 && right_total == 0) {
863 			continue;		// not audible
864 		}
865 
866 		// allocate a channel
867 		ch = &loop_channels[numLoopChannels];
868 
869 		if (left_total > 255) {
870 			left_total = 255;
871 		}
872 		if (right_total > 255) {
873 			right_total = 255;
874 		}
875 
876 		ch->master_vol = 127;
877 		ch->leftvol = left_total;
878 		ch->rightvol = right_total;
879 		ch->thesfx = loop->sfx;
880 		ch->doppler = loop->doppler;
881 		ch->dopplerScale = loop->dopplerScale;
882 		ch->oldDopplerScale = loop->oldDopplerScale;
883 		numLoopChannels++;
884 		if (numLoopChannels == MAX_CHANNELS) {
885 			return;
886 		}
887 	}
888 }
889 
890 //=============================================================================
891 
892 /*
893 =================
894 S_ByteSwapRawSamples
895 
896 If raw data has been loaded in little endien binary form, this must be done.
897 If raw data was calculated, as with ADPCM, this should not be called.
898 =================
899 */
S_ByteSwapRawSamples(int samples,int width,int s_channels,const byte * data)900 void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
901 	int		i;
902 
903 	if ( width != 2 ) {
904 		return;
905 	}
906 	if ( LittleShort( 256 ) == 256 ) {
907 		return;
908 	}
909 
910 	if ( s_channels == 2 ) {
911 		samples <<= 1;
912 	}
913 	for ( i = 0 ; i < samples ; i++ ) {
914 		((short *)data)[i] = LittleShort( ((short *)data)[i] );
915 	}
916 }
917 
918 /*
919 ============
920 S_RawSamples
921 
922 Music streaming
923 ============
924 */
S_Base_RawSamples(int stream,int samples,int rate,int width,int s_channels,const byte * data,float volume)925 void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
926 	int		i;
927 	int		src, dst;
928 	float	scale;
929 	int		intVolume;
930 	portable_samplepair_t *rawsamples;
931 
932 	if ( !s_soundStarted || s_soundMuted ) {
933 		return;
934 	}
935 
936 	if ( (stream < 0) || (stream >= MAX_RAW_STREAMS) ) {
937 		return;
938 	}
939 	rawsamples = s_rawsamples[stream];
940 
941 	intVolume = 256 * volume;
942 
943 	if ( s_rawend[stream] < s_soundtime ) {
944 		Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend[stream], s_soundtime );
945 		s_rawend[stream] = s_soundtime;
946 	}
947 
948 	scale = (float)rate / dma.speed;
949 
950 //Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend[stream]);
951 	if (s_channels == 2 && width == 2)
952 	{
953 		if (scale == 1.0)
954 		{	// optimized case
955 			for (i=0 ; i<samples ; i++)
956 			{
957 				dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
958 				s_rawend[stream]++;
959 				rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
960 				rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
961 			}
962 		}
963 		else
964 		{
965 			for (i=0 ; ; i++)
966 			{
967 				src = i*scale;
968 				if (src >= samples)
969 					break;
970 				dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
971 				s_rawend[stream]++;
972 				rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
973 				rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
974 			}
975 		}
976 	}
977 	else if (s_channels == 1 && width == 2)
978 	{
979 		for (i=0 ; ; i++)
980 		{
981 			src = i*scale;
982 			if (src >= samples)
983 				break;
984 			dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
985 			s_rawend[stream]++;
986 			rawsamples[dst].left = ((short *)data)[src] * intVolume;
987 			rawsamples[dst].right = ((short *)data)[src] * intVolume;
988 		}
989 	}
990 	else if (s_channels == 2 && width == 1)
991 	{
992 		intVolume *= 256;
993 
994 		for (i=0 ; ; i++)
995 		{
996 			src = i*scale;
997 			if (src >= samples)
998 				break;
999 			dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
1000 			s_rawend[stream]++;
1001 			rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
1002 			rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
1003 		}
1004 	}
1005 	else if (s_channels == 1 && width == 1)
1006 	{
1007 		intVolume *= 256;
1008 
1009 		for (i=0 ; ; i++)
1010 		{
1011 			src = i*scale;
1012 			if (src >= samples)
1013 				break;
1014 			dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
1015 			s_rawend[stream]++;
1016 			rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
1017 			rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
1018 		}
1019 	}
1020 
1021 	if ( s_rawend[stream] > s_soundtime + MAX_RAW_SAMPLES ) {
1022 		Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend[stream], s_soundtime );
1023 	}
1024 }
1025 
1026 //=============================================================================
1027 
1028 /*
1029 =====================
1030 S_UpdateEntityPosition
1031 
1032 let the sound system know where an entity currently is
1033 ======================
1034 */
S_Base_UpdateEntityPosition(int entityNum,const vec3_t origin)1035 void S_Base_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
1036 	if ( entityNum < 0 || entityNum > MAX_GENTITIES ) {
1037 		Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
1038 	}
1039 	VectorCopy( origin, loopSounds[entityNum].origin );
1040 }
1041 
1042 
1043 /*
1044 ============
1045 S_Respatialize
1046 
1047 Change the volumes of all the playing sounds for changes in their positions
1048 ============
1049 */
S_Base_Respatialize(int entityNum,const vec3_t head,vec3_t axis[3],int inwater)1050 void S_Base_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) {
1051 	int			i;
1052 	channel_t	*ch;
1053 	vec3_t		origin;
1054 
1055 	if ( !s_soundStarted || s_soundMuted ) {
1056 		return;
1057 	}
1058 
1059 	listener_number = entityNum;
1060 	VectorCopy(head, listener_origin);
1061 	VectorCopy(axis[0], listener_axis[0]);
1062 	VectorCopy(axis[1], listener_axis[1]);
1063 	VectorCopy(axis[2], listener_axis[2]);
1064 
1065 	// update spatialization for dynamic sounds
1066 	ch = s_channels;
1067 	for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
1068 		if ( !ch->thesfx ) {
1069 			continue;
1070 		}
1071 		// anything coming from the view entity will always be full volume
1072 		if (ch->entnum == listener_number) {
1073 			ch->leftvol = ch->master_vol;
1074 			ch->rightvol = ch->master_vol;
1075 		} else {
1076 			if (ch->fixed_origin) {
1077 				VectorCopy( ch->origin, origin );
1078 			} else {
1079 				VectorCopy( loopSounds[ ch->entnum ].origin, origin );
1080 			}
1081 
1082 			S_SpatializeOrigin (origin, ch->master_vol, &ch->leftvol, &ch->rightvol);
1083 		}
1084 	}
1085 
1086 	// add loopsounds
1087 	S_AddLoopSounds ();
1088 }
1089 
1090 
1091 /*
1092 ========================
1093 S_ScanChannelStarts
1094 
1095 Returns qtrue if any new sounds were started since the last mix
1096 ========================
1097 */
S_ScanChannelStarts(void)1098 qboolean S_ScanChannelStarts( void ) {
1099 	channel_t		*ch;
1100 	int				i;
1101 	qboolean		newSamples;
1102 
1103 	newSamples = qfalse;
1104 	ch = s_channels;
1105 
1106 	for (i=0; i<MAX_CHANNELS ; i++, ch++) {
1107 		if ( !ch->thesfx ) {
1108 			continue;
1109 		}
1110 		// if this channel was just started this frame,
1111 		// set the sample count to it begins mixing
1112 		// into the very first sample
1113 		if ( ch->startSample == START_SAMPLE_IMMEDIATE ) {
1114 			ch->startSample = s_paintedtime;
1115 			newSamples = qtrue;
1116 			continue;
1117 		}
1118 
1119 		// if it is completely finished by now, clear it
1120 		if ( ch->startSample + (ch->thesfx->soundLength) <= s_paintedtime ) {
1121 			S_ChannelFree(ch);
1122 		}
1123 	}
1124 
1125 	return newSamples;
1126 }
1127 
1128 /*
1129 ============
1130 S_Update
1131 
1132 Called once each time through the main loop
1133 ============
1134 */
S_Base_Update(void)1135 void S_Base_Update( void ) {
1136 	int			i;
1137 	int			total;
1138 	channel_t	*ch;
1139 
1140 	if ( !s_soundStarted || s_soundMuted ) {
1141 //		Com_DPrintf ("not started or muted\n");
1142 		return;
1143 	}
1144 
1145 	//
1146 	// debugging output
1147 	//
1148 	if ( s_show->integer == 2 ) {
1149 		total = 0;
1150 		ch = s_channels;
1151 		for (i=0 ; i<MAX_CHANNELS; i++, ch++) {
1152 			if (ch->thesfx && (ch->leftvol || ch->rightvol) ) {
1153 				Com_Printf ("%d %d %s\n", ch->leftvol, ch->rightvol, ch->thesfx->soundName);
1154 				total++;
1155 			}
1156 		}
1157 
1158 		Com_Printf ("----(%i)---- painted: %i\n", total, s_paintedtime);
1159 	}
1160 
1161 	// add raw data from streamed samples
1162 	S_UpdateBackgroundTrack();
1163 
1164 	// mix some sound
1165 	S_Update_();
1166 }
1167 
S_GetSoundtime(void)1168 void S_GetSoundtime(void)
1169 {
1170 	int		samplepos;
1171 	static	int		buffers;
1172 	static	int		oldsamplepos;
1173 	int		fullsamples;
1174 
1175 	fullsamples = dma.samples / dma.channels;
1176 
1177 	if( CL_VideoRecording( ) )
1178 	{
1179 		s_soundtime += (int)ceil( dma.speed / cl_aviFrameRate->value );
1180 		return;
1181 	}
1182 
1183 	// it is possible to miscount buffers if it has wrapped twice between
1184 	// calls to S_Update.  Oh well.
1185 	samplepos = SNDDMA_GetDMAPos();
1186 	if (samplepos < oldsamplepos)
1187 	{
1188 		buffers++;					// buffer wrapped
1189 
1190 		if (s_paintedtime > 0x40000000)
1191 		{	// time to chop things off to avoid 32 bit limits
1192 			buffers = 0;
1193 			s_paintedtime = fullsamples;
1194 			S_Base_StopAllSounds ();
1195 		}
1196 	}
1197 	oldsamplepos = samplepos;
1198 
1199 	s_soundtime = buffers*fullsamples + samplepos/dma.channels;
1200 
1201 #if 0
1202 // check to make sure that we haven't overshot
1203 	if (s_paintedtime < s_soundtime)
1204 	{
1205 		Com_DPrintf ("S_Update_ : overflow\n");
1206 		s_paintedtime = s_soundtime;
1207 	}
1208 #endif
1209 
1210 	if ( dma.submission_chunk < 256 ) {
1211 		s_paintedtime = s_soundtime + s_mixPreStep->value * dma.speed;
1212 	} else {
1213 		s_paintedtime = s_soundtime + dma.submission_chunk;
1214 	}
1215 }
1216 
1217 
S_Update_(void)1218 void S_Update_(void) {
1219 	unsigned        endtime;
1220 	int				samps;
1221 	static			float	lastTime = 0.0f;
1222 	float			ma, op;
1223 	float			thisTime, sane;
1224 	static			int ot = -1;
1225 
1226 	if ( !s_soundStarted || s_soundMuted ) {
1227 		return;
1228 	}
1229 
1230 	thisTime = Com_Milliseconds();
1231 
1232 	// Updates s_soundtime
1233 	S_GetSoundtime();
1234 
1235 	if (s_soundtime == ot) {
1236 		return;
1237 	}
1238 	ot = s_soundtime;
1239 
1240 	// clear any sound effects that end before the current time,
1241 	// and start any new sounds
1242 	S_ScanChannelStarts();
1243 
1244 	sane = thisTime - lastTime;
1245 	if (sane<11) {
1246 		sane = 11;			// 85hz
1247 	}
1248 
1249 	ma = s_mixahead->value * dma.speed;
1250 	op = s_mixPreStep->value + sane*dma.speed*0.01;
1251 
1252 	if (op < ma) {
1253 		ma = op;
1254 	}
1255 
1256 	// mix ahead of current position
1257 	endtime = s_soundtime + ma;
1258 
1259 	// mix to an even submission block size
1260 	endtime = (endtime + dma.submission_chunk-1)
1261 		& ~(dma.submission_chunk-1);
1262 
1263 	// never mix more than the complete buffer
1264 	samps = dma.samples >> (dma.channels-1);
1265 	if (endtime - s_soundtime > samps)
1266 		endtime = s_soundtime + samps;
1267 
1268 
1269 
1270 	SNDDMA_BeginPainting ();
1271 
1272 	S_PaintChannels (endtime);
1273 
1274 	SNDDMA_Submit ();
1275 
1276 	lastTime = thisTime;
1277 }
1278 
1279 
1280 
1281 /*
1282 ===============================================================================
1283 
1284 background music functions
1285 
1286 ===============================================================================
1287 */
1288 
1289 /*
1290 ======================
1291 S_StopBackgroundTrack
1292 ======================
1293 */
S_Base_StopBackgroundTrack(void)1294 void S_Base_StopBackgroundTrack( void ) {
1295 	if(!s_backgroundStream)
1296 		return;
1297 	S_CodecCloseStream(s_backgroundStream);
1298 	s_backgroundStream = NULL;
1299 	s_rawend[0] = 0;
1300 }
1301 
1302 /*
1303 ======================
1304 S_StartBackgroundTrack
1305 ======================
1306 */
S_Base_StartBackgroundTrack(const char * intro,const char * loop)1307 void S_Base_StartBackgroundTrack( const char *intro, const char *loop ){
1308 	if ( !intro ) {
1309 		intro = "";
1310 	}
1311 	if ( !loop || !loop[0] ) {
1312 		loop = intro;
1313 	}
1314 	Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop );
1315 
1316 	if ( !intro[0] ) {
1317 		return;
1318 	}
1319 
1320 	if( !loop ) {
1321 		s_backgroundLoop[0] = 0;
1322 	} else {
1323 		Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
1324 	}
1325 
1326 	// close the background track, but DON'T reset s_rawend
1327 	// if restarting the same back ground track
1328 	if(s_backgroundStream)
1329 	{
1330 		S_CodecCloseStream(s_backgroundStream);
1331 		s_backgroundStream = NULL;
1332 	}
1333 
1334 	// Open stream
1335 	s_backgroundStream = S_CodecOpenStream(intro);
1336 	if(!s_backgroundStream) {
1337 		Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", intro );
1338 		return;
1339 	}
1340 
1341 	if(s_backgroundStream->info.channels != 2 || s_backgroundStream->info.rate != 22050) {
1342 		Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", intro );
1343 	}
1344 }
1345 
1346 /*
1347 ======================
1348 S_UpdateBackgroundTrack
1349 ======================
1350 */
S_UpdateBackgroundTrack(void)1351 void S_UpdateBackgroundTrack( void ) {
1352 	int		bufferSamples;
1353 	int		fileSamples;
1354 	byte	raw[30000];		// just enough to fit in a mac stack frame
1355 	int		fileBytes;
1356 	int		r;
1357 	static	float	musicVolume = 0.5f;
1358 
1359 	if(!s_backgroundStream) {
1360 		return;
1361 	}
1362 
1363 	// graeme see if this is OK
1364 	musicVolume = (musicVolume + (s_musicVolume->value * 2))/4.0f;
1365 
1366 	// don't bother playing anything if musicvolume is 0
1367 	if ( musicVolume <= 0 ) {
1368 		return;
1369 	}
1370 
1371 	// see how many samples should be copied into the raw buffer
1372 	if ( s_rawend[0] < s_soundtime ) {
1373 		s_rawend[0] = s_soundtime;
1374 	}
1375 
1376 	while ( s_rawend[0] < s_soundtime + MAX_RAW_SAMPLES ) {
1377 		bufferSamples = MAX_RAW_SAMPLES - (s_rawend[0] - s_soundtime);
1378 
1379 		// decide how much data needs to be read from the file
1380 		fileSamples = bufferSamples * s_backgroundStream->info.rate / dma.speed;
1381 
1382 		// our max buffer size
1383 		fileBytes = fileSamples * (s_backgroundStream->info.width * s_backgroundStream->info.channels);
1384 		if ( fileBytes > sizeof(raw) ) {
1385 			fileBytes = sizeof(raw);
1386 			fileSamples = fileBytes / (s_backgroundStream->info.width * s_backgroundStream->info.channels);
1387 		}
1388 
1389 		// Read
1390 		r = S_CodecReadStream(s_backgroundStream, fileBytes, raw);
1391 		if(r < fileBytes)
1392 		{
1393 			fileBytes = r;
1394 			fileSamples = r / (s_backgroundStream->info.width * s_backgroundStream->info.channels);
1395 		}
1396 
1397 		if(r > 0)
1398 		{
1399 			// add to raw buffer
1400 			S_Base_RawSamples( 0, fileSamples, s_backgroundStream->info.rate,
1401 				s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, musicVolume );
1402 		}
1403 		else
1404 		{
1405 			// loop
1406 			if(s_backgroundLoop[0])
1407 			{
1408 				S_CodecCloseStream(s_backgroundStream);
1409 				s_backgroundStream = NULL;
1410 				S_Base_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop );
1411 				if(!s_backgroundStream)
1412 					return;
1413 			}
1414 			else
1415 			{
1416 				S_Base_StopBackgroundTrack();
1417 				return;
1418 			}
1419 		}
1420 
1421 	}
1422 }
1423 
1424 
1425 /*
1426 ======================
1427 S_FreeOldestSound
1428 ======================
1429 */
1430 
S_FreeOldestSound(void)1431 void S_FreeOldestSound( void ) {
1432 	int	i, oldest, used;
1433 	sfx_t	*sfx;
1434 	sndBuffer	*buffer, *nbuffer;
1435 
1436 	oldest = Com_Milliseconds();
1437 	used = 0;
1438 
1439 	for (i=1 ; i < s_numSfx ; i++) {
1440 		sfx = &s_knownSfx[i];
1441 		if (sfx->inMemory && sfx->lastTimeUsed<oldest) {
1442 			used = i;
1443 			oldest = sfx->lastTimeUsed;
1444 		}
1445 	}
1446 
1447 	sfx = &s_knownSfx[used];
1448 
1449 	Com_DPrintf("S_FreeOldestSound: freeing sound %s\n", sfx->soundName);
1450 
1451 	buffer = sfx->soundData;
1452 	while(buffer != NULL) {
1453 		nbuffer = buffer->next;
1454 		SND_free(buffer);
1455 		buffer = nbuffer;
1456 	}
1457 	sfx->inMemory = qfalse;
1458 	sfx->soundData = NULL;
1459 }
1460 
1461 // =======================================================================
1462 // Shutdown sound engine
1463 // =======================================================================
1464 
S_Base_Shutdown(void)1465 void S_Base_Shutdown( void ) {
1466 	if ( !s_soundStarted ) {
1467 		return;
1468 	}
1469 
1470 	SNDDMA_Shutdown();
1471 
1472 	s_soundStarted = 0;
1473 
1474 	Cmd_RemoveCommand("s_info");
1475 }
1476 
1477 /*
1478 ================
1479 S_Init
1480 ================
1481 */
S_Base_Init(soundInterface_t * si)1482 qboolean S_Base_Init( soundInterface_t *si ) {
1483 	qboolean	r;
1484 
1485 	if( !si ) {
1486 		return qfalse;
1487 	}
1488 
1489 	s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE);
1490 	s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE);
1491 	s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE);
1492 	s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT);
1493 	s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT);
1494 
1495 	r = SNDDMA_Init();
1496 
1497 	if ( r ) {
1498 		s_soundStarted = 1;
1499 		s_soundMuted = 1;
1500 //		s_numSfx = 0;
1501 
1502 		Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
1503 
1504 		s_soundtime = 0;
1505 		s_paintedtime = 0;
1506 
1507 		S_Base_StopAllSounds( );
1508 	} else {
1509 		return qfalse;
1510 	}
1511 
1512 	si->Shutdown = S_Base_Shutdown;
1513 	si->StartSound = S_Base_StartSound;
1514 	si->StartLocalSound = S_Base_StartLocalSound;
1515 	si->StartBackgroundTrack = S_Base_StartBackgroundTrack;
1516 	si->StopBackgroundTrack = S_Base_StopBackgroundTrack;
1517 	si->RawSamples = S_Base_RawSamples;
1518 	si->StopAllSounds = S_Base_StopAllSounds;
1519 	si->ClearLoopingSounds = S_Base_ClearLoopingSounds;
1520 	si->AddLoopingSound = S_Base_AddLoopingSound;
1521 	si->AddRealLoopingSound = S_Base_AddRealLoopingSound;
1522 	si->StopLoopingSound = S_Base_StopLoopingSound;
1523 	si->Respatialize = S_Base_Respatialize;
1524 	si->UpdateEntityPosition = S_Base_UpdateEntityPosition;
1525 	si->Update = S_Base_Update;
1526 	si->DisableSounds = S_Base_DisableSounds;
1527 	si->BeginRegistration = S_Base_BeginRegistration;
1528 	si->RegisterSound = S_Base_RegisterSound;
1529 	si->ClearSoundBuffer = S_Base_ClearSoundBuffer;
1530 	si->SoundInfo = S_Base_SoundInfo;
1531 	si->SoundList = S_Base_SoundList;
1532 
1533 #if USE_VOIP
1534 	si->StartCapture = S_Base_StartCapture;
1535 	si->AvailableCaptureSamples = S_Base_AvailableCaptureSamples;
1536 	si->Capture = S_Base_Capture;
1537 	si->StopCapture = S_Base_StopCapture;
1538 	si->MasterGain = S_Base_MasterGain;
1539 #endif
1540 
1541 	return qtrue;
1542 }
1543