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