mirror of
https://github.com/fabiangreffrath/woof.git
synced 2025-09-21 19:03:11 -04:00
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:
parent
fb984ea390
commit
52aa64eb8a
@ -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;
|
||||
|
||||
|
@ -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
|
||||
//
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user