1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 2014-2020 by Sonic Team Junior.
4 //
5 // This program is free software distributed under the
6 // terms of the GNU General Public License, version 2.
7 // See the 'LICENSE' file for more details.
8 //-----------------------------------------------------------------------------
9 /// \file
10 /// \brief SDL Mixer interface for sound
11 
12 #ifdef HAVE_LIBGME
13 #ifdef HAVE_ZLIB
14 #ifndef _MSC_VER
15 #ifndef _LARGEFILE64_SOURCE
16 #define _LARGEFILE64_SOURCE
17 #endif
18 #endif
19 
20 #ifndef _LFS64_LARGEFILE
21 #define _LFS64_LARGEFILE
22 #endif
23 
24 #ifndef _FILE_OFFSET_BITS
25 #define _FILE_OFFSET_BITS 0
26 #endif
27 
28 #include <zlib.h>
29 #endif // HAVE_ZLIB
30 #endif // HAVE_LIBGME
31 
32 #include "../doomdef.h"
33 #include "../doomstat.h" // menuactive
34 
35 #if defined(HAVE_SDL) && defined(HAVE_MIXER) && SOUND==SOUND_MIXER
36 
37 #include "../sounds.h"
38 #include "../s_sound.h"
39 #include "../i_sound.h"
40 #include "../w_wad.h"
41 #include "../z_zone.h"
42 #include "../byteptr.h"
43 
44 #ifdef _MSC_VER
45 #pragma warning(disable : 4214 4244)
46 #endif
47 #include "SDL.h"
48 #ifdef _MSC_VER
49 #pragma warning(default : 4214 4244)
50 #endif
51 
52 #ifdef HAVE_MIXERX
53 #include "SDL_mixer_ext.h"
54 #else
55 #include "SDL_mixer.h"
56 #endif
57 
58 /* This is the version number macro for the current SDL_mixer version: */
59 #ifndef SDL_MIXER_COMPILEDVERSION
60 #define SDL_MIXER_COMPILEDVERSION \
61 	SDL_VERSIONNUM(MIX_MAJOR_VERSION, MIX_MINOR_VERSION, MIX_PATCHLEVEL)
62 #endif
63 
64 /* This macro will evaluate to true if compiled with SDL_mixer at least X.Y.Z */
65 #ifndef SDL_MIXER_VERSION_ATLEAST
66 #define SDL_MIXER_VERSION_ATLEAST(X, Y, Z) \
67 	(SDL_MIXER_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
68 #endif
69 
70 // thanks alam for making the buildbots happy!
71 #if SDL_MIXER_VERSION_ATLEAST(2,0,2)
72 #define MUS_MP3_MAD MUS_MP3_MAD_UNUSED
73 #define MUS_MODPLUG MUS_MODPLUG_UNUSED
74 #endif
75 
76 #ifdef HAVE_LIBGME
77 #include "gme/gme.h"
78 #define GME_TREBLE 5.0f
79 #define GME_BASS 1.0f
80 #endif // HAVE_LIBGME
81 
82 static UINT16 BUFFERSIZE = 2048;
83 static UINT16 SAMPLERATE = 44100;
84 
85 #ifdef HAVE_OPENMPT
86 #include "libopenmpt/libopenmpt.h"
87 #endif
88 
89 /// ------------------------
90 /// Audio Declarations
91 /// ------------------------
92 
93 UINT8 sound_started = false;
94 
95 static Mix_Music *music;
96 static UINT8 music_volume, sfx_volume, internal_volume;
97 static float loop_point;
98 static float song_length; // length in seconds
99 static boolean songpaused;
100 static UINT32 music_bytes;
101 static boolean is_looping;
102 
103 // fading
104 static boolean is_fading;
105 static UINT8 fading_source;
106 static UINT8 fading_target;
107 static UINT32 fading_timer;
108 static UINT32 fading_duration;
109 static INT32 fading_id;
110 static void (*fading_callback)(void);
111 static boolean fading_nocleanup;
112 
113 #ifdef HAVE_LIBGME
114 static Music_Emu *gme;
115 static UINT16 current_track;
116 #endif
117 
118 #ifdef HAVE_OPENMPT
119 static int mod_err = OPENMPT_ERROR_OK;
120 static const char *mod_err_str;
121 static UINT16 current_subsong;
122 static size_t probesize;
123 static int result;
124 #endif
125 
126 #ifdef HAVE_MIXERX
Midiplayer_Onchange(void)127 static void Midiplayer_Onchange(void)
128 {
129 	boolean restart = false;
130 
131 	if (I_SongType() != MU_NONE && I_SongType() != MU_MID_EX && I_SongType() != MU_MID)
132 		return;
133 
134 	if (Mix_GetMidiPlayer() != cv_midiplayer.value)
135 	{
136 		if (Mix_SetMidiPlayer(cv_midiplayer.value)) // <> 0 means error
137 			CONS_Alert(CONS_ERROR, "Midi player error: %s", Mix_GetError());
138 		else
139 			restart = true;
140 	}
141 
142 	if (stricmp(Mix_GetSoundFonts(), cv_midisoundfontpath.string))
143 	{
144 		if (!Mix_SetSoundFonts(cv_midisoundfontpath.string)) // == 0 means error
145 			CONS_Alert(CONS_ERROR, "Sound font error: %s", Mix_GetError());
146 		else
147 			restart = true;
148 	}
149 
150 	Mix_Timidity_addToPathList(cv_miditimiditypath.string);
151 
152 	if (restart)
153 		S_StartEx(true);
154 }
155 
MidiSoundfontPath_Onchange(void)156 static void MidiSoundfontPath_Onchange(void)
157 {
158 	if (Mix_GetMidiPlayer() != MIDI_Fluidsynth || (I_SongType() != MU_NONE && I_SongType() != MU_MID_EX))
159 		return;
160 
161 	if (stricmp(Mix_GetSoundFonts(), cv_midisoundfontpath.string))
162 	{
163 		char *miditoken;
164 		char *source = strdup(cv_midisoundfontpath.string);
165 		boolean proceed = true;
166 		// check if file exists; menu calls this method at every keystroke
167 
168 		// get first token
169 		miditoken = strtok(source, ";");
170 
171 		while (miditoken != NULL)
172 		{
173 			SDL_RWops *rw = SDL_RWFromFile(miditoken, "r");
174 			if (rw != NULL)
175 				SDL_RWclose(rw);
176 			else
177 			{
178 				proceed = false;
179 				break;
180 			}
181 			miditoken = strtok(NULL, ";");
182 		}
183 
184 		free(source);
185 
186 		if (proceed)
187 		{
188 			if (!Mix_SetSoundFonts(cv_midisoundfontpath.string))
189 				CONS_Alert(CONS_ERROR, "Sound font error: %s", Mix_GetError());
190 			else
191 				S_StartEx(true);
192 		}
193 	}
194 }
195 
196 // make sure that s_sound.c does not already verify these
197 // which happens when: defined(HAVE_MIXERX) && !defined(HAVE_MIXER)
198 static CV_PossibleValue_t midiplayer_cons_t[] = {{MIDI_OPNMIDI, "OPNMIDI"}, {MIDI_Fluidsynth, "Fluidsynth"}, {MIDI_Timidity, "Timidity"}, {MIDI_Native, "Native"}, {0, NULL}};
199 consvar_t cv_midiplayer = CVAR_INIT ("midiplayer", "OPNMIDI" /*MIDI_OPNMIDI*/, CV_CALL|CV_NOINIT|CV_SAVE, midiplayer_cons_t, Midiplayer_Onchange);
200 consvar_t cv_midisoundfontpath = CVAR_INIT ("midisoundfont", "sf2/8bitsf.SF2", CV_CALL|CV_NOINIT|CV_SAVE, NULL, MidiSoundfontPath_Onchange);
201 consvar_t cv_miditimiditypath = CVAR_INIT ("midisoundbank", "./timidity", CV_SAVE, NULL, NULL);
202 #endif
203 
var_cleanup(void)204 static void var_cleanup(void)
205 {
206 	song_length = loop_point = 0.0f;
207 	music_bytes = fading_source = fading_target =\
208 	 fading_timer = fading_duration = 0;
209 
210 	songpaused = is_looping =\
211 	 is_fading = false;
212 
213 	// HACK: See music_loop, where we want the fade timing to proceed after a non-looping
214 	// song has stopped playing
215 	if (!fading_nocleanup)
216 		fading_callback = NULL;
217 	else
218 		fading_nocleanup = false; // use it once, set it back immediately
219 
220 	internal_volume = 100;
221 }
222 
223 #if defined (HAVE_LIBGME) && defined (HAVE_ZLIB)
get_zlib_error(int zErr)224 static const char* get_zlib_error(int zErr)
225 {
226 	switch (zErr)
227 	{
228 		case Z_ERRNO:
229 			return "Z_ERRNO";
230 		case Z_STREAM_ERROR:
231 			return "Z_STREAM_ERROR";
232 		case Z_DATA_ERROR:
233 			return "Z_DATA_ERROR";
234 		case Z_MEM_ERROR:
235 			return "Z_MEM_ERROR";
236 		case Z_BUF_ERROR:
237 			return "Z_BUF_ERROR";
238 		case Z_VERSION_ERROR:
239 			return "Z_VERSION_ERROR";
240 		default:
241 			return "unknown error";
242 	}
243 }
244 #endif
245 
246 /// ------------------------
247 /// Audio System
248 /// ------------------------
249 
I_StartupSound(void)250 void I_StartupSound(void)
251 {
252 	//I_Assert(!sound_started);
253 	if (sound_started)
254 		return;
255 
256 #ifdef _WIN32
257 	// Force DirectSound instead of WASAPI
258 	// SDL 2.0.6+ defaults to the latter and it screws up our sound effects
259 	SDL_setenv("SDL_AUDIODRIVER", "directsound", 1);
260 #endif
261 
262 	// EE inits audio first so we're following along.
263 	if (SDL_WasInit(SDL_INIT_AUDIO) == SDL_INIT_AUDIO)
264 	{
265 		CONS_Debug(DBG_DETAILED, "SDL Audio already started\n");
266 		return;
267 	}
268 	else if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
269 	{
270 		CONS_Alert(CONS_ERROR, "Error initializing SDL Audio: %s\n", SDL_GetError());
271 		// call to start audio failed -- we do not have it
272 		return;
273 	}
274 
275 	fading_nocleanup = false;
276 
277 	var_cleanup();
278 
279 	music = NULL;
280 	music_volume = sfx_volume = 0;
281 
282 #ifdef HAVE_MIXERX
283 	Mix_SetMidiPlayer(cv_midiplayer.value);
284 	Mix_SetSoundFonts(cv_midisoundfontpath.string);
285 	Mix_Timidity_addToPathList(cv_miditimiditypath.string);
286 #endif
287 #if SDL_MIXER_VERSION_ATLEAST(1,2,11)
288 	Mix_Init(MIX_INIT_FLAC|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_MOD);
289 #endif
290 
291 	if (Mix_OpenAudio(SAMPLERATE, AUDIO_S16SYS, 2, BUFFERSIZE) < 0)
292 	{
293 		CONS_Alert(CONS_ERROR, "Error starting SDL_Mixer: %s\n", Mix_GetError());
294 		// call to start audio failed -- we do not have it
295 		return;
296 	}
297 
298 #ifdef HAVE_OPENMPT
299 	CONS_Printf("libopenmpt version: %s\n", openmpt_get_string("library_version"));
300 	CONS_Printf("libopenmpt build date: %s\n", openmpt_get_string("build"));
301 #endif
302 
303 	sound_started = true;
304 	songpaused = false;
305 	Mix_AllocateChannels(256);
306 }
307 
I_ShutdownSound(void)308 void I_ShutdownSound(void)
309 {
310 	if (!sound_started)
311 		return; // not an error condition
312 	sound_started = false;
313 
314 	Mix_CloseAudio();
315 #if SDL_MIXER_VERSION_ATLEAST(1,2,11)
316 	Mix_Quit();
317 #endif
318 
319 	SDL_QuitSubSystem(SDL_INIT_AUDIO);
320 
321 #ifdef HAVE_LIBGME
322 	if (gme)
323 		gme_delete(gme);
324 #endif
325 #ifdef HAVE_OPENMPT
326 	if (openmpt_mhandle)
327 		openmpt_module_destroy(openmpt_mhandle);
328 #endif
329 }
330 
I_UpdateSound(void)331 void I_UpdateSound(void)
332 {
333 }
334 
335 /// ------------------------
336 /// SFX
337 /// ------------------------
338 
339 // this is as fast as I can possibly make it.
340 // sorry. more asm needed.
ds2chunk(void * stream)341 static Mix_Chunk *ds2chunk(void *stream)
342 {
343 	UINT16 ver,freq;
344 	UINT32 samples, i, newsamples;
345 	UINT8 *sound;
346 
347 	SINT8 *s;
348 	INT16 *d;
349 	INT16 o;
350 	fixed_t step, frac;
351 
352 	// lump header
353 	ver = READUINT16(stream); // sound version format?
354 	if (ver != 3) // It should be 3 if it's a doomsound...
355 		return NULL; // onos! it's not a doomsound!
356 	freq = READUINT16(stream);
357 	samples = READUINT32(stream);
358 
359 	// convert from signed 8bit ???hz to signed 16bit 44100hz.
360 	switch(freq)
361 	{
362 	case 44100:
363 		if (samples >= UINT32_MAX>>2)
364 			return NULL; // would wrap, can't store.
365 		newsamples = samples;
366 		break;
367 	case 22050:
368 		if (samples >= UINT32_MAX>>3)
369 			return NULL; // would wrap, can't store.
370 		newsamples = samples<<1;
371 		break;
372 	case 11025:
373 		if (samples >= UINT32_MAX>>4)
374 			return NULL; // would wrap, can't store.
375 		newsamples = samples<<2;
376 		break;
377 	default:
378 		frac = (44100 << FRACBITS) / (UINT32)freq;
379 		if (!(frac & 0xFFFF)) // other solid multiples (change if FRACBITS != 16)
380 			newsamples = samples * (frac >> FRACBITS);
381 		else // strange and unusual fractional frequency steps, plus anything higher than 44100hz.
382 			newsamples = FixedMul(FixedDiv(samples, freq), 44100) + 1; // add 1 to counter truncation.
383 		if (newsamples >= UINT32_MAX>>2)
384 			return NULL; // would and/or did wrap, can't store.
385 		break;
386 	}
387 	sound = Z_Malloc(newsamples<<2, PU_SOUND, NULL); // samples * frequency shift * bytes per sample * channels
388 
389 	s = (SINT8 *)stream;
390 	d = (INT16 *)sound;
391 
392 	i = 0;
393 	switch(freq)
394 	{
395 	case 44100: // already at the same rate? well that makes it simple.
396 		while(i++ < samples)
397 		{
398 			o = ((INT16)(*s++)+0x80)<<8; // changed signedness and shift up to 16 bits
399 			*d++ = o; // left channel
400 			*d++ = o; // right channel
401 		}
402 		break;
403 	case 22050: // unwrap 2x
404 		while(i++ < samples)
405 		{
406 			o = ((INT16)(*s++)+0x80)<<8; // changed signedness and shift up to 16 bits
407 			*d++ = o; // left channel
408 			*d++ = o; // right channel
409 			*d++ = o; // left channel
410 			*d++ = o; // right channel
411 		}
412 		break;
413 	case 11025: // unwrap 4x
414 		while(i++ < samples)
415 		{
416 			o = ((INT16)(*s++)+0x80)<<8; // changed signedness and shift up to 16 bits
417 			*d++ = o; // left channel
418 			*d++ = o; // right channel
419 			*d++ = o; // left channel
420 			*d++ = o; // right channel
421 			*d++ = o; // left channel
422 			*d++ = o; // right channel
423 			*d++ = o; // left channel
424 			*d++ = o; // right channel
425 		}
426 		break;
427 	default: // convert arbitrary hz to 44100.
428 		step = 0;
429 		frac = ((UINT32)freq << FRACBITS) / 44100 + 1; //Add 1 to counter truncation.
430 		while (i < samples)
431 		{
432 			o = (INT16)(*s+0x80)<<8; // changed signedness and shift up to 16 bits
433 			while (step < FRACUNIT) // this is as fast as I can make it.
434 			{
435 				*d++ = o; // left channel
436 				*d++ = o; // right channel
437 				step += frac;
438 			}
439 			do {
440 				i++; s++;
441 				step -= FRACUNIT;
442 			} while (step >= FRACUNIT);
443 		}
444 		break;
445 	}
446 
447 	// return Mixer Chunk.
448 	return Mix_QuickLoad_RAW(sound, (Uint32)((UINT8*)d-sound));
449 }
450 
I_GetSfx(sfxinfo_t * sfx)451 void *I_GetSfx(sfxinfo_t *sfx)
452 {
453 	void *lump;
454 	Mix_Chunk *chunk;
455 	SDL_RWops *rw;
456 #ifdef HAVE_LIBGME
457 	Music_Emu *emu;
458 	gme_info_t *info;
459 #endif
460 
461 	if (sfx->lumpnum == LUMPERROR)
462 		sfx->lumpnum = S_GetSfxLumpNum(sfx);
463 	sfx->length = W_LumpLength(sfx->lumpnum);
464 
465 	lump = W_CacheLumpNum(sfx->lumpnum, PU_SOUND);
466 
467 	// convert from standard DoomSound format.
468 	chunk = ds2chunk(lump);
469 	if (chunk)
470 	{
471 		Z_Free(lump);
472 		return chunk;
473 	}
474 
475 	// Not a doom sound? Try something else.
476 #ifdef HAVE_LIBGME
477 	// VGZ format
478 	if (((UINT8 *)lump)[0] == 0x1F
479 		&& ((UINT8 *)lump)[1] == 0x8B)
480 	{
481 #ifdef HAVE_ZLIB
482 		UINT8 *inflatedData;
483 		size_t inflatedLen;
484 		z_stream stream;
485 		int zErr; // Somewhere to handle any error messages zlib tosses out
486 
487 		memset(&stream, 0x00, sizeof (z_stream)); // Init zlib stream
488 		// Begin the inflation process
489 		inflatedLen = *(UINT32 *)lump + (sfx->length-4); // Last 4 bytes are the decompressed size, typically
490 		inflatedData = (UINT8 *)Z_Malloc(inflatedLen, PU_SOUND, NULL); // Make room for the decompressed data
491 		stream.total_in = stream.avail_in = sfx->length;
492 		stream.total_out = stream.avail_out = inflatedLen;
493 		stream.next_in = (UINT8 *)lump;
494 		stream.next_out = inflatedData;
495 
496 		zErr = inflateInit2(&stream, 32 + MAX_WBITS);
497 		if (zErr == Z_OK) // We're good to go
498 		{
499 			zErr = inflate(&stream, Z_FINISH);
500 			if (zErr == Z_STREAM_END) {
501 				// Run GME on new data
502 				if (!gme_open_data(inflatedData, inflatedLen, &emu, SAMPLERATE))
503 				{
504 					short *mem;
505 					UINT32 len;
506 					gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
507 
508 					Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around
509 					Z_Free(lump); // We're done with the uninflated lump now, too.
510 
511 					gme_start_track(emu, 0);
512 					gme_set_equalizer(emu, &eq);
513 					gme_track_info(emu, &info, 0);
514 
515 					len = (info->play_length * 441 / 10) << 2;
516 					mem = Z_Malloc(len, PU_SOUND, 0);
517 					gme_play(emu, len >> 1, mem);
518 					gme_free_info(info);
519 					gme_delete(emu);
520 
521 					return Mix_QuickLoad_RAW((Uint8 *)mem, len);
522 				}
523 			}
524 			else
525 				CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", get_zlib_error(zErr), stream.msg);
526 			(void)inflateEnd(&stream);
527 		}
528 		else // Hold up, zlib's got a problem
529 			CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", get_zlib_error(zErr), stream.msg);
530 		Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up
531 #else
532 		return NULL; // No zlib support
533 #endif
534 	}
535 	// Try to read it as a GME sound
536 	else if (!gme_open_data(lump, sfx->length, &emu, SAMPLERATE))
537 	{
538 		short *mem;
539 		UINT32 len;
540 		gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
541 
542 		Z_Free(lump);
543 
544 		gme_start_track(emu, 0);
545 		gme_set_equalizer(emu, &eq);
546 		gme_track_info(emu, &info, 0);
547 
548 		len = (info->play_length * 441 / 10) << 2;
549 		mem = Z_Malloc(len, PU_SOUND, 0);
550 		gme_play(emu, len >> 1, mem);
551 		gme_free_info(info);
552 		gme_delete(emu);
553 
554 		return Mix_QuickLoad_RAW((Uint8 *)mem, len);
555 	}
556 #endif
557 
558 	// Try to load it as a WAVE or OGG using Mixer.
559 	rw = SDL_RWFromMem(lump, sfx->length);
560 	if (rw != NULL)
561 	{
562 		chunk = Mix_LoadWAV_RW(rw, 1);
563 		return chunk;
564 	}
565 
566 	return NULL; // haven't been able to get anything
567 }
568 
I_FreeSfx(sfxinfo_t * sfx)569 void I_FreeSfx(sfxinfo_t *sfx)
570 {
571 	if (sfx->data)
572 	{
573 		Mix_Chunk *chunk = (Mix_Chunk*)sfx->data;
574 		UINT8 *abufdata = NULL;
575 		if (chunk->allocated == 0)
576 		{
577 			// We allocated the data in this chunk, so get the abuf from mixer, then let it free the chunk, THEN we free the data
578 			// I believe this should ensure the sound is not playing when we free it
579 			abufdata = chunk->abuf;
580 		}
581 		Mix_FreeChunk(sfx->data);
582 		if (abufdata)
583 		{
584 			// I'm going to assume we used Z_Malloc to allocate this data.
585 			Z_Free(abufdata);
586 		}
587 	}
588 	sfx->data = NULL;
589 	sfx->lumpnum = LUMPERROR;
590 }
591 
I_StartSound(sfxenum_t id,UINT8 vol,UINT8 sep,UINT8 pitch,UINT8 priority,INT32 channel)592 INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
593 {
594 	UINT8 volume = (((UINT16)vol + 1) * (UINT16)sfx_volume) / 62; // (256 * 31) / 62 == 127
595 	INT32 handle = Mix_PlayChannel(channel, S_sfx[id].data, 0);
596 	Mix_Volume(handle, volume);
597 	Mix_SetPanning(handle, min((UINT16)(0xff-sep)<<1, 0xff), min((UINT16)(sep)<<1, 0xff));
598 	(void)pitch; // Mixer can't handle pitch
599 	(void)priority; // priority and channel management is handled by SRB2...
600 	return handle;
601 }
602 
I_StopSound(INT32 handle)603 void I_StopSound(INT32 handle)
604 {
605 	Mix_HaltChannel(handle);
606 }
607 
I_SoundIsPlaying(INT32 handle)608 boolean I_SoundIsPlaying(INT32 handle)
609 {
610 	return Mix_Playing(handle);
611 }
612 
I_UpdateSoundParams(INT32 handle,UINT8 vol,UINT8 sep,UINT8 pitch)613 void I_UpdateSoundParams(INT32 handle, UINT8 vol, UINT8 sep, UINT8 pitch)
614 {
615 	UINT8 volume = (((UINT16)vol + 1) * (UINT16)sfx_volume) / 62; // (256 * 31) / 62 == 127
616 	Mix_Volume(handle, volume);
617 	Mix_SetPanning(handle, min((UINT16)(0xff-sep)<<1, 0xff), min((UINT16)(sep)<<1, 0xff));
618 	(void)pitch;
619 }
620 
I_SetSfxVolume(UINT8 volume)621 void I_SetSfxVolume(UINT8 volume)
622 {
623 	sfx_volume = volume;
624 }
625 
626 /// ------------------------
627 /// Music Utilities
628 /// ------------------------
629 
get_real_volume(UINT8 volume)630 static UINT32 get_real_volume(UINT8 volume)
631 {
632 #ifdef _WIN32
633 	if (I_SongType() == MU_MID)
634 		// HACK: Until we stop using native MIDI,
635 		// disable volume changes
636 		return ((UINT32)31*128/31); // volume = 31
637 	else
638 #endif
639 		// convert volume to mixer's 128 scale
640 		// then apply internal_volume as a percentage
641 		return ((UINT32)volume*128/31) * (UINT32)internal_volume / 100;
642 }
643 
get_adjusted_position(UINT32 position)644 static UINT32 get_adjusted_position(UINT32 position)
645 {
646 	// all in milliseconds
647 	UINT32 length = I_GetSongLength();
648 	UINT32 looppoint = I_GetSongLoopPoint();
649 	if (length)
650 		return position >= length ? (position % (length-looppoint)) : position;
651 	else
652 		return position;
653 }
654 
do_fading_callback(void)655 static void do_fading_callback(void)
656 {
657 	if (fading_callback)
658 		(*fading_callback)();
659 	fading_callback = NULL;
660 }
661 
662 /// ------------------------
663 /// Music Hooks
664 /// ------------------------
665 
count_music_bytes(int chan,void * stream,int len,void * udata)666 static void count_music_bytes(int chan, void *stream, int len, void *udata)
667 {
668 	(void)chan;
669 	(void)stream;
670 	(void)udata;
671 
672 	if (!music || I_SongType() == MU_GME || I_SongType() == MU_MOD || I_SongType() == MU_MID)
673 		return;
674 	music_bytes += len;
675 }
676 
music_loop(void)677 static void music_loop(void)
678 {
679 	if (is_looping)
680 	{
681 		Mix_PlayMusic(music, 0);
682 		Mix_SetMusicPosition(loop_point);
683 		music_bytes = (UINT32)(loop_point*44100.0L*4); //assume 44.1khz, 4-byte length (see I_GetSongPosition)
684 	}
685 	else
686 	{
687 		// HACK: Let fade timing proceed beyond the end of a
688 		// non-looping song. This is a specific case where the timing
689 		// should persist after stopping a song, so I don't believe
690 		// this should apply every time the user stops a song.
691 		// This is auto-unset in var_cleanup, called by I_StopSong
692 		fading_nocleanup = true;
693 		I_StopSong();
694 	}
695 }
696 
music_fade(UINT32 interval,void * param)697 static UINT32 music_fade(UINT32 interval, void *param)
698 {
699 	(void)param;
700 
701 	if (!is_fading ||
702 		internal_volume == fading_target ||
703 		fading_duration == 0)
704 	{
705 		I_StopFadingSong();
706 		do_fading_callback();
707 		return 0;
708 	}
709 	else if (songpaused) // don't decrement timer
710 		return interval;
711 	else if ((fading_timer -= 10) <= 0)
712 	{
713 		internal_volume = fading_target;
714 		Mix_VolumeMusic(get_real_volume(music_volume));
715 		I_StopFadingSong();
716 		do_fading_callback();
717 		return 0;
718 	}
719 	else
720 	{
721 		UINT8 delta = abs(fading_target - fading_source);
722 		fixed_t factor = FixedDiv(fading_duration - fading_timer, fading_duration);
723 		if (fading_target < fading_source)
724 			internal_volume = max(min(internal_volume, fading_source - FixedMul(delta, factor)), fading_target);
725 		else if (fading_target > fading_source)
726 			internal_volume = min(max(internal_volume, fading_source + FixedMul(delta, factor)), fading_target);
727 		Mix_VolumeMusic(get_real_volume(music_volume));
728 		return interval;
729 	}
730 }
731 
732 #ifdef HAVE_LIBGME
mix_gme(void * udata,Uint8 * stream,int len)733 static void mix_gme(void *udata, Uint8 *stream, int len)
734 {
735 	int i;
736 	short *p;
737 
738 	(void)udata;
739 
740 	// no gme? no music.
741 	if (!gme || gme_track_ended(gme) || songpaused)
742 		return;
743 
744 	// play gme into stream
745 	gme_play(gme, len/2, (short *)stream);
746 
747 	// Limiter to prevent music from being disorted with some formats
748 	if (music_volume >= 18)
749 		music_volume = 18;
750 
751 	// apply volume to stream
752 	for (i = 0, p = (short *)stream; i < len/2; i++, p++)
753 		*p = ((INT32)*p) * (music_volume*internal_volume/100)*2 / 40;
754 }
755 #endif
756 
757 #ifdef HAVE_OPENMPT
mix_openmpt(void * udata,Uint8 * stream,int len)758 static void mix_openmpt(void *udata, Uint8 *stream, int len)
759 {
760 	int i;
761 	short *p;
762 
763 	(void)udata;
764 
765 	if (!openmpt_mhandle || songpaused)
766 		return;
767 
768 	// Play module into stream
769 	openmpt_module_read_interleaved_stereo(openmpt_mhandle, SAMPLERATE, BUFFERSIZE, (short *)stream);
770 
771 	// Limiter to prevent music from being disorted with some formats
772 	if (music_volume >= 18)
773 		music_volume = 18;
774 
775 	// apply volume to stream
776 	for (i = 0, p = (short *)stream; i < len/2; i++, p++)
777 		*p = ((INT32)*p) * (music_volume*internal_volume/100)*2 / 40;
778 }
779 #endif
780 
781 /// ------------------------
782 /// Music System
783 /// ------------------------
784 
I_InitMusic(void)785 void I_InitMusic(void)
786 {
787 }
788 
I_ShutdownMusic(void)789 void I_ShutdownMusic(void)
790 {
791 	I_UnloadSong();
792 }
793 
794 /// ------------------------
795 /// Music Properties
796 /// ------------------------
797 
I_SongType(void)798 musictype_t I_SongType(void)
799 {
800 #ifdef HAVE_LIBGME
801 	if (gme)
802 		return MU_GME;
803 	else
804 #endif
805 #ifdef HAVE_OPENMPT
806 	if (openmpt_mhandle)
807 		return MU_MOD_EX;
808 #endif
809 	if (!music)
810 		return MU_NONE;
811 	else if (Mix_GetMusicType(music) == MUS_MID)
812 	{
813 #ifdef HAVE_MIXERX
814 		if (Mix_GetMidiPlayer() != MIDI_Native)
815 			return MU_MID_EX;
816 		else
817 #endif
818 		return MU_MID;
819 	}
820 	else if (Mix_GetMusicType(music) == MUS_MOD || Mix_GetMusicType(music) == MUS_MODPLUG)
821 		return MU_MOD;
822 	else if (Mix_GetMusicType(music) == MUS_MP3 || Mix_GetMusicType(music) == MUS_MP3_MAD)
823 		return MU_MP3;
824 	else
825 		return (musictype_t)Mix_GetMusicType(music);
826 }
827 
I_SongPlaying(void)828 boolean I_SongPlaying(void)
829 {
830 	return (
831 #ifdef HAVE_LIBGME
832 		(I_SongType() == MU_GME && gme) ||
833 #endif
834 #ifdef HAVE_OPENMPT
835 		(I_SongType() == MU_MOD_EX && openmpt_mhandle) ||
836 #endif
837 		music != NULL
838 	);
839 }
840 
I_SongPaused(void)841 boolean I_SongPaused(void)
842 {
843 	return songpaused;
844 }
845 
846 /// ------------------------
847 /// Music Effects
848 /// ------------------------
849 
I_SetSongSpeed(float speed)850 boolean I_SetSongSpeed(float speed)
851 {
852 	if (speed > 250.0f)
853 		speed = 250.0f; //limit speed up to 250x
854 #ifdef HAVE_LIBGME
855 	if (gme)
856 	{
857 		SDL_LockAudio();
858 		gme_set_tempo(gme, speed);
859 		SDL_UnlockAudio();
860 		return true;
861 	}
862 	else
863 #endif
864 #ifdef HAVE_OPENMPT
865 	if (openmpt_mhandle)
866 	{
867 		if (speed > 4.0f)
868 			speed = 4.0f; // Limit this to 4x to prevent crashing, stupid fix but... ~SteelT 27/9/19
869 #if OPENMPT_API_VERSION_MAJOR < 1 && OPENMPT_API_VERSION_MINOR < 5
870 		{
871 			// deprecated in 0.5.0
872 			char modspd[13];
873 			sprintf(modspd, "%g", speed);
874 			openmpt_module_ctl_set(openmpt_mhandle, "play.tempo_factor", modspd);
875 		}
876 #else
877 		openmpt_module_ctl_set_floatingpoint(openmpt_mhandle, "play.tempo_factor", (double)speed);
878 #endif
879 		return true;
880 	}
881 #else
882 	(void)speed;
883 	return false;
884 #endif
885 	return false;
886 }
887 
888 /// ------------------------
889 ///  MUSIC SEEKING
890 /// ------------------------
891 
I_GetSongLength(void)892 UINT32 I_GetSongLength(void)
893 {
894 	INT32 length;
895 
896 #ifdef HAVE_LIBGME
897 	if (gme)
898 	{
899 		gme_info_t *info;
900 		gme_err_t gme_e = gme_track_info(gme, &info, current_track);
901 
902 		if (gme_e != NULL)
903 		{
904 			CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
905 			length = 0;
906 		}
907 		else
908 		{
909 			// reconstruct info->play_length, from GME source
910 			// we only want intro + 1 loop, not 2
911 			length = info->length;
912 			if (length <= 0)
913 			{
914 				length = info->intro_length + info->loop_length; // intro + 1 loop
915 				if (length <= 0)
916 					length = 150 * 1000; // 2.5 minutes
917 			}
918 		}
919 
920 		gme_free_info(info);
921 		return max(length, 0);
922 	}
923 	else
924 #endif
925 #ifdef HAVE_OPENMPT
926 	if (openmpt_mhandle)
927 		return (UINT32)(openmpt_module_get_duration_seconds(openmpt_mhandle) * 1000.);
928 	else
929 #endif
930 	if (!music || I_SongType() == MU_MOD || I_SongType() == MU_MID)
931 		return 0;
932 	else
933 	{
934 #ifdef HAVE_MIXERX
935 		double xlength = Mix_GetMusicTotalTime(music);
936 		if (xlength >= 0)
937 			return (UINT32)(xlength*1000);
938 #endif
939 		// VERY IMPORTANT to set your LENGTHMS= in your song files, folks!
940 		// SDL mixer can't read music length itself.
941 		length = (UINT32)(song_length*1000);
942 		if (!length)
943 			CONS_Debug(DBG_DETAILED, "Getting music length: music is missing LENGTHMS= tag. Needed for seeking.\n");
944 		return length;
945 	}
946 }
947 
I_SetSongLoopPoint(UINT32 looppoint)948 boolean I_SetSongLoopPoint(UINT32 looppoint)
949 {
950 	if (!music || I_SongType() == MU_GME || I_SongType() == MU_MOD || I_SongType() == MU_MID || !is_looping)
951 		return false;
952 	else
953 	{
954 		UINT32 length = I_GetSongLength();
955 
956 		if (length > 0)
957 			looppoint %= length;
958 
959 		loop_point = max((float)(looppoint / 1000.0L), 0);
960 		return true;
961 	}
962 }
963 
I_GetSongLoopPoint(void)964 UINT32 I_GetSongLoopPoint(void)
965 {
966 #ifdef HAVE_LIBGME
967 	if (gme)
968 	{
969 		INT32 looppoint;
970 		gme_info_t *info;
971 		gme_err_t gme_e = gme_track_info(gme, &info, current_track);
972 
973 		if (gme_e != NULL)
974 		{
975 			CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
976 			looppoint = 0;
977 		}
978 		else
979 			looppoint = info->intro_length > 0 ? info->intro_length : 0;
980 
981 		gme_free_info(info);
982 		return max(looppoint, 0);
983 	}
984 	else
985 #endif
986 	if (!music || I_SongType() == MU_MOD || I_SongType() == MU_MID)
987 		return 0;
988 	else
989 		return (UINT32)(loop_point * 1000);
990 }
991 
I_SetSongPosition(UINT32 position)992 boolean I_SetSongPosition(UINT32 position)
993 {
994 	UINT32 length;
995 #ifdef HAVE_LIBGME
996 	if (gme)
997 	{
998 		// this is unstable, so fail silently
999 		return true;
1000 		// this isn't required technically, but GME thread-locks for a second
1001 		// if you seek too high from the counter
1002 		// length = I_GetSongLength();
1003 		// if (length)
1004 		// 	position = get_adjusted_position(position);
1005 
1006 		// SDL_LockAudio();
1007 		// gme_err_t gme_e = gme_seek(gme, position);
1008 		// SDL_UnlockAudio();
1009 
1010 		// if (gme_e != NULL)
1011 		// {
1012 		// 	CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
1013 		// 	return false;
1014 		// }
1015 		// else
1016 		// 	return true;
1017 	}
1018 	else
1019 #endif
1020 #ifdef HAVE_OPENMPT
1021 	if (openmpt_mhandle)
1022 	{
1023 		// This isn't 100% correct because we don't account for loop points because we can't get them.
1024 		// But if you seek past end of song, OpenMPT seeks to 0. So adjust the position anyway.
1025 		openmpt_module_set_position_seconds(openmpt_mhandle, (double)(get_adjusted_position(position)/1000.0L)); // returns new position
1026 		return true;
1027 	}
1028 	else
1029 #endif
1030 	if (!music || I_SongType() == MU_MID)
1031 		return false;
1032 	else if (I_SongType() == MU_MOD)
1033 		return Mix_SetMusicPosition(position); // Goes by channels
1034 	else
1035 	{
1036 		// Because SDL mixer can't identify song length, if you have
1037 		// a position input greater than the real length, then
1038 		// music_bytes becomes inaccurate.
1039 
1040 		length = I_GetSongLength(); // get it in MS
1041 		if (length)
1042 			position = get_adjusted_position(position);
1043 
1044 		Mix_RewindMusic(); // needed for mp3
1045 		if(Mix_SetMusicPosition((float)(position/1000.0L)) == 0)
1046 			music_bytes = (UINT32)(position/1000.0L*44100.0L*4); //assume 44.1khz, 4-byte length (see I_GetSongPosition)
1047 		else
1048 			// NOTE: This block fires on incorrect song format,
1049 			// NOT if position input is greater than song length.
1050 			music_bytes = 0;
1051 
1052 		return true;
1053 	}
1054 }
1055 
I_GetSongPosition(void)1056 UINT32 I_GetSongPosition(void)
1057 {
1058 #ifdef HAVE_LIBGME
1059 	if (gme)
1060 	{
1061 		INT32 position = gme_tell(gme);
1062 
1063 		gme_info_t *info;
1064 		gme_err_t gme_e = gme_track_info(gme, &info, current_track);
1065 
1066 		if (gme_e != NULL)
1067 		{
1068 			CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
1069 			return position;
1070 		}
1071 		else
1072 		{
1073 			// adjust position, since GME's counter keeps going past loop
1074 			if (info->length > 0)
1075 				position %= info->length;
1076 			else if (info->intro_length + info->loop_length > 0)
1077 				position = position >= (info->intro_length + info->loop_length) ? (position % info->loop_length) : position;
1078 			else
1079 				position %= 150 * 1000; // 2.5 minutes
1080 		}
1081 
1082 		gme_free_info(info);
1083 		return max(position, 0);
1084 	}
1085 	else
1086 #endif
1087 #ifdef HAVE_OPENMPT
1088 	if (openmpt_mhandle)
1089 		// This will be incorrect if we adjust for length because we can't get loop points.
1090 		// So return unadjusted. See note in SetMusicPosition: we adjust for that.
1091 		return (UINT32)(openmpt_module_get_position_seconds(openmpt_mhandle)*1000.);
1092 		//return get_adjusted_position((UINT32)(openmpt_module_get_position_seconds(openmpt_mhandle)*1000.));
1093 	else
1094 #endif
1095 	if (!music || I_SongType() == MU_MID)
1096 		return 0;
1097 	else
1098 	{
1099 #ifdef HAVE_MIXERX
1100 		double xposition = Mix_GetMusicPosition(music);
1101 		if (xposition >= 0)
1102 			return (UINT32)(xposition*1000);
1103 #endif
1104 		return (UINT32)(music_bytes/44100.0L*1000.0L/4); //assume 44.1khz
1105 		// 4 = byte length for 16-bit samples (AUDIO_S16SYS), stereo (2-channel)
1106 		// This is hardcoded in I_StartupSound. Other formats for factor:
1107 		// 8M: 1 | 8S: 2 | 16M: 2 | 16S: 4
1108 	}
1109 }
1110 
1111 /// ------------------------
1112 /// Music Playback
1113 /// ------------------------
1114 
I_LoadSong(char * data,size_t len)1115 boolean I_LoadSong(char *data, size_t len)
1116 {
1117 	const char *key1 = "LOOP";
1118 	const char *key2 = "POINT=";
1119 	const char *key3 = "MS=";
1120 	const size_t key1len = strlen(key1);
1121 	const size_t key2len = strlen(key2);
1122 	const size_t key3len = strlen(key3);
1123 	char *p = data;
1124 	SDL_RWops *rw;
1125 
1126 	if (music
1127 #ifdef HAVE_LIBGME
1128 		|| gme
1129 #endif
1130 #ifdef HAVE_OPENMPT
1131 		|| openmpt_mhandle
1132 #endif
1133 	)
1134 		I_UnloadSong();
1135 
1136 	// always do this whether or not a music already exists
1137 	var_cleanup();
1138 
1139 #ifdef HAVE_LIBGME
1140 	if ((UINT8)data[0] == 0x1F
1141 		&& (UINT8)data[1] == 0x8B)
1142 	{
1143 #ifdef HAVE_ZLIB
1144 		UINT8 *inflatedData;
1145 		size_t inflatedLen;
1146 		z_stream stream;
1147 		int zErr; // Somewhere to handle any error messages zlib tosses out
1148 
1149 		memset(&stream, 0x00, sizeof (z_stream)); // Init zlib stream
1150 		// Begin the inflation process
1151 		inflatedLen = *(UINT32 *)(data + (len-4)); // Last 4 bytes are the decompressed size, typically
1152 		inflatedData = (UINT8 *)Z_Calloc(inflatedLen, PU_MUSIC, NULL); // Make room for the decompressed data
1153 		stream.total_in = stream.avail_in = len;
1154 		stream.total_out = stream.avail_out = inflatedLen;
1155 		stream.next_in = (UINT8 *)data;
1156 		stream.next_out = inflatedData;
1157 
1158 		zErr = inflateInit2(&stream, 32 + MAX_WBITS);
1159 		if (zErr == Z_OK) // We're good to go
1160 		{
1161 			zErr = inflate(&stream, Z_FINISH);
1162 			if (zErr == Z_STREAM_END)
1163 			{
1164 				// Run GME on new data
1165 				if (!gme_open_data(inflatedData, inflatedLen, &gme, SAMPLERATE))
1166 				{
1167 					Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around
1168 					return true;
1169 				}
1170 			}
1171 			else
1172 				CONS_Alert(CONS_ERROR, "Encountered %s when running inflate: %s\n", get_zlib_error(zErr), stream.msg);
1173 			(void)inflateEnd(&stream);
1174 		}
1175 		else // Hold up, zlib's got a problem
1176 			CONS_Alert(CONS_ERROR, "Encountered %s when running inflateInit: %s\n", get_zlib_error(zErr), stream.msg);
1177 		Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up
1178 		return false;
1179 #else
1180 		CONS_Alert(CONS_ERROR, "Cannot decompress VGZ; no zlib support\n");
1181 		return false;
1182 #endif
1183 	}
1184 	else if (!gme_open_data(data, len, &gme, SAMPLERATE))
1185 		return true;
1186 #endif
1187 
1188 #ifdef HAVE_MIXERX
1189 	if (Mix_GetMidiPlayer() != cv_midiplayer.value)
1190 		Mix_SetMidiPlayer(cv_midiplayer.value);
1191 	if (stricmp(Mix_GetSoundFonts(), cv_midisoundfontpath.string))
1192 		Mix_SetSoundFonts(cv_midisoundfontpath.string);
1193 	Mix_Timidity_addToPathList(cv_miditimiditypath.string); // this overwrites previous custom path
1194 #endif
1195 
1196 #ifdef HAVE_OPENMPT
1197 	/*
1198 		If the size of the data to be checked is bigger than the recommended size (> 2048 bytes)
1199 		Let's just set the probe size to the recommended size
1200 		Otherwise let's give it the full data size
1201 	*/
1202 
1203 	if (len > openmpt_probe_file_header_get_recommended_size())
1204 		probesize = openmpt_probe_file_header_get_recommended_size();
1205 	else
1206 		probesize = len;
1207 
1208 	result = openmpt_probe_file_header(OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, data, probesize, len, NULL, NULL, NULL, NULL, NULL, NULL);
1209 
1210 	if (result == OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS) // We only cared if it succeeded, continue on if not.
1211 	{
1212 		openmpt_mhandle = openmpt_module_create_from_memory2(data, len, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1213 		if (!openmpt_mhandle) // Failed to create module handle? Show error and return!
1214 		{
1215 			mod_err = openmpt_module_error_get_last(openmpt_mhandle);
1216 			mod_err_str = openmpt_error_string(mod_err);
1217 			CONS_Alert(CONS_ERROR, "openmpt_module_create_from_memory2: %s\n", mod_err_str);
1218 			return false;
1219 		}
1220 		else
1221 			return true; // All good and we're ready for music playback!
1222 	}
1223 #endif
1224 
1225 	// Let's see if Mixer is able to load this.
1226 	rw = SDL_RWFromMem(data, len);
1227 	{
1228 		music = Mix_LoadMUS_RW(rw, 1);
1229 	}
1230 	if (!music)
1231 	{
1232 		CONS_Alert(CONS_ERROR, "Mix_LoadMUS_RW: %s\n", Mix_GetError());
1233 		return false;
1234 	}
1235 
1236 	// Find the OGG loop point.
1237 	loop_point = 0.0f;
1238 	song_length = 0.0f;
1239 
1240 	while ((UINT32)(p - data) < len)
1241 	{
1242 		if (fpclassify(loop_point) == FP_ZERO && !strncmp(p, key1, key1len))
1243 		{
1244 			p += key1len; // skip LOOP
1245 			if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=?
1246 			{
1247 				p += key2len; // skip POINT=
1248 				loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count.
1249 				// because SDL_Mixer is USELESS and can't even tell us
1250 				// something simple like the frequency of the streaming music,
1251 				// we are unfortunately forced to assume that ALL MUSIC is 44100hz.
1252 				// This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly.
1253 			}
1254 			else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
1255 			{
1256 				p += key3len; // skip MS=
1257 				loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds.
1258 				// Everything that uses LOOPMS will work perfectly with SDL_Mixer.
1259 			}
1260 		}
1261 
1262 		if (fpclassify(loop_point) != FP_ZERO) // Got what we needed
1263 			break;
1264 		else // continue searching
1265 			p++;
1266 	}
1267 	return true;
1268 }
1269 
I_UnloadSong(void)1270 void I_UnloadSong(void)
1271 {
1272 	I_StopSong();
1273 
1274 #ifdef HAVE_LIBGME
1275 	if (gme)
1276 	{
1277 		gme_delete(gme);
1278 		gme = NULL;
1279 	}
1280 #endif
1281 #ifdef HAVE_OPENMPT
1282 	if (openmpt_mhandle)
1283 	{
1284 		openmpt_module_destroy(openmpt_mhandle);
1285 		openmpt_mhandle = NULL;
1286 	}
1287 #endif
1288 	if (music)
1289 	{
1290 		Mix_FreeMusic(music);
1291 		music = NULL;
1292 	}
1293 }
1294 
I_PlaySong(boolean looping)1295 boolean I_PlaySong(boolean looping)
1296 {
1297 #ifdef HAVE_LIBGME
1298 	if (gme)
1299 	{
1300 		gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0};
1301 #if defined (GME_VERSION) && GME_VERSION >= 0x000603
1302 		if (looping)
1303 			gme_set_autoload_playback_limit(gme, 0);
1304 #endif
1305 		gme_set_equalizer(gme, &eq);
1306 		gme_start_track(gme, 0);
1307 		current_track = 0;
1308 		Mix_HookMusic(mix_gme, gme);
1309 		return true;
1310 	}
1311 	else
1312 #endif
1313 #ifdef HAVE_OPENMPT
1314 	if (openmpt_mhandle)
1315 	{
1316 		openmpt_module_select_subsong(openmpt_mhandle, 0);
1317 		openmpt_module_set_render_param(openmpt_mhandle, OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH, cv_modfilter.value);
1318 		if (looping)
1319 			openmpt_module_set_repeat_count(openmpt_mhandle, -1); // Always repeat
1320 		current_subsong = 0;
1321 		Mix_HookMusic(mix_openmpt, openmpt_mhandle);
1322 		return true;
1323 	}
1324 	else
1325 #endif
1326 	if (!music)
1327 		return false;
1328 
1329 	if (fpclassify(song_length) == FP_ZERO && (I_SongType() == MU_OGG || I_SongType() == MU_MP3 || I_SongType() == MU_FLAC))
1330 		CONS_Debug(DBG_DETAILED, "This song is missing a LENGTHMS= tag! Required to make seeking work properly.\n");
1331 
1332 	if (I_SongType() != MU_MOD && I_SongType() != MU_MID && Mix_PlayMusic(music, 0) == -1)
1333 	{
1334 		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
1335 		return false;
1336 	}
1337 	else if ((I_SongType() == MU_MOD || I_SongType() == MU_MID || I_SongType() == MU_MID_EX) && Mix_PlayMusic(music, looping ? -1 : 0) == -1) // if MOD, loop forever
1338 	{
1339 		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
1340 		return false;
1341 	}
1342 
1343 	is_looping = looping;
1344 
1345 	I_SetMusicVolume(music_volume);
1346 
1347 	if (I_SongType() != MU_MOD && I_SongType() != MU_MID && I_SongType() != MU_MID_EX)
1348 		Mix_HookMusicFinished(music_loop); // don't bother counting if MOD
1349 
1350 	if(I_SongType() != MU_MOD && I_SongType() != MU_MID && I_SongType() != MU_MID_EX && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
1351 		CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError());
1352 
1353 	return true;
1354 }
1355 
I_StopSong(void)1356 void I_StopSong(void)
1357 {
1358 	// HACK: See music_loop on why we want fade timing to proceed
1359 	// after end of song
1360 	if (!fading_nocleanup)
1361 		I_StopFadingSong();
1362 
1363 #ifdef HAVE_LIBGME
1364 	if (gme)
1365 	{
1366 		Mix_HookMusic(NULL, NULL);
1367 		current_track = -1;
1368 	}
1369 #endif
1370 #ifdef HAVE_OPENMPT
1371 	if (openmpt_mhandle)
1372 	{
1373 		Mix_HookMusic(NULL, NULL);
1374 		current_subsong = -1;
1375 	}
1376 #endif
1377 	if (music)
1378 	{
1379 		Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
1380 		Mix_HookMusicFinished(NULL);
1381 		Mix_HaltMusic();
1382 	}
1383 
1384 	var_cleanup();
1385 }
1386 
I_PauseSong(void)1387 void I_PauseSong(void)
1388 {
1389 	if(I_SongType() == MU_MID) // really, SDL Mixer? why can't you pause MIDI???
1390 		return;
1391 
1392 	if(I_SongType() != MU_GME && I_SongType() != MU_MOD && I_SongType() != MU_MID)
1393 		Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
1394 
1395 	Mix_PauseMusic();
1396 	songpaused = true;
1397 }
1398 
I_ResumeSong(void)1399 void I_ResumeSong(void)
1400 {
1401 	if (I_SongType() == MU_MID)
1402 		return;
1403 
1404 	if (I_SongType() != MU_GME && I_SongType() != MU_MOD && I_SongType() != MU_MID)
1405 	{
1406 		while(Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes) != 0) { }
1407 			// HACK: fixes issue of multiple effect callbacks being registered
1408 
1409 		if(music && I_SongType() != MU_MOD && I_SongType() != MU_MID && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
1410 			CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError());
1411 	}
1412 
1413 	Mix_ResumeMusic();
1414 	songpaused = false;
1415 }
1416 
I_SetMusicVolume(UINT8 volume)1417 void I_SetMusicVolume(UINT8 volume)
1418 {
1419 	if (!I_SongPlaying())
1420 		return;
1421 
1422 #ifdef _WIN32
1423 	if (I_SongType() == MU_MID)
1424 		// HACK: Until we stop using native MIDI,
1425 		// disable volume changes
1426 		music_volume = 31;
1427 	else
1428 #endif
1429 		music_volume = volume;
1430 
1431 	Mix_VolumeMusic(get_real_volume(music_volume));
1432 }
1433 
I_SetSongTrack(int track)1434 boolean I_SetSongTrack(int track)
1435 {
1436 #ifdef HAVE_LIBGME
1437 	// If the specified track is within the number of tracks playing, then change it
1438 	if (gme)
1439 	{
1440 		if (current_track == track)
1441 			return false;
1442 		SDL_LockAudio();
1443 		if (track >= 0 && track < gme_track_count(gme)-1)
1444 		{
1445 			gme_err_t gme_e = gme_start_track(gme, track);
1446 			if (gme_e != NULL)
1447 			{
1448 				CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
1449 				return false;
1450 			}
1451 			current_track = track;
1452 			SDL_UnlockAudio();
1453 			return true;
1454 		}
1455 		SDL_UnlockAudio();
1456 		return false;
1457 	}
1458 	else
1459 #endif
1460 #ifdef HAVE_OPENMPT
1461 	if (openmpt_mhandle)
1462 	{
1463 		if (current_subsong == track)
1464 			return false;
1465 		SDL_LockAudio();
1466 		if (track >= 0 && track < openmpt_module_get_num_subsongs(openmpt_mhandle))
1467 		{
1468 			openmpt_module_select_subsong(openmpt_mhandle, track);
1469 			current_subsong = track;
1470 			SDL_UnlockAudio();
1471 			return true;
1472 		}
1473 		SDL_UnlockAudio();
1474 
1475 		return false;
1476 	}
1477 #endif
1478 	if (I_SongType() == MU_MOD)
1479 		return !Mix_SetMusicPosition(track);
1480 	(void)track;
1481 	return false;
1482 }
1483 
1484 /// ------------------------
1485 /// MUSIC FADING
1486 /// ------------------------
1487 
I_SetInternalMusicVolume(UINT8 volume)1488 void I_SetInternalMusicVolume(UINT8 volume)
1489 {
1490 	internal_volume = volume;
1491 	if (!I_SongPlaying())
1492 		return;
1493 	Mix_VolumeMusic(get_real_volume(music_volume));
1494 }
1495 
I_StopFadingSong(void)1496 void I_StopFadingSong(void)
1497 {
1498 	if (fading_id)
1499 		SDL_RemoveTimer(fading_id);
1500 	is_fading = false;
1501 	fading_source = fading_target = fading_timer = fading_duration = fading_id = 0;
1502 	// don't unset fading_nocleanup here just yet; fading_callback is cleaned up
1503 	// in var_cleanup()
1504 }
1505 
I_FadeSongFromVolume(UINT8 target_volume,UINT8 source_volume,UINT32 ms,void (* callback)(void))1506 boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void))
1507 {
1508 	INT16 volume_delta;
1509 
1510 	source_volume = min(source_volume, 100);
1511 	volume_delta = (INT16)(target_volume - source_volume);
1512 
1513 	I_StopFadingSong();
1514 
1515 	if (!ms && volume_delta)
1516 	{
1517 		I_SetInternalMusicVolume(target_volume);
1518 		if (callback)
1519 			(*callback)();
1520 		return true;
1521 
1522 	}
1523 	else if (!volume_delta)
1524 	{
1525 		if (callback)
1526 			(*callback)();
1527 		return true;
1528 	}
1529 
1530 	// Round MS to nearest 10
1531 	// If n - lower > higher - n, then round up
1532 	ms = (ms - ((ms / 10) * 10) > (((ms / 10) * 10) + 10) - ms) ?
1533 		(((ms / 10) * 10) + 10) // higher
1534 		: ((ms / 10) * 10); // lower
1535 
1536 	if (!ms)
1537 		I_SetInternalMusicVolume(target_volume);
1538 	else if (source_volume != target_volume)
1539 	{
1540 		fading_id = SDL_AddTimer(10, music_fade, NULL);
1541 		if (fading_id)
1542 		{
1543 			is_fading = true;
1544 			fading_timer = fading_duration = ms;
1545 			fading_source = source_volume;
1546 			fading_target = target_volume;
1547 			fading_callback = callback;
1548 
1549 			if (internal_volume != source_volume)
1550 				I_SetInternalMusicVolume(source_volume);
1551 		}
1552 	}
1553 
1554 	return is_fading;
1555 }
1556 
I_FadeSong(UINT8 target_volume,UINT32 ms,void (* callback)(void))1557 boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void))
1558 {
1559 	return I_FadeSongFromVolume(target_volume, internal_volume, ms, callback);
1560 }
1561 
I_FadeOutStopSong(UINT32 ms)1562 boolean I_FadeOutStopSong(UINT32 ms)
1563 {
1564 	return I_FadeSongFromVolume(0, internal_volume, ms, &I_StopSong);
1565 }
1566 
I_FadeInPlaySong(UINT32 ms,boolean looping)1567 boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
1568 {
1569 	if (I_PlaySong(looping))
1570 		return I_FadeSongFromVolume(100, 0, ms, NULL);
1571 	else
1572 		return false;
1573 }
1574 #endif
1575