diff --git a/src/android/i_sound.c b/src/android/i_sound.c
index fa9e326c0e687c92e22999084aa2c977465f3621..6f70134b0aba750bfc2682ae2efe41b49d469798 100644
--- a/src/android/i_sound.c
+++ b/src/android/i_sound.c
@@ -74,19 +74,19 @@ void I_ResumeSong(INT32 handle)
         (void)handle;
 }
 
-boolean I_MIDIPlaying(void)
+boolean I_MusicPlaying(void)
 {
 	return false;
 }
 
-boolean I_MusicPlaying(void)
+boolean I_MusicPaused(void)
 {
 	return false;
 }
 
-boolean I_MusicPaused(void)
+musictype_t I_MusicType(void)
 {
-	return false;
+	return MU_NONE;
 }
 
 //
@@ -158,6 +158,22 @@ boolean I_SetSongSpeed(float speed)
         return false;
 }
 
+UINT32 I_GetMusicLength(void)
+{
+        return 0;
+}
+
+boolean I_SetMusicLoopPoint(UINT32 looppoint)
+{
+        (void)looppoint;
+        return false;
+}
+
+UINT32 I_GetMusicLoopPoint(void)
+{
+	return 0;
+}
+
 boolean I_SetMusicPosition(UINT32 position)
 {
         (void)position;
diff --git a/src/djgppdos/i_sound.c b/src/djgppdos/i_sound.c
index 4a1afa3012e9999303a9ec65585a9c184565298d..dbf7a01422661c7e47f164693d3fbf1d3edf8668 100644
--- a/src/djgppdos/i_sound.c
+++ b/src/djgppdos/i_sound.c
@@ -551,6 +551,22 @@ boolean I_SetSongSpeed(float speed)
 	return false;
 }
 
+UINT32 I_GetMusicLength(void)
+{
+	return 0;
+}
+
+boolean I_SetMusicLoopPoint(UINT32 looppoint)
+{
+        (void)looppoint;
+        return false;
+}
+
+UINT32 I_GetMusicLoopPoint(void)
+{
+	return 0;
+}
+
 boolean I_SetMusicPosition(UINT32 position)
 {
     (void)position;
@@ -559,12 +575,7 @@ boolean I_SetMusicPosition(UINT32 position)
 
 UINT32 I_GetMusicPosition(void)
 {
-    return 0.;
-}
-
-boolean I_MIDIPlaying(void)
-{
-	return (boolean)currsong && music_started;
+    return 0;
 }
 
 boolean I_MusicPlaying(void)
@@ -576,3 +587,8 @@ boolean I_MusicPaused(void)
 {
 	return false;
 }
+
+musictype_t I_MusicType(void)
+{
+	return MU_NONE;
+}
diff --git a/src/dummy/i_sound.c b/src/dummy/i_sound.c
index 26d7827099e5828a6ddcd35f64bacd7994438742..4cea29049ef4ac11de69f8abe88426aeeccaa341 100644
--- a/src/dummy/i_sound.c
+++ b/src/dummy/i_sound.c
@@ -146,6 +146,22 @@ boolean I_SetSongTrack(int track)
 	return false;
 }
 
+UINT32 I_GetMusicLength(void)
+{
+	return 0;
+}
+
+boolean I_SetMusicLoopPoint(UINT32 looppoint)
+{
+        (void)looppoint;
+        return false;
+}
+
+UINT32 I_GetMusicLoopPoint(void)
+{
+	return 0;
+}
+
 boolean I_SetMusicPosition(UINT32 position)
 {
     (void)position;
@@ -157,17 +173,17 @@ UINT32 I_GetMusicPosition(void)
     return 0;
 }
 
-boolean I_MIDIPlaying(void)
+boolean I_MusicPlaying(void)
 {
 	return false;
 }
 
-boolean I_MusicPlaying(void)
+boolean I_MusicPaused(void)
 {
 	return false;
 }
 
-boolean I_MusicPaused(void)
+musictype_t I_MusicType(void)
 {
-	return false;
+	return MU_NONE;
 }
diff --git a/src/i_sound.h b/src/i_sound.h
index ff9a9f196c2675d559c2c60f831fc0b54571e34d..a88038b39e1b513aadc2039a7be43f6b29253e52 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -18,6 +18,21 @@
 #include "sounds.h"
 #include "command.h"
 
