void MidiParser_SMF::parseNextEvent(EventInfo &info) { info.start = _position._play_pos; info.delta = readVLQ(_position._play_pos); // Process the next info. If mpMalformedPitchBends // was set, we must skip over any pitch bend events // because they are from Simon games and are not // real pitch bend events, they're just two-byte // prefixes before the real info. do { if ((_position._play_pos[0] & 0xF0) >= 0x80) info.event = *(_position._play_pos++); else info.event = _position._running_status; } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _position._play_pos++); if (info.event < 0x80) return; _position._running_status = info.event; switch (info.command()) { case 0x9: // Note On info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); if (info.basic.param2 == 0) info.event = info.channel() | 0x80; info.length = 0; break; case 0xC: case 0xD: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = 0; break; case 0x8: case 0xA: case 0xB: case 0xE: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); 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._play_pos++); info.basic.param2 = *(_position._play_pos++); break; case 0x3: // Song Select info.basic.param1 = *(_position._play_pos++); 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._play_pos); info.ext.data = _position._play_pos; _position._play_pos += info.length; break; case 0xF: // META event info.ext.type = *(_position._play_pos++); info.length = readVLQ(_position._play_pos); info.ext.data = _position._play_pos; _position._play_pos += info.length; break; default: warning("MidiParser_SMF::parseNextEvent: Unsupported event code %x", info.event); } } }
void MidiParser_RO::parseNextEvent (EventInfo &info) { _markerCount += _lastMarkerCount; _lastMarkerCount = 0; info.delta = 0; do { info.start = _position._play_pos; info.event = *(_position._play_pos++); if (info.command() == 0xA) { ++_lastMarkerCount; info.event = 0xF0; } else if (info.event == 0xF0 || info.event == 0xF1) { byte delay = *(_position._play_pos++); info.delta += delay; if (info.event == 0xF1) { // This event is, as far as we have been able // to determine, only used in one single song // in EGA Loom. It seems to be meant for adding // values greater than 255 to info.delta. See // bug #1498785. info.delta += 256; } continue; } break; } while (true); // Seems to indicate EOT if (info.event == 0) { info.event = 0xFF; info.ext.type = 0x2F; info.length = 0; info.ext.data = 0; return; } if (info.event < 0x80) return; _position._running_status = info.event; switch (info.command()) { case 0xC: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = 0; break; case 0x8: case 0x9: case 0xB: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); if (info.command() == 0x9 && info.basic.param2 == 0) info.event = info.channel() | 0x80; info.length = 0; break; case 0xF: // Marker and EOT messages info.length = 0; info.ext.data = 0; if (info.event == 0xFF) { _autoLoop = true; info.ext.type = 0x2F; } else { info.ext.type = 0x7F; // Bogus META } info.event = 0xFF; break; } }
void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { info.start = _position._play_pos; info.delta = readVLQ2(_position._play_pos) - _inserted_delta; // Process the next event. _inserted_delta = 0; info.event = *(_position._play_pos++); switch (info.event >> 4) { case 0x9: // Note On info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); info.length = readVLQ(_position._play_pos); if (info.basic.param2 == 0) { info.event = info.channel() | 0x80; info.length = 0; } break; case 0xC: case 0xD: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = 0; break; case 0x8: case 0xA: case 0xE: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); break; case 0xB: info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); // This isn't a full XMIDI implementation, but it should // hopefully be "good enough" for most things. switch (info.basic.param1) { // Simplified XMIDI looping. case 0x74: { // XMIDI_CONTROLLER_FOR_LOOP byte *pos = _position._play_pos; if (_loopCount < ARRAYSIZE(_loop) - 1) _loopCount++; else warning("XMIDI: Exceeding maximum loop count %d", ARRAYSIZE(_loop)); _loop[_loopCount].pos = pos; _loop[_loopCount].repeat = info.basic.param2; break; } case 0x75: // XMIDI_CONTORLLER_NEXT_BREAK if (_loopCount >= 0) { if (info.basic.param2 < 64) { // End the current loop. _loopCount--; } else { // Repeat 0 means "loop forever". if (_loop[_loopCount].repeat) { if (--_loop[_loopCount].repeat == 0) _loopCount--; else _position._play_pos = _loop[_loopCount].pos; } else { _position._play_pos = _loop[_loopCount].pos; } } } break; case 0x77: // XMIDI_CONTROLLER_CALLBACK_TRIG if (_callbackProc) _callbackProc(info.basic.param2, _callbackData); break; case 0x6e: // XMIDI_CONTROLLER_CHAN_LOCK case 0x6f: // XMIDI_CONTROLLER_CHAN_LOCK_PROT case 0x70: // XMIDI_CONTROLLER_VOICE_PROT case 0x71: // XMIDI_CONTROLLER_TIMBRE_PROT case 0x72: // XMIDI_CONTROLLER_BANK_CHANGE case 0x73: // XMIDI_CONTROLLER_IND_CTRL_PREFIX case 0x76: // XMIDI_CONTROLLER_CLEAR_BB_COUNT case 0x78: // XMIDI_CONTROLLER_SEQ_BRANCH_INDEX default: if (info.basic.param1 >= 0x6e && info.basic.param1 <= 0x78) { warning("Unsupported XMIDI controller %d (0x%2x)", info.basic.param1, info.basic.param1); } } // Should we really keep passing the XMIDI controller events to // the MIDI driver, or should we turn them into some kind of // NOP events? (Dummy meta events, perhaps?) Ah well, it has // worked so far, so it shouldn't cause any damage... break; case 0xF: // Meta or SysEx event switch (info.event & 0x0F) { case 0x2: // Song Position Pointer info.basic.param1 = *(_position._play_pos++); info.basic.param2 = *(_position._play_pos++); break; case 0x3: // Song Select info.basic.param1 = *(_position._play_pos++); 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._play_pos); info.ext.data = _position._play_pos; _position._play_pos += info.length; break; case 0xF: // META event info.ext.type = *(_position._play_pos++); info.length = readVLQ(_position._play_pos); info.ext.data = _position._play_pos; _position._play_pos += info.length; if (info.ext.type == 0x51 && info.length == 3) { // Tempo event. We want to make these constant 500,000. info.ext.data[0] = 0x07; info.ext.data[1] = 0xA1; info.ext.data[2] = 0x20; } break; default: warning("MidiParser_XMIDI::parseNextEvent: Unsupported event code %x", info.event); } } }
void MidiParser_SH::parseNextEvent(EventInfo &info) { Common::StackLock lock(_mutex); // warning("parseNextEvent"); // there is no delta right at the start of the music data // this order is essential, otherwise notes will get delayed or even go missing if (_position._playPos != _tracks[0]) { info.delta = *(_position._playPos++); } else { info.delta = 0; } info.start = _position._playPos; info.event = *_position._playPos++; //warning("Event %x", info.event); _position._runningStatus = info.event; switch (info.command()) { case 0xC: { // program change int idx = *_position._playPos++; info.basic.param1 = idx & 0x7f; 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) { // NoteOn with param2==0 is a NoteOff info.event = info.channel() | 0x80; } info.length = 0; break; case 0xF: if (info.event == 0xFF) { error("SysEx META event 0xFF"); byte type = *(_position._playPos++); switch(type) { case 0x2F: // End of Track allNotesOff(); stopPlaying(); unloadMusic(); return; case 0x51: warning("TODO: 0xFF / 0x51"); return; default: warning("TODO: 0xFF / %x Unknown", type); break; } } else if (info.event == 0xFC) { // Official End-Of-Track signal debugC(kDebugLevelMusic, "Music: System META event 0xFC"); byte type = *(_position._playPos++); switch (type) { case 0x80: // end of track, triggers looping debugC(kDebugLevelMusic, "Music: META event triggered looping"); jumpToTick(0, true, true, false); break; case 0x81: // end of track, stop playing debugC(kDebugLevelMusic, "Music: META event triggered music stop"); stopPlaying(); unloadMusic(); break; default: error("MidiParser_SH::parseNextEvent: Unknown META event 0xFC type %x", type); break; } } else { warning("TODO: %x / Unknown", info.event); break; } break; default: warning("MidiParser_SH::parseNextEvent: Unsupported event code %x", info.event); break; }// switch (info.command()) }
void MidiParser_S1D::parseNextEvent(EventInfo &info) { info.start = _position._playPos; info.length = 0; info.delta = _noDelta ? 0 : readVLQ2(_position._playPos); _noDelta = false; info.event = *_position._playPos++; if (!(info.event & 0x80)) { _noDelta = true; info.event |= 0x80; } if (info.event == 0xFC) { // This means End of Track. // Rewrite in SMF (MIDI transmission) form. info.event = 0xFF; info.ext.type = 0x2F; } else { switch (info.command()) { case 0x8: // note off info.basic.param1 = *_position._playPos++; info.basic.param2 = 0; break; case 0x9: // note on info.basic.param1 = *_position._playPos++; info.basic.param2 = *_position._playPos++; // Rewrite note on events with velocity 0 as note off events. // This is the actual meaning of this, but theoretically this // should not need to be rewritten, since all MIDI devices should // interpret it like that. On the other hand all our MidiParser // implementations do it and there seems to be code in MidiParser // which relies on this for tracking active notes. if (info.basic.param2 == 0) { info.event = info.channel() | 0x80; } break; case 0xA: { // loop control // In case the stop mode(?) is set to 0x80 this will stop the // track over here. const int16 loopIterations = int8(*_position._playPos++); if (!loopIterations) { _loops[info.channel()].start = _position._playPos; } else { if (!_loops[info.channel()].timer) { if (_loops[info.channel()].start) { _loops[info.channel()].timer = uint16(loopIterations); _loops[info.channel()].end = _position._playPos; // Go to the start of the loop _position._playPos = _loops[info.channel()].start; } } else { if (_loops[info.channel()].timer) _position._playPos = _loops[info.channel()].start; --_loops[info.channel()].timer; } } // We need to read the next midi event here. Since we can not // safely pass this event to the MIDI event processing. chainEvent(info); } break; case 0xB: // auto stop marker(?) // In case the stop mode(?) is set to 0x80 this will stop the // track. // We need to read the next midi event here. Since we can not // safely pass this event to the MIDI event processing. chainEvent(info); break; case 0xC: // program change info.basic.param1 = *_position._playPos++; info.basic.param2 = 0; break; case 0xD: // jump to loop end if (_loops[info.channel()].end) _position._playPos = _loops[info.channel()].end; // We need to read the next midi event here. Since we can not // safely pass this event to the MIDI event processing. chainEvent(info); break; default: // The original called some other function from here, which seems // not to be MIDI related. warning("MidiParser_S1D: default case %d", info.channel()); // We need to read the next midi event here. Since we can not // safely pass this event to the MIDI event processing. chainEvent(info); break; } } }