mirror of
https://github.com/fabiangreffrath/woof.git
synced 2025-09-24 04:29:34 -04:00
Add MIDI compatibility levels
winmm_complevel: 0: Vanilla (Emulates DMX MPU-401 mode) 1: Standard (Emulates MS GS Synth) (Default) 2: Full (Send everything to device, including SysEx)
This commit is contained in:
parent
ba358c81a0
commit
ca8dc6c3e1
235
src/i_winmusic.c
235
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_device = "";
|
||||
int winmm_complevel = COMP_STANDARD;
|
||||
int winmm_reset_type = RESET_TYPE_GM;
|
||||
int winmm_reset_delay = 0;
|
||||
|
||||
@ -263,6 +271,14 @@ static void SendLongMsg(unsigned int delta_time, const byte *ptr,
|
||||
WriteBufferPad();
|
||||
}
|
||||
|
||||
static void SendNullRPN(unsigned int delta_time, byte 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;
|
||||
@ -375,6 +391,9 @@ static void ResetDevice(void)
|
||||
SendShortMsg(0, MIDI_EVENT_CONTROLLER, i, MIDI_CONTROLLER_ALL_SOUND_OFF, 0);
|
||||
}
|
||||
|
||||
MIDI_ResetFallback();
|
||||
use_fallback = false;
|
||||
|
||||
switch (winmm_reset_type)
|
||||
{
|
||||
case RESET_TYPE_NONE:
|
||||
@ -387,8 +406,7 @@ static void ResetDevice(void)
|
||||
|
||||
case RESET_TYPE_GS:
|
||||
SendLongMsg(0, gs_reset, sizeof(gs_reset));
|
||||
MIDI_ResetFallback();
|
||||
use_fallback = true;
|
||||
use_fallback = (winmm_complevel != COMP_VANILLA);
|
||||
break;
|
||||
|
||||
case RESET_TYPE_XG:
|
||||
@ -590,7 +608,7 @@ static void SendSysExMsg(unsigned int delta_time, const midi_event_t *event)
|
||||
// Disable instrument fallback and give priority to MIDI file. Fallback
|
||||
// assumes GS (SC-55 level) and the MIDI file could be GM, GM2, XG, or
|
||||
// GS (SC-88 or higher). Preserve the composer's intent.
|
||||
if (winmm_reset_type == RESET_TYPE_GS)
|
||||
if (use_fallback)
|
||||
{
|
||||
MIDI_ResetFallback();
|
||||
use_fallback = false;
|
||||
@ -806,7 +824,10 @@ 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;
|
||||
|
||||
@ -816,20 +837,96 @@ static void SendMetaMsg(unsigned int delta_time, const midi_event_t *event,
|
||||
}
|
||||
}
|
||||
|
||||
static boolean AddToBuffer(unsigned int delta_time, const midi_event_t *event,
|
||||
win_midi_track_t *track)
|
||||
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_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:
|
||||
@ -850,6 +947,23 @@ static boolean AddToBuffer(unsigned int delta_time, 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:
|
||||
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)
|
||||
{
|
||||
@ -870,6 +984,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->data.channel.channel);
|
||||
}
|
||||
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->data.channel.channel);
|
||||
}
|
||||
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->data.channel.channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EMIDI_CONTROLLER_TRACK_DESIGNATION:
|
||||
case EMIDI_CONTROLLER_TRACK_EXCLUSION:
|
||||
case EMIDI_CONTROLLER_PROGRAM_CHANGE:
|
||||
@ -887,14 +1060,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;
|
||||
@ -911,8 +1090,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:
|
||||
@ -923,6 +1120,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_Standard;
|
||||
|
||||
static void RestartLoop(void)
|
||||
{
|
||||
unsigned int i;
|
||||
@ -1194,15 +1395,9 @@ static boolean I_WIN_InitMusic(int device)
|
||||
hBufferReturnEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if (winmm_reset_type == RESET_TYPE_GS)
|
||||
{
|
||||
MIDI_InitFallback();
|
||||
use_fallback = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
use_fallback = false;
|
||||
}
|
||||
AddToBuffer = (winmm_complevel == COMP_VANILLA) ? AddToBuffer_Vanilla
|
||||
: AddToBuffer_Standard;
|
||||
MIDI_InitFallback();
|
||||
|
||||
printf("Windows MIDI Init: Using '%s'.\n", winmm_device);
|
||||
|
||||
|
@ -93,6 +93,7 @@ extern int mus_gain;
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
extern char *winmm_device;
|
||||
extern int winmm_complevel;
|
||||
extern int winmm_reset_type;
|
||||
extern int winmm_reset_delay;
|
||||
#endif
|
||||
@ -505,6 +506,13 @@ default_t defaults[] = {
|
||||
"Native MIDI device"
|
||||
},
|
||||
|
||||
{
|
||||
"winmm_complevel",
|
||||
(config_t *) &winmm_complevel, NULL,
|
||||
{1}, {0, 2}, number, ss_none, wad_no,
|
||||
"Native MIDI compatibility level (0 = Vanilla, 1 = Standard, 2 = Full)"
|
||||
},
|
||||
|
||||
{
|
||||
"winmm_reset_type",
|
||||
(config_t *) &winmm_reset_type, NULL,
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user