Port recent SMF changes to XMI and HMI:

- Handle SysEx messages instead of ignoring them.
- Don't lose time for unhandled events with delays.
This commit is contained in:
Randy Heit 2016-01-20 18:37:05 -06:00
parent 7f6b421e87
commit 710fa55288
3 changed files with 128 additions and 30 deletions

View file

@ -600,9 +600,9 @@ public:
protected: protected:
void Heapify(); void Heapify();
unsigned int Parent(unsigned int i) { return (i + 1u) / 2u - 1u; } unsigned int Parent(unsigned int i) const { return (i + 1u) / 2u - 1u; }
unsigned int Left(unsigned int i) { return (i + 1u) * 2u - 1u; } unsigned int Left(unsigned int i) const { return (i + 1u) * 2u - 1u; }
unsigned int Right(unsigned int i) { return (i + 1u) * 2u; } unsigned int Right(unsigned int i) const { return (i + 1u) * 2u; }
}; };
class HMISong : public MIDIStreamer class HMISong : public MIDIStreamer
@ -630,7 +630,7 @@ protected:
struct TrackInfo; struct TrackInfo;
void ProcessInitialMetaEvents (); void ProcessInitialMetaEvents ();
DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay); DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom);
TrackInfo *FindNextDue (); TrackInfo *FindNextDue ();
static DWORD ReadVarLenHMI(TrackInfo *); static DWORD ReadVarLenHMI(TrackInfo *);
@ -673,7 +673,7 @@ protected:
void AdvanceSong(DWORD time); void AdvanceSong(DWORD time);
void ProcessInitialMetaEvents(); void ProcessInitialMetaEvents();
DWORD *SendCommand (DWORD *event, EventSource track, DWORD delay); DWORD *SendCommand (DWORD *event, EventSource track, DWORD delay, ptrdiff_t room, bool &sysex_noroom);
EventSource FindNextDue(); EventSource FindNextDue();
BYTE *MusHeader; BYTE *MusHeader;

View file

@ -523,7 +523,12 @@ DWORD *HMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time)
// Play all events for this tick. // Play all events for this tick.
do do
{ {
DWORD *new_events = SendCommand(events, TrackDue, time); bool sysex_noroom = false;
DWORD *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom);
if (sysex_noroom)
{
return events;
}
TrackDue = FindNextDue(); TrackDue = FindNextDue();
if (new_events != events) if (new_events != events)
{ {
@ -568,7 +573,7 @@ void HMISong::AdvanceTracks(DWORD time)
// //
//========================================================================== //==========================================================================
DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom)
{ {
DWORD len; DWORD len;
BYTE event, data1 = 0, data2 = 0; BYTE event, data1 = 0, data2 = 0;
@ -584,10 +589,20 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay)
return events + 3; return events + 3;
} }
sysex_noroom = false;
size_t start_p = track->TrackP;
CHECK_FINISHED CHECK_FINISHED
event = track->TrackBegin[track->TrackP++]; event = track->TrackBegin[track->TrackP++];
CHECK_FINISHED CHECK_FINISHED
// The actual event type will be filled in below. If it's not a NOP,
// the events pointer will be advanced once the actual event is written.
// Otherwise, we do it at the end of the function.
events[0] = delay;
events[1] = 0;
events[2] = MEVT_NOP << 24;
if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND && event != 0xFe) if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND && event != 0xFe)
{ {
// Normal short message // Normal short message
@ -626,17 +641,10 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay)
data2 = VolumeControllerChange(event & 15, data2); data2 = VolumeControllerChange(event & 15, data2);
} }
events[0] = delay;
events[1] = 0;
if (event != MIDI_META) if (event != MIDI_META)
{ {
events[2] = event | (data1<<8) | (data2<<16); events[2] = event | (data1<<8) | (data2<<16);
} }
else
{
events[2] = MEVT_NOP << 24;
}
events += 3;
if (ReadVarLen == ReadVarLenHMI && (event & 0x70) == (MIDI_NOTEON & 0x70)) if (ReadVarLen == ReadVarLenHMI && (event & 0x70) == (MIDI_NOTEON & 0x70))
{ // HMI note on events include the time until an implied note off event. { // HMI note on events include the time until an implied note off event.
@ -645,13 +653,41 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay)
} }
else else
{ {
// Skip SysEx events just because I don't want to bother with them. // SysEx events could potentially not have enough room in the buffer...
// The old MIDI player ignored them too, so this won't break
// anything that played before.
if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) if (event == MIDI_SYSEX || event == MIDI_SYSEXEND)
{ {
len = ReadVarLen(track); len = ReadVarLen(track);
track->TrackP += len; if (len >= (MAX_EVENTS-1)*3*4)
{ // This message will never fit. Throw it away.
track->TrackP += len;
}
else if (len + 12 >= (size_t)room * 4)
{ // Not enough room left in this buffer. Backup and wait for the next one.
track->TrackP = start_p;
sysex_noroom = true;
return events;
}
else
{
BYTE *msg = (BYTE *)&events[3];
if (event == MIDI_SYSEX)
{ // Need to add the SysEx marker to the message.
events[2] = (MEVT_LONGMSG << 24) | (len + 1);
*msg++ = MIDI_SYSEX;
}
else
{
events[2] = (MEVT_LONGMSG << 24) | len;
}
memcpy(msg, &track->TrackBegin[track->TrackP], len);
msg += len;
// Must pad with 0
while ((size_t)msg & 3)
{
*msg++ = 0;
}
track->TrackP += len;
}
} }
else if (event == MIDI_META) else if (event == MIDI_META)
{ {
@ -677,7 +713,6 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay)
events[0] = delay; events[0] = delay;
events[1] = 0; events[1] = 0;
events[2] = (MEVT_TEMPO << 24) | Tempo; events[2] = (MEVT_TEMPO << 24) | Tempo;
events += 3;
break; break;
} }
track->TrackP += len; track->TrackP += len;
@ -720,6 +755,18 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay)
{ {
track->Delay = ReadVarLen(track); track->Delay = ReadVarLen(track);
} }
// Advance events pointer unless this is a non-delaying NOP.
if (events[0] != 0 || MEVT_EVENTTYPE(events[2]) != MEVT_NOP)
{
if (MEVT_EVENTTYPE(events[2]) == MEVT_LONGMSG)
{
events += 3 + ((MEVT_EVENTPARM(events[2]) + 3) >> 2);
}
else
{
events += 3;
}
}
return events; return events;
} }

