void MidiParser::onTimer() { uint32 end_time; uint32 event_time; if (!_position._play_pos || !_driver) return; _abort_parse = false; end_time = _position._play_time + _timer_rate; // Scan our hanging notes for any // that should be turned off. if (_hanging_notes_count) { NoteTimer *ptr = &_hanging_notes[0]; int i; for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) { if (ptr->time_left) { if (ptr->time_left <= _timer_rate) { sendToDriver(0x80 | ptr->channel, ptr->note, 0); ptr->time_left = 0; --_hanging_notes_count; } else { ptr->time_left -= _timer_rate; } } } } while (!_abort_parse) { EventInfo &info = _next_event; event_time = _position._last_event_time + info.delta * _psec_per_tick; if (event_time > end_time) break; // Process the next info. _position._last_event_tick += info.delta; if (info.event < 0x80) { warning("Bad command or running status %02X", info.event); _position._play_pos = 0; 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); 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(_next_event); } 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 * _psec_per_tick - (end_time - event_time)); else activeNote(info.channel(), info.basic.param1, true); } sendToDriver(info.event, info.basic.param1, info.basic.param2); } if (!_abort_parse) { _position._last_event_time = event_time; parseNextEvent(_next_event); } } if (!_abort_parse) { _position._play_time = end_time; _position._play_tick = (_position._play_time - _position._last_event_time) / _psec_per_tick + _position._last_event_tick; } }
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()) }