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