1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "client.h"
23
24 void S_Update_();
25 void S_Stop_f(void);
26
27
28 // only begin attenuating sound volumes when outside the FULLVOLUME range
29 #define SOUND_FULLVOLUME 50
30 #define SOUND_LOOPATTENUATE 0.003
31
32 int s_registration_sequence;
33
34 channel_t channels[MAX_CHANNELS];
35
36 static qboolean snd_initialized;
37 static qboolean s_registering;
38
39 dma_t dma;
40
41 vec3_t listener_origin;
42 vec3_t listener_forward;
43 vec3_t listener_right;
44 vec3_t listener_up;
45
46 int soundtime; // sample pairs
47 int paintedtime; // sample pairs
48
49 // during registration it is possible to have more sounds
50 // than could actually be referenced during gameplay,
51 // because we don't want to free anything until we are
52 // sure we won't need it.
53 #define MAX_SFX (MAX_SOUNDS * 2)
54 sfx_t known_sfx[MAX_SFX];
55 int num_sfx;
56
57 #define MAX_PLAYSOUNDS 128
58 playsound_t s_playsounds[MAX_PLAYSOUNDS];
59 playsound_t s_freeplays;
60 playsound_t s_pendingplays;
61
62 cvar_t *s_volume;
63 cvar_t *s_loadas8bit;
64 cvar_t *s_mixahead;
65 cvar_t *s_bits;
66 cvar_t *s_rate;
67 cvar_t *s_channels;
68 cvar_t *s_device;
69
70 cvar_t *s_ambient;
71
72 cvar_t *s_testsound;
73 cvar_t *s_show;
74
75 int s_beginofs;
76 int s_rawend;
77
78 portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
79
80
81 /*
82 S_Info_f
83 */
S_Info_f(void)84 void S_Info_f(void){
85 if(!snd_initialized){
86 Com_Printf("Sound system not started.\n");
87 return;
88 }
89
90 Com_Printf("%5d channels\n", dma.channels);
91 Com_Printf("%5d samples\n", dma.samples);
92 Com_Printf("%5d offset\n", dma.offset);
93 Com_Printf("%5d bits\n", dma.bits);
94 Com_Printf("%5d chunk\n", dma.chunk);
95 Com_Printf("%5d speed\n", dma.rate);
96 Com_Printf("0x%lx dma buffer\n", (unsigned long)dma.buffer);
97 }
98
99
100 /*
101 S_Play_f
102 */
S_Play_f(void)103 void S_Play_f(void){
104 int i;
105 char name[256];
106 sfx_t *sfx;
107
108 i = 1;
109 while(i < Cmd_Argc()){
110 if(!strrchr(Cmd_Argv(i), '.')){
111 strcpy(name, Cmd_Argv(i));
112 strcat(name, ".wav");
113 } else
114 strcpy(name, Cmd_Argv(i));
115 sfx = S_RegisterSound(name);
116 S_StartSound(NULL, cl.playernum + 1, 0, sfx, 1.0, 1.0, 0);
117 i++;
118 }
119 }
120
121
122 /*
123 S_List_f
124 */
S_List_f(void)125 void S_List_f(void){
126 int i;
127 sfx_t *sfx;
128 sfxcache_t *sc;
129 int size, total;
130
131 total = 0;
132 for(sfx = known_sfx, i = 0; i < num_sfx; i++, sfx++){
133 if(!sfx->registration_sequence)
134 continue;
135 sc = sfx->cache;
136 if(sc){
137 size = sc->length * sc->width *(sc->stereo + 1);
138 total += size;
139 if(sc->loopstart >= 0)
140 Com_Printf("L");
141 else
142 Com_Printf(" ");
143 Com_Printf("(%2db) %6i : %s\n", sc->width*8, size, sfx->name);
144 } else {
145 if(sfx->name[0] == '*')
146 Com_Printf(" placeholder : %s\n", sfx->name);
147 else
148 Com_Printf(" not loaded : %s\n", sfx->name);
149 }
150 }
151 Com_Printf("Total resident: %i\n", total);
152 }
153
154
155 /*
156 S_Reload_f
157 */
S_Reload_f(void)158 void S_Reload_f(void){
159 S_RegisterSounds();
160 }
161
162
163 /*
164 S_Restart_f
165 */
S_Restart_f(void)166 void S_Restart_f(void){
167 S_Shutdown();
168 S_Init();
169
170 S_Reload_f();
171 }
172
173
174 /*
175 S_Init
176 */
S_Init(void)177 void S_Init(void){
178
179 if(Cvar_VariableValue("s_disable")){
180 Com_Printf("Sound disabled.\n");
181 return;
182 }
183
184 Com_Printf("Sound initialization..\n");
185
186 s_volume = Cvar_Get("s_volume", "0.7", CVAR_ARCHIVE);
187 s_loadas8bit = Cvar_Get("s_loadas8bit", "0", CVAR_ARCHIVE);
188 s_mixahead = Cvar_Get("s_mixahead", "0.05", CVAR_ARCHIVE);
189 s_bits = Cvar_Get("s_bits", "16", CVAR_ARCHIVE);
190 s_rate = Cvar_Get("s_rate", "", CVAR_ARCHIVE);
191 s_channels = Cvar_Get("s_channels", "2", CVAR_ARCHIVE);
192 s_device = Cvar_Get("s_device", "default", CVAR_ARCHIVE);
193
194 s_ambient = Cvar_Get("s_ambient", "1", CVAR_ARCHIVE);
195
196 s_testsound = Cvar_Get("s_testsound", "0", 0);
197 s_show = Cvar_Get("s_show", "0", 0);
198
199 Cmd_AddCommand("s_restart", S_Restart_f);
200 Cmd_AddCommand("s_reload", S_Reload_f);
201 Cmd_AddCommand("s_play", S_Play_f);
202 Cmd_AddCommand("s_stop", S_Stop_f);
203 Cmd_AddCommand("s_list", S_List_f);
204 Cmd_AddCommand("s_info", S_Info_f);
205
206 S_InitScaletable();
207
208 num_sfx = 0;
209 soundtime = 0;
210 paintedtime = 0;
211
212 S_Stop_f();
213
214 snd_initialized = SND_Init();
215
216 Com_Printf("Sound initialized %dbit %dKHz %dch.\n", dma.bits,
217 (dma.rate / 1000), dma.channels);
218 }
219
220
221 /*
222 S_Shutdown
223 */
S_Shutdown(void)224 void S_Shutdown(void){
225 int i;
226 sfx_t *sfx;
227
228 if(!snd_initialized)
229 return;
230
231 SND_Shutdown();
232
233 snd_initialized = false;
234
235 Cmd_RemoveCommand("s_play");
236 Cmd_RemoveCommand("s_stop");
237 Cmd_RemoveCommand("s_list");
238 Cmd_RemoveCommand("s_info");
239
240 // free all sounds
241 for(i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++){
242 if(!sfx->name[0])
243 continue;
244 if(sfx->cache)
245 Z_Free(sfx->cache);
246 memset(sfx, 0, sizeof(*sfx));
247 }
248
249 num_sfx = 0;
250 }
251
252
253 /*
254 S_FindName
255 */
S_FindName(char * name,qboolean create)256 sfx_t *S_FindName(char *name, qboolean create){
257 int i;
258 sfx_t *sfx;
259
260 if(!name)
261 Com_Error(ERR_FATAL, "S_FindName: NULL\n");
262 if(!name[0])
263 Com_Error(ERR_FATAL, "S_FindName: empty name\n");
264
265 if(strlen(name) >= MAX_QPATH)
266 Com_Error(ERR_FATAL, "Sound name too long: %s", name);
267
268 // see if already loaded
269 for(i = 0; i < num_sfx; i++)
270 if(!strcmp(known_sfx[i].name, name))
271 return &known_sfx[i];
272
273 if(!create)
274 return NULL;
275
276 // find a free sfx
277 for(i = 0; i < num_sfx; i++)
278 if(!known_sfx[i].name[0])
279 break;
280
281 if(i == num_sfx){
282 if(num_sfx == MAX_SFX)
283 Com_Error(ERR_FATAL, "S_FindName: out of sfx_t");
284 num_sfx++;
285 }
286
287 sfx = &known_sfx[i];
288 memset(sfx, 0, sizeof(*sfx));
289 strcpy(sfx->name, name);
290 sfx->registration_sequence = s_registration_sequence;
291
292 return sfx;
293 }
294
295
296 /*
297 S_AliasName
298 */
S_AliasName(char * aliasname,char * truename)299 sfx_t *S_AliasName(char *aliasname, char *truename){
300 sfx_t *sfx;
301 char *s;
302 int i;
303
304 s = Z_Malloc(MAX_QPATH);
305 strcpy(s, truename);
306
307 // find a free sfx
308 for(i = 0; i < num_sfx; i++)
309 if(!known_sfx[i].name[0])
310 break;
311
312 if(i == num_sfx){
313 if(num_sfx == MAX_SFX)
314 Com_Error(ERR_FATAL, "S_FindName: out of sfx_t");
315 num_sfx++;
316 }
317
318 sfx = &known_sfx[i];
319 memset(sfx, 0, sizeof(*sfx));
320 strcpy(sfx->name, aliasname);
321 sfx->registration_sequence = s_registration_sequence;
322 sfx->truename = s;
323
324 return sfx;
325 }
326
327
328 /*
329 S_BeginRegistration
330 */
S_BeginRegistration(void)331 void S_BeginRegistration(void){
332 s_registration_sequence++;
333 s_registering = true;
334 }
335
336 /*
337 S_RegisterSound
338 */
S_RegisterSound(char * name)339 sfx_t *S_RegisterSound(char *name){
340 sfx_t *sfx;
341
342 if(!snd_initialized)
343 return NULL;
344
345 sfx = S_FindName(name, true);
346 sfx->registration_sequence = s_registration_sequence;
347
348 if(!s_registering)
349 S_LoadSound(sfx);
350
351 return sfx;
352 }
353
354
355 /*
356 S_EndRegistration
357 */
S_EndRegistration(void)358 void S_EndRegistration(void){
359 int i;
360 sfx_t *sfx;
361
362 // free any sounds not from this registration sequence
363 for(i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++){
364 if(!sfx->name[0])
365 continue;
366 if(sfx->registration_sequence != s_registration_sequence){ // don't need this sound
367 if(sfx->cache) // it is possible to have a leftover
368 Z_Free(sfx->cache); // from a server that didn't finish loading
369 memset(sfx, 0, sizeof(*sfx));
370 }
371 }
372
373 // load everything in
374 for(i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++){
375 if(!sfx->name[0])
376 continue;
377 S_LoadSound(sfx);
378 }
379
380 s_registering = false;
381 }
382
383
384 /*
385 S_RegisterSounds
386 */
S_RegisterSounds(void)387 void S_RegisterSounds(void){
388 int i;
389
390 S_BeginRegistration();
391 CL_RegisterTEntSounds();
392 for(i = 1; i < MAX_SOUNDS; i++){
393 if(!cl.configstrings[CS_SOUNDS + i][0])
394 break;
395 cl.sound_precache[i] = S_RegisterSound(cl.configstrings[CS_SOUNDS + i]);
396 }
397 S_EndRegistration();
398 }
399
400
401 /*
402 S_PickChannel
403 */
S_PickChannel(int entnum,int entchannel)404 channel_t *S_PickChannel(int entnum, int entchannel){
405 int ch_idx;
406 int first_to_die;
407 int life_left;
408 channel_t *ch;
409
410 if(entchannel < 0)
411 Com_Error(ERR_DROP, "S_PickChannel: entchannel < 0");
412
413 // Check for replacement sound, or find the best one to replace
414 first_to_die = -1;
415 life_left = 0x7fffffff;
416 for(ch_idx = 0; ch_idx < MAX_CHANNELS; ch_idx++){
417 if(entchannel != 0 // channel 0 never overrides
418 && channels[ch_idx].entnum == entnum
419 && channels[ch_idx].entchannel == entchannel){ // always override sound from same entity
420 first_to_die = ch_idx;
421 break;
422 }
423
424 // don't let monster sounds override player sounds
425 if(channels[ch_idx].entnum == cl.playernum + 1 && entnum != cl.playernum + 1 && channels[ch_idx].sfx)
426 continue;
427
428 if(channels[ch_idx].end - paintedtime < life_left){
429 life_left = channels[ch_idx].end - paintedtime;
430 first_to_die = ch_idx;
431 }
432 }
433
434 if(first_to_die == -1)
435 return NULL;
436
437 ch = &channels[first_to_die];
438 memset(ch, 0, sizeof(*ch));
439
440 return ch;
441 }
442
443
444 /*
445 S_SpatializeOrigin
446
447 Used for spatializing channels and autosounds
448 */
S_SpatializeOrigin(vec3_t origin,float master_vol,float dist_mult,int * left_vol,int * right_vol)449 void S_SpatializeOrigin(vec3_t origin, float master_vol, float dist_mult, int *left_vol, int *right_vol){
450 vec_t dot;
451 vec_t dist;
452 vec_t lscale, rscale, scale;
453 vec3_t source_vec;
454
455 if(cls.state != ca_active){
456 *left_vol = *right_vol = 255;
457 return;
458 }
459
460 // calculate stereo seperation and distance attenuation
461 VectorSubtract(origin, listener_origin, source_vec);
462
463 dist = VectorNormalize(source_vec);
464 dist -= SOUND_FULLVOLUME;
465 if(dist < 0)
466 dist = 0; // close enough to be at full volume
467 dist *= dist_mult; // different attenuation levels
468
469 dot = DotProduct(listener_right, source_vec);
470
471 if(dma.channels == 1 || !dist_mult){ // no attenuation = no spatialization
472 rscale = 1.0;
473 lscale = 1.0;
474 } else {
475 rscale = 0.5 *(1.0 + dot);
476 lscale = 0.5 *(1.0 - dot);
477 }
478
479 // add in distance effect
480 scale =(1.0 - dist) * rscale;
481 *right_vol =(int)(master_vol * scale);
482 if(*right_vol < 0)
483 *right_vol = 0;
484
485 scale =(1.0 - dist) * lscale;
486 *left_vol =(int)(master_vol * scale);
487 if(*left_vol < 0)
488 *left_vol = 0;
489 }
490
491
492 /*
493 S_Spatialize
494 */
S_Spatialize(channel_t * ch)495 void S_Spatialize(channel_t *ch){
496 vec3_t origin;
497
498 // anything coming from the view entity will always be full volume
499 if(ch->entnum == cl.playernum + 1){
500 ch->leftvol = ch->master_vol;
501 ch->rightvol = ch->master_vol;
502 return;
503 }
504
505 if(ch->fixed_origin){
506 VectorCopy(ch->origin, origin);
507 } else
508 CL_GetEntitySoundOrigin(ch->entnum, origin);
509
510 S_SpatializeOrigin(origin, ch->master_vol, ch->dist_mult, &ch->leftvol, &ch->rightvol);
511 }
512
513
514 /*
515 S_AllocPlaysound
516 */
S_AllocPlaysound(void)517 playsound_t *S_AllocPlaysound(void){
518 playsound_t *ps;
519
520 ps = s_freeplays.next;
521 if(ps == &s_freeplays)
522 return NULL; // no free playsounds
523
524 // unlink from freelist
525 ps->prev->next = ps->next;
526 ps->next->prev = ps->prev;
527
528 return ps;
529 }
530
531
532 /*
533 S_FreePlaysound
534 */
S_FreePlaysound(playsound_t * ps)535 void S_FreePlaysound(playsound_t *ps){
536 // unlink from channel
537 ps->prev->next = ps->next;
538 ps->next->prev = ps->prev;
539
540 // add to free list
541 ps->next = s_freeplays.next;
542 s_freeplays.next->prev = ps;
543 ps->prev = &s_freeplays;
544 s_freeplays.next = ps;
545 }
546
547
548 /*
549 S_IssuePlaysound
550
551 Take the next playsound and begin it on the channel
552 This is never called directly by S_Play*, but only
553 by the update loop.
554 */
S_IssuePlaysound(playsound_t * ps)555 void S_IssuePlaysound(playsound_t *ps){
556 channel_t *ch;
557 sfxcache_t *sc;
558
559 if(s_show->value)
560 Com_Printf("Issue %i\n", ps->begin);
561 // pick a channel to play on
562 ch = S_PickChannel(ps->entnum, ps->entchannel);
563 if(!ch){
564 S_FreePlaysound(ps);
565 return;
566 }
567
568 // spatialize
569 if(ps->attenuation == ATTN_STATIC)
570 ch->dist_mult = ps->attenuation * 0.001;
571 else
572 ch->dist_mult = ps->attenuation * 0.0005;
573 ch->master_vol = ps->volume;
574 ch->entnum = ps->entnum;
575 ch->entchannel = ps->entchannel;
576 ch->sfx = ps->sfx;
577 VectorCopy(ps->origin, ch->origin);
578 ch->fixed_origin = ps->fixed_origin;
579
580 S_Spatialize(ch);
581
582 ch->pos = 0;
583 sc = S_LoadSound(ch->sfx);
584 ch->end = paintedtime + sc->length;
585
586 // free the playsound
587 S_FreePlaysound(ps);
588 }
589
S_RegisterSexedSound(entity_state_t * ent,char * base)590 struct sfx_s *S_RegisterSexedSound(entity_state_t *ent, char *base){
591 int n;
592 char *p;
593 struct sfx_s *sfx;
594 char model[MAX_QPATH];
595 char skin[MAX_QPATH];
596 char sexedFilename[MAX_QPATH];
597 char maleFilename[MAX_QPATH];
598
599 extern char registered_skins[MAX_MD2SKINS][MAX_QPATH];
600 extern int registered_skins_index;
601
602 qboolean skin_registered = false;
603
604 // determine what model the client is using
605 model[0] = skin[0] = 0;
606 n = CS_PLAYERSKINS + ent->number - 1;
607 if(cl.configstrings[n][0]){
608 p = strchr(cl.configstrings[n], '\\');
609 if(p){
610 p += 1;
611 strcpy(model, p);
612 strcpy(skin, p);
613 p = strchr(model, '/');
614 if(p)
615 *p = 0;
616 }
617 }
618
619 // ensure the skin is loaded before we play the wrong sound
620 if(!strcmp(model, "male")){ // dont worry about males
621 skin_registered = true;
622 } else if(model[0] && skin[0]){ // but check everyone else (female, cyborg, ..)
623 for(n = 0; n < registered_skins_index; n++){
624 if(!strcmp(registered_skins[n], skin)){
625 skin_registered = true;
626 break;
627 }
628 }
629 }
630
631 // catch any unregistered skins, theyre being displayed
632 // as males so make them sound like males
633 if(!skin_registered)
634 strcpy(model, "male");
635
636 // see if we already know of the model specific sound
637 Com_sprintf(sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base + 1);
638 sfx = S_FindName(sexedFilename, false);
639
640 if(!sfx){ // not yet registered, so try model specific sound
641 sfx = S_RegisterSound(sexedFilename);
642 }
643 if(!sfx){ // that didn't work, so use male, and alias it for future calls
644 Com_sprintf(maleFilename, sizeof(maleFilename), "player/male/%s", base + 1);
645 sfx = S_AliasName(sexedFilename, maleFilename);
646 }
647
648 return sfx;
649 }
650
651
652 /*
653 S_StartSound
654
655 Validates the parms and ques the sound up
656 if pos is NULL, the sound will be dynamically sourced from the entity
657 Entchannel 0 will never override a playing sound
658 */
S_StartSound(vec3_t origin,int entnum,int entchannel,sfx_t * sfx,float fvol,float attenuation,float timeofs)659 void S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs){
660 sfxcache_t *sc;
661 int vol;
662 playsound_t *ps, *sort;
663 int start;
664
665 if(!snd_initialized)
666 return;
667
668 if(!sfx)
669 return;
670
671 if(sfx->name[0] == '*')
672 sfx = S_RegisterSexedSound(&cl_entities[entnum].current, sfx->name);
673
674 // make sure the sound is loaded
675 sc = S_LoadSound(sfx);
676 if(!sc)
677 return; // couldn't load the sound's data
678
679 vol = fvol * 255;
680
681 // make the playsound_t
682 ps = S_AllocPlaysound();
683 if(!ps)
684 return;
685
686 if(origin){
687 VectorCopy(origin, ps->origin);
688 ps->fixed_origin = true;
689 } else
690 ps->fixed_origin = false;
691
692 ps->entnum = entnum;
693 ps->entchannel = entchannel;
694 ps->attenuation = attenuation;
695 ps->volume = vol;
696 ps->sfx = sfx;
697
698 // drift s_beginofs
699 start = cl.frame.servertime * 0.001 * dma.rate + s_beginofs;
700 if(start < paintedtime){
701 start = paintedtime;
702 s_beginofs = start -(cl.frame.servertime * 0.001 * dma.rate);
703 } else if(start > paintedtime + 0.3 * dma.rate){
704 start = paintedtime + 0.1 * dma.rate;
705 s_beginofs = start -(cl.frame.servertime * 0.001 * dma.rate);
706 } else {
707 s_beginofs -= 10;
708 }
709
710 if(!timeofs)
711 ps->begin = paintedtime;
712 else
713 ps->begin = start + timeofs * dma.rate;
714
715 // sort into the pending sound list
716 for(sort = s_pendingplays.next;
717 sort != &s_pendingplays && sort->begin < ps->begin;
718 sort = sort->next)
719 ;
720
721 ps->next = sort;
722 ps->prev = sort->prev;
723
724 ps->next->prev = ps;
725 ps->prev->next = ps;
726 }
727
728
729 /*
730 S_StartLocalSound
731 */
S_StartLocalSound(char * sound)732 void S_StartLocalSound(char *sound){
733 sfx_t *sfx;
734
735 if(!snd_initialized)
736 return;
737
738 sfx = S_RegisterSound(sound);
739 if(!sfx){
740 Com_Printf("S_StartLocalSound: can't cache %s\n", sound);
741 return;
742 }
743 S_StartSound(NULL, cl.playernum + 1, 0, sfx, 1, 1, 0);
744 }
745
746
747 /*
748 S_ClearBuffer
749 */
S_ClearBuffer(void)750 void S_ClearBuffer(void){
751 int clear;
752
753 if(!snd_initialized)
754 return;
755
756 s_rawend = 0;
757
758 if(dma.bits == 8)
759 clear = 0x80;
760 else
761 clear = 0;
762
763 SND_BeginPainting();
764 if(dma.buffer){
765 /* buffer may be read-only, clear manually */
766 memset(dma.buffer, clear, dma.samples * dma.bits / 8);
767 }
768 SND_Submit();
769 }
770
771 /*
772 S_Stop_f
773 */
S_Stop_f(void)774 void S_Stop_f(void){
775 int i;
776
777 // clear all the playsounds
778 memset(s_playsounds, 0, sizeof(s_playsounds));
779 s_freeplays.next = s_freeplays.prev = &s_freeplays;
780 s_pendingplays.next = s_pendingplays.prev = &s_pendingplays;
781
782 for(i = 0; i < MAX_PLAYSOUNDS; i++){
783 s_playsounds[i].prev = &s_freeplays;
784 s_playsounds[i].next = s_freeplays.next;
785 s_playsounds[i].prev->next = &s_playsounds[i];
786 s_playsounds[i].next->prev = &s_playsounds[i];
787 }
788
789 // clear all the channels
790 memset(channels, 0, sizeof(channels));
791
792 S_ClearBuffer();
793 }
794
795 /*
796 S_AddLoopSounds
797
798 Entities with a ->sound field will generate looped sounds
799 that are automatically started, stopped, and merged together
800 as the entities are sent to the client
801 */
S_AddLoopSounds(void)802 void S_AddLoopSounds(void){
803 int i, j;
804 int sounds[MAX_EDICTS];
805 int left, right, left_total, right_total;
806 channel_t *ch;
807 sfx_t *sfx;
808 sfxcache_t *sc;
809 int num;
810 entity_state_t *ent;
811
812 if(!s_ambient->value)
813 return;
814
815 for(i = 0; i < cl.frame.num_entities; i++){
816 num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1);
817 ent = &cl_parse_entities[num];
818 sounds[i] = ent->sound;
819 }
820
821 for(i = 0; i < cl.frame.num_entities; i++){
822 if(!sounds[i])
823 continue;
824
825 sfx = cl.sound_precache[sounds[i]];
826 if(!sfx)
827 continue; // bad sound effect
828 sc = sfx->cache;
829 if(!sc)
830 continue;
831
832 num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1);
833 ent = &cl_parse_entities[num];
834
835 // find the total contribution of all sounds of this type
836 S_SpatializeOrigin(ent->origin, 255.0, SOUND_LOOPATTENUATE,
837 &left_total, &right_total);
838 for(j = i + 1; j < cl.frame.num_entities; j++){
839 if(sounds[j] != sounds[i])
840 continue;
841 sounds[j] = 0; // don't check this again later
842
843 num = (cl.frame.parse_entities + j) & (MAX_PARSE_ENTITIES - 1);
844 ent = &cl_parse_entities[num];
845
846 S_SpatializeOrigin(ent->origin, 255.0, SOUND_LOOPATTENUATE,
847 &left, &right);
848 left_total += left;
849 right_total += right;
850 }
851
852 if(left_total == 0 && right_total == 0)
853 continue; // not audible
854
855 // allocate a channel
856 ch = S_PickChannel(0, 0);
857 if(!ch)
858 return;
859
860 if(left_total > 255)
861 left_total = 255;
862 if(right_total > 255)
863 right_total = 255;
864 ch->leftvol = left_total;
865 ch->rightvol = right_total;
866 ch->autosound = true; // remove next frame
867 ch->sfx = sfx;
868 /* sometimes, the sc->length argument can become 0, and in that
869 * case we get a SIGFPE in the next modulo. The workaround checks
870 * for this situation and sets the pos and end to zero if true */
871 if(sc->length == 0){
872 ch->pos = ch->end = 0;
873 } else {
874 ch->pos = paintedtime % sc->length;
875 ch->end = paintedtime + sc->length - ch->pos;
876 }
877 }
878 }
879
880
881 /*
882 S_Update
883
884 Called once each time through the main loop
885 */
S_Update(vec3_t origin,vec3_t forward,vec3_t right,vec3_t up)886 void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up){
887 int i;
888 int total;
889 channel_t *ch;
890 channel_t *combine;
891
892 if(!snd_initialized)
893 return;
894
895 if(cls.state != ca_active){
896 S_Stop_f();
897 return;
898 }
899
900 // rebuild scale tables if volume is modified
901 if(s_volume->modified)
902 S_InitScaletable();
903
904 VectorCopy(origin, listener_origin);
905 VectorCopy(forward, listener_forward);
906 VectorCopy(right, listener_right);
907 VectorCopy(up, listener_up);
908
909 combine = NULL;
910
911 // update spatialization for dynamic sounds
912 ch = channels;
913 for(i = 0; i < MAX_CHANNELS; i++, ch++){
914 if(!ch->sfx)
915 continue;
916 if(ch->autosound){ // autosounds are regenerated fresh each frame
917 memset(ch, 0, sizeof(*ch));
918 continue;
919 }
920 S_Spatialize(ch); // respatialize channel
921 if(!ch->leftvol && !ch->rightvol){
922 memset(ch, 0, sizeof(*ch));
923 continue;
924 }
925 }
926
927 // add loopsounds
928 S_AddLoopSounds();
929
930 // debugging output
931 if(s_show->value){
932 total = 0;
933 ch = channels;
934 for(i = 0; i < MAX_CHANNELS; i++, ch++)
935 if(ch->sfx &&(ch->leftvol || ch->rightvol)){
936 Com_Printf("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name);
937 total++;
938 }
939
940 Com_Printf("----(%i)---- painted: %i\n", total, paintedtime);
941 }
942
943 // mix some sound
944 S_Update_();
945 }
946
GetSoundtime(void)947 void GetSoundtime(void){
948 int offset;
949 static int buffers;
950 static int oldoffset;
951 int fullsamples;
952
953 fullsamples = dma.samples / dma.channels;
954
955 // it is possible to miscount buffers if it has wrapped twice between
956 // calls to S_Update. Oh well.
957 offset = SND_GetDMAPos();
958
959 if(offset < oldoffset){
960 buffers++; // buffer wrapped
961
962 if(paintedtime > 0x40000000){ // time to chop things off to avoid 32 bit limits
963 buffers = 0;
964 paintedtime = fullsamples;
965 S_Stop_f();
966 }
967 }
968 oldoffset = offset;
969
970 soundtime = buffers * fullsamples + offset / dma.channels;
971 }
972
973
S_Update_(void)974 void S_Update_(void){
975 unsigned endtime;
976 int samps;
977
978 if(!snd_initialized)
979 return;
980
981 SND_BeginPainting();
982
983 if(!dma.buffer)
984 return;
985
986 // Updates DMA time
987 GetSoundtime();
988
989 // check to make sure that we haven't overshot
990 if(paintedtime < soundtime){
991 Com_DPrintf("S_Update_ : overflow\n");
992 paintedtime = soundtime;
993 }
994
995 // mix ahead of current position
996 endtime = soundtime + s_mixahead->value * dma.rate;
997
998 // mix to an even submission block size
999 endtime = (endtime + dma.chunk - 1)
1000 & ~(dma.chunk - 1);
1001 samps = dma.samples >>(dma.channels - 1);
1002 if(endtime - soundtime > samps)
1003 endtime = soundtime + samps;
1004
1005 S_PaintChannels(endtime);
1006
1007 SND_Submit();
1008 }
1009