diff options
-rw-r--r-- | audio/midiparser.cpp | 138 | ||||
-rw-r--r-- | audio/midiparser.h | 6 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.cpp | 264 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.h | 6 | ||||
-rw-r--r-- | engines/sci/sound/music.cpp | 5 | ||||
-rw-r--r-- | engines/sci/sound/music.h | 1 |
6 files changed, 205 insertions, 215 deletions
diff --git a/audio/midiparser.cpp b/audio/midiparser.cpp index 9c144c2479..2454575413 100644 --- a/audio/midiparser.cpp +++ b/audio/midiparser.cpp @@ -44,7 +44,8 @@ _centerPitchWheelOnUnload(false), _sendSustainOffOnNotesOff(false), _numTracks(0), _activeTrack(255), -_abortParse(0) { +_abortParse(false), +_jumpingToTick(false) { memset(_activeNotes, 0, sizeof(_activeNotes)); memset(_tracks, 0, sizeof(_tracks)); _nextEvent.start = NULL; @@ -204,49 +205,22 @@ void MidiParser::onTimer() { return; } - if (info.event == 0xF0) { - // SysEx event - // Check for trailing 0xF7 -- if present, remove it. - if (info.ext.data[info.length-1] == 0xF7) - _driver->sysEx(info.ext.data, (uint16)info.length-1); + if (info.command() == 0x8) { + activeNote(info.channel(), info.basic.param1, false); + } else if (info.command() == 0x9) { + if (info.length > 0) + hangingNote(info.channel(), info.basic.param1, info.length * _psecPerTick - (endTime - eventTime)); else - _driver->sysEx(info.ext.data, (uint16)info.length); - } else if (info.event == 0xFF) { - // META event - if (info.ext.type == 0x2F) { - // End of Track must be processed by us, - // as well as sending it to the output device. - if (_autoLoop) { - jumpToTick(0); - parseNextEvent(_nextEvent); - } else { - stopPlaying(); - _driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length); - } - return; - } else if (info.ext.type == 0x51) { - if (info.length >= 3) { - setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]); - } - } - _driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length); - } else { - if (info.command() == 0x8) { - activeNote(info.channel(), info.basic.param1, false); - } else if (info.command() == 0x9) { - if (info.length > 0) - hangingNote(info.channel(), info.basic.param1, info.length * _psecPerTick - (endTime - eventTime)); - else - activeNote(info.channel(), info.basic.param1, true); - } - sendToDriver(info.event, info.basic.param1, info.basic.param2); + activeNote(info.channel(), info.basic.param1, true); } + processEvent(info); - if (!_abortParse) { - _position._lastEventTime = eventTime; - parseNextEvent(_nextEvent); - } + if (_abortParse) + break; + + _position._lastEventTime = eventTime; + parseNextEvent(_nextEvent); } if (!_abortParse) { @@ -255,6 +229,45 @@ void MidiParser::onTimer() { } } +void MidiParser::processEvent(const EventInfo &info, bool fireEvents) { + if (info.event == 0xF0) { + // SysEx event + // Check for trailing 0xF7 -- if present, remove it. + if (fireEvents) { + if (info.ext.data[info.length-1] == 0xF7) + _driver->sysEx(info.ext.data, (uint16)info.length-1); + else + _driver->sysEx(info.ext.data, (uint16)info.length); + } + } else if (info.event == 0xFF) { + // META event + if (info.ext.type == 0x2F) { + // End of Track must be processed by us, + // as well as sending it to the output device. + if (_autoLoop) { + jumpToTick(0); + parseNextEvent(_nextEvent); + } else { + stopPlaying(); + if (fireEvents) + _driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length); + } + _abortParse = true; + return; + } else if (info.ext.type == 0x51) { + if (info.length >= 3) { + setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]); + } + } + if (fireEvents) + _driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length); + } else { + if (fireEvents) + sendToDriver(info.event, info.basic.param1, info.basic.param2); + } +} + + void MidiParser::allNotesOff() { if (!_driver) return; @@ -370,6 +383,9 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d if (_activeTrack >= _numTracks) return false; + assert(!_jumpingToTick); // This function is not re-entrant + _jumpingToTick = true; + Tracker currentPos(_position); EventInfo currentEvent(_nextEvent); @@ -390,34 +406,19 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d _position._playTick = _position._lastEventTick; _position._playTime = _position._lastEventTime; - if (info.event == 0xFF) { - if (info.ext.type == 0x2F) { // End of track - _position = currentPos; - _nextEvent = currentEvent; - return false; - } else { - if (info.ext.type == 0x51 && info.length >= 3) // Tempo - setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]); - if (fireEvents) - _driver->metaEvent(info.ext.type, info.ext.data, (uint16) info.length); - } - } else if (fireEvents) { - if (info.event == 0xF0) { - if (info.ext.data[info.length-1] == 0xF7) - _driver->sysEx(info.ext.data, (uint16)info.length-1); - else - _driver->sysEx(info.ext.data, (uint16)info.length); - } else { - // The note on sending code is used by the SCUMM engine. Other engine using this code - // (such as SCI) have issues with this, as all the notes sent can be heard when a song - // is fast-forwarded. Thus, if the engine requests it, don't send note on events. - if (info.command() == 0x9 && dontSendNoteOn) { - // Don't send note on; doing so creates a "warble" with some instruments on the MT-32. - // Refer to patch #3117577 - } else { - sendToDriver(info.event, info.basic.param1, info.basic.param2); - } - } + // Some special processing for the fast-forward case + if (info.command() == 0x9 && dontSendNoteOn) { + // Don't send note on; doing so creates a "warble" with + // some instruments on the MT-32. Refer to patch #3117577 + } else if (info.event == 0xFF && info.ext.type == 0x2F) { + // End of track + // This means that we failed to find the right tick. + _position = currentPos; + _nextEvent = currentEvent; + _jumpingToTick = false; + return false; + } else { + processEvent(info, fireEvents); } parseNextEvent(_nextEvent); @@ -441,6 +442,7 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d } _abortParse = true; + _jumpingToTick = false; return true; } diff --git a/audio/midiparser.h b/audio/midiparser.h index bb9749b97f..05d0cbe1db 100644 --- a/audio/midiparser.h +++ b/audio/midiparser.h @@ -105,8 +105,8 @@ struct EventInfo { ///< will occur, and the MidiParser will have to generate one itself. ///< For all other events, this value should always be zero. - byte channel() { return event & 0x0F; } ///< Separates the MIDI channel from the event. - byte command() { return event >> 4; } ///< Separates the command code from the event. + 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. }; /** @@ -287,12 +287,14 @@ protected: ///< so each event is parsed only once; this permits ///< simulated events in certain formats. bool _abortParse; ///< If a jump or other operation interrupts parsing, flag to abort. + bool _jumpingToTick; ///< True if currently inside jumpToTick protected: static uint32 readVLQ(byte * &data); virtual void resetTracking(); virtual void allNotesOff(); virtual void parseNextEvent(EventInfo &info) = 0; + virtual void processEvent(const EventInfo &info, bool fireEvents = true); void activeNote(byte channel, byte note, bool active); void hangingNote(byte channel, byte note, uint32 ticksLeft, bool recycle = true); diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index b367eeead0..d6ff4f6cc8 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -53,11 +53,6 @@ MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion, SciMusic *music) : _masterVolume = 15; _volume = 127; - _signalSet = false; - _signalToSet = 0; - _dataincAdd = false; - _dataincToAdd = 0; - _jumpToHoldTick = false; _resetOnPause = false; _pSnd = 0; } @@ -440,25 +435,6 @@ void MidiParser_SCI::sendToDriver(uint32 midi) { } void MidiParser_SCI::parseNextEvent(EventInfo &info) { - // Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs - if (_dataincAdd) { - _dataincAdd = false; - _pSnd->dataInc += _dataincToAdd; - debugC(4, kDebugLevelSound, "datainc %04x", _dataincToAdd); - } - if (_signalSet) { - _signalSet = false; - _pSnd->setSignal(_signalToSet); - - debugC(4, kDebugLevelSound, "signal %04x", _signalToSet); - } - if (_jumpToHoldTick) { - _jumpToHoldTick = false; - _pSnd->inFastForward = true; - jumpToTick(_loopTick, false, false); - _pSnd->inFastForward = false; - } - info.start = _position._playPos; info.delta = 0; while (*_position._playPos == 0xF8) { @@ -480,6 +456,79 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { case 0xC: info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; + break; + case 0xD: + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = 0; + break; + + case 0xB: + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); + info.length = 0; + break; + + case 0x8: + case 0x9: + case 0xA: + case 0xE: + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); + if (info.command() == 0x9 && info.basic.param2 == 0) + info.event = info.channel() | 0x80; + info.length = 0; + break; + + case 0xF: // System Common, Meta or SysEx event + switch (info.event & 0x0F) { + case 0x2: // Song Position Pointer + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); + break; + + case 0x3: // Song Select + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = 0; + break; + + case 0x6: + case 0x8: + case 0xA: + case 0xB: + case 0xC: + case 0xE: + info.basic.param1 = info.basic.param2 = 0; + break; + + case 0x0: // SysEx + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; + break; + + case 0xF: // META event + info.ext.type = *(_position._playPos++); + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; + break; + default: + warning( + "MidiParser_SCI::parseNextEvent: Unsupported event code %x", + info.event); + } // // System Common, Meta or SysEx event + }// switch (info.command()) +} + +void MidiParser_SCI::processEvent(const EventInfo &info, bool fireEvents) { + if (!fireEvents) { + // We don't do any processing that should be done while skipping events + MidiParser::processEvent(info, fireEvents); + return; + } + + switch (info.command()) { + case 0xC: if (info.channel() == 0xF) {// SCI special case if (info.basic.param1 != kSetSignalLoop) { // At least in kq5/french&mac the first scene in the intro has @@ -496,27 +545,23 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { // of the stream, but at a fixed location a few commands later. // That is probably why this signal isn't triggered // immediately there. - if (_soundVersion <= SCI_VERSION_0_LATE || - _position._playTick || info.delta) { - if (!_pSnd->inFastForward) { - _signalSet = true; - _signalToSet = info.basic.param1; + if (_soundVersion <= SCI_VERSION_0_LATE || _position._playTick) { + if (!_jumpingToTick) { + _pSnd->setSignal(info.basic.param1); + debugC(4, kDebugLevelSound, "signal %04x", info.basic.param1); } } } else { - _loopTick = _position._playTick + info.delta; + _loopTick = _position._playTick; } + + // Done with this event. + return; } - break; - case 0xD: - info.basic.param1 = *(_position._playPos++); - info.basic.param2 = 0; - break; + // Break to let parent handle the rest. + break; case 0xB: - info.basic.param1 = *(_position._playPos++); - info.basic.param2 = *(_position._playPos++); - // Reference for some events: // http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference // Handle common special events @@ -538,50 +583,48 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { switch (info.basic.param1) { case kSetReverb: // Already handled above - break; + return; case kMidiHold: // Check if the hold ID marker is the same as the hold ID // marker set for that song by cmdSetSoundHold. // If it is, loop back, but don't stop notes when jumping. - // We need to wait for the delta of the current event before - // jumping, thus the jump will be performed on the next - // parseNextEvent() call, like with the signal set events. - // In LSL6, room 360, song 381, this ends up jumping forward - // one tick (the hold marker occurs at playtick 27, with - // _loopTick being 15 and the event itself having a delta of - // 13, total = 28) - bug #3614566. if (info.basic.param2 == _pSnd->hold) { - _jumpToHoldTick = true; + jumpToTick(_loopTick, false, false); + // Done with this event. + return; } - break; + return; case kUpdateCue: - if (!_pSnd->inFastForward) { - _dataincAdd = true; + if (!_jumpingToTick) { + int inc; switch (_soundVersion) { case SCI_VERSION_0_EARLY: case SCI_VERSION_0_LATE: - _dataincToAdd = info.basic.param2; + inc = info.basic.param2; break; case SCI_VERSION_1_EARLY: case SCI_VERSION_1_LATE: case SCI_VERSION_2_1: - _dataincToAdd = 1; + inc = 1; break; default: error("unsupported _soundVersion"); } + _pSnd->dataInc += inc; + debugC(4, kDebugLevelSound, "datainc %04x", inc); + } - break; + return; case kResetOnPause: _resetOnPause = info.basic.param2; - break; + return; // Unhandled SCI commands case 0x46: // LSL3 - binoculars case 0x61: // Iceman (AdLib?) case 0x73: // Hoyle case 0xD1: // KQ4, when riding the unicorn // Obscure SCI commands - ignored - break; + return; // Standard MIDI commands case 0x01: // mod wheel case 0x04: // foot controller @@ -596,96 +639,49 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { case 0x4B: // voice mapping // TODO: is any support for this needed at the MIDI parser level? warning("Unhanded SCI MIDI command 0x%x - voice mapping (parameter %d)", info.basic.param1, info.basic.param2); - break; + return; default: warning("Unhandled SCI MIDI command 0x%x (parameter %d)", info.basic.param1, info.basic.param2); - break; + return; } + } - info.length = 0; - break; - case 0x8: - case 0x9: - case 0xA: - case 0xE: - info.basic.param1 = *(_position._playPos++); - info.basic.param2 = *(_position._playPos++); - if (info.command() == 0x9 && info.basic.param2 == 0) - info.event = info.channel() | 0x80; - info.length = 0; + // Break to let parent handle the rest. break; + case 0xF: // META event + if (info.ext.type == 0x2F) {// end of track reached + if (_pSnd->loop) + _pSnd->loop--; + // QFG3 abuses the hold flag. Its scripts call kDoSoundSetHold, + // but sometimes there's no hold marker in the associated songs + // (e.g. song 110, during the intro). The original interpreter + // treats this case as an infinite loop (bug #3311911). + if (_pSnd->loop || _pSnd->hold > 0) { + jumpToTick(_loopTick); + + // Done with this event. + return; - case 0xF: // System Common, Meta or SysEx event - switch (info.event & 0x0F) { - case 0x2: // Song Position Pointer - info.basic.param1 = *(_position._playPos++); - info.basic.param2 = *(_position._playPos++); - break; + } else { + _pSnd->status = kSoundStopped; + _pSnd->setSignal(SIGNAL_OFFSET); - case 0x3: // Song Select - info.basic.param1 = *(_position._playPos++); - info.basic.param2 = 0; - break; + debugC(4, kDebugLevelSound, "signal EOT"); + } + } - case 0x6: - case 0x8: - case 0xA: - case 0xB: - case 0xC: - case 0xE: - info.basic.param1 = info.basic.param2 = 0; - break; + // Break to let parent handle the rest. + break; - case 0x0: // SysEx - info.length = readVLQ(_position._playPos); - info.ext.data = _position._playPos; - _position._playPos += info.length; - break; + default: + // Break to let parent handle the rest. + break; + } - case 0xF: // META event - info.ext.type = *(_position._playPos++); - info.length = readVLQ(_position._playPos); - info.ext.data = _position._playPos; - _position._playPos += info.length; - if (info.ext.type == 0x2F) {// end of track reached - if (_pSnd->loop) - _pSnd->loop--; - // QFG3 abuses the hold flag. Its scripts call kDoSoundSetHold, - // but sometimes there's no hold marker in the associated songs - // (e.g. song 110, during the intro). The original interpreter - // treats this case as an infinite loop (bug #3311911). - if (_pSnd->loop || _pSnd->hold > 0) { - // TODO: this jump is also vulnerable to the same lockup as - // the MIDI hold one above. However, we can't perform the - // jump on the next tick like with the MIDI hold jump above, - // as there aren't any subsequent MIDI events after this one. - // This assert is here to detect cases where the song ends - // up jumping forward, like with bug #3614566 (see above). - // (Exception: delta == 0, in which case the loop points - // at the previous event, which is fine.) - assert(_loopTick + info.delta < _position._playTick || - ((_loopTick == _position._playTick && info.delta == 0))); - - uint32 extraDelta = info.delta; - _pSnd->inFastForward = true; - jumpToTick(_loopTick); - _pSnd->inFastForward = false; - _nextEvent.delta += extraDelta; - } else { - _pSnd->status = kSoundStopped; - _pSnd->setSignal(SIGNAL_OFFSET); - - debugC(4, kDebugLevelSound, "signal EOT"); - } - } - break; - default: - warning( - "MidiParser_SCI::parseNextEvent: Unsupported event code %x", - info.event); - } // // System Common, Meta or SysEx event - }// switch (info.command()) + + // Let parent handle the rest + MidiParser::processEvent(info, fireEvents); } byte MidiParser_SCI::getSongReverb() { diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 7bd68994c8..5784dca1ab 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -89,6 +89,7 @@ public: protected: void parseNextEvent(EventInfo &info); + void processEvent(const EventInfo &info, bool fireEvents = true); byte *midiMixChannels(); byte *midiFilterChannels(int channelMask); byte midiGetNextChannel(long ticker); @@ -106,11 +107,6 @@ protected: byte _masterVolume; // the overall master volume (same for all tracks) byte _volume; // the global volume of the current track - bool _signalSet; - int16 _signalToSet; - bool _dataincAdd; - int16 _dataincToAdd; - bool _jumpToHoldTick; bool _resetOnPause; bool _channelUsed[16]; diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 1628a22386..8c6d0d6431 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -521,11 +521,7 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { pSnd->pMidiParser->jumpToTick(0); else { // Fast forward to the last position and perform associated events when loading - pSnd->inFastForward = true; - // we set this flag, so that the midiparser doesn't set any signals for scripts - // if we don't do this, at least accessing the debugger will reset previously set signals pSnd->pMidiParser->jumpToTick(pSnd->ticker, true, true, true); - pSnd->inFastForward = false; } // Restore looping and hold @@ -765,7 +761,6 @@ MusicEntry::MusicEntry() { resourceId = 0; isQueued = false; - inFastForward = false; dataInc = 0; ticker = 0; diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index b582e7f1a9..40236c8445 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -65,7 +65,6 @@ public: uint16 resourceId; bool isQueued; // for SCI0 only! - bool inFastForward; // if we are currently fast-forwarding (disables any signals to scripts) uint16 dataInc; uint16 ticker; |