[Scummvm-cvs-logs] CVS: scummvm/sound midiparser.cpp,1.4,1.5 midiparser.h,1.9,1.10 midiparser_smf.cpp,1.9,1.10 midiparser_xmidi.cpp,1.8,1.9

Jamieson Christian jamieson630 at users.sourceforge.net
Thu May 22 08:35:14 CEST 2003


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

Modified Files:
	midiparser.cpp midiparser.h midiparser_smf.cpp 
	midiparser_xmidi.cpp 
Log Message:
Added "smart-jump" capability to MidiParser.

Index: midiparser.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/midiparser.cpp,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- midiparser.cpp	21 May 2003 06:14:14 -0000	1.4
+++ midiparser.cpp	22 May 2003 15:34:30 -0000	1.5
@@ -39,19 +39,26 @@
 _tempo (500000),
 _psec_per_tick (5208), // 500000 / 96
 _autoLoop (false),
+_smartJump (false),
 _num_tracks (0),
 _active_track (255),
 _play_pos (0),
 _play_time (0),
+_play_tick (0),
 _last_event_time (0),
 _last_event_tick (0),
-_running_status (0)
-{ }
+_running_status (0),
+_hanging_notes_count (0)
+{
+	memset (_active_notes, 0, 128);
+}
 
 void MidiParser::property (int prop, int value) {
 	switch (prop) {
 	case mpAutoLoop:
 		_autoLoop = (value != 0);
+	case mpSmartJump:
+		_smartJump = (value != 0);
 	}
 }
 
@@ -71,6 +78,61 @@
 	return value;
 }
 
+void MidiParser::activeNote (byte channel, byte note, bool active) {
+	if (note >= 128 || channel >= 16)
+		return;
+
+	if (active)
+		_active_notes[note] |= (1 << channel);
+	else
+		_active_notes[note] &= ~(1 << channel);
+
+	// See if there are hanging notes that we can cancel
+	NoteTimer *ptr = _hanging_notes;
+	int i;
+	for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) {
+		if (ptr->channel == channel && ptr->note == note && ptr->time_left) {
+			ptr->time_left = 0;
+			--_hanging_notes_count;
+			break;
+		}
+	}
+}
+
+void MidiParser::hangingNote (byte channel, byte note, uint32 time_left) {
+	NoteTimer *best = 0;
+	NoteTimer *ptr = _hanging_notes;
+	int i;
+
+	if (_hanging_notes_count >= ARRAYSIZE(_hanging_notes)) {
+		printf ("WARNING! MidiParser::hangingNote(): Exceeded polyphony!\n");
+		return;
+	}
+
+	for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) {
+		if (ptr->channel == channel && ptr->note == note) {
+			if (ptr->time_left && ptr->time_left < time_left)
+				return;
+			best = ptr;
+			if (ptr->time_left)
+				--_hanging_notes_count;
+			break;
+		} else if (!best && ptr->time_left == 0) {
+			best = ptr;
+		}
+	}
+
+	if (best) {
+		best->channel = channel;
+		best->note = note;
+		best->time_left = time_left;
+		++_hanging_notes_count;
+	} else {
+		// We checked this up top. We should never get here!
+		printf ("WARNING! MidiParser::hangingNote(): Internal error!\n");
+	}
+}
+
 void MidiParser::onTimer() {
 	uint32 end_time;
 	uint32 event_time;
@@ -80,6 +142,24 @@
 
 	end_time = _play_time + _timer_rate;
 
+	// Scan our hanging notes for any
+	// that should be turned off.
+	if (_hanging_notes_count) {
+		NoteTimer *ptr = &_hanging_notes[0];
+		int i;
+		for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) {
+			if (ptr->time_left) {
+				if (ptr->time_left <= _timer_rate) {
+					_driver->send (0x80 | ptr->channel | ptr->note << 8);
+					ptr->time_left = 0;
+					--_hanging_notes_count;
+				} else {
+					ptr->time_left -= _timer_rate;
+				}
+			}
+		}
+	}
+
 	while (true) {
 		EventInfo &info = _next_event;
 
@@ -94,11 +174,10 @@
 			_play_pos = 0;
 			return;
 		}
