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