mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-02 14:51:40 +00:00
MIDI: Fix MIDI parser tracker overflow
The MIDI parser tracks the number of ticks and microseconds since the start of playback of the current track. If a track loops infinitely, the tracker eventually overflows (after about 71 minutes). The parser would then go haywire, sending out the MIDI events at full speed and not terminating hanging notes, resulting in loads of polyphony overrun errors. This does not affect looping using jumpToTick or autoLoop, because that resets the tracker. This change fixes this for the XMIDI parser. processEvent implementations can now set a bool flag on the event, indicating that the event has caused a loop. The MIDI parser will then reset the ticks and microseconds on the tracker to 0, maintaining the deltas between the previous event values and current values. The absolute number of ticks and microseconds since track start do not seem to be needed anywhere; only the difference with the previous event is relevant. This should fix bugs #6275 and #4354.
This commit is contained in:
parent
5da83d8ea0
commit
da64fdc3d1
@ -211,6 +211,7 @@ void MidiParser::onTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
bool loopEvent = false;
|
||||
while (!_abortParse) {
|
||||
EventInfo &info = _nextEvent;
|
||||
|
||||
@ -219,7 +220,6 @@ void MidiParser::onTimer() {
|
||||
break;
|
||||
|
||||
// Process the next info.
|
||||
_position._lastEventTick += info.delta;
|
||||
if (info.event < 0x80) {
|
||||
warning("Bad command or running status %02X", info.event);
|
||||
_position._playPos = 0;
|
||||
@ -241,8 +241,11 @@ void MidiParser::onTimer() {
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
loopEvent |= info.loop;
|
||||
|
||||
if (!_abortParse) {
|
||||
_position._lastEventTime = eventTime;
|
||||
_position._lastEventTick += info.delta;
|
||||
parseNextEvent(_nextEvent);
|
||||
}
|
||||
}
|
||||
@ -250,6 +253,15 @@ void MidiParser::onTimer() {
|
||||
if (!_abortParse) {
|
||||
_position._playTime = endTime;
|
||||
_position._playTick = (_position._playTime - _position._lastEventTime) / _psecPerTick + _position._lastEventTick;
|
||||
if (loopEvent) {
|
||||
// One of the processed events has looped (part of) the MIDI data.
|
||||
// Infinite looping will cause the tracker to overflow eventually.
|
||||
// Reset the tracker positions to prevent this from happening.
|
||||
_position._playTime -= _position._lastEventTime;
|
||||
_position._lastEventTime = 0;
|
||||
_position._playTick -= _position._lastEventTick;
|
||||
_position._lastEventTick = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,9 +104,12 @@ struct EventInfo {
|
||||
///< For Note On events, a non-zero value indicates that no Note Off event
|
||||
///< will occur, and the MidiParser will have to generate one itself.
|
||||
///< For all other events, this value should always be zero.
|
||||
bool loop; ///< Indicates that this event loops (part of) the MIDI data.
|
||||
|
||||
byte channel() const { return event & 0x0F; } ///< Separates the MIDI channel from the event.
|
||||
byte command() const { return event >> 4; } ///< Separates the command code from the event.
|
||||
|
||||
EventInfo() : start(0), delta(0), event(0), length(0), loop(false) { basic.param1 = 0; basic.param2 = 0; ext.type = 0; ext.data = 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -166,6 +166,7 @@ bool MidiParser_XMIDI::jumpToIndex(uint8 index, bool stopNotes) {
|
||||
void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
|
||||
info.start = _position._playPos;
|
||||
info.delta = readVLQ2(_position._playPos);
|
||||
info.loop = false;
|
||||
|
||||
// Process the next event.
|
||||
info.event = *(_position._playPos++);
|
||||
@ -230,12 +231,15 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) {
|
||||
} else {
|
||||
// Repeat 0 means "loop forever".
|
||||
if (_loop[_loopCount].repeat) {
|
||||
if (--_loop[_loopCount].repeat == 0)
|
||||
if (--_loop[_loopCount].repeat == 0) {
|
||||
_loopCount--;
|
||||
else
|
||||
} else {
|
||||
_position._playPos = _loop[_loopCount].pos;
|
||||
info.loop = true;
|
||||
}
|
||||
} else {
|
||||
_position._playPos = _loop[_loopCount].pos;
|
||||
info.loop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user