shithub: choc

Download patch

ref: 9e9bb23d201c68a63690f96ba0c73110f4d999f4
parent: e4d9b85aa45fe31cb9b18a1ac8e9a95c7bc6ea5b
author: Simon Howard <[email protected]>
date: Tue Oct 7 20:12:50 EDT 2014

midi: Fix "D_DDTBLU disease".

The Doom II MAP14/MAP20 music has a hanging note at the end of the
track that is never turned off. If this is not reset when the track
loops, there is a continuous (and annoying) drone sound throughout
the next iteration of the song. Some information is here:

http://www.doomworld.com/vb/source-ports/66802-the-d-ddtblu-disease/

This changes the mus2mid code to generate an "all notes off"
controller event at the start of the MIDI track. This is specifically
done at the start and not the end of the track because otherwise the
looping of tracks like D_RUNNING is affected.

Thanks to a whole host of people for help on this: @plumsinus for
reporting the bug, @bradharding for devising a fix as part of Doom
Retro, and Quasar for feedback and his own fix to the Eternity Engine.
This fixes #412.

--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -961,6 +961,21 @@
     }
 }
 
+// Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event.
+static void AllNotesOff(opl_channel_data_t *channel, unsigned int param)
+{
+    unsigned int i;
+
+    for (i = 0; i < OPL_NUM_VOICES; ++i)
+    {
+        if (voices[i].channel == channel)
+        {
+            VoiceKeyOff(&voices[i]);
+            ReleaseVoice(&voices[i]);
+        }
+    }
+}
+
 static void ControllerEvent(opl_track_data_t *track, midi_event_t *event)
 {
     unsigned int controller;
@@ -982,6 +997,10 @@
     {
         case MIDI_CONTROLLER_MAIN_VOLUME:
             SetChannelVolume(channel, param);
+            break;
+
+        case MIDI_CONTROLLER_ALL_NOTES_OFF:
+            AllNotesOff(channel, param);
             break;
 
         default:
--- a/src/midifile.h
+++ b/src/midifile.h
@@ -48,7 +48,9 @@
     MIDI_CONTROLLER_DATA_ENTRY      = 0x5,
 
     MIDI_CONTROLLER_MAIN_VOLUME     = 0x7,
-    MIDI_CONTROLLER_PAN             = 0xa
+    MIDI_CONTROLLER_PAN             = 0xa,
+
+    MIDI_CONTROLLER_ALL_NOTES_OFF   = 0x7b,
 } midi_controller_t;
 
 typedef enum
--- a/src/mus2mid.c
+++ b/src/mus2mid.c
@@ -383,7 +383,7 @@
 // Given a MUS channel number, get the MIDI channel number to use
 // in the outputted file.
 
-static int GetMIDIChannel(int mus_channel)
+static int GetMIDIChannel(int mus_channel, MEMFILE *midioutput)
 {
     // Find the MIDI channel to use for this MUS channel.
     // MUS channel 15 is the percusssion channel.
@@ -400,6 +400,12 @@
         if (channel_map[mus_channel] == -1)
         {
             channel_map[mus_channel] = AllocateMIDIChannel();
+
+            // First time using the channel, send an "all notes off"
+            // event. This fixes "The D_DDTBLU disease" described here:
+            // http://www.doomworld.com/vb/source-ports/66802-the
+            WriteChangeController_Valueless(channel_map[mus_channel], 0x7b,
+                                            midioutput);
         }
 
         return channel_map[mus_channel];
@@ -514,7 +520,7 @@
                 return true;
             }
 
-            channel = GetMIDIChannel(eventdescriptor & 0x0F);
+            channel = GetMIDIChannel(eventdescriptor & 0x0F, midioutput);
             event = eventdescriptor & 0x70;
 
             switch (event)