2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // snd_dma.c -- main control for any streaming sound output device
30 void S_SoundList(void);
32 void S_StopAllSounds(qboolean clear
);
33 void S_StopAllSoundsC(void);
35 // =======================================================================
36 // Internal sound data & structures
37 // =======================================================================
39 channel_t channels
[MAX_CHANNELS
];
43 static qboolean snd_ambient
= 1;
44 qboolean snd_initialized
= false;
46 // pointer should go away
47 volatile dma_t
*shm
= 0;
50 vec3_t listener_origin
;
51 vec3_t listener_forward
;
52 vec3_t listener_right
;
54 vec_t sound_nominal_clip_dist
=1000.0;
56 int soundtime
; // sample PAIRS
57 int paintedtime
; // sample PAIRS
61 sfx_t
*known_sfx
; // hunk allocated [MAX_SFX]
64 sfx_t
*ambient_sfx
[NUM_AMBIENTS
];
66 int desired_speed
= 11025;
67 int desired_bits
= 16;
71 cvar_t bgmvolume
= {"bgmvolume", "1", true};
72 cvar_t bgmtype
= {"bgmtype", "cd", true}; // cd or none
74 cvar_t volume
= {"volume", "0.7", true};
76 cvar_t nosound
= {"nosound", "0"};
77 cvar_t precache
= {"precache", "1"};
78 cvar_t loadas8bit
= {"loadas8bit", "0"};
79 cvar_t bgmbuffer
= {"bgmbuffer", "4096"};
80 cvar_t ambient_level
= {"ambient_level", "0.3"};
81 cvar_t ambient_fade
= {"ambient_fade", "100"};
82 cvar_t snd_noextraupdate
= {"snd_noextraupdate", "0"};
83 cvar_t snd_show
= {"snd_show", "0"};
84 cvar_t _snd_mixahead
= {"_snd_mixahead", "0.1", true};
87 // ====================================================================
88 // User-setable variables
89 // ====================================================================
93 // Fake dma is a synchronous faking of the DMA progress used for
94 // isolating performance in the renderer. The fakedma_updates is
95 // number of times S_Update() is called per second.
98 qboolean fakedma
= false;
99 int fakedma_updates
= 15;
102 void S_AmbientOff (void)
108 void S_AmbientOn (void)
114 void S_SoundInfo_f(void)
116 if (!sound_started
|| !shm
)
118 Con_Printf ("sound system not started\n");
122 Con_Printf("%5d stereo\n", shm
->channels
- 1);
123 Con_Printf("%5d samples\n", shm
->samples
);
124 Con_Printf("%5d samplepos\n", shm
->samplepos
);
125 Con_Printf("%5d samplebits\n", shm
->samplebits
);
126 Con_Printf("%5d submission_chunk\n", shm
->submission_chunk
);
127 Con_Printf("%5d speed\n", shm
->speed
);
128 Con_Printf("0x%x dma buffer\n", shm
->buffer
);
129 Con_Printf("%5d total_channels\n", total_channels
);
139 void S_Startup (void)
143 if (!snd_initialized
)
153 Con_Printf("S_Startup: SNDDMA_Init failed.\n");
172 Con_Printf("\nSound Initialization\n");
174 if (COM_CheckParm("-nosound"))
177 if (COM_CheckParm("-simsound"))
180 Cmd_AddCommand("play", S_Play
);
181 Cmd_AddCommand("playvol", S_PlayVol
);
182 Cmd_AddCommand("stopsound", S_StopAllSoundsC
);
183 Cmd_AddCommand("soundlist", S_SoundList
);
184 Cmd_AddCommand("soundinfo", S_SoundInfo_f
);
186 Cvar_RegisterVariable(&nosound
);
187 Cvar_RegisterVariable(&volume
);
188 Cvar_RegisterVariable(&precache
);
189 Cvar_RegisterVariable(&loadas8bit
);
190 Cvar_RegisterVariable(&bgmvolume
);
191 Cvar_RegisterVariable(&bgmbuffer
);
192 Cvar_RegisterVariable(&bgmtype
);
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
);
199 if (host_parms
.memsize
< 0x800000)
201 Cvar_Set ("loadas8bit", "1");
202 Con_Printf ("loading all sounds as 8bit\n");
207 snd_initialized
= true;
211 SND_InitScaletable ();
213 known_sfx
= Hunk_AllocName (MAX_SFX
*sizeof(sfx_t
), "sfx_t");
216 // create a piece of DMA memory
220 shm
= (void *) Hunk_AllocName(sizeof(*shm
), "shm");
221 shm
->splitbuffer
= 0;
222 shm
->samplebits
= 16;
225 shm
->samples
= 32768;
227 shm
->soundalive
= true;
228 shm
->gamealive
= true;
229 shm
->submission_chunk
= 1;
230 shm
->buffer
= Hunk_AllocName(1<<16, "shmbuf");
233 Con_Printf ("Sound sampling rate: %i\n", shm
->speed
);
235 // provides a tick sound until washed clean
238 // shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging
240 ambient_sfx
[AMBIENT_WATER
] = S_PrecacheSound ("ambience/water1.wav");
241 ambient_sfx
[AMBIENT_SKY
] = S_PrecacheSound ("ambience/wind2.wav");
243 S_StopAllSounds (true);
247 // =======================================================================
248 // Shutdown sound engine
249 // =======================================================================
251 void S_Shutdown(void)
270 // =======================================================================
272 // =======================================================================
280 sfx_t
*S_FindName (char *name
)
286 Sys_Error ("S_FindName: NULL\n");
288 if (Q_strlen(name
) >= MAX_QPATH
)
289 Sys_Error ("Sound name too long: %s", name
);
291 // see if already loaded
292 for (i
=0 ; i
< num_sfx
; i
++)
293 if (!Q_strcmp(known_sfx
[i
].name
, name
))
295 return &known_sfx
[i
];
298 if (num_sfx
== MAX_SFX
)
299 Sys_Error ("S_FindName: out of sfx_t");
302 strcpy (sfx
->name
, name
);
316 void S_TouchSound (char *name
)
323 sfx
= S_FindName (name
);
324 Cache_Check (&sfx
->cache
);
333 sfx_t
*S_PrecacheSound (char *name
)
337 if (!sound_started
|| nosound
.value
)
340 sfx
= S_FindName (name
);
350 //=============================================================================
357 channel_t
*SND_PickChannel(int entnum
, int entchannel
)
363 // Check for replacement sound, or find the best one to replace
365 life_left
= 0x7fffffff;
366 for (ch_idx
=NUM_AMBIENTS
; ch_idx
< NUM_AMBIENTS
+ MAX_DYNAMIC_CHANNELS
; ch_idx
++)
368 if (entchannel
!= 0 // channel 0 never overrides
369 && channels
[ch_idx
].entnum
== entnum
370 && (channels
[ch_idx
].entchannel
== entchannel
|| entchannel
== -1) )
371 { // allways override sound from same entity
372 first_to_die
= ch_idx
;
376 // don't let monster sounds override player sounds
377 if (channels
[ch_idx
].entnum
== cl
.viewentity
&& entnum
!= cl
.viewentity
&& channels
[ch_idx
].sfx
)
380 if (channels
[ch_idx
].end
- paintedtime
< life_left
)
382 life_left
= channels
[ch_idx
].end
- paintedtime
;
383 first_to_die
= ch_idx
;
387 if (first_to_die
== -1)
390 if (channels
[first_to_die
].sfx
)
391 channels
[first_to_die
].sfx
= NULL
;
393 return &channels
[first_to_die
];
401 void SND_Spatialize(channel_t
*ch
)
405 vec_t lscale
, rscale
, scale
;
409 // anything coming from the view entity will allways be full volume
410 if (ch
->entnum
== cl
.viewentity
)
412 ch
->leftvol
= ch
->master_vol
;
413 ch
->rightvol
= ch
->master_vol
;
417 // calculate stereo seperation and distance attenuation
420 VectorSubtract(ch
->origin
, listener_origin
, source_vec
);
422 dist
= VectorNormalize(source_vec
) * ch
->dist_mult
;
424 dot
= DotProduct(listener_right
, source_vec
);
426 if (shm
->channels
== 1)
437 // add in distance effect
438 scale
= (1.0 - dist
) * rscale
;
439 ch
->rightvol
= (int) (ch
->master_vol
* scale
);
440 if (ch
->rightvol
< 0)
443 scale
= (1.0 - dist
) * lscale
;
444 ch
->leftvol
= (int) (ch
->master_vol
* scale
);
450 // =======================================================================
451 // Start a sound effect
452 // =======================================================================
454 void S_StartSound(int entnum
, int entchannel
, sfx_t
*sfx
, vec3_t origin
, float fvol
, float attenuation
)
456 channel_t
*target_chan
, *check
;
473 // pick a channel to play on
474 target_chan
= SND_PickChannel(entnum
, entchannel
);
479 memset (target_chan
, 0, sizeof(*target_chan
));
480 VectorCopy(origin
, target_chan
->origin
);
481 target_chan
->dist_mult
= attenuation
/ sound_nominal_clip_dist
;
482 target_chan
->master_vol
= vol
;
483 target_chan
->entnum
= entnum
;
484 target_chan
->entchannel
= entchannel
;
485 SND_Spatialize(target_chan
);
487 if (!target_chan
->leftvol
&& !target_chan
->rightvol
)
488 return; // not audible at all
491 sc
= S_LoadSound (sfx
);
494 target_chan
->sfx
= NULL
;
495 return; // couldn't load the sound's data
498 target_chan
->sfx
= sfx
;
499 target_chan
->pos
= 0.0;
500 target_chan
->end
= paintedtime
+ sc
->length
;
502 // if an identical sound has also been started this frame, offset the pos
503 // a bit to keep it from just making the first one louder
504 check
= &channels
[NUM_AMBIENTS
];
505 for (ch_idx
=NUM_AMBIENTS
; ch_idx
< NUM_AMBIENTS
+ MAX_DYNAMIC_CHANNELS
; ch_idx
++, check
++)
507 if (check
== target_chan
)
509 if (check
->sfx
== sfx
&& !check
->pos
)
511 skip
= rand () % (int)(0.1*shm
->speed
);
512 if (skip
>= target_chan
->end
)
513 skip
= target_chan
->end
- 1;
514 target_chan
->pos
+= skip
;
515 target_chan
->end
-= skip
;
522 void S_StopSound(int entnum
, int entchannel
)
526 for (i
=0 ; i
<MAX_DYNAMIC_CHANNELS
; i
++)
528 if (channels
[i
].entnum
== entnum
529 && channels
[i
].entchannel
== entchannel
)
532 channels
[i
].sfx
= NULL
;
538 void S_StopAllSounds(qboolean clear
)
545 total_channels
= MAX_DYNAMIC_CHANNELS
+ NUM_AMBIENTS
; // no statics
547 for (i
=0 ; i
<MAX_CHANNELS
; i
++)
549 channels
[i
].sfx
= NULL
;
551 Q_memset(channels
, 0, MAX_CHANNELS
* sizeof(channel_t
));
557 void S_StopAllSoundsC (void)
559 S_StopAllSounds (true);
562 void S_ClearBuffer (void)
567 if (!sound_started
|| !shm
|| (!shm
->buffer
&& !pDSBuf
))
569 if (!sound_started
|| !shm
|| !shm
->buffer
)
573 if (shm
->samplebits
== 8)
588 while ((hresult
= pDSBuf
->lpVtbl
->Lock(pDSBuf
, 0, gSndBufSize
, &pData
, &dwSize
, NULL
, NULL
, 0)) != DS_OK
)
590 if (hresult
!= DSERR_BUFFERLOST
)
592 Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n");
599 Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n");
605 Q_memset(pData
, clear
, shm
->samples
* shm
->samplebits
/8);
607 pDSBuf
->lpVtbl
->Unlock(pDSBuf
, pData
, dwSize
, NULL
, 0);
613 Q_memset(shm
->buffer
, clear
, shm
->samples
* shm
->samplebits
/8);
623 void S_StaticSound (sfx_t
*sfx
, vec3_t origin
, float vol
, float attenuation
)
631 if (total_channels
== MAX_CHANNELS
)
633 Con_Printf ("total_channels == MAX_CHANNELS\n");
637 ss
= &channels
[total_channels
];
640 sc
= S_LoadSound (sfx
);
644 if (sc
->loopstart
== -1)
646 Con_Printf ("Sound %s not looped\n", sfx
->name
);
651 VectorCopy (origin
, ss
->origin
);
652 ss
->master_vol
= vol
;
653 ss
->dist_mult
= (attenuation
/64) / sound_nominal_clip_dist
;
654 ss
->end
= paintedtime
+ sc
->length
;
660 //=============================================================================
664 S_UpdateAmbientSounds
667 void S_UpdateAmbientSounds (void)
677 // calc ambient sound levels
681 l
= Mod_PointInLeaf (listener_origin
, cl
.worldmodel
);
682 if (!l
|| !ambient_level
.value
)
684 for (ambient_channel
= 0 ; ambient_channel
< NUM_AMBIENTS
; ambient_channel
++)
685 channels
[ambient_channel
].sfx
= NULL
;
689 for (ambient_channel
= 0 ; ambient_channel
< NUM_AMBIENTS
; ambient_channel
++)
691 chan
= &channels
[ambient_channel
];
692 chan
->sfx
= ambient_sfx
[ambient_channel
];
694 vol
= ambient_level
.value
* l
->ambient_sound_level
[ambient_channel
];
698 // don't adjust volume too fast
699 if (chan
->master_vol
< vol
)
701 chan
->master_vol
+= host_frametime
* ambient_fade
.value
;
702 if (chan
->master_vol
> vol
)
703 chan
->master_vol
= vol
;
705 else if (chan
->master_vol
> vol
)
707 chan
->master_vol
-= host_frametime
* ambient_fade
.value
;
708 if (chan
->master_vol
< vol
)
709 chan
->master_vol
= vol
;
712 chan
->leftvol
= chan
->rightvol
= chan
->master_vol
;
721 Called once each time through the main loop
724 void S_Update(vec3_t origin
, vec3_t forward
, vec3_t right
, vec3_t up
)
731 if (!sound_started
|| (snd_blocked
> 0))
734 VectorCopy(origin
, listener_origin
);
735 VectorCopy(forward
, listener_forward
);
736 VectorCopy(right
, listener_right
);
737 VectorCopy(up
, listener_up
);
739 // update general area ambient sound sources
740 S_UpdateAmbientSounds ();
744 // update spatialization for static and dynamic sounds
745 ch
= channels
+NUM_AMBIENTS
;
746 for (i
=NUM_AMBIENTS
; i
<total_channels
; i
++, ch
++)
750 SND_Spatialize(ch
); // respatialize channel
751 if (!ch
->leftvol
&& !ch
->rightvol
)
754 // try to combine static sounds with a previous channel of the same
755 // sound effect so we don't mix five torches every frame
757 if (i
>= MAX_DYNAMIC_CHANNELS
+ NUM_AMBIENTS
)
759 // see if it can just use the last one
760 if (combine
&& combine
->sfx
== ch
->sfx
)
762 combine
->leftvol
+= ch
->leftvol
;
763 combine
->rightvol
+= ch
->rightvol
;
764 ch
->leftvol
= ch
->rightvol
= 0;
768 combine
= channels
+MAX_DYNAMIC_CHANNELS
+ NUM_AMBIENTS
;
769 for (j
=MAX_DYNAMIC_CHANNELS
+ NUM_AMBIENTS
; j
<i
; j
++, combine
++)
770 if (combine
->sfx
== ch
->sfx
)
773 if (j
== total_channels
)
781 combine
->leftvol
+= ch
->leftvol
;
782 combine
->rightvol
+= ch
->rightvol
;
783 ch
->leftvol
= ch
->rightvol
= 0;
799 for (i
=0 ; i
<total_channels
; i
++, ch
++)
800 if (ch
->sfx
&& (ch
->leftvol
|| ch
->rightvol
) )
802 //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name);
806 Con_Printf ("----(%i)----\n", total
);
813 void GetSoundtime(void)
817 static int oldsamplepos
;
820 fullsamples
= shm
->samples
/ shm
->channels
;
822 // it is possible to miscount buffers if it has wrapped twice between
823 // calls to S_Update. Oh well.
825 soundtime
= SNDDMA_GetSamples();
827 samplepos
= SNDDMA_GetDMAPos();
830 if (samplepos
< oldsamplepos
)
832 buffers
++; // buffer wrapped
834 if (paintedtime
> 0x40000000)
835 { // time to chop things off to avoid 32 bit limits
837 paintedtime
= fullsamples
;
838 S_StopAllSounds (true);
841 oldsamplepos
= samplepos
;
843 soundtime
= buffers
*fullsamples
+ samplepos
/shm
->channels
;
847 void S_ExtraUpdate (void)
854 if (snd_noextraupdate
.value
)
855 return; // don't pollute timings
864 if (!sound_started
|| (snd_blocked
> 0))
870 // check to make sure that we haven't overshot
871 if (paintedtime
< soundtime
)
873 //Con_Printf ("S_Update_ : overflow\n");
874 paintedtime
= soundtime
;
877 // mix ahead of current position
878 endtime
= soundtime
+ _snd_mixahead
.value
* shm
->speed
;
879 samps
= shm
->samples
>> (shm
->channels
-1);
880 if (endtime
- soundtime
> samps
)
881 endtime
= soundtime
+ samps
;
884 // if the buffer was lost or stopped, restore it and/or restart it
890 if (pDSBuf
->lpVtbl
->GetStatus (pDSBuf
, &dwStatus
) != DD_OK
)
891 Con_Printf ("Couldn't get sound buffer status\n");
893 if (dwStatus
& DSBSTATUS_BUFFERLOST
)
894 pDSBuf
->lpVtbl
->Restore (pDSBuf
);
896 if (!(dwStatus
& DSBSTATUS_PLAYING
))
897 pDSBuf
->lpVtbl
->Play(pDSBuf
, 0, 0, DSBPLAY_LOOPING
);
902 S_PaintChannels (endtime
);
908 ===============================================================================
912 ===============================================================================
925 if (!Q_strrchr(Cmd_Argv(i
), '.'))
927 Q_strcpy(name
, Cmd_Argv(i
));
928 Q_strcat(name
, ".wav");
931 Q_strcpy(name
, Cmd_Argv(i
));
932 sfx
= S_PrecacheSound(name
);
933 S_StartSound(hash
++, 0, sfx
, listener_origin
, 1.0, 1.0);
949 if (!Q_strrchr(Cmd_Argv(i
), '.'))
951 Q_strcpy(name
, Cmd_Argv(i
));
952 Q_strcat(name
, ".wav");
955 Q_strcpy(name
, Cmd_Argv(i
));
956 sfx
= S_PrecacheSound(name
);
957 vol
= Q_atof(Cmd_Argv(i
+1));
958 S_StartSound(hash
++, 0, sfx
, listener_origin
, vol
, 1.0);
963 void S_SoundList(void)
971 for (sfx
=known_sfx
, i
=0 ; i
<num_sfx
; i
++, sfx
++)
973 sc
= Cache_Check (&sfx
->cache
);
976 size
= sc
->length
*sc
->width
*(sc
->stereo
+1);
978 if (sc
->loopstart
>= 0)
982 Con_Printf("(%2db) %6i : %s\n",sc
->width
*8, size
, sfx
->name
);
984 Con_Printf ("Total resident: %i\n", total
);
988 void S_LocalSound (char *sound
)
997 sfx
= S_PrecacheSound (sound
);
1000 Con_Printf ("S_LocalSound: can't cache %s\n", sound
);
1003 S_StartSound (cl
.viewentity
, -1, sfx
, vec3_origin
, 1, 1);
1007 void S_ClearPrecache (void)
1012 void S_BeginPrecaching (void)
1017 void S_EndPrecaching (void)