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