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