[Scummvm-cvs-logs] CVS: scummvm/sound midiparser_smf.cpp,1.5,1.6 midiparser_xmidi.cpp,1.4,1.5

Jamieson Christian jamieson630 at users.sourceforge.net
Mon May 19 11:49:09 CEST 2003


Update of /cvsroot/scummvm/scummvm/sound
In directory sc8-pr-cvs1:/tmp/cvs-serv29013/scummvm/sound

Modified Files:
	midiparser_smf.cpp midiparser_xmidi.cpp 
Log Message:
Moved common parsing logic into MidiParser base class.
Added auto-loop capability.

Index: midiparser_smf.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/midiparser_smf.cpp,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- midiparser_smf.cpp	19 May 2003 09:04:50 -0000	1.5
+++ midiparser_smf.cpp	19 May 2003 18:48:18 -0000	1.6
@@ -36,51 +36,18 @@
 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 @@
 	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::parseNextEvent (EventInfo &info) {
+	info.start = _play_pos;
+	info.delta = readVLQ (_play_pos);
 
-void MidiParser_SMF::onTimer() {
-	if (!_play_pos || !_driver)
+	// 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;
+	switch (info.event >> 4) {
+	case 0xC: case 0xD:
+		info.param1 = *(_play_pos++);
+		info.param2 = 0;
+		break;
 
-	end_time = psec;
-	pos = _play_pos;
+	case 0x8: case 0x9: case 0xA: case 0xB: case 0xE:
+		info.param1 = *(_play_pos++);
+		info.param2 = *(_play_pos++);
+		break;
 
-	while (true) {
-		oldpos = pos;
-		delta = readVLQ (pos);
-		event_time = _last_event_time + delta * _psec_per_tick;
-		if (event_time > end_time) {
-			pos = oldpos;
+	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);
-
-				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;
-					}
-				}
+		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;
+			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 @@
 	// 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 @@
 	*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; }

Index: midiparser_xmidi.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/midiparser_xmidi.cpp,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- midiparser_xmidi.cpp	19 May 2003 00:12:16 -0000	1.4
+++ midiparser_xmidi.cpp	19 May 2003 18:48:18 -0000	1.5
@@ -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 @@
 //
 //////////////////////////////////////////////////
 
-// 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 @@
 	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;
-		}
-
-		// 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;
+	// 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;
+	}
 
-			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;
-				}
-			}
-			break;
+	// 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);
 
-		case 0xC: // Program Change
-		case 0xD: // Channel Aftertouch
-			if (transmit)
-				_driver->send (event | (pos[0] << 8));
-			++pos;
-			break;
+		// 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;
+		}
 
-		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;
-			break;
+		if (i) {
+			ptr->channel = info.channel();
+			ptr->note = info.param1;
+			ptr->off_time = _last_event_tick + info.delta + note_length;
+		}
+		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 0xC: case 0xD:
+		info.param1 = *(_play_pos++);
+		info.param2 = 0;
+		break;
 
-			case 0x3: // Song Select
-				if (transmit)
-					_driver->send (event | (pos[0] << 8));
-				++pos;
-				break;
+	case 0x8: case 0xA: case 0xB: case 0xE:
+		info.param1 = *(_play_pos++);
+		info.param2 = *(_play_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 0xF: // Meta or SysEx event
+		switch (info.event & 0x0F) {
+		case 0x2: // Song Position Pointer
+			info.param1 = *(_play_pos++);
+			info.param2 = *(_play_pos++);
+			break;
 
-			case 0x0: // SysEx
-				length = readVLQ (pos);
-				if (transmit)
-					_driver->sysEx (pos, (uint16)(length - 1));
-				pos += length;
-				break;
+		case 0x3: // Song Select
+			info.param1 = *(_play_pos++);
+			info.param2 = 0;
+			break;
 
-			case 0xF: // META event
-				event = *pos++;
-				length = readVLQ (pos);
+		case 0x6: case 0x8: case 0xA: case 0xB: case 0xC: case 0xE:
+			info.param1 = info.param2 = 0;
+			break;
 
-				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 @@
 					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 @@
 		// 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 @@
 }
 
 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; }





More information about the Scummvm-git-logs mailing list