Beispiel #1
0
void MidiParser::hangAllActiveNotes() {
	// Search for note off events until we have
	// accounted for every active note.
	uint16 temp_active[128];
	memcpy(temp_active, _active_notes, sizeof (temp_active));

	uint32 advance_tick = _position._last_event_tick;
	while (true) {
		int i;
		for (i = 0; i < 128; ++i)
			if (temp_active[i] != 0)
				break;
		if (i == 128)
			break;
		parseNextEvent(_next_event);
		advance_tick += _next_event.delta;
		if (_next_event.command() == 0x8) {
			if (temp_active[_next_event.basic.param1] & (1 << _next_event.channel())) {
				hangingNote(_next_event.channel(), _next_event.basic.param1, (advance_tick - _position._last_event_tick) * _psec_per_tick, false);
				temp_active[_next_event.basic.param1] &= ~(1 << _next_event.channel());
			}
		} else if (_next_event.event == 0xFF && _next_event.ext.type == 0x2F) {
			// warning("MidiParser::hangAllActiveNotes(): Hit End of Track with active notes left");
			for (i = 0; i < 128; ++i) {
				for (int j = 0; j < 16; ++j) {
					if (temp_active[i] & (1 << j)) {
						activeNote(j, i, false);
						sendToDriver(0x80 | j, i, 0);
					}
				}
			}
			break;
		}
	}
}
Beispiel #2
0
bool MidiParser::setTrack(int track) {
	if (track < 0 || track >= _num_tracks)
		return false;
	// We allow restarting the track via setTrack when
	// it isn't playing anymore. This allows us to reuse
	// a MidiParser when a track has finished and will
	// be restarted via setTrack by the client again.
	// This isn't exactly how setTrack behaved before though,
	// the old MidiParser code did not allow setTrack to be
	// used to restart a track, which was already finished.
	//
	// TODO: Check if any engine has problem with this
	// handling, if so we need to find a better way to handle
	// track restarts. (KYRA relies on this working)
	else if (track == _active_track && isPlaying())
		return true;

	if (_smartJump)
		hangAllActiveNotes();
	else
		allNotesOff();

	resetTracking();
	memset(_active_notes, 0, sizeof(_active_notes));
	_active_track = track;
	_position._play_pos = _tracks[track];
	parseNextEvent(_next_event);
	return true;
}
Beispiel #3
0
bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool dontSendNoteOn) {
	if (_active_track >= _num_tracks)
		return false;

	Tracker currentPos(_position);
	EventInfo currentEvent(_next_event);

	resetTracking();
	_position._play_pos = _tracks[_active_track];
	parseNextEvent(_next_event);
	if (tick > 0) {
		while (true) {
			EventInfo &info = _next_event;
			if (_position._last_event_tick + info.delta >= tick) {
				_position._play_time += (tick - _position._last_event_tick) * _psec_per_tick;
				_position._play_tick = tick;
				break;
			}

			_position._last_event_tick += info.delta;
			_position._last_event_time += info.delta * _psec_per_tick;
			_position._play_tick = _position._last_event_tick;
			_position._play_time = _position._last_event_time;

			if (info.event == 0xFF) {
				if (info.ext.type == 0x2F) { // End of track
					_position = currentPos;
					_next_event = currentEvent;
					return false;
				} else {
					if (info.ext.type == 0x51 && info.length >= 3) // Tempo
						setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
					if (fireEvents)
						_driver->metaEvent(info.ext.type, info.ext.data, (uint16) info.length);
				}
			} else if (fireEvents) {
				if (info.event == 0xF0) {
					if (info.ext.data[info.length-1] == 0xF7)
						_driver->sysEx(info.ext.data, (uint16)info.length-1);
					else
						_driver->sysEx(info.ext.data, (uint16)info.length);
				} else {
					// The note on sending code is used by the SCUMM engine. Other engine using this code
					// (such as SCI) have issues with this, as all the notes sent can be heard when a song
					// is fast-forwarded.	Thus, if the engine requests it, don't send note on events.
					if (info.command() == 0x9 && dontSendNoteOn) {
						// Don't send note on; doing so creates a "warble" with some instruments on the MT-32.
						// Refer to patch #3117577
					} else {
						sendToDriver(info.event, info.basic.param1, info.basic.param2);
					}
				}
			}

			parseNextEvent(_next_event);
		}
	}

	if (stopNotes) {
		if (!_smartJump || !currentPos._play_pos) {
			allNotesOff();
		} else {
			EventInfo targetEvent(_next_event);
			Tracker targetPosition(_position);

			_position = currentPos;
			_next_event = currentEvent;
			hangAllActiveNotes();

			_next_event = targetEvent;
			_position = targetPosition;
		}
	}

	_abort_parse = true;
	return true;
}
Beispiel #4
0
void MidiParser::onTimer() {
	uint32 end_time;
	uint32 event_time;

	if (!_position._play_pos || !_driver)
		return;

	_abort_parse = false;
	end_time = _position._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) {
					sendToDriver(0x80 | ptr->channel, ptr->note, 0);
					ptr->time_left = 0;
					--_hanging_notes_count;
				} else {
					ptr->time_left -= _timer_rate;
				}
			}
		}
	}

	while (!_abort_parse) {
		EventInfo &info = _next_event;

		event_time = _position._last_event_time + info.delta * _psec_per_tick;
		if (event_time > end_time)
			break;

		// Process the next info.
		_position._last_event_tick += info.delta;
		if (info.event < 0x80) {
			warning("Bad command or running status %02X", info.event);
			_position._play_pos = 0;
			return;
		}

		if (info.event == 0xF0) {
			// SysEx event
			// Check for trailing 0xF7 -- if present, remove it.
			if (info.ext.data[info.length-1] == 0xF7)
				_driver->sysEx(info.ext.data, (uint16)info.length-1);
			else
				_driver->sysEx(info.ext.data, (uint16)info.length);
		} else if (info.event == 0xFF) {
			// META event
			if (info.ext.type == 0x2F) {
				// End of Track must be processed by us,
				// as well as sending it to the output device.
				if (_autoLoop) {
					jumpToTick(0);
					parseNextEvent(_next_event);
				} else {
					stopPlaying();
					_driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length);
				}
				return;
			} else if (info.ext.type == 0x51) {
				if (info.length >= 3) {
					setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
				}
			}
			_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);
			}
			sendToDriver(info.event, info.basic.param1, info.basic.param2);
		}


		if (!_abort_parse) {
			_position._last_event_time = event_time;
			parseNextEvent(_next_event);
		}
	}

	if (!_abort_parse) {
		_position._play_time = end_time;
		_position._play_tick = (_position._play_time - _position._last_event_time) / _psec_per_tick + _position._last_event_tick;
	}
}
Beispiel #5
0
void MidiParser_S1D::chainEvent(EventInfo &info) {
	// When we chain an event, we add up the old delta.
	uint32 delta = info.delta;
	parseNextEvent(info);
	info.delta += delta;
}
Beispiel #6
0
bool MidiParser::jumpToTick(uint32 tick, bool fireEvents) {
	if (_active_track >= _num_tracks)
		return false;

	Tracker currentPos(_position);
	EventInfo currentEvent(_next_event);

	resetTracking();
	_position._play_pos = _tracks[_active_track];
	parseNextEvent(_next_event);
	if (tick > 0) {
		while (true) {
			EventInfo &info = _next_event;
			if (_position._last_event_tick + info.delta >= tick) {
				_position._play_time += (tick - _position._last_event_tick) * _psec_per_tick;
				_position._play_tick = tick;
				break;
			}

			_position._last_event_tick += info.delta;
			_position._last_event_time += info.delta * _psec_per_tick;
			_position._play_tick = _position._last_event_tick;
			_position._play_time = _position._last_event_time;

			if (info.event == 0xFF) {
				if (info.ext.type == 0x2F) { // End of track
					_position = currentPos;
					_next_event = currentEvent;
					return false;
				} else {
					if (info.ext.type == 0x51 && info.length >= 3) // Tempo
						setTempo(info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
					if (fireEvents)
						_driver->metaEvent(info.ext.type, info.ext.data, (uint16) info.length);
				}
			} else if (fireEvents) {
				if (info.event == 0xF0) {
					if (info.ext.data[info.length-1] == 0xF7)
						_driver->sysEx(info.ext.data, (uint16)info.length-1);
					else
						_driver->sysEx(info.ext.data, (uint16)info.length);
				} else
					_driver->send(info.event, info.basic.param1, info.basic.param2);
			}

			parseNextEvent(_next_event);
		}
	}

	if (!_smartJump || !currentPos._play_pos) {
		allNotesOff();
	} else {
		EventInfo targetEvent(_next_event);
		Tracker targetPosition(_position);

		_position = currentPos;
		_next_event = currentEvent;
		hangAllActiveNotes();

		_next_event = targetEvent;
		_position = targetPosition;
	}

	_abort_parse = true;
	return true;
}