diff --git a/src/s_sound.c b/src/s_sound.c
index 0235d8376ce791b7859ab8997153709a2b336848..83000d321c666b2f11ceb9356b26ec9f93681a79 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1466,31 +1466,38 @@ static UINT16 W_CheckForMusicDefInPwad(UINT16 wadid)
 
 void S_LoadMusicDefs(UINT16 wadnum)
 {
-	UINT16 lump;
-	char *buf;
-	char *buf2;
+	UINT16 lumpnum;
+	char *lump, *buf;
+	char *musdeftext;
 	char *stoken;
 	char *value;
+	char *textline;
 	size_t size;
 	INT32 i;
 	musicdef_t *def = NULL;
 	UINT16 line = 1; // for better error msgs
 
-	lump = W_CheckForMusicDefInPwad(wadnum);
-	if (lump == INT16_MAX)
+	lumpnum = W_CheckForMusicDefInPwad(wadnum);
+	if (lumpnum == INT16_MAX)
 		return;
 
-	buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE);
-	size = W_LumpLengthPwad(wadnum, lump);
+	lump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+	size = W_LumpLengthPwad(wadnum, lumpnum);
+
+	// Null-terminated MUSICDEF lump.
+	musdeftext = malloc(size+1);
+	if (!musdeftext)
+		I_Error("S_LoadMusicDefs: No more free memory for the parser\n");
+	M_Memcpy(musdeftext, lump, size);
+	musdeftext[size] = '\0';
 
 	// for strtok
-	buf2 = malloc(size+1);
-	if (!buf2)
-		I_Error("S_LoadMusicDefs: No more free memory\n");
-	M_Memcpy(buf2,buf,size);
-	buf2[size] = '\0';
+	buf = malloc(size+1);
+	if (!buf)
+		I_Error("S_LoadMusicDefs: No more free memory for the parser\n");
+	M_Memcpy(buf, musdeftext, size+1);
 
