Rewrite MIDI capital tone fallback (CTF) emulation (#1666)

More accurate to original SC-55 (1.21). Some minor intentional differences to avoid silence. Enabled by default, configure with `midi_ctf` in config.
This commit is contained in:
ceski 2024-04-25 20:10:02 -07:00 committed by GitHub
parent fb984ea390
commit 52aa64eb8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 361 additions and 369 deletions

View File

@ -60,6 +60,7 @@ enum
int midi_complevel = COMP_STANDARD;
int midi_reset_type = RESET_TYPE_GM;
int midi_reset_delay = -1;
boolean midi_ctf = true;
static const byte gm_system_on[] =
{
@ -86,7 +87,7 @@ static const byte ff_loopEnd[] =
'l', 'o', 'o', 'p', 'E', 'n', 'd'
};
static boolean use_fallback;
static boolean allow_bank_select;
static byte channel_volume[MIDI_CHANNELS_PER_TRACK];
static float volume_factor = 0.0f;
@ -212,28 +213,64 @@ static void SendControlChange(byte channel, byte number, byte value)
static void SendProgramChange(byte channel, byte program)
{
const byte message[] = {MIDI_EVENT_PROGRAM_CHANGE | channel, program};
byte message[] = {MIDI_EVENT_PROGRAM_CHANGE | channel, program};
if (midi_ctf)
{
const midi_fallback_t fallback = MIDI_ProgramFallback(channel, program);
if (fallback.type == FALLBACK_DRUMS)
{
message[1] = fallback.value;
}
else if (fallback.type == FALLBACK_BANK_MSB)
{
SendControlChange(channel, MIDI_CONTROLLER_BANK_SELECT_MSB,
fallback.value);
}
}
MIDI_SendShortMsg(message, sizeof(message));
}
static void SendProgramChangeCTF(byte channel, byte program,
const midi_fallback_t *fallback)
static void SendBankSelectMSB(byte channel, byte value)
{
switch (fallback->type)
if (allow_bank_select)
{
case FALLBACK_DRUMS:
SendProgramChange(channel, fallback->value);
break;
case FALLBACK_BANK_MSB:
SendControlChange(channel, MIDI_CONTROLLER_BANK_SELECT_MSB,
fallback->value);
// Fall through.
default:
SendProgramChange(channel, program);
break;
if (midi_ctf)
{
MIDI_UpdateBankMSB(channel, value);
}
}
else
{
value = 0;
}
SendControlChange(channel, MIDI_CONTROLLER_BANK_SELECT_MSB, value);
}
static void SendBankSelectLSB(byte channel, byte value)
{
if (allow_bank_select)
{
if (midi_ctf)
{
const midi_fallback_t fallback =
MIDI_BankLSBFallback(channel, value);
if (fallback.type == FALLBACK_BANK_LSB)
{
value = fallback.value;
}
}
}
else
{
value = 0;
}
SendControlChange(channel, MIDI_CONTROLLER_BANK_SELECT_LSB, value);
}
// Writes an RPN message set to NULL (0x7F). Prevents accidental data entry.
@ -388,8 +425,10 @@ static void ResetDevice(void)
// For notes/sound off called prior to this function.
ResetDelayBytes(96);
MIDI_ResetFallback();
use_fallback = false;
if (midi_ctf)
{
MIDI_ResetFallback();
}
switch (midi_reset_type)
{
@ -401,7 +440,6 @@ static void ResetDevice(void)
case RESET_TYPE_GS:
MIDI_SendLongMsg(gs_reset, sizeof(gs_reset));
ResetDelayBytes(sizeof(gs_reset));
use_fallback = (midi_complevel != COMP_VANILLA);
break;
case RESET_TYPE_XG:
@ -454,16 +492,21 @@ static void SendSysExMsg(const midi_event_t *event)
switch (event->data.sysex.type)
{
case MIDI_SYSEX_RESET:
if (use_fallback)
if (midi_ctf)
{
MIDI_ResetFallback();
use_fallback = false;
}
MIDI_SendLongMsg(data, length);
ResetVolume();
break;
case MIDI_SYSEX_RHYTHM_PART:
if (midi_ctf)
{
MIDI_UpdateDrumMap(event->data.sysex.channel, data[8]);
}
// Fall through.
case MIDI_SYSEX_OTHER:
MIDI_SendLongMsg(data, length);
break;
@ -507,8 +550,7 @@ static void CheckFFLoop(const midi_event_t *event)
}
}
static void SendEMIDI(const midi_event_t *event, midi_track_t *track,
const midi_fallback_t *fallback)
static void SendEMIDI(const midi_event_t *event, midi_track_t *track)
{
unsigned int i;
unsigned int flag;
@ -560,8 +602,8 @@ static void SendEMIDI(const midi_event_t *event, midi_track_t *track,
if (track->emidi_program || track->elapsed_time < ticks_per_beat)
{
track->emidi_program = true;
SendProgramChangeCTF(event->data.channel.channel,
event->data.channel.param2, fallback);
SendProgramChange(event->data.channel.channel,
event->data.channel.param2);
}
break;
@ -632,7 +674,7 @@ static void SendEMIDI(const midi_event_t *event, midi_track_t *track,
}
}
static void SendMetaMsg(const midi_event_t *event, midi_track_t *track)
static void ProcessMetaEvent(const midi_event_t *event, midi_track_t *track)
{
switch (event->data.meta.type)
{
@ -663,20 +705,27 @@ static void ProcessEvent_Vanilla(const midi_event_t *event, midi_track_t *track)
{
switch (event->event_type)
{
case MIDI_EVENT_SYSEX:
break;
case MIDI_EVENT_META:
SendMetaMsg(event, track);
ProcessMetaEvent(event, track);
break;
case MIDI_EVENT_CONTROLLER:
switch (event->data.channel.param1)
{
case MIDI_CONTROLLER_BANK_SELECT_MSB:
// DMX has broken bank select support and runs in GM mode.
// Actual behavior (MSB is okay):
//SendBankSelectMSB(event->data.channel.channel,
// event->data.channel.param2);
SendBankSelectMSB(event->data.channel.channel, 0);
break;
case MIDI_CONTROLLER_BANK_SELECT_LSB:
// DMX has broken bank select support and runs in GM mode.
SendChannelMsgZero(event);
// Actual behavior (LSB is sent as MSB!):
//SendBankSelectMSB(event->data.channel.channel,
// event->data.channel.param2);
SendBankSelectLSB(event->data.channel.channel, 0);
break;
case MIDI_CONTROLLER_MODULATION:
@ -717,7 +766,8 @@ static void ProcessEvent_Vanilla(const midi_event_t *event, midi_track_t *track)
break;
case MIDI_EVENT_PROGRAM_CHANGE:
SendChannelMsg(event, false);
SendProgramChange(event->data.channel.channel,
event->data.channel.param1);
break;
default:
@ -731,13 +781,6 @@ static void ProcessEvent_Vanilla(const midi_event_t *event, midi_track_t *track)
static void ProcessEvent_Standard(const midi_event_t *event,
midi_track_t *track)
{
midi_fallback_t fallback = {FALLBACK_NONE, 0};
if (use_fallback)
{
MIDI_CheckFallback(event, &fallback, midi_complevel == COMP_FULL);
}
switch ((int)event->event_type)
{
case MIDI_EVENT_SYSEX:
@ -748,7 +791,7 @@ static void ProcessEvent_Standard(const midi_event_t *event,
return;
case MIDI_EVENT_META:
SendMetaMsg(event, track);
ProcessMetaEvent(event, track);
return;
}
@ -762,7 +805,6 @@ static void ProcessEvent_Standard(const midi_event_t *event,
case MIDI_EVENT_CONTROLLER:
switch (event->data.channel.param1)
{
case MIDI_CONTROLLER_BANK_SELECT_MSB:
case MIDI_CONTROLLER_MODULATION:
case MIDI_CONTROLLER_DATA_ENTRY_MSB:
case MIDI_CONTROLLER_PAN:
@ -786,15 +828,14 @@ static void ProcessEvent_Standard(const midi_event_t *event,
case MIDI_CONTROLLER_VOLUME_LSB:
break;
case MIDI_CONTROLLER_BANK_SELECT_MSB:
SendBankSelectMSB(event->data.channel.channel,
event->data.channel.param2);
break;
case MIDI_CONTROLLER_BANK_SELECT_LSB:
if (fallback.type == FALLBACK_BANK_LSB)
{
SendChannelMsgZero(event);
}
else
{
SendChannelMsg(event, true);
}
SendBankSelectLSB(event->data.channel.channel,
event->data.channel.param2);
break;
case MIDI_CONTROLLER_NRPN_LSB:
@ -864,7 +905,7 @@ static void ProcessEvent_Standard(const midi_event_t *event,
case EMIDI_CONTROLLER_LOOP_END:
case EMIDI_CONTROLLER_GLOBAL_LOOP_BEGIN:
case EMIDI_CONTROLLER_GLOBAL_LOOP_END:
SendEMIDI(event, track, &fallback);
SendEMIDI(event, track);
break;
case MIDI_CONTROLLER_RESET_ALL_CTRLS:
@ -907,8 +948,8 @@ static void ProcessEvent_Standard(const midi_event_t *event,
case MIDI_EVENT_PROGRAM_CHANGE:
if (!track->emidi_program)
{
SendProgramChangeCTF(event->data.channel.channel,
event->data.channel.param1, &fallback);
SendProgramChange(event->data.channel.channel,
event->data.channel.param1);
}
break;
@ -1216,9 +1257,23 @@ static boolean I_MID_InitMusic(int device)
return false;
}
ProcessEvent = (midi_complevel == COMP_VANILLA) ? ProcessEvent_Vanilla
: ProcessEvent_Standard;
MIDI_InitFallback();
switch (midi_complevel)
{
case COMP_VANILLA:
ProcessEvent = ProcessEvent_Vanilla;
allow_bank_select = false;
break;
case COMP_STANDARD:
ProcessEvent = ProcessEvent_Standard;
allow_bank_select = (midi_reset_type != RESET_TYPE_GM);
break;
case COMP_FULL:
ProcessEvent = ProcessEvent_Standard;
allow_bank_select = true;
break;
}
music_initialized = true;

View File

@ -93,6 +93,7 @@ extern int mus_gain;
extern int midi_complevel;
extern int midi_reset_type;
extern int midi_reset_delay;
extern boolean midi_ctf;
extern int opl_gain;
extern boolean demobar;
extern boolean smoothlight;
@ -558,6 +559,13 @@ default_t defaults[] = {
"Delay after reset for native MIDI (-1 = Auto, 0 = None, 1-2000 = Milliseconds)"
},
{
"midi_ctf",
(config_t *) &midi_ctf, NULL,
{1}, {0, 1}, number, ss_none, wad_no,
"1 to fix invalid instruments by emulating SC-55 capital tone fallback"
},
//
// QOL features
//

View File

@ -15,329 +15,257 @@
// MIDI instrument fallback support
//
#include "midifallback.h"
#include "doomtype.h"
#include "i_printf.h"
#include "midifile.h"
#include <string.h>
static const byte drums_table[128] =
#include "midifallback.h"
#include "i_printf.h"
static const byte presets[128][128] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x18, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
[1] = // Variation #1
{
[38] = 1,
[57] = 1,
[60] = 1,
[80] = 1,
[81] = 1,
[98] = 1,
[102] = 1,
[104] = 1,
[120] = 1,
[121] = 1,
[122] = 1,
[123] = 1,
[124] = 1,
[125] = 1,
[126] = 1,
[127] = 1,
},
[2] = // Variation #2
{
[102] = 2,
[120] = 2,
[122] = 2,
[123] = 2,
[124] = 2,
[125] = 2,
[126] = 2,
[127] = 2,
},
[3] = // Variation #3
{
[122] = 3,
[123] = 3,
[124] = 3,
[125] = 3,
[126] = 3,
[127] = 3,
},
[4] = // Variation #4
{
[122] = 4,
[124] = 4,
[125] = 4,
[126] = 4,
},
[5] = // Variation #5
{
[122] = 5,
[124] = 5,
[125] = 5,
[126] = 5,
},
[6] = // Variation #6
{
[125] = 6,
},
[7] = // Variation #7
{
[125] = 7,
},
[8] = // Variation #8
{
[0] = 8,
[1] = 8,
[2] = 8,
[3] = 8,
[4] = 8,
[5] = 8,
[6] = 8,
[11] = 8,
[12] = 8,
[14] = 8,
[16] = 8,
[17] = 8,
[19] = 8,
[21] = 8,
[24] = 8,
[25] = 8,
[26] = 8,
[27] = 8,
[28] = 8,
[30] = 8,
[31] = 8,
[38] = 8,
[39] = 8,
[40] = 8,
[48] = 8,
[50] = 8,
[61] = 8,
[62] = 8,
[63] = 8,
[80] = 8,
[81] = 8,
[107] = 8,
[115] = 8,
[116] = 8,
[117] = 8,
[118] = 8,
[125] = 8,
},
[9] = // Variation #9
{
[14] = 9,
[118] = 9,
[125] = 9,
},
[16] = // Variation #16
{
[0] = 16,
[4] = 16,
[5] = 16,
[6] = 16,
[16] = 16,
[19] = 16,
[24] = 16,
[25] = 16,
[28] = 16,
[39] = 16,
[62] = 16,
[63] = 16,
},
[24] = // Variation #24
{
[4] = 24,
[6] = 24,
},
[32] = // Variation #32
{
[16] = 32,
[17] = 32,
[24] = 32,
[52] = 32,
},
};
static const byte drums[128] =
{
[8] = 8,
[16] = 16,
[24] = 24,
[25] = 25,
[32] = 32,
[40] = 40,
[48] = 48,
[56] = 56,
};
static byte variation[128][128];
static byte bank_msb[MIDI_CHANNELS_PER_TRACK];
static byte drum_map[MIDI_CHANNELS_PER_TRACK];
static boolean selected[MIDI_CHANNELS_PER_TRACK];
static boolean GetProgramFallback(byte idx, byte program,
midi_fallback_t *fallback)
{
if (drum_map[idx] == 0) // Normal channel
{
if (bank_msb[idx] == 0 || variation[bank_msb[idx]][program])
{
// Found a capital or variation for this bank select MSB.
selected[idx] = true;
return false;
}
fallback->type = FALLBACK_BANK_MSB;
if (!selected[idx] || bank_msb[idx] > 63)
{
// Fall to capital when no instrument has (successfully)
// selected this variation or if the variation is above 63.
fallback->value = 0;
I_Printf(VB_WARNING,
"midifallback: warning: ch=%d [bank_msb=%d prog=%d] "
"falling back to [bank_msb=%d prog=%d]",
idx, bank_msb[idx], program, fallback->value, program);
return true;
}
// A previous instrument used this variation but it's not
// valid for the current instrument. Fall to the next valid
// "sub-capital" (next variation that is a multiple of 8).
fallback->value = (bank_msb[idx] / 8) * 8;
while (fallback->value > 0)
{
if (variation[fallback->value][program])
{
break;
}
fallback->value -= 8;
}
I_Printf(VB_WARNING,
"midifallback: warning: ch=%d [bank_msb=%d prog=%d] "
"falling back to [bank_msb=%d prog=%d]",
idx, bank_msb[idx], program, fallback->value, program);
return true;
}
else // Drums channel
{
if (program != drums_table[program])
{
// Use drum set from drums fallback table.
// Drums 0-63 and 127: same as original SC-55 (1.00 - 1.21).
// Drums 64-126: standard drum set (0).
fallback->type = FALLBACK_DRUMS;
fallback->value = drums_table[program];
selected[idx] = true;
I_Printf(VB_WARNING,
"midifallback: warning: ch=%d [prog=%d] "
"falling back to [prog=%d] (drums)",
idx, program, fallback->value);
return true;
}
}
return false;
}
void MIDI_CheckFallback(const midi_event_t *event, midi_fallback_t *fallback,
boolean allow_sysex)
{
byte idx;
byte program;
switch ((int)event->event_type)
{
case MIDI_EVENT_SYSEX:
if (allow_sysex && event->data.sysex.type == MIDI_SYSEX_RHYTHM_PART)
{
// GS allows drums on any channel using SysEx messages.
// The message format is:
// F0 41 10 42 12 40 <ch> 15 <map> <sum> F7
// <ch> is [11-19, 10, 1A-1F] for channels 1-16.
// <map> is 00-02 for off (normal part) or drum map 1/2.
// <sum> is checksum.
drum_map[event->data.sysex.channel] = event->data.sysex.data[8];
}
break;
case MIDI_EVENT_CONTROLLER:
idx = event->data.channel.channel;
switch (event->data.channel.param1)
{
case MIDI_CONTROLLER_BANK_SELECT_MSB:
bank_msb[idx] = event->data.channel.param2;
selected[idx] = false;
break;
case MIDI_CONTROLLER_BANK_SELECT_LSB:
selected[idx] = false;
if (event->data.channel.param2 > 0)
{
// Bank select LSB > 0 not supported. This also
// preserves user's current SC-XX map.
fallback->type = FALLBACK_BANK_LSB;
fallback->value = 0;
I_Printf(VB_WARNING,
"midifallback: warning: ch=%d [bank_lsb=%d] "
"replaced by [bank_lsb=%d]",
idx, event->data.channel.param2,
fallback->value);
return;
}
break;
case EMIDI_CONTROLLER_PROGRAM_CHANGE:
program = event->data.channel.param2;
if (GetProgramFallback(idx, program, fallback))
{
return;
}
break;
}
break;
case MIDI_EVENT_PROGRAM_CHANGE:
idx = event->data.channel.channel;
program = event->data.channel.param1;
if (GetProgramFallback(idx, program, fallback))
{
return;
}
break;
}
fallback->type = FALLBACK_NONE;
fallback->value = 0;
}
void MIDI_ResetFallback(void)
{
int i;
for (i = 0; i < MIDI_CHANNELS_PER_TRACK; i++)
{
bank_msb[i] = 0;
drum_map[i] = 0;
selected[i] = false;
}
memset(bank_msb, 0, sizeof(bank_msb));
memset(drum_map, 0, sizeof(drum_map));
// Channel 10 (index 9) is set to drum map 1 by default.
drum_map[9] = 1;
}
void MIDI_InitFallback(void)
void MIDI_UpdateBankMSB(byte idx, byte value)
{
byte program;
MIDI_ResetFallback();
// Capital
for (program = 0; program < 128; program++)
{
variation[0][program] = 1;
}
// Variation #1
variation[1][38] = 1;
variation[1][57] = 1;
variation[1][60] = 1;
variation[1][80] = 1;
variation[1][81] = 1;
variation[1][98] = 1;
variation[1][102] = 1;
variation[1][104] = 1;
variation[1][120] = 1;
variation[1][121] = 1;
variation[1][122] = 1;
variation[1][123] = 1;
variation[1][124] = 1;
variation[1][125] = 1;
variation[1][126] = 1;
variation[1][127] = 1;
// Variation #2
variation[2][102] = 1;
variation[2][120] = 1;
variation[2][122] = 1;
variation[2][123] = 1;
variation[2][124] = 1;
variation[2][125] = 1;
variation[2][126] = 1;
variation[2][127] = 1;
// Variation #3
variation[3][122] = 1;
variation[3][123] = 1;
variation[3][124] = 1;
variation[3][125] = 1;
variation[3][126] = 1;
variation[3][127] = 1;
// Variation #4
variation[4][122] = 1;
variation[4][124] = 1;
variation[4][125] = 1;
variation[4][126] = 1;
// Variation #5
variation[5][122] = 1;
variation[5][124] = 1;
variation[5][125] = 1;
variation[5][126] = 1;
// Variation #6
variation[6][125] = 1;
// Variation #7
variation[7][125] = 1;
// Variation #8
variation[8][0] = 1;
variation[8][1] = 1;
variation[8][2] = 1;
variation[8][3] = 1;
variation[8][4] = 1;
variation[8][5] = 1;
variation[8][6] = 1;
variation[8][11] = 1;
variation[8][12] = 1;
variation[8][14] = 1;
variation[8][16] = 1;
variation[8][17] = 1;
variation[8][19] = 1;
variation[8][21] = 1;
variation[8][24] = 1;
variation[8][25] = 1;
variation[8][26] = 1;
variation[8][27] = 1;
variation[8][28] = 1;
variation[8][30] = 1;
variation[8][31] = 1;
variation[8][38] = 1;
variation[8][39] = 1;
variation[8][40] = 1;
variation[8][48] = 1;
variation[8][50] = 1;
variation[8][61] = 1;
variation[8][62] = 1;
variation[8][63] = 1;
variation[8][80] = 1;
variation[8][81] = 1;
variation[8][107] = 1;
variation[8][115] = 1;
variation[8][116] = 1;
variation[8][117] = 1;
variation[8][118] = 1;
variation[8][125] = 1;
// Variation #9
variation[9][14] = 1;
variation[9][118] = 1;
variation[9][125] = 1;
// Variation #16
variation[16][0] = 1;
variation[16][4] = 1;
variation[16][5] = 1;
variation[16][6] = 1;
variation[16][16] = 1;
variation[16][19] = 1;
variation[16][24] = 1;
variation[16][25] = 1;
variation[16][28] = 1;
variation[16][39] = 1;
variation[16][62] = 1;
variation[16][63] = 1;
// Variation #24
variation[24][4] = 1;
variation[24][6] = 1;
// Variation #32
variation[32][16] = 1;
variation[32][17] = 1;
variation[32][24] = 1;
variation[32][52] = 1;
// CM-64 Map (PCM)
for (program = 0; program < 64; program++)
{
variation[126][program] = 1;
}
// CM-64 Map (LA)
for (program = 0; program < 128; program++)
{
variation[127][program] = 1;
}
bank_msb[idx] = value;
}
void MIDI_UpdateDrumMap(byte idx, byte value)
{
drum_map[idx] = value;
}
midi_fallback_t MIDI_BankLSBFallback(byte idx, byte value)
{
midi_fallback_t fallback;
if (value == 0)
{
// Bank select LSB is already zero. No fallback required.
fallback.type = FALLBACK_NONE;
fallback.value = 0;
}
else
{
// Bank select LSB is not supported.
fallback.type = FALLBACK_BANK_LSB;
fallback.value = 0;
I_Printf(VB_DEBUG, "midifallback: ch=%d [lsb=%d] to [lsb=%d]",
idx, value, fallback.value);
}
return fallback;
}
midi_fallback_t MIDI_ProgramFallback(byte idx, byte program)
{
midi_fallback_t fallback;
if (drum_map[idx] == 0) // Normal channel
{
const byte variation = bank_msb[idx];
if (variation == 0 || variation == presets[variation][program])
{
// Found a capital or variation. No fallback required.
fallback.type = FALLBACK_NONE;
fallback.value = 0;
}
else
{
fallback.type = FALLBACK_BANK_MSB;
if (variation > 63 || program > 119)
{
// Fall to capital.
fallback.value = 0;
}
else
{
// Fall to sub-capital (next multiple of 8).
fallback.value = presets[variation & ~7][program];
}
I_Printf(VB_DEBUG, "midifallback: ch=%d pc=%d [msb=%d] to [msb=%d]",
idx, program, variation, fallback.value);
}
}
else // Drums Channel
{
if (program == 0 || program == drums[program])
{
// Found a drum set. No fallback required.
fallback.type = FALLBACK_NONE;
fallback.value = 0;
}
else
{
// Fall to sub-drum set (next multiple of 8).
fallback.type = FALLBACK_DRUMS;
fallback.value = drums[program & ~7];
I_Printf(VB_DEBUG, "midifallback: ch=%d (drums) [pc=%d] to [pc=%d]",
idx, program, fallback.value);
}
}
return fallback;
}

View File

@ -35,9 +35,10 @@ typedef struct midi_fallback_t
byte value;
} midi_fallback_t;
void MIDI_CheckFallback(const midi_event_t *event, midi_fallback_t *fallback,
boolean allow_sysex);
void MIDI_ResetFallback(void);
void MIDI_InitFallback(void);
void MIDI_UpdateBankMSB(byte idx, byte value);
void MIDI_UpdateDrumMap(byte idx, byte value);
midi_fallback_t MIDI_BankLSBFallback(byte idx, byte value);
midi_fallback_t MIDI_ProgramFallback(byte idx, byte program);
#endif // MIDIFALLBACK_H