diff --git a/libraries/zmusic/midisources/midisource_xmi.cpp b/libraries/zmusic/midisources/midisource_xmi.cpp
index bbb254f88..2e1e51214 100644
--- a/libraries/zmusic/midisources/midisource_xmi.cpp
+++ b/libraries/zmusic/midisources/midisource_xmi.cpp
@@ -35,6 +35,7 @@
 // HEADER FILES ------------------------------------------------------------
 
 #include "midisource.h"
+#include "zmusic/mididefs.h"
 #include "zmusic/m_swap.h"
 
 // MACROS ------------------------------------------------------------------
@@ -51,14 +52,6 @@
 		return events; \
 	}
 
-#ifndef MAKE_ID
-#ifndef __BIG_ENDIAN__
-#define MAKE_ID(a,b,c,d)	((uint32_t)((a)|((b)<<8)|((c)<<16)|((d)<<24)))
-#else
-#define MAKE_ID(a,b,c,d)	((uint32_t)((d)|((c)<<8)|((b)<<16)|((a)<<24)))
-#endif
-#endif
-
 // TYPES -------------------------------------------------------------------
 
 struct LoopInfo
diff --git a/libraries/zmusic/zmusic/midiconfig.h b/libraries/zmusic/zmusic/midiconfig.h
index b49dbac0a..edb3d125f 100644
--- a/libraries/zmusic/zmusic/midiconfig.h
+++ b/libraries/zmusic/zmusic/midiconfig.h
@@ -126,3 +126,14 @@ struct WildMidiConfig
 };
 
 
+struct DumbConfig
+{
+	int  mod_samplerate;
+    int  mod_volramp;
+    int  mod_interp;
+    bool mod_autochip;
+    int  mod_autochip_size_force;
+    int  mod_autochip_size_scan;
+    int  mod_autochip_scan_threshold;
+    float mod_dumb_mastervolume;
+};
diff --git a/libraries/zmusic/zmusic/mididefs.h b/libraries/zmusic/zmusic/mididefs.h
index 97bdc8a76..67c4f1d09 100644
--- a/libraries/zmusic/zmusic/mididefs.h
+++ b/libraries/zmusic/zmusic/mididefs.h
@@ -53,3 +53,11 @@ struct SoundStreamInfo
 	int mNumChannels;
 };
 
+#ifndef MAKE_ID
+#ifndef __BIG_ENDIAN__
+#define MAKE_ID(a,b,c,d)	((uint32_t)((a)|((b)<<8)|((c)<<16)|((d)<<24)))
+#else
+#define MAKE_ID(a,b,c,d)	((uint32_t)((d)|((c)<<8)|((b)<<16)|((a)<<24)))
+#endif
+#endif
+
diff --git a/src/sound/backend/i_sound.h b/src/sound/backend/i_sound.h
index f45a6f925..bc18f9cda 100644
--- a/src/sound/backend/i_sound.h
+++ b/src/sound/backend/i_sound.h
@@ -40,12 +40,6 @@
 class FileReader;
 struct FSoundChan;
 
-enum ECodecType
-{
-	CODEC_Unknown,
-	CODEC_Vorbis,
-};
-
 enum EStartSoundFlags
 {
 	SNDF_LOOP=1,
@@ -55,6 +49,13 @@ enum EStartSoundFlags
 	SNDF_NOREVERB=16,
 };
 
