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