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