+// copied from SDL mixer, plus GME
+typedef enum {
+    MU_NONE,
+    MU_CMD,
+    MU_WAV,
+    MU_MOD,
+    MU_MID,
+    MU_OGG,
+    MU_MP3,
+    MU_MP3_MAD_UNUSED, // use MU_MP3 instead
+    MU_FLAC,
+    MU_MODPLUG_UNUSED, // use MU_MOD instead
+	MU_GME
+} musictype_t;
+
 /**	\brief Sound subsystem runing and waiting
 */
 extern UINT8 sound_started;
@@ -132,12 +147,6 @@ void I_PauseSong(INT32 handle);
 */
 void I_ResumeSong(INT32 handle);
 
-/**	\brief Get MIDI music status
-
-	\return boolean
-*/
-boolean I_MIDIPlaying(void);
-
 /**	\brief Get general music status
 
 	\return boolean
@@ -150,6 +159,8 @@ boolean I_MusicPlaying(void);
 */
 boolean I_MusicPaused(void);
 
+musictype_t I_MusicType(void);
+
 //
 //  MIDI I/O
 //
@@ -224,6 +235,12 @@ void I_ShutdownDigMusic(void);
 
 boolean I_SetSongSpeed(float speed);
 
+UINT32 I_GetMusicLength(void);
+
+boolean I_SetMusicLoopPoint(UINT32 looppoint);
+
+UINT32 I_GetMusicLoopPoint(void);
+
 boolean I_SetMusicPosition(UINT32 position);
 
 UINT32 I_GetMusicPosition(void);
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index d6aeb9a31726ae191e1fd8e5397c0bfd9aba1009..83aab8430f103f2cf71d0218755104e7e545064a 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -2266,9 +2266,8 @@ static int lib_sSpeedMusic(lua_State *L)
 }
 
 #ifdef HAVE_LUA_MUSICPLUS
-static int lib_sSetMusicPosition(lua_State *L)
+static int lib_sGetMusicLength(lua_State *L)
 {
-	UINT32 position = 0;
 	player_t *player = NULL;
 	NOHUD
 	if (!lua_isnone(L, 2) && lua_isuserdata(L, 2))
@@ -2278,41 +2277,50 @@ static int lib_sSetMusicPosition(lua_State *L)
 			return LUA_ErrInvalid(L, "player_t");
 	}
 	if (!player || P_IsLocalPlayer(player))
-		lua_pushboolean(L, S_SetMusicPosition(position));
+		lua_pushinteger(L, (int)S_GetMusicLength());
 	else
 		lua_pushnil(L);
 	return 1;
 }
 
-static int lib_sGetMusicPosition(lua_State *L)
+static int lib_sSetMusicLoopPoint(lua_State *L)
 {
+	UINT32 looppoint = (UINT32)luaL_checkinteger(L, 1);
+	player_t *player = NULL;
 	NOHUD
-	lua_pushinteger(L, (UINT32)S_GetMusicPosition());
+	if (!lua_isnone(L, 2) && lua_isuserdata(L, 2))
+	{
+		player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER));
+		if (!player)
+			return LUA_ErrInvalid(L, "player_t");
+	}
+	if (!player || P_IsLocalPlayer(player))
+		lua_pushboolean(L, S_SetMusicLoopPoint(looppoint));
+	else
+		lua_pushnil(L);
 	return 1;
 }
 
-static int lib_sPauseMusic(lua_State *L)
+static int lib_sGetMusicLoopPoint(lua_State *L)
 {
 	player_t *player = NULL;
 	NOHUD
-	if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
+	if (!lua_isnone(L, 2) && lua_isuserdata(L, 2))
 	{
-		player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+		player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER));
 		if (!player)
 			return LUA_ErrInvalid(L, "player_t");
 	}
 	if (!player || P_IsLocalPlayer(player))
-	{
-		S_PauseAudio();
-		lua_pushboolean(L, true);
-	}
+		lua_pushinteger(L, (int)S_GetMusicLoopPoint());
 	else
 		lua_pushnil(L);
 	return 1;
 }
 
-static int lib_sResumeMusic(lua_State *L)
+static int lib_sSetMusicPosition(lua_State *L)
 {
+	UINT32 position = (UINT32)luaL_checkinteger(L, 1);
 	player_t *player = NULL;
 	NOHUD
 	if (!lua_isnone(L, 2) && lua_isuserdata(L, 2))
@@ -2322,16 +2330,30 @@ static int lib_sResumeMusic(lua_State *L)
 			return LUA_ErrInvalid(L, "player_t");
 	}
 	if (!player || P_IsLocalPlayer(player))
