From 220c118a8fd4459e8947b3896bef386dc8fe51c7 Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Sat, 16 Jan 2010 16:06:41 +0000 Subject: SCI: Add support for earlier MT-32 patch format svn-id: r47321 --- engines/sci/sound/music.cpp | 6 ++ engines/sci/sound/softseq/midi.cpp | 188 ++++++++++++++++++++++++++++++--- engines/sci/sound/softseq/mididriver.h | 7 ++ 3 files changed, 185 insertions(+), 16 deletions(-) (limited to 'engines') diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 55013f4db6..95e3c66221 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -317,6 +317,12 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { // Find out what channels to filter for SCI0 channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(_soundVersion)); + + // Enable rhythm channel when requested + channelFilterMask &= ~(1 << MIDI_RHYTHM_CHANNEL); + if (_pMidiDrv->hasRhythmChannel()) + channelFilterMask |= (1 << MIDI_RHYTHM_CHANNEL); + pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); // Fast forward to the last position and perform associated events when loading diff --git a/engines/sci/sound/softseq/midi.cpp b/engines/sci/sound/softseq/midi.cpp index ff774786f2..84a3d62df7 100644 --- a/engines/sci/sound/softseq/midi.cpp +++ b/engines/sci/sound/softseq/midi.cpp @@ -37,24 +37,34 @@ namespace Sci { class MidiPlayer_Midi : public MidiPlayer { public: enum { - kVoices = 32 + kVoices = 32, + kReverbConfigNr = 11, + kMaxSysExSize = 264 }; MidiPlayer_Midi(); virtual ~MidiPlayer_Midi(); int open(ResourceManager *resMan); + void close(); void send(uint32 b); + void sysEx(const byte *msg, uint16 length); bool hasRhythmChannel() const { return true; } byte getPlayId(SciVersion soundVersion); int getPolyphony() const { return kVoices; } void setVolume(byte volume); int getVolume(); + void setReverb(byte reverb); void playSwitch(bool play); private: bool isMt32GmPatch(const byte *data, int size); void readMt32GmPatch(const byte *data, int size); + void readMt32Patch(const byte *data, int size); + void sendMt32SysEx(const uint32 addr, Common::MemoryReadStream *str, int len, bool noDelay); + void sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay); + void setMt32Volume(byte volume); + void resetMt32(); void noteOn(int channel, int note, int velocity); void setPatch(int channel, int patch); @@ -76,10 +86,11 @@ private: }; bool _isMt32; - bool _isHardwareMt32; + bool _hasReverb; bool _playSwitch; int _masterVolume; + byte _reverbConfig[kReverbConfigNr][3]; Channel _channels[16]; uint8 _percussionMap[128]; int8 _keyShift[128]; @@ -87,18 +98,21 @@ private: uint8 _patchMap[128]; uint8 _velocityMapIdx[128]; uint8 _velocityMap[4][128]; + byte _goodbyeMsg[20]; + byte _sysExBuf[kMaxSysExSize]; }; -MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _isHardwareMt32(false) { +MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false) { MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI); _driver = createMidi(midiType); - if (midiType == MD_MT32) + if (midiType == MD_MT32 || ConfMan.getBool("native_mt32")) _isMt32 = true; - else if ((midiType != MD_FLUIDSYNTH) && (midiType != MD_TIMIDITY) && ConfMan.getBool("native_mt32")) { - _isMt32 = true; - _isHardwareMt32 = true; - } + + _sysExBuf[0] = 0x41; + _sysExBuf[1] = 0x10; + _sysExBuf[2] = 0x16; + _sysExBuf[3] = 0x12; } MidiPlayer_Midi::~MidiPlayer_Midi() { @@ -262,6 +276,12 @@ int MidiPlayer_Midi::getVolume() { return _masterVolume; } +void MidiPlayer_Midi::setReverb(byte reverb) { + _reverb = CLIP(reverb, 0, kReverbConfigNr - 1); + if (_hasReverb) + sendMt32SysEx(0x100001, _reverbConfig[_reverb], 3, true); +} + void MidiPlayer_Midi::playSwitch(bool play) { _playSwitch = play; if (play) @@ -302,6 +322,100 @@ bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) return isMt32Gm; } +void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::MemoryReadStream *str, int len, bool noDelay = false) { + if (len + 8 > kMaxSysExSize) { + warning("SysEx message exceed maximum size; ignoring"); + return; + } + + uint16 chk = 0; + + _sysExBuf[4] = (addr >> 16) & 0xff; + _sysExBuf[5] = (addr >> 8) & 0xff; + _sysExBuf[6] = addr & 0xff; + + for (int i = 0; i < len; i++) + _sysExBuf[7 + i] = str->readByte(); + + for (int i = 4; i < 7 + len; i++) + chk += _sysExBuf[i]; + + _sysExBuf[7 + len] = 128 - chk % 128; + + if (noDelay) + _driver->sysEx(_sysExBuf, len + 8); + else + sysEx(_sysExBuf, len + 8); +} + +void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay = false) { + Common::MemoryReadStream *str = new Common::MemoryReadStream(buf, len); + sendMt32SysEx(addr, str, len, noDelay); + delete str; +} + +void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { + Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size); + + // Send before-SysEx text + str->seek(0x14); + sendMt32SysEx(0x200000, str, 20); + + // Save goodbye message + str->read(_goodbyeMsg, 20); + + // Skip volume, we leave the MT-32 volume alone + str->seek(2, SEEK_CUR); + + // Reverb default only used in (roughly) SCI0/SCI01 + _reverb = str->readByte(); + _hasReverb = true; + + // Skip reverb SysEx message + str->seek(11, SEEK_CUR); + + // Read reverb data + for (int i = 0; i < kReverbConfigNr; i++) { + _reverbConfig[i][0] = str->readByte(); + _reverbConfig[i][1] = str->readByte(); + _reverbConfig[i][2] = str->readByte(); + } + + // Patches 1-48 + sendMt32SysEx(0x50000, str, 256); + sendMt32SysEx(0x50200, str, 128); + + // Timbres + byte timbresNr = str->readByte(); + for (int i = 0; i < timbresNr; i++) + sendMt32SysEx(0x80000 + (i << 9), str, 246); + + uint16 flag = str->readUint16BE(); + + if (!str->eos() && (flag == 0xabcd)) { + // Patches 49-96 + sendMt32SysEx(0x50300, str, 256); + sendMt32SysEx(0x50500, str, 128); + flag = str->readUint16BE(); + } + + if (!str->eos() && (flag == 0xdcba)) { + // Rhythm key map + sendMt32SysEx(0x30110, str, 256); + // Partial reserve + sendMt32SysEx(0x100004, str, 9); + } + + // Send after-SysEx text + str->seek(0); + sendMt32SysEx(0x200000, str, 20); + + // Send the mystery SysEx + sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6); + + delete str; +} + void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { memcpy(_patchMap, data, 0x80); memcpy(_keyShift, data + 0x80, 0x80); @@ -334,9 +448,7 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { error("Failed to find end of sysEx"); int len = sysExEnd - (midi + i); - _driver->sysEx(midi + i, len); - if (_isHardwareMt32) - g_system->delayMillis(100); + sysEx(midi + i, len); i += len + 1; // One more for the 0x7f break; @@ -368,6 +480,17 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { } } +void MidiPlayer_Midi::setMt32Volume(byte volume) { + sendMt32SysEx(0x100016, &volume, 1); +} + +void MidiPlayer_Midi::resetMt32() { + sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true); + + // This seems to require a longer delay than usual + g_system->delayMillis(150); +} + int MidiPlayer_Midi::open(ResourceManager *resMan) { assert(resMan != NULL); @@ -377,6 +500,11 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { return retval; } + if (_isMt32) { + resetMt32(); + setMt32Volume(80); + } + Resource *res = NULL; if (!_isMt32) { @@ -393,14 +521,19 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { if (isMt32GmPatch(res->data, res->size)) { readMt32GmPatch(res->data, res->size); + strncpy((char *)_goodbyeMsg, " ScummVM ", 20); } else { - // TODO - warning("Old MT-32 patch format currently not supported"); - for (uint i = 0; i < 127; i++) { + if (!_isMt32) { + warning("MT-32 to GM translation not yet supported"); + } + + readMt32Patch(res->data, res->size); + + // No mapping + for (uint i = 0; i < 128; i++) { _percussionMap[i] = i; _patchMap[i] = i; - for (uint j = 0; j < 4; j++) - _velocityMap[j][i] = i; + _velocityMap[0][i] = i; _keyShift[i] = 0; _volAdjust[i] = 0; _velocityMapIdx[i] = 0; @@ -410,6 +543,29 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { return 0; } +void MidiPlayer_Midi::close() { + if (_isMt32) { + // Send goodbye message + sendMt32SysEx(0x200000, _goodbyeMsg, 20); + } + + _driver->close(); +} + +void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) { + _driver->sysEx(msg, length); + + // Wait the time it takes to send the SysEx data + uint32 delay = (length + 2) * 1000 / 3125; + + // Plus an additional delay for the MT-32 rev00 + if (_isMt32) + delay += 40; + + g_system->delayMillis(delay); + g_system->updateScreen(); +} + byte MidiPlayer_Midi::getPlayId(SciVersion soundVersion) { switch (soundVersion) { case SCI_VERSION_0_EARLY: diff --git a/engines/sci/sound/softseq/mididriver.h b/engines/sci/sound/softseq/mididriver.h index 8a8813d062..a1e6390a06 100644 --- a/engines/sci/sound/softseq/mididriver.h +++ b/engines/sci/sound/softseq/mididriver.h @@ -67,7 +67,11 @@ enum { class MidiPlayer : public MidiDriver { protected: MidiDriver *_driver; + byte _reverb; + public: + MidiPlayer() : _reverb(0) { } + int open() { ResourceManager *resMan = ((SciEngine *)g_engine)->getResourceManager(); // HACK return open(resMan); @@ -93,6 +97,9 @@ public: return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0; } + virtual byte getReverb() { return _reverb; } + virtual void setReverb(byte reverb) { _reverb = reverb; } + virtual void playSwitch(bool play) { if (!play) { // Send "All Sound Off" on all channels -- cgit v1.2.3