+enum ECodecType
+{
+	CODEC_Unknown,
+	CODEC_Vorbis,
+};
+
+
 class SoundStream
 {
 public:
diff --git a/src/sound/music/i_music.cpp b/src/sound/music/i_music.cpp
index 9abe79901..828fa7cf5 100644
--- a/src/sound/music/i_music.cpp
+++ b/src/sound/music/i_music.cpp
@@ -47,7 +47,9 @@
 #include "stats.h"
 #include "vm.h"
 #include "s_music.h"
+#include "i_soundfont.h"
 #include "../libraries/zmusic/midisources/midisource.h"
+#include "../libraries/dumb/include/dumb.h"
 
 EXTERN_CVAR(Float, gme_stereodepth)
 
@@ -88,6 +90,17 @@ int		nomusic = 0;
 float	relative_volume = 1.f;
 float	saved_relative_volume = 1.0f;	// this could be used to implement an ACS FadeMusic function
 
+//==========================================================================
+//
+// dumb_decode_vorbis
+//
+//==========================================================================
+
+static short* dumb_decode_vorbis_(int outlen, const void* oggstream, int sizebytes)
+{
+	return GSnd->DecodeSample(outlen, oggstream, sizebytes, CODEC_Vorbis);
+}
+
 //==========================================================================
 //
 // CVAR snd_musicvolume
@@ -140,6 +153,7 @@ void I_InitMusic (void)
 #endif // _WIN32
 	
 	MusicDown = false;
+	dumb_decode_vorbis = dumb_decode_vorbis_;
 }
 
 
@@ -457,15 +471,25 @@ MusInfo *I_RegisterSong (FileReader &reader, MidiDeviceSetting *device)
 	// Check for module formats
 	else
 	{
-		streamsource = MOD_OpenSong(reader);
+		auto mreader = new FileReaderMusicInterface(reader);
+		Dumb_SetupConfig(&dumbConfig);
+		streamsource = MOD_OpenSong(mreader, &dumbConfig, (int)GSnd->GetOutputRate());
+		delete mreader;
 	}
 	if (info == nullptr && streamsource == nullptr)
 	{
 		streamsource = SndFile_OpenSong(reader);
 	}
 	
-	if (streamsource) info = OpenStreamSong(streamsource);
-
+	if (streamsource)
+	{
+		info = OpenStreamSong(streamsource);
+		if (!info)
+		{
+			// If this fails we have no more valid data - but it couldn't be a CDDA file anyway.
+			return nullptr;
+		}
+	}
     if (info == nullptr)
     {
         // Check for CDDA "format"
diff --git a/src/sound/music/i_musicinterns.h b/src/sound/music/i_musicinterns.h
index c0c36d3ec..2767e4092 100644
--- a/src/sound/music/i_musicinterns.h
+++ b/src/sound/music/i_musicinterns.h
@@ -26,11 +26,11 @@ extern OpnConfig opnConfig;
 extern GUSConfig gusConfig;
 extern TimidityConfig timidityConfig;
 extern WildMidiConfig wildMidiConfig;
+extern DumbConfig dumbConfig;
 
 
 class MIDIStreamer;
 
-
 // Base class for streaming MUS and MIDI files ------------------------------
 
 class MIDIStreamer : public MusInfo
@@ -108,34 +108,6 @@ protected:
 	std::unique_ptr<SoundStream> Stream;
 };
 
-// Anything supported by the sound system out of the box --------------------
-
-class StreamSource
-{
-protected:
-	bool m_Looping = true;
-	int m_OutputRate;
-	
-public:
-
-	StreamSource (int outputRate) { m_OutputRate = outputRate; }
-	virtual ~StreamSource () {}
-	virtual void SetPlayMode(bool looping) { m_Looping = looping; }
-	virtual bool Start() { return true; }
-	virtual bool SetPosition(unsigned position) { return false; }
-	virtual bool SetSubsong(int subsong) { return false; }
-	virtual bool GetData(void *buffer, size_t len) = 0;
-	virtual SoundStreamInfo GetFormat() { return {65536, m_OutputRate, 2  }; }	// Default format is: System's output sample rate, 32 bit float, stereo
-	virtual FString GetStats() { return ""; }
-	virtual void ChangeSettingInt(const char *name, int value) {  }
-	virtual void ChangeSettingNum(const char *name, double value) {  }
-	virtual void ChangeSettingString(const char *name, const char *value) {  }
-
-protected:
-	StreamSource() = default;
-};
-
-
 // CD track/disk played through the multimedia system -----------------------
 
 class CDSong : public MusInfo