+		lua_pushboolean(L, S_SetMusicPosition(position));
+	else
+		lua_pushnil(L);
+	return 1;
+}
+
+static int lib_sGetMusicPosition(lua_State *L)
+{
+	player_t *player = NULL;
+	NOHUD
+	if (!lua_isnone(L, 2) && lua_isuserdata(L, 2))
 	{
-		S_ResumeAudio();
-		lua_pushboolean(L, true);
+		player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER));
+		if (!player)
+			return LUA_ErrInvalid(L, "player_t");
 	}
+	if (!player || P_IsLocalPlayer(player))
+		lua_pushinteger(L, (int)S_GetMusicPosition());
 	else
 		lua_pushnil(L);
 	return 1;
 }
 
-static int lib_sStopMusic(lua_State *L)
+static int lib_sPauseMusic(lua_State *L)
 {
 	player_t *player = NULL;
 	NOHUD
@@ -2343,7 +2365,7 @@ static int lib_sStopMusic(lua_State *L)
 	}
 	if (!player || P_IsLocalPlayer(player))
 	{
-		S_StopMusic();
+		S_PauseAudio();
 		lua_pushboolean(L, true);
 	}
 	else
@@ -2351,7 +2373,7 @@ static int lib_sStopMusic(lua_State *L)
 	return 1;
 }
 
-static int lib_sDigitalPlaying(lua_State *L)
+static int lib_sResumeMusic(lua_State *L)
 {
 	player_t *player = NULL;
 	NOHUD
@@ -2362,13 +2384,16 @@ static int lib_sDigitalPlaying(lua_State *L)
 			return LUA_ErrInvalid(L, "player_t");
 	}
 	if (!player || P_IsLocalPlayer(player))
-		lua_pushboolean(L, !S_MIDIPlaying() && S_MusicPlaying());
+	{
+		S_ResumeAudio();
+		lua_pushboolean(L, true);
+	}
 	else
 		lua_pushnil(L);
 	return 1;
 }
 
-static int lib_sMidiPlaying(lua_State *L)
+static int lib_sStopMusic(lua_State *L)
 {
 	player_t *player = NULL;
 	NOHUD
@@ -2379,7 +2404,10 @@ static int lib_sMidiPlaying(lua_State *L)
 			return LUA_ErrInvalid(L, "player_t");
 	}
 	if (!player || P_IsLocalPlayer(player))
-		lua_pushboolean(L, S_MIDIPlaying());
+	{
+		S_StopMusic();
+		lua_pushboolean(L, true);
+	}
 	else
 		lua_pushnil(L);
 	return 1;
@@ -2419,6 +2447,23 @@ static int lib_sMusicPaused(lua_State *L)
 	return 1;
 }
 
+static int lib_sMusicType(lua_State *L)
+{
+	player_t *player = NULL;
+	NOHUD
+	if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
+	{
+		player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+		if (!player)
+			return LUA_ErrInvalid(L, "player_t");
+	}
+	if (!player || P_IsLocalPlayer(player))
+		lua_pushinteger(L, S_MusicType());
+	else
+		lua_pushnil(L);
+	return 1;
+}
+
 static int lib_sMusicName(lua_State *L)
 {
 	player_t *player = NULL;
@@ -2850,15 +2895,17 @@ static luaL_Reg lib[] = {
 	{"S_ChangeMusic",lib_sChangeMusic},
 	{"S_SpeedMusic",lib_sSpeedMusic},
 #ifdef HAVE_LUA_MUSICPLUS
+	{"S_GetMusicLength",lib_sGetMusicLength},
+	{"S_SetMusicLoopPoint",lib_sSetMusicLoopPoint},
+	{"S_GetMusicLoopPoint",lib_sGetMusicLoopPoint},
 	{"S_SetMusicPosition",lib_sSetMusicPosition},
 	{"S_GetMusicPosition",lib_sGetMusicPosition},
 	{"S_PauseMusic",lib_sPauseMusic},
 	{"S_ResumeMusic",lib_sResumeMusic},
 	{"S_StopMusic",lib_sStopMusic},
-	{"S_DigitalPlaying",lib_sDigitalPlaying},
-	{"S_MidiPlaying",lib_sMidiPlaying},
 	{"S_MusicPlaying",lib_sMusicPlaying},
 	{"S_MusicPaused",lib_sMusicPaused},
+	{"S_MusicType",lib_sMusicType},
 	{"S_MusicName",lib_sMusicName},
 	{"S_MusicExists",lib_sMusicExists},
 #endif
diff --git a/src/s_sound.c b/src/s_sound.c
index bd27e4da26d2594673f968f541a19e0c343aa0a4..3b0c87463af25c27e48e85697409ba9fc7ab6935 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;
 
@@ -1412,11 +1412,31 @@ void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping)
 	I_SetSongTrack(mflags & MUSIC_TRACKMASK);
 }
 
