AUDIO: XMIDI: implement support for TIMB chunk

implement support for TIMB chunk inside XMIDI-parser (forwarding of data to driver)
implement actual support for TIMB chunk inside Miles Audio MT32 driver
This commit is contained in:
Martin Kiewitz 2015-06-30 16:05:01 +02:00
parent 4ff695524a
commit f7ac1e944a
4 changed files with 126 additions and 14 deletions

View File

@ -370,6 +370,7 @@ public:
public: public:
typedef void (*XMidiCallbackProc)(byte eventData, void *refCon); typedef void (*XMidiCallbackProc)(byte eventData, void *refCon);
typedef void (*XMidiNewTimbreListProc)(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize);
MidiParser(); MidiParser();
virtual ~MidiParser() { allNotesOff(); } virtual ~MidiParser() { allNotesOff(); }
@ -395,7 +396,7 @@ public:
static void defaultXMidiCallback(byte eventData, void *refCon); static void defaultXMidiCallback(byte eventData, void *refCon);
static MidiParser *createParser_SMF(); static MidiParser *createParser_SMF();
static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0); static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0, XMidiNewTimbreListProc newTimbreListProc = NULL, MidiDriver_BASE *newTimbreListDriver = NULL);
static MidiParser *createParser_QT(); static MidiParser *createParser_QT();
static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); } static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); }
}; };

View File

@ -43,6 +43,22 @@ protected:
XMidiCallbackProc _callbackProc; XMidiCallbackProc _callbackProc;
void *_callbackData; void *_callbackData;
// TODO:
// This should possibly get cleaned up at some point, but it's very tricks.
// We need to support XMIDI TIMB for 7th guest, which uses
// Miles Audio drivers. The MT32 driver needs to get the TIMB chunk, so that it
// can install all required timbres before the song starts playing.
// But we can't easily implement this directly like for example creating
// a special Miles Audio class for usage in this XMIDI-class, because other engines use this
// XMIDI-parser but w/o using Miles Audio drivers.
XMidiNewTimbreListProc _newTimbreListProc;
MidiDriver_BASE *_newTimbreListDriver;
byte *_tracksTimbreList[120]; ///< Timbre-List for each track.
uint32 _tracksTimbreListSize[120]; ///< Size of the Timbre-List for each track.
byte *_activeTrackTimbreList;
uint32 _activeTrackTimbreListSize;
protected: protected:
uint32 readVLQ2(byte * &data); uint32 readVLQ2(byte * &data);
void parseNextEvent(EventInfo &info); void parseNextEvent(EventInfo &info);
@ -53,7 +69,17 @@ protected:
} }
public: public:
MidiParser_XMIDI(XMidiCallbackProc proc, void *data) : _callbackProc(proc), _callbackData(data), _loopCount(-1) {} MidiParser_XMIDI(XMidiCallbackProc proc, void *data, XMidiNewTimbreListProc newTimbreListProc, MidiDriver_BASE *newTimbreListDriver) {
_callbackProc = proc;
_callbackData = data;
_loopCount = -1;
_newTimbreListProc = newTimbreListProc;
_newTimbreListDriver = newTimbreListDriver;
memset(_tracksTimbreList, 0, sizeof(_tracksTimbreList));
memset(_tracksTimbreListSize, 0, sizeof(_tracksTimbreListSize));
_activeTrackTimbreList = NULL;
_activeTrackTimbreListSize = 0;
}
~MidiParser_XMIDI() { } ~MidiParser_XMIDI() { }
bool loadMusic(byte *data, uint32 size); bool loadMusic(byte *data, uint32 size);
@ -322,11 +348,16 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
// Skip this. // Skip this.
pos += 4; pos += 4;
} else if (!memcmp(pos, "TIMB", 4)) { } else if (!memcmp(pos, "TIMB", 4)) {
// Custom timbres? // Custom timbres
// We don't support them. // chunk data is as follows:
// Read the length, skip it, and hope there was nothing there. // UINT16LE timbre count (amount of custom timbres used by this track)
// BYTE patchId
// BYTE bankId
// * timbre count
pos += 4; pos += 4;
len = read4high(pos); len = read4high(pos);
_tracksTimbreList[tracksRead] = pos; // Skip the length bytes
_tracksTimbreListSize[tracksRead] = len;
pos += (len + 1) & ~1; pos += (len + 1) & ~1;
} else if (!memcmp(pos, "EVNT", 4)) { } else if (!memcmp(pos, "EVNT", 4)) {
// Ahh! What we're looking for at last. // Ahh! What we're looking for at last.
@ -350,6 +381,12 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) {
resetTracking(); resetTracking();
setTempo(500000); setTempo(500000);
setTrack(0); setTrack(0);
_activeTrackTimbreList = _tracksTimbreList[0];
_activeTrackTimbreListSize = _tracksTimbreListSize[0];
if (_newTimbreListProc)
_newTimbreListProc(_newTimbreListDriver, _activeTrackTimbreList, _activeTrackTimbreListSize);
return true; return true;
} }
@ -360,6 +397,6 @@ void MidiParser::defaultXMidiCallback(byte eventData, void *data) {
warning("MidiParser: defaultXMidiCallback(%d)", eventData); warning("MidiParser: defaultXMidiCallback(%d)", eventData);
} }
MidiParser *MidiParser::createParser_XMIDI(XMidiCallbackProc proc, void *data) { MidiParser *MidiParser::createParser_XMIDI(XMidiCallbackProc proc, void *data, XMidiNewTimbreListProc newTimbreListProc, MidiDriver_BASE *newTimbreListDriver) {
return new MidiParser_XMIDI(proc, data); return new MidiParser_XMIDI(proc, data, newTimbreListProc, newTimbreListDriver);
} }

