A minor removal of a few pieces of dead code. Nothing major. This is
[divverent/darkplaces.git] / snd_dma.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_dma.c -- main control for any streaming sound output device
21
22 #include "quakedef.h"
23
24 #ifdef _WIN32
25 #include "winquake.h"
26 #endif
27
28 void S_Play(void);
29 void S_PlayVol(void);
30 void S_SoundList(void);
31 void S_Update_();
32 void S_StopAllSounds(qboolean clear);
33 void S_StopAllSoundsC(void);
34
35 // =======================================================================
36 // Internal sound data & structures
37 // =======================================================================
38
39 channel_t   channels[MAX_CHANNELS];
40 int                     total_channels;
41
42 int                             snd_blocked = 0;
43 static qboolean snd_ambient = 1;
44 qboolean                snd_initialized = false;
45
46 // pointer should go away
47 volatile dma_t  *shm = 0;
48 volatile dma_t sn;
49
50 vec3_t          listener_origin;
51 vec3_t          listener_forward;
52 vec3_t          listener_right;
53 vec3_t          listener_up;
54 vec_t           sound_nominal_clip_dist=1000.0;
55
56 int                     soundtime;              // sample PAIRS
57 int             paintedtime;    // sample PAIRS
58
59
60 //LordHavoc: increased the client sound limit from 512 to 4096 for the Nehahra movie
61 #define MAX_SFX         4096
62 sfx_t           *known_sfx;             // hunk allocated [MAX_SFX]
63 int                     num_sfx;
64
65 sfx_t           *ambient_sfx[NUM_AMBIENTS];
66
67 int sound_started = 0;
68
69 cvar_t bgmvolume = {CVAR_SAVE, "bgmvolume", "1"};
70 cvar_t volume = {CVAR_SAVE, "volume", "0.7"};
71
72 cvar_t nosound = {0, "nosound", "0"};
73 cvar_t precache = {0, "precache", "1"};
74 cvar_t bgmbuffer = {0, "bgmbuffer", "4096"};
75 cvar_t ambient_level = {0, "ambient_level", "0.3"};
76 cvar_t ambient_fade = {0, "ambient_fade", "100"};
77 cvar_t snd_noextraupdate = {0, "snd_noextraupdate", "0"};
78 cvar_t snd_show = {0, "snd_show", "0"};
79 cvar_t _snd_mixahead = {CVAR_SAVE, "_snd_mixahead", "0.1"};
80 cvar_t snd_swapstereo = {CVAR_SAVE, "snd_swapstereo", "0"};
81
82
83 // ====================================================================
84 // User-setable variables
85 // ====================================================================
86
87
88 //
89 // Fake dma is a synchronous faking of the DMA progress used for
90 // isolating performance in the renderer.  The fakedma_updates is
91 // number of times S_Update() is called per second.
92 //
93
94 qboolean fakedma = false;
95 int fakedma_updates = 15;
96
97
98 void S_AmbientOff (void)
99 {
100         snd_ambient = false;
101 }
102
103
104 void S_AmbientOn (void)
105 {
106         snd_ambient = true;
107 }
108
109
110 void S_SoundInfo_f(void)
111 {
112         if (!sound_started || !shm)
113         {
114                 Con_Printf ("sound system not started\n");
115                 return;
116         }
117         
118     Con_Printf("%5d stereo\n", shm->channels - 1);
119     Con_Printf("%5d samples\n", shm->samples);
120     Con_Printf("%5d samplepos\n", shm->samplepos);
121     Con_Printf("%5d samplebits\n", shm->samplebits);
122     Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
123     Con_Printf("%5d speed\n", shm->speed);
124     Con_Printf("0x%x dma buffer\n", shm->buffer);
125         Con_Printf("%5d total_channels\n", total_channels);
126 }
127
128
129 /*
130 ================
131 S_Startup
132 ================
133 */
134
135 void S_Startup (void)
136 {
137         int             rc;
138
139         if (!snd_initialized)
140                 return;
141
142         if (!fakedma)
143         {
144                 rc = SNDDMA_Init();
145
146                 if (!rc)
147                 {
148                         Con_Printf("S_Startup: SNDDMA_Init failed.\n");
149                         sound_started = 0;
150                         return;
151                 }
152         }
153
154         sound_started = 1;
155 }
156
157
158 void S_Play2(void);
159
160 mempool_t *snd_mempool;
161
162 /*
163 ================
164 S_Init
165 ================
166 */
167 void S_Init (void)
168 {
169         Con_Printf("\nSound Initialization\n");
170
171         Cvar_RegisterVariable(&volume);
172         Cvar_RegisterVariable(&bgmvolume);
173
174         if (COM_CheckParm("-nosound"))
175                 return;
176
177         snd_mempool = Mem_AllocPool("sound");
178
179         if (COM_CheckParm("-simsound"))
180                 fakedma = true;
181
182         Cmd_AddCommand("play", S_Play);
183         Cmd_AddCommand("play2", S_Play2);
184         Cmd_AddCommand("playvol", S_PlayVol);
185         Cmd_AddCommand("stopsound", S_StopAllSoundsC);
186         Cmd_AddCommand("soundlist", S_SoundList);
187         Cmd_AddCommand("soundinfo", S_SoundInfo_f);
188
189         Cvar_RegisterVariable(&nosound);
190         Cvar_RegisterVariable(&precache);
191         Cvar_RegisterVariable(&bgmbuffer);
192         Cvar_RegisterVariable(&ambient_level);
193         Cvar_RegisterVariable(&ambient_fade);
194         Cvar_RegisterVariable(&snd_noextraupdate);
195         Cvar_RegisterVariable(&snd_show);
196         Cvar_RegisterVariable(&_snd_mixahead);
197         Cvar_RegisterVariable(&snd_swapstereo); // LordHavoc: for people with backwards sound wiring
198
199         snd_initialized = true;
200
201         known_sfx = Mem_Alloc(snd_mempool, MAX_SFX*sizeof(sfx_t));
202         num_sfx = 0;
203
204         S_Startup ();
205
206         SND_InitScaletable ();
207
208 // create a piece of DMA memory
209
210         if (fakedma)
211         {
212                 shm = (void *) Mem_Alloc(snd_mempool, sizeof(*shm));
213                 shm->splitbuffer = 0;
214                 shm->samplebits = 16;
215                 shm->speed = 22050;
216                 shm->channels = 2;
217                 shm->samples = 32768;
218                 shm->samplepos = 0;
219                 shm->soundalive = true;
220                 shm->gamealive = true;
221                 shm->submission_chunk = 1;
222                 shm->buffer = Mem_Alloc(snd_mempool, shm->channels * shm->samples * (shm->samplebits / 8));
223         }
224
225         if (!sound_started)
226                 return;
227
228         Con_Printf ("Sound sampling rate: %i\n", shm->speed);
229
230         // provides a tick sound until washed clean
231
232         ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav");
233         ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav");
234
235         S_StopAllSounds (true);
236 }
237
238
239 // =======================================================================
240 // Shutdown sound engine
241 // =======================================================================
242
243 void S_Shutdown(void)
244 {
245         if (!sound_started)
246                 return;
247
248         if (shm)
249                 shm->gamealive = 0;
250
251         shm = 0;
252         sound_started = 0;
253
254         if (!fakedma)
255                 SNDDMA_Shutdown();
256 }
257
258
259 // =======================================================================
260 // Load a sound
261 // =======================================================================
262
263 /*
264 ==================
265 S_FindName
266
267 ==================
268 */
269 sfx_t *S_FindName (char *name)
270 {
271         int             i;
272         sfx_t   *sfx;
273
274         if (!name)
275                 Host_Error ("S_FindName: NULL\n");
276
277         if (strlen(name) >= MAX_QPATH)
278                 Host_Error ("Sound name too long: %s", name);
279
280 // see if already loaded
281         for (i=0 ; i < num_sfx ; i++)
282                 if (!strcmp(known_sfx[i].name, name))
283                 {
284                         return &known_sfx[i];
285                 }
286
287         if (num_sfx == MAX_SFX)
288                 Sys_Error ("S_FindName: out of sfx_t");
289         
290         sfx = &known_sfx[i];
291         strcpy (sfx->name, name);
292
293         num_sfx++;
294         
295         return sfx;
296 }
297
298
299 /*
300 ==================
301 S_TouchSound
302
303 ==================
304 */
305 void S_TouchSound (char *name)
306 {
307         sfx_t   *sfx;
308         
309         if (!sound_started)
310                 return;
311
312         sfx = S_FindName (name);
313 }
314
315 /*
316 ==================
317 S_PrecacheSound
318
319 ==================
320 */
321 sfx_t *S_PrecacheSound (char *name)
322 {
323         sfx_t   *sfx;
324
325         if (!sound_started || nosound.integer)
326                 return NULL;
327
328         sfx = S_FindName (name);
329
330 // cache it in
331         if (precache.integer)
332                 S_LoadSound (sfx);
333
334         return sfx;
335 }
336
337
338 //=============================================================================
339
340 /*
341 =================
342 SND_PickChannel
343 =================
344 */
345 channel_t *SND_PickChannel(int entnum, int entchannel)
346 {
347     int ch_idx;
348     int first_to_die;
349     int life_left;
350
351 // Check for replacement sound, or find the best one to replace
352     first_to_die = -1;
353     life_left = 0x7fffffff;
354     for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++)
355     {
356                 if (entchannel != 0             // channel 0 never overrides
357                 && channels[ch_idx].entnum == entnum
358                 && (channels[ch_idx].entchannel == entchannel || entchannel == -1) )
359                 {       // always override sound from same entity
360                         first_to_die = ch_idx;
361                         break;
362                 }
363
364                 // don't let monster sounds override player sounds
365                 if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx)
366                         continue;
367
368                 if (channels[ch_idx].end - paintedtime < life_left)
369                 {
370                         life_left = channels[ch_idx].end - paintedtime;
371                         first_to_die = ch_idx;
372                 }
373    }
374
375         if (first_to_die == -1)
376                 return NULL;
377
378         if (channels[first_to_die].sfx)
379                 channels[first_to_die].sfx = NULL;
380
381     return &channels[first_to_die];
382 }
383
384 /*
385 =================
386 SND_Spatialize
387 =================
388 */
389 void SND_Spatialize(channel_t *ch)
390 {
391     vec_t dot;
392     vec_t dist;
393     vec_t lscale, rscale, scale;
394     vec3_t source_vec;
395         sfx_t *snd;
396
397 // anything coming from the view entity will always be full volume
398 // LordHavoc: make sounds with ATTN_NONE have no spatialization
399         if (ch->entnum == cl.viewentity || ch->dist_mult == 0)
400         {
401                 ch->leftvol = ch->master_vol;
402                 ch->rightvol = ch->master_vol;
403                 return;
404         }
405
406 // calculate stereo seperation and distance attenuation
407
408         snd = ch->sfx;
409         VectorSubtract(ch->origin, listener_origin, source_vec);
410
411         dist = VectorNormalizeLength(source_vec) * ch->dist_mult;
412
413         dot = DotProduct(listener_right, source_vec);
414
415         if (shm->channels == 1)
416         {
417                 rscale = 1.0;
418                 lscale = 1.0;
419         }
420         else
421         {
422                 rscale = 1.0 + dot;
423                 lscale = 1.0 - dot;
424         }
425
426 // add in distance effect
427         scale = (1.0 - dist) * rscale;
428         ch->rightvol = (int) (ch->master_vol * scale);
429         if (ch->rightvol < 0)
430                 ch->rightvol = 0;
431
432         scale = (1.0 - dist) * lscale;
433         ch->leftvol = (int) (ch->master_vol * scale);
434         if (ch->leftvol < 0)
435                 ch->leftvol = 0;
436 }
437
438
439 // =======================================================================
440 // Start a sound effect
441 // =======================================================================
442
443 void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
444 {
445         channel_t *target_chan, *check;
446         sfxcache_t      *sc;
447         int             vol;
448         int             ch_idx;
449         int             skip;
450
451         if (!sound_started)
452                 return;
453
454         if (!sfx)
455                 return;
456
457         if (nosound.integer)
458                 return;
459
460         vol = fvol*255;
461
462 // pick a channel to play on
463         target_chan = SND_PickChannel(entnum, entchannel);
464         if (!target_chan)
465                 return;
466
467 // spatialize
468         memset (target_chan, 0, sizeof(*target_chan));
469         VectorCopy(origin, target_chan->origin);
470         target_chan->dist_mult = attenuation / sound_nominal_clip_dist;
471         target_chan->master_vol = vol;
472         target_chan->entnum = entnum;
473         target_chan->entchannel = entchannel;
474         SND_Spatialize(target_chan);
475
476         if (!target_chan->leftvol && !target_chan->rightvol)
477                 return;         // not audible at all
478
479 // new channel
480         sc = S_LoadSound (sfx);
481         if (!sc)
482         {
483                 target_chan->sfx = NULL;
484                 return;         // couldn't load the sound's data
485         }
486
487         target_chan->sfx = sfx;
488         target_chan->pos = 0.0;
489     target_chan->end = paintedtime + sc->length;
490
491 // if an identical sound has also been started this frame, offset the pos
492 // a bit to keep it from just making the first one louder
493         check = &channels[NUM_AMBIENTS];
494     for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++)
495     {
496                 if (check == target_chan)
497                         continue;
498                 if (check->sfx == sfx && !check->pos)
499                 {
500                         // LordHavoc: fixed skip calculations
501                         skip = 0.1 * shm->speed;
502                         if (skip > sc->length)
503                                 skip = sc->length;
504                         if (skip > 0)
505                                 skip = rand() % skip;
506                         target_chan->pos += skip;
507                         target_chan->end -= skip;
508                         break;
509                 }
510
511         }
512 }
513
514 void S_StopSound(int entnum, int entchannel)
515 {
516         int i;
517
518         for (i=0 ; i<MAX_DYNAMIC_CHANNELS ; i++)
519         {
520                 if (channels[i].entnum == entnum
521                         && channels[i].entchannel == entchannel)
522                 {
523                         channels[i].end = 0;
524                         channels[i].sfx = NULL;
525                         return;
526                 }
527         }
528 }
529
530 void S_StopAllSounds(qboolean clear)
531 {
532         int             i;
533
534         if (!sound_started)
535                 return;
536
537         total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;   // no statics
538
539         for (i=0 ; i<MAX_CHANNELS ; i++)
540                 if (channels[i].sfx)
541                         channels[i].sfx = NULL;
542
543         memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
544
545         if (clear)
546                 S_ClearBuffer ();
547 }
548
549 void S_StopAllSoundsC (void)
550 {
551         S_StopAllSounds (true);
552 }
553
554 void S_ClearBuffer (void)
555 {
556         int             clear;
557
558 #ifdef _WIN32
559         if (!sound_started || !shm || (!shm->buffer && !pDSBuf))
560 #else
561         if (!sound_started || !shm || !shm->buffer)
562 #endif
563                 return;
564
565         if (shm->samplebits == 8)
566                 clear = 0x80;
567         else
568                 clear = 0;
569
570 #ifdef _WIN32
571         if (pDSBuf)
572         {
573                 DWORD   dwSize;
574                 DWORD   *pData;
575                 int             reps;
576                 HRESULT hresult;
577
578                 reps = 0;
579
580                 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK)
581                 {
582                         if (hresult != DSERR_BUFFERLOST)
583                         {
584                                 Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n");
585                                 S_Shutdown ();
586                                 return;
587                         }
588
589                         if (++reps > 10000)
590                         {
591                                 Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n");
592                                 S_Shutdown ();
593                                 return;
594                         }
595                 }
596
597                 memset(pData, clear, shm->samples * shm->samplebits/8);
598
599                 pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0);
600
601         }
602         else
603 #endif
604         {
605                 int             setsize = shm->samples * shm->samplebits / 8;
606                 char    *buf = shm->buffer;
607
608                 while (setsize--)
609                         *buf++ = 0;
610
611 // on i586/i686 optimized versions of glibc, glibc *wrongly* IMO,
612 // reads the memory area before writing to it causing a seg fault
613 // since the memory is PROT_WRITE only and not PROT_READ|PROT_WRITE
614 //              memset(shm->buffer, clear, shm->samples * shm->samplebits/8);
615         }
616 }
617
618
619 /*
620 =================
621 S_StaticSound
622 =================
623 */
624 void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
625 {
626         channel_t       *ss;
627         sfxcache_t              *sc;
628
629         if (!sfx)
630                 return;
631
632         if (total_channels == MAX_CHANNELS)
633         {
634                 Con_Printf ("total_channels == MAX_CHANNELS\n");
635                 return;
636         }
637
638         ss = &channels[total_channels];
639         total_channels++;
640
641         sc = S_LoadSound (sfx);
642         if (!sc)
643                 return;
644
645         if (sc->loopstart == -1)
646         {
647                 Con_Printf ("Sound %s not looped\n", sfx->name);
648                 return;
649         }
650
651         ss->sfx = sfx;
652         VectorCopy (origin, ss->origin);
653         ss->master_vol = vol;
654         ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist;
655     ss->end = paintedtime + sc->length;
656
657         SND_Spatialize (ss);
658 }
659
660
661 //=============================================================================
662
663 /*
664 ===================
665 S_UpdateAmbientSounds
666 ===================
667 */
668 void S_UpdateAmbientSounds (void)
669 {
670         mleaf_t         *l;
671         float           vol;
672         int                     ambient_channel;
673         channel_t       *chan;
674
675         // LordHavoc: kill ambient sounds until proven otherwise
676         for (ambient_channel = 0 ; ambient_channel < NUM_AMBIENTS;ambient_channel++)
677                 channels[ambient_channel].sfx = NULL;
678
679         if (!snd_ambient || !cl.worldmodel || ambient_level.value <= 0)
680                 return;
681
682         l = Mod_PointInLeaf (listener_origin, cl.worldmodel);
683         if (!l)
684                 return;
685
686 // calc ambient sound levels
687         for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
688         {
689                 chan = &channels[ambient_channel];
690                 chan->sfx = ambient_sfx[ambient_channel];
691
692                 vol = ambient_level.value * l->ambient_sound_level[ambient_channel];
693                 if (vol < 8)
694                         vol = 0;
695
696         // don't adjust volume too fast
697                 if (chan->master_vol < vol)
698                 {
699                         chan->master_vol += host_realframetime * ambient_fade.value;
700                         if (chan->master_vol > vol)
701                                 chan->master_vol = vol;
702                 }
703                 else if (chan->master_vol > vol)
704                 {
705                         chan->master_vol -= host_realframetime * ambient_fade.value;
706                         if (chan->master_vol < vol)
707                                 chan->master_vol = vol;
708                 }
709
710                 chan->leftvol = chan->rightvol = chan->master_vol;
711         }
712 }
713
714
715 /*
716 ============
717 S_Update
718
719 Called once each time through the main loop
720 ============
721 */
722 void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
723 {
724         int                     i, j;
725         int                     total;
726         channel_t       *ch;
727         channel_t       *combine;
728
729         if (!sound_started || (snd_blocked > 0))
730                 return;
731
732         VectorCopy(origin, listener_origin);
733         VectorCopy(forward, listener_forward);
734         VectorCopy(right, listener_right);
735         VectorCopy(up, listener_up);
736
737 // update general area ambient sound sources
738         S_UpdateAmbientSounds ();
739
740         combine = NULL;
741
742 // update spatialization for static and dynamic sounds
743         ch = channels+NUM_AMBIENTS;
744         for (i=NUM_AMBIENTS ; i<total_channels; i++, ch++)
745         {
746                 if (!ch->sfx)
747                         continue;
748                 SND_Spatialize(ch);         // respatialize channel
749                 if (!ch->leftvol && !ch->rightvol)
750                         continue;
751
752         // try to combine static sounds with a previous channel of the same
753         // sound effect so we don't mix five torches every frame
754
755                 if (i > MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS)
756                 {
757                 // see if it can just use the last one
758                         if (combine && combine->sfx == ch->sfx)
759                         {
760                                 combine->leftvol += ch->leftvol;
761                                 combine->rightvol += ch->rightvol;
762                                 ch->leftvol = ch->rightvol = 0;
763                                 continue;
764                         }
765                 // search for one
766                         combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;
767                         for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; j<i; j++, combine++)
768                                 if (combine->sfx == ch->sfx)
769                                         break;
770
771                         if (j == total_channels)
772                         {
773                                 combine = NULL;
774                         }
775                         else
776                         {
777                                 if (combine != ch)
778                                 {
779                                         combine->leftvol += ch->leftvol;
780                                         combine->rightvol += ch->rightvol;
781                                         ch->leftvol = ch->rightvol = 0;
782                                 }
783                                 continue;
784                         }
785                 }
786         }
787
788 //
789 // debugging output
790 //
791         if (snd_show.integer)
792         {
793                 total = 0;
794                 ch = channels;
795                 for (i=0 ; i<total_channels; i++, ch++)
796                         if (ch->sfx && (ch->leftvol || ch->rightvol) )
797                                 total++;
798
799                 Con_Printf ("----(%i)----\n", total);
800         }
801
802 // mix some sound
803         S_Update_();
804 }
805
806 void GetSoundtime(void)
807 {
808         int             samplepos;
809         static  int             buffers;
810         static  int             oldsamplepos;
811         int             fullsamples;
812
813         fullsamples = shm->samples / shm->channels;
814
815 // it is possible to miscount buffers if it has wrapped twice between
816 // calls to S_Update.  Oh well.
817 #ifdef __sun__
818         soundtime = SNDDMA_GetSamples();
819 #else
820         samplepos = SNDDMA_GetDMAPos();
821
822
823         if (samplepos < oldsamplepos)
824         {
825                 buffers++;                                      // buffer wrapped
826
827                 if (paintedtime > 0x40000000)
828                 {       // time to chop things off to avoid 32 bit limits
829                         buffers = 0;
830                         paintedtime = fullsamples;
831                         S_StopAllSounds (true);
832                 }
833         }
834         oldsamplepos = samplepos;
835
836         soundtime = buffers*fullsamples + samplepos/shm->channels;
837 #endif
838 }
839
840 void IN_Accumulate (void);
841
842 void S_ExtraUpdate (void)
843 {
844
845 #ifdef _WIN32
846         IN_Accumulate ();
847 #endif
848
849         if (snd_noextraupdate.integer)
850                 return;         // don't pollute timings
851         S_Update_();
852 }
853
854 void S_Update_(void)
855 {
856         unsigned        endtime;
857         int                             samps;
858
859         if (!sound_started || (snd_blocked > 0))
860                 return;
861
862 // Updates DMA time
863         GetSoundtime();
864
865 // check to make sure that we haven't overshot
866         if (paintedtime < soundtime)
867                 paintedtime = soundtime;
868
869 // mix ahead of current position
870         endtime = soundtime + _snd_mixahead.value * shm->speed;
871         samps = shm->samples >> (shm->channels-1);
872         if (endtime - soundtime > samps)
873                 endtime = soundtime + samps;
874
875 #ifdef _WIN32
876 // if the buffer was lost or stopped, restore it and/or restart it
877         {
878                 DWORD   dwStatus;
879
880                 if (pDSBuf)
881                 {
882                         if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK)
883                                 Con_Printf ("Couldn't get sound buffer status\n");
884
885                         if (dwStatus & DSBSTATUS_BUFFERLOST)
886                                 pDSBuf->lpVtbl->Restore (pDSBuf);
887
888                         if (!(dwStatus & DSBSTATUS_PLAYING))
889                                 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
890                 }
891         }
892 #endif
893
894         S_PaintChannels (endtime);
895
896         SNDDMA_Submit ();
897 }
898
899 /*
900 ===============================================================================
901
902 console functions
903
904 ===============================================================================
905 */
906
907 void S_Play(void)
908 {
909         static int hash=345;
910         int     i;
911         char name[256];
912         sfx_t   *sfx;
913
914         i = 1;
915         while (i<Cmd_Argc())
916         {
917                 if (!strrchr(Cmd_Argv(i), '.'))
918                 {
919                         strcpy(name, Cmd_Argv(i));
920                         strcat(name, ".wav");
921                 }
922                 else
923                         strcpy(name, Cmd_Argv(i));
924                 sfx = S_PrecacheSound(name);
925                 S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0);
926                 i++;
927         }
928 }
929
930 void S_Play2(void)
931 {
932         static int hash=345;
933         int     i;
934         char name[256];
935         sfx_t   *sfx;
936
937         i = 1;
938         while (i<Cmd_Argc())
939         {
940                 if (!strrchr(Cmd_Argv(i), '.'))
941                 {
942                         strcpy(name, Cmd_Argv(i));
943                         strcat(name, ".wav");
944                 }
945                 else
946                         strcpy(name, Cmd_Argv(i));
947                 sfx = S_PrecacheSound(name);
948                 S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 0.0);
949                 i++;
950         }
951 }
952
953 void S_PlayVol(void)
954 {
955         static int hash=543;
956         int i;
957         float vol;
958         char name[256];
959         sfx_t   *sfx;
960
961         i = 1;
962         while (i<Cmd_Argc())
963         {
964                 if (!strrchr(Cmd_Argv(i), '.'))
965                 {
966                         strcpy(name, Cmd_Argv(i));
967                         strcat(name, ".wav");
968                 }
969                 else
970                         strcpy(name, Cmd_Argv(i));
971                 sfx = S_PrecacheSound(name);
972                 vol = atof(Cmd_Argv(i+1));
973                 S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0);
974                 i+=2;
975         }
976 }
977
978 void S_SoundList(void)
979 {
980         int             i;
981         sfx_t   *sfx;
982         sfxcache_t      *sc;
983         int             size, total;
984
985         total = 0;
986         for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)
987         {
988                 sc = sfx->sfxcache;
989                 if (!sc)
990                         continue;
991                 size = sc->length*sc->width*(sc->stereo+1);
992                 total += size;
993                 if (sc->loopstart >= 0)
994                         Con_Printf ("L");
995                 else
996                         Con_Printf (" ");
997                 Con_Printf("(%2db) %6i : %s\n",sc->width*8,  size, sfx->name);
998         }
999         Con_Printf ("Total resident: %i\n", total);
1000 }
1001
1002
1003 void S_LocalSound (char *sound)
1004 {
1005         sfx_t   *sfx;
1006
1007         if (nosound.integer)
1008                 return;
1009         if (!sound_started)
1010                 return;
1011
1012         sfx = S_PrecacheSound (sound);
1013         if (!sfx)
1014         {
1015                 Con_Printf ("S_LocalSound: can't precache %s\n", sound);
1016                 return;
1017         }
1018         S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1);
1019 }
1020
1021
1022 void S_ClearPrecache (void)
1023 {
1024 }
1025
1026
1027 void S_BeginPrecaching (void)
1028 {
1029 }
1030
1031
1032 void S_EndPrecaching (void)
1033 {
1034 }
1035