diff options
author | Jamieson Christian | 2003-05-19 18:48:18 +0000 |
---|---|---|
committer | Jamieson Christian | 2003-05-19 18:48:18 +0000 |
commit | 3dc788da63520b8baad2b5c0726f34168ab55356 (patch) | |
tree | d376c0e484c1d6d90b5697af7ad69fa249c6df51 | |
parent | e8771e7684048690e39d3e76f71bdea193f3d1f3 (diff) | |
download | scummvm-rg350-3dc788da63520b8baad2b5c0726f34168ab55356.tar.gz scummvm-rg350-3dc788da63520b8baad2b5c0726f34168ab55356.tar.bz2 scummvm-rg350-3dc788da63520b8baad2b5c0726f34168ab55356.zip |
Moved common parsing logic into MidiParser base class.
Added auto-loop capability.
svn-id: r7692
-rw-r--r-- | scummvm.dsp | 4 | ||||
-rw-r--r-- | sound/midiparser.cpp | 207 | ||||
-rw-r--r-- | sound/midiparser.h | 67 | ||||
-rw-r--r-- | sound/midiparser_smf.cpp | 307 | ||||
-rw-r--r-- | sound/midiparser_xmidi.cpp | 319 | ||||
-rw-r--r-- | sound/module.mk | 1 |
6 files changed, 422 insertions, 483 deletions
diff --git a/scummvm.dsp b/scummvm.dsp index 5aa5b269a6..06b17d54d2 100644 --- a/scummvm.dsp +++ b/scummvm.dsp @@ -262,6 +262,10 @@ SOURCE=.\sound\mididrv.h # End Source File
# Begin Source File
+SOURCE=.\sound\midiparser.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\sound\midiparser.h
# End Source File
# Begin Source File
diff --git a/sound/midiparser.cpp b/sound/midiparser.cpp new file mode 100644 index 0000000000..46eff182d7 --- /dev/null +++ b/sound/midiparser.cpp @@ -0,0 +1,207 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "midiparser.h" +#include "mididrv.h" +#include "common/util.h" + +#include <stdio.h> +#include <memory.h> + +////////////////////////////////////////////////// +// +// MidiParser implementation +// +////////////////////////////////////////////////// + +MidiParser::MidiParser() : +_driver (0), +_timer_rate (0x4A0000), +_ppqn (96), +_tempo (500000), +_psec_per_tick (5208), // 500000 / 96 +_autoLoop (false), +_num_tracks (0), +_active_track (255), +_play_pos (0), +_play_time (0), +_last_event_time (0), +_last_event_tick (0), +_running_status (0) +{ } + +void MidiParser::property (int prop, int value) { + switch (prop) { + case mpAutoLoop: + _autoLoop = (value != 0); + } +} + +// This is the conventional (i.e. SMF) variable length quantity +uint32 MidiParser::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::onTimer() { + uint32 end_time; + uint32 event_time; + + if (!_play_pos || !_driver) + return; + + end_time = _play_time + _timer_rate; + + while (true) { + EventInfo &info = _next_event; + + event_time = _last_event_time + info.delta * _psec_per_tick; + if (event_time > end_time) + break; + + // Process the next info. + _last_event_tick += info.delta; + if (info.event < 0x80) { + printf ("ERROR! Bad command or running status %02X", info.event); + _play_pos = 0; + return; + } + _running_status = info.event; + + if (info.event == 0xF0) { + // SysEx event + _driver->sysEx (info.data, (uint16) info.length); + } else if (info.event == 0xFF) { + // META event + if (info.type == 0x2F) { + // End of Track must be processed by us, + // as well as sending it to the output device. + allNotesOff(); + if (_autoLoop) { + _play_pos = _tracks[_active_track]; + parseNextEvent (_next_event); + } else { + _play_pos = 0; + _driver->metaEvent (info.type, info.data, (uint16) info.length); + } + return; + } else if (info.type == 0x51) { + if (info.length >= 3) { + _tempo = info.data[0] << 16 | info.data[1] << 8 | info.data[2]; + _psec_per_tick = (_tempo + (_ppqn >> 2)) / _ppqn; + } + } + _driver->metaEvent (info.type, info.data, (uint16) info.length); + } else { + _driver->send (info.event | info.param1 << 8 | info.param2 << 16); + } + + + _last_event_time = event_time; + parseNextEvent (_next_event); + } + + _play_time = end_time; +} + +void MidiParser::allNotesOff() { + if (!_driver) + return; + + int i; + for (i = 0; i < 15; ++i) { + _driver->send (0x007BB0 | i); + } +} + +void MidiParser::resetTracking() { + _play_pos = 0; + _tempo = 500000; + _psec_per_tick = 500000 / _ppqn; + _play_time = 0; + _last_event_time = 0; + _last_event_tick = 0; + _running_status = 0; +} + +void MidiParser::setTrack (byte track) { + if (track >= _num_tracks || track == _active_track) + return; + resetTracking(); + allNotesOff(); + _active_track = track; + _play_pos = _tracks[track]; + parseNextEvent (_next_event); +} + +void MidiParser::jumpToTick (uint32 tick) { + if (_active_track >= _num_tracks) + return; + resetTracking(); + allNotesOff(); + + _play_pos = _tracks[_active_track]; + parseNextEvent (_next_event); + if (tick == 0) + return; + + while (true) { + EventInfo &info = _next_event; + if (_last_event_tick + info.delta >= tick) { + _play_time += (tick - _last_event_tick) * _psec_per_tick; + break; + } + + _last_event_tick += info.delta; + _play_time += info.delta * _psec_per_tick; + _last_event_time = _play_time; + + if (info.event == 0xFF) { + if (info.type == 0x2F) { // End of track + if (_autoLoop) { + _play_pos = _tracks[_active_track]; + parseNextEvent (_next_event); + } else { + _play_pos = 0; + _driver->metaEvent (0x2F, info.data, (uint16) info.length); + } + break; + } else if (info.type == 0x51) { // Tempo + if (info.length >= 3) { + _tempo = info.data[0] << 16 | info.data[1] << 8 | info.data[2]; + _psec_per_tick = (_tempo + (_ppqn >> 2)) / _ppqn; + } + } + } + + parseNextEvent (_next_event); + } +} diff --git a/sound/midiparser.h b/sound/midiparser.h index ab165d4f51..8bc837465c 100644 --- a/sound/midiparser.h +++ b/sound/midiparser.h @@ -28,29 +28,86 @@ class MidiParser; class MidiDriver; +struct EventInfo { + byte * start; // Points to delta + uint32 delta; + byte event; + union { + struct { + byte param1; + byte param2; + }; + struct { + byte type; // Used for METAs + byte * data; // Used for SysEx and METAs + uint32 length; // Used for SysEx and METAs + }; + }; + + byte channel() { return event & 0x0F; } + byte command() { return event >> 4; } +}; + class MidiParser { protected: MidiDriver *_driver; uint32 _timer_rate; + uint32 _ppqn; // Pulses (ticks) Per Quarter Note + uint32 _tempo; // Microseconds per quarter note + uint32 _psec_per_tick; // Microseconds per tick (_tempo / _ppqn) + bool _autoLoop; // For lightweight clients that don't monitor events + + byte * _tracks[16]; + byte _num_tracks; + byte _active_track; + + byte * _play_pos; + uint32 _play_time; + uint32 _last_event_time; + uint32 _last_event_tick; + byte _running_status; // Cache of last MIDI command, used in compressed streams + EventInfo _next_event; + +protected: + static uint32 readVLQ (byte * &data); + void resetTracking(); + void allNotesOff(); + virtual void parseNextEvent (EventInfo &info) = 0; + + // Multi-byte read helpers + 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; + } public: enum { - mpMalformedPitchBends = 1 + mpMalformedPitchBends = 1, + mpAutoLoop = 2 }; public: + MidiParser(); virtual ~MidiParser() { } virtual bool loadMusic (byte *data, uint32 size) = 0; virtual void unloadMusic() = 0; - virtual void property (int prop, int value) { } + virtual void property (int prop, int value); void setMidiDriver (MidiDriver *driver) { _driver = driver; } void setTimerRate (uint32 rate) { _timer_rate = rate / 500; } - virtual void onTimer() = 0; + void onTimer(); - virtual void setTrack (byte track) = 0; - virtual void jumpToTick (uint32 tick) = 0; + void setTrack (byte track); + void jumpToTick (uint32 tick); static MidiParser *createParser_SMF(); static MidiParser *createParser_XMIDI(); 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; } diff --git a/sound/midiparser_xmidi.cpp b/sound/midiparser_xmidi.cpp index 91dbc7b619..d35bf93c02 100644 --- a/sound/midiparser_xmidi.cpp +++ b/sound/midiparser_xmidi.cpp @@ -35,64 +35,28 @@ struct NoteTimer { byte channel; byte note; - uint32 time_left; + uint32 off_time; }; class MidiParser_XMIDI : public MidiParser { protected: byte *_data; - uint16 _num_tracks; - byte *_tracks [16]; - - byte _active_track; - byte *_play_pos; - uint32 _play_time; - uint32 _last_event_time; - NoteTimer _notes_cache[32]; + uint32 _inserted_delta; // Track simulated deltas for note-off events 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); uint32 readVLQ2 (byte * &data); - - void playToTime (uint32 psec, bool transmit); + void parseNextEvent (EventInfo &info); public: ~MidiParser_XMIDI() { } bool loadMusic (byte *data, uint32 size); void unloadMusic(); - - void setMidiDriver (MidiDriver *driver) { _driver = driver; } - void setTimerRate (uint32 rate) { _timer_rate = rate; } - void onTimer(); - - void setTrack (byte track); - void jumpToTick (uint32 tick); }; -// This delta time is based on an assumed tempo of -// 120 quarter notes per minute (500,000 microseconds per quarter node) -// and 60 ticks per quarter note, i.e. 120 Hz overall. -// 500,000 / 60 = 8333.33 microseconds per tick. -#define MICROSECONDS_PER_TICK 8333 - - - ////////////////////////////////////////////////// // // MidiParser_XMIDI implementation @@ -102,22 +66,6 @@ public: // ////////////////////////////////////////////////// -// This is the conventional (i.e. SMF) variable length quantity -uint32 MidiParser_XMIDI::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; -} - // This is a special XMIDI variable length quantity uint32 MidiParser_XMIDI::readVLQ2 (byte * &pos) { uint32 value = 0; @@ -131,154 +79,104 @@ uint32 MidiParser_XMIDI::readVLQ2 (byte * &pos) { return value; } -void MidiParser_XMIDI::onTimer() { - if (!_play_pos || !_driver) - return; - playToTime (_play_time + _timer_rate, true); -} +void MidiParser_XMIDI::parseNextEvent (EventInfo &info) { + info.start = _play_pos; + info.delta = readVLQ2 (_play_pos) - _inserted_delta; -void MidiParser_XMIDI::playToTime (uint32 psec, bool transmit) { - uint32 delta; - uint32 end_time; - uint32 event_time; - byte *pos; - byte *oldpos; - byte event; - uint32 length; - int i; - NoteTimer *ptr; - byte note; - byte vel; + // Scan our active notes for the note + // with the nearest off time. It might turn out + // to be closer than the next regular event. uint32 note_length; - - end_time = psec; - pos = _play_pos; - - // Send any necessary note off events. - ptr = &_notes_cache[0]; + NoteTimer *best = 0; + NoteTimer *ptr = &_notes_cache[0]; + int i; for (i = ARRAYSIZE(_notes_cache); i; --i, ++ptr) { - if (ptr->time_left) { - if (ptr->time_left <= _timer_rate) { - if (transmit) - _driver->send (0x80 | ptr->channel | (ptr->note << 8)); - ptr->time_left = 0; - } else { - ptr->time_left -= _timer_rate; - } - } + if (ptr->off_time && ptr->off_time >= _last_event_tick && (!best || ptr->off_time < best->off_time)) + best = ptr; } - while (true) { - oldpos = pos; - delta = readVLQ2 (pos); - event_time = _last_event_time + delta * MICROSECONDS_PER_TICK; - if (event_time > end_time) { - pos = oldpos; - break; + // See if we need to simulate a note off event. + if (best && (best->off_time - _last_event_tick) <= info.delta) { + _play_pos = info.start; + info.delta = best->off_time - _last_event_tick; + info.event = 0x80 | best->channel; + info.param1 = best->note; + info.param2 = 0; + best->off_time = 0; + _inserted_delta += info.delta; + return; + } + + // Process the next event. + _inserted_delta = 0; + info.event = *(_play_pos++); + switch (info.event >> 4) { + case 0x9: // Note On + info.param1 = *(_play_pos++); + info.param2 = *(_play_pos++); + note_length = readVLQ (_play_pos); + + // In addition to sending this back, we must + // store a note timer so we know when to turn it off. + ptr = &_notes_cache[0]; + for (i = ARRAYSIZE(_notes_cache); i; --i, ++ptr) { + if (!ptr->off_time) + break; } - // Process the next event. - event = *pos++; - switch (event >> 4) { - case 0x9: // Note On - note = pos[0]; - vel = pos[1]; - pos += 2; - note_length = readVLQ (pos) * MICROSECONDS_PER_TICK; - - ptr = &_notes_cache[0]; - for (i = ARRAYSIZE(_notes_cache); i; --i, ++ptr) { - if (!ptr->time_left) { - ptr->time_left = note_length; - ptr->channel = event & 0x0F; - ptr->note = note; - if (transmit) - _driver->send (event | (note << 8) | (vel << 16)); - break; - } - } + if (i) { + ptr->channel = info.channel(); + ptr->note = info.param1; + ptr->off_time = _last_event_tick + info.delta + note_length; + } + break; + + case 0xC: case 0xD: + info.param1 = *(_play_pos++); + info.param2 = 0; + break; + + case 0x8: case 0xA: case 0xB: case 0xE: + info.param1 = *(_play_pos++); + info.param2 = *(_play_pos++); + break; + + case 0xF: // Meta or SysEx event + switch (info.event & 0x0F) { + case 0x2: // Song Position Pointer + info.param1 = *(_play_pos++); + info.param2 = *(_play_pos++); break; - 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 0x8: // Note Off (do these ever occur in XMIDI??) - 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); - - if (event == 0x2F) { - // End of song must be processed by us, - // as well as sending it to the output device. - ptr = &_notes_cache[0]; - for (i = ARRAYSIZE(_notes_cache); i; --i, ++ptr) { - if (ptr->time_left) { - if (transmit) - _driver->send (0x80 | ptr->channel | (ptr->note << 8)); - ptr->time_left = 0; - } - } - _play_pos = 0; - if (transmit) - _driver->metaEvent (event, pos, (uint16) length); - return; - } + case 0x0: // SysEx + info.length = readVLQ (_play_pos); + info.data = _play_pos; + _play_pos += info.length; + break; - 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; + if (info.type == 0x51 && info.length == 3) { + // Tempo event. We want to make these constant 500,000. + info.data[0] = 0x07; + info.data[1] = 0xA1; + info.data[2] = 0x20; } + break; } - - _last_event_time = event_time; } - - _play_time = end_time; - _play_pos = pos; } bool MidiParser_XMIDI::loadMusic (byte *data, uint32 size) { @@ -336,7 +234,7 @@ bool MidiParser_XMIDI::loadMusic (byte *data, uint32 size) { return false; } - _num_tracks = read2low (pos); + _num_tracks = (byte) read2low (pos); if (chunk_len > 2) { printf ("Chunk length %d is greater than 2\n", (int) chunk_len); @@ -415,7 +313,9 @@ bool MidiParser_XMIDI::loadMusic (byte *data, uint32 size) { // will persist beyond this call, i.e. we do NOT // copy the data to our own buffer. Take warning.... _data = data; - _active_track = 255; + _ppqn = 60; + resetTracking(); + _inserted_delta = 0; setTrack (0); return true; } @@ -424,45 +324,12 @@ bool MidiParser_XMIDI::loadMusic (byte *data, uint32 size) { } void MidiParser_XMIDI::unloadMusic() { - int i; - NoteTimer *ptr; - - _play_pos = NULL; - _data = NULL; + resetTracking(); + allNotesOff(); + _inserted_delta = 0; + _data = 0; _num_tracks = 0; - _active_track = 0; - _play_time = 0; - _last_event_time = 0; - - // Send any necessary note off events. - ptr = &_notes_cache[0]; - for (i = ARRAYSIZE(_notes_cache); i; --i, ++ptr) { - if (ptr->time_left) { - _driver->send ((0x80 | ptr->channel) | (ptr->note << 8)); - ptr->time_left = 0; - } - } -} - -void MidiParser_XMIDI::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]; -} - -void MidiParser_XMIDI::jumpToTick (uint32 tick) { - if (_active_track >= _num_tracks) - return; - _play_pos = _tracks[_active_track]; - _play_time = 0; - _last_event_time = 0; - if (tick > 0) { - printf ("jumpToTick (%ld) not completely implemented!\n", (long) tick); - playToTime (tick * MICROSECONDS_PER_TICK - 1, false); - } + _active_track = 255; } MidiParser *MidiParser::createParser_XMIDI() { return new MidiParser_XMIDI; } diff --git a/sound/module.mk b/sound/module.mk index 9f49440b71..9d502ef665 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -2,6 +2,7 @@ MODULE := sound MODULE_OBJS = \ sound/fmopl.o \ + sound/midiparser.o \ sound/midiparser_smf.o \ sound/midiparser_xmidi.o \ sound/mixer.o \ |