MIDI: Fix XMIDI custom timbre loading

XMIDI files have an optional header defining custom timbres used for each
track. Before playback these timbres have to be sent to the MIDI device. The
old implementation of this functionality had two problems:

- The custom timbres were only sent during loading of the XMIDI data, for the
first track. If another track was played, the custom timbres for that track
would not be loaded.
- The custom timbres were sent after starting playback of the track. This
caused the sending of the timbres to sometimes overlap with the start of the
playback of the track, causing wrong instruments and bad timing.

If fixed these issues by moving the loading of the timbres to a new event
handler which is triggered during the setTrack function, before playback is
started.

This fixes problems with some MT-32 tracks in The 7th Guest, f.e. the second
track that plays on the main menu after starting the game.
This commit is contained in:
NMIError 2020-06-04 18:19:40 +02:00 committed by Eugene Sandulenko
parent 2f4937bd6d
commit 50772c1fbc
3 changed files with 23 additions and 10 deletions

View File

@ -372,6 +372,9 @@ bool MidiParser::setTrack(int track) {
memset(_activeNotes, 0, sizeof(_activeNotes));
if (_disableAutoStartPlayback)
_doParse = false;
onTrackStart(track);
_activeTrack = track;
_position._playPos = _tracks[track];
parseNextEvent(_nextEvent);

View File

@ -307,6 +307,13 @@ protected:
void hangingNote(byte channel, byte note, uint32 ticksLeft, bool recycle = true);
void hangAllActiveNotes();
/**
* Called before starting playback of a track.
* Can be implemented by subclasses if they need to
* perform actions at this point.
*/
virtual void onTrackStart(uint8 track) { };
virtual void sendToDriver(uint32 b);
void sendToDriver(byte status, byte firstOp, byte secondOp) {
sendToDriver(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16));

View File

@ -73,8 +73,6 @@ protected:
byte *_tracksTimbreList[120]; ///< Timbre-List for each track.
uint32 _tracksTimbreListSize[120]; ///< Size of the Timbre-List for each track.
byte *_activeTrackTimbreList;
uint32 _activeTrackTimbreListSize;
protected:
uint32 readVLQ2(byte * &data);
@ -92,6 +90,7 @@ protected:
MidiParser::resetTracking();
_loopCount = -1;
}
void onTrackStart(uint8 track) override;
void sendToDriver(uint32 b) override;
void sendMetaEventToDriver(byte type, byte *data, uint16 length) override;
@ -102,9 +101,7 @@ public:
_newTimbreListProc(newTimbreListProc),
_newTimbreListDriver(newTimbreListDriver),
_source(source),
_loopCount(-1),
_activeTrackTimbreList(NULL),
_activeTrackTimbreListSize(0) {
_loopCount(-1) {
memset(_loop, 0, sizeof(_loop));
memset(_trackBranches, 0, sizeof(_trackBranches));
memset(_tracksTimbreList, 0, sizeof(_tracksTimbreList));
@ -438,6 +435,9 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
int tracksRead = 0;
uint32 branchOffsets[128];
memset(branchOffsets, 0, sizeof(branchOffsets));
memset(_trackBranches, 0, sizeof(_trackBranches));
memset(_tracksTimbreList, 0, sizeof(_tracksTimbreList));
memset(_tracksTimbreListSize, 0, sizeof(_tracksTimbreListSize));
while (tracksRead < _numTracks) {
if (!memcmp(pos, "FORM", 4)) {
// Skip this plus the 4 bytes after it.
@ -511,12 +511,9 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
_ppqn = 60;
resetTracking();
setTempo(500000);
setTrack(0);
_activeTrackTimbreList = _tracksTimbreList[0];
_activeTrackTimbreListSize = _tracksTimbreListSize[0];
if (_newTimbreListProc)
_newTimbreListProc(_newTimbreListDriver, _activeTrackTimbreList, _activeTrackTimbreListSize);
// Start playback of the first track.
setTrack(0);
return true;
}
@ -524,6 +521,12 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
return false;
}
void MidiParser_XMIDI::onTrackStart(uint8 track) {
// Load custom timbres
if (_newTimbreListProc && _tracksTimbreListSize[track] > 0)
_newTimbreListProc(_newTimbreListDriver, _tracksTimbreList[track], _tracksTimbreListSize[track]);
}
void MidiParser_XMIDI::sendToDriver(uint32 b) {
if (_source < 0) {
MidiParser::sendToDriver(b);