From c6035f5956ab373675ff8bca3164a7cb8ab18dc2 Mon Sep 17 00:00:00 2001
From: mazmazz <mar.marcoz@outlook.com>
Date: Sat, 18 Aug 2018 14:18:29 -0400
Subject: [PATCH] I_GetMusicLength implementation

---
 src/i_sound.h         |  2 ++
 src/s_sound.c         |  7 ++++-
 src/s_sound.h         |  3 ++
 src/sdl/mixer_sound.c | 69 ++++++++++++++++++++++++++++++++-----------
 4 files changed, 62 insertions(+), 19 deletions(-)

diff --git a/src/i_sound.h b/src/i_sound.h
index ff9a9f196..98cc7523c 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -224,6 +224,8 @@ void I_ShutdownDigMusic(void);
 
 boolean I_SetSongSpeed(float speed);
 
+UINT32 I_GetMusicLength(void);
+
 boolean I_SetMusicPosition(UINT32 position);
 
 UINT32 I_GetMusicPosition(void);
diff --git a/src/s_sound.c b/src/s_sound.c
index bd27e4da2..16d808f63 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1389,7 +1389,7 @@ void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping)
 	if(LUAh_MusicChange(music_name, mmusic, newmusic, &mflags, &looping))
 		return;
 #else
-	strncpy(newmusic, mmusic, 7);	
+	strncpy(newmusic, mmusic, 7);
 #endif
 	newmusic[6] = 0;
 
@@ -1417,6 +1417,11 @@ boolean S_SpeedMusic(float speed)
 	return I_SetSongSpeed(speed);
 }
 
+UINT32 S_GetMusicLength(void)
+{
+	return I_GetMusicLength();
+}
+
 boolean S_SetMusicPosition(UINT32 position)
 {
 	return I_SetMusicPosition(position);
diff --git a/src/s_sound.h b/src/s_sound.h
index bb33233db..94b12ba0b 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -135,6 +135,9 @@ void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping);
 // Set Speed of Music
 boolean S_SpeedMusic(float speed);
 
+// Get Length of Music
+UINT32 S_GetMusicLength(void);
+
 // Set Position of Music
 boolean S_SetMusicPosition(UINT32 position);
 
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 2b3653fe7..9b9887103 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -66,6 +66,7 @@ static boolean midimode;
 static Mix_Music *music;
 static UINT8 music_volume, midi_volume, sfx_volume;
 static float loop_point;
+static float music_length; // length in seconds
 static boolean songpaused;
 static UINT32 music_bytes;
 static boolean is_looping;
@@ -693,36 +694,49 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 	// Find the OGG loop point.
 	is_looping = looping;
 	loop_point = 0.0f;
+	music_length = 0.0f;
 	if (looping)
 	{
 		const char *key1 = "LOOP";
 		const char *key2 = "POINT=";
 		const char *key3 = "MS=";
+		const char *key4 = "LENGTHMS=";
 		const size_t key1len = strlen(key1);
 		const size_t key2len = strlen(key2);
 		const size_t key3len = strlen(key3);
+		const size_t key4len = strlen(key4);
 		char *p = data;
 		while ((UINT32)(p - data) < len)
 		{
-			if (strncmp(p++, key1, key1len))
-				continue;
-			p += key1len-1; // skip OOP (the L was skipped in strncmp)
-			if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=?
+			if (!loop_point && !strncmp(p, key1, key1len))
 			{
-				p += key2len; // skip POINT=
-				loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count.
-				// because SDL_Mixer is USELESS and can't even tell us
-				// something simple like the frequency of the streaming music,
-				// we are unfortunately forced to assume that ALL MUSIC is 44100hz.
-				// This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly.
+				p += key1len; // skip LOOP
+				if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=?
+				{
+					p += key2len; // skip POINT=
+					loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count.
+					// because SDL_Mixer is USELESS and can't even tell us
+					// something simple like the frequency of the streaming music,
+					// we are unfortunately forced to assume that ALL MUSIC is 44100hz.
+					// This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly.
+				}
+				else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
+				{
+					p += key3len; // skip MS=
+					loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds.
+					// Everything that uses LOOPMS will work perfectly with SDL_Mixer.
+				}
 			}
-			else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
+			else if (!music_length && !strncmp(p, key4, key4len)) // is it LENGTHMS=?
 			{
-				p += key3len; // skip MS=
-				loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds.
-				// Everything that uses LOOPMS will work perfectly with SDL_Mixer.
+				p += key4len; // skip LENGTHMS
+				music_length = (float)(atoi(p) / 1000.0L);
 			}
-			// Neither?! Continue searching.
+
+			if (loop_point && music_length) // Got what we needed
+				break;
+			else // continue searching
+				p++;
 		}
 	}
 
@@ -792,18 +806,37 @@ boolean I_SetSongSpeed(float speed)
 	return false;
 }
 
+UINT32 I_GetMusicLength(void)
+{
+	// VERY IMPORTANT to set your LENGTHMS= in your song files, folks!
+	// SDL mixer can't read music length itself.
+
+	if (midimode)
+		return 0;
+	UINT32 length = (UINT32)(music_length*1000);
+	if (!length)
+		CONS_Debug(DBG_BASIC, "Getting music length: music is missing LENGTHMS= in music tag.\n");
+	return length;
+}
+
 boolean I_SetMusicPosition(UINT32 position)
 {
 	if(midimode || !music)
 		return false;
+
+	// Because SDL mixer can't identify song length, if you have
+	// a position input greater than the real length, then
+	// music_bytes becomes inaccurate.
+	UINT32 length = I_GetMusicLength(); // get it in MS
+	if (length)
+		position %= length;
+
 	Mix_RewindMusic(); // needed for mp3
 	if(Mix_SetMusicPosition((float)(position/1000.0L)) == 0)
-		music_bytes = position/1000.0L*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetSongPositon)
+		music_bytes = position/1000.0L*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetSongPosition)
 	else
 		// NOTE: This block fires on incorrect song format,
 		// NOT if position input is greater than song length.
-		// This means music_bytes will be inaccurate because we can't compare to
-		// max song length. So, don't write your scripts to seek beyond the song.
 		music_bytes = 0;
 	return true;
 }