1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file s_sound.c
12 /// \brief System-independent sound and music routines
13
14 #include "doomdef.h"
15 #include "doomstat.h"
16 #include "command.h"
17 #include "g_game.h"
18 #include "m_argv.h"
19 #include "r_main.h" // R_PointToAngle2() used to calc stereo sep.
20 #include "r_skins.h" // for skins
21 #include "i_system.h"
22 #include "i_sound.h"
23 #include "s_sound.h"
24 #include "w_wad.h"
25 #include "z_zone.h"
26 #include "d_main.h"
27 #include "r_sky.h" // skyflatnum
28 #include "p_local.h" // camera info
29 #include "fastcmp.h"
30 #include "m_misc.h" // for tunes command
31 #include "m_cond.h" // for conditionsets
32 #include "lua_hook.h" // MusicChange hook
33
34 #ifdef HW3SOUND
35 // 3D Sound Interface
36 #include "hardware/hw3sound.h"
37 #else
38 static INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *vol, INT32 *sep, INT32 *pitch, sfxinfo_t *sfxinfo);
39 #endif
40
41 CV_PossibleValue_t soundvolume_cons_t[] = {{0, "MIN"}, {31, "MAX"}, {0, NULL}};
42 static void SetChannelsNum(void);
43 static void Command_Tunes_f(void);
44 static void Command_RestartAudio_f(void);
45
46 // Sound system toggles
47 static void GameMIDIMusic_OnChange(void);
48 static void GameSounds_OnChange(void);
49 static void GameDigiMusic_OnChange(void);
50 static void MusicPref_OnChange(void);
51
52 #ifdef HAVE_OPENMPT
53 static void ModFilter_OnChange(void);
54 #endif
55
56 static lumpnum_t S_GetMusicLumpNum(const char *mname);
57
58 static boolean S_CheckQueue(void);
59
60 #if defined (_WINDOWS) && !defined (SURROUND) //&& defined (_X86_)
61 #define SURROUND
62 #endif
63
64 #ifdef _WINDOWS
65 consvar_t cv_samplerate = CVAR_INIT ("samplerate", "44100", 0, CV_Unsigned, NULL); //Alam: For easy hacking?
66 #else
67 consvar_t cv_samplerate = CVAR_INIT ("samplerate", "22050", 0, CV_Unsigned, NULL); //Alam: For easy hacking?
68 #endif
69
70 // stereo reverse
71 consvar_t stereoreverse = CVAR_INIT ("stereoreverse", "Off", CV_SAVE, CV_OnOff, NULL);
72
73 // if true, all sounds are loaded at game startup
74 static consvar_t precachesound = CVAR_INIT ("precachesound", "Off", CV_SAVE, CV_OnOff, NULL);
75
76 // actual general (maximum) sound & music volume, saved into the config
77 consvar_t cv_soundvolume = CVAR_INIT ("soundvolume", "18", CV_SAVE, soundvolume_cons_t, NULL);
78 consvar_t cv_digmusicvolume = CVAR_INIT ("digmusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL);
79 consvar_t cv_midimusicvolume = CVAR_INIT ("midimusicvolume", "18", CV_SAVE, soundvolume_cons_t, NULL);
80
Captioning_OnChange(void)81 static void Captioning_OnChange(void)
82 {
83 S_ResetCaptions();
84 if (cv_closedcaptioning.value)
85 S_StartSound(NULL, sfx_menu1);
86 }
87
88 consvar_t cv_closedcaptioning = CVAR_INIT ("closedcaptioning", "Off", CV_SAVE|CV_CALL, CV_OnOff, Captioning_OnChange);
89
90 // number of channels available
91 consvar_t cv_numChannels = CVAR_INIT ("snd_channels", "32", CV_SAVE|CV_CALL, CV_Unsigned, SetChannelsNum);
92
93 static consvar_t surround = CVAR_INIT ("surround", "Off", CV_SAVE, CV_OnOff, NULL);
94
95 consvar_t cv_resetmusic = CVAR_INIT ("resetmusic", "Off", CV_SAVE, CV_OnOff, NULL);
96 consvar_t cv_resetmusicbyheader = CVAR_INIT ("resetmusicbyheader", "Yes", CV_SAVE, CV_YesNo, NULL);
97
98 static CV_PossibleValue_t cons_1upsound_t[] = {
99 {0, "Jingle"},
100 {1, "Sound"},
101 {0, NULL}
102 };
103 consvar_t cv_1upsound = CVAR_INIT ("1upsound", "Jingle", CV_SAVE, cons_1upsound_t, NULL);
104
105 // Sound system toggles, saved into the config
106 consvar_t cv_gamedigimusic = CVAR_INIT ("digimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameDigiMusic_OnChange);
107 consvar_t cv_gamemidimusic = CVAR_INIT ("midimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameMIDIMusic_OnChange);
108 consvar_t cv_gamesounds = CVAR_INIT ("sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameSounds_OnChange);
109
110 // Music preference
111 static CV_PossibleValue_t cons_musicpref_t[] = {
112 {0, "Digital"},
113 {1, "MIDI"},
114 {0, NULL}
115 };
116 consvar_t cv_musicpref = CVAR_INIT ("musicpref", "Digital", CV_SAVE|CV_CALL|CV_NOINIT, cons_musicpref_t, MusicPref_OnChange);
117
118 // Window focus sound sytem toggles
119 consvar_t cv_playmusicifunfocused = CVAR_INIT ("playmusicifunfocused", "No", CV_SAVE, CV_YesNo, NULL);
120 consvar_t cv_playsoundsifunfocused = CVAR_INIT ("playsoundsifunfocused", "No", CV_SAVE, CV_YesNo, NULL);
121
122 #ifdef HAVE_OPENMPT
123 openmpt_module *openmpt_mhandle = NULL;
124 static CV_PossibleValue_t interpolationfilter_cons_t[] = {{0, "Default"}, {1, "None"}, {2, "Linear"}, {4, "Cubic"}, {8, "Windowed sinc"}, {0, NULL}};
125 consvar_t cv_modfilter = CVAR_INIT ("modfilter", "0", CV_SAVE|CV_CALL, interpolationfilter_cons_t, ModFilter_OnChange);
126 #endif
127
128 #define S_MAX_VOLUME 127
129
130 // when to clip out sounds
131 // Does not fit the large outdoor areas.
132 // added 2-2-98 in 8 bit volume control (before (1200*0x10000))
133 #define S_CLIPPING_DIST (1536*0x10000)
134
135 // Distance to origin when sounds should be maxed out.
136 // This should relate to movement clipping resolution
137 // (see BLOCKMAP handling).
138 // Originally: (200*0x10000).
139 // added 2-2-98 in 8 bit volume control (before (160*0x10000))
140 #define S_CLOSE_DIST (160*0x10000)
141
142 // added 2-2-98 in 8 bit volume control (before remove the +4)
143 #define S_ATTENUATOR ((S_CLIPPING_DIST-S_CLOSE_DIST)>>(FRACBITS+4))
144
145 // Adjustable by menu.
146 #define NORM_VOLUME snd_MaxVolume
147
148 #define NORM_PITCH 128
149 #define NORM_PRIORITY 64
150 #define NORM_SEP 128
151
152 #define S_PITCH_PERTURB 1
153 #define S_STEREO_SWING (96*0x10000)
154
155 #ifdef SURROUND
156 #define SURROUND_SEP -128
157 #endif
158
159 // percent attenuation from front to back
160 #define S_IFRACVOL 30
161
162 // the set of channels available
163 static channel_t *channels = NULL;
164 static INT32 numofchannels = 0;
165
166 caption_t closedcaptions[NUMCAPTIONS];
167
S_ResetCaptions(void)168 void S_ResetCaptions(void)
169 {
170 UINT8 i;
171 for (i = 0; i < NUMCAPTIONS; i++)
172 {
173 closedcaptions[i].c = NULL;
174 closedcaptions[i].s = NULL;
175 closedcaptions[i].t = 0;
176 closedcaptions[i].b = 0;
177 }
178 }
179
180 //
181 // Internals.
182 //
183 static void S_StopChannel(INT32 cnum);
184
185 //
186 // S_getChannel
187 //
188 // If none available, return -1. Otherwise channel #.
189 //
S_getChannel(const void * origin,sfxinfo_t * sfxinfo)190 static INT32 S_getChannel(const void *origin, sfxinfo_t *sfxinfo)
191 {
192 // channel number to use
193 INT32 cnum;
194
195 channel_t *c;
196
197 // Find an open channel
198 for (cnum = 0; cnum < numofchannels; cnum++)
199 {
200 if (!channels[cnum].sfxinfo)
201 break;
202
203 // Now checks if same sound is being played, rather
204 // than just one sound per mobj
205 else if (sfxinfo == channels[cnum].sfxinfo && (sfxinfo->pitch & SF_NOMULTIPLESOUND))
206 {
207 return -1;
208 break;
209 }
210 else if (sfxinfo == channels[cnum].sfxinfo && sfxinfo->singularity == true)
211 {
212 S_StopChannel(cnum);
213 break;
214 }
215 else if (origin && channels[cnum].origin == origin && channels[cnum].sfxinfo == sfxinfo)
216 {
217 if (sfxinfo->pitch & SF_NOINTERRUPT)
218 return -1;
219 else
220 S_StopChannel(cnum);
221 break;
222 }
223 else if (origin && channels[cnum].origin == origin
224 && channels[cnum].sfxinfo->name != sfxinfo->name
225 && channels[cnum].sfxinfo->pitch == SF_TOTALLYSINGLE && sfxinfo->pitch == SF_TOTALLYSINGLE)
226 {
227 S_StopChannel(cnum);
228 break;
229 }
230 }
231
232 // None available
233 if (cnum == numofchannels)
234 {
235 // Look for lower priority
236 for (cnum = 0; cnum < numofchannels; cnum++)
237 if (channels[cnum].sfxinfo->priority <= sfxinfo->priority)
238 break;
239
240 if (cnum == numofchannels)
241 {
242 // No lower priority. Sorry, Charlie.
243 return -1;
244 }
245 else
246 {
247 // Otherwise, kick out lower priority.
248 S_StopChannel(cnum);
249 }
250 }
251
252 c = &channels[cnum];
253
254 // channel is decided to be cnum.
255 c->sfxinfo = sfxinfo;
256 c->origin = origin;
257
258 return cnum;
259 }
260
S_RegisterSoundStuff(void)261 void S_RegisterSoundStuff(void)
262 {
263 if (dedicated)
264 {
265 sound_disabled = true;
266 return;
267 }
268
269 CV_RegisterVar(&stereoreverse);
270 CV_RegisterVar(&precachesound);
271
272 CV_RegisterVar(&surround);
273 CV_RegisterVar(&cv_samplerate);
274 CV_RegisterVar(&cv_resetmusic);
275 CV_RegisterVar(&cv_resetmusicbyheader);
276 CV_RegisterVar(&cv_1upsound);
277 CV_RegisterVar(&cv_playsoundsifunfocused);
278 CV_RegisterVar(&cv_playmusicifunfocused);
279 CV_RegisterVar(&cv_gamesounds);
280 CV_RegisterVar(&cv_gamedigimusic);
281 CV_RegisterVar(&cv_gamemidimusic);
282 CV_RegisterVar(&cv_musicpref);
283 #ifdef HAVE_OPENMPT
284 CV_RegisterVar(&cv_modfilter);
285 #endif
286 #ifdef HAVE_MIXERX
287 CV_RegisterVar(&cv_midiplayer);
288 CV_RegisterVar(&cv_midisoundfontpath);
289 CV_RegisterVar(&cv_miditimiditypath);
290 #endif
291
292 COM_AddCommand("tunes", Command_Tunes_f);
293 COM_AddCommand("restartaudio", Command_RestartAudio_f);
294 }
295
SetChannelsNum(void)296 static void SetChannelsNum(void)
297 {
298 INT32 i;
299
300 // Allocating the internal channels for mixing
301 // (the maximum number of sounds rendered
302 // simultaneously) within zone memory.
303 if (channels)
304 S_StopSounds();
305
306 Z_Free(channels);
307 channels = NULL;
308
309
310 if (cv_numChannels.value == 999999999) //Alam_GBC: OH MY ROD!(ROD rimmiced with GOD!)
311 CV_StealthSet(&cv_numChannels,cv_numChannels.defaultvalue);
312
313 #ifdef HW3SOUND
314 if (hws_mode != HWS_DEFAULT_MODE)
315 {
316 HW3S_SetSourcesNum();
317 return;
318 }
319 #endif
320 if (cv_numChannels.value)
321 channels = (channel_t *)Z_Malloc(cv_numChannels.value * sizeof (channel_t), PU_STATIC, NULL);
322 numofchannels = cv_numChannels.value;
323
324 // Free all channels for use
325 for (i = 0; i < numofchannels; i++)
326 channels[i].sfxinfo = 0;
327
328 S_ResetCaptions();
329 }
330
331
332 // Retrieve the lump number of sfx
333 //
S_GetSfxLumpNum(sfxinfo_t * sfx)334 lumpnum_t S_GetSfxLumpNum(sfxinfo_t *sfx)
335 {
336 char namebuf[9];
337 lumpnum_t sfxlump;
338
339 sprintf(namebuf, "ds%s", sfx->name);
340
341 sfxlump = W_CheckNumForName(namebuf);
342 if (sfxlump != LUMPERROR)
343 return sfxlump;
344
345 strlcpy(namebuf, sfx->name, sizeof namebuf);
346
347 sfxlump = W_CheckNumForName(namebuf);
348 if (sfxlump != LUMPERROR)
349 return sfxlump;
350
351 return W_GetNumForName("dsthok");
352 }
353
354 //
355 // Sound Status
356 //
357
S_SoundDisabled(void)358 boolean S_SoundDisabled(void)
359 {
360 return (
361 sound_disabled ||
362 ( window_notinfocus && ! cv_playsoundsifunfocused.value )
363 );
364 }
365
366 // Stop all sounds, load level info, THEN start sounds.
S_StopSounds(void)367 void S_StopSounds(void)
368 {
369 INT32 cnum;
370
371 #ifdef HW3SOUND
372 if (hws_mode != HWS_DEFAULT_MODE)
373 {
374 HW3S_StopSounds();
375 return;
376 }
377 #endif
378
379 // kill all playing sounds at start of level
380 for (cnum = 0; cnum < numofchannels; cnum++)
381 if (channels[cnum].sfxinfo)
382 S_StopChannel(cnum);
383
384 S_ResetCaptions();
385 }
386
S_StopSoundByID(void * origin,sfxenum_t sfx_id)387 void S_StopSoundByID(void *origin, sfxenum_t sfx_id)
388 {
389 INT32 cnum;
390
391 // Sounds without origin can have multiple sources, they shouldn't
392 // be stopped by new sounds.
393 if (!origin)
394 return;
395 #ifdef HW3SOUND
396 if (hws_mode != HWS_DEFAULT_MODE)
397 {
398 HW3S_StopSoundByID(origin, sfx_id);
399 return;
400 }
401 #endif
402 for (cnum = 0; cnum < numofchannels; cnum++)
403 {
404 if (channels[cnum].sfxinfo == &S_sfx[sfx_id] && channels[cnum].origin == origin)
405 {
406 S_StopChannel(cnum);
407 break;
408 }
409 }
410 }
411
S_StopSoundByNum(sfxenum_t sfxnum)412 void S_StopSoundByNum(sfxenum_t sfxnum)
413 {
414 INT32 cnum;
415
416 #ifdef HW3SOUND
417 if (hws_mode != HWS_DEFAULT_MODE)
418 {
419 HW3S_StopSoundByNum(sfxnum);
420 return;
421 }
422 #endif
423 for (cnum = 0; cnum < numofchannels; cnum++)
424 {
425 if (channels[cnum].sfxinfo == &S_sfx[sfxnum])
426 {
427 S_StopChannel(cnum);
428 break;
429 }
430 }
431 }
432
S_StartCaption(sfxenum_t sfx_id,INT32 cnum,UINT16 lifespan)433 void S_StartCaption(sfxenum_t sfx_id, INT32 cnum, UINT16 lifespan)
434 {
435 UINT8 i, set, moveup, start;
436 boolean same = false;
437 sfxinfo_t *sfx;
438
439 if (!cv_closedcaptioning.value) // no captions at all
440 return;
441
442 // check for bogus sound #
443 // I_Assert(sfx_id >= 0); -- allowing sfx_None; this shouldn't be allowed directly if S_StartCaption is ever exposed to Lua by itself
444 I_Assert(sfx_id < NUMSFX);
445
446 sfx = &S_sfx[sfx_id];
447
448 if (sfx->caption[0] == '/') // no caption for this one
449 return;
450
451 start = ((closedcaptions[0].s && (closedcaptions[0].s-S_sfx == sfx_None)) ? 1 : 0);
452
453 if (sfx_id)
454 {
455 for (i = start; i < (set = NUMCAPTIONS-1); i++)
456 {
457 same = ((sfx == closedcaptions[i].s) || (closedcaptions[i].s && fastcmp(sfx->caption, closedcaptions[i].s->caption)));
458 if (same)
459 {
460 set = i;
461 break;
462 }
463 }
464 }
465 else
466 {
467 set = 0;
468 same = (closedcaptions[0].s == sfx);
469 }
470
471 moveup = 255;
472
473 if (!same)
474 {
475 for (i = start; i < set; i++)
476 {
477 if (!(closedcaptions[i].c || closedcaptions[i].s) || (sfx->priority >= closedcaptions[i].s->priority))
478 {
479 set = i;
480 if (closedcaptions[i].s && (sfx->priority >= closedcaptions[i].s->priority))
481 moveup = i;
482 break;
483 }
484 }
485 for (i = NUMCAPTIONS-1; i > set; i--)
486 {
487 if (sfx == closedcaptions[i].s)
488 {
489 closedcaptions[i].c = NULL;
490 closedcaptions[i].s = NULL;
491 closedcaptions[i].t = 0;
492 closedcaptions[i].b = 0;
493 }
494 }
495 }
496
497 if (moveup != 255)
498 {
499 for (i = moveup; i < NUMCAPTIONS-1; i++)
500 {
501 if (!(closedcaptions[i].c || closedcaptions[i].s))
502 break;
503 }
504 for (; i > set; i--)
505 {
506 closedcaptions[i].c = closedcaptions[i-1].c;
507 closedcaptions[i].s = closedcaptions[i-1].s;
508 closedcaptions[i].t = closedcaptions[i-1].t;
509 closedcaptions[i].b = closedcaptions[i-1].b;
510 }
511 }
512
513 closedcaptions[set].c = ((cnum == -1) ? NULL : &channels[cnum]);
514 closedcaptions[set].s = sfx;
515 closedcaptions[set].t = lifespan;
516 closedcaptions[set].b = 2; // bob
517 }
518
S_StartSoundAtVolume(const void * origin_p,sfxenum_t sfx_id,INT32 volume)519 void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
520 {
521 const INT32 initial_volume = volume;
522 INT32 sep, pitch, priority, cnum;
523 const sfxenum_t actual_id = sfx_id;
524 sfxinfo_t *sfx;
525
526 const mobj_t *origin = (const mobj_t *)origin_p;
527
528 listener_t listener = {0,0,0,0};
529 listener_t listener2 = {0,0,0,0};
530
531 mobj_t *listenmobj = players[displayplayer].mo;
532 mobj_t *listenmobj2 = NULL;
533
534 if (S_SoundDisabled() || !sound_started)
535 return;
536
537 // Don't want a sound? Okay then...
538 if (sfx_id == sfx_None)
539 return;
540
541 if (players[displayplayer].awayviewtics)
542 listenmobj = players[displayplayer].awayviewmobj;
543
544 if (splitscreen)
545 {
546 listenmobj2 = players[secondarydisplayplayer].mo;
547 if (players[secondarydisplayplayer].awayviewtics)
548 listenmobj2 = players[secondarydisplayplayer].awayviewmobj;
549 }
550
551 #ifdef HW3SOUND
552 if (hws_mode != HWS_DEFAULT_MODE)
553 {
554 HW3S_StartSound(origin, sfx_id);
555 return;
556 };
557 #endif
558
559 if (camera.chase && !players[displayplayer].awayviewtics)
560 {
561 listener.x = camera.x;
562 listener.y = camera.y;
563 listener.z = camera.z;
564 listener.angle = camera.angle;
565 }
566 else if (listenmobj)
567 {
568 listener.x = listenmobj->x;
569 listener.y = listenmobj->y;
570 listener.z = listenmobj->z;
571 listener.angle = listenmobj->angle;
572 }
573 else if (origin)
574 return;
575
576 if (listenmobj2)
577 {
578 if (camera2.chase && !players[secondarydisplayplayer].awayviewtics)
579 {
580 listener2.x = camera2.x;
581 listener2.y = camera2.y;
582 listener2.z = camera2.z;
583 listener2.angle = camera2.angle;
584 }
585 else
586 {
587 listener2.x = listenmobj2->x;
588 listener2.y = listenmobj2->y;
589 listener2.z = listenmobj2->z;
590 listener2.angle = listenmobj2->angle;
591 }
592 }
593
594 // check for bogus sound #
595 I_Assert(sfx_id >= 1);
596 I_Assert(sfx_id < NUMSFX);
597
598 sfx = &S_sfx[sfx_id];
599
600 if (sfx->skinsound != -1 && origin && origin->skin)
601 {
602 // redirect player sound to the sound in the skin table
603 sfx_id = ((skin_t *)origin->skin)->soundsid[sfx->skinsound];
604 sfx = &S_sfx[sfx_id];
605 }
606
607 // Initialize sound parameters
608 pitch = NORM_PITCH;
609 priority = NORM_PRIORITY;
610
611 if (splitscreen && listenmobj2) // Copy the sound for the split player
612 {
613 // Check to see if it is audible, and if not, modify the params
614 if (origin && origin != listenmobj2)
615 {
616 INT32 rc;
617 rc = S_AdjustSoundParams(listenmobj2, origin, &volume, &sep, &pitch, sfx);
618
619 if (!rc)
620 goto dontplay; // Maybe the other player can hear it...
621
622 if (origin->x == listener2.x && origin->y == listener2.y)
623 sep = NORM_SEP;
624 }
625 else if (!origin)
626 // Do not play origin-less sounds for the second player.
627 // The first player will be able to hear it just fine,
628 // we really don't want it playing twice.
629 goto dontplay;
630 else
631 sep = NORM_SEP;
632
633 // try to find a channel
634 cnum = S_getChannel(origin, sfx);
635
636 if (cnum < 0)
637 return; // If there's no free channels, it's not gonna be free for player 1, either.
638
639 // This is supposed to handle the loading/caching.
640 // For some odd reason, the caching is done nearly
641 // each time the sound is needed?
642
643 // cache data if necessary
644 // NOTE: set sfx->data NULL sfx->lump -1 to force a reload
645 if (!sfx->data)
646 sfx->data = I_GetSfx(sfx);
647
648 // increase the usefulness
649 if (sfx->usefulness++ < 0)
650 sfx->usefulness = -1;
651
652 #ifdef SURROUND
653 // Avoid channel reverse if surround
654 if (stereoreverse.value && sep != SURROUND_SEP)
655 sep = (~sep) & 255;
656 #else
657 if (stereoreverse.value)
658 sep = (~sep) & 255;
659 #endif
660
661 // Handle closed caption input.
662 S_StartCaption(actual_id, cnum, MAXCAPTIONTICS);
663
664 // Assigns the handle to one of the channels in the
665 // mix/output buffer.
666 channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum);
667 }
668
669 dontplay:
670
671 // Check to see if it is audible, and if not, modify the params
672 if (origin && origin != listenmobj)
673 {
674 INT32 rc;
675 rc = S_AdjustSoundParams(listenmobj, origin, &volume, &sep, &pitch, sfx);
676
677 if (!rc)
678 return;
679
680 if (origin->x == listener.x && origin->y == listener.y)
681 sep = NORM_SEP;
682 }
683 else
684 sep = NORM_SEP;
685
686 // try to find a channel
687 cnum = S_getChannel(origin, sfx);
688
689 if (cnum < 0)
690 return;
691
692 // This is supposed to handle the loading/caching.
693 // For some odd reason, the caching is done nearly
694 // each time the sound is needed?
695
696 // cache data if necessary
697 // NOTE: set sfx->data NULL sfx->lump -1 to force a reload
698 if (!sfx->data)
699 sfx->data = I_GetSfx(sfx);
700
701 // increase the usefulness
702 if (sfx->usefulness++ < 0)
703 sfx->usefulness = -1;
704
705 #ifdef SURROUND
706 // Avoid channel reverse if surround
707 if (stereoreverse.value && sep != SURROUND_SEP)
708 sep = (~sep) & 255;
709 #else
710 if (stereoreverse.value)
711 sep = (~sep) & 255;
712 #endif
713
714 // Handle closed caption input.
715 S_StartCaption(actual_id, cnum, MAXCAPTIONTICS);
716
717 // Assigns the handle to one of the channels in the
718 // mix/output buffer.
719 channels[cnum].volume = initial_volume;
720 channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum);
721 }
722
S_StartSound(const void * origin,sfxenum_t sfx_id)723 void S_StartSound(const void *origin, sfxenum_t sfx_id)
724 {
725 if (S_SoundDisabled())
726 return;
727
728 if (mariomode) // Sounds change in Mario mode!
729 {
730 switch (sfx_id)
731 {
732 // case sfx_altow1:
733 // case sfx_altow2:
734 // case sfx_altow3:
735 // case sfx_altow4:
736 // sfx_id = sfx_mario8;
737 // break;
738 case sfx_thok:
739 case sfx_wepfir:
740 sfx_id = sfx_mario7;
741 break;
742 case sfx_pop:
743 sfx_id = sfx_mario5;
744 break;
745 case sfx_jump:
746 sfx_id = sfx_mario6;
747 break;
748 case sfx_shield:
749 case sfx_wirlsg:
750 case sfx_forcsg:
751 case sfx_elemsg:
752 case sfx_armasg:
753 case sfx_attrsg:
754 case sfx_s3k3e:
755 case sfx_s3k3f:
756 case sfx_s3k41:
757 sfx_id = sfx_mario3;
758 break;
759 case sfx_itemup:
760 sfx_id = sfx_mario4;
761 break;
762 // case sfx_tink:
763 // sfx_id = sfx_mario1;
764 // break;
765 // case sfx_cgot:
766 // sfx_id = sfx_mario9;
767 // break;
768 // case sfx_lose:
769 // sfx_id = sfx_mario2;
770 // break;
771 default:
772 break;
773 }
774 }
775 if (maptol & TOL_XMAS) // Some sounds change for xmas
776 {
777 switch (sfx_id)
778 {
779 case sfx_ideya:
780 case sfx_nbmper:
781 case sfx_ncitem:
782 case sfx_ngdone:
783 ++sfx_id;
784 default:
785 break;
786 }
787 }
788
789 // the volume is handled 8 bits
790 #ifdef HW3SOUND
791 if (hws_mode != HWS_DEFAULT_MODE)
792 HW3S_StartSound(origin, sfx_id);
793 else
794 #endif
795 S_StartSoundAtVolume(origin, sfx_id, 255);
796 }
797
S_StopSound(void * origin)798 void S_StopSound(void *origin)
799 {
800 INT32 cnum;
801
802 // Sounds without origin can have multiple sources, they shouldn't
803 // be stopped by new sounds.
804 if (!origin)
805 return;
806
807 #ifdef HW3SOUND
808 if (hws_mode != HWS_DEFAULT_MODE)
809 {
810 HW3S_StopSound(origin);
811 return;
812 }
813 #endif
814 for (cnum = 0; cnum < numofchannels; cnum++)
815 {
816 if (channels[cnum].sfxinfo && channels[cnum].origin == origin)
817 {
818 S_StopChannel(cnum);
819 break;
820 }
821 }
822 }
823
824 //
825 // Updates music & sounds
826 //
827 static INT32 actualsfxvolume; // check for change through console
828 static INT32 actualdigmusicvolume;
829 static INT32 actualmidimusicvolume;
830
S_UpdateSounds(void)831 void S_UpdateSounds(void)
832 {
833 INT32 audible, cnum, volume, sep, pitch;
834 channel_t *c;
835
836 listener_t listener;
837 listener_t listener2;
838
839 mobj_t *listenmobj = players[displayplayer].mo;
840 mobj_t *listenmobj2 = NULL;
841
842 memset(&listener, 0, sizeof(listener_t));
843 memset(&listener2, 0, sizeof(listener_t));
844
845 // Update sound/music volumes, if changed manually at console
846 if (actualsfxvolume != cv_soundvolume.value)
847 S_SetSfxVolume (cv_soundvolume.value);
848 if (actualdigmusicvolume != cv_digmusicvolume.value)
849 S_SetDigMusicVolume (cv_digmusicvolume.value);
850 if (actualmidimusicvolume != cv_midimusicvolume.value)
851 S_SetMIDIMusicVolume (cv_midimusicvolume.value);
852
853 // We're done now, if we're not in a level.
854 if (gamestate != GS_LEVEL)
855 {
856 #ifndef NOMUMBLE
857 // Stop Mumble cutting out. I'm sick of it.
858 I_UpdateMumble(NULL, listener);
859 #endif
860
861 goto notinlevel;
862 }
863
864 if (dedicated || sound_disabled)
865 return;
866
867 if (players[displayplayer].awayviewtics)
868 listenmobj = players[displayplayer].awayviewmobj;
869
870 if (splitscreen)
871 {
872 listenmobj2 = players[secondarydisplayplayer].mo;
873 if (players[secondarydisplayplayer].awayviewtics)
874 listenmobj2 = players[secondarydisplayplayer].awayviewmobj;
875 }
876
877 if (camera.chase && !players[displayplayer].awayviewtics)
878 {
879 listener.x = camera.x;
880 listener.y = camera.y;
881 listener.z = camera.z;
882 listener.angle = camera.angle;
883 }
884 else if (listenmobj)
885 {
886 listener.x = listenmobj->x;
887 listener.y = listenmobj->y;
888 listener.z = listenmobj->z;
889 listener.angle = listenmobj->angle;
890 }
891
892 #ifndef NOMUMBLE
893 I_UpdateMumble(players[consoleplayer].mo, listener);
894 #endif
895
896 #ifdef HW3SOUND
897 if (hws_mode != HWS_DEFAULT_MODE)
898 {
899 HW3S_UpdateSources();
900 goto notinlevel;
901 }
902 #endif
903
904 if (listenmobj2)
905 {
906 if (camera2.chase && !players[secondarydisplayplayer].awayviewtics)
907 {
908 listener2.x = camera2.x;
909 listener2.y = camera2.y;
910 listener2.z = camera2.z;
911 listener2.angle = camera2.angle;
912 }
913 else
914 {
915 listener2.x = listenmobj2->x;
916 listener2.y = listenmobj2->y;
917 listener2.z = listenmobj2->z;
918 listener2.angle = listenmobj2->angle;
919 }
920 }
921
922 for (cnum = 0; cnum < numofchannels; cnum++)
923 {
924 c = &channels[cnum];
925
926 if (c->sfxinfo)
927 {
928 if (I_SoundIsPlaying(c->handle))
929 {
930 // initialize parameters
931 volume = c->volume; // 8 bits internal volume precision
932 pitch = NORM_PITCH;
933 sep = NORM_SEP;
934
935 // check non-local sounds for distance clipping
936 // or modify their params
937 if (c->origin && ((c->origin != players[consoleplayer].mo) ||
938 (splitscreen && c->origin != players[secondarydisplayplayer].mo)))
939 {
940 // Whomever is closer gets the sound, but only in splitscreen.
941 if (listenmobj && listenmobj2 && splitscreen)
942 {
943 const mobj_t *soundmobj = c->origin;
944
945 fixed_t dist1, dist2;
946 dist1 = P_AproxDistance(listener.x-soundmobj->x, listener.y-soundmobj->y);
947 dist2 = P_AproxDistance(listener2.x-soundmobj->x, listener2.y-soundmobj->y);
948
949 if (dist1 <= dist2)
950 {
951 // Player 1 gets the sound
952 audible = S_AdjustSoundParams(listenmobj, c->origin, &volume, &sep, &pitch,
953 c->sfxinfo);
954 }
955 else
956 {
957 // Player 2 gets the sound
958 audible = S_AdjustSoundParams(listenmobj2, c->origin, &volume, &sep, &pitch,
959 c->sfxinfo);
960 }
961
962 if (audible)
963 I_UpdateSoundParams(c->handle, volume, sep, pitch);
964 else
965 S_StopChannel(cnum);
966 }
967 else if (listenmobj && !splitscreen)
968 {
969 // In the case of a single player, he or she always should get updated sound.
970 audible = S_AdjustSoundParams(listenmobj, c->origin, &volume, &sep, &pitch,
971 c->sfxinfo);
972
973 if (audible)
974 I_UpdateSoundParams(c->handle, volume, sep, pitch);
975 else
976 S_StopChannel(cnum);
977 }
978 }
979 }
980 else
981 {
982 // if channel is allocated but sound has stopped, free it
983 S_StopChannel(cnum);
984 }
985 }
986 }
987
988 notinlevel:
989 I_UpdateSound();
990 }
991
S_UpdateClosedCaptions(void)992 void S_UpdateClosedCaptions(void)
993 {
994 UINT8 i;
995 boolean gamestopped = (paused || P_AutoPause());
996 for (i = 0; i < NUMCAPTIONS; i++) // update captions
997 {
998 if (!closedcaptions[i].s)
999 continue;
1000
1001 if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped)
1002 continue;
1003
1004 if (!(--closedcaptions[i].t))
1005 {
1006 closedcaptions[i].c = NULL;
1007 closedcaptions[i].s = NULL;
1008 }
1009 else if (closedcaptions[i].c && !I_SoundIsPlaying(closedcaptions[i].c->handle))
1010 {
1011 closedcaptions[i].c = NULL;
1012 if (closedcaptions[i].t > CAPTIONFADETICS)
1013 closedcaptions[i].t = CAPTIONFADETICS;
1014 }
1015 }
1016 }
1017
S_SetSfxVolume(INT32 volume)1018 void S_SetSfxVolume(INT32 volume)
1019 {
1020 if (volume < 0 || volume > 31)
1021 CONS_Alert(CONS_WARNING, "sfxvolume should be between 0-31\n");
1022
1023 CV_SetValue(&cv_soundvolume, volume&0x1F);
1024 actualsfxvolume = cv_soundvolume.value; // check for change of var
1025
1026 #ifdef HW3SOUND
1027 hws_mode == HWS_DEFAULT_MODE ? I_SetSfxVolume(volume&0x1F) : HW3S_SetSfxVolume(volume&0x1F);
1028 #else
1029 // now hardware volume
1030 I_SetSfxVolume(volume&0x1F);
1031 #endif
1032 }
1033
S_ClearSfx(void)1034 void S_ClearSfx(void)
1035 {
1036 #ifndef DJGPPDOS
1037 size_t i;
1038 for (i = 1; i < NUMSFX; i++)
1039 I_FreeSfx(S_sfx + i);
1040 #endif
1041 }
1042
S_StopChannel(INT32 cnum)1043 static void S_StopChannel(INT32 cnum)
1044 {
1045 channel_t *c = &channels[cnum];
1046
1047 if (c->sfxinfo)
1048 {
1049 // stop the sound playing
1050 if (I_SoundIsPlaying(c->handle))
1051 I_StopSound(c->handle);
1052
1053 // degrade usefulness of sound data
1054 c->sfxinfo->usefulness--;
1055 c->sfxinfo = 0;
1056 }
1057
1058 c->origin = NULL;
1059 }
1060
1061 //
1062 // S_CalculateSoundDistance
1063 //
1064 // Calculates the distance between two points for a sound.
1065 // Clips the distance to prevent overflow.
1066 //
S_CalculateSoundDistance(fixed_t sx1,fixed_t sy1,fixed_t sz1,fixed_t sx2,fixed_t sy2,fixed_t sz2)1067 fixed_t S_CalculateSoundDistance(fixed_t sx1, fixed_t sy1, fixed_t sz1, fixed_t sx2, fixed_t sy2, fixed_t sz2)
1068 {
1069 fixed_t approx_dist, adx, ady;
1070
1071 // calculate the distance to sound origin and clip it if necessary
1072 adx = abs((sx1>>FRACBITS) - (sx2>>FRACBITS));
1073 ady = abs((sy1>>FRACBITS) - (sy2>>FRACBITS));
1074
1075 // From _GG1_ p.428. Approx. euclidian distance fast.
1076 // Take Z into account
1077 adx = adx + ady - ((adx < ady ? adx : ady)>>1);
1078 ady = abs((sz1>>FRACBITS) - (sz2>>FRACBITS));
1079 approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
1080
1081 if (approx_dist >= FRACUNIT/2)
1082 approx_dist = FRACUNIT/2-1;
1083
1084 approx_dist <<= FRACBITS;
1085
1086 return approx_dist;
1087 }
1088
1089 //
1090 // Changes volume, stereo-separation, and pitch variables
1091 // from the norm of a sound effect to be played.
1092 // If the sound is not audible, returns a 0.
1093 // Otherwise, modifies parameters and returns 1.
1094 //
S_AdjustSoundParams(const mobj_t * listener,const mobj_t * source,INT32 * vol,INT32 * sep,INT32 * pitch,sfxinfo_t * sfxinfo)1095 INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *vol, INT32 *sep, INT32 *pitch,
1096 sfxinfo_t *sfxinfo)
1097 {
1098 fixed_t approx_dist;
1099 angle_t angle;
1100
1101 listener_t listensource;
1102
1103 (void)pitch;
1104 if (!listener)
1105 return false;
1106
1107 if (listener == players[displayplayer].mo && camera.chase)
1108 {
1109 listensource.x = camera.x;
1110 listensource.y = camera.y;
1111 listensource.z = camera.z;
1112 listensource.angle = camera.angle;
1113 }
1114 else if (splitscreen && listener == players[secondarydisplayplayer].mo && camera2.chase)
1115 {
1116 listensource.x = camera2.x;
1117 listensource.y = camera2.y;
1118 listensource.z = camera2.z;
1119 listensource.angle = camera2.angle;
1120 }
1121 else
1122 {
1123 listensource.x = listener->x;
1124 listensource.y = listener->y;
1125 listensource.z = listener->z;
1126 listensource.angle = listener->angle;
1127 }
1128
1129 if (sfxinfo->pitch & SF_OUTSIDESOUND) // Rain special case
1130 {
1131 fixed_t x, y, yl, yh, xl, xh, newdist;
1132
1133 if (R_PointInSubsector(listensource.x, listensource.y)->sector->ceilingpic == skyflatnum)
1134 approx_dist = 0;
1135 else
1136 {
1137 // Essentially check in a 1024 unit radius of the player for an outdoor area.
1138 yl = listensource.y - 1024*FRACUNIT;
1139 yh = listensource.y + 1024*FRACUNIT;
1140 xl = listensource.x - 1024*FRACUNIT;
1141 xh = listensource.x + 1024*FRACUNIT;
1142 approx_dist = 1024*FRACUNIT;
1143 for (y = yl; y <= yh; y += FRACUNIT*64)
1144 for (x = xl; x <= xh; x += FRACUNIT*64)
1145 {
1146 if (R_PointInSubsector(x, y)->sector->ceilingpic == skyflatnum)
1147 {
1148 // Found the outdoors!
1149 newdist = S_CalculateSoundDistance(listensource.x, listensource.y, 0, x, y, 0);
1150 if (newdist < approx_dist)
1151 {
1152 approx_dist = newdist;
1153 }
1154 }
1155 }
1156 }
1157 }
1158 else
1159 {
1160 approx_dist = S_CalculateSoundDistance(listensource.x, listensource.y, listensource.z,
1161 source->x, source->y, source->z);
1162 }
1163
1164 // Ring loss, deaths, etc, should all be heard louder.
1165 if (sfxinfo->pitch & SF_X8AWAYSOUND)
1166 approx_dist = FixedDiv(approx_dist,8*FRACUNIT);
1167
1168 // Combine 8XAWAYSOUND with 4XAWAYSOUND and get.... 32XAWAYSOUND?
1169 if (sfxinfo->pitch & SF_X4AWAYSOUND)
1170 approx_dist = FixedDiv(approx_dist,4*FRACUNIT);
1171
1172 if (sfxinfo->pitch & SF_X2AWAYSOUND)
1173 approx_dist = FixedDiv(approx_dist,2*FRACUNIT);
1174
1175 if (approx_dist > S_CLIPPING_DIST)
1176 return 0;
1177
1178 // angle of source to listener
1179 angle = R_PointToAngle2(listensource.x, listensource.y, source->x, source->y);
1180
1181 if (angle > listensource.angle)
1182 angle = angle - listensource.angle;
1183 else
1184 angle = angle + InvAngle(listensource.angle);
1185
1186 #ifdef SURROUND
1187 // Produce a surround sound for angle from 105 till 255
1188 if (surround.value == 1 && (angle > ANG105 && angle < ANG255 ))
1189 *sep = SURROUND_SEP;
1190 else
1191 #endif
1192 {
1193 angle >>= ANGLETOFINESHIFT;
1194
1195 // stereo separation
1196 *sep = 128 - (FixedMul(S_STEREO_SWING, FINESINE(angle))>>FRACBITS);
1197 }
1198
1199 // volume calculation
1200 /* not sure if it should be > (no =), but this matches the old behavior */
1201 if (approx_dist >= S_CLOSE_DIST)
1202 {
1203 // distance effect
1204 INT32 n = (15 * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS));
1205 *vol = FixedMul(*vol * FRACUNIT / 255, n) / S_ATTENUATOR;
1206 }
1207
1208 return (*vol > 0);
1209 }
1210
1211 // Searches through the channels and checks if a sound is playing
1212 // on the given origin.
S_OriginPlaying(void * origin)1213 INT32 S_OriginPlaying(void *origin)
1214 {
1215 INT32 cnum;
1216 if (!origin)
1217 return false;
1218
1219 #ifdef HW3SOUND
1220 if (hws_mode != HWS_DEFAULT_MODE)
1221 return HW3S_OriginPlaying(origin);
1222 #endif
1223
1224 for (cnum = 0; cnum < numofchannels; cnum++)
1225 if (channels[cnum].origin == origin)
1226 return 1;
1227 return 0;
1228 }
1229
1230 // Searches through the channels and checks if a given id
1231 // is playing anywhere.
S_IdPlaying(sfxenum_t id)1232 INT32 S_IdPlaying(sfxenum_t id)
1233 {
1234 INT32 cnum;
1235
1236 #ifdef HW3SOUND
1237 if (hws_mode != HWS_DEFAULT_MODE)
1238 return HW3S_IdPlaying(id);
1239 #endif
1240
1241 for (cnum = 0; cnum < numofchannels; cnum++)
1242 if ((size_t)(channels[cnum].sfxinfo - S_sfx) == (size_t)id)
1243 return 1;
1244 return 0;
1245 }
1246
1247 // Searches through the channels and checks for
1248 // origin x playing sound id y.
S_SoundPlaying(void * origin,sfxenum_t id)1249 INT32 S_SoundPlaying(void *origin, sfxenum_t id)
1250 {
1251 INT32 cnum;
1252 if (!origin)
1253 return 0;
1254
1255 #ifdef HW3SOUND
1256 if (hws_mode != HWS_DEFAULT_MODE)
1257 return HW3S_SoundPlaying(origin, id);
1258 #endif
1259
1260 for (cnum = 0; cnum < numofchannels; cnum++)
1261 {
1262 if (channels[cnum].origin == origin
1263 && (size_t)(channels[cnum].sfxinfo - S_sfx) == (size_t)id)
1264 return 1;
1265 }
1266 return 0;
1267 }
1268
1269 //
1270 // S_StartSoundName
1271 // Starts a sound using the given name.
1272 #define MAXNEWSOUNDS 10
1273 static sfxenum_t newsounds[MAXNEWSOUNDS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1274
S_StartSoundName(void * mo,const char * soundname)1275 void S_StartSoundName(void *mo, const char *soundname)
1276 {
1277 INT32 i, soundnum = 0;
1278 // Search existing sounds...
1279 for (i = sfx_None + 1; i < NUMSFX; i++)
1280 {
1281 if (!S_sfx[i].name)
1282 continue;
1283 if (!stricmp(S_sfx[i].name, soundname))
1284 {
1285 soundnum = i;
1286 break;
1287 }
1288 }
1289
1290 if (!soundnum)
1291 {
1292 for (i = 0; i < MAXNEWSOUNDS; i++)
1293 {
1294 if (newsounds[i] == 0)
1295 break;
1296 if (!S_IdPlaying(newsounds[i]))
1297 {
1298 S_RemoveSoundFx(newsounds[i]);
1299 break;
1300 }
1301 }
1302
1303 if (i == MAXNEWSOUNDS)
1304 {
1305 CONS_Debug(DBG_GAMELOGIC, "Cannot load another extra sound!\n");
1306 return;
1307 }
1308
1309 soundnum = S_AddSoundFx(soundname, false, 0, false);
1310 newsounds[i] = soundnum;
1311 }
1312
1313 S_StartSound(mo, soundnum);
1314 }
1315
1316 //
1317 // Initializes sound stuff, including volume
1318 // Sets channels, SFX volume,
1319 // allocates channel buffer, sets S_sfx lookup.
1320 //
S_InitSfxChannels(INT32 sfxVolume)1321 void S_InitSfxChannels(INT32 sfxVolume)
1322 {
1323 INT32 i;
1324
1325 if (dedicated)
1326 return;
1327
1328 S_SetSfxVolume(sfxVolume);
1329
1330 SetChannelsNum();
1331
1332 // Note that sounds have not been cached (yet).
1333 for (i = 1; i < NUMSFX; i++)
1334 {
1335 S_sfx[i].usefulness = -1; // for I_GetSfx()
1336 S_sfx[i].lumpnum = LUMPERROR;
1337 }
1338
1339 // precache sounds if requested by cmdline, or precachesound var true
1340 if (!sound_disabled && (M_CheckParm("-precachesound") || precachesound.value))
1341 {
1342 // Initialize external data (all sounds) at start, keep static.
1343 CONS_Printf(M_GetText("Loading sounds... "));
1344
1345 for (i = 1; i < NUMSFX; i++)
1346 if (S_sfx[i].name)
1347 S_sfx[i].data = I_GetSfx(&S_sfx[i]);
1348
1349 CONS_Printf(M_GetText(" pre-cached all sound data\n"));
1350 }
1351 }
1352
1353 /// ------------------------
1354 /// Music
1355 /// ------------------------
1356
1357 #ifdef MUSICSLOT_COMPATIBILITY
1358 const char *compat_special_music_slots[16] =
1359 {
1360 "_title", // 1036 title screen
1361 "_intro", // 1037 intro
1362 "_clear", // 1038 level clear
1363 "_inv", // 1039 invincibility
1364 "_shoes", // 1040 super sneakers
1365 "_minv", // 1041 Mario invincibility
1366 "_drown", // 1042 drowning
1367 "_gover", // 1043 game over
1368 "_1up", // 1044 extra life
1369 "_conti", // 1045 continue screen
1370 "_super", // 1046 Super Sonic
1371 "_chsel", // 1047 character select
1372 "_creds", // 1048 credits
1373 "_inter", // 1049 Race Results
1374 "_stjr", // 1050 Sonic Team Jr. Presents
1375 ""
1376 };
1377 #endif
1378
1379 static char music_name[7]; // up to 6-character name
1380 static void *music_data;
1381 static UINT16 music_flags;
1382 static boolean music_looping;
1383
1384 static char queue_name[7];
1385 static UINT16 queue_flags;
1386 static boolean queue_looping;
1387 static UINT32 queue_position;
1388 static UINT32 queue_fadeinms;
1389
1390 static tic_t pause_starttic;
1391
1392 /// ------------------------
1393 /// Music Definitions
1394 /// ------------------------
1395
1396 enum
1397 {
1398 MUSICDEF_220,
1399 MUSICDEF_221,
1400 };
1401
1402 musicdef_t soundtestsfx = {
1403 "_STSFX", // prevents exactly one valid track name from being used on the sound test
1404 "Sound Effects",
1405 "",
1406 "SEGA, VAdaPEGA, other sources",
1407 1, // show on soundtest page 1
1408 0, // with no conditions
1409 0,
1410 0,
1411 0,
1412 false,
1413 NULL
1414 };
1415
1416 musicdef_t *musicdefstart = &soundtestsfx;
1417
1418 //
1419 // search for music definition in wad
1420 //
W_CheckForMusicDefInPwad(UINT16 wadid)1421 static UINT16 W_CheckForMusicDefInPwad(UINT16 wadid)
1422 {
1423 UINT16 i;
1424 lumpinfo_t *lump_p;
1425
1426 lump_p = wadfiles[wadid]->lumpinfo;
1427 for (i = 0; i < wadfiles[wadid]->numlumps; i++, lump_p++)
1428 if (memcmp(lump_p->name, "MUSICDEF", 8) == 0)
1429 return i;
1430
1431 return INT16_MAX; // not found
1432 }
1433
1434 static void
MusicDefStrcpy(char * p,const char * s,size_t n,int version)1435 MusicDefStrcpy (char *p, const char *s, size_t n, int version)
1436 {
1437 strlcpy(p, s, n);
1438 if (version == MUSICDEF_220)
1439 {
1440 while (( p = strchr(p, '_') ))
1441 *p++ = ' '; // turn _ into spaces.
1442 }
1443 }
1444
1445 static boolean
ReadMusicDefFields(UINT16 wadnum,int line,boolean fields,char * stoken,musicdef_t ** defp,int * versionp)1446 ReadMusicDefFields (UINT16 wadnum, int line, boolean fields, char *stoken,
1447 musicdef_t **defp, int *versionp)
1448 {
1449 musicdef_t *def;
1450 int version;
1451
1452 char *value;
1453 char *textline;
1454 int i;
1455
1456 if (!stricmp(stoken, "lump"))
1457 {
1458 value = strtok(NULL, " ");
1459 if (!value)
1460 {
1461 CONS_Alert(CONS_WARNING,
1462 "MUSICDEF: Field '%s' is missing name. (file %s, line %d)\n",
1463 stoken, wadfiles[wadnum]->filename, line);
1464 return false;
1465 }
1466 else
1467 {
1468 musicdef_t *prev = NULL;
1469 def = musicdefstart;
1470
1471 // Search if this is a replacement
1472 //CONS_Printf("S_LoadMusicDefs: Searching for song replacement...\n");
1473 while (def)
1474 {
1475 if (!stricmp(def->name, value))
1476 {
1477 //CONS_Printf("S_LoadMusicDefs: Found song replacement '%s'\n", def->name);
1478 break;
1479 }
1480
1481 prev = def;
1482 def = def->next;
1483 }
1484
1485 // Nothing found, add to the end.
1486 if (!def)
1487 {
1488 def = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL);
1489 STRBUFCPY(def->name, value);
1490 strlwr(def->name);
1491 def->bpm = TICRATE<<(FRACBITS-1); // FixedDiv((60*TICRATE)<<FRACBITS, 120<<FRACBITS)
1492 if (prev != NULL)
1493 prev->next = def;
1494 //CONS_Printf("S_LoadMusicDefs: Added song '%s'\n", def->name);
1495 }
1496
1497 (*defp) = def;
1498 }
1499 }
1500 else if (!stricmp(stoken, "version"))
1501 {
1502 if (fields)/* is this not the first field? */
1503 {
1504 CONS_Alert(CONS_WARNING,
1505 "MUSICDEF: Field '%s' must come first. (file %s, line %d)\n",
1506 stoken, wadfiles[wadnum]->filename, line);
1507 return false;
1508 }
1509 else
1510 {
1511 value = strtok(NULL, " ");
1512 if (!value)
1513 {
1514 CONS_Alert(CONS_WARNING,
1515 "MUSICDEF: Field '%s' is missing version. (file %s, line %d)\n",
1516 stoken, wadfiles[wadnum]->filename, line);
1517 return false;
1518 }
1519 else
1520 {
1521 if (strcasecmp(value, "2.2.0"))
1522 (*versionp) = MUSICDEF_221;
1523 }
1524 }
1525 }
1526 else
1527 {
1528 version = (*versionp);
1529
1530 if (version == MUSICDEF_220)
1531 value = strtok(NULL, " =");
1532 else
1533 {
1534 value = strtok(NULL, "");
1535
1536 if (value)
1537 {
1538 // Find the equals sign.
1539 value = strchr(value, '=');
1540 }
1541 }
1542
1543 if (!value)
1544 {
1545 CONS_Alert(CONS_WARNING,
1546 "MUSICDEF: Field '%s' is missing value. (file %s, line %d)\n",
1547 stoken, wadfiles[wadnum]->filename, line);
1548 return false;
1549 }
1550 else
1551 {
1552 def = (*defp);
1553
1554 if (!def)
1555 {
1556 CONS_Alert(CONS_ERROR,
1557 "MUSICDEF: No music definition before field '%s'. (file %s, line %d)\n",
1558 stoken, wadfiles[wadnum]->filename, line);
1559 return false;
1560 }
1561
1562 if (version != MUSICDEF_220)
1563 {
1564 // Skip the equals sign.
1565 value++;
1566
1567 // Now skip funny whitespace.
1568 value += strspn(value, "\t ");
1569 }
1570
1571 textline = value;
1572 i = atoi(value);
1573
1574 /* based ignored lumps */
1575 if (!stricmp(stoken, "usage")) {
1576 #if 0 // Ignore for now
1577 STRBUFCPY(def->usage, textline);
1578 #endif
1579 } else if (!stricmp(stoken, "source")) {
1580 #if 0 // Ignore for now
1581 STRBUFCPY(def->source, textline);
1582 #endif
1583 } else if (!stricmp(stoken, "title")) {
1584 MusicDefStrcpy(def->title, textline,
1585 sizeof def->title, version);
1586 } else if (!stricmp(stoken, "alttitle")) {
1587 MusicDefStrcpy(def->alttitle, textline,
1588 sizeof def->alttitle, version);
1589 } else if (!stricmp(stoken, "authors")) {
1590 MusicDefStrcpy(def->authors, textline,
1591 sizeof def->authors, version);
1592 } else if (!stricmp(stoken, "soundtestpage")) {
1593 def->soundtestpage = (UINT8)i;
1594 } else if (!stricmp(stoken, "soundtestcond")) {
1595 // Convert to map number
1596 if (textline[0] >= 'A' && textline[0] <= 'Z' && textline[2] == '\0')
1597 i = M_MapNumber(textline[0], textline[1]);
1598 def->soundtestcond = (INT16)i;
1599 } else if (!stricmp(stoken, "stoppingtime")) {
1600 double stoppingtime = atof(textline)*TICRATE;
1601 def->stoppingtics = (tic_t)stoppingtime;
1602 } else if (!stricmp(stoken, "bpm")) {
1603 double bpm = atof(textline);
1604 fixed_t bpmf = FLOAT_TO_FIXED(bpm);
1605 if (bpmf > 0)
1606 def->bpm = FixedDiv((60*TICRATE)<<FRACBITS, bpmf);
1607 } else if (!stricmp(stoken, "loopms")) {
1608 def->loop_ms = atoi(textline);
1609 } else {
1610 CONS_Alert(CONS_WARNING,
1611 "MUSICDEF: Invalid field '%s'. (file %s, line %d)\n",
1612 stoken, wadfiles[wadnum]->filename, line);
1613 }
1614 }
1615 }
1616
1617 return true;
1618 }
1619
S_LoadMusicDefs(UINT16 wadnum)1620 void S_LoadMusicDefs(UINT16 wadnum)
1621 {
1622 UINT16 lumpnum;
1623 char *lump;
1624 char *musdeftext;
1625 size_t size;
1626
1627 char *lf;
1628 char *stoken;
1629
1630 size_t nlf = 0xFFFFFFFF;
1631 size_t ncr;
1632
1633 musicdef_t *def = NULL;
1634 int version = MUSICDEF_220;
1635 int line = 1; // for better error msgs
1636 boolean fields = false;
1637
1638 lumpnum = W_CheckForMusicDefInPwad(wadnum);
1639 if (lumpnum == INT16_MAX)
1640 return;
1641
1642 lump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
1643 size = W_LumpLengthPwad(wadnum, lumpnum);
1644
1645 // Null-terminated MUSICDEF lump.
1646 musdeftext = malloc(size+1);
1647 if (!musdeftext)
1648 I_Error("S_LoadMusicDefs: No more free memory for the parser\n");
1649 M_Memcpy(musdeftext, lump, size);
1650 musdeftext[size] = '\0';
1651
1652 // Find music def
1653 stoken = musdeftext;
1654 for (;;)
1655 {
1656 lf = strpbrk(stoken, "\r\n");
1657 if (lf)
1658 {
1659 if (*lf == '\n')
1660 nlf = 1;
1661 else
1662 nlf = 0;
1663 *lf++ = '\0';/* now we can delimit to here */
1664 }
1665
1666 stoken = strtok(stoken, " ");
1667 if (stoken)
1668 {
1669 if (! ReadMusicDefFields(wadnum, line, fields, stoken,
1670 &def, &version))
1671 break;
1672 fields = true;
1673 }
1674
1675 if (lf)
1676 {
1677 do
1678 {
1679 line += nlf;
1680 ncr = strspn(lf, "\r");/* skip CR */
1681 lf += ncr;
1682 nlf = strspn(lf, "\n");
1683 lf += nlf;
1684 }
1685 while (nlf || ncr) ;
1686
1687 stoken = lf;/* now the next nonempty line */
1688 }
1689 else
1690 break;/* EOF */
1691 }
1692
1693 free(musdeftext);
1694 }
1695
1696 //
1697 // S_InitMusicDefs
1698 //
1699 // Simply load music defs in all wads.
1700 //
S_InitMusicDefs(void)1701 void S_InitMusicDefs(void)
1702 {
1703 UINT16 i;
1704 for (i = 0; i < numwadfiles; i++)
1705 S_LoadMusicDefs(i);
1706 }
1707
1708 musicdef_t **soundtestdefs = NULL;
1709 INT32 numsoundtestdefs = 0;
1710 UINT8 soundtestpage = 1;
1711
1712 //
1713 // S_PrepareSoundTest
1714 //
1715 // Prepare sound test. What am I, your butler?
1716 //
S_PrepareSoundTest(void)1717 boolean S_PrepareSoundTest(void)
1718 {
1719 musicdef_t *def;
1720 INT32 pos = numsoundtestdefs = 0;
1721
1722 for (def = musicdefstart; def; def = def->next)
1723 {
1724 if (!(def->soundtestpage & soundtestpage))
1725 continue;
1726 def->allowed = false;
1727 numsoundtestdefs++;
1728 }
1729
1730 if (!numsoundtestdefs)
1731 return false;
1732
1733 if (soundtestdefs)
1734 Z_Free(soundtestdefs);
1735
1736 if (!(soundtestdefs = Z_Malloc(numsoundtestdefs*sizeof(musicdef_t *), PU_STATIC, NULL)))
1737 I_Error("S_PrepareSoundTest(): could not allocate soundtestdefs.");
1738
1739 for (def = musicdefstart; def /*&& i < numsoundtestdefs*/; def = def->next)
1740 {
1741 if (!(def->soundtestpage & soundtestpage))
1742 continue;
1743 soundtestdefs[pos++] = def;
1744 if (def->soundtestcond > 0 && !(mapvisited[def->soundtestcond-1] & MV_BEATEN))
1745 continue;
1746 if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond))
1747 continue;
1748 def->allowed = true;
1749 }
1750
1751 return true;
1752 }
1753
1754
1755 /// ------------------------
1756 /// Music Status
1757 /// ------------------------
1758
S_DigMusicDisabled(void)1759 boolean S_DigMusicDisabled(void)
1760 {
1761 return digital_disabled;
1762 }
1763
S_MIDIMusicDisabled(void)1764 boolean S_MIDIMusicDisabled(void)
1765 {
1766 return midi_disabled;
1767 }
1768
S_MusicDisabled(void)1769 boolean S_MusicDisabled(void)
1770 {
1771 return (midi_disabled && digital_disabled);
1772 }
1773
S_MusicPlaying(void)1774 boolean S_MusicPlaying(void)
1775 {
1776 return I_SongPlaying();
1777 }
1778
S_MusicPaused(void)1779 boolean S_MusicPaused(void)
1780 {
1781 return I_SongPaused();
1782 }
1783
S_MusicNotInFocus(void)1784 boolean S_MusicNotInFocus(void)
1785 {
1786 return (
1787 ( window_notinfocus && ! cv_playmusicifunfocused.value )
1788 );
1789 }
1790
S_MusicType(void)1791 musictype_t S_MusicType(void)
1792 {
1793 return I_SongType();
1794 }
1795
S_MusicName(void)1796 const char *S_MusicName(void)
1797 {
1798 return music_name;
1799 }
1800
S_MusicExists(const char * mname,boolean checkMIDI,boolean checkDigi)1801 boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi)
1802 {
1803 return (
1804 (checkDigi ? W_CheckNumForName(va("O_%s", mname)) != LUMPERROR : false)
1805 || (checkMIDI ? W_CheckNumForName(va("D_%s", mname)) != LUMPERROR : false)
1806 );
1807 }
1808
1809 /// ------------------------
1810 /// Music Effects
1811 /// ------------------------
1812
S_SpeedMusic(float speed)1813 boolean S_SpeedMusic(float speed)
1814 {
1815 return I_SetSongSpeed(speed);
1816 }
1817
1818 /// ------------------------
1819 /// Music Seeking
1820 /// ------------------------
1821
S_GetMusicLength(void)1822 UINT32 S_GetMusicLength(void)
1823 {
1824 return I_GetSongLength();
1825 }
1826
S_SetMusicLoopPoint(UINT32 looppoint)1827 boolean S_SetMusicLoopPoint(UINT32 looppoint)
1828 {
1829 return I_SetSongLoopPoint(looppoint);
1830 }
1831
S_GetMusicLoopPoint(void)1832 UINT32 S_GetMusicLoopPoint(void)
1833 {
1834 return I_GetSongLoopPoint();
1835 }
1836
S_SetMusicPosition(UINT32 position)1837 boolean S_SetMusicPosition(UINT32 position)
1838 {
1839 return I_SetSongPosition(position);
1840 }
1841
S_GetMusicPosition(void)1842 UINT32 S_GetMusicPosition(void)
1843 {
1844 return I_GetSongPosition();
1845 }
1846
1847 /// ------------------------
1848 /// Music Stacking (Jingles)
1849 /// In this section: mazmazz doesn't know how to do dynamic arrays or struct pointers!
1850 /// ------------------------
1851
1852 char music_stack_nextmusname[7];
1853 boolean music_stack_noposition = false;
1854 UINT32 music_stack_fadeout = 0;
1855 UINT32 music_stack_fadein = 0;
1856 static musicstack_t *music_stacks = NULL;
1857 static musicstack_t *last_music_stack = NULL;
1858
S_SetStackAdjustmentStart(void)1859 void S_SetStackAdjustmentStart(void)
1860 {
1861 if (!pause_starttic)
1862 pause_starttic = gametic;
1863 }
1864
S_AdjustMusicStackTics(void)1865 void S_AdjustMusicStackTics(void)
1866 {
1867 if (pause_starttic)
1868 {
1869 musicstack_t *mst;
1870 for (mst = music_stacks; mst; mst = mst->next)
1871 mst->tic += gametic - pause_starttic;
1872 pause_starttic = 0;
1873 }
1874 }
1875
S_ResetMusicStack(void)1876 static void S_ResetMusicStack(void)
1877 {
1878 musicstack_t *mst, *mst_next;
1879 for (mst = music_stacks; mst; mst = mst_next)
1880 {
1881 mst_next = mst->next;
1882 Z_Free(mst);
1883 }
1884 music_stacks = last_music_stack = NULL;
1885 }
1886
S_RemoveMusicStackEntry(musicstack_t * entry)1887 static void S_RemoveMusicStackEntry(musicstack_t *entry)
1888 {
1889 musicstack_t *mst;
1890 for (mst = music_stacks; mst; mst = mst->next)
1891 {
1892 if (mst == entry)
1893 {
1894 // Remove ourselves from the chain and link
1895 // prev and next together
1896
1897 if (mst->prev)
1898 mst->prev->next = mst->next;
1899 else
1900 music_stacks = mst->next;
1901
1902 if (mst->next)
1903 mst->next->prev = mst->prev;
1904 else
1905 last_music_stack = mst->prev;
1906
1907 break;
1908 }
1909 }
1910 Z_Free(entry);
1911 }
1912
S_RemoveMusicStackEntryByStatus(UINT16 status)1913 static void S_RemoveMusicStackEntryByStatus(UINT16 status)
1914 {
1915 musicstack_t *mst, *mst_next;
1916
1917 if (!status)
1918 return;
1919
1920 for (mst = music_stacks; mst; mst = mst_next)
1921 {
1922 mst_next = mst->next;
1923 if (mst->status == status)
1924 S_RemoveMusicStackEntry(mst);
1925 }
1926 }
1927
S_AddMusicStackEntry(const char * mname,UINT16 mflags,boolean looping,UINT32 position,UINT16 status)1928 static void S_AddMusicStackEntry(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status)
1929 {
1930 musicstack_t *mst, *new_mst;
1931
1932 // if the first entry is empty, force master onto it
1933 if (!music_stacks)
1934 {
1935 music_stacks = Z_Calloc(sizeof (*mst), PU_MUSIC, NULL);
1936 strncpy(music_stacks->musname, (status == JT_MASTER ? mname : (S_CheckQueue() ? queue_name : mapmusname)), 7);
1937 music_stacks->musflags = (status == JT_MASTER ? mflags : (S_CheckQueue() ? queue_flags : mapmusflags));
1938 music_stacks->looping = (status == JT_MASTER ? looping : (S_CheckQueue() ? queue_looping : true));
1939 music_stacks->position = (status == JT_MASTER ? position : (S_CheckQueue() ? queue_position : S_GetMusicPosition()));
1940 music_stacks->tic = gametic;
1941 music_stacks->status = JT_MASTER;
1942 music_stacks->mlumpnum = S_GetMusicLumpNum(music_stacks->musname);
1943 music_stacks->noposition = S_CheckQueue();
1944
1945 if (status == JT_MASTER)
1946 return; // we just added the user's entry here
1947 }
1948
1949 // look for an empty slot to park ourselves
1950 for (mst = music_stacks; mst->next; mst = mst->next);
1951
1952 // create our new entry
1953 new_mst = Z_Calloc(sizeof (*new_mst), PU_MUSIC, NULL);
1954 strncpy(new_mst->musname, mname, 7);
1955 new_mst->musname[6] = 0;
1956 new_mst->musflags = mflags;
1957 new_mst->looping = looping;
1958 new_mst->position = position;
1959 new_mst->tic = gametic;
1960 new_mst->status = status;
1961 new_mst->mlumpnum = S_GetMusicLumpNum(new_mst->musname);
1962 new_mst->noposition = false;
1963
1964 mst->next = new_mst;
1965 new_mst->prev = mst;
1966 new_mst->next = NULL;
1967 last_music_stack = new_mst;
1968 }
1969
S_GetMusicStackEntry(UINT16 status,boolean fromfirst,INT16 startindex)1970 static musicstack_t *S_GetMusicStackEntry(UINT16 status, boolean fromfirst, INT16 startindex)
1971 {
1972 musicstack_t *mst, *start_mst = NULL, *mst_next;
1973
1974 // if the first entry is empty, force master onto it
1975 // fixes a memory corruption bug
1976 if (!music_stacks && status != JT_MASTER)
1977 S_AddMusicStackEntry(mapmusname, mapmusflags, true, S_GetMusicPosition(), JT_MASTER);
1978
1979 if (startindex >= 0)
1980 {
1981 INT16 i = 0;
1982 for (mst = music_stacks; mst && i <= startindex; mst = mst->next, i++)
1983 start_mst = mst;
1984 }
1985 else
1986 start_mst = (fromfirst ? music_stacks : last_music_stack);
1987
1988 for (mst = start_mst; mst; mst = mst_next)
1989 {
1990 mst_next = (fromfirst ? mst->next : mst->prev);
1991
1992 if (!status || mst->status == status)
1993 {
1994 if (P_EvaluateMusicStatus(mst->status, mst->musname))
1995 {
1996 if (!S_MusicExists(mst->musname, !midi_disabled, !digital_disabled)) // paranoia
1997 S_RemoveMusicStackEntry(mst); // then continue
1998 else
1999 return mst;
2000 }
2001 else
2002 S_RemoveMusicStackEntry(mst); // then continue
2003 }
2004 }
2005
2006 return NULL;
2007 }
2008
S_RetainMusic(const char * mname,UINT16 mflags,boolean looping,UINT32 position,UINT16 status)2009 void S_RetainMusic(const char *mname, UINT16 mflags, boolean looping, UINT32 position, UINT16 status)
2010 {
2011 musicstack_t *mst;
2012
2013 if (!status) // we use this as a null indicator, don't push
2014 {
2015 CONS_Alert(CONS_ERROR, "Music stack entry must have a nonzero status.\n");
2016 return;
2017 }
2018 else if (status == JT_MASTER) // enforce only one JT_MASTER
2019 {
2020 for (mst = music_stacks; mst; mst = mst->next)
2021 {
2022 if (mst->status == JT_MASTER)
2023 {
2024 CONS_Alert(CONS_ERROR, "Music stack can only have one JT_MASTER entry.\n");
2025 return;
2026 }
2027 }
2028 }
2029 else // remove any existing status
2030 S_RemoveMusicStackEntryByStatus(status);
2031
2032 S_AddMusicStackEntry(mname, mflags, looping, position, status);
2033 }
2034
S_RecallMusic(UINT16 status,boolean fromfirst)2035 boolean S_RecallMusic(UINT16 status, boolean fromfirst)
2036 {
2037 UINT32 newpos = 0;
2038 boolean mapmuschanged = false;
2039 musicstack_t *result;
2040 musicstack_t *entry = Z_Calloc(sizeof (*result), PU_MUSIC, NULL);
2041 boolean currentmidi = (I_SongType() == MU_MID || I_SongType() == MU_MID_EX);
2042 boolean midipref = cv_musicpref.value;
2043
2044 if (status)
2045 result = S_GetMusicStackEntry(status, fromfirst, -1);
2046 else
2047 result = S_GetMusicStackEntry(JT_NONE, false, -1);
2048
2049 if (result && !S_MusicExists(result->musname, !midi_disabled, !digital_disabled))
2050 {
2051 Z_Free(entry);
2052 return false; // music doesn't exist, so don't do anything
2053 }
2054
2055 // make a copy of result, since we make modifications to our copy
2056 if (result)
2057 {
2058 *entry = *result;
2059 strncpy(entry->musname, result->musname, 7);
2060 }
2061
2062 // no result, just grab mapmusname
2063 if (!result || !entry->musname[0] || ((status == JT_MASTER || (music_stacks ? !music_stacks->status : false)) && !entry->status))
2064 {
2065 strncpy(entry->musname, mapmusname, 7);
2066 entry->musflags = mapmusflags;
2067 entry->looping = true;
2068 entry->position = mapmusposition;
2069 entry->tic = gametic;
2070 entry->status = JT_MASTER;
2071 entry->mlumpnum = S_GetMusicLumpNum(entry->musname);
2072 entry->noposition = false; // don't set this until we do the mapmuschanged check, below. Else, this breaks some resumes.
2073 }
2074
2075 if (entry->status == JT_MASTER)
2076 {
2077 mapmuschanged = strnicmp(entry->musname, mapmusname, 7);
2078 if (mapmuschanged)
2079 {
2080 strncpy(entry->musname, mapmusname, 7);
2081 entry->musflags = mapmusflags;
2082 entry->looping = true;
2083 entry->position = mapmusposition;
2084 entry->tic = gametic;
2085 entry->status = JT_MASTER;
2086 entry->mlumpnum = S_GetMusicLumpNum(entry->musname);
2087 entry->noposition = true;
2088 }
2089 S_ResetMusicStack();
2090 }
2091 else if (!entry->status)
2092 {
2093 Z_Free(entry);
2094 return false;
2095 }
2096
2097 if (strncmp(entry->musname, S_MusicName(), 7) || // don't restart music if we're already playing it
2098 (midipref != currentmidi && S_PrefAvailable(midipref, entry->musname))) // but do if the user's preference has changed
2099 {
2100 if (music_stack_fadeout)
2101 S_ChangeMusicEx(entry->musname, entry->musflags, entry->looping, 0, music_stack_fadeout, 0);
2102 else
2103 {
2104 S_ChangeMusicEx(entry->musname, entry->musflags, entry->looping, 0, 0, music_stack_fadein);
2105
2106 if (!entry->noposition && !music_stack_noposition) // HACK: Global boolean to toggle position resuming, e.g., de-superize
2107 {
2108 UINT32 poslapse = 0;
2109
2110 // To prevent the game from jumping past the end of the music, we need
2111 // to check if we can get the song's length. Otherwise, if the lapsed resume time goes
2112 // over a LOOPPOINT, mixer_sound.c will be unable to calculate the new resume position.
2113 if (S_GetMusicLength())
2114 poslapse = (UINT32)((float)(gametic - entry->tic)/(float)TICRATE*(float)MUSICRATE);
2115
2116 newpos = entry->position + poslapse;
2117 }
2118
2119 // If the newly recalled music lumpnum does not match the lumpnum that we stored in stack,
2120 // then discard the new position. That way, we will not recall an invalid position
2121 // when the music is replaced or digital/MIDI is toggled.
2122 if (newpos > 0 && S_MusicPlaying() && S_GetMusicLumpNum(entry->musname) == entry->mlumpnum)
2123 S_SetMusicPosition(newpos);
2124 else
2125 {
2126 S_StopFadingMusic();
2127 S_SetInternalMusicVolume(100);
2128 }
2129 }
2130 music_stack_noposition = false;
2131 music_stack_fadeout = 0;
2132 music_stack_fadein = JINGLEPOSTFADE;
2133 }
2134
2135 Z_Free(entry);
2136 return true;
2137 }
2138
2139 /// ------------------------
2140 /// Music Playback
2141 /// ------------------------
2142
S_GetMusicLumpNum(const char * mname)2143 static lumpnum_t S_GetMusicLumpNum(const char *mname)
2144 {
2145 boolean midipref = cv_musicpref.value;
2146
2147 if (S_PrefAvailable(midipref, mname))
2148 return W_GetNumForName(va(midipref ? "d_%s":"o_%s", mname));
2149 else if (S_PrefAvailable(!midipref, mname))
2150 return W_GetNumForName(va(midipref ? "o_%s":"d_%s", mname));
2151 else
2152 return LUMPERROR;
2153 }
2154
S_LoadMusic(const char * mname)2155 static boolean S_LoadMusic(const char *mname)
2156 {
2157 lumpnum_t mlumpnum;
2158 void *mdata;
2159
2160 if (S_MusicDisabled())
2161 return false;
2162
2163 mlumpnum = S_GetMusicLumpNum(mname);
2164
2165 if (mlumpnum == LUMPERROR)
2166 {
2167 CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded: lump not found!\n", mname);
2168 return false;
2169 }
2170
2171 // load & register it
2172 mdata = W_CacheLumpNum(mlumpnum, PU_MUSIC);
2173
2174
2175 if (I_LoadSong(mdata, W_LumpLength(mlumpnum)))
2176 {
2177 strncpy(music_name, mname, 7);
2178 music_name[6] = 0;
2179 music_data = mdata;
2180 return true;
2181 }
2182 else
2183 {
2184 CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded: engine failure!\n", mname);
2185 return false;
2186 }
2187 }
2188
S_UnloadMusic(void)2189 static void S_UnloadMusic(void)
2190 {
2191 I_UnloadSong();
2192
2193 #ifndef HAVE_SDL //SDL uses RWOPS
2194 Z_ChangeTag(music_data, PU_CACHE);
2195 #endif
2196 music_data = NULL;
2197
2198 music_name[0] = 0;
2199 music_flags = 0;
2200 music_looping = false;
2201 }
2202
S_PlayMusic(boolean looping,UINT32 fadeinms)2203 static boolean S_PlayMusic(boolean looping, UINT32 fadeinms)
2204 {
2205 musicdef_t *def;
2206
2207 if (S_MusicDisabled())
2208 return false;
2209
2210 if ((!fadeinms && !I_PlaySong(looping)) ||
2211 (fadeinms && !I_FadeInPlaySong(fadeinms, looping)))
2212 {
2213 CONS_Alert(CONS_ERROR, "Music %.6s could not be played: engine failure!\n", music_name);
2214 S_UnloadMusic();
2215 return false;
2216 }
2217
2218 /* set loop point from MUSICDEF */
2219 for (def = musicdefstart; def; def = def->next)
2220 {
2221 if (strcasecmp(def->name, music_name) == 0)
2222 {
2223 if (def->loop_ms)
2224 S_SetMusicLoopPoint(def->loop_ms);
2225 break;
2226 }
2227 }
2228
2229 S_InitMusicVolume(); // switch between digi and sequence volume
2230
2231 if (S_MusicNotInFocus())
2232 S_PauseAudio();
2233
2234 return true;
2235 }
2236
S_QueueMusic(const char * mmusic,UINT16 mflags,boolean looping,UINT32 position,UINT32 fadeinms)2237 static void S_QueueMusic(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 fadeinms)
2238 {
2239 strncpy(queue_name, mmusic, 7);
2240 queue_flags = mflags;
2241 queue_looping = looping;
2242 queue_position = position;
2243 queue_fadeinms = fadeinms;
2244 }
2245
S_CheckQueue(void)2246 static boolean S_CheckQueue(void)
2247 {
2248 return queue_name[0];
2249 }
2250
S_ClearQueue(void)2251 static void S_ClearQueue(void)
2252 {
2253 queue_name[0] = queue_flags = queue_looping = queue_position = queue_fadeinms = 0;
2254 }
2255
S_ChangeMusicToQueue(void)2256 static void S_ChangeMusicToQueue(void)
2257 {
2258 S_ChangeMusicEx(queue_name, queue_flags, queue_looping, queue_position, 0, queue_fadeinms);
2259 S_ClearQueue();
2260 }
2261
S_ChangeMusicEx(const char * mmusic,UINT16 mflags,boolean looping,UINT32 position,UINT32 prefadems,UINT32 fadeinms)2262 void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms)
2263 {
2264 char newmusic[7];
2265 boolean currentmidi = (I_SongType() == MU_MID || I_SongType() == MU_MID_EX);
2266 boolean midipref = cv_musicpref.value;
2267
2268 if (S_MusicDisabled())
2269 return;
2270
2271 strncpy(newmusic, mmusic, 7);
2272 if (LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms))
2273 return;
2274 newmusic[6] = 0;
2275
2276 // No Music (empty string)
2277 if (newmusic[0] == 0)
2278 {
2279 if (prefadems)
2280 I_FadeSong(0, prefadems, &S_StopMusic);
2281 else
2282 S_StopMusic();
2283 return;
2284 }
2285
2286 if (prefadems) // queue music change for after fade // allow even if the music is the same
2287 // && S_MusicPlaying() // Let the delay happen even if we're not playing music
2288 {
2289 CONS_Debug(DBG_DETAILED, "Now fading out song %s\n", music_name);
2290 S_QueueMusic(newmusic, mflags, looping, position, fadeinms);
2291 I_FadeSong(0, prefadems, S_ChangeMusicToQueue);
2292 return;
2293 }
2294 else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET) ||
2295 (midipref != currentmidi && S_PrefAvailable(midipref, newmusic)))
2296 {
2297 CONS_Debug(DBG_DETAILED, "Now playing song %s\n", newmusic);
2298
2299 S_StopMusic();
2300
2301 if (!S_LoadMusic(newmusic))
2302 return;
2303
2304 music_flags = mflags;
2305 music_looping = looping;
2306
2307 if (!S_PlayMusic(looping, fadeinms))
2308 return;
2309
2310 if (position)
2311 I_SetSongPosition(position);
2312
2313 I_SetSongTrack(mflags & MUSIC_TRACKMASK);
2314 }
2315 else if (fadeinms) // let fades happen with same music
2316 {
2317 I_SetSongPosition(position);
2318 I_FadeSong(100, fadeinms, NULL);
2319 }
2320 else // reset volume to 100 with same music
2321 {
2322 I_StopFadingSong();
2323 I_FadeSong(100, 500, NULL);
2324 }
2325 }
2326
S_StopMusic(void)2327 void S_StopMusic(void)
2328 {
2329 if (!I_SongPlaying())
2330 return;
2331
2332 if (I_SongPaused())
2333 I_ResumeSong();
2334
2335 S_SpeedMusic(1.0f);
2336 I_StopSong();
2337 S_UnloadMusic(); // for now, stopping also means you unload the song
2338
2339 if (cv_closedcaptioning.value)
2340 {
2341 if (closedcaptions[0].s-S_sfx == sfx_None)
2342 {
2343 if (gamestate != wipegamestate)
2344 {
2345 closedcaptions[0].c = NULL;
2346 closedcaptions[0].s = NULL;
2347 closedcaptions[0].t = 0;
2348 closedcaptions[0].b = 0;
2349 }
2350 else
2351 closedcaptions[0].t = CAPTIONFADETICS;
2352 }
2353 }
2354 }
2355
2356 //
2357 // Stop and resume music, during game PAUSE.
2358 //
S_PauseAudio(void)2359 void S_PauseAudio(void)
2360 {
2361 if (I_SongPlaying() && !I_SongPaused())
2362 I_PauseSong();
2363
2364 S_SetStackAdjustmentStart();
2365 }
2366
S_ResumeAudio(void)2367 void S_ResumeAudio(void)
2368 {
2369 if (S_MusicNotInFocus())
2370 return;
2371
2372 if (I_SongPlaying() && I_SongPaused())
2373 I_ResumeSong();
2374
2375 S_AdjustMusicStackTics();
2376 }
2377
S_SetMusicVolume(INT32 digvolume,INT32 seqvolume)2378 void S_SetMusicVolume(INT32 digvolume, INT32 seqvolume)
2379 {
2380 if (digvolume < 0)
2381 digvolume = cv_digmusicvolume.value;
2382 if (seqvolume < 0)
2383 seqvolume = cv_midimusicvolume.value;
2384
2385 if (digvolume < 0 || digvolume > 31)
2386 CONS_Alert(CONS_WARNING, "digmusicvolume should be between 0-31\n");
2387 CV_SetValue(&cv_digmusicvolume, digvolume&31);
2388 actualdigmusicvolume = cv_digmusicvolume.value; //check for change of var
2389
2390 if (seqvolume < 0 || seqvolume > 31)
2391 CONS_Alert(CONS_WARNING, "midimusicvolume should be between 0-31\n");
2392 CV_SetValue(&cv_midimusicvolume, seqvolume&31);
2393 actualmidimusicvolume = cv_midimusicvolume.value; //check for change of var
2394
2395 switch(I_SongType())
2396 {
2397 case MU_MID:
2398 case MU_MID_EX:
2399 //case MU_MOD:
2400 //case MU_GME:
2401 I_SetMusicVolume(seqvolume&31);
2402 break;
2403 default:
2404 I_SetMusicVolume(digvolume&31);
2405 break;
2406 }
2407 }
2408
2409 /// ------------------------
2410 /// Music Fading
2411 /// ------------------------
2412
S_SetInternalMusicVolume(INT32 volume)2413 void S_SetInternalMusicVolume(INT32 volume)
2414 {
2415 I_SetInternalMusicVolume(min(max(volume, 0), 100));
2416 }
2417
S_StopFadingMusic(void)2418 void S_StopFadingMusic(void)
2419 {
2420 I_StopFadingSong();
2421 }
2422
S_FadeMusicFromVolume(UINT8 target_volume,INT16 source_volume,UINT32 ms)2423 boolean S_FadeMusicFromVolume(UINT8 target_volume, INT16 source_volume, UINT32 ms)
2424 {
2425 if (source_volume < 0)
2426 return I_FadeSong(target_volume, ms, NULL);
2427 else
2428 return I_FadeSongFromVolume(target_volume, source_volume, ms, NULL);
2429 }
2430
S_FadeOutStopMusic(UINT32 ms)2431 boolean S_FadeOutStopMusic(UINT32 ms)
2432 {
2433 return I_FadeSong(0, ms, &S_StopMusic);
2434 }
2435
2436 /// ------------------------
2437 /// Init & Others
2438 /// ------------------------
2439
2440 //
2441 // Per level startup code.
2442 // Kills playing sounds at start of level,
2443 // determines music if any, changes music.
2444 //
S_StartEx(boolean reset)2445 void S_StartEx(boolean reset)
2446 {
2447 if (mapmusflags & MUSIC_RELOADRESET)
2448 {
2449 strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7);
2450 mapmusname[6] = 0;
2451 mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
2452 mapmusposition = mapheaderinfo[gamemap-1]->muspos;
2453 }
2454
2455 if (RESETMUSIC || reset)
2456 S_StopMusic();
2457 S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
2458
2459 S_ResetMusicStack();
2460 music_stack_noposition = false;
2461 music_stack_fadeout = 0;
2462 music_stack_fadein = JINGLEPOSTFADE;
2463 }
2464
Command_Tunes_f(void)2465 static void Command_Tunes_f(void)
2466 {
2467 const char *tunearg;
2468 UINT16 tunenum, track = 0;
2469 UINT32 position = 0;
2470 const size_t argc = COM_Argc();
2471
2472 if (argc < 2) //tunes slot ...
2473 {
2474 CONS_Printf("tunes <name/num> [track] [speed] [position] / <-show> / <-default> / <-none>:\n");
2475 CONS_Printf(M_GetText("Play an arbitrary music lump. If a map number is used, 'MAP##M' is played.\n"));
2476 CONS_Printf(M_GetText("If the format supports multiple songs, you can specify which one to play.\n\n"));
2477 CONS_Printf(M_GetText("* With \"-show\", shows the currently playing tune and track.\n"));
2478 CONS_Printf(M_GetText("* With \"-default\", returns to the default music for the map.\n"));
2479 CONS_Printf(M_GetText("* With \"-none\", any music playing will be stopped.\n"));
2480 return;
2481 }
2482
2483 tunearg = COM_Argv(1);
2484 tunenum = (UINT16)atoi(tunearg);
2485 track = 0;
2486
2487 if (!strcasecmp(tunearg, "-show"))
2488 {
2489 CONS_Printf(M_GetText("The current tune is: %s [track %d]\n"),
2490 mapmusname, (mapmusflags & MUSIC_TRACKMASK));
2491 return;
2492 }
2493 if (!strcasecmp(tunearg, "-none"))
2494 {
2495 S_StopMusic();
2496 return;
2497 }
2498 else if (!strcasecmp(tunearg, "-default"))
2499 {
2500 tunearg = mapheaderinfo[gamemap-1]->musname;
2501 track = mapheaderinfo[gamemap-1]->mustrack;
2502 }
2503 else if (!tunearg[2] && toupper(tunearg[0]) >= 'A' && toupper(tunearg[0]) <= 'Z')
2504 tunenum = (UINT16)M_MapNumber(tunearg[0], tunearg[1]);
2505
2506 if (tunenum && tunenum >= 1036)
2507 {
2508 CONS_Alert(CONS_NOTICE, M_GetText("Valid music slots are 1 to 1035.\n"));
2509 return;
2510 }
2511 if (!tunenum && strlen(tunearg) > 6) // This is automatic -- just show the error just in case
2512 CONS_Alert(CONS_NOTICE, M_GetText("Music name too long - truncated to six characters.\n"));
2513
2514 if (argc > 2)
2515 track = (UINT16)atoi(COM_Argv(2))-1;
2516
2517 if (tunenum)
2518 snprintf(mapmusname, 7, "%sM", G_BuildMapName(tunenum));
2519 else
2520 strncpy(mapmusname, tunearg, 7);
2521
2522 if (argc > 4)
2523 position = (UINT32)atoi(COM_Argv(4));
2524
2525 mapmusname[6] = 0;
2526 mapmusflags = (track & MUSIC_TRACKMASK);
2527 mapmusposition = position;
2528
2529 S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
2530
2531 if (argc > 3)
2532 {
2533 float speed = (float)atof(COM_Argv(3));
2534 if (speed > 0.0f)
2535 S_SpeedMusic(speed);
2536 }
2537 }
2538
Command_RestartAudio_f(void)2539 static void Command_RestartAudio_f(void)
2540 {
2541 S_StopMusic();
2542 S_StopSounds();
2543 I_ShutdownMusic();
2544 I_ShutdownSound();
2545 I_StartupSound();
2546 I_InitMusic();
2547
2548 // These must be called or no sound and music until manually set.
2549
2550 I_SetSfxVolume(cv_soundvolume.value);
2551 S_SetMusicVolume(cv_digmusicvolume.value, cv_midimusicvolume.value);
2552 if (Playing()) // Gotta make sure the player is in a level
2553 P_RestoreMusic(&players[consoleplayer]);
2554 }
2555
GameSounds_OnChange(void)2556 void GameSounds_OnChange(void)
2557 {
2558 if (M_CheckParm("-nosound") || M_CheckParm("-noaudio"))
2559 return;
2560
2561 if (sound_disabled)
2562 {
2563 sound_disabled = false;
2564 I_StartupSound(); // will return early if initialised
2565 S_InitSfxChannels(cv_soundvolume.value);
2566 S_StartSound(NULL, sfx_strpst);
2567 }
2568 else
2569 {
2570 sound_disabled = true;
2571 S_StopSounds();
2572 }
2573 }
2574
GameDigiMusic_OnChange(void)2575 void GameDigiMusic_OnChange(void)
2576 {
2577 if (M_CheckParm("-nomusic") || M_CheckParm("-noaudio"))
2578 return;
2579 else if (M_CheckParm("-nodigmusic"))
2580 return;
2581
2582 if (digital_disabled)
2583 {
2584 digital_disabled = false;
2585 I_StartupSound(); // will return early if initialised
2586 I_InitMusic();
2587
2588 if (Playing())
2589 P_RestoreMusic(&players[consoleplayer]);
2590 else if ((!cv_musicpref.value || midi_disabled) && S_DigExists("_clear"))
2591 S_ChangeMusicInternal("_clear", false);
2592 }
2593 else
2594 {
2595 digital_disabled = true;
2596 if (S_MusicType() != MU_MID && S_MusicType() != MU_MID_EX)
2597 {
2598 S_StopMusic();
2599 if (!midi_disabled)
2600 {
2601 if (Playing())
2602 P_RestoreMusic(&players[consoleplayer]);
2603 else
2604 S_ChangeMusicInternal("_clear", false);
2605 }
2606 }
2607 }
2608 }
2609
GameMIDIMusic_OnChange(void)2610 void GameMIDIMusic_OnChange(void)
2611 {
2612 if (M_CheckParm("-nomusic") || M_CheckParm("-noaudio"))
2613 return;
2614 else if (M_CheckParm("-nomidimusic"))
2615 return;
2616
2617 if (midi_disabled)
2618 {
2619 midi_disabled = false;
2620 I_StartupSound(); // will return early if initialised
2621 I_InitMusic();
2622
2623 if (Playing())
2624 P_RestoreMusic(&players[consoleplayer]);
2625 else if ((cv_musicpref.value || digital_disabled) && S_MIDIExists("_clear"))
2626 S_ChangeMusicInternal("_clear", false);
2627 }
2628 else
2629 {
2630 midi_disabled = true;
2631 if (S_MusicType() == MU_MID || S_MusicType() == MU_MID_EX)
2632 {
2633 S_StopMusic();
2634 if (!digital_disabled)
2635 {
2636 if (Playing())
2637 P_RestoreMusic(&players[consoleplayer]);
2638 else
2639 S_ChangeMusicInternal("_clear", false);
2640 }
2641 }
2642 }
2643 }
2644
MusicPref_OnChange(void)2645 void MusicPref_OnChange(void)
2646 {
2647 if (M_CheckParm("-nomusic") || M_CheckParm("-noaudio") ||
2648 M_CheckParm("-nomidimusic") || M_CheckParm("-nodigmusic"))
2649 return;
2650
2651 if (Playing())
2652 P_RestoreMusic(&players[consoleplayer]);
2653 else if (S_PrefAvailable(cv_musicpref.value, "_clear"))
2654 S_ChangeMusicInternal("_clear", false);
2655 }
2656
2657 #ifdef HAVE_OPENMPT
ModFilter_OnChange(void)2658 void ModFilter_OnChange(void)
2659 {
2660 if (openmpt_mhandle)
2661 openmpt_module_set_render_param(openmpt_mhandle, OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH, cv_modfilter.value);
2662 }
2663 #endif
2664