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