+musictype_t S_MusicType()
+{
+	return I_MusicType();
+}
+
 boolean S_SpeedMusic(float speed)
 {
 	return I_SetSongSpeed(speed);
 }
 
+UINT32 S_GetMusicLength(void)
+{
+	return I_GetMusicLength();
+}
+
+boolean S_SetMusicLoopPoint(UINT32 looppoint)
+{
+	return I_SetMusicLoopPoint(looppoint);
+}
+
+UINT32 S_GetMusicLoopPoint(void)
+{
+	return I_GetMusicLoopPoint();
+}
+
 boolean S_SetMusicPosition(UINT32 position)
 {
 	return I_SetMusicPosition(position);
@@ -1589,11 +1609,6 @@ void S_ResumeAudio(void)
 	I_ResumeCD();
 }
 
-boolean S_MIDIPlaying(void)
-{
-	return I_MIDIPlaying();
-}
-
 boolean S_MusicPlaying(void)
 {
 	return I_MusicPlaying();
diff --git a/src/s_sound.h b/src/s_sound.h
index bb33233db2938829f1c217c56e77f88272d233e6..096a03f1f4bd5d69a2a50fe2400a501f32c78c77 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -14,6 +14,7 @@
 #ifndef __S_SOUND__
 #define __S_SOUND__
 
+#include "i_sound.h"
 #include "sounds.h"
 #include "m_fixed.h"
 #include "command.h"
@@ -132,9 +133,21 @@ void S_StopSound(void *origin);
 #define S_ChangeMusicInternal(a,b) S_ChangeMusic(a,0,b)
 void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping);
 
+// Get music type
+musictype_t S_MusicType();
+
 // Set Speed of Music
 boolean S_SpeedMusic(float speed);
 
+// Get Length of Music
+UINT32 S_GetMusicLength(void);
+
+// Set LoopPoint of Music
+boolean S_SetMusicLoopPoint(UINT32 looppoint);
+
+// Get LoopPoint of Music
+UINT32 S_GetMusicLoopPoint(void);
+
 // Set Position of Music
 boolean S_SetMusicPosition(UINT32 position);
 
@@ -148,9 +161,6 @@ void S_StopMusic(void);
 void S_PauseAudio(void);
 void S_ResumeAudio(void);
 
-// Gets MIDI music status
-boolean S_MIDIPlaying(void);
-
 // Gets general music status
 boolean S_MusicPlaying(void);
 
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 6af1bc0f0c8f403d1602240f2554fc95c5a46e83..9e9d0718493508bee4b3b14da48e228a6709f55e 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;
@@ -80,6 +81,15 @@ static Music_Emu *gme;
 static INT32 current_track;
 #endif
 
