From 9e9bb23d201c68a63690f96ba0c73110f4d999f4 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Wed, 8 Oct 2014 00:12:50 -0400 Subject: 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. --- src/i_oplmusic.c | 19 +++++++++++++++++++ src/midifile.h | 4 +++- src/mus2mid.c | 10 ++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 5f9ab462..bcc8fe68 100644 --- a/src/i_oplmusic.c +++ b/src/i_oplmusic.c @@ -961,6 +961,21 @@ static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume) } } +// 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; @@ -984,6 +999,10 @@ static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) SetChannelVolume(channel, param); break; + case MIDI_CONTROLLER_ALL_NOTES_OFF: + AllNotesOff(channel, param); + break; + default: #ifdef OPL_MIDI_DEBUG fprintf(stderr, "Unknown MIDI controller type: %i\n", controller); diff --git a/src/midifile.h b/src/midifile.h index b539b1b5..490c2a0a 100644 --- a/src/midifile.h +++ b/src/midifile.h @@ -48,7 +48,9 @@ typedef enum 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 diff --git a/src/mus2mid.c b/src/mus2mid.c index 2bf5ef9a..3cbcb0dd 100644 --- a/src/mus2mid.c +++ b/src/mus2mid.c @@ -383,7 +383,7 @@ static int AllocateMIDIChannel(void) // 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 @@ static int GetMIDIChannel(int mus_channel) 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 @@ boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput) return true; } - channel = GetMIDIChannel(eventdescriptor & 0x0F); + channel = GetMIDIChannel(eventdescriptor & 0x0F, midioutput); event = eventdescriptor & 0x70; switch (event) -- cgit v1.2.3