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