mirror of
https://github.com/libretro/scummvm.git
synced 2024-11-27 11:20:40 +00:00
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:
parent
4ff695524a
commit
f7ac1e944a
@ -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(); }
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user