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