@@ -174,10 +146,13 @@ void OPN_SetupConfig(OpnConfig *config, const char *Args);
 bool GUS_SetupConfig(GUSConfig *config, const char *args);
 bool Timidity_SetupConfig(TimidityConfig* config, const char* args);
 bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args);
+void Dumb_SetupConfig(DumbConfig* config);
 
 // Module played via foo_dumb -----------------------------------------------
 
-StreamSource *MOD_OpenSong(FileReader &reader);
+class StreamSource;
+
+StreamSource *MOD_OpenSong(MusicIO::FileInterface* reader, DumbConfig* config, int samplerate);
 StreamSource *GME_OpenSong(FileReader &reader, const char *fmt, float depth);
 StreamSource *SndFile_OpenSong(FileReader &fr);
 StreamSource* XA_OpenSong(FileReader& reader);
diff --git a/src/sound/music/midi_cvars.cpp b/src/sound/music/midi_cvars.cpp
index d90765085..8aafe9fee 100644
--- a/src/sound/music/midi_cvars.cpp
+++ b/src/sound/music/midi_cvars.cpp
@@ -43,6 +43,7 @@
 #include "../libraries/timidity/timidity/timidity.h"
 #include "../libraries/timidityplus/timiditypp/timidity.h"
 #include "../libraries/oplsynth/oplsynth/oplio.h"
+#include "../libraries/dumb/include/dumb.h"
 
 #ifdef _WIN32
 // do this without including windows.h for this one single prototype
@@ -64,6 +65,7 @@ OpnConfig opnConfig;
 GUSConfig gusConfig;
 TimidityConfig timidityConfig;
 WildMidiConfig wildMidiConfig;
+DumbConfig dumbConfig;
 
 //==========================================================================
 //
@@ -912,14 +914,54 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args)
 	return true;
 }
 
+//==========================================================================
+//
 // This one is for Win32 MMAPI.
