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