diff --git a/audio/midiparser.cpp b/audio/midiparser.cpp index af5b6519aa0..f1b3c4e307a 100644 --- a/audio/midiparser.cpp +++ b/audio/midiparser.cpp @@ -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; + } } } diff --git a/audio/midiparser.h b/audio/midiparser.h index 6cf8b7aa983..7dfdc1c13c8 100644 --- a/audio/midiparser.h +++ b/audio/midiparser.h @@ -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; } }; /** diff --git a/audio/midiparser_xmidi.cpp b/audio/midiparser_xmidi.cpp index 958c2f9358b..6c1d069ed6e 100644 --- a/audio/midiparser_xmidi.cpp +++ b/audio/midiparser_xmidi.cpp @@ -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; } } }