+//
+//==========================================================================
 
 CVAR(Bool, snd_midiprecache, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
 
+//==========================================================================
+//
 // GME
+//
+//==========================================================================
 
 CUSTOM_CVAR(Float, gme_stereodepth, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
 {
 	if (currSong != nullptr)
 		currSong->ChangeSettingNum("GME.stereodepth", *self);
 }
+
+//==========================================================================
+//
+// Dumb
+//
+//==========================================================================
+
+CVAR(Int,  mod_samplerate,				0,	   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CVAR(Int,  mod_volramp,					2,	   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CVAR(Int,  mod_interp,					DUMB_LQ_CUBIC,	CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CVAR(Bool, mod_autochip,				false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CVAR(Int,  mod_autochip_size_force,		100,   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CVAR(Int,  mod_autochip_size_scan,		500,   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CVAR(Int,  mod_autochip_scan_threshold, 12,	   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+CUSTOM_CVAR(Float, mod_dumb_mastervolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+{
+	if (self < 0.5f) self = 0.5f;
+	else if (self > 16.f) self = 16.f;
+}
+
+
+void Dumb_SetupConfig(DumbConfig* config)
+{
+	config->mod_samplerate					= mod_samplerate;			
+    config->mod_volramp                     = mod_volramp;            
+    config->mod_interp                      = mod_interp;               
+    config->mod_autochip                    = mod_autochip;
+    config->mod_autochip_size_force         = mod_autochip_size_force;
+    config->mod_autochip_size_scan          = mod_autochip_size_scan;
+    config->mod_autochip_scan_threshold     = mod_autochip_scan_threshold;
+    config->mod_dumb_mastervolume           = mod_dumb_mastervolume;
+}
diff --git a/src/sound/musicformats/music_dumb.cpp b/src/sound/musicformats/music_dumb.cpp
index bad3471b5..4161ebf1d 100644
--- a/src/sound/musicformats/music_dumb.cpp
+++ b/src/sound/musicformats/music_dumb.cpp
@@ -37,23 +37,18 @@
 
 #include <math.h>
 #include <mutex>
-#include "i_musicinterns.h"
+#include <string>
+#include <stdint.h>
+#include "streamsource.h"
 
 
 #undef CDECL	// w32api's windef.h defines this
 #include "../dumb/include/dumb.h"
 #include "../dumb/include/internal/it.h"
-
-//==========================================================================
-//
-// dumb_decode_vorbis
-//
-//==========================================================================
-
-static short *DUMBCALLBACK dumb_decode_vorbis_(int outlen, const void *oggstream, int sizebytes)
-{
-	return GSnd->DecodeSample(outlen, oggstream, sizebytes, CODEC_Vorbis);
-}
+#include "m_swap.h"
+#include "zmusic/mididefs.h"
+#include "zmusic/midiconfig.h"
+#include "../..//libraries/music_common/fileio.h"
 
 // MACROS ------------------------------------------------------------------
 
@@ -62,20 +57,22 @@ static short *DUMBCALLBACK dumb_decode_vorbis_(int outlen, const void *oggstream
 class DumbSong : public StreamSource
 {
 public:
-	DumbSong(DUH *myduh);
+	DumbSong(DUH *myduh, DumbConfig *config, int samplerate);
 	~DumbSong();
 	//bool SetPosition(int ms);
 	bool SetSubsong(int subsong) override;
 	bool Start() override;
 	SoundStreamInfo GetFormat() override;
-	FString GetStats() override;
+	void ChangeSettingNum(const char* setting, double val) override;
+	std::string GetStats() override;
 
-	FString Codec;
-	FString TrackerVersion;
-	FString FormatVersion;
+	std::string Codec;
+	std::string TrackerVersion;
+	std::string FormatVersion;
 	int NumChannels;
 	int NumPatterns;
 	int NumOrders;
+	float MasterVolume;
 
 protected:
 	int srate, interp, volramp;
@@ -98,6 +95,12 @@ protected:
 
 #pragma pack(1)
 
+#if defined(__GNUC__)
+#define FORCE_PACKED __attribute__((__packed__))
+#else
+#define FORCE_PACKED
+#endif
+
 typedef struct tagITFILEHEADER
 {
 	uint32_t id;			// 0x4D504D49
@@ -143,19 +146,6 @@ typedef struct MODMIDICFG
 
 // PUBLIC DATA DEFINITIONS -------------------------------------------------
 
-CVAR(Int,  mod_samplerate,				0,	   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
-CVAR(Int,  mod_volramp,					2,	   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
-CVAR(Int,  mod_interp,					DUMB_LQ_CUBIC,	CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
-CVAR(Bool, mod_autochip,				false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
-CVAR(Int,  mod_autochip_size_force,		100,   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
-CVAR(Int,  mod_autochip_size_scan,		500,   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
-CVAR(Int,  mod_autochip_scan_threshold, 12,	   CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
-CUSTOM_CVAR(Float, mod_dumb_mastervolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
-{
-	if (self < 0.5f) self = 0.5f;
-	else if (self > 16.f) self = 16.f;
-}
-
 // PRIVATE DATA DEFINITIONS ------------------------------------------------
 
 // CODE --------------------------------------------------------------------
@@ -205,12 +195,17 @@ static void ReadDUH(DUH * duh, DumbSong *info, bool meta, bool dos)
 		}
 	}
 
-	info->Codec = duh_get_tag(duh, "FORMAT");
-	info->TrackerVersion = duh_get_tag(duh, "TRACKERVERSION");
-	info->FormatVersion = duh_get_tag(duh, "FORMATVERSION");
+	// std::string does not like nullptr assignments. Was this really necessary? :(
+	auto a = duh_get_tag(duh, "FORMAT");
+	if (a) info->Codec = a;
+	a = duh_get_tag(duh, "TRACKERVERSION");
+	if (a) info->TrackerVersion = a;
+	a = duh_get_tag(duh, "FORMATVERSION");
+	if (a) info->FormatVersion = a;
+
 
 #if 0
-	FString name;
+	std::string name;
 
 	if (itsd->n_samples)
 	{
@@ -276,12 +271,12 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, DumbSong *info, bool meta
 		LittleShort(pifh->smpnum)*4 +
 		LittleShort(pifh->patnum)*4 > size) return false;
 
-	FString ver;
+	char ver[40];
 
-	ver.Format("IT v%u.%02x", LittleShort(pifh->cmwt) >> 8, LittleShort(pifh->cmwt) & 255);
+	snprintf(ver, 40, "IT v%u.%02x", LittleShort(pifh->cmwt) >> 8, LittleShort(pifh->cmwt) & 255);
 	info->Codec = ver;
 
-	ver.Format("%u.%02x", LittleShort(pifh->cwtv) >> 8, LittleShort(pifh->cwtv) & 255);
+	snprintf(ver, 40, "%u.%02x", LittleShort(pifh->cwtv) >> 8, LittleShort(pifh->cwtv) & 255);
 	info->TrackerVersion = ver;
 
 	//if ( pifh->smpnum ) info.info_set_int( field_samples, LittleShort(pifh->smpnum) );
@@ -296,7 +291,7 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, DumbSong *info, bool meta
 	unsigned msgend = msgoffset + LittleShort(pifh->msglength);
 
 	uint32_t * offset;
-//	FString name;
+//	std::string name;
 	
 	if (meta)
 	{
@@ -466,7 +461,7 @@ static bool ReadIT(const uint8_t * ptr, unsigned size, DumbSong *info, bool meta
 	if ( meta && ( LittleShort(pifh->special) & 1 ) && ( msgend - msgoffset ) && ( msgend < size ) )
 	{
 		const char * str = (const char *) ptr + msgoffset;
-		FString msg(str);
+		std::string msg(str);
 		//info.meta_add( field_comment, string_utf8_from_it_multiline( msg ) );
 	}
 
@@ -558,7 +553,7 @@ static DUMBFILE_SYSTEM mem_dfs = {
 //
 //==========================================================================
 
-DUMBFILE *dumb_read_allfile(dumbfile_mem_status *filestate, uint8_t *start, FileReader &reader, int lenhave, int lenfull)
+DUMBFILE *dumb_read_allfile(dumbfile_mem_status *filestate, uint8_t *start, MusicIO::FileInterface *reader, int lenhave, int lenfull)
 {
 	filestate->size = lenfull;
 	filestate->offset = 0;
@@ -568,7 +563,7 @@ DUMBFILE *dumb_read_allfile(dumbfile_mem_status *filestate, uint8_t *start, File
     {
         uint8_t *mem = new uint8_t[lenfull];
         memcpy(mem, start, lenhave);
-        if (reader.Read(mem + lenhave, lenfull - lenhave) != (lenfull - lenhave))
+        if (reader->read(mem + lenhave, lenfull - lenhave) != (lenfull - lenhave))
         {
             delete[] mem;
             return NULL;
@@ -587,12 +582,12 @@ DUMBFILE *dumb_read_allfile(dumbfile_mem_status *filestate, uint8_t *start, File
 //
 //==========================================================================
 
-static void MOD_SetAutoChip(DUH *duh)
+static void MOD_SetAutoChip(DUH *duh, DumbConfig *config)
 {
-	int size_force = mod_autochip_size_force;
-	int size_scan = mod_autochip_size_scan;
-	int scan_threshold_8 = ((mod_autochip_scan_threshold * 0x100) + 50) / 100;
-	int scan_threshold_16 = ((mod_autochip_scan_threshold * 0x10000) + 50) / 100;
+	int size_force = config->mod_autochip_size_force;
+	int size_scan = config->mod_autochip_size_scan;
+	int scan_threshold_8 = ((config->mod_autochip_scan_threshold * 0x100) + 50) / 100;
+	int scan_threshold_16 = ((config->mod_autochip_scan_threshold * 0x10000) + 50) / 100;
 	DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh);
 
 	if (itsd)
@@ -773,7 +768,7 @@ static void MOD_SetAutoChip(DUH *duh)
 //
 //==========================================================================
 
-StreamSource *MOD_OpenSong(FileReader &reader)
+StreamSource* MOD_OpenSong(MusicIO::FileInterface *reader, DumbConfig* config, int samplerate)
 {
 	DUH *duh = 0;
 	int headsize;
@@ -789,14 +784,16 @@ StreamSource *MOD_OpenSong(FileReader &reader)
 	bool is_it = false;
 	bool is_dos = true;
 
-    int size = (int)reader.GetLength();
-	auto fpos = reader.Tell();
+	auto fpos = reader->tell();
+	reader->seek(0, SEEK_END);
+    int size = (int)reader->tell();
+	reader->seek(fpos, SEEK_SET);
 
 	filestate.ptr = start;
 	filestate.offset = 0;
 	headsize = MIN((int)sizeof(start), size);
 
-    if (headsize != reader.Read(start, headsize))
+    if (headsize != reader->read(start, headsize))
     {
         return NULL;
     }
@@ -908,7 +905,7 @@ StreamSource *MOD_OpenSong(FileReader &reader)
 		{
 			if (!(f = dumb_read_allfile(&filestate, start, reader, headsize, size)))
 			{
-                reader.Seek(fpos, FileReader::SeekSet);
+                reader->seek(fpos, SEEK_SET);
 				return NULL;
 			}
 		}
@@ -930,11 +927,11 @@ StreamSource *MOD_OpenSong(FileReader &reader)
 	}
 	if ( duh )
 	{
-		if (mod_autochip)
+		if (config->mod_autochip)
 		{
-			MOD_SetAutoChip(duh);
+			MOD_SetAutoChip(duh, config);
 		}
-		state = new DumbSong(duh);
+		state = new DumbSong(duh, config, samplerate);
 
 		if (is_it) ReadIT(filestate.ptr, size, state, false);
 		else ReadDUH(duh, state, false, is_dos);
@@ -942,7 +939,7 @@ StreamSource *MOD_OpenSong(FileReader &reader)
 	else
 	{
 		// Reposition file pointer for other codecs to do their checks.
-        reader.Seek(fpos, FileReader::SeekSet);
+        reader->seek(fpos, SEEK_SET);
 	}
 	if (filestate.ptr != (uint8_t *)start)
 	{
@@ -983,7 +980,7 @@ bool DumbSong::GetData(void *buffer, size_t sizebytes)
 			// Convert to float
 			for (int i = 0; i < written * 2; ++i)
 			{
-				((float *)buffer)[i] = (((int *)buffer)[i] / (float)(1 << 24)) * mod_dumb_mastervolume;
+				((float *)buffer)[i] = (((int *)buffer)[i] / (float)(1 << 24)) * MasterVolume;
 			}
 		}
 		buffer = (uint8_t *)buffer + written * 8;
@@ -992,30 +989,42 @@ bool DumbSong::GetData(void *buffer, size_t sizebytes)
 	return true;
 }
 
+//==========================================================================
+//
+// ChangeSetting
+//
+//==========================================================================
+
+void DumbSong::ChangeSettingNum(const char* setting, double val)
+{
+	if (!stricmp(setting, "dumb.mastervolume"))
+		MasterVolume = (float)val;
+}
+
 //==========================================================================
 //
 // DumbSong constructor
 //
 //==========================================================================
 
-DumbSong::DumbSong(DUH *myduh)
+DumbSong::DumbSong(DUH* myduh, DumbConfig* config, int samplerate)
 {
-	dumb_decode_vorbis = dumb_decode_vorbis_;
 	duh = myduh;
 	sr = NULL;
 	eof = false;
-	interp = mod_interp;
-	volramp = mod_volramp;
+	interp = config->mod_interp;
+	volramp = config->mod_volramp;
 	written = 0;
 	length = 0;
 	start_order = 0;
-	if (mod_samplerate != 0)
+	MasterVolume = (float)config->mod_dumb_mastervolume;
+	if (config->mod_samplerate != 0)
 	{
-		srate = mod_samplerate;
+		srate = config->mod_samplerate;
 	}
 	else
 	{
-		srate = (int)GSnd->GetOutputRate();
+		srate = samplerate;
 	}
 	delta = 65536.0 / srate;
 }
@@ -1186,12 +1195,11 @@ retry:
 //
 //==========================================================================
 
-FString DumbSong::GetStats()
+std::string DumbSong::GetStats()
 {
 	//return StreamSong::GetStats();
 	DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr);
 	DUMB_IT_SIGDATA *itsd = duh_get_it_sigdata(duh);
-	FString out;
 
 	int channels = 0;
 	for (int i = 0; i < DUMB_IT_N_CHANNELS; i++)
@@ -1206,12 +1214,13 @@ FString DumbSong::GetStats()
 
 	if (itsr == NULL || itsd == NULL)
 	{
-		out = "Problem getting stats";
+		return "Problem getting stats";
 	}
 	else
 	{
-		out.Format("%s, Order:%3d/%d Patt:%2d/%d Row:%2d/%2d Chan:%2d/%2d Speed:%2d Tempo:%3d",
-			Codec.GetChars(),
+		char out[120];
+		snprintf(out, 120, "%s, Order:%3d/%d Patt:%2d/%d Row:%2d/%2d Chan:%2d/%2d Speed:%2d Tempo:%3d",
+			Codec.c_str(),
 			itsr->order, NumOrders,
 			(itsd->order && itsr->order < itsd->n_orders ? itsd->order[itsr->order] : 0), NumPatterns,
 			itsr->row, itsr->n_rows,
@@ -1219,7 +1228,7 @@ FString DumbSong::GetStats()
 			itsr->speed,
 			itsr->tempo
 			);
+		return out;
 	}
-	return out;
 }
 
diff --git a/src/sound/musicformats/music_gme.cpp b/src/sound/musicformats/music_gme.cpp
index 4f87b1c90..23b61d293 100644
--- a/src/sound/musicformats/music_gme.cpp
+++ b/src/sound/musicformats/music_gme.cpp
@@ -43,6 +43,7 @@
 #include <mutex>
 #include "v_text.h"
 #include "templates.h"
+#include "streamsource.h"
 
 // MACROS ------------------------------------------------------------------
 
@@ -56,7 +57,7 @@ public:
 	bool SetSubsong(int subsong) override;
 	bool Start() override;
 	void ChangeSettingNum(const char *name, double val) override;
-	FString GetStats() override;
+	std::string GetStats() override;
 	bool GetData(void *buffer, size_t len) override;
 	SoundStreamInfo GetFormat() override;
 
@@ -276,17 +277,15 @@ bool GMESong::StartTrack(int track, bool getcritsec)
 //
 //==========================================================================
 
-FString GMESong::GetStats()
+std::string GMESong::GetStats()
 {
-	FString out;
+	char out[80];
 
 	if (TrackInfo != NULL)
 	{
 		int time = gme_tell(Emu);
-		out.Format(
-			"Track: " TEXTCOLOR_YELLOW "%d" TEXTCOLOR_NORMAL
-			"  Time:" TEXTCOLOR_YELLOW "%3d:%02d:%03d" TEXTCOLOR_NORMAL
-			"  System: " TEXTCOLOR_YELLOW "%s" TEXTCOLOR_NORMAL,
+		snprintf(out, 80, 
+			"Track: %d  Time: %3d:%02d:%03d  System: %s",
 			CurrTrack,
 			time/60000,
 			(time/1000) % 60,
diff --git a/src/sound/musicformats/music_libsndfile.cpp b/src/sound/musicformats/music_libsndfile.cpp
index 7c1e3588b..7c7f505ad 100644
--- a/src/sound/musicformats/music_libsndfile.cpp
+++ b/src/sound/musicformats/music_libsndfile.cpp
@@ -39,6 +39,7 @@
 #include "v_text.h"
 #include "templates.h"
 #include "m_fixed.h"
+#include "streamsource.h"
 
 // MACROS ------------------------------------------------------------------
 
@@ -49,7 +50,7 @@ class SndFileSong : public StreamSource
 public:
 	SndFileSong(FileReader &reader, SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass);
 	~SndFileSong();
-	FString GetStats() override;
+	std::string GetStats() override;
 	SoundStreamInfo GetFormat() override;
 	bool GetData(void *buffer, size_t len) override;
 	
@@ -328,18 +329,17 @@ SndFileSong::~SndFileSong()
 //
 //==========================================================================
 
-FString SndFileSong::GetStats()
+std::string SndFileSong::GetStats()
 {
-	FString out;
+	char out[80];
 	
 	size_t SamplePos;
 	
 	SamplePos = Decoder->getSampleOffset();
 	int time = int (SamplePos / SampleRate);
 	
-	out.Format(
-		"Track: " TEXTCOLOR_YELLOW "%s, %dHz" TEXTCOLOR_NORMAL
-		"  Time:" TEXTCOLOR_YELLOW "%02d:%02d" TEXTCOLOR_NORMAL,
+	snprintf(out, 80,
+		"Track: %s, %dHz  Time: %02d:%02d",
 		Channels == 2? "Stereo" : "Mono", SampleRate,
 		time/60,
 		time % 60);
diff --git a/src/sound/musicformats/music_opl.cpp b/src/sound/musicformats/music_opl.cpp
index 93327d7d1..35aa878fa 100644
--- a/src/sound/musicformats/music_opl.cpp
+++ b/src/sound/musicformats/music_opl.cpp
@@ -32,6 +32,7 @@
 */
 
 #include "i_musicinterns.h"
+#include "streamsource.h"
 #include "../libraries/oplsynth/oplsynth/opl.h"
 #include "../libraries/oplsynth/oplsynth/opl_mus_player.h"
 
diff --git a/src/sound/musicformats/music_stream.cpp b/src/sound/musicformats/music_stream.cpp
index ccf24700d..fdfaa52b2 100644
--- a/src/sound/musicformats/music_stream.cpp
+++ b/src/sound/musicformats/music_stream.cpp
@@ -33,6 +33,7 @@
 */
 
 #include "i_musicinterns.h"
+#include "streamsource.h"
 
 class StreamSong : public MusInfo
 {
@@ -185,7 +186,8 @@ FString StreamSong::GetStats()
 	}
 	if (m_Source != NULL)
 	{
-		s2 = m_Source->GetStats();
+		auto stat = m_Source->GetStats();
+		s2 = stat.c_str();
 	}
 	if (s1.IsEmpty() && s2.IsEmpty()) return "No song loaded\n";
 	if (s1.IsEmpty()) return s2;
diff --git a/src/sound/musicformats/music_xa.cpp b/src/sound/musicformats/music_xa.cpp
index ada0abe31..5c06d3369 100644
--- a/src/sound/musicformats/music_xa.cpp
+++ b/src/sound/musicformats/music_xa.cpp
@@ -1,4 +1,5 @@
 #include "i_musicinterns.h"
+#include "streamsource.h"
 /**
  * PlayStation XA (ADPCM) source support for MultiVoc
  * Adapted and remixed from superxa2wav