]> icculus.org git repositories - divverent/darkplaces.git/blob - snd_main.c
gave names to nearly all structs and enums which should make for better C++ error...
[divverent/darkplaces.git] / snd_main.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // snd_main.c -- main control for any streaming sound output device
21
22 #include "quakedef.h"
23
24 #include "snd_main.h"
25 #include "snd_ogg.h"
26
27
28 void S_Play(void);
29 void S_PlayVol(void);
30 void S_Play2(void);
31 void S_SoundList(void);
32 void S_Update_();
33
34
35 // =======================================================================
36 // Internal sound data & structures
37 // =======================================================================
38
39 channel_t channels[MAX_CHANNELS];
40 unsigned int total_channels;
41
42 int snd_blocked = 0;
43 cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0"};
44 cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1"};
45
46 volatile dma_t *shm = 0;
47 volatile dma_t sn;
48
49 vec3_t listener_origin;
50 matrix4x4_t listener_matrix;
51 vec_t sound_nominal_clip_dist=1000.0;
52 mempool_t *snd_mempool;
53
54 int soundtime;
55 int paintedtime;
56
57 // Linked list of known sfx
58 sfx_t *known_sfx = NULL;
59
60 qboolean sound_started = false;
61 qboolean sound_spatialized = false;
62
63 // Fake dma is a synchronous faking of the DMA progress used for
64 // isolating performance in the renderer.
65 qboolean fakedma = false;
66
67 cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1"};
68 cvar_t volume = {CVAR_SAVE, "volume", "0.7"};
69 cvar_t snd_staticvolume = {CVAR_SAVE, "snd_staticvolume", "1"};
70
71 cvar_t nosound = {0, "nosound", "0"};
72 cvar_t snd_precache = {0, "snd_precache", "1"};
73 cvar_t bgmbuffer = {0, "bgmbuffer", "4096"};
74 cvar_t ambient_level = {0, "ambient_level", "0.3"};
75 cvar_t ambient_fade = {0, "ambient_fade", "100"};
76 cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0"};
77 cvar_t snd_show = {0, "snd_show", "0"};
78 cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1"};
79 cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0"};
80
81 // Ambient sounds
82 sfx_t* ambient_sfxs [2] = { NULL, NULL };
83 const char* ambient_names [2] = { "sound/ambience/water1.wav", "sound/ambience/wind2.wav" };
84
85
86 // ====================================================================
87 // Functions
88 // ====================================================================
89
90 void S_FreeSfx (sfx_t *sfx, qboolean force);
91
92
93 void S_SoundInfo_f(void)
94 {
95         if (!sound_started)
96         {
97                 Con_Print("sound system not started\n");
98                 return;
99         }
100
101         Con_Printf("%5d stereo\n", shm->format.channels - 1);
102         Con_Printf("%5d samples\n", shm->samples);
103         Con_Printf("%5d samplepos\n", shm->samplepos);
104         Con_Printf("%5d samplebits\n", shm->format.width * 8);
105         Con_Printf("%5d speed\n", shm->format.speed);
106         Con_Printf("%p dma buffer\n", shm->buffer);
107         Con_Printf("%5u total_channels\n", total_channels);
108 }
109
110
111 void S_Startup(void)
112 {
113         if (!snd_initialized.integer)
114                 return;
115
116         shm = &sn;
117         memset((void *)shm, 0, sizeof(*shm));
118
119         // create a piece of DMA memory
120         if (fakedma)
121         {
122                 shm->format.width = 2;
123                 shm->format.speed = 22050;
124                 shm->format.channels = 2;
125                 shm->samples = 32768;
126                 shm->samplepos = 0;
127                 shm->buffer = (qbyte *)Mem_Alloc(snd_mempool, shm->format.channels * shm->samples * shm->format.width);
128         }
129         else
130         {
131                 if (!SNDDMA_Init())
132                 {
133                         Con_Print("S_Startup: SNDDMA_Init failed.\n");
134                         shm = NULL;
135                         sound_started = false;
136                         sound_spatialized = false;
137                         return;
138                 }
139         }
140
141         sound_started = true;
142
143         Con_Printf("Sound format: %dHz, %d bit, %d channels\n", shm->format.speed,
144                            shm->format.width * 8, shm->format.channels);
145 }
146
147 void S_Shutdown(void)
148 {
149         if (!sound_started)
150                 return;
151
152         if (fakedma)
153                 Mem_Free(shm->buffer);
154         else
155                 SNDDMA_Shutdown();
156
157         shm = NULL;
158         sound_started = false;
159         sound_spatialized = false;
160 }
161
162 void S_Restart_f(void)
163 {
164         S_Shutdown();
165         S_Startup();
166 }
167
168 /*
169 ================
170 S_Init
171 ================
172 */
173 void S_Init(void)
174 {
175         Con_DPrint("\nSound Initialization\n");
176
177         Cvar_RegisterVariable(&volume);
178         Cvar_RegisterVariable(&bgmvolume);
179         Cvar_RegisterVariable(&snd_staticvolume);
180
181 // COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio)
182         if (COM_CheckParm("-nosound") || COM_CheckParm("-safe"))
183                 return;
184
185         snd_mempool = Mem_AllocPool("sound", 0, NULL);
186
187 // COMMANDLINEOPTION: Sound: -simsound runs sound mixing but with no output
188         if (COM_CheckParm("-simsound"))
189                 fakedma = true;
190
191         Cmd_AddCommand("play", S_Play);
192         Cmd_AddCommand("play2", S_Play2);
193         Cmd_AddCommand("playvol", S_PlayVol);
194         Cmd_AddCommand("stopsound", S_StopAllSounds);
195         Cmd_AddCommand("soundlist", S_SoundList);
196         Cmd_AddCommand("soundinfo", S_SoundInfo_f);
197         Cmd_AddCommand("snd_restart", S_Restart_f);
198
199         Cvar_RegisterVariable(&nosound);
200         Cvar_RegisterVariable(&snd_precache);
201         Cvar_RegisterVariable(&snd_initialized);
202         Cvar_RegisterVariable(&snd_streaming);
203         Cvar_RegisterVariable(&bgmbuffer);
204         Cvar_RegisterVariable(&ambient_level);
205         Cvar_RegisterVariable(&ambient_fade);
206         Cvar_RegisterVariable(&snd_noextraupdate);
207         Cvar_RegisterVariable(&snd_show);
208         Cvar_RegisterVariable(&_snd_mixahead);
209         Cvar_RegisterVariable(&snd_swapstereo); // for people with backwards sound wiring
210
211         Cvar_SetValueQuick(&snd_initialized, true);
212
213         known_sfx = NULL;
214
215         total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;   // no statics
216         memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
217
218         OGG_OpenLibrary ();
219 }
220
221
222 /*
223 ================
224 S_Terminate
225
226 Shutdown and free all resources
227 ================
228 */
229 void S_Terminate (void)
230 {
231         S_Shutdown ();
232         OGG_CloseLibrary ();
233
234         // Free all SFXs
235         while (known_sfx != NULL)
236                 S_FreeSfx (known_sfx, true);
237
238         Cvar_SetValueQuick (&snd_initialized, false);
239         Mem_FreePool (&snd_mempool);
240 }
241
242
243 // =======================================================================
244 // Load a sound
245 // =======================================================================
246
247 /*
248 ==================
249 S_FindName
250
251 ==================
252 */
253 sfx_t *S_FindName (const char *name)
254 {
255         sfx_t *sfx;
256
257         if (!snd_initialized.integer)
258                 return NULL;
259
260         if (strlen (name) >= sizeof (sfx->name))
261         {
262                 Con_Printf ("S_FindName: sound name too long (%s)", name);
263                 return NULL;
264         }
265
266         // Look for this sound in the list of known sfx
267         for (sfx = known_sfx; sfx != NULL; sfx = sfx->next)
268                 if(!strcmp (sfx->name, name))
269                         return sfx;
270
271         // Add a sfx_t struct for this sound
272         sfx = (sfx_t *)Mem_Alloc (snd_mempool, sizeof (*sfx));
273         memset (sfx, 0, sizeof(*sfx));
274         strlcpy (sfx->name, name, sizeof (sfx->name));
275         sfx->memsize = sizeof(*sfx);
276         sfx->next = known_sfx;
277         known_sfx = sfx;
278
279         return sfx;
280 }
281
282
283 /*
284 ==================
285 S_FreeSfx
286
287 ==================
288 */
289 void S_FreeSfx (sfx_t *sfx, qboolean force)
290 {
291         unsigned int i;
292
293         // Never free a locked sfx unless forced
294         if (!force && (sfx->locks > 0 || (sfx->flags & SFXFLAG_PERMANENTLOCK)))
295                 return;
296
297         Con_DPrintf ("S_FreeSfx: freeing %s\n", sfx->name);
298
299         // Remove it from the list of known sfx
300         if (sfx == known_sfx)
301                 known_sfx = known_sfx->next;
302         else
303         {
304                 sfx_t *prev_sfx;
305
306                 for (prev_sfx = known_sfx; prev_sfx != NULL; prev_sfx = prev_sfx->next)
307                         if (prev_sfx->next == sfx)
308                         {
309                                 prev_sfx->next = sfx->next;
310                                 break;
311                         }
312                 if (prev_sfx == NULL)
313                 {
314                         Con_Printf ("S_FreeSfx: Can't find SFX %s in the list!\n", sfx->name);
315                         return;
316                 }
317         }
318
319         // Stop all channels using this sfx
320         for (i = 0; i < total_channels; i++)
321                 if (channels[i].sfx == sfx)
322                         S_StopChannel (i);
323
324         // Free it
325         if (sfx->fetcher != NULL && sfx->fetcher->free != NULL)
326                 sfx->fetcher->free (sfx);
327         Mem_Free (sfx);
328 }
329
330
331 /*
332 ==================
333 S_ServerSounds
334
335 ==================
336 */
337 void S_ServerSounds (char serversound [][MAX_QPATH], unsigned int numsounds)
338 {
339         sfx_t *sfx;
340         sfx_t *sfxnext;
341         unsigned int i;
342
343         // Start the ambient sounds and make them loop
344         for (i = 0; i < sizeof (ambient_sfxs) / sizeof (ambient_sfxs[0]); i++)
345         {
346                 // Precache it if it's not done (request a lock to make sure it will never be freed)
347                 if (ambient_sfxs[i] == NULL)
348                         ambient_sfxs[i] = S_PrecacheSound (ambient_names[i], false, true);
349                 if (ambient_sfxs[i] != NULL)
350                 {
351                         // Add a lock to the SFX while playing. It will be
352                         // removed by S_StopAllSounds at the end of the level
353                         S_LockSfx (ambient_sfxs[i]);
354
355                         channels[i].sfx = ambient_sfxs[i];
356                         channels[i].flags |= CHANNELFLAG_FORCELOOP;
357                         channels[i].master_vol = 0;
358                 }
359         }
360
361         // Remove 1 lock from all sfx with the SFXFLAG_SERVERSOUND flag, and remove the flag
362         for (sfx = known_sfx; sfx != NULL; sfx = sfx->next)
363                 if (sfx->flags & SFXFLAG_SERVERSOUND)
364                 {
365                         S_UnlockSfx (sfx);
366                         sfx->flags &= ~SFXFLAG_SERVERSOUND;
367                 }
368
369         // Add 1 lock and the SFXFLAG_SERVERSOUND flag to each sfx in "serversound"
370         for (i = 1; i < numsounds; i++)
371         {
372                 sfx = S_FindName (serversound[i]);
373                 if (sfx != NULL)
374                 {
375                         S_LockSfx (sfx);
376                         sfx->flags |= SFXFLAG_SERVERSOUND;
377                 }
378         }
379
380         // Free all unlocked sfx
381         for (sfx = known_sfx;sfx;sfx = sfxnext)
382         {
383                 sfxnext = sfx->next;
384                 S_FreeSfx (sfx, false);
385         }
386 }
387
388
389 /*
390 ==================
391 S_PrecacheSound
392
393 ==================
394 */
395 sfx_t *S_PrecacheSound (const char *name, qboolean complain, qboolean lock)
396 {
397         sfx_t *sfx;
398
399         if (!snd_initialized.integer)
400                 return NULL;
401
402         sfx = S_FindName (name);
403         if (sfx == NULL)
404                 return NULL;
405
406         if (lock)
407                 S_LockSfx (sfx);
408
409         if (!nosound.integer && snd_precache.integer)
410                 S_LoadSound(sfx, complain);
411
412         return sfx;
413 }
414
415 /*
416 ==================
417 S_LockSfx
418
419 Add a lock to a SFX
420 ==================
421 */
422 void S_LockSfx (sfx_t *sfx)
423 {
424         sfx->locks++;
425 }
426
427 /*
428 ==================
429 S_UnlockSfx
430
431 Remove a lock from a SFX
432 ==================
433 */
434 void S_UnlockSfx (sfx_t *sfx)
435 {
436         sfx->locks--;
437 }
438
439
440 //=============================================================================
441
442 /*
443 =================
444 SND_PickChannel
445
446 Picks a channel based on priorities, empty slots, number of channels
447 =================
448 */
449 channel_t *SND_PickChannel(int entnum, int entchannel)
450 {
451         int ch_idx;
452         int first_to_die;
453         int life_left;
454         channel_t* ch;
455
456 // Check for replacement sound, or find the best one to replace
457         first_to_die = -1;
458         life_left = 0x7fffffff;
459         for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++)
460         {
461                 ch = &channels[ch_idx];
462                 if (entchannel != 0             // channel 0 never overrides
463                 && ch->entnum == entnum
464                 && (ch->entchannel == entchannel || entchannel == -1) )
465                 {       // always override sound from same entity
466                         first_to_die = ch_idx;
467                         break;
468                 }
469
470                 // don't let monster sounds override player sounds
471                 if (ch->entnum == cl.viewentity && entnum != cl.viewentity && ch->sfx)
472                         continue;
473
474                 // don't override looped sounds
475                 if ((ch->flags & CHANNELFLAG_FORCELOOP) != 0 ||
476                         (ch->sfx != NULL && ch->sfx->loopstart >= 0))
477                         continue;
478
479                 if (ch->end - paintedtime < life_left)
480                 {
481                         life_left = ch->end - paintedtime;
482                         first_to_die = ch_idx;
483                 }
484         }
485
486         if (first_to_die == -1)
487                 return NULL;
488
489         S_StopChannel (first_to_die);
490
491         return &channels[first_to_die];
492 }
493
494 /*
495 =================
496 SND_Spatialize
497
498 Spatializes a channel
499 =================
500 */
501 void SND_Spatialize(channel_t *ch, qboolean isstatic)
502 {
503         vec_t dist, scale, pan;
504         vec3_t source_vec;
505
506         // anything coming from the view entity will always be full volume
507         // LordHavoc: make sounds with ATTN_NONE have no spatialization
508         if (ch->entnum == cl.viewentity || ch->dist_mult == 0)
509         {
510                 ch->leftvol = ch->master_vol;
511                 ch->rightvol = ch->master_vol;
512         }
513         else
514         {
515                 // update sound origin if we know about the entity
516                 if (ch->entnum > 0 && cls.state == ca_connected && cl_entities[ch->entnum].render.model)
517                 {
518                         //Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl_entities[ch->entnum].persistent.trail_origin[0], cl_entities[ch->entnum].persistent.trail_origin[1], cl_entities[ch->entnum].persistent.trail_origin[2]);
519                         VectorCopy(cl_entities[ch->entnum].persistent.trail_origin, ch->origin);
520                 }
521
522                 // calculate stereo seperation and distance attenuation
523                 Matrix4x4_Transform(&listener_matrix, ch->origin, source_vec);
524                 dist = VectorNormalizeLength(source_vec);
525                 // distance
526                 scale = ch->master_vol * (1.0 - (dist * ch->dist_mult));
527                 // panning
528                 pan = scale * source_vec[1];
529                 // calculate the volumes
530                 ch->leftvol = (int) (scale + pan);
531                 ch->rightvol = (int) (scale - pan);
532                 //Con_Printf("%f %f %f:%f %f %f:%f %f:%d %d\n", ch->origin[0], ch->origin[1], ch->origin[2], source_vec[0], source_vec[1], source_vec[2], scale, pan, ch->leftvol, ch->rightvol);
533         }
534
535         // Adjust volume of static sounds
536         if (isstatic)
537         {
538                 ch->leftvol *= snd_staticvolume.value;
539                 ch->rightvol *= snd_staticvolume.value;
540         }
541
542         // clamp volumes
543         ch->leftvol = bound(0, ch->leftvol, 255);
544         ch->rightvol = bound(0, ch->rightvol, 255);
545 }
546
547
548 // =======================================================================
549 // Start a sound effect
550 // =======================================================================
551
552 void S_PlaySfxOnChannel (sfx_t *sfx, channel_t *target_chan, unsigned int flags, vec3_t origin, float fvol, float attenuation, qboolean isstatic)
553 {
554         // Initialize the channel
555         memset (target_chan, 0, sizeof (*target_chan));
556         VectorCopy (origin, target_chan->origin);
557         target_chan->master_vol = fvol * 255;
558         target_chan->sfx = sfx;
559         target_chan->end = paintedtime + sfx->total_length;
560         target_chan->lastptime = paintedtime;
561         target_chan->flags = flags;
562
563         // If it's a static sound
564         if (isstatic)
565         {
566                 if (sfx->loopstart == -1)
567                         Con_DPrintf("Quake compatibility warning: Static sound \"%s\" is not looped\n", sfx->name);
568                 target_chan->dist_mult = attenuation / (64.0f * sound_nominal_clip_dist);
569         }
570         else
571                 target_chan->dist_mult = attenuation / sound_nominal_clip_dist;
572
573         // Lock the SFX during play
574         S_LockSfx (sfx);
575 }
576
577
578 int S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
579 {
580         channel_t *target_chan, *check;
581         int             ch_idx;
582         int             skip;
583
584         if (!sound_started || !sfx || nosound.integer)
585                 return -1;
586         if (!sfx->fetcher)
587         {
588                 Con_DPrintf ("S_StartSound: \"%s\" hasn't been precached\n", sfx->name);
589                 return -1;
590         }
591
592         if (entnum && entnum >= cl_max_entities)
593                 CL_ExpandEntities(entnum);
594
595         // Pick a channel to play on
596         target_chan = SND_PickChannel(entnum, entchannel);
597         if (!target_chan)
598                 return -1;
599
600         S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_NONE, origin, fvol, attenuation, false);
601         target_chan->entnum = entnum;
602         target_chan->entchannel = entchannel;
603
604         SND_Spatialize(target_chan, false);
605
606         // if an identical sound has also been started this frame, offset the pos
607         // a bit to keep it from just making the first one louder
608         check = &channels[NUM_AMBIENTS];
609         for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++)
610         {
611                 if (check == target_chan)
612                         continue;
613                 if (check->sfx == sfx && !check->pos)
614                 {
615                         skip = 0.1 * sfx->format.speed;
616                         if (skip > (int)sfx->total_length)
617                                 skip = (int)sfx->total_length;
618                         if (skip > 0)
619                                 skip = rand() % skip;
620                         target_chan->pos += skip;
621                         target_chan->end -= skip;
622                         break;
623                 }
624         }
625
626         return (target_chan - channels);
627 }
628
629 void S_StopChannel (unsigned int channel_ind)
630 {
631         channel_t *ch;
632
633         if (channel_ind >= total_channels)
634                 return;
635
636         ch = &channels[channel_ind];
637         if (ch->sfx != NULL)
638         {
639                 sfx_t *sfx = ch->sfx;
640
641                 if (sfx->fetcher != NULL)
642                 {
643                         snd_fetcher_endsb_t fetcher_endsb = sfx->fetcher->endsb;
644                         if (fetcher_endsb != NULL)
645                                 fetcher_endsb (ch);
646                 }
647
648                 // Remove the lock it holds
649                 S_UnlockSfx (sfx);
650
651                 ch->sfx = NULL;
652         }
653         ch->end = 0;
654 }
655
656
657 qboolean S_SetChannelFlag (unsigned int ch_ind, unsigned int flag, qboolean value)
658 {
659         if (ch_ind >= total_channels)
660                 return false;
661
662         if (flag != CHANNELFLAG_FORCELOOP &&
663                 flag != CHANNELFLAG_PAUSED &&
664                 flag != CHANNELFLAG_FULLVOLUME)
665                 return false;
666
667         if (value)
668                 channels[ch_ind].flags |= flag;
669         else
670                 channels[ch_ind].flags &= ~flag;
671
672         return true;
673 }
674
675 void S_StopSound(int entnum, int entchannel)
676 {
677         unsigned int i;
678
679         for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++)
680                 if (channels[i].entnum == entnum && channels[i].entchannel == entchannel)
681                 {
682                         S_StopChannel (i);
683                         return;
684                 }
685 }
686
687 void S_StopAllSounds (void)
688 {
689         unsigned int i;
690         unsigned char *pbuf;
691
692         if (!sound_started)
693                 return;
694
695         for (i = 0; i < total_channels; i++)
696                 S_StopChannel (i);
697
698         total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;   // no statics
699         memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
700
701         // Clear sound buffer
702         pbuf = (unsigned char *)S_LockBuffer();
703         if (pbuf != NULL)
704         {
705                 int setsize = shm->samples * shm->format.width;
706                 int     clear = (shm->format.width == 1) ? 0x80 : 0;
707
708                 // FIXME: is it (still) true? (check with OSS and ALSA)
709                 // on i586/i686 optimized versions of glibc, glibc *wrongly* IMO,
710                 // reads the memory area before writing to it causing a seg fault
711                 // since the memory is PROT_WRITE only and not PROT_READ|PROT_WRITE
712                 //memset(shm->buffer, clear, shm->samples * shm->format.width);
713                 while (setsize--)
714                         *pbuf++ = clear;
715
716                 S_UnlockBuffer ();
717         }
718 }
719
720 void S_PauseGameSounds (qboolean toggle)
721 {
722         unsigned int i;
723
724         for (i = 0; i < total_channels; i++)
725         {
726                 channel_t *ch;
727
728                 ch = &channels[i];
729                 if (ch->sfx != NULL && ! (ch->flags & CHANNELFLAG_LOCALSOUND))
730                         S_SetChannelFlag (i, CHANNELFLAG_PAUSED, toggle);
731         }
732 }
733
734 void S_SetChannelVolume (unsigned int ch_ind, float fvol)
735 {
736         channels[ch_ind].master_vol = fvol * 255;
737 }
738
739
740 /*
741 =================
742 S_StaticSound
743 =================
744 */
745 void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
746 {
747         channel_t       *target_chan;
748
749         if (!sound_started || !sfx || nosound.integer)
750                 return;
751         if (!sfx->fetcher)
752         {
753                 Con_DPrintf ("S_StaticSound: \"%s\" hasn't been precached\n", sfx->name);
754                 return;
755         }
756
757         if (total_channels == MAX_CHANNELS)
758         {
759                 Con_Print("S_StaticSound: total_channels == MAX_CHANNELS\n");
760                 return;
761         }
762
763         target_chan = &channels[total_channels++];
764         S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_FORCELOOP, origin, fvol, attenuation, true);
765
766         SND_Spatialize (target_chan, true);
767 }
768
769
770 //=============================================================================
771
772 /*
773 ===================
774 S_UpdateAmbientSounds
775
776 ===================
777 */
778 void S_UpdateAmbientSounds (void)
779 {
780         float           vol;
781         int                     ambient_channel;
782         channel_t       *chan;
783         qbyte           ambientlevels[NUM_AMBIENTS];
784
785         if (ambient_level.value <= 0 || !cl.worldmodel || !cl.worldmodel->brush.AmbientSoundLevelsForPoint)
786                 return;
787
788         cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels));
789
790         // Calc ambient sound levels
791         for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
792         {
793                 chan = &channels[ambient_channel];
794                 if (chan->sfx == NULL || chan->sfx->fetcher == NULL)
795                         continue;
796
797                 vol = ambient_level.value * ambientlevels[ambient_channel];
798                 if (vol < 8)
799                         vol = 0;
800
801                 // Don't adjust volume too fast
802                 if (chan->master_vol < vol)
803                 {
804                         chan->master_vol += host_realframetime * ambient_fade.value;
805                         if (chan->master_vol > vol)
806                                 chan->master_vol = vol;
807                 }
808                 else if (chan->master_vol > vol)
809                 {
810                         chan->master_vol -= host_realframetime * ambient_fade.value;
811                         if (chan->master_vol < vol)
812                                 chan->master_vol = vol;
813                 }
814
815                 chan->leftvol = chan->rightvol = chan->master_vol;
816         }
817 }
818
819
820 /*
821 ============
822 S_Update
823
824 Called once each time through the main loop
825 ============
826 */
827 void S_Update(const matrix4x4_t *listenermatrix)
828 {
829         unsigned int i, j, total;
830         channel_t *ch, *combine;
831
832         if (!snd_initialized.integer || (snd_blocked > 0))
833                 return;
834
835         Matrix4x4_Invert_Simple(&listener_matrix, listenermatrix);
836         Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin);
837
838         // update general area ambient sound sources
839         S_UpdateAmbientSounds ();
840
841         combine = NULL;
842
843         // update spatialization for static and dynamic sounds
844         ch = channels+NUM_AMBIENTS;
845         for (i=NUM_AMBIENTS ; i<total_channels; i++, ch++)
846         {
847                 if (!ch->sfx)
848                         continue;
849                 SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS);         // respatialize channel
850                 if (!ch->leftvol && !ch->rightvol)
851                         continue;
852
853                 // try to combine static sounds with a previous channel of the same
854                 // sound effect so we don't mix five torches every frame
855                 if (i > MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS)
856                 {
857                         // see if it can just use the last one
858                         if (combine && combine->sfx == ch->sfx)
859                         {
860                                 combine->leftvol += ch->leftvol;
861                                 combine->rightvol += ch->rightvol;
862                                 ch->leftvol = ch->rightvol = 0;
863                                 continue;
864                         }
865                         // search for one
866                         combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;
867                         for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; j<i; j++, combine++)
868                                 if (combine->sfx == ch->sfx)
869                                         break;
870
871                         if (j == total_channels)
872                                 combine = NULL;
873                         else
874                         {
875                                 if (combine != ch)
876                                 {
877                                         combine->leftvol += ch->leftvol;
878                                         combine->rightvol += ch->rightvol;
879                                         ch->leftvol = ch->rightvol = 0;
880                                 }
881                                 continue;
882                         }
883                 }
884         }
885
886         sound_spatialized = true;
887
888         // debugging output
889         if (snd_show.integer)
890         {
891                 total = 0;
892                 ch = channels;
893                 for (i=0 ; i<total_channels; i++, ch++)
894                         if (ch->sfx && (ch->leftvol || ch->rightvol) )
895                                 total++;
896
897                 Con_Printf("----(%u)----\n", total);
898         }
899
900         S_Update_();
901 }
902
903 void GetSoundtime(void)
904 {
905         int             samplepos;
906         static  int             buffers;
907         static  int             oldsamplepos;
908         int             fullsamples;
909
910         fullsamples = shm->samples / shm->format.channels;
911
912         // it is possible to miscount buffers if it has wrapped twice between
913         // calls to S_Update.  Oh well.
914         samplepos = SNDDMA_GetDMAPos();
915
916         if (samplepos < oldsamplepos)
917         {
918                 buffers++;                                      // buffer wrapped
919
920                 if (paintedtime > 0x40000000)
921                 {       // time to chop things off to avoid 32 bit limits
922                         buffers = 0;
923                         paintedtime = fullsamples;
924                         S_StopAllSounds ();
925                 }
926         }
927         oldsamplepos = samplepos;
928
929         soundtime = buffers * fullsamples + samplepos / shm->format.channels;
930 }
931
932 void S_ExtraUpdate (void)
933 {
934         if (snd_noextraupdate.integer || !sound_spatialized)
935                 return;
936
937         S_Update_();
938 }
939
940 void S_Update_(void)
941 {
942         unsigned        endtime;
943         int                             samps;
944
945         if (!sound_started || (snd_blocked > 0))
946                 return;
947
948         // Updates DMA time
949         GetSoundtime();
950
951         // check to make sure that we haven't overshot
952         if (paintedtime < soundtime)
953                 paintedtime = soundtime;
954
955         // mix ahead of current position
956         endtime = soundtime + _snd_mixahead.value * shm->format.speed;
957         samps = shm->samples >> (shm->format.channels - 1);
958         if (endtime > (unsigned int)(soundtime + samps))
959                 endtime = soundtime + samps;
960
961         S_PaintChannels (endtime);
962
963         SNDDMA_Submit ();
964 }
965
966 /*
967 ===============================================================================
968
969 console functions
970
971 ===============================================================================
972 */
973
974 static void S_Play_Common(float fvol, float attenuation)
975 {
976         int     i, ch_ind;
977         char name[256];
978         sfx_t   *sfx;
979
980         i = 1;
981         while (i<Cmd_Argc())
982         {
983                 // Get the name
984                 strlcpy(name, Cmd_Argv(i), sizeof(name));
985                 if (!strrchr(name, '.'))
986                         strlcat(name, ".wav", sizeof(name));
987                 i++;
988
989                 // If we need to get the volume from the command line
990                 if (fvol == -1.0f)
991                 {
992                         fvol = atof(Cmd_Argv(i));
993                         i++;
994                 }
995
996                 sfx = S_PrecacheSound (name, true, false);
997                 if (sfx)
998                 {
999                         ch_ind = S_StartSound(-1, 0, sfx, listener_origin, fvol, attenuation);
1000
1001                         // Free the sfx if the file didn't exist
1002                         if (ch_ind < 0)
1003                                 S_FreeSfx (sfx, false);
1004                         else
1005                                 channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND;
1006                 }
1007         }
1008 }
1009
1010 void S_Play(void)
1011 {
1012         S_Play_Common (1.0f, 1.0f);
1013 }
1014
1015 void S_Play2(void)
1016 {
1017         S_Play_Common (1.0f, 0.0f);
1018 }
1019
1020 void S_PlayVol(void)
1021 {
1022         S_Play_Common (-1.0f, 0.0f);
1023 }
1024
1025 void S_SoundList(void)
1026 {
1027         unsigned int i;
1028         sfx_t   *sfx;
1029         int             size, total;
1030
1031         total = 0;
1032         for (sfx = known_sfx, i = 0; sfx != NULL; sfx = sfx->next, i++)
1033         {
1034                 if (sfx->fetcher != NULL)
1035                 {
1036                         size = (int)sfx->memsize;
1037                         total += size;
1038                         Con_Printf ("%c%c%c%c(%2db, %6s) %8i : %s\n",
1039                                                 (sfx->loopstart >= 0) ? 'L' : ' ',
1040                                                 (sfx->flags & SFXFLAG_STREAMED) ? 'S' : ' ',
1041                                                 (sfx->locks > 0) ? 'K' : ' ',
1042                                                 (sfx->flags & SFXFLAG_PERMANENTLOCK) ? 'P' : ' ',
1043                                                 sfx->format.width * 8,
1044                                                 (sfx->format.channels == 1) ? "mono" : "stereo",
1045                                                 size,
1046                                                 sfx->name);
1047                 }
1048                 else
1049                         Con_Printf ("    (  unknown  ) unloaded : %s\n", sfx->name);
1050         }
1051         Con_Printf("Total resident: %i\n", total);
1052 }
1053
1054
1055 qboolean S_LocalSound (const char *sound)
1056 {
1057         sfx_t   *sfx;
1058         int             ch_ind;
1059
1060         if (!snd_initialized.integer || nosound.integer)
1061                 return true;
1062
1063         sfx = S_PrecacheSound (sound, true, false);
1064         if (!sfx)
1065         {
1066                 Con_Printf("S_LocalSound: can't precache %s\n", sound);
1067                 return false;
1068         }
1069
1070         // Local sounds must not be freed
1071         sfx->flags |= SFXFLAG_PERMANENTLOCK;
1072
1073         ch_ind = S_StartSound (cl.viewentity, 0, sfx, vec3_origin, 1, 0);
1074         if (ch_ind < 0)
1075                 return false;
1076
1077         channels[ch_ind].flags |= CHANNELFLAG_LOCALSOUND;
1078         return true;
1079 }