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 \ | 
