blob: 3789381552c80476c80172fe7a359756451a022b [file] [log] [blame]
Franklin Wei5d05b9d2018-02-11 15:34:30 -05001/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
Franklin Wei5d05b9d2018-02-11 15:34:30 -050024void S_Play(void);
25void S_PlayVol(void);
26void S_SoundList(void);
27void S_Update_();
28void S_StopAllSounds(qboolean clear);
29void S_StopAllSoundsC(void);
30
31// =======================================================================
32// Internal sound data & structures
33// =======================================================================
34
35channel_t channels[MAX_CHANNELS];
36int total_channels;
37
38int snd_blocked = 0;
39static qboolean snd_ambient = 1;
40qboolean snd_initialized = false;
41
42// pointer should go away
43volatile dma_t *shm = 0;
44volatile dma_t sn;
45
46vec3_t listener_origin;
47vec3_t listener_forward;
48vec3_t listener_right;
49vec3_t listener_up;
50vec_t sound_nominal_clip_dist=1000.0;
51
52int soundtime; // sample PAIRS
53int paintedtime; // sample PAIRS
54
55
56#define MAX_SFX 512
57sfx_t *known_sfx; // hunk allocated [MAX_SFX]
58int num_sfx;
59
60sfx_t *ambient_sfx[NUM_AMBIENTS];
61
62// lowest rockbox supported
63int desired_speed = SAMPR_16;
64int desired_bits = 16;
65
66int sound_started=0;
67
68cvar_t bgmvolume = {"bgmvolume", "1", true};
69cvar_t volume = {"volume", "0.7", true};
70
71cvar_t nosound = {"nosound", "0"};
72cvar_t precache = {"precache", "1"};
73cvar_t loadas8bit = {"loadas8bit", "0"};
74cvar_t bgmbuffer = {"bgmbuffer", "4096"};
75cvar_t ambient_level = {"ambient_level", "0.3"};
76cvar_t ambient_fade = {"ambient_fade", "100"};
77cvar_t snd_noextraupdate = {"snd_noextraupdate", "0"};
78cvar_t snd_show = {"snd_show", "0"};
79cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", true};
80
81
82// ====================================================================
83// User-setable variables
84// ====================================================================
85
86
87//
88// Fake dma is a synchronous faking of the DMA progress used for
89// isolating performance in the renderer. The fakedma_updates is
90// number of times S_Update() is called per second.
91//
92
93qboolean fakedma = false;
94int fakedma_updates = 15;
95
96
97void S_AmbientOff (void)
98{
99 snd_ambient = false;
100}
101
102
103void S_AmbientOn (void)
104{
105 snd_ambient = true;
106}
107
108
109void S_SoundInfo_f(void)
110{
111 if (!sound_started || !shm)
112 {
113 Con_Printf ("sound system not started\n");
114 return;
115 }
116
117 Con_Printf("%5d stereo\n", shm->channels - 1);
118 Con_Printf("%5d samples\n", shm->samples);
119 Con_Printf("%5d samplepos\n", shm->samplepos);
120 Con_Printf("%5d samplebits\n", shm->samplebits);
121 Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
122 Con_Printf("%5d speed\n", shm->speed);
123 Con_Printf("0x%x dma buffer\n", shm->buffer);
124 Con_Printf("%5d total_channels\n", total_channels);
125}
126
127
128/*
129================
130S_Startup
131================
132*/
133
134void S_Startup (void)
135{
136 int rc;
137
138 if (!snd_initialized)
139 return;
140
141 if (!fakedma)
142 {
143 rc = SNDDMA_Init();
144
145 if (!rc)
146 {
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500147 Con_Printf("S_Startup: SNDDMA_Init failed.\n");
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500148 sound_started = 0;
149 return;
150 }
151 }
152
153 sound_started = 1;
154}
155
156
157/*
158================
159S_Init
160================
161*/
162void S_Init (void)
163{
164
165 Con_Printf("\nSound Initialization\n");
166
167 if (COM_CheckParm("-nosound"))
168 return;
169
170 if (COM_CheckParm("-simsound"))
171 fakedma = true;
172
173 Cmd_AddCommand("play", S_Play);
174 Cmd_AddCommand("playvol", S_PlayVol);
175 Cmd_AddCommand("stopsound", S_StopAllSoundsC);
176 Cmd_AddCommand("soundlist", S_SoundList);
177 Cmd_AddCommand("soundinfo", S_SoundInfo_f);
178
179 Cvar_RegisterVariable(&nosound);
180 Cvar_RegisterVariable(&volume);
181 Cvar_RegisterVariable(&precache);
182 Cvar_RegisterVariable(&loadas8bit);
183 Cvar_RegisterVariable(&bgmvolume);
184 Cvar_RegisterVariable(&bgmbuffer);
185 Cvar_RegisterVariable(&ambient_level);
186 Cvar_RegisterVariable(&ambient_fade);
187 Cvar_RegisterVariable(&snd_noextraupdate);
188 Cvar_RegisterVariable(&snd_show);
189 Cvar_RegisterVariable(&_snd_mixahead);
190
191 if (host_parms.memsize < 0x800000)
192 {
193 Cvar_Set ("loadas8bit", "1");
194 Con_Printf ("loading all sounds as 8bit\n");
195 }
196
197
198
199 snd_initialized = true;
200
201 S_Startup ();
202
203 known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t");
204 num_sfx = 0;
205
206// create a piece of DMA memory
207
208 if (fakedma)
209 {
210 shm = (void *) Hunk_AllocName(sizeof(*shm), "shm");
211 shm->splitbuffer = 0;
212 shm->samplebits = 16;
213 shm->speed = 22050;
214 shm->channels = 2;
215 shm->samples = 32768;
216 shm->samplepos = 0;
217 shm->soundalive = true;
218 shm->gamealive = true;
219 shm->submission_chunk = 1;
220 shm->buffer = Hunk_AllocName(1<<16, "shmbuf");
221 }
222
223 if ( shm ) {
224 Con_Printf ("Sound sampling rate: %i\n", shm->speed);
225 }
226
227 // provides a tick sound until washed clean
228
229// if (shm->buffer)
230// shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging
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
243void S_Shutdown(void)
244{
245
246 if (!sound_started)
247 return;
248
249 if (shm)
250 shm->gamealive = 0;
251
252 shm = 0;
253 sound_started = 0;
254
255 if (!fakedma)
256 {
257 SNDDMA_Shutdown();
258 }
259}
260
261
262// =======================================================================
263// Load a sound
264// =======================================================================
265
266/*
267==================
268S_FindName
269
270==================
271*/
272sfx_t *S_FindName (char *name)
273{
274 int i;
275 sfx_t *sfx;
276
277 if (!name)
278 Sys_Error ("S_FindName: NULL\n");
279
280 if (Q_strlen(name) >= MAX_QPATH)
281 Sys_Error ("Sound name too long: %s", name);
282
283// see if already loaded
284 for (i=0 ; i < num_sfx ; i++)
285 if (!Q_strcmp(known_sfx[i].name, name))
286 {
287 return &known_sfx[i];
288 }
289
290 if (num_sfx == MAX_SFX)
291 Sys_Error ("S_FindName: out of sfx_t");
292
293 sfx = &known_sfx[i];
294 strcpy (sfx->name, name);
295
296 num_sfx++;
297
298 return sfx;
299}
300
301
302/*
303==================
304S_TouchSound
305
306==================
307*/
308void S_TouchSound (char *name)
309{
310 sfx_t *sfx;
311
312 if (!sound_started)
313 return;
314
315 sfx = S_FindName (name);
316 Cache_Check (&sfx->cache);
317}
318
319/*
320==================
321S_PrecacheSound
322
323==================
324*/
325sfx_t *S_PrecacheSound (char *name)
326{
327 sfx_t *sfx;
328
329 if (!sound_started || nosound.value)
330 return NULL;
331
332 sfx = S_FindName (name);
333
334// cache it in
335 if (precache.value)
336 S_LoadSound (sfx);
337
338 return sfx;
339}
340
341
342//=============================================================================
343
344/*
345=================
346SND_PickChannel
347=================
348*/
349channel_t *SND_PickChannel(int entnum, int entchannel)
350{
351 int ch_idx;
352 int first_to_die;
353 int life_left;
354
355// Check for replacement sound, or find the best one to replace
356 first_to_die = -1;
357 life_left = 0x7fffffff;
358 for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++)
359 {
360 if (entchannel != 0 // channel 0 never overrides
361 && channels[ch_idx].entnum == entnum
362 && (channels[ch_idx].entchannel == entchannel || entchannel == -1) )
363 { // allways override sound from same entity
364 first_to_die = ch_idx;
365 break;
366 }
367
368 // don't let monster sounds override player sounds
369 if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx)
370 continue;
371
372 if (channels[ch_idx].end - paintedtime < life_left)
373 {
374 life_left = channels[ch_idx].end - paintedtime;
375 first_to_die = ch_idx;
376 }
377 }
378
379 if (first_to_die == -1)
380 return NULL;
381
382 if (channels[first_to_die].sfx)
383 channels[first_to_die].sfx = NULL;
384
385 return &channels[first_to_die];
386}
387
388/*
389=================
390SND_Spatialize
391=================
392*/
393void SND_Spatialize(channel_t *ch)
394{
395 vec_t dot;
396 vec_t ldist, rdist, dist;
397 vec_t lscale, rscale, scale;
398 vec3_t source_vec;
399 sfx_t *snd;
400
401// anything coming from the view entity will allways be full volume
402 if (ch->entnum == cl.viewentity)
403 {
404 ch->leftvol = ch->master_vol;
405 ch->rightvol = ch->master_vol;
406 return;
407 }
408
409// calculate stereo seperation and distance attenuation
410
411 snd = ch->sfx;
412 VectorSubtract(ch->origin, listener_origin, source_vec);
413
414 dist = VectorNormalize(source_vec) * ch->dist_mult;
415
416 dot = DotProduct(listener_right, source_vec);
417
418 if (shm->channels == 1)
419 {
420 rscale = 1.0;
421 lscale = 1.0;
422 }
423 else
424 {
425 rscale = 1.0 + dot;
426 lscale = 1.0 - dot;
427 }
428
429// add in distance effect
430 scale = (1.0 - dist) * rscale;
431 ch->rightvol = (int) (ch->master_vol * scale);
432 if (ch->rightvol < 0)
433 ch->rightvol = 0;
434
435 scale = (1.0 - dist) * lscale;
436 ch->leftvol = (int) (ch->master_vol * scale);
437 if (ch->leftvol < 0)
438 ch->leftvol = 0;
439}
440
441
442// =======================================================================
443// Start a sound effect
444// =======================================================================
445
446void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
447{
448 channel_t *target_chan, *check;
449 sfxcache_t *sc;
450 int vol;
451 int ch_idx;
452 int skip;
453
454 if (!sound_started)
455 return;
456
457 if (!sfx)
458 return;
459
460 if (nosound.value)
461 return;
462
463 vol = fvol*255;
464
465// pick a channel to play on
466 target_chan = SND_PickChannel(entnum, entchannel);
467 if (!target_chan)
468 return;
469
470// spatialize
471 memset (target_chan, 0, sizeof(*target_chan));
472 VectorCopy(origin, target_chan->origin);
473 target_chan->dist_mult = attenuation / sound_nominal_clip_dist;
474 target_chan->master_vol = vol;
475 target_chan->entnum = entnum;
476 target_chan->entchannel = entchannel;
477 SND_Spatialize(target_chan);
478
479 if (!target_chan->leftvol && !target_chan->rightvol)
480 return; // not audible at all
481
482// new channel
483 sc = S_LoadSound (sfx);
484 if (!sc)
485 {
486 target_chan->sfx = NULL;
487 return; // couldn't load the sound's data
488 }
489
490 target_chan->sfx = sfx;
491 target_chan->pos = 0.0;
492 target_chan->end = paintedtime + sc->length;
493
494// if an identical sound has also been started this frame, offset the pos
495// a bit to keep it from just making the first one louder
496 check = &channels[NUM_AMBIENTS];
497 for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++)
498 {
499 if (check == target_chan)
500 continue;
501 if (check->sfx == sfx && !check->pos)
502 {
503 skip = rand () % (int)(0.1*shm->speed);
504 if (skip >= target_chan->end)
505 skip = target_chan->end - 1;
506 target_chan->pos += skip;
507 target_chan->end -= skip;
508 break;
509 }
510
511 }
512}
513
514void 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
530void 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 Q_memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
544
545 if (clear)
546 S_ClearBuffer ();
547}
548
549void S_StopAllSoundsC (void)
550{
551 S_StopAllSounds (true);
552}
553
554void S_ClearBuffer (void)
555{
556 int clear;
557
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500558 if (!sound_started || !shm || !shm->buffer)
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500559 return;
560
561 if (shm->samplebits == 8)
562 clear = 0x80;
563 else
564 clear = 0;
565
Vencislav Atanasov183e45e2019-07-28 23:31:50 +0300566 Q_memset(shm->buffer, clear, shm->samples * shm->samplebits/8);
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500567}
568
569
570/*
571=================
572S_StaticSound
573=================
574*/
575void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
576{
577 channel_t *ss;
578 sfxcache_t *sc;
579
580 if (!sfx)
581 return;
582
583 if (total_channels == MAX_CHANNELS)
584 {
585 Con_Printf ("total_channels == MAX_CHANNELS\n");
586 return;
587 }
588
589 ss = &channels[total_channels];
590 total_channels++;
591
592 sc = S_LoadSound (sfx);
593 if (!sc)
594 return;
595
596 if (sc->loopstart == -1)
597 {
598 Con_Printf ("Sound %s not looped\n", sfx->name);
599 return;
600 }
601
602 ss->sfx = sfx;
603 VectorCopy (origin, ss->origin);
604 ss->master_vol = vol;
605 ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist;
606 ss->end = paintedtime + sc->length;
607
608 SND_Spatialize (ss);
609}
610
611
612//=============================================================================
613
614/*
615===================
616S_UpdateAmbientSounds
617===================
618*/
619void S_UpdateAmbientSounds (void)
620{
621 mleaf_t *l;
622 float vol;
623 int ambient_channel;
624 channel_t *chan;
625
626 if (!snd_ambient)
627 return;
628
629// calc ambient sound levels
630 if (!cl.worldmodel)
631 return;
632
633 l = Mod_PointInLeaf (listener_origin, cl.worldmodel);
634 if (!l || !ambient_level.value)
635 {
636 for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
637 channels[ambient_channel].sfx = NULL;
638 return;
639 }
640
641 for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
642 {
643 chan = &channels[ambient_channel];
644 chan->sfx = ambient_sfx[ambient_channel];
645
646 vol = ambient_level.value * l->ambient_sound_level[ambient_channel];
647 if (vol < 8)
648 vol = 0;
649
650 // don't adjust volume too fast
651 if (chan->master_vol < vol)
652 {
653 chan->master_vol += host_frametime * ambient_fade.value;
654 if (chan->master_vol > vol)
655 chan->master_vol = vol;
656 }
657 else if (chan->master_vol > vol)
658 {
659 chan->master_vol -= host_frametime * ambient_fade.value;
660 if (chan->master_vol < vol)
661 chan->master_vol = vol;
662 }
663
664 chan->leftvol = chan->rightvol = chan->master_vol;
665 }
666}
667
668
669/*
670============
671S_Update
672
673Called once each time through the main loop
674============
675*/
676void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
677{
678 int i, j;
679 int total;
680 channel_t *ch;
681 channel_t *combine;
682
683 if (!sound_started || (snd_blocked > 0))
684 return;
685
686 VectorCopy(origin, listener_origin);
687 VectorCopy(forward, listener_forward);
688 VectorCopy(right, listener_right);
689 VectorCopy(up, listener_up);
690
691// update general area ambient sound sources
692 S_UpdateAmbientSounds ();
693
694 combine = NULL;
695
696// update spatialization for static and dynamic sounds
697 ch = channels+NUM_AMBIENTS;
698 for (i=NUM_AMBIENTS ; i<total_channels; i++, ch++)
699 {
700 if (!ch->sfx)
701 continue;
702 SND_Spatialize(ch); // respatialize channel
703 if (!ch->leftvol && !ch->rightvol)
704 continue;
705
706 // try to combine static sounds with a previous channel of the same
707 // sound effect so we don't mix five torches every frame
708
709 if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS)
710 {
711 // see if it can just use the last one
712 if (combine && combine->sfx == ch->sfx)
713 {
714 combine->leftvol += ch->leftvol;
715 combine->rightvol += ch->rightvol;
716 ch->leftvol = ch->rightvol = 0;
717 continue;
718 }
719 // search for one
720 combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;
721 for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; j<i; j++, combine++)
722 if (combine->sfx == ch->sfx)
723 break;
724
725 if (j == total_channels)
726 {
727 combine = NULL;
728 }
729 else
730 {
731 if (combine != ch)
732 {
733 combine->leftvol += ch->leftvol;
734 combine->rightvol += ch->rightvol;
735 ch->leftvol = ch->rightvol = 0;
736 }
737 continue;
738 }
739 }
740
741
742 }
743
744//
745// debugging output
746//
747 if (snd_show.value)
748 {
749 total = 0;
750 ch = channels;
751 for (i=0 ; i<total_channels; i++, ch++)
752 if (ch->sfx && (ch->leftvol || ch->rightvol) )
753 {
754 //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name);
755 total++;
756 }
757
758 Con_Printf ("----(%i)----\n", total);
759 }
760
761// mix some sound
762 S_Update_();
763}
764
765void GetSoundtime(void)
766{
767 int samplepos;
768 static int buffers;
769 static int oldsamplepos;
770 int fullsamples;
771
772 fullsamples = shm->samples / shm->channels;
773
774// it is possible to miscount buffers if it has wrapped twice between
775// calls to S_Update. Oh well.
776#ifdef __sun__
777 soundtime = SNDDMA_GetSamples();
778#else
779 samplepos = SNDDMA_GetDMAPos();
780
781
782 if (samplepos < oldsamplepos)
783 {
784 buffers++; // buffer wrapped
785
786 if (paintedtime > 0x40000000)
787 { // time to chop things off to avoid 32 bit limits
788 buffers = 0;
789 paintedtime = fullsamples;
790 S_StopAllSounds (true);
791 }
792 }
793 oldsamplepos = samplepos;
794
795 soundtime = buffers*fullsamples + samplepos/shm->channels;
796#endif
797}
798
799void S_ExtraUpdate (void)
800{
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500801 if (snd_noextraupdate.value)
802 return; // don't pollute timings
803 S_Update_();
804}
805
806void S_Update_(void)
807{
808#ifndef SDL
809
810 unsigned endtime;
811 int samps;
812
813 if (!sound_started || (snd_blocked > 0))
814 return;
815
816// Updates DMA time
817 GetSoundtime();
818
819// check to make sure that we haven't overshot
820 if (paintedtime < soundtime)
821 {
822 //Con_Printf ("S_Update_ : overflow\n");
823 paintedtime = soundtime;
824 }
825
826// mix ahead of current position
827 endtime = soundtime + _snd_mixahead.value * shm->speed;
828 samps = shm->samples >> (shm->channels-1);
829 if (endtime - soundtime > samps)
830 endtime = soundtime + samps;
831
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500832 S_PaintChannels (endtime);
833
834 SNDDMA_Submit ();
835#endif /* ! SDL */
836}
837
838/*
839===============================================================================
840
841console functions
842
843===============================================================================
844*/
845
846void S_Play(void)
847{
848 static int hash=345;
849 int i;
850 char name[256];
851 sfx_t *sfx;
852
853 i = 1;
854 while (i<Cmd_Argc())
855 {
856 if (!Q_strrchr(Cmd_Argv(i), '.'))
857 {
858 Q_strcpy(name, Cmd_Argv(i));
859 Q_strcat(name, ".wav");
860 }
861 else
862 Q_strcpy(name, Cmd_Argv(i));
863 sfx = S_PrecacheSound(name);
864 S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0);
865 i++;
866 }
867}
868
869void S_PlayVol(void)
870{
871 static int hash=543;
872 int i;
873 float vol;
874 char name[256];
875 sfx_t *sfx;
876
877 i = 1;
878 while (i<Cmd_Argc())
879 {
880 if (!Q_strrchr(Cmd_Argv(i), '.'))
881 {
882 Q_strcpy(name, Cmd_Argv(i));
883 Q_strcat(name, ".wav");
884 }
885 else
886 Q_strcpy(name, Cmd_Argv(i));
887 sfx = S_PrecacheSound(name);
888 vol = Q_atof(Cmd_Argv(i+1));
889 S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0);
890 i+=2;
891 }
892}
893
894void S_SoundList(void)
895{
896 int i;
897 sfx_t *sfx;
898 sfxcache_t *sc;
899 int size, total;
900
901 total = 0;
902 for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)
903 {
904 sc = Cache_Check (&sfx->cache);
905 if (!sc)
906 continue;
907 size = sc->length*sc->width*(sc->stereo+1);
908 total += size;
909 if (sc->loopstart >= 0)
910 Con_Printf ("L");
911 else
912 Con_Printf (" ");
913 Con_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name);
914 }
915 Con_Printf ("Total resident: %i\n", total);
916}
917
918
919void S_LocalSound (char *sound)
920{
921 sfx_t *sfx;
922
923 if (nosound.value)
924 return;
925 if (!sound_started)
926 return;
927
928 sfx = S_PrecacheSound (sound);
929 if (!sfx)
930 {
931 Con_Printf ("S_LocalSound: can't cache %s\n", sound);
932 return;
933 }
934 S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1);
935}
936
937
938void S_ClearPrecache (void)
939{
940}
941
942
943void S_BeginPrecaching (void)
944{
945}
946
947
948void S_EndPrecaching (void)
949{
950}
951