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