+static void varcleanup(void)
+{
+	loop_point = music_length =\
+	 music_bytes = 0;
+
+	songpaused = is_looping =\
+	 midimode = false;
+}
+
 void I_StartupSound(void)
 {
 	I_Assert(!sound_started);
@@ -315,6 +325,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
 					len = (info->play_length * 441 / 10) << 2;
 					mem = Z_Malloc(len, PU_SOUND, NULL);
 					gme_play(emu, len >> 1, mem);
+					gme_free_info(info);
 					gme_delete(emu);
 
 					return Mix_QuickLoad_RAW((Uint8 *)mem, len);
@@ -387,6 +398,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
 		len = (info->play_length * 441 / 10) << 2;
 		mem = Z_Malloc(len, PU_SOUND, NULL);
 		gme_play(emu, len >> 1, mem);
+		gme_free_info(info);
 		gme_delete(emu);
 
 		return Mix_QuickLoad_RAW((Uint8 *)mem, len);
@@ -443,9 +455,25 @@ void I_SetSfxVolume(UINT8 volume)
 // Music
 //
 
+musictype_t I_MusicType(void)
+{
+	if (gme)
+		return MU_GME;
+	else if (midimode)
+		return MU_MID;
+	else if (!music)
+		return MU_NONE;
+	else if (Mix_GetMusicType(music) == MUS_MOD || Mix_GetMusicType(music) == MUS_MODPLUG_UNUSED)
+		return MU_MOD;
+	else if (Mix_GetMusicType(music) == MUS_MP3 || Mix_GetMusicType(music) == MUS_MP3_MAD_UNUSED)
+		return MU_MP3;
+	else
+		return (musictype_t)Mix_GetMusicType(music);
+}
+
 static void count_music_bytes(int chan, void *stream, int len, void *udata)
 {
-	if(midimode || !music)
+	if (gme || midimode || !music || I_MusicType() == MU_MOD)
 		return;
 	music_bytes += len;
 }
@@ -457,15 +485,10 @@ static void music_loop(void)
 	{
 		Mix_PlayMusic(music, 0);
 		Mix_SetMusicPosition(loop_point);
-		music_bytes = loop_point/1000.0L*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetMusicPosition)
+		music_bytes = loop_point*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetMusicPosition)
 	}
 	else
-	{
-		Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
-		music_bytes = 0;
-			// be consistent with FMOD, otherwise I'd prefer to freeze music_bytes
-			// since the other flags indicate music is still playing.
-	}
+		I_StopDigSong();
 }
 
 static UINT32 music_fadeout(UINT32 interval)
@@ -526,8 +549,13 @@ void I_ShutdownMusic(void)
 void I_PauseSong(INT32 handle)
 {
 	(void)handle;
-	if(!midimode)
+
+	if(midimode) // really, SDL Mixer? why can't you pause MIDI???
+		return;
+
+	if(!gme && I_MusicType() != MU_MOD)
 		Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
+
 	Mix_PauseMusic();
 	songpaused = true;
 }
@@ -535,23 +563,23 @@ void I_PauseSong(INT32 handle)
 void I_ResumeSong(INT32 handle)
 {
 	(void)handle;
-	if(!midimode)
+
+	if (midimode)
+		return;
+
+	if (!gme && I_MusicType() != MU_MOD)
 	{
 		while(Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes) != 0) { }
 			// HACK: fixes issue of multiple effect callbacks being registered
+
 		if(music && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
-			// midimode and music must be checked in case nothing is actually playing
 			CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError());
 	}
+
 	Mix_ResumeMusic();
 	songpaused = false;
 }
 
-boolean I_MIDIPlaying(void)
-{
-	return midimode && music;
-}
-
 boolean I_MusicPlaying(void)
 {
 	return (boolean)music;
@@ -559,7 +587,7 @@ boolean I_MusicPlaying(void)
 
 boolean I_MusicPaused(void)
 {
-	return Mix_PausedMusic();
+	return songpaused;
 }
 
 //
@@ -588,8 +616,7 @@ void I_ShutdownDigMusic(void)
 #endif
 	if (!music)
 		return;
-	is_fadingin = is_fadingout = is_looping = false;
-	music_bytes = 0;
+	varcleanup();
 	SDL_RemoveTimer(fading_id);
 	Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
 	Mix_HookMusicFinished(NULL);
@@ -610,7 +637,8 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 
 	if (lumpnum == LUMPERROR)
 		return false;
-	midimode = false;
+
+	varcleanup();
 
 	data = (char *)W_CacheLumpNum(lumpnum, PU_MUSIC);
 	len = W_LumpLength(lumpnum);
@@ -723,50 +751,127 @@ 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);
+
+		// for mp3 wide chars
+		const char *key1w = "L\0O\0O\0P\0";
+		const char *key2w = "P\0O\0I\0N\0T\0\0\0\xFF\xFE";
+		const char *key3w = "M\0S\0\0\0\xFF\xFE";
+		const char *key4w = "L\0E\0N\0G\0T\0H\0M\0S\0\0\0\xFF\xFE";
+		const char *wterm = "\0\0";
+		char wval[10];
+
+		size_t wstart, wp;
 		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 (!music_length && !strncmp(p, key4, key4len)) // is it LENGTHMS=?
+			{
+				p += key4len; // skip LENGTHMS
+				music_length = (float)(atoi(p) / 1000.0L);
 			}
