diff options
author | Torbjörn Andersson | 2008-07-06 18:37:52 +0000 |
---|---|---|
committer | Torbjörn Andersson | 2008-07-06 18:37:52 +0000 |
commit | 1cc9af6b1023d438193587171890bf2ca7d5adbf (patch) | |
tree | b972d3e52b7de613c1fb1b7c835dacbed0e540a4 | |
parent | 95ca13246eed820a3513ab91a517283a1263cc46 (diff) | |
download | scummvm-rg350-1cc9af6b1023d438193587171890bf2ca7d5adbf.tar.gz scummvm-rg350-1cc9af6b1023d438193587171890bf2ca7d5adbf.tar.bz2 scummvm-rg350-1cc9af6b1023d438193587171890bf2ca7d5adbf.zip |
Implemented Good Enough(TM) XMIDI looping. This is used by Kyrandia 2 (the
loop hack is no longer needed, and has been removed), and will be used by
Discworld.
svn-id: r32930
-rw-r--r-- | engines/kyra/sound.cpp | 15 | ||||
-rw-r--r-- | sound/midiparser_xmidi.cpp | 52 |
2 files changed, 51 insertions, 16 deletions
diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp index f56c43aabd..c768255272 100644 --- a/engines/kyra/sound.cpp +++ b/engines/kyra/sound.cpp @@ -249,21 +249,6 @@ void SoundMidiPC::close() { } void SoundMidiPC::send(uint32 b) { - // HACK: For Kyrandia, we make the simplifying assumption that a song - // either loops in its entirety, or not at all. So if we see a FOR_LOOP - // controller event, we turn on looping even if there isn't any - // corresponding NEXT_BREAK event. - // - // This is a gross over-simplification of how XMIDI handles loops. If - // anyone feels like doing a proper implementation, please refer to - // the Exult project, and do it in midiparser_xmidi.cpp - - if ((b & 0xFFF0) == 0x74B0 && _eventFromMusic) { - debugC(9, kDebugLevelMain | kDebugLevelSound, "SoundMidiPC: Looping song"); - _musicParser->property(MidiParser::mpAutoLoop, true); - return; - } - if (_passThrough) { if ((b & 0xFFF0) == 0x007BB0) return; diff --git a/sound/midiparser_xmidi.cpp b/sound/midiparser_xmidi.cpp index d2aac49351..08fdfd657b 100644 --- a/sound/midiparser_xmidi.cpp +++ b/sound/midiparser_xmidi.cpp @@ -38,6 +38,14 @@ protected: NoteTimer _notes_cache[32]; uint32 _inserted_delta; // Track simulated deltas for note-off events + struct Loop { + byte *pos; + byte repeat; + }; + + Loop _loop[4]; + int _loopCount; + protected: uint32 readVLQ2(byte * &data); void resetTracking(); @@ -83,7 +91,47 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { info.basic.param2 = 0; break; - case 0x8: case 0xA: case 0xB: case 0xE: + case 0xB: + info.basic.param1 = *(_position._play_pos++); + info.basic.param2 = *(_position._play_pos++); + + // Simplified XMIDI looping. + // + // I would really like to turn the loop events into some sort + // of NOP event (perhaps a dummy META event?), but for now we + // just pass them on to the MIDI driver. That has worked in the + // past, so it shouldn't cause any actual damage... + + if (info.basic.param1 == 0x74) { + // XMIDI_CONTROLLER_FOR_LOOP + byte *pos = _position._play_pos - 3; + if (_loopCount < 0 || _loop[_loopCount].pos != pos) { + if (_loopCount < ARRAYSIZE(_loop) - 1) + _loopCount++; + + _loop[_loopCount].pos = pos; + _loop[_loopCount].repeat = info.basic.param2; + } + } else if (info.basic.param1 == 0x75) { + // XMIDI_CONTROLLER_NEXT_BREAK + if (_loopCount >= 0) { + if (info.basic.param2 < 64) { + _loopCount--; + } + + if (_loopCount >= 0) { + _position._play_pos = _loop[_loopCount].pos; + // Repeat 0 means "loop forever". + if (_loop[_loopCount].repeat) { + if (--_loop[_loopCount].repeat == 0) + _loopCount--; + } + } + } + } + break; + + case 0x8: case 0xA: case 0xE: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); break; @@ -136,6 +184,8 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { uint32 chunk_len; char buf[32]; + _loopCount = -1; + unloadMusic(); byte *pos = data; |