From 2cf49f9361ca1bae3a9c36369844ba25801fa060 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sun, 19 Aug 2018 22:05:23 -0400
Subject: [PATCH] I_FadeOutStopMusic, I_FadeInStartDigSong,
 S_ChangeMusicWithFade implementation

* First attempt, a little messy but works fine
---
 src/i_sound.h         |   7 ++-
 src/s_sound.c         |  51 ++++++++++++++++---
 src/s_sound.h         |   7 ++-
 src/sdl/mixer_sound.c | 115 ++++++++++++++++++++++++++++++++++++++----
 4 files changed, 162 insertions(+), 18 deletions(-)

diff --git a/src/i_sound.h b/src/i_sound.h
index 94548456c5..3c8a2ca62b 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -251,10 +251,15 @@ void I_SetInternalMusicVolume(UINT8 volume);
 
 void I_StopFadingMusic(void);
 
-boolean I_FadeMusicFromLevel(UINT8 target_volume, UINT16 source_volume, UINT32 ms);
+boolean I_FadeMusicFromLevel(UINT8 target_volume, UINT8 source_volume, UINT32 ms, boolean stopafterfade);
 
 boolean I_FadeMusic(UINT8 target_volume, UINT32 ms);
 
+boolean I_FadeOutStopMusic(UINT32 ms);
+
+boolean I_FadeInStartDigSong(const char *musicname, UINT16 track, boolean looping, UINT32 position, UINT32 fadeinms, boolean queuepostfade);
+#define I_QueueDigSongPostFade(a,b,c,d,e) I_FadeInStartDigSong(a,b,c,d,e,1)
+
 /**	\brief The I_StartDigSong function
 
 	\param	musicname	music lump name
diff --git a/src/s_sound.c b/src/s_sound.c
index 46ceb549d1..9e96480e1a 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1379,7 +1379,7 @@ static boolean S_DigMusic(const char *mname, boolean looping)
 	return true;
 }
 
-void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping)
+void S_ChangeMusicWithFade(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms)
 {
 	if ((nomidimusic || music_disabled) && (nodigimusic || digital_disabled))
 		return;
@@ -1402,13 +1402,47 @@ void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping)
 
 	if (strncmp(music_name, newmusic, 6))
 	{
-		S_StopMusic(); // shutdown old music
-		if (!S_DigMusic(newmusic, looping) && !S_MIDIMusic(newmusic, looping))
+		if (S_MusicExists(newmusic, false, true) && !nodigimusic && !digital_disabled) // digmusic?
 		{
-			CONS_Alert(CONS_ERROR, M_GetText("Music lump %.6s not found!\n"), newmusic);
-			return;
+			if (prefadems) //have to queue post-fade
+			{
+				I_FadeOutStopMusic(prefadems);
+				I_QueueDigSongPostFade(newmusic, mflags & MUSIC_TRACKMASK, looping, position, fadeinms);
+
+				// HACK: set the vars now and hope everything works out
+				strncpy(music_name, newmusic, 7);
+				music_name[6] = 0;
+				music_lumpnum = LUMPERROR;
+				music_data = NULL;
+				music_handle = 0;
+				return;
+			}
+			else
+			{
+				S_StopMusic();
+				if (!S_DigMusic(newmusic, looping))
+				{
+					CONS_Alert(CONS_ERROR, M_GetText("Music lump %.6s not found!\n"), newmusic);
+					return;
+				}
+			}
+		}
+		else if (S_MusicExists(newmusic, true, false) && !nomidimusic && !music_disabled) // midimusic?
+		{
+			// HACK: We don't support fade for MIDI right now, so
+			// just fall to old behavior verbatim. This technically should be implemented in
+			// the interfaces, even as a stub.
+
+			S_StopMusic();
+
+			if (!S_MIDIMusic(newmusic, looping))
+			{
+				CONS_Alert(CONS_ERROR, M_GetText("Music lump %.6s not found!\n"), newmusic);
+				return;
+			}
 		}
 	}
+
 	I_SetSongTrack(mflags & MUSIC_TRACKMASK);
 }
 
@@ -1491,7 +1525,12 @@ boolean S_FadeMusicFromLevel(UINT8 target_volume, INT16 source_volume, UINT32 ms
 	if (source_volume < 0)
 		return I_FadeMusic(target_volume, ms);
 	else
-		return I_FadeMusicFromLevel(target_volume, source_volume, ms);
+		return I_FadeMusicFromLevel(target_volume, source_volume, ms, false);
+}
+
+boolean S_FadeOutStopMusic(UINT32 ms)
+{
+	return I_FadeOutStopMusic(ms);
 }
 
 void S_SetDigMusicVolume(INT32 volume)
diff --git a/src/s_sound.h b/src/s_sound.h
index 20a0f81e07..cdcacd14e7 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -130,8 +130,10 @@ void S_StopSound(void *origin);
 // note: music flags 12 bits for tracknum (gme, other formats with more than one track)
 //       13-15 aren't used yet
 //       and the last bit we ignore (internal game flag for resetting music on reload)
-#define S_ChangeMusicInternal(a,b) S_ChangeMusic(a,0,b)
-void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping);
+void S_ChangeMusicWithFade(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms);
+#define S_FadeInChangeMusic(a,b,c,d) S_ChangeMusicWithFade(a,b,c,0,0,d)
+#define S_ChangeMusicInternal(a,b) S_ChangeMusicWithFade(a,0,b,0,0,0)
+#define S_ChangeMusic(a,b,c) S_ChangeMusicWithFade(a,b,c,0,0,0)
 
 // Get music type
 musictype_t S_MusicType();
@@ -176,6 +178,7 @@ boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi);
 void S_StopFadingMusic(void);
 boolean S_FadeMusicFromLevel(UINT8 target_volume, INT16 source_volume, UINT32 ms);
 #define S_FadeMusic(a, b) S_FadeMusicFromLevel(a, -1, b)
+boolean S_FadeOutStopMusic(UINT32 ms);
 
 //
 // Updates music & sounds
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 1b7c810086..71421ea620 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -75,6 +75,12 @@ static UINT8 fading_target;
 static UINT32 fading_steps;
 static INT16 fading_volume_step;
 static INT32 fading_id;
+static char queue_music_name[7]; // up to 6-character name
+static UINT16 queue_track;
+static boolean queue_looping;
+static UINT32 queue_position;
+static UINT32 queue_fadeinms;
+static boolean queue_stopafterfade;
 
 #ifdef HAVE_LIBGME
 static Music_Emu *gme;
@@ -93,6 +99,14 @@ static void varcleanup(void)
 	internal_volume = 100;
 }
 
+static void queuecleanup(void)
+{
+	queue_track = queue_looping =\
+	 queue_position = queue_fadeinms =\
+	 queue_stopafterfade = 0;
+	queue_music_name[0] = 0;
+}
+
 static UINT32 get_real_volume(UINT8 volume)
 {
 	// convert volume to mixer's 128 scale
@@ -115,6 +129,7 @@ void I_StartupSound(void)
 	}
 
 	varcleanup();
+	queuecleanup();
 	music = NULL;
 	music_volume = midi_volume = sfx_volume = 0;
 
@@ -508,14 +523,31 @@ static void music_loop(void)
 		I_StopDigSong();
 }
 
+static void run_queue()
+{
+	if (queue_stopafterfade)
+		I_StopDigSong();
+	else if (queue_music_name[0] && I_StartDigSong(queue_music_name, queue_looping))
+	{
+		I_SetSongTrack(queue_track);
+		if (queue_fadeinms)
+			I_FadeMusicFromLevel(100, 0, queue_fadeinms, false);
+		if (queue_position)
+			I_SetMusicPosition(queue_position);
+	}
+	queuecleanup();
+}
+
 static UINT32 music_fade(UINT32 interval, void *param)
 {
 	if (!is_fading ||
+		midimode || // stub out MIDI, see bug in I_SetMIDIMusicVolume
 		internal_volume == fading_target ||
 		fading_steps == 0 ||
 		fading_volume_step == 0)
 	{
 		I_StopFadingMusic();
+		queuecleanup();
 		return 0;
 	}
 	else if (
@@ -524,6 +556,7 @@ static UINT32 music_fade(UINT32 interval, void *param)
 	{
 		internal_volume = fading_target;
 		Mix_VolumeMusic(get_real_volume(midimode ? midi_volume : music_volume));
+		run_queue();
 		return 0;
 	}
 	else
@@ -644,6 +677,7 @@ void I_ShutdownDigMusic(void)
 	if (!music)
 		return;
 	varcleanup();
+	queuecleanup();
 	SDL_RemoveTimer(fading_id);
 	Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
 	Mix_HookMusicFinished(NULL);
@@ -1176,7 +1210,7 @@ boolean I_SetSongTrack(int track)
 void I_SetInternalMusicVolume(UINT8 volume)
 {
 	internal_volume = volume;
-	if (!music)
+	if (midimode || !music) // stub out MIDI, see bug in I_SetMIDIMusicVolume
 		return;
 	Mix_VolumeMusic(get_real_volume(midimode ? midi_volume : music_volume));
 }
@@ -1189,23 +1223,39 @@ void I_StopFadingMusic(void)
 	fading_target = fading_steps = fading_volume_step = fading_id = 0;
 }
 
-boolean I_FadeMusicFromLevel(UINT8 target_volume, UINT8 source_volume, UINT32 ms)
+boolean I_FadeMusicFromLevel(UINT8 target_volume, UINT8 source_volume, UINT32 ms, boolean stopafterfade)
 {
 	UINT32 target_steps, ms_per_step;
 	INT16 target_volume_step, volume_delta;
 
 	source_volume = min(source_volume, 100);
-	volume_delta = (INT16)(target_volume - source_volume));
+	volume_delta = (INT16)(target_volume - source_volume);
 
 	I_StopFadingMusic();
 
 	if (!ms && volume_delta)
 	{
-		I_SetInternalMusicVolume(target_volume);
-		return true;
+		if (stopafterfade)
+		{
+			I_StopDigSong();
+			return true;
+		}
+		else
+		{
+			I_SetInternalMusicVolume(target_volume);
+			return true;
+		}
 	}
 	else if (!volume_delta)
-		return true;
+	{
+		if (stopafterfade)
+		{
+			I_StopDigSong();
+			return true;
+		}
+		else
+			return true;
+	}
 
 	// Round MS to nearest 10
 	// If n - lower > higher - n, then round up
@@ -1229,6 +1279,7 @@ boolean I_FadeMusicFromLevel(UINT8 target_volume, UINT8 source_volume, UINT32 ms
 			fading_target = target_volume;
 			fading_steps = target_steps;
 			fading_volume_step = target_volume_step;
+			queue_stopafterfade = stopafterfade;
 
 			if (internal_volume != source_volume)
 				I_SetInternalMusicVolume(source_volume);
@@ -1240,7 +1291,45 @@ boolean I_FadeMusicFromLevel(UINT8 target_volume, UINT8 source_volume, UINT32 ms
 
 boolean I_FadeMusic(UINT8 target_volume, UINT32 ms)
 {
-	return I_FadeMusicFromLevel(target_volume, internal_volume, ms);
+	return I_FadeMusicFromLevel(target_volume, internal_volume, ms, false);
+}
+
+boolean I_FadeOutStopMusic(UINT32 ms)
+{
+	return I_FadeMusicFromLevel(0, internal_volume, ms, true);
+}
+
+boolean I_FadeInStartDigSong(const char *musicname, UINT16 track, boolean looping, UINT32 position, UINT32 fadeinms, boolean queuepostfade)
+{
+	if (musicname[0] == 0)
+		return true; // nothing to play
+	else if (queuepostfade && is_fading)
+	{
+		strncpy(queue_music_name, musicname, 7);
+		queue_music_name[6] = 0;
+		queue_track = track;
+		queue_looping = looping;
+		queue_position = position;
+		queue_fadeinms = fadeinms;
+		queue_stopafterfade = false;
+
+		return true;
+	}
+	else
+	{
+		if (I_StartDigSong(musicname, looping))
+		{
+			I_SetSongTrack(track);
+			if (fadeinms)
+				I_FadeMusicFromLevel(100, 0, fadeinms, false);
+			if (position)
+				I_SetMusicPosition(position);
+			return true;
+		}
+		else
+			return false;
+	}
+
 }
 
 //
@@ -1264,10 +1353,18 @@ void I_ShutdownMIDIMusic(void)
 
 void I_SetMIDIMusicVolume(UINT8 volume)
 {
-	midi_volume = volume;
+	// HACK: Until we stop using native MIDI,
+	// disable volume changes
+	// Why: In Windows, MIDI volume messes with the executable's volume setting
+	// in the OS volume mixer. So any EXE sharing that same filename and directory
+	// will be affected by this volume bug.
+
+	(void)volume;
+	midi_volume = 31;
+	//midi_volume = volume;
 	if (!midimode || !music)
 		return;
-	Mix_VolumeMusic(get_real_volume(volume));
+	Mix_VolumeMusic((UINT32)midi_volume*128/31);
 }
 
 INT32 I_RegisterSong(void *data, size_t len)
-- 
GitLab