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