-			else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
+			// below: search MP3 or other tags that use wide char encoding
+			else if (!loop_point && !memcmp(p, key1w, key1len*2)) // LOOP wide char
 			{
-				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 += key1len*2;
+				if (!memcmp(p, key2w, (key2len+1)*2)) // POINT= wide char
+				{
+					p += (key2len+1)*2;
+					wstart = (size_t)p;
+					wp = 0;
+					while (wp < 9 && memcmp(p, wterm, 2))
+					{
+						wval[wp] = *p;
+						p += 2;
+						wp = ((size_t)(p-wstart))/2;
+					}
+					wval[min(wp, 9)] = 0;
+					loop_point = (float)((44.1L+atoi(wval) / 44100.0L));
+				}
+				else if (!memcmp(p, key3w, (key3len+1)*2)) // MS= wide char
+				{
+					p += (key3len+1)*2;
+					wstart = (size_t)p;
+					wp = 0;
+					while (wp < 9 && memcmp(p, wterm, 2))
+					{
+						wval[wp] = *p;
+						p += 2;
+						wp = ((size_t)(p-wstart))/2;
+					}
+					wval[min(wp, 9)] = 0;
+					loop_point = (float)(atoi(wval) / 1000.0L);
+				}
 			}
-			// Neither?! Continue searching.
+			else if (!music_length && !memcmp(p, key4w, (key4len+1)*2)) // LENGTHMS= wide char
+			{
+				p += (key4len+1)*2;
+				wstart = (size_t)p;
+				wp = 0;
+				while (wp < 9 && memcmp(p, wterm, 2))
+				{
+					wval[wp] = *p;
+					p += 2;
+					wp = ((size_t)(p-wstart))/2;
+				}
+				wval[min(wp, 9)] = 0;
+				music_length = (float)(atoi(wval) / 1000.0L);
+			}
+
+			if (loop_point && music_length && music_length > loop_point) // Got what we needed
+				// the last case is a sanity check, in case the wide char searches were false matches.
+				break;
+			else // continue searching
+				p++;
 		}
 	}
 
-	if (Mix_PlayMusic(music, 0) == -1)
+	if (I_MusicType() != MU_MOD && Mix_PlayMusic(music, 0) == -1)
+	{
+		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
+		return true;
+	}
+	else if ((I_MusicType() == MU_MOD) && Mix_PlayMusic(music, -1) == -1) // if MOD, loop forever
 	{
 		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
 		return true;
 	}
+
 	Mix_VolumeMusic((UINT32)music_volume*128/31);
 
-	Mix_HookMusicFinished(music_loop);
+	if (I_MusicType() != MU_MOD)
+		Mix_HookMusicFinished(music_loop); // don't bother counting if MOD
 
-	music_bytes = 0;
-	if(!Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
+	if(I_MusicType() != MU_MOD && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
 		CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError());
 
 	return true;
@@ -788,8 +893,7 @@ void I_StopDigSong(void)
 #endif
 	if (!music)
 		return;
-	is_looping = false;
-	music_bytes = 0;
+	varcleanup();
 	Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
 	Mix_HookMusicFinished(NULL);
 	Mix_FreeMusic(music);
@@ -822,30 +926,175 @@ boolean I_SetSongSpeed(float speed)
 	return false;
 }
 
