diff options
Diffstat (limited to 'sound/midiparser_smf.cpp')
-rw-r--r-- | sound/midiparser_smf.cpp | 307 |
1 files changed, 55 insertions, 252 deletions
diff --git a/sound/midiparser_smf.cpp b/sound/midiparser_smf.cpp index 69123c7d6f..ca6fdddcfd 100644 --- a/sound/midiparser_smf.cpp +++ b/sound/midiparser_smf.cpp @@ -36,51 +36,18 @@ class MidiParser_SMF : public MidiParser { protected: byte *_data; byte *_buffer; - uint16 _num_tracks; - byte *_tracks [16]; - bool _malformedPitchBends; - byte _active_track; - byte *_play_pos; - uint32 _play_time; - uint32 _last_event_time; - byte _running_status; // Cached MIDI command - - uint32 _ppqn; - uint32 _psec_per_tick; // Microseconds per delta tick protected: - uint32 read4high (byte * &data) { - uint32 val = 0; - int i; - for (i = 0; i < 4; ++i) val = (val << 8) | *data++; - return val; - } - uint16 read2low (byte * &data) { - uint16 val = 0; - int i; - for (i = 0; i < 2; ++i) val |= (*data++) << (i * 8); - return val; - } - uint32 readVLQ (byte * &data); - void compressToType0(); - void playToTime (uint32 psec, bool transmit); - void allNotesOff(); + void parseNextEvent (EventInfo &info); public: ~MidiParser_SMF(); bool loadMusic (byte *data, uint32 size); void unloadMusic(); - void property (int property, int value); - void setMidiDriver (MidiDriver *driver) { _driver = driver; } - void setTimerRate (uint32 rate) { _timer_rate = rate; } - void onTimer(); - - void setTrack (byte track); - void jumpToTick (uint32 tick); }; @@ -106,147 +73,70 @@ void MidiParser_SMF::property (int prop, int value) { switch (prop) { case mpMalformedPitchBends: _malformedPitchBends = (value > 0); + default: + MidiParser::property (prop, value); } } -// This is the conventional (i.e. SMF) variable length quantity -uint32 MidiParser_SMF::readVLQ (byte * &data) { - byte str; - uint32 value = 0; - int i; - - for (i = 0; i < 4; ++i) { - str = data[0]; - ++data; - value = (value << 7) | (str & 0x7F); - if (!(str & 0x80)) - break; - } - return value; -} - -void MidiParser_SMF::onTimer() { - if (!_play_pos || !_driver) +void MidiParser_SMF::parseNextEvent (EventInfo &info) { + info.start = _play_pos; + info.delta = readVLQ (_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 ((_play_pos[0] & 0xF0) >= 0x80) + info.event = *(_play_pos++); + else + info.event = _running_status; + } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _play_pos++); + if (info.event < 0x80) return; - playToTime (_play_time + _timer_rate, true); -} -void MidiParser_SMF::playToTime (uint32 psec, bool transmit) { - uint32 delta; - uint32 end_time; - uint32 event_time; - byte *pos; - byte *oldpos; - byte event; - uint32 length; - - end_time = psec; - pos = _play_pos; - - while (true) { - oldpos = pos; - delta = readVLQ (pos); - event_time = _last_event_time + delta * _psec_per_tick; - if (event_time > end_time) { - pos = oldpos; + switch (info.event >> 4) { + case 0xC: case 0xD: + info.param1 = *(_play_pos++); + info.param2 = 0; + break; + + case 0x8: case 0x9: case 0xA: case 0xB: case 0xE: + info.param1 = *(_play_pos++); + info.param2 = *(_play_pos++); + break; + + case 0xF: // System Common, Meta or SysEx event + switch (info.event & 0x0F) { + case 0x2: // Song Position Pointer + info.param1 = *(_play_pos++); + info.param2 = *(_play_pos++); break; - } - - // Process the next event. - do { - if ((pos[0] & 0xF0) >= 0x80) - event = *pos++; - else - event = _running_status; - } while (_malformedPitchBends && (event & 0xF0) == 0xE0 && pos++); - - if (event < 0x80) { - printf ("ERROR! Bad command or running status %02X", event); - _play_pos = 0; - return; - } - _running_status = event; - switch (event >> 4) { - case 0xC: // Program Change - case 0xD: // Channel Aftertouch - if (transmit) - _driver->send (event | (pos[0] << 8)); - ++pos; + case 0x3: // Song Select + info.param1 = *(_play_pos++); + info.param2 = 0; break; - case 0x9: // Note On - case 0x8: // Note Off - case 0xA: // Key Aftertouch - case 0xB: // Control Change - case 0xE: // Pitch Bender Change - if (transmit) - _driver->send (event | (pos[0] << 8) | (pos[1] << 16)); - pos += 2; + case 0x6: case 0x8: case 0xA: case 0xB: case 0xC: case 0xE: + info.param1 = info.param2 = 0; break; - case 0xF: // Meta or SysEx event - switch (event & 0x0F) { - case 0x2: // Song Position Pointer - if (transmit) - _driver->send (event | (pos[0] << 8) | (pos[1] << 16)); - pos += 2; - break; - - case 0x3: // Song Select - if (transmit) - _driver->send (event | (pos[0] << 8)); - ++pos; - break; - - case 0x6: // Tune Request - case 0x8: // MIDI Timing Clock - case 0xA: // Sequencer Start - case 0xB: // Sequencer Continue - case 0xC: // Sequencer Stop - case 0xE: // Active Sensing - if (transmit) - _driver->send (event); - break; - - case 0x0: // SysEx - length = readVLQ (pos); - if (transmit) - _driver->sysEx (pos, (uint16)(length - 1)); - pos += length; - break; - - case 0xF: // META event - event = *pos++; - length = readVLQ (pos); + case 0x0: // SysEx + info.length = readVLQ (_play_pos); + info.data = _play_pos; + _play_pos += info.length; + break; - if (event == 0x2F) { - // End of Track must be processed by us, - // as well as sending it to the output device. - _play_pos = 0; - if (transmit) { - _driver->metaEvent (event, pos, (uint16) length); - } - return; - } else if (event == 0x51) { - if (length >= 3) { - delta = pos[0] << 16 | pos[1] << 8 | pos[2]; - _psec_per_tick = (delta + (_ppqn >> 2)) / _ppqn; - } - } - - if (transmit) - _driver->metaEvent (event, pos, (uint16) length); - pos += length; - break; - } + case 0xF: // META event + info.type = *(_play_pos++); + info.length = readVLQ (_play_pos); + info.data = _play_pos; + _play_pos += info.length; + break; } - - _last_event_time = event_time; } - - _play_time = end_time; - _play_pos = pos; } bool MidiParser_SMF::loadMusic (byte *data, uint32 size) { @@ -349,8 +239,7 @@ bool MidiParser_SMF::loadMusic (byte *data, uint32 size) { // Note that we assume the original data passed in // will persist beyond this call, i.e. we do NOT // copy the data to our own buffer. Take warning.... - _active_track = 255; - _psec_per_tick = (500000 + (_ppqn >> 2)) / _ppqn; // Default to 120 BPM + resetTracking(); setTrack (0); return true; } @@ -464,98 +353,12 @@ void MidiParser_SMF::compressToType0() { *output++ = 0x00; } -void MidiParser_SMF::allNotesOff() { - if (!_driver) - return; - - int i; - for (i = 0; i < 15; ++i) { - _driver->send (0x007BB0 | i); - } -} - void MidiParser_SMF::unloadMusic() { - _play_pos = NULL; - _data = NULL; + resetTracking(); + allNotesOff(); + _data = 0; _num_tracks = 0; _active_track = 255; - _play_time = 0; - _last_event_time = 0; - _running_status = 0; - allNotesOff(); -} - -void MidiParser_SMF::setTrack (byte track) { - if (track >= _num_tracks || track == _active_track) - return; - _active_track = track; - _play_time = 0; - _last_event_time = 0; - _play_pos = _tracks[track]; - _running_status = 0; - allNotesOff(); -} - -void MidiParser_SMF::jumpToTick (uint32 tick) { - if (_active_track >= _num_tracks) - return; - _play_pos = _tracks[_active_track]; - _play_time = 0; - _last_event_time = 0; - allNotesOff(); - if (tick == 0) - return; - - uint32 current_tick = 0; - byte *start; - uint32 event_count = 0; - - while (current_tick < tick) { - start = _play_pos; - uint32 delta = readVLQ (_play_pos); - - if (current_tick + delta >= tick) { - _play_pos = start; - _play_time += (tick - current_tick) * _psec_per_tick; - break; - } - - ++event_count; - current_tick += delta; - _play_time += delta * _psec_per_tick; - _last_event_time = _play_time; - - byte event; - do { - event = *_play_pos; - if (event < 0x80) - event = _running_status; - } while (_malformedPitchBends && (event & 0xF0) == 0xE0 && _play_pos++); - _running_status = event; - - if (command_lengths[(event >> 4) - 8] > 0) { - _play_pos += command_lengths[(event >> 4) - 8]; - } else if (special_lengths[event & 0xF] > 0) { - _play_pos += special_lengths[event & 0xF]; - } else if (event == 0xF0) { - uint32 length = readVLQ (++_play_pos); - _play_pos += length; - } else if (event == 0xFF) { - event = *(++_play_pos); - uint32 length = readVLQ (++_play_pos); - if (event == 0x2F) { // End of track - _play_pos = 0; - _driver->metaEvent (event, _play_pos, (uint16) length); - break; - } else if (event == 0x51) { // Tempo - if (length >= 3) { - delta = _play_pos[0] << 16 | _play_pos[1] << 8 | _play_pos[2]; - _psec_per_tick = (delta + (_ppqn >> 2)) / _ppqn; - } - } - _play_pos += length; - } - } } MidiParser *MidiParser::createParser_SMF() { return new MidiParser_SMF; } |