View File

@ -75,6 +75,8 @@ extern MidiDriver *MidiDriver_Miles_AdLib_create(const Common::String instrument
extern MidiDriver *MidiDriver_Miles_MT32_create(const Common::String instrumentDataFilename); extern MidiDriver *MidiDriver_Miles_MT32_create(const Common::String instrumentDataFilename);
extern void MidiDriver_Miles_MT32_processXMIDITimbreChunk(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize);
} // End of namespace Audio } // End of namespace Audio
#endif // AUDIO_MILES_MIDIDRIVER_H #endif // AUDIO_MILES_MIDIDRIVER_H

View File

@ -36,6 +36,9 @@ namespace Audio {
#define MILES_MT32_PATCHES_COUNT 128 #define MILES_MT32_PATCHES_COUNT 128
#define MILES_MT32_CUSTOMTIMBRE_COUNT 64 #define MILES_MT32_CUSTOMTIMBRE_COUNT 64
#define MILES_MT32_TIMBREBANK_STANDARD_ROLAND 0
#define MILES_MT32_TIMBREBANK_MELODIC_MODULE 127
#define MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE 14 #define MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE 14
#define MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE 58 #define MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE 58
#define MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT 4 #define MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT 4
@ -111,6 +114,7 @@ protected:
int _baseFreq; int _baseFreq;
public: public:
void processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize);
private: private:
void resetMT32(); void resetMT32();
@ -327,6 +331,7 @@ void MidiDriver_Miles_MT32::MT32SysEx(const uint32 targetAddress, const byte *da
delay += 40; delay += 40;
g_system->delayMillis(delay); g_system->delayMillis(delay);
g_system->updateScreen();
} }
// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php // MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php
@ -550,11 +555,6 @@ void MidiDriver_Miles_MT32::setupPatch(byte patchBank, byte patchId) {
if (patchBank) { if (patchBank) {
// non-built-in bank // non-built-in bank
int16 customTimbreId = searchCustomTimbre(patchBank, patchId); int16 customTimbreId = searchCustomTimbre(patchBank, patchId);
if (customTimbreId < 0) {
// currently not loaded, try to install it
// Miles Audio didn't do this here, I'm not exactly sure when it called the install code
customTimbreId = installCustomTimbre(patchBank, patchId);
}
if (customTimbreId >= 0) { if (customTimbreId >= 0) {
// now available? -> use this timbre // now available? -> use this timbre
writePatchTimbre(patchId, 2, customTimbreId); // Group MEMORY writePatchTimbre(patchId, 2, customTimbreId); // Group MEMORY
@ -571,11 +571,54 @@ void MidiDriver_Miles_MT32::setupPatch(byte patchBank, byte patchId) {
} }
} }
void MidiDriver_Miles_MT32::processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize) {
uint16 timbreCount = 0;
uint32 expectedSize = 0;
const byte *timbreListSeeker = timbreListPtr;
if (timbreListSize < 2) {
warning("MILES-MT32: XMIDI-TIMB chunk - not enough bytes in chunk");
return;
}
timbreCount = READ_LE_UINT16(timbreListPtr);
expectedSize = timbreCount * 2;
if (expectedSize > timbreListSize) {
warning("MILES-MT32: XMIDI-TIMB chunk - size mismatch");
return;
}
timbreListSeeker += 2;
while (timbreCount) {
const byte patchId = *timbreListSeeker++;
const byte patchBank = *timbreListSeeker++;
int16 customTimbreId = 0;
switch (patchBank) {
case MILES_MT32_TIMBREBANK_STANDARD_ROLAND:
case MILES_MT32_TIMBREBANK_MELODIC_MODULE:
// ignore those 2 banks
break;
default:
// Check, if this timbre was already loaded
customTimbreId = searchCustomTimbre(patchBank, patchId);
if (customTimbreId < 0) {
// currently not loaded, try to install it
installCustomTimbre(patchBank, patchId);
}
}
timbreCount--;
}
}
// //
int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) { int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) {
switch(patchBank) { switch(patchBank) {
case 0: // Standard Roland MT32 bank case MILES_MT32_TIMBREBANK_STANDARD_ROLAND: // Standard Roland MT32 bank
case 127: // Reserved for melodic mode case MILES_MT32_TIMBREBANK_MELODIC_MODULE: // Reserved for melodic mode
return -1; return -1;
default: default:
break; break;
@ -641,6 +684,25 @@ int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) {
uint32 targetAddressPartial3 = targetAddress + 0x000102; uint32 targetAddressPartial3 = targetAddress + 0x000102;
uint32 targetAddressPartial4 = targetAddress + 0x00013C; uint32 targetAddressPartial4 = targetAddress + 0x00013C;
#if 0
byte parameterData[MILES_MT32_PATCHDATA_TOTAL_SIZE + 1];
uint16 parameterDataPos = 0;
memcpy(parameterData, instrumentPtr->commonParameter, MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[0], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[1], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[2], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[3], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE);
parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE;
parameterData[parameterDataPos] = MILES_MT32_SYSEX_TERMINATOR;
MT32SysEx(targetAddressCommon, parameterData);
#endif
// upload common parameter data // upload common parameter data
MT32SysEx(targetAddressCommon, instrumentPtr->commonParameter); MT32SysEx(targetAddressCommon, instrumentPtr->commonParameter);
// upload partial parameter data // upload partial parameter data
@ -649,6 +711,8 @@ int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) {
MT32SysEx(targetAddressPartial3, instrumentPtr->partialParameters[2]); MT32SysEx(targetAddressPartial3, instrumentPtr->partialParameters[2]);
MT32SysEx(targetAddressPartial4, instrumentPtr->partialParameters[3]); MT32SysEx(targetAddressPartial4, instrumentPtr->partialParameters[3]);
setupPatch(patchBank, patchId);
return customTimbreId; return customTimbreId;
} }
@ -806,4 +870,12 @@ MidiDriver *MidiDriver_Miles_MT32_create(const Common::String instrumentDataFile
return new MidiDriver_Miles_MT32(instrumentTablePtr, instrumentTableCount); return new MidiDriver_Miles_MT32(instrumentTablePtr, instrumentTableCount);
} }
void MidiDriver_Miles_MT32_processXMIDITimbreChunk(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize) {
MidiDriver_Miles_MT32 *driverMT32 = dynamic_cast<MidiDriver_Miles_MT32 *>(driver);
if (driverMT32) {
driverMT32->processXMIDITimbreChunk(timbreListPtr, timbreListSize);
}
}
} // End of namespace Audio } // End of namespace Audio