-	stoken = strtok (buf2, "\r\n ");
+	stoken = strtok(buf, "\r\n ");
 	// Find music def
 	while (stoken)
 	{
@@ -1562,9 +1569,47 @@ skip_lump:
 		}
 		else
 		{
-			value = strtok(NULL, "\r\n= ");
+			// If this is set true, the line was invalid.
+			boolean brokenline = false;
+
+			// Delimit only by line break.
+			value = strtok(NULL, "\r\n");
 
+			// Find the equals sign.
+			value = strchr(value, '=');
+
+			// It's not there?!
 			if (!value)
+				brokenline = true;
+			else
+			{
+				// Skip the equals sign.
+				value++;
+
+				// Now skip funny whitespace.
+				if (value[0] == '\0') // :NOTHING:
+					brokenline = true;
+				else
+					value += strspn(value, "\t ");
+			}
+
+			// If the line is valid, copy the text line from the lump data.
+			if (!brokenline)
+			{
+				// strtok returns memory that already belongs to the input string.
+				value = musdeftext + (value - buf);
+
+				// Find the length of the line.
+				size = strcspn(value, "\r\n");
+
+				// Copy the line.
+				textline = malloc(size+1);
+				if (!textline)
+					I_Error("S_LoadMusicDefs: No more free memory for text line\n");
+				M_Memcpy(textline, value, size);
+				textline[size] = '\0';
+			}
+			else
 			{
 				CONS_Alert(CONS_WARNING, "MUSICDEF: Field '%s' is missing value. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line);
 				stoken = strtok(NULL, "\r\n"); // skip end of line
@@ -1574,53 +1619,45 @@ skip_lump:
 			if (!def)
 			{
 				CONS_Alert(CONS_ERROR, "MUSICDEF: No music definition before field '%s'. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line);
-				free(buf2);
+				free(textline);
+				free(buf);
+				free(musdeftext);
 				return;
 			}
 
-			i = atoi(value);
+			i = atoi(textline);
 
 			if (!stricmp(stoken, "usage")) {
 #if 0 // Ignore for now
-				STRBUFCPY(def->usage, value);
-				for (value = def->usage; *value; value++)
-					if (*value == '_') *value = ' '; // turn _ into spaces.
+				STRBUFCPY(def->usage, textline);
 				//CONS_Printf("S_LoadMusicDefs: Set usage to '%s'\n", def->usage);
 #endif
 			} else if (!stricmp(stoken, "source")) {
 #if 0 // Ignore for now
-				STRBUFCPY(def->source, value);
-				for (value = def->source; *value; value++)
-					if (*value == '_') *value = ' '; // turn _ into spaces.
+				STRBUFCPY(def->source, textline);
 				//CONS_Printf("S_LoadMusicDefs: Set source to '%s'\n", def->usage);
 #endif
 			} else if (!stricmp(stoken, "title")) {
-				STRBUFCPY(def->title, value);
-				for (value = def->title; *value; value++)
-					if (*value == '_') *value = ' '; // turn _ into spaces.
+				STRBUFCPY(def->title, textline);
 				//CONS_Printf("S_LoadMusicDefs: Set title to '%s'\n", def->source);
 			} else if (!stricmp(stoken, "alttitle")) {
-				STRBUFCPY(def->alttitle, value);
-				for (value = def->alttitle; *value; value++)
-					if (*value == '_') *value = ' '; // turn _ into spaces.
+				STRBUFCPY(def->alttitle, textline);
 				//CONS_Printf("S_LoadMusicDefs: Set alttitle to '%s'\n", def->source);
 			} else if (!stricmp(stoken, "authors")) {
-				STRBUFCPY(def->authors, value);
-				for (value = def->authors; *value; value++)
-					if (*value == '_') *value = ' '; // turn _ into spaces.
+				STRBUFCPY(def->authors, textline);
 				//CONS_Printf("S_LoadMusicDefs: Set authors to '%s'\n", def->source);
 			} else if (!stricmp(stoken, "soundtestpage")) {
 				def->soundtestpage = (UINT8)i;
 			} else if (!stricmp(stoken, "soundtestcond")) {
 				// Convert to map number
-				if (value[0] >= 'A' && value[0] <= 'Z' && value[2] == '\0')
-					i = M_MapNumber(value[0], value[1]);
+				if (textline[0] >= 'A' && textline[0] <= 'Z' && textline[2] == '\0')
+					i = M_MapNumber(textline[0], textline[1]);
 				def->soundtestcond = (INT16)i;
 			} else if (!stricmp(stoken, "stoppingtime")) {
-				double stoppingtime = atof(value)*TICRATE;
+				double stoppingtime = atof(textline)*TICRATE;
 				def->stoppingtics = (tic_t)stoppingtime;
 			} else if (!stricmp(stoken, "bpm")) {
-				double bpm = atof(value);
+				double bpm = atof(textline);
 				fixed_t bpmf = FLOAT_TO_FIXED(bpm);
 				if (bpmf > 0)
 					def->bpm = FixedDiv((60*TICRATE)<<FRACBITS, bpmf);
@@ -1628,13 +1665,17 @@ skip_lump:
 				CONS_Alert(CONS_WARNING, "MUSICDEF: Invalid field '%s'. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line);
 			}
 
+			// Free the temporary text line from memory.
+			free(textline);
+
 skip_field:
 			stoken = strtok(NULL, "\r\n= ");
 			line++;
 		}
 	}
 
-	free(buf2);
+	free(buf);
+	free(musdeftext);
 	return;
 }
 
diff --git a/tools/musicdef-2.2.1/Makefile b/tools/musicdef-2.2.1/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..403d27b0433d81e028ccc3c65f21b28b4d3daf35
--- /dev/null
+++ b/tools/musicdef-2.2.1/Makefile
@@ -0,0 +1 @@
+musicdef-2.2.1:
diff --git a/tools/musicdef-2.2.1/musicdef-2.2.1.c b/tools/musicdef-2.2.1/musicdef-2.2.1.c
new file mode 100644
index 0000000000000000000000000000000000000000..d73f16efc2bf673b184dc25d3925aea840453e22
--- /dev/null
+++ b/tools/musicdef-2.2.1/musicdef-2.2.1.c
@@ -0,0 +1,76 @@
+/*
+Copyright 2019 James R.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#else
+#include <strings.h>
+#endif
+
+int
+main (int ac, char **av)
+{
+	char line[256];
+	char buf[256];
+	char *var;
+	char *val;
+	char *p;
+	int n;
+	(void)ac;
+	(void)av;
+	fputs(
+			"Copyright 2019 James R.\n"
+			"All rights reserved.\n"
+			"\n"
+			"Usage: musicdef-2.2.1 < old-MUSICDEF > new-MUSICDEF\n"
+			"\n"
+			,stderr);
+	while (fgets(line, sizeof line, stdin))
+	{
+		memcpy(buf, line, sizeof buf);
+		if (( var = strtok(buf, " =") ))
+		{
+			if (!(
+						strcasecmp(var, "TITLE") &&
+						strcasecmp(var, "AUTHORS")
+			)){
+				if (( val = strtok(0, "") ))
+				{
+					for (p = val; ( p = strchr(p, '_') ); )
+					{
+						n = strspn(p, "_");
+						memset(p, ' ', n);
+						p += n;
+					}
+					printf("%s %s", var, val);
+					continue;
+				}
+			}
+		}
+		fputs(line, stdout);
+	}
+	return 0;
+}