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