/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "sci/sci.h" #include "common/config-manager.h" #include "common/file.h" #include "sound/fmopl.h" #include "sound/softsynth/emumidi.h" #include "sci/resource.h" #include "sci/sound/drivers/mididriver.h" #include "sci/sound/drivers/map-mt32-to-gm.h" namespace Sci { class MidiPlayer_Midi : public MidiPlayer { public: enum { kVoices = 32, kReverbConfigNr = 11, kMaxSysExSize = 264 }; MidiPlayer_Midi(SciVersion version); 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() const; int getPolyphony() const { return kVoices; } int getFirstChannel() const; int getLastChannel() const; 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 readMt32DrvData(); void mapMt32ToGm(byte *data, size_t size); uint8 lookupGmInstrument(const char *iname); uint8 lookupGmRhythmKey(const char *iname); uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins); void sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *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); void controlChange(int channel, int control, int value); struct Channel { byte mappedPatch; byte patch; int velocityMapIdx; bool playing; int8 keyShift; int8 volAdjust; uint8 pan; uint8 hold; uint8 volume; Channel() : mappedPatch(MIDI_UNMAPPED), patch(MIDI_UNMAPPED), velocityMapIdx(0), playing(false), keyShift(0), volAdjust(0), pan(0x80), hold(0), volume(0x7f) { } }; bool _isMt32; bool _useMT32Track; bool _hasReverb; bool _playSwitch; int _masterVolume; byte _reverbConfig[kReverbConfigNr][3]; Channel _channels[16]; uint8 _percussionMap[128]; int8 _keyShift[128]; int8 _volAdjust[128]; uint8 _patchMap[128]; uint8 _velocityMapIdx[128]; uint8 _velocityMap[4][128]; // These are extensions used for our own MT-32 to GM mapping uint8 _pitchBendRange[128]; uint8 _percussionVelocityScale[128]; byte _goodbyeMsg[20]; byte _sysExBuf[kMaxSysExSize]; }; MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _useMT32Track(true) { MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI); _driver = createMidi(dev); if (MidiDriver::getMusicType(dev) == MT_MT32 || ConfMan.getBool("native_mt32")) _isMt32 = true; _sysExBuf[0] = 0x41; _sysExBuf[1] = 0x10; _sysExBuf[2] = 0x16; _sysExBuf[3] = 0x12; } MidiPlayer_Midi::~MidiPlayer_Midi() { delete _driver; } void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) { uint8 patch = _channels[channel].mappedPatch; assert(channel <= 15); assert(note <= 127); assert(velocity <= 127); if (channel == MIDI_RHYTHM_CHANNEL) { if (_percussionMap[note] == MIDI_UNMAPPED) { debugC(kDebugLevelSound, "[Midi] Percussion instrument %i is unmapped", note); return; } note = _percussionMap[note]; // Scale velocity; velocity = velocity * _percussionVelocityScale[note] / 127; } else if (patch >= 128) { if (patch == MIDI_UNMAPPED) return; // Map to rhythm channel = MIDI_RHYTHM_CHANNEL; note = patch - 128; // Scale velocity; velocity = velocity * _percussionVelocityScale[note] / 127; } else { int8 keyshift = _channels[channel].keyShift; int shiftNote = note + keyshift; if (keyshift > 0) { while (shiftNote > 127) shiftNote -= 12; } else { while (shiftNote < 0) shiftNote += 12; } note = shiftNote; // We assume that velocity 0 maps to 0 (for note off) int mapIndex = _channels[channel].velocityMapIdx; assert(velocity <= 127); velocity = _velocityMap[mapIndex][velocity]; } _channels[channel].playing = true; _driver->send(0x90 | channel, note, velocity); } void MidiPlayer_Midi::controlChange(int channel, int control, int value) { assert(channel <= 15); switch (control) { case 0x07: _channels[channel].volume = value; if (!_playSwitch) return; value += _channels[channel].volAdjust; if (value > 0x7f) value = 0x7f; if (value < 0) value = 1; value *= _masterVolume; if (value != 0) { value /= 15; if (value == 0) value = 1; } break; case 0x0a: if (_channels[channel].pan == value) return; _channels[channel].pan = value; break; case 0x40: if (_channels[channel].hold == value) return; _channels[channel].hold = value; break; case 0x7b: if (!_channels[channel].playing) return; _channels[channel].playing = false; } _driver->send(0xb0 | channel, control, value); } void MidiPlayer_Midi::setPatch(int channel, int patch) { bool resetVol = false; assert(channel <= 15); if ((channel == MIDI_RHYTHM_CHANNEL) || (_channels[channel].patch == patch)) return; _channels[channel].patch = patch; _channels[channel].velocityMapIdx = _velocityMapIdx[patch]; if (_channels[channel].mappedPatch == MIDI_UNMAPPED) resetVol = true; _channels[channel].mappedPatch = _patchMap[patch]; if (_patchMap[patch] == MIDI_UNMAPPED) { debugC(kDebugLevelSound, "[Midi] Channel %i set to unmapped patch %i", channel, patch); _driver->send(0xb0 | channel, 0x7b, 0); _driver->send(0xb0 | channel, 0x40, 0); return; } if (_patchMap[patch] >= 128) { // Mapped to rhythm, don't send channel commands return; } if (_channels[channel].keyShift != _keyShift[patch]) { _channels[channel].keyShift = _keyShift[patch]; _driver->send(0xb0 | channel, 0x7b, 0); _driver->send(0xb0 | channel, 0x40, 0); resetVol = true; } if (resetVol || (_channels[channel].volAdjust != _volAdjust[patch])) { _channels[channel].volAdjust = _volAdjust[patch]; controlChange(channel, 0x07, _channels[channel].volume); } uint8 bendRange = _pitchBendRange[patch]; if (bendRange != MIDI_UNMAPPED) _driver->setPitchBendRange(channel, bendRange); _driver->send(0xc0 | channel, _patchMap[patch], 0); // Send a pointless command to work around a firmware bug in common // USB-MIDI cables. If the first MIDI command in a USB packet is a // Cx or Dx command, the second command in the packet is dropped // somewhere. // FIXME: consider putting a workaround in the MIDI backend drivers // instead. // Known to be affected: alsa, coremidi // Known *not* to be affected: windows (only seems to send one MIDI // command per USB packet even if the device allows larger packets). _driver->send(0xb0 | channel, 0x0a, _channels[channel].pan); } void MidiPlayer_Midi::send(uint32 b) { byte command = b & 0xf0; byte channel = b & 0xf; byte op1 = (b >> 8) & 0x7f; byte op2 = (b >> 16) & 0x7f; // In early SCI0, we may also get events for AdLib rhythm channels. // While an MT-32 would ignore those with the default channel mapping, // we filter these out for the benefit of other MIDI devices. if (channel < 1 || channel > 9) return; switch (command) { case 0x80: noteOn(channel, op1, 0); break; case 0x90: noteOn(channel, op1, op2); break; case 0xb0: controlChange(channel, op1, op2); break; case 0xc0: setPatch(channel, op1); break; case 0xe0: _driver->send(b); break; default: warning("Ignoring MIDI event %02x", command); } } // We return 1 for mt32, because if we remap channels to 0 for mt32, those won't get played at all // NOTE: SSCI uses channels 1 through 8 for General MIDI as well, in the drivers I checked int MidiPlayer_Midi::getFirstChannel() const { if (_isMt32) return 1; return 0; } int MidiPlayer_Midi::getLastChannel() const { if (_isMt32) return 8; return 15; } void MidiPlayer_Midi::setVolume(byte volume) { _masterVolume = volume; if (!_playSwitch) return; for (uint i = 1; i < 10; i++) { if (_channels[i].volume != 0xff) controlChange(i, 0x07, _channels[i].volume & 0x7f); } } 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) setVolume(_masterVolume); else { for (uint i = 1; i < 10; i++) _driver->send(0xb0 | i, 7, 0); } } bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) { if (size < 1155) return false; if (size > 16889) return true; bool isMt32 = false; bool isMt32Gm = false; if (READ_LE_UINT16(data + 1153) + 1155 == size) isMt32Gm = true; int pos = 492 + 246 * data[491]; if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd)) pos += 386; if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba)) pos += 267; if (size == pos) isMt32 = true; if (isMt32 == isMt32Gm) error("Failed to detect MT-32 patch format"); return isMt32Gm; } void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *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); byte volume = CLIP(str->readUint16LE(), 0, 100); setMt32Volume(volume); // 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); memcpy(_volAdjust, data + 0x100, 0x80); memcpy(_percussionMap, data + 0x180, 0x80); _channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[0x200]; memcpy(_velocityMapIdx, data + 0x201, 0x80); memcpy(_velocityMap, data + 0x281, 0x200); uint16 midiSize = READ_LE_UINT16(data + 0x481); if (midiSize > 0) { if (size < midiSize + 1155) error("Failed to read MIDI data"); const byte *midi = data + 1155; byte command = 0; uint i = 0; while (i < midiSize) { byte op1, op2; if (midi[i] & 0x80) command = midi[i++]; switch (command & 0xf0) { case 0xf0: { byte *sysExEnd = (byte *)memchr(midi + i, 0xf7, midiSize - i); if (!sysExEnd) error("Failed to find end of sysEx"); int len = sysExEnd - (midi + i); sysEx(midi + i, len); i += len + 1; // One more for the 0x7f break; } case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: if (i + 1 >= midiSize) error("MIDI command exceeds data size"); op1 = midi[i++]; op2 = midi[i++]; _driver->send(command, op1, op2); break; case 0xc0: case 0xd0: if (i >= midiSize) error("MIDI command exceeds data size"); op1 = midi[i++]; _driver->send(command, op1, 0); break; default: error("Failed to find MIDI command byte"); } } } } void MidiPlayer_Midi::readMt32DrvData() { Common::File f; if (f.open("MT32.DRV")) { int size = f.size(); assert(size >= 166); // Send before-SysEx text f.seek(0x59); // Skip 2 extra 0 bytes in some drivers if (f.readUint16LE() != 0) f.seek(-2, SEEK_CUR); sendMt32SysEx(0x200000, static_cast(&f), 20); // Send after-SysEx text (SSCI sends this before every song) sendMt32SysEx(0x200000, static_cast(&f), 20); // Save goodbye message f.read(_goodbyeMsg, 20); // Set volume byte volume = CLIP(f.readUint16LE(), 0, 100); setMt32Volume(volume); byte reverbSysEx[13]; // This old driver should have a full reverb SysEx if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7)) error("Error reading MT32.DRV"); // Send reverb SysEx sysEx(reverbSysEx + 1, 11); _hasReverb = false; f.seek(0x29); // Read AdLib->MT-32 patch map for (int i = 0; i < 48; i++) { _patchMap[i] = f.readByte(); } f.close(); } else { error("Failed to open MT32.DRV"); } } byte MidiPlayer_Midi::lookupGmInstrument(const char *iname) { int i = 0; while (Mt32MemoryTimbreMaps[i].name) { if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0) return getGmInstrument(Mt32MemoryTimbreMaps[i]); i++; } return MIDI_UNMAPPED; } byte MidiPlayer_Midi::lookupGmRhythmKey(const char *iname) { int i = 0; while (Mt32MemoryTimbreMaps[i].name) { if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0) return Mt32MemoryTimbreMaps[i].gmRhythmKey; i++; } return MIDI_UNMAPPED; } uint8 MidiPlayer_Midi::getGmInstrument(const Mt32ToGmMap &Mt32Ins) { if (Mt32Ins.gmInstr == MIDI_MAPPED_TO_RHYTHM) return Mt32Ins.gmRhythmKey + 0x80; else return Mt32Ins.gmInstr; } void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { // FIXME: Clean this up int memtimbres, patches; uint8 group, number, keyshift, finetune, bender_range; uint8 *patchpointer; uint32 pos; int i; for (i = 0; i < 128; i++) { _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[i]); _pitchBendRange[i] = 12; } for (i = 0; i < 128; i++) _percussionMap[i] = Mt32PresetRhythmKeymap[i]; memtimbres = *(data + 0x1eb); pos = 0x1ec + memtimbres * 0xf6; if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xabcd)) { patches = 96; pos += 2 + 8 * 48; } else { patches = 48; } debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Patches detected", patches); debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Memory Timbres", memtimbres); debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping patches.."); for (i = 0; i < patches; i++) { char name[11]; if (i < 48) patchpointer = data + 0x6b + 8 * i; else patchpointer = data + 0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2; group = *patchpointer; number = *(patchpointer + 1); keyshift = *(patchpointer + 2); finetune = *(patchpointer + 3); bender_range = *(patchpointer + 4); debugCN(kDebugLevelSound, " [%03d] ", i); switch (group) { case 1: number += 64; // Fall through case 0: _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[number]); debugCN(kDebugLevelSound, "%s -> ", Mt32PresetTimbreMaps[number].name); break; case 2: if (number < memtimbres) { strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10); name[10] = 0; _patchMap[i] = lookupGmInstrument(name); debugCN(kDebugLevelSound, "%s -> ", name); } else { _patchMap[i] = 0xff; debugCN(kDebugLevelSound, "[Invalid] -> "); } break; case 3: _patchMap[i] = getGmInstrument(Mt32RhythmTimbreMaps[number]); debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number].name); break; default: break; } if (_patchMap[i] == MIDI_UNMAPPED) { debugC(kDebugLevelSound, "[Unmapped]"); } else { if (_patchMap[i] >= 128) { debugC(kDebugLevelSound, "%s [Rhythm]", GmPercussionNames[_patchMap[i] - 128]); } else { debugC(kDebugLevelSound, "%s", GmInstrumentNames[_patchMap[i]]); } } _keyShift[i] = CLIP(keyshift, 0, 48) - 24; _pitchBendRange[i] = CLIP(bender_range, 0, 24); } if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xdcba)) { debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping percussion.."); for (i = 0; i < 64 ; i++) { number = *(data + pos + 4 * i + 2); byte ins = i + 24; debugCN(kDebugLevelSound, " [%03d] ", ins); if (number < 64) { char name[11]; strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10); name[10] = 0; debugCN(kDebugLevelSound, "%s -> ", name); _percussionMap[ins] = lookupGmRhythmKey(name); } else { if (number < 94) { debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number - 64].name); _percussionMap[ins] = Mt32RhythmTimbreMaps[number - 64].gmRhythmKey; } else { debugCN(kDebugLevelSound, "[Key %03i] -> ", number); _percussionMap[ins] = MIDI_UNMAPPED; } } if (_percussionMap[ins] == MIDI_UNMAPPED) debugC(kDebugLevelSound, "[Unmapped]"); else debugC(kDebugLevelSound, "%s", GmPercussionNames[_percussionMap[ins]]); _percussionVelocityScale[ins] = *(data + pos + 4 * i + 3) * 127 / 100; } } } 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); int retval = _driver->open(); if (retval != 0) { warning("Failed to open MIDI driver"); return retval; } // By default use no mapping for (uint i = 0; i < 128; i++) { _percussionMap[i] = i; _patchMap[i] = i; _velocityMap[0][i] = i; _velocityMap[1][i] = i; _velocityMap[2][i] = i; _velocityMap[3][i] = i; _keyShift[i] = 0; _volAdjust[i] = 0; _velocityMapIdx[i] = 0; _pitchBendRange[i] = MIDI_UNMAPPED; _percussionVelocityScale[i] = 127; } Resource *res = NULL; if (_isMt32) { // MT-32 resetMt32(); res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); if (res) { if (isMt32GmPatch(res->data, res->size)) { readMt32GmPatch(res->data, res->size); strncpy((char *)_goodbyeMsg, " ScummVM ", 20); } else { readMt32Patch(res->data, res->size); } } else { readMt32DrvData(); } } else { // General MIDI res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0); if (res && isMt32GmPatch(res->data, res->size)) { // There is a GM patch readMt32GmPatch(res->data, res->size); // Detect the format of patch 1, so that we know what play mask to use res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); if (!res) _useMT32Track = false; else _useMT32Track = !isMt32GmPatch(res->data, res->size); // Check if the songs themselves have a GM track if (!_useMT32Track) { if (!resMan->isGMTrackIncluded()) _useMT32Track = true; } } else { // No GM patch found, map instruments using MT-32 patch warning("Game has no native support for General MIDI, applying auto-mapping"); // TODO: The MT-32 <-> GM mapping hasn't been worked on for SCI1 games. Throw // a warning to the user if (getSciVersion() >= SCI_VERSION_1_EGA) warning("The automatic mapping for General MIDI hasn't been worked on for " "SCI1 games. Music might sound wrong or broken. Please choose another " "music driver for this game (e.g. Adlib or MT-32) if you are " "experiencing issues with music"); // Modify velocity map to make low velocity notes a little louder for (uint i = 1; i < 0x40; i++) { _velocityMap[0][i] = 0x20 + (i - 1) / 2; _velocityMap[1][i] = 0x20 + (i - 1) / 2; _velocityMap[2][i] = 0x20 + (i - 1) / 2; _velocityMap[3][i] = 0x20 + (i - 1) / 2; } res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); if (res) { if (!isMt32GmPatch(res->data, res->size)) mapMt32ToGm(res->data, res->size); else error("MT-32 patch has wrong type"); } else { // No MT-32 patch present, try to read from MT32.DRV Common::File f; if (f.open("MT32.DRV")) { int size = f.size(); assert(size >= 70); f.seek(0x29); // Read AdLib->MT-32 patch map for (int i = 0; i < 48; i++) _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[f.readByte() & 0x7f]); } } } } 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() const { switch (_version) { case SCI_VERSION_0_EARLY: case SCI_VERSION_0_LATE: return 0x01; default: if (_isMt32) return 0x0c; else return _useMT32Track ? 0x0c : 0x07; } } MidiPlayer *MidiPlayer_Midi_create(SciVersion version) { return new MidiPlayer_Midi(version); } } // End of namespace Sci