aboutsummaryrefslogtreecommitdiff
path: root/sound/midiparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sound/midiparser.cpp')
-rw-r--r--sound/midiparser.cpp207
1 files changed, 207 insertions, 0 deletions
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);
+ }
+}