View file

@ -337,7 +337,12 @@ DWORD *XMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time)
// Play all events for this tick. // Play all events for this tick.
do do
{ {
DWORD *new_events = SendCommand(events, EventDue, time); bool sysex_noroom = false;
DWORD *new_events = SendCommand(events, EventDue, time, max_event_p - events, sysex_noroom);
if (sysex_noroom)
{
return events;
}
EventDue = FindNextDue(); EventDue = FindNextDue();
if (new_events != events) if (new_events != events)
{ {
@ -382,7 +387,7 @@ void XMISong::AdvanceSong(DWORD time)
// //
//========================================================================== //==========================================================================
DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay) DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay, ptrdiff_t room, bool &sysex_noroom)
{ {
DWORD len; DWORD len;
BYTE event, data1 = 0, data2 = 0; BYTE event, data1 = 0, data2 = 0;
@ -399,10 +404,20 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay)
TrackInfo *track = CurrSong; TrackInfo *track = CurrSong;
sysex_noroom = false;
size_t start_p = track->EventP;
CHECK_FINISHED CHECK_FINISHED
event = track->EventChunk[track->EventP++]; event = track->EventChunk[track->EventP++];
CHECK_FINISHED CHECK_FINISHED
// The actual event type will be filled in below. If it's not a NOP,
// the events pointer will be advanced once the actual event is written.
// Otherwise, we do it at the end of the function.
events[0] = delay;
events[1] = 0;
events[2] = MEVT_NOP << 24;
if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND) if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND)
{ {
// Normal short message // Normal short message
@ -499,10 +514,6 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay)
{ {
events[2] = event | (data1<<8) | (data2<<16); events[2] = event | (data1<<8) | (data2<<16);
} }
else
{
events[2] = MEVT_NOP << 24;
}
events += 3; events += 3;
@ -513,13 +524,41 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay)
} }
else else
{ {
// Skip SysEx events just because I don't want to bother with them. // SysEx events could potentially not have enough room in the buffer...
// The old MIDI player ignored them too, so this won't break
// anything that played before.
if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) if (event == MIDI_SYSEX || event == MIDI_SYSEXEND)
{ {
len = track->ReadVarLen (); len = track->ReadVarLen();
track->EventP += len; if (len >= (MAX_EVENTS-1)*3*4)
{ // This message will never fit. Throw it away.
track->EventP += len;
}
else if (len + 12 >= (size_t)room * 4)
{ // Not enough room left in this buffer. Backup and wait for the next one.
track->EventP = start_p;
sysex_noroom = true;
return events;
}
else
{
BYTE *msg = (BYTE *)&events[3];
if (event == MIDI_SYSEX)
{ // Need to add the SysEx marker to the message.
events[2] = (MEVT_LONGMSG << 24) | (len + 1);
*msg++ = MIDI_SYSEX;
}
else
{
events[2] = (MEVT_LONGMSG << 24) | len;
}
memcpy(msg, &track->EventChunk[track->EventP++], len);
msg += len;
// Must pad with 0
while ((size_t)msg & 3)
{
*msg++ = 0;
}
track->EventP += len;
}
} }
else if (event == MIDI_META) else if (event == MIDI_META)
{ {
@ -551,6 +590,18 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay)
{ {
track->Delay = track->ReadDelay(); track->Delay = track->ReadDelay();
} }
// Advance events pointer unless this is a non-delaying NOP.
if (events[0] != 0 || MEVT_EVENTTYPE(events[2]) != MEVT_NOP)
{
if (MEVT_EVENTTYPE(events[2]) == MEVT_LONGMSG)
{
events += 3 + ((MEVT_EVENTPARM(events[2]) + 3) >> 2);
}
else
{
events += 3;
}
}
return events; return events;
} }