+UINT32 I_GetMusicLength(void)
+{
+	INT32 length;
+
+	if (gme)
+	{
+		gme_info_t *info;
+		gme_err_t gme_e = gme_track_info(gme, &info, current_track);
+
+		if (gme_e != NULL)
+		{
+			CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+			length = 0;
+		}
+		else
+		{
+			// reconstruct info->play_length, from GME source
+			// we only want intro + 1 loop, not 2
+			length = info->length;
+			if (length <= 0)
+			{
+				length = info->intro_length + info->loop_length; // intro + 1 loop
+				if (length <= 0)
+					length = 150 * 1000; // 2.5 minutes
+			}
+		}
+
+		gme_free_info(info);
+		return max(length, 0);
+	}
+	else if (midimode || !music || I_MusicType() == MU_MOD)
+		return 0;
+	else
+	{
+		// VERY IMPORTANT to set your LENGTHMS= in your song files, folks!
+		// SDL mixer can't read music length itself.
+		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_SetMusicLoopPoint(UINT32 looppoint)
+{
+	if (midimode || gme || !music || I_MusicType() == MU_MOD || !is_looping)
+		return false;
+	else
+	{
+		UINT32 length = I_GetMusicLength();
+
+		if (length > 0)
+			looppoint %= length;
+
+		loop_point = max((float)(looppoint / 1000.0L), 0);
+		return true;
+	}
+}
+
+UINT32 I_GetMusicLoopPoint(void)
+{
+	if (gme)
+	{
+		INT32 looppoint;
+		gme_info_t *info;
+		gme_err_t gme_e = gme_track_info(gme, &info, current_track);
+
+		if (gme_e != NULL)
+		{
+			CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+			looppoint = 0;
+		}
+		else
+			looppoint = info->intro_length > 0 ? info->intro_length : 0;
+
+		gme_free_info(info);
+		return max(looppoint, 0);
+	}
+	else if (midimode || !music || I_MusicType() == MU_MOD)
+		return 0;
+	else
+		return (UINT32)(loop_point * 1000);
+}
+
 boolean I_SetMusicPosition(UINT32 position)
 {
-	if(midimode || !music)
+	UINT32 length;
+
+	if (gme)
+	{
+		// this isn't required technically, but GME thread-locks for a second
+		// if you seek too high from the counter
+		length = I_GetMusicLength();
+		if (length)
+			position %= length;
+
+		SDL_LockAudio();
+		gme_err_t gme_e = gme_seek(gme, position);
+		SDL_UnlockAudio();
+
+		if (gme_e != NULL)
+		{
+			CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+			return false;
+		}
+		else
+			return true;
+	}
+	else if (midimode || !music)
 		return false;
-	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)
+	else if (I_MusicType() == MU_MOD)
+		return Mix_SetMusicPosition(position); // Goes by channels
 	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;
+	{
+		// Because SDL mixer can't identify song length, if you have
+		// a position input greater than the real length, then
+		// music_bytes becomes inaccurate.
+
+		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_GetSongPosition)
+		else
+			// NOTE: This block fires on incorrect song format,
+			// NOT if position input is greater than song length.
+			music_bytes = 0;
+
+		return true;
+	}
 }
 
 UINT32 I_GetMusicPosition(void)
 {
-	if(midimode)
+	if (gme)
+	{
+		INT32 position = gme_tell(gme);
+
+		gme_info_t *info;
+		gme_err_t gme_e = gme_track_info(gme, &info, current_track);
+
+		if (gme_e != NULL)
+		{
+			CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+			return position;
+		}
+		else
+		{
+			// adjust position, since GME's counter keeps going past loop
+			if (info->length > 0)
+				position %= info->length;
+			else if (info->intro_length + info->loop_length > 0)
+				position = ((position - info->intro_length) % info->loop_length) + info->intro_length;
+			else
+				position %= 150 * 1000; // 2.5 minutes
+		}
+
+		gme_free_info(info);
+		return max(position, 0);
+	}
+	else if (midimode || !music)
 		return 0;
-	return music_bytes/44100.0L*1000.0L/4; //assume 44.1khz
-	// 4 = byte length for 16-bit samples (AUDIO_S16SYS), stereo (2-channel)
-	// This is hardcoded in I_StartupSound. Other formats for factor:
-	// 8M: 1 | 8S: 2 | 16M: 2 | 16S: 4
+	else
+		return music_bytes/44100.0L*1000.0L/4; //assume 44.1khz
+		// 4 = byte length for 16-bit samples (AUDIO_S16SYS), stereo (2-channel)
+		// This is hardcoded in I_StartupSound. Other formats for factor:
+		// 8M: 1 | 8S: 2 | 16M: 2 | 16S: 4
 }
 
 boolean I_SetSongTrack(int track)
@@ -874,7 +1123,10 @@ boolean I_SetSongTrack(int track)
 		SDL_UnlockAudio();
 		return false;
 	}
+	else
 #endif
+	if (I_MusicType() == MU_MOD)
+		return !Mix_SetMusicPosition(track);
 	(void)track;
 	return false;
 }