-		_running_status = info.event;
 
 		if (info.event == 0xF0) {
 			// SysEx event
-			_driver->sysEx (info.ext.data, (uint16) info.ext.length);
+			_driver->sysEx (info.ext.data, (uint16) info.length);
 		} else if (info.event == 0xFF) {
 			// META event
 			if (info.ext.type == 0x2F) {
@@ -110,17 +189,25 @@
 					parseNextEvent (_next_event);
 				} else {
 					_play_pos = 0;
-					_driver->metaEvent (info.ext.type, info.ext.data, (uint16) info.ext.length);
+					_driver->metaEvent (info.ext.type, info.ext.data, (uint16) info.length);
 				}
 				return;
 			} else if (info.ext.type == 0x51) {
-				if (info.ext.length >= 3) {
+				if (info.length >= 3) {
 					_tempo = info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2];
 					_psec_per_tick = (_tempo + (_ppqn >> 2)) / _ppqn;
 				}
 			}
-			_driver->metaEvent (info.ext.type, info.ext.data, (uint16) info.ext.length);
+			_driver->metaEvent (info.ext.type, info.ext.data, (uint16) info.length);
 		} else {
+			if (info.command() == 0x8) {
+				activeNote (info.channel(), info.basic.param1, false);
+			} else if (info.command() == 0x9) {
+				if (info.length > 0)
+					hangingNote (info.channel(), info.basic.param1, info.length * _psec_per_tick - (end_time - event_time));
+				else
+					activeNote (info.channel(), info.basic.param1, true);
+			}
 			_driver->send (info.event | info.basic.param1 << 8 | info.basic.param2 << 16);
 		}
 
@@ -130,6 +217,7 @@
 	}
 
 	_play_time = end_time;
+	_play_tick = (_play_time - _last_event_time) / _psec_per_tick + _last_event_tick;
 }
 
 void MidiParser::allNotesOff() {
@@ -137,9 +225,12 @@
 		return;
 
 	int i;
-	for (i = 0; i < 15; ++i) {
+	for (i = 0; i < 15; ++i)
 		_driver->send (0x007BB0 | i);
-	}
+	for (i = 0; i < ARRAYSIZE(_hanging_notes); ++i)
+		_hanging_notes[i].time_left = 0;
+	_hanging_notes_count = 0;
+	memset (_active_notes, 0, 128);
 }
 
 void MidiParser::resetTracking() {
@@ -147,6 +238,7 @@
 	_tempo = 500000;
 	_psec_per_tick = 500000 / _ppqn;
 	_play_time = 0;
+	_play_tick = 0;
 	_last_event_time = 0;
 	_last_event_tick = 0;
 	_running_status = 0;
@@ -169,9 +261,32 @@
 void MidiParser::jumpToTick (uint32 tick) {
 	if (_active_track >= _num_tracks)
 		return;
-	resetTracking();
-	allNotesOff();
 
+	if (!_smartJump) {
+		allNotesOff();
+	} else {
+		// Search for note off events until we have
+		// accounted for every active note.
+		uint32 advance_tick = _last_event_tick;
+		while (true) {
+			int i;
+			for (i = 0; i < 128; ++i)
+				if (_active_notes[i] != 0) break;
+			if (i == 128) break;
+			parseNextEvent (_next_event);
+			advance_tick += _next_event.delta;
+			if (_next_event.command() == 0x8) {
+				if (_active_notes [_next_event.basic.param1] & (1 << _next_event.channel())) {
+					hangingNote (_next_event.channel(), _next_event.basic.param1, (advance_tick - _last_event_tick) * _psec_per_tick);
+					_active_notes [_next_event.basic.param1] &= ~ (1 << _next_event.channel());
+				}
+			} else if (_next_event.event == 0xFF && _next_event.ext.type == 0x2F) {
+				break;
+			}
+		}
+	}
+
+	resetTracking();
 	_play_pos = _tracks[_active_track];
 	parseNextEvent (_next_event);
 	if (tick == 0)
@@ -181,10 +296,12 @@
 		EventInfo &info = _next_event;
 		if (_last_event_tick + info.delta >= tick) {
 			_play_time += (tick - _last_event_tick) * _psec_per_tick;
+			_play_tick = tick;
 			break;
 		}
 
 		_last_event_tick += info.delta;
+		_play_tick = _last_event_tick;
 		_play_time += info.delta * _psec_per_tick;
 		_last_event_time = _play_time;
 
@@ -195,11 +312,11 @@
 					parseNextEvent (_next_event);
 				} else {
 					_play_pos = 0;
-					_driver->metaEvent (0x2F, info.ext.data, (uint16) info.ext.length);
+					_driver->metaEvent (0x2F, info.ext.data, (uint16) info.length);
 				}
 				break;
 			} else if (info.ext.type == 0x51) { // Tempo
-				if (info.ext.length >= 3) {
+				if (info.length >= 3) {
 					_tempo = info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2];
 					_psec_per_tick = (_tempo + (_ppqn >> 2)) / _ppqn;
 				}

Index: midiparser.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/midiparser.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- midiparser.h	21 May 2003 06:14:14 -0000	1.9
+++ midiparser.h	22 May 2003 15:34:30 -0000	1.10
@@ -40,15 +40,27 @@
 		struct {
 			byte   type; // Used for METAs
 			byte * data; // Used for SysEx and METAs
-			uint32 length; // Used for SysEx and METAs
 		} ext;
 	};
