From f7ac1e944a273923735f1a79335cd043040b2c6a Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Tue, 30 Jun 2015 16:05:01 +0200 Subject: [PATCH] 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 --- audio/midiparser.h | 3 +- audio/midiparser_xmidi.cpp | 49 +++++++++++++++++++--- audio/miles.h | 2 + audio/miles_mt32.cpp | 86 ++++++++++++++++++++++++++++++++++---- 4 files changed, 126 insertions(+), 14 deletions(-) diff --git a/audio/midiparser.h b/audio/midiparser.h index 9c10462cd76..2cca56b14c9 100644 --- a/audio/midiparser.h +++ b/audio/midiparser.h @@ -370,6 +370,7 @@ public: public: typedef void (*XMidiCallbackProc)(byte eventData, void *refCon); + typedef void (*XMidiNewTimbreListProc)(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize); MidiParser(); virtual ~MidiParser() { allNotesOff(); } @@ -395,7 +396,7 @@ public: static void defaultXMidiCallback(byte eventData, void *refCon); 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 void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); } }; diff --git a/audio/midiparser_xmidi.cpp b/audio/midiparser_xmidi.cpp index 95aa5d72f3c..8742d7aad17 100644 --- a/audio/midiparser_xmidi.cpp +++ b/audio/midiparser_xmidi.cpp @@ -43,6 +43,22 @@ protected: XMidiCallbackProc _callbackProc; 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: uint32 readVLQ2(byte * &data); void parseNextEvent(EventInfo &info); @@ -53,7 +69,17 @@ protected: } 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() { } bool loadMusic(byte *data, uint32 size); @@ -322,11 +348,16 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { // Skip this. pos += 4; } else if (!memcmp(pos, "TIMB", 4)) { - // Custom timbres? - // We don't support them. - // Read the length, skip it, and hope there was nothing there. + // Custom timbres + // chunk data is as follows: + // UINT16LE timbre count (amount of custom timbres used by this track) + // BYTE patchId + // BYTE bankId + // * timbre count pos += 4; len = read4high(pos); + _tracksTimbreList[tracksRead] = pos; // Skip the length bytes + _tracksTimbreListSize[tracksRead] = len; pos += (len + 1) & ~1; } else if (!memcmp(pos, "EVNT", 4)) { // Ahh! What we're looking for at last. @@ -350,6 +381,12 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { resetTracking(); setTempo(500000); setTrack(0); + _activeTrackTimbreList = _tracksTimbreList[0]; + _activeTrackTimbreListSize = _tracksTimbreListSize[0]; + + if (_newTimbreListProc) + _newTimbreListProc(_newTimbreListDriver, _activeTrackTimbreList, _activeTrackTimbreListSize); + return true; } @@ -360,6 +397,6 @@ void MidiParser::defaultXMidiCallback(byte eventData, void *data) { warning("MidiParser: defaultXMidiCallback(%d)", eventData); } -MidiParser *MidiParser::createParser_XMIDI(XMidiCallbackProc proc, void *data) { - return new MidiParser_XMIDI(proc, data); +MidiParser *MidiParser::createParser_XMIDI(XMidiCallbackProc proc, void *data, XMidiNewTimbreListProc newTimbreListProc, MidiDriver_BASE *newTimbreListDriver) { + return new MidiParser_XMIDI(proc, data, newTimbreListProc, newTimbreListDriver); } diff --git a/audio/miles.h b/audio/miles.h index f9306069a7e..08586233fce 100644 --- a/audio/miles.h +++ b/audio/miles.h @@ -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 void MidiDriver_Miles_MT32_processXMIDITimbreChunk(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize); + } // End of namespace Audio #endif // AUDIO_MILES_MIDIDRIVER_H diff --git a/audio/miles_mt32.cpp b/audio/miles_mt32.cpp index 889dad3bc55..d28006e7729 100644 --- a/audio/miles_mt32.cpp +++ b/audio/miles_mt32.cpp @@ -36,6 +36,9 @@ namespace Audio { #define MILES_MT32_PATCHES_COUNT 128 #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_PARTIALPARAMETER_SIZE 58 #define MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT 4 @@ -111,6 +114,7 @@ protected: int _baseFreq; public: + void processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize); private: void resetMT32(); @@ -327,6 +331,7 @@ void MidiDriver_Miles_MT32::MT32SysEx(const uint32 targetAddress, const byte *da delay += 40; g_system->delayMillis(delay); + g_system->updateScreen(); } // 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) { // non-built-in bank 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) { // now available? -> use this timbre 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) { switch(patchBank) { - case 0: // Standard Roland MT32 bank - case 127: // Reserved for melodic mode + case MILES_MT32_TIMBREBANK_STANDARD_ROLAND: // Standard Roland MT32 bank + case MILES_MT32_TIMBREBANK_MELODIC_MODULE: // Reserved for melodic mode return -1; default: break; @@ -641,6 +684,25 @@ int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) { uint32 targetAddressPartial3 = targetAddress + 0x000102; 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 MT32SysEx(targetAddressCommon, instrumentPtr->commonParameter); // upload partial parameter data @@ -649,6 +711,8 @@ int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) { MT32SysEx(targetAddressPartial3, instrumentPtr->partialParameters[2]); MT32SysEx(targetAddressPartial4, instrumentPtr->partialParameters[3]); + setupPatch(patchBank, patchId); + return customTimbreId; } @@ -806,4 +870,12 @@ MidiDriver *MidiDriver_Miles_MT32_create(const Common::String instrumentDataFile 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(driver); + + if (driverMT32) { + driverMT32->processXMIDITimbreChunk(timbreListPtr, timbreListSize); + } +} + } // End of namespace Audio