commit 0812f49b460239c873fbe46f16717d6aedf04070
Author: ceski <56656010+ceski-1@users.noreply.github.com>
AuthorDate: Wed Apr 26 10:19:01 2023 -0700
Commit: GitHub <noreply@github.com>
CommitDate: Wed Apr 26 20:19:01 2023 +0300
win midi: Detect SysEx "part level" messages (#1595)
* win midi: Detect SysEx "part level" messages
* Fix formatting
* Add comment
---
src/i_winmusic.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 57 insertions(+), 2 deletions(-)
diff --git a/src/i_winmusic.c b/src/i_winmusic.c
index 7fe780d4..6fd4e014 100644
--- a/src/i_winmusic.c
+++ b/src/i_winmusic.c
@@ -503,6 +503,36 @@ static void ResetDevice(void)
}
}
+// Normally, volume is controlled by channel volume messages. Roland defined a
+// special SysEx message called "part level" that is equivalent to this. MS GS
+// Wavetable Synth ignores these messages, but other MIDI devices support them.
+
+static boolean IsPartLevel(const byte *msg, int length)
+{
+ if (length == 10 &&
+ msg[0] == 0x41 && // Roland
+ msg[2] == 0x42 && // GS
+ msg[3] == 0x12 && // DT1
+ msg[4] == 0x40 && // Address MSB
+ msg[5] >= 0x10 && // Address
+ msg[5] <= 0x1F && // Address
+ msg[6] == 0x19 && // Address LSB
+ msg[9] == 0xF7) // SysEx EOX
+ {
+ const byte checksum =
+ 128 - ((int) msg[4] + msg[5] + msg[6] + msg[7]) % 128;
+
+ if (msg[8] == checksum)
+ {
+ // GS Part Level (aka Channel Volume)
+ // 41 <dev> 42 12 40 <ch> 19 <vol> <sum> F7
+ return true;
+ }
+ }
+
+ return false;
+}
+
static boolean IsSysExReset(const byte *msg, int length)
{
if (length < 5)
@@ -713,8 +743,33 @@ static boolean AddToBuffer(unsigned int delta_time, midi_event_t *event,
switch ((int)event->event_type)
{
case MIDI_EVENT_SYSEX:
- SendSysExMsg(delta_time, event->data.sysex.data,
- event->data.sysex.length);
+ if (IsPartLevel(event->data.sysex.data, event->data.sysex.length))
+ {
+ const byte *data = event->data.sysex.data;
+ byte channel;
+
+ // Convert "block number" to a channel number.
+ if (data[5] == 0x10) // Channel 10
+ {
+ channel = 9;
+ }
+ else if (data[5] < 0x1A) // Channels 1-9
+ {
+ channel = (data[5] & 0x0F) - 1;
+ }
+ else // Channels 11-16
+ {
+ channel = data[5] & 0x0F;
+ }
+
+ // Replace SysEx part level message with channel volume message.
+ SendVolumeMsg(delta_time, channel, data[7]);
+ }
+ else
+ {
+ SendSysExMsg(delta_time, event->data.sysex.data,
+ event->data.sysex.length);
+ }
return false;
case MIDI_EVENT_META: