diff --git a/src/s_sound.c b/src/s_sound.c
index c4c92ebf514f9bae83ed5075adb73923e98c886e..d84e20ab4dce202360dbc1a9540ade7c565bae9c 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1441,6 +1441,12 @@ static tic_t     pause_starttic;
 /// Music Definitions
 /// ------------------------
 
+enum
+{
+	MUSICDEF_220,
+	MUSICDEF_221,
+};
+
 musicdef_t soundtestsfx = {
 	"_STSFX", // prevents exactly one valid track name from being used on the sound test
 	"Sound Effects",
@@ -1472,188 +1478,164 @@ static UINT16 W_CheckForMusicDefInPwad(UINT16 wadid)
 	return INT16_MAX; // not found
 }
 
-void S_LoadMusicDefs(UINT16 wadnum)
+static void
+MusicDefStrcpy (char *p, const char *s, size_t n, int version)
 {
-	UINT16 lumpnum;
-	char *lump, *buf;
-	char *musdeftext;
-	char *stoken;
+	strlcpy(p, s, n);
+	if (version == MUSICDEF_220)
+	{
+		while (( p = strchr(p, '_') ))
+			*p++ = ' '; // turn _ into spaces.
+	}
+}
+
+static boolean
+ReadMusicDefFields (UINT16 wadnum, int line, boolean fields, char *stoken,
+		musicdef_t **defp, int *versionp)
+{
+	musicdef_t *def;
+	int version;
+
 	char *value;
 	char *textline;
-	size_t size;
-	INT32 i;
-	musicdef_t *def = NULL;
-	UINT16 line = 1; // for better error msgs
+	int i;
 
-	lumpnum = W_CheckForMusicDefInPwad(wadnum);
-	if (lumpnum == INT16_MAX)
-		return;
+	if (!stricmp(stoken, "lump"))
+	{
+		value = strtok(NULL, " ");
+		if (!value)
+		{
+			CONS_Alert(CONS_WARNING,
+					"MUSICDEF: Field '%s' is missing name. (file %s, line %d)\n",
+					stoken, wadfiles[wadnum]->filename, line);
+			return false;
+		}
+		else
+		{
+			musicdef_t *prev = NULL;
+			def = musicdefstart;
 
-	lump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-	size = W_LumpLengthPwad(wadnum, lumpnum);
+			// Search if this is a replacement
+			//CONS_Printf("S_LoadMusicDefs: Searching for song replacement...\n");
+			while (def)
+			{
+				if (!stricmp(def->name, value))
+				{
+					//CONS_Printf("S_LoadMusicDefs: Found song replacement '%s'\n", def->name);
+					break;
+				}
 
-	// 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';
+				prev = def;
+				def = def->next;
+			}
 
-	// for strtok
-	buf = malloc(size+1);
-	if (!buf)
-		I_Error("S_LoadMusicDefs: No more free memory for the parser\n");
-	M_Memcpy(buf, musdeftext, size+1);
+			// Nothing found, add to the end.
+			if (!def)
+			{
+				def = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL);
+				STRBUFCPY(def->name, value);
+				strlwr(def->name);
+				def->bpm = TICRATE<<(FRACBITS-1); // FixedDiv((60*TICRATE)<<FRACBITS, 120<<FRACBITS)
+				if (prev != NULL)
+					prev->next = def;
+				//CONS_Printf("S_LoadMusicDefs: Added song '%s'\n", def->name);
+			}
 
-	stoken = strtok(buf, "\r\n ");
-	// Find music def
-	while (stoken)
+			(*defp) = def;
+		}
+	}
+	else if (!stricmp(stoken, "version"))
 	{
-		/*if ((stoken[0] == '/' && stoken[1] == '/')
-			|| (stoken[0] == '#')) // skip comments
+		if (fields)/* is this not the first field? */
 		{
-			stoken = strtok(NULL, "\r\n"); // skip end of line
-			if (def)
-				stoken = strtok(NULL, "\r\n= ");
-			else
-				stoken = strtok(NULL, "\r\n ");
-			line++;
+			CONS_Alert(CONS_WARNING,
+					"MUSICDEF: Field '%s' must come first. (file %s, line %d)\n",
+					stoken, wadfiles[wadnum]->filename, line);
+			return false;
 		}
-		else*/ if (!stricmp(stoken, "lump"))
+		else
 		{
-			value = strtok(NULL, "\r\n ");
-
+			value = strtok(NULL, " ");
 			if (!value)
 			{
-				CONS_Alert(CONS_WARNING, "MUSICDEF: Lump '%s' is missing name. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line);
-				stoken = strtok(NULL, "\r\n"); // skip end of line
-				goto skip_lump;
+				CONS_Alert(CONS_WARNING,
+						"MUSICDEF: Field '%s' is missing version. (file %s, line %d)\n",
+						stoken, wadfiles[wadnum]->filename, line);
+				return false;
 			}
-
-			// No existing musicdefs
-			/*if (!musicdefstart)
+			else
 			{
-				musicdefstart = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL);
-				STRBUFCPY(musicdefstart->name, value);
-				strlwr(musicdefstart->name);
-				def = musicdefstart;
-				//CONS_Printf("S_LoadMusicDefs: Initialized musicdef w/ song '%s'\n", def->name);
+				if (strcasecmp(value, "2.2.0"))
+					(*versionp) = MUSICDEF_221;
 			}
-			else*/
-			{
-				musicdef_t *prev = NULL;
-				def = musicdefstart;
-
-				// Search if this is a replacement
-				//CONS_Printf("S_LoadMusicDefs: Searching for song replacement...\n");
-				while (def)
-				{
-					if (!stricmp(def->name, value))
-					{
-						//CONS_Printf("S_LoadMusicDefs: Found song replacement '%s'\n", def->name);
-						break;
-					}
+		}
+	}
+	else
+	{
+		version = (*versionp);
 
-					prev = def;
-					def = def->next;
-				}
+		if (version == MUSICDEF_220)
+			value = strtok(NULL, " =");
+		else
+		{
+			value = strtok(NULL, "");
 
-				// Nothing found, add to the end.
-				if (!def)
-				{
-					def = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL);
-					STRBUFCPY(def->name, value);
-					strlwr(def->name);
-					def->bpm = TICRATE<<(FRACBITS-1); // FixedDiv((60*TICRATE)<<FRACBITS, 120<<FRACBITS)
-					if (prev != NULL)
-						prev->next = def;
-					//CONS_Printf("S_LoadMusicDefs: Added song '%s'\n", def->name);
-				}
+			if (value)
+			{
+				// Find the equals sign.
+				value = strchr(value, '=');
 			}
+		}
 
-skip_lump:
-			stoken = strtok(NULL, "\r\n ");
-			line++;
+		if (!value)
+		{
+			CONS_Alert(CONS_WARNING,
+					"MUSICDEF: Field '%s' is missing value. (file %s, line %d)\n",
+					stoken, wadfiles[wadnum]->filename, line);
+			return false;
 		}
 		else
 		{
-			// If this is set true, the line was invalid.
-			boolean brokenline = false;
+			def = (*defp);
 
-			// Delimit only by line break.
-			value = strtok(NULL, "\r\n");
-
-			// Find the equals sign.
-			value = strchr(value, '=');
+			if (!def)
+			{
+				CONS_Alert(CONS_ERROR,
+						"MUSICDEF: No music definition before field '%s'. (file %s, line %d)\n",
+						stoken, wadfiles[wadnum]->filename, line);
+				return false;
+			}
 
-			// It's not there?!
-			if (!value)
-				brokenline = true;
-			else
+			if (version != MUSICDEF_220)
 			{
 				// 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
-				goto skip_field;
-			}
-
-			if (!def)
-			{
-				CONS_Alert(CONS_ERROR, "MUSICDEF: No music definition before field '%s'. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line);
-				free(textline);
-				free(buf);
-				free(musdeftext);
-				return;
+				value += strspn(value, "\t ");
 			}
 
-			i = atoi(textline);
+			textline = value;
+			i = atoi(value);
 
+			/* based ignored lumps */
 			if (!stricmp(stoken, "usage")) {
 #if 0 // Ignore for now
 				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, textline);
-				//CONS_Printf("S_LoadMusicDefs: Set source to '%s'\n", def->usage);
 #endif
 			} else if (!stricmp(stoken, "title")) {
-				STRBUFCPY(def->title, textline);
-				//CONS_Printf("S_LoadMusicDefs: Set title to '%s'\n", def->source);
+				MusicDefStrcpy(def->title, textline,
+						sizeof def->title, version);
 			} else if (!stricmp(stoken, "alttitle")) {
-				STRBUFCPY(def->alttitle, textline);
-				//CONS_Printf("S_LoadMusicDefs: Set alttitle to '%s'\n", def->source);
+				MusicDefStrcpy(def->alttitle, textline,
+						sizeof def->alttitle, version);
 			} else if (!stricmp(stoken, "authors")) {
-				STRBUFCPY(def->authors, textline);
-				//CONS_Printf("S_LoadMusicDefs: Set authors to '%s'\n", def->source);
+				MusicDefStrcpy(def->authors, textline,
+						sizeof def->authors, version);
 			} else if (!stricmp(stoken, "soundtestpage")) {
 				def->soundtestpage = (UINT8)i;
 			} else if (!stricmp(stoken, "soundtestcond")) {
@@ -1670,21 +1652,90 @@ skip_lump:
 				if (bpmf > 0)
 					def->bpm = FixedDiv((60*TICRATE)<<FRACBITS, bpmf);
 			} else {
-				CONS_Alert(CONS_WARNING, "MUSICDEF: Invalid field '%s'. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line);
+				CONS_Alert(CONS_WARNING,
+						"MUSICDEF: Invalid field '%s'. (file %s, line %d)\n",
+						stoken, wadfiles[wadnum]->filename, line);
 			}
+		}
+	}
+
+	return true;
+}
+
+void S_LoadMusicDefs(UINT16 wadnum)
+{
+	UINT16 lumpnum;
+	char *lump;
+	char *musdeftext;
+	size_t size;
+
+	char *lf;
+	char *stoken;
+
+	size_t nlf;
+	size_t ncr;
+
+	musicdef_t *def = NULL;
+	int version = MUSICDEF_220;
+	int line = 1; // for better error msgs
+	boolean fields = false;
+
+	lumpnum = W_CheckForMusicDefInPwad(wadnum);
+	if (lumpnum == INT16_MAX)
+		return;
+
+	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';
+
+	// Find music def
+	stoken = musdeftext;
+	for (;;)
+	{
+		lf = strpbrk(stoken, "\r\n");
+		if (lf)
+		{
+			if (*lf == '\n')
+				nlf = 1;
+			else
+				nlf = 0;
+			*lf++ = '\0';/* now we can delimit to here */
+		}
 
-			// Free the temporary text line from memory.
-			free(textline);
+		stoken = strtok(stoken, " ");
+		if (stoken)
+		{
+			if (! ReadMusicDefFields(wadnum, line, fields, stoken,
+						&def, &version))
+				break;
+			fields = true;
+		}
+
+		if (lf)
+		{
+			do
+			{
+				line += nlf;
+				ncr = strspn(lf, "\r");/* skip CR */
+				lf += ncr;
+				nlf = strspn(lf, "\n");
+				lf += nlf;
+			}
+			while (nlf || ncr) ;
 
-skip_field:
-			stoken = strtok(NULL, "\r\n= ");
-			line++;
+			stoken = lf;/* now the next nonempty line */
 		}
+		else
+			break;/* EOF */
 	}
 
-	free(buf);
 	free(musdeftext);
-	return;
 }
 
 //