commit 48868398eb0bd02cc16ba2d28828ffafbc251876
Author: ceski <56656010+ceski-1@users.noreply.github.com>
AuthorDate: Mon May 15 12:49:11 2023 -0700
Commit: ceski <56656010+ceski-1@users.noreply.github.com>
CommitDate: Tue May 16 09:36:21 2023 -0700
Add MIDI compatibility levels
---
src/i_sound.c | 1 +
src/i_sound.h | 1 +
src/i_winmusic.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++---
src/m_config.c | 7 ++
src/midifile.h | 15 ++++
src/setup/sound.c | 2 +
6 files changed, 238 insertions(+), 10 deletions(-)
diff --git a/src/i_sound.c b/src/i_sound.c
index 6505fa15..a892b86a 100644
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@ -507,6 +507,7 @@ void I_BindSoundVariables(void)
M_BindIntVariable("gus_ram_kb", &gus_ram_kb);
#ifdef _WIN32
M_BindStringVariable("winmm_midi_device", &winmm_midi_device);
+ M_BindIntVariable("winmm_complevel", &winmm_complevel);
M_BindIntVariable("winmm_reset_type", &winmm_reset_type);
M_BindIntVariable("winmm_reset_delay", &winmm_reset_delay);
#endif
diff --git a/src/i_sound.h b/src/i_sound.h
index e827d72c..7674a6b7 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -272,6 +272,7 @@ extern char *music_pack_path;
extern char *timidity_cfg_path;
#ifdef _WIN32
extern char *winmm_midi_device;
+extern int winmm_complevel;
extern int winmm_reset_type;
extern int winmm_reset_delay;
#endif
diff --git a/src/i_winmusic.c b/src/i_winmusic.c
index c60ba430..446bf538 100644
--- a/src/i_winmusic.c
+++ b/src/i_winmusic.c
@@ -34,6 +34,13 @@
#include "midifile.h"
#include "midifallback.h"
+enum
+{
+ COMP_VANILLA,
+ COMP_STANDARD,
+ COMP_FULL,
+};
+
enum
{
RESET_TYPE_NONE,
@@ -43,6 +50,7 @@ enum
};
char *winmm_midi_device = NULL;
+int winmm_complevel = COMP_VANILLA;
int winmm_reset_type = RESET_TYPE_GM;
int winmm_reset_delay = 0;
@@ -260,6 +268,15 @@ static void SendLongMsg(unsigned int delta_time, const byte *ptr,
WriteBufferPad();
}
+static void SendNullRPN(unsigned int delta_time, const midi_event_t *event)
+{
+ const byte channel = event->data.channel.channel;
+ SendShortMsg(delta_time, MIDI_EVENT_CONTROLLER, channel,
+ MIDI_CONTROLLER_RPN_LSB, MIDI_RPN_NULL);
+ SendShortMsg(0, MIDI_EVENT_CONTROLLER, channel,
+ MIDI_CONTROLLER_RPN_MSB, MIDI_RPN_NULL);
+}
+
static void SendNOPMsg(unsigned int delta_time)
{
native_event_t native_event;
@@ -383,7 +400,7 @@ static void ResetDevice(void)
case RESET_TYPE_GS:
SendLongMsg(0, gs_reset, sizeof(gs_reset));
- use_fallback = true;
+ use_fallback = (winmm_complevel != COMP_VANILLA);
break;
case RESET_TYPE_XG:
@@ -812,30 +829,109 @@ static void SendMetaMsg(unsigned int delta_time, const midi_event_t *event,
break;
case MIDI_META_MARKER:
- CheckFFLoop(event);
+ if (winmm_complevel != COMP_VANILLA)
+ {
+ CheckFFLoop(event);
+ }
+ SendNOPMsg(delta_time);
+ break;
+
+ default:
+ SendNOPMsg(delta_time);
+ break;
+ }
+}
+
+static boolean AddToBuffer_Vanilla(unsigned int delta_time,
+ const midi_event_t *event,
+ win_midi_track_t *track)
+{
+ switch ((int) event->event_type)
+ {
+ case MIDI_EVENT_SYSEX:
SendNOPMsg(delta_time);
+ return false;
+
+ case MIDI_EVENT_META:
+ SendMetaMsg(delta_time, event, track);
+ break;
+
+ case MIDI_EVENT_CONTROLLER:
+ switch (event->data.channel.param1)
+ {
+ case MIDI_CONTROLLER_BANK_SELECT_MSB:
+ case MIDI_CONTROLLER_BANK_SELECT_LSB:
+ // DMX has broken bank select support and runs in GM mode.
+ SendChannelMsg(delta_time, event, false);
+ break;
+
+ case MIDI_CONTROLLER_MODULATION:
+ case MIDI_CONTROLLER_PAN:
+ case MIDI_CONTROLLER_EXPRESSION:
+ case MIDI_CONTROLLER_HOLD1_PEDAL:
+ case MIDI_CONTROLLER_SOFT_PEDAL:
+ case MIDI_CONTROLLER_REVERB:
+ case MIDI_CONTROLLER_CHORUS:
+ case MIDI_CONTROLLER_ALL_SOUND_OFF:
+ case MIDI_CONTROLLER_ALL_NOTES_OFF:
+ SendChannelMsg(delta_time, event, true);
+ break;
+
+ case MIDI_CONTROLLER_VOLUME_MSB:
+ SendVolumeMsg(delta_time, event);
+ break;
+
+ case MIDI_CONTROLLER_RESET_ALL_CTRLS:
+ // MS GS Wavetable Synth resets volume if param2 isn't zero.
+ SendChannelMsg(delta_time, event, false);
+ break;
+
+ default:
+ SendNOPMsg(delta_time);
+ break;
+ }
+ break;
+
+ case MIDI_EVENT_NOTE_OFF:
+ case MIDI_EVENT_NOTE_ON:
+ case MIDI_EVENT_PITCH_BEND:
+ SendChannelMsg(delta_time, event, true);
+ break;
+
+ case MIDI_EVENT_PROGRAM_CHANGE:
+ SendChannelMsg(delta_time, event, false);
break;
default:
SendNOPMsg(delta_time);
break;
}
+
+ return true;
}
-static boolean AddToBuffer(unsigned int delta_time, const midi_event_t *event,
- win_midi_track_t *track)
+static boolean AddToBuffer_Standard(unsigned int delta_time,
+ const midi_event_t *event,
+ win_midi_track_t *track)
{
midi_fallback_t fallback = {FALLBACK_NONE, 0};
if (use_fallback)
{
- MIDI_CheckFallback(event, &fallback, true);
+ MIDI_CheckFallback(event, &fallback, winmm_complevel == COMP_FULL);
}
switch ((int) event->event_type)
{
case MIDI_EVENT_SYSEX:
- SendSysExMsg(delta_time, event);
+ if (winmm_complevel == COMP_FULL)
+ {
+ SendSysExMsg(delta_time, event);
+ }
+ else
+ {
+ SendNOPMsg(delta_time);
+ }
return false;
case MIDI_EVENT_META:
@@ -851,11 +947,28 @@ static boolean AddToBuffer(unsigned int delta_time, const midi_event_t *event,
return true;
}
- switch ((int)event->event_type)
+ switch ((int) event->event_type)
{
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:
+ case MIDI_CONTROLLER_EXPRESSION:
+ case MIDI_CONTROLLER_DATA_ENTRY_LSB:
+ case MIDI_CONTROLLER_HOLD1_PEDAL:
+ case MIDI_CONTROLLER_SOFT_PEDAL:
+ case MIDI_CONTROLLER_REVERB:
+ case MIDI_CONTROLLER_CHORUS:
+ case MIDI_CONTROLLER_ALL_SOUND_OFF:
+ case MIDI_CONTROLLER_ALL_NOTES_OFF:
+ case MIDI_CONTROLLER_POLY_MODE_OFF:
+ case MIDI_CONTROLLER_POLY_MODE_ON:
+ SendChannelMsg(delta_time, event, true);
+ break;
+
case MIDI_CONTROLLER_VOLUME_MSB:
if (track->emidi_volume)
{
@@ -876,6 +989,65 @@ static boolean AddToBuffer(unsigned int delta_time, const midi_event_t *event,
fallback.type != FALLBACK_BANK_LSB);
break;
+ case MIDI_CONTROLLER_NRPN_LSB:
+ case MIDI_CONTROLLER_NRPN_MSB:
+ if (winmm_complevel == COMP_FULL)
+ {
+ SendChannelMsg(delta_time, event, true);
+ }
+ else
+ {
+ // MS GS Wavetable Synth nulls RPN for any NRPN.
+ SendNullRPN(delta_time, event);
+ }
+ break;
+
+ case MIDI_CONTROLLER_RPN_LSB:
+ switch (event->data.channel.param2)
+ {
+ case MIDI_RPN_PITCH_BEND_SENS_LSB:
+ case MIDI_RPN_FINE_TUNING_LSB:
+ case MIDI_RPN_COARSE_TUNING_LSB:
+ case MIDI_RPN_NULL:
+ SendChannelMsg(delta_time, event, true);
+ break;
+
+ default:
+ if (winmm_complevel == COMP_FULL)
+ {
+ SendChannelMsg(delta_time, event, true);
+ }
+ else
+ {
+ // MS GS Wavetable Synth ignores other RPNs.
+ SendNullRPN(delta_time, event);
+ }
+ break;
+ }
+ break;
+
+ case MIDI_CONTROLLER_RPN_MSB:
+ switch (event->data.channel.param2)
+ {
+ case MIDI_RPN_MSB:
+ case MIDI_RPN_NULL:
+ SendChannelMsg(delta_time, event, true);
+ break;
+
+ default:
+ if (winmm_complevel == COMP_FULL)
+ {
+ SendChannelMsg(delta_time, event, true);
+ }
+ else
+ {
+ // MS GS Wavetable Synth ignores other RPNs.
+ SendNullRPN(delta_time, event);
+ }
+ break;
+ }
+ break;
+
case EMIDI_CONTROLLER_TRACK_DESIGNATION:
case EMIDI_CONTROLLER_TRACK_EXCLUSION:
case EMIDI_CONTROLLER_PROGRAM_CHANGE:
@@ -893,14 +1065,20 @@ static boolean AddToBuffer(unsigned int delta_time, const midi_event_t *event,
break;
default:
- SendChannelMsg(delta_time, event, true);
+ if (winmm_complevel == COMP_FULL)
+ {
+ SendChannelMsg(delta_time, event, true);
+ }
+ else
+ {
+ SendNOPMsg(delta_time);
+ }
break;
}
break;
case MIDI_EVENT_NOTE_OFF:
case MIDI_EVENT_NOTE_ON:
- case MIDI_EVENT_AFTERTOUCH:
case MIDI_EVENT_PITCH_BEND:
SendChannelMsg(delta_time, event, true);
break;
@@ -917,8 +1095,26 @@ static boolean AddToBuffer(unsigned int delta_time, const midi_event_t *event,
}
break;
+ case MIDI_EVENT_AFTERTOUCH:
+ if (winmm_complevel == COMP_FULL)
+ {
+ SendChannelMsg(delta_time, event, true);
+ }
+ else
+ {
+ SendNOPMsg(delta_time);
+ }
+ break;
+
case MIDI_EVENT_CHAN_AFTERTOUCH:
- SendChannelMsg(delta_time, event, false);
+ if (winmm_complevel == COMP_FULL)
+ {
+ SendChannelMsg(delta_time, event, false);
+ }
+ else
+ {
+ SendNOPMsg(delta_time);
+ }
break;
default:
@@ -929,6 +1125,10 @@ static boolean AddToBuffer(unsigned int delta_time, const midi_event_t *event,
return true;
}
+static boolean (*AddToBuffer)(unsigned int delta_time,
+ const midi_event_t *event,
+ win_midi_track_t *track) = AddToBuffer_Vanilla;
+
static void RestartLoop(void)
{
unsigned int i;
@@ -1181,6 +1381,8 @@ static boolean I_WIN_InitMusic(void)
hBufferReturnEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ AddToBuffer = (winmm_complevel == COMP_VANILLA) ? AddToBuffer_Vanilla
+ : AddToBuffer_Standard;
MIDI_InitFallback();
return true;
diff --git a/src/m_config.c b/src/m_config.c
index 6a07b8eb..79dcfe88 100644
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -1058,6 +1058,13 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_STRING(winmm_midi_device),
+ //!
+ // Compatibility level for native Windows MIDI, default 0. Valid values are
+ // 0 (Vanilla), 1 (Standard), 2 (Full).
+ //
+
+ CONFIG_VARIABLE_INT(winmm_complevel),
+
//!
// Reset device type for native Windows MIDI, default 1. Valid values are
// 0 (None), 1 (GM Mode), 2 (GS Mode), 3 (XG Mode).
diff --git a/src/midifile.h b/src/midifile.h
index 3d06cc28..035d6f8a 100644
--- a/src/midifile.h
+++ b/src/midifile.h
@@ -23,6 +23,12 @@ typedef struct midi_track_iter_s midi_track_iter_t;
#define MIDI_CHANNELS_PER_TRACK 16
+#define MIDI_RPN_MSB 0x00
+#define MIDI_RPN_PITCH_BEND_SENS_LSB 0x00
+#define MIDI_RPN_FINE_TUNING_LSB 0x01
+#define MIDI_RPN_COARSE_TUNING_LSB 0x02
+#define MIDI_RPN_NULL 0x7F
+
typedef enum
{
MIDI_EVENT_NOTE_OFF = 0x80,
@@ -48,20 +54,29 @@ typedef enum
MIDI_CONTROLLER_DATA_ENTRY_MSB = 0x06,
MIDI_CONTROLLER_VOLUME_MSB = 0x07,
MIDI_CONTROLLER_PAN = 0x0A,
+ MIDI_CONTROLLER_EXPRESSION = 0x0B,
MIDI_CONTROLLER_BANK_SELECT_LSB = 0x20,
MIDI_CONTROLLER_DATA_ENTRY_LSB = 0x26,
MIDI_CONTROLLER_VOLUME_LSB = 0X27,
+ MIDI_CONTROLLER_HOLD1_PEDAL = 0x40,
+ MIDI_CONTROLLER_SOFT_PEDAL = 0x43,
+
MIDI_CONTROLLER_REVERB = 0x5B,
MIDI_CONTROLLER_CHORUS = 0x5D,
+ MIDI_CONTROLLER_NRPN_LSB = 0x62,
+ MIDI_CONTROLLER_NRPN_MSB = 0x63,
MIDI_CONTROLLER_RPN_LSB = 0x64,
MIDI_CONTROLLER_RPN_MSB = 0x65,
MIDI_CONTROLLER_ALL_SOUND_OFF = 0x78,
MIDI_CONTROLLER_RESET_ALL_CTRLS = 0x79,
MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7B,
+
+ MIDI_CONTROLLER_POLY_MODE_OFF = 0x7E,
+ MIDI_CONTROLLER_POLY_MODE_ON = 0x7F,
} midi_controller_t;
typedef enum
diff --git a/src/setup/sound.c b/src/setup/sound.c
index 82200f20..77d9c162 100644
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@ -84,6 +84,7 @@ static char **midi_names;
static int midi_num_devices;
static int midi_index;
char *winmm_midi_device = NULL;
+int winmm_complevel = 0;
int winmm_reset_type = 1;
int winmm_reset_delay = 0;
#endif
@@ -357,6 +358,7 @@ void BindSoundVariables(void)
M_BindStringVariable("timidity_cfg_path", &timidity_cfg_path);
#ifdef _WIN32
M_BindStringVariable("winmm_midi_device", &winmm_midi_device);
+ M_BindIntVariable("winmm_complevel", &winmm_complevel);
M_BindIntVariable("winmm_reset_type", &winmm_reset_type);
M_BindIntVariable("winmm_reset_delay", &winmm_reset_delay);
#endif