aboutsummaryrefslogtreecommitdiff
path: root/src/s_sound.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/s_sound.c')
-rw-r--r--src/s_sound.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/src/s_sound.c b/src/s_sound.c
new file mode 100644
index 0000000..fa28e9a
--- /dev/null
+++ b/src/s_sound.c
@@ -0,0 +1,688 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION: Platform-independent sound code
30 *
31 *-----------------------------------------------------------------------------*/
32
33// killough 3/7/98: modified to allow arbitrary listeners in spy mode
34// killough 5/2/98: reindented, removed useless code, beautified
35
36#ifdef HAVE_CONFIG_H
37#include "config.h"
38#endif
39
40#include "doomstat.h"
41#include "s_sound.h"
42#include "i_sound.h"
43#include "i_system.h"
44#include "d_main.h"
45#include "r_main.h"
46#include "m_random.h"
47#include "w_wad.h"
48#include "lprintf.h"
49
50// when to clip out sounds
51// Does not fit the large outdoor areas.
52#define S_CLIPPING_DIST (1200<<FRACBITS)
53
54// Distance tp origin when sounds should be maxed out.
55// This should relate to movement clipping resolution
56// (see BLOCKMAP handling).
57// Originally: (200*0x10000).
58
59#define S_CLOSE_DIST (160<<FRACBITS)
60#define S_ATTENUATOR ((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS)
61
62// Adjustable by menu.
63#define NORM_PITCH 128
64#define NORM_PRIORITY 64
65#define NORM_SEP 128
66#define S_STEREO_SWING (96<<FRACBITS)
67
68const char* S_music_files[NUMMUSIC]; // cournia - stores music file names
69
70typedef struct
71{
72 sfxinfo_t *sfxinfo; // sound information (if null, channel avail.)
73 void *origin; // origin of sound
74 int handle; // handle of the sound being played
75 int is_pickup; // killough 4/25/98: whether sound is a player's weapon
76} channel_t;
77
78// the set of channels available
79static channel_t *channels;
80
81// These are not used, but should be (menu).
82// Maximum volume of a sound effect.
83// Internal default is max out of 0-15.
84int snd_SfxVolume = 15;
85
86// Maximum volume of music. Useless so far.
87int snd_MusicVolume = 15;
88
89// whether songs are mus_paused
90static boolean mus_paused;
91
92// music currently being played
93static musicinfo_t *mus_playing;
94
95// following is set
96// by the defaults code in M_misc:
97// number of channels available
98int default_numChannels;
99int numChannels;
100
101//jff 3/17/98 to keep track of last IDMUS specified music num
102int idmusnum;
103
104//
105// Internals.
106//
107
108void S_StopChannel(int cnum);
109
110int S_AdjustSoundParams(mobj_t *listener, mobj_t *source,
111 int *vol, int *sep, int *pitch);
112
113static int S_getChannel(void *origin, sfxinfo_t *sfxinfo, int is_pickup);
114
115// Initializes sound stuff, including volume
116// Sets channels, SFX and music volume,
117// allocates channel buffer, sets S_sfx lookup.
118//
119
120void S_Init(int sfxVolume, int musicVolume)
121{
122 //jff 1/22/98 skip sound init if sound not enabled
123 numChannels = default_numChannels;
124 if (snd_card && !nosfxparm)
125 {
126 int i;
127
128 lprintf(LO_CONFIRM, "S_Init: default sfx volume %d\n", sfxVolume);
129
130 // Whatever these did with DMX, these are rather dummies now.
131 I_SetChannels();
132
133 S_SetSfxVolume(sfxVolume);
134
135 // Allocating the internal channels for mixing
136 // (the maximum numer of sounds rendered
137 // simultaneously) within zone memory.
138 // CPhipps - calloc
139 channels =
140 (channel_t *) calloc(numChannels,sizeof(channel_t));
141
142 // Note that sounds have not been cached (yet).
143 for (i=1 ; i<NUMSFX ; i++)
144 S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
145 }
146
147 // CPhipps - music init reformatted
148 if (mus_card && !nomusicparm) {
149 S_SetMusicVolume(musicVolume);
150
151 // no sounds are playing, and they are not mus_paused
152 mus_paused = 0;
153 }
154}
155
156void S_Stop(void)
157{
158 int cnum;
159
160 //jff 1/22/98 skip sound init if sound not enabled
161 if (snd_card && !nosfxparm)
162 for (cnum=0 ; cnum<numChannels ; cnum++)
163 if (channels[cnum].sfxinfo)
164 S_StopChannel(cnum);
165}
166
167//
168// Per level startup code.
169// Kills playing sounds at start of level,
170// determines music if any, changes music.
171//
172void S_Start(void)
173{
174 int mnum;
175
176 // kill all playing sounds at start of level
177 // (trust me - a good idea)
178
179 S_Stop();
180
181 //jff 1/22/98 return if music is not enabled
182 if (!mus_card || nomusicparm)
183 return;
184
185 // start new music for the level
186 mus_paused = 0;
187
188 if (idmusnum!=-1)
189 mnum = idmusnum; //jff 3/17/98 reload IDMUS music if not -1
190 else
191 if (gamemode == commercial)
192 mnum = mus_runnin + gamemap - 1;
193 else
194 {
195 static const int spmus[] = // Song - Who? - Where?
196 {
197 mus_e3m4, // American e4m1
198 mus_e3m2, // Romero e4m2
199 mus_e3m3, // Shawn e4m3
200 mus_e1m5, // American e4m4
201 mus_e2m7, // Tim e4m5
202 mus_e2m4, // Romero e4m6
203 mus_e2m6, // J.Anderson e4m7 CHIRON.WAD
204 mus_e2m5, // Shawn e4m8
205 mus_e1m9 // Tim e4m9
206 };
207
208 if (gameepisode < 4)
209 mnum = mus_e1m1 + (gameepisode-1)*9 + gamemap-1;
210 else
211 mnum = spmus[gamemap-1];
212 }
213 S_ChangeMusic(mnum, true);
214}
215
216void S_StartSoundAtVolume(void *origin_p, int sfx_id, int volume)
217{
218 int sep, pitch, priority, cnum, is_pickup;
219 sfxinfo_t *sfx;
220 mobj_t *origin = (mobj_t *) origin_p;
221
222 //jff 1/22/98 return if sound is not enabled
223 if (!snd_card || nosfxparm)
224 return;
225
226 is_pickup = sfx_id & PICKUP_SOUND || sfx_id == sfx_oof || (compatibility_level >= prboom_2_compatibility && sfx_id == sfx_noway); // killough 4/25/98
227 sfx_id &= ~PICKUP_SOUND;
228
229 // check for bogus sound #
230 if (sfx_id < 1 || sfx_id > NUMSFX)
231 I_Error("S_StartSoundAtVolume: Bad sfx #: %d", sfx_id);
232
233 sfx = &S_sfx[sfx_id];
234
235 // Initialize sound parameters
236 if (sfx->link)
237 {
238 pitch = sfx->pitch;
239 priority = sfx->priority;
240 volume += sfx->volume;
241
242 if (volume < 1)
243 return;
244
245 if (volume > snd_SfxVolume)
246 volume = snd_SfxVolume;
247 }
248 else
249 {
250 pitch = NORM_PITCH;
251 priority = NORM_PRIORITY;
252 }
253
254 // Check to see if it is audible, modify the params
255 // killough 3/7/98, 4/25/98: code rearranged slightly
256
257 if (!origin || origin == players[displayplayer].mo) {
258 sep = NORM_SEP;
259 volume *= 8;
260 } else
261 if (!S_AdjustSoundParams(players[displayplayer].mo, origin, &volume,
262 &sep, &pitch))
263 return;
264 else
265 if ( origin->x == players[displayplayer].mo->x &&
266 origin->y == players[displayplayer].mo->y)
267 sep = NORM_SEP;
268
269 // hacks to vary the sfx pitches
270 if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit)
271 pitch += 8 - (M_Random()&15);
272 else
273 if (sfx_id != sfx_itemup && sfx_id != sfx_tink)
274 pitch += 16 - (M_Random()&31);
275
276 if (pitch<0)
277 pitch = 0;
278
279 if (pitch>255)
280 pitch = 255;
281
282 // kill old sound
283 for (cnum=0 ; cnum<numChannels ; cnum++)
284 if (channels[cnum].sfxinfo && channels[cnum].origin == origin &&
285 (comp[comp_sound] || channels[cnum].is_pickup == is_pickup))
286 {
287 S_StopChannel(cnum);
288 break;
289 }
290
291 // try to find a channel
292 cnum = S_getChannel(origin, sfx, is_pickup);
293
294 if (cnum<0)
295 return;
296
297 // get lumpnum if necessary
298 // killough 2/28/98: make missing sounds non-fatal
299 if (sfx->lumpnum < 0 && (sfx->lumpnum = I_GetSfxLumpNum(sfx)) < 0)
300 return;
301
302 // increase the usefulness
303 if (sfx->usefulness++ < 0)
304 sfx->usefulness = 1;
305
306 // Assigns the handle to one of the channels in the mix/output buffer.
307 { // e6y: [Fix] Crash with zero-length sounds.
308 int h = I_StartSound(sfx_id, cnum, volume, sep, pitch, priority);
309 if (h != -1) channels[cnum].handle = h;
310 }
311}
312
313void S_StartSound(void *origin, int sfx_id)
314{
315 S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);
316}
317
318void S_StopSound(void *origin)
319{
320 int cnum;
321
322 //jff 1/22/98 return if sound is not enabled
323 if (!snd_card || nosfxparm)
324 return;
325
326 for (cnum=0 ; cnum<numChannels ; cnum++)
327 if (channels[cnum].sfxinfo && channels[cnum].origin == origin)
328 {
329 S_StopChannel(cnum);
330 break;
331 }
332}
333
334
335//
336// Stop and resume music, during game PAUSE.
337//
338void S_PauseSound(void)
339{
340 //jff 1/22/98 return if music is not enabled
341 if (!mus_card || nomusicparm)
342 return;
343
344 if (mus_playing && !mus_paused)
345 {
346 I_PauseSong(mus_playing->handle);
347 mus_paused = true;
348 }
349}
350
351void S_ResumeSound(void)
352{
353 //jff 1/22/98 return if music is not enabled
354 if (!mus_card || nomusicparm)
355 return;
356
357 if (mus_playing && mus_paused)
358 {
359 I_ResumeSong(mus_playing->handle);
360 mus_paused = false;
361 }
362}
363
364
365//
366// Updates music & sounds
367//
368void S_UpdateSounds(void* listener_p)
369{
370 mobj_t *listener = (mobj_t*) listener_p;
371 int cnum;
372
373 //jff 1/22/98 return if sound is not enabled
374 if (!snd_card || nosfxparm)
375 return;
376
377#ifdef UPDATE_MUSIC
378 I_UpdateMusic();
379#endif
380
381 for (cnum=0 ; cnum<numChannels ; cnum++)
382 {
383 sfxinfo_t *sfx;
384 channel_t *c = &channels[cnum];
385 if ((sfx = c->sfxinfo))
386 {
387 if (I_SoundIsPlaying(c->handle))
388 {
389 // initialize parameters
390 int volume = snd_SfxVolume;
391 int pitch = NORM_PITCH;
392 int sep = NORM_SEP;
393
394 if (sfx->link)
395 {
396 pitch = sfx->pitch;
397 volume += sfx->volume;
398 if (volume < 1)
399 {
400 S_StopChannel(cnum);
401 continue;
402 }
403 else
404 if (volume > snd_SfxVolume)
405 volume = snd_SfxVolume;
406 }
407
408 // check non-local sounds for distance clipping
409 // or modify their params
410 if (c->origin && listener_p != c->origin) { // killough 3/20/98
411 if (!S_AdjustSoundParams(listener, c->origin,
412 &volume, &sep, &pitch))
413 S_StopChannel(cnum);
414 else
415 I_UpdateSoundParams(c->handle, volume, sep, pitch);
416 }
417 }
418 else // if channel is allocated but sound has stopped, free it
419 S_StopChannel(cnum);
420 }
421 }
422}
423
424
425
426void S_SetMusicVolume(int volume)
427{
428 //jff 1/22/98 return if music is not enabled
429 if (!mus_card || nomusicparm)
430 return;
431 if (volume < 0 || volume > 15)
432 I_Error("S_SetMusicVolume: Attempt to set music volume at %d", volume);
433 I_SetMusicVolume(volume);
434 snd_MusicVolume = volume;
435}
436
437
438
439void S_SetSfxVolume(int volume)
440{
441 //jff 1/22/98 return if sound is not enabled
442 if (!snd_card || nosfxparm)
443 return;
444 if (volume < 0 || volume > 127)
445 I_Error("S_SetSfxVolume: Attempt to set sfx volume at %d", volume);
446 snd_SfxVolume = volume;
447}
448
449
450
451// Starts some music with the music id found in sounds.h.
452//
453void S_StartMusic(int m_id)
454{
455 //jff 1/22/98 return if music is not enabled
456 if (!mus_card || nomusicparm)
457 return;
458 S_ChangeMusic(m_id, false);
459}
460
461
462
463void S_ChangeMusic(int musicnum, int looping)
464{
465 musicinfo_t *music;
466 int music_file_failed; // cournia - if true load the default MIDI music
467 char* music_filename; // cournia
468
469 //jff 1/22/98 return if music is not enabled
470 if (!mus_card || nomusicparm)
471 return;
472
473 if (musicnum <= mus_None || musicnum >= NUMMUSIC)
474 I_Error("S_ChangeMusic: Bad music number %d", musicnum);
475
476 music = &S_music[musicnum];
477
478 if (mus_playing == music)
479 return;
480
481 // shutdown old music
482 S_StopMusic();
483
484 // get lumpnum if neccessary
485 if (!music->lumpnum)
486 {
487 char namebuf[9];
488 sprintf(namebuf, "d_%s", music->name);
489 music->lumpnum = W_GetNumForName(namebuf);
490 }
491
492 music_file_failed = 1;
493
494 // proff_fs - only load when from IWAD
495 if (lumpinfo[music->lumpnum].source == source_iwad)
496 {
497 // cournia - check to see if we can play a higher quality music file
498 // rather than the default MIDI
499 music_filename = I_FindFile(S_music_files[musicnum], "");
500 if (music_filename)
501 {
502 music_file_failed = I_RegisterMusic(music_filename, music);
503 free(music_filename);
504 }
505 }
506
507 if (music_file_failed)
508 {
509 //cournia - could not load music file, play default MIDI music
510
511 // load & register it
512 music->data = W_CacheLumpNum(music->lumpnum);
513 music->handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum));
514 }
515
516 // play it
517 I_PlaySong(music->handle, looping);
518
519 mus_playing = music;
520}
521
522
523void S_StopMusic(void)
524{
525 //jff 1/22/98 return if music is not enabled
526 if (!mus_card || nomusicparm)
527 return;
528
529 if (mus_playing)
530 {
531 if (mus_paused)
532 I_ResumeSong(mus_playing->handle);
533
534 I_StopSong(mus_playing->handle);
535 I_UnRegisterSong(mus_playing->handle);
536 if (mus_playing->lumpnum >= 0)
537 W_UnlockLumpNum(mus_playing->lumpnum); // cph - release the music data
538
539 mus_playing->data = 0;
540 mus_playing = 0;
541 }
542}
543
544
545
546void S_StopChannel(int cnum)
547{
548 int i;
549 channel_t *c = &channels[cnum];
550
551 //jff 1/22/98 return if sound is not enabled
552 if (!snd_card || nosfxparm)
553 return;
554
555 if (c->sfxinfo)
556 {
557 // stop the sound playing
558 if (I_SoundIsPlaying(c->handle))
559 I_StopSound(c->handle);
560
561 // check to see
562 // if other channels are playing the sound
563 for (i=0 ; i<numChannels ; i++)
564 if (cnum != i && c->sfxinfo == channels[i].sfxinfo)
565 break;
566
567 // degrade usefulness of sound data
568 c->sfxinfo->usefulness--;
569 c->sfxinfo = 0;
570 }
571}
572
573//
574// Changes volume, stereo-separation, and pitch variables
575// from the norm of a sound effect to be played.
576// If the sound is not audible, returns a 0.
577// Otherwise, modifies parameters and returns 1.
578//
579
580int S_AdjustSoundParams(mobj_t *listener, mobj_t *source,
581 int *vol, int *sep, int *pitch)
582{
583 fixed_t adx, ady,approx_dist;
584 angle_t angle;
585
586 //jff 1/22/98 return if sound is not enabled
587 if (!snd_card || nosfxparm)
588 return 0;
589
590 // e6y
591 // Fix crash when the program wants to S_AdjustSoundParams() for player
592 // which is not displayplayer and displayplayer was not spawned at the moment.
593 // It happens in multiplayer demos only.
594 //
595 // Stack trace is:
596 // P_SetupLevel() \ P_LoadThings() \ P_SpawnMapThing() \ P_SpawnPlayer(players[0]) \
597 // P_SetupPsprites() \ P_BringUpWeapon() \ S_StartSound(players[0]->mo, sfx_sawup) \
598 // S_StartSoundAtVolume() \ S_AdjustSoundParams(players[displayplayer]->mo, ...);
599 // players[displayplayer]->mo is NULL
600 //
601 // There is no more crash on e1cmnet3.lmp between e1m2 and e1m3
602 // http://competn.doom2.net/pub/compet-n/doom/coop/movies/e1cmnet3.zip
603 if (!listener)
604 return 0;
605
606 // calculate the distance to sound origin
607 // and clip it if necessary
608 adx = D_abs(listener->x - source->x);
609 ady = D_abs(listener->y - source->y);
610
611 // From _GG1_ p.428. Appox. eucledian distance fast.
612 approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
613
614 if (!approx_dist) // killough 11/98: handle zero-distance as special case
615 {
616 *sep = NORM_SEP;
617 *vol = snd_SfxVolume;
618 return *vol > 0;
619 }
620
621 if (approx_dist > S_CLIPPING_DIST)
622 return 0;
623
624 // angle of source to listener
625 angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y);
626
627 if (angle <= listener->angle)
628 angle += 0xffffffff;
629 angle -= listener->angle;
630 angle >>= ANGLETOFINESHIFT;
631
632 // stereo separation
633 *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS);
634
635 // volume calculation
636 if (approx_dist < S_CLOSE_DIST)
637 *vol = snd_SfxVolume*8;
638 else
639 // distance effect
640 *vol = (snd_SfxVolume * ((S_CLIPPING_DIST-approx_dist)>>FRACBITS) * 8)
641 / S_ATTENUATOR;
642
643 return (*vol > 0);
644}
645
646//
647// S_getChannel :
648// If none available, return -1. Otherwise channel #.
649//
650// killough 4/25/98: made static, added is_pickup argument
651
652static int S_getChannel(void *origin, sfxinfo_t *sfxinfo, int is_pickup)
653{
654 // channel number to use
655 int cnum;
656 channel_t *c;
657
658 //jff 1/22/98 return if sound is not enabled
659 if (!snd_card || nosfxparm)
660 return -1;
661
662 // Find an open channel
663 for (cnum=0; cnum<numChannels && channels[cnum].sfxinfo; cnum++)
664 if (origin && channels[cnum].origin == origin &&
665 channels[cnum].is_pickup == is_pickup)
666 {
667 S_StopChannel(cnum);
668 break;
669 }
670
671 // None available
672 if (cnum == numChannels)
673 { // Look for lower priority
674 for (cnum=0 ; cnum<numChannels ; cnum++)
675 if (channels[cnum].sfxinfo->priority >= sfxinfo->priority)
676 break;
677 if (cnum == numChannels)
678 return -1; // No lower priority. Sorry, Charlie.
679 else
680 S_StopChannel(cnum); // Otherwise, kick out lower priority.
681 }
682
683 c = &channels[cnum]; // channel is decided to be cnum.
684 c->sfxinfo = sfxinfo;
685 c->origin = origin;
686 c->is_pickup = is_pickup; // killough 4/25/98
687 return cnum;
688}