@@ -936,9 +1188,8 @@ void I_ShutdownMIDIMusic(void)
 {
 	if (!midimode || !music)
 		return;
-	is_looping = false;
+	varcleanup();
 	//MIDI does count correctly, but dummy out because unsupported
-	//music_bytes = 0;
 	//Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
 	Mix_FreeMusic(music);
 	music = NULL;
@@ -972,6 +1223,8 @@ boolean I_PlaySong(INT32 handle, boolean looping)
 {
 	(void)handle;
 
+	varcleanup();
+
 	midimode = true;
 
 	if (Mix_PlayMusic(music, looping ? -1 : 0) == -1)
@@ -985,7 +1238,6 @@ boolean I_PlaySong(INT32 handle, boolean looping)
 	//MIDI does count correctly, but dummy out because unsupported
 	//If this is enabled, you need to edit Mix_PlayMusic above to never loop (0)
 	//and register the music_loop callback
-	//music_bytes = 0;
 	//if(!Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
 	//	CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError());
 
@@ -998,10 +1250,8 @@ void I_StopSong(INT32 handle)
 	if (!midimode || !music)
 		return;
 
-	is_looping = false;
-
+	varcleanup();
 	//MIDI does count correctly, but dummy out because unsupported
-	//music_bytes = 0;
 	//Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
 	(void)handle;
 	Mix_HaltMusic();
@@ -1012,10 +1262,8 @@ void I_UnRegisterSong(INT32 handle)
 	if (!midimode || !music)
 		return;
 
-	is_looping = false;
-
+	varcleanup();
 	//MIDI does count correctly, but dummy out because unsupported
-	//music_bytes = 0;
 	//Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
 	(void)handle;
 	Mix_FreeMusic(music);
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index 42b45c56393ae29edbdb6c615bb4a6bb9ac6cd00..e88d874fd76410841aaba1ceb161b2285d0cde7d 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -1649,12 +1649,6 @@ void I_StopSong(INT32 handle)
 #endif
 }
 
-boolean I_MIDIPlaying(void)
-{
-	// todo: no way to tell specifically if MIDI is playing, implement midimode
-	return !nomidimusic && nodigimusic && musicStarted;
-}
-
 boolean I_MusicPlaying(void)
 {
 	return musicStarted;
@@ -1665,6 +1659,11 @@ boolean I_MusicPaused(void)
 	return Mix_PausedMusic();
 }
 
+musictype_t I_MusicType(void)
+{
+	return MU_NONE;
+}
+
 void I_UnRegisterSong(INT32 handle)
 {
 #ifdef HAVE_MIXER
@@ -1989,6 +1988,22 @@ boolean I_SetSongSpeed(float speed)
 	return false;
 }
 
+UINT32 I_GetMusicLength(void)
+{
+	return 0;
+}
+
+boolean I_SetMusicLoopPoint(UINT32 looppoint)
+{
+        (void)looppoint;
+        return false;
+}
+
+UINT32 I_GetMusicLoopPoint(void)
+{
+	return 0;
+}
+
 boolean I_SetMusicPosition(UINT32 position)
 {
 	(void)position;
diff --git a/src/win32/win_snd.c b/src/win32/win_snd.c
index f8668694571dee24eadb2fb48e5f012a8d8422c4..066392a41dc5646c8e300f85c8d8c4e387da5e96 100644
--- a/src/win32/win_snd.c
+++ b/src/win32/win_snd.c
@@ -468,11 +468,6 @@ void I_ResumeSong(INT32 handle)
 		FMR_MUSIC(FMOD_Channel_SetPaused(music_channel, false));
 }
 
-boolean I_MIDIPlaying(void)
-{
-	return midimode && music_stream;
-}
-
 boolean I_MusicPlaying(void)
 {
 	return (boolean)music_stream;
@@ -486,6 +481,11 @@ boolean I_MusicPaused(void)
 	return fmpaused;
 }
 
+musictype_t I_MusicType(void)
+{
+	return MU_NONE;
+}
+
 void I_InitDigMusic(void)
 {
 }
@@ -774,6 +774,26 @@ boolean I_SetSongSpeed(float speed)
 	return true;
 }
 
+UINT32 I_GetMusicLength()
+{
+	if (midimode)
+		return 0;
+	UINT32 length;
+	e = FMOD_Sound_GetLength(music_stream, length, FMOD_TIMEUNIT_MS);
+	return length;
+}
+
+boolean I_SetMusicLoopPoint(UINT32 looppoint)
+{
+        (void)looppoint;
+        return false;
+}
+
+UINT32 I_GetMusicLoopPoint(void)
+{
+	return 0;
+}
+
 boolean I_SetMusicPosition(UINT32 position)
 {
 	if(midimode)