+	uint32 length; // Used for SysEx and METAs, and note lengths
 
 	byte channel() { return event & 0x0F; }
 	byte command() { return event >> 4; }
 };
 
+struct NoteTimer {
+	byte channel;
+	byte note;
+	uint32 time_left;
+	NoteTimer() : channel(0), note(0), time_left(0) {}
+};
+
 class MidiParser {
+private:
+	uint16    _active_notes[128]; // Each uint16 is a bit mask for channels that have that note on
+	NoteTimer _hanging_notes[32]; // Supports "smart" jump with notes still playing
+	byte      _hanging_notes_count;
+
 protected:
 	MidiDriver *_driver;
 	uint32 _timer_rate;
@@ -56,6 +68,7 @@
 	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
+	bool   _smartJump;      // Support smart expiration of hanging notes when jumping
 
 	byte * _tracks[16];
 	byte   _num_tracks;
@@ -63,9 +76,10 @@
 
 	byte * _play_pos;
 	uint32 _play_time;
+	uint32 _play_tick;
 	uint32 _last_event_time;
 	uint32 _last_event_tick;
-	byte   _running_status; // Cache of last MIDI command, used in compressed streams
+	byte   _running_status;
 	EventInfo _next_event;
 
 protected:
@@ -74,6 +88,9 @@
 	virtual void allNotesOff();
 	virtual void parseNextEvent (EventInfo &info) = 0;
 
+	void activeNote (byte channel, byte note, bool active);
+	void hangingNote (byte channel, byte note, uint32 ticks_left);
+
 	// Multi-byte read helpers
 	uint32 read4high (byte * &data) {
 		uint32 val = 0;
@@ -91,7 +108,8 @@
 public:
 	enum {
 		mpMalformedPitchBends = 1,
-		mpAutoLoop = 2
+		mpAutoLoop = 2,
+		mpSmartJump = 3
 	};
 
 public:

Index: midiparser_smf.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/midiparser_smf.cpp,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- midiparser_smf.cpp	20 May 2003 20:58:59 -0000	1.9
+++ midiparser_smf.cpp	22 May 2003 15:34:30 -0000	1.10
@@ -97,7 +97,7 @@
 	if (info.event < 0x80)
 		return;
 
-	switch (info.event >> 4) {
+	switch (info.command()) {
 	case 0xC: case 0xD:
 		info.basic.param1 = *(_play_pos++);
 		info.basic.param2 = 0;
@@ -106,6 +106,9 @@
 	case 0x8: case 0x9: case 0xA: case 0xB: case 0xE:
 		info.basic.param1 = *(_play_pos++);
 		info.basic.param2 = *(_play_pos++);
+		if (info.command() == 0x9 && info.basic.param2 == 0)
+			info.event = info.channel() | 0x80;
+		info.length = 0;
 		break;
 
 	case 0xF: // System Common, Meta or SysEx event
@@ -125,16 +128,16 @@
 			break;
 
 		case 0x0: // SysEx
-			info.ext.length = readVLQ (_play_pos);
+			info.length = readVLQ (_play_pos);
 			info.ext.data = _play_pos;
-			_play_pos += info.ext.length;
+			_play_pos += info.length;
 			break;
 
 		case 0xF: // META event
 			info.ext.type = *(_play_pos++);
-			info.ext.length = readVLQ (_play_pos);
+			info.length = readVLQ (_play_pos);
 			info.ext.data = _play_pos;
-			_play_pos += info.ext.length;
+			_play_pos += info.length;
 			break;
 		}
 	}

Index: midiparser_xmidi.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/sound/midiparser_xmidi.cpp,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- midiparser_xmidi.cpp	20 May 2003 14:55:47 -0000	1.8
+++ midiparser_xmidi.cpp	22 May 2003 15:34:30 -0000	1.9
@@ -32,13 +32,6 @@
 //
 //////////////////////////////////////////////////
 
-struct NoteTimer {
-	byte channel;
-	byte note;
-	uint32 off_time;
-	NoteTimer() : channel(0), note(0), off_time(0) {}
-};
-
 class MidiParser_XMIDI : public MidiParser {
 protected:
 	byte *_data;
@@ -47,7 +40,6 @@
 
 protected:
 	uint32 readVLQ2 (byte * &data);
-	void allNotesOff();
 	void resetTracking();
 	void parseNextEvent (EventInfo &info);
 
@@ -87,30 +79,6 @@
 	info.start = _play_pos;
 	info.delta = readVLQ2 (_play_pos) - _inserted_delta;
 
-	// 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;
-	NoteTimer *best = 0;
-	NoteTimer *ptr = &_notes_cache[0];
-	int i;
-	for (i = ARRAYSIZE(_notes_cache); i; --i, ++ptr) {
-		if (ptr->off_time && ptr->off_time >= _last_event_tick && (!best || ptr->off_time < best->off_time))
-			best = ptr;
-	}
-
-	// 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.basic.param1 = best->note;
-		info.basic.param2 = 0;
-		best->off_time = 0;
-		_inserted_delta += info.delta;
-		return;
-	}
-
 	// Process the next event.
 	_inserted_delta = 0;
 	info.event = *(_play_pos++);
@@ -118,20 +86,10 @@
 	case 0x9: // Note On
 		info.basic.param1 = *(_play_pos++);
 		info.basic.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;
-		}
-
-		if (i) {
-			ptr->channel = info.channel();
-			ptr->note = info.basic.param1;
-			ptr->off_time = _last_event_tick + info.delta + note_length;
+		info.length = readVLQ (_play_pos);
+		if (info.basic.param2 == 0) {
+			info.event = info.channel() | 0x80;
+			info.length = 0;
 		}
 		break;
 
@@ -162,17 +120,17 @@
 			break;
 
 		case 0x0: // SysEx
-			info.ext.length = readVLQ (_play_pos);
+			info.length = readVLQ (_play_pos);
 			info.ext.data = _play_pos;
-			_play_pos += info.ext.length;
+			_play_pos += info.length;
 			break;
 
 		case 0xF: // META event
 			info.ext.type = *(_play_pos++);
-			info.ext.length = readVLQ (_play_pos);
+			info.length = readVLQ (_play_pos);
 			info.ext.data = _play_pos;
-			_play_pos += info.ext.length;
-			if (info.ext.type == 0x51 && info.ext.length == 3) {
+			_play_pos += info.length;
+			if (info.ext.type == 0x51 && info.length == 3) {
 				// Tempo event. We want to make these constant 500,000.
 				info.ext.data[0] = 0x07;
 				info.ext.data[1] = 0xA1;
@@ -334,15 +292,6 @@
 	_data = 0;
 	_num_tracks = 0;
 	_active_track = 255;
-}
-
-void MidiParser_XMIDI::allNotesOff() {
-	MidiParser::allNotesOff();
-
-	// Reset the list of active notes.
-	int i;
-	for (i = 0; i < ARRAYSIZE(_notes_cache); ++i)
-		_notes_cache[i].off_time = 0;
 }
 
 void MidiParser_XMIDI::resetTracking() {





More information about the Scummvm-git-logs mailing list