aboutsummaryrefslogtreecommitdiff
path: root/sound/midiparser_smf.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sound/midiparser_smf.cpp')
-rw-r--r--sound/midiparser_smf.cpp307
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; }