/* 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 3 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, see . * */ #include "base/version.h" #include "common/debug.h" #include "common/system.h" #include "scumm/imuse/drivers/midi.h" // This makes older titles play new system style, with 16 virtual channels and // dynamic allocation (instead of playing on fixed channels). //#define FORCE_NEWSTYLE_CHANNEL_ALLOCATION namespace IMSMidi { using namespace Scumm; /******************************* * General Midi driver ********************************/ struct ChannelNode; class IMuseChannel_Midi : public MidiChannel { public: IMuseChannel_Midi(IMuseDriver_GMidi *drv, int number); virtual ~IMuseChannel_Midi() override {} MidiDriver *device() override { return _drv; } byte getNumber() override { return _number; } virtual bool allocate(); void release() override { _allocated = false; } void send(uint32 b) override { if (_drv) _drv->send((b & ~0x0F) | _number); } // Regular messages void noteOff(byte note) override; void noteOn(byte note, byte velocity) override; void controlChange(byte control, byte value) override; virtual void programChange(byte program) override; void pitchBend(int16 bend) override; // Control Change and SCUMM specific functions void pitchBendFactor(byte value) override { pitchBend(0); _pitchBendSensitivity = value; } void transpose(int8 value) override { _transpose = value; pitchBend(_pitchBendTemp); } void detune(int16 value) override { _detune = value; pitchBend(_pitchBendTemp); } void priority(byte value) override { _prio = value; } void sustain(bool value) override; void allNotesOff() override; virtual void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override {} virtual void setOutput(ChannelNode*) {} protected: virtual void sendMidi(byte evt, byte par1, byte par2) { if (_drv) _drv->send(((par2) << 16) | ((par1) << 8) | ((evt & 0xF0) | _number)); } IMuseDriver_GMidi *_drv; const byte _number; const bool _newSystem; int16 _pitchBend; ChannelNode *_dummyNode; private: void noteOffIntern(byte note); void noteOnIntern(byte note, byte velocity); virtual bool validateTransmission(byte note) const { return true; } void setNotePlaying(byte note) { _drv->setNoteFlag(_number, note); } void clearNotePlaying(byte note) { _drv->clearNoteFlag(_number, note); } bool isNotePlaying(byte note) const { return _drv->queryNoteFlag(_number, note); } void setNoteSustained(byte note) { _drv->setSustainFlag(_number, note); } void clearNoteSustained(byte note) { _drv->clearSustainFlag(_number, note); } bool isNoteSustained(byte note) const { return _drv->querySustainFlag(_number, note); } virtual void sendNoteOff(byte note); virtual void sendNoteOn(byte note, byte velocity); bool _allocated; byte _polyphony; byte _channelUsage; bool _exhaust; byte _prio; int16 _detune; int8 _transpose; int16 _pitchBendTemp; byte _pitchBendSensitivity; bool _sustain; ChannelNode *&_idleChain; ChannelNode *&_activeChain; }; struct ChannelNode { ChannelNode() : _prev(nullptr), _next(nullptr), _in(nullptr), _number(0), _note(0), _addr(0) {} ChannelNode *_prev; ChannelNode *_next; IMuseChannel_Midi *_in; byte _number; byte _note; // MT-32 only uint32 _addr; }; void connect(ChannelNode *&chain, ChannelNode *node) { if (!node || node->_prev || node->_next) return; if ((node->_next = chain)) chain->_prev = node; chain = node; } void disconnect(ChannelNode *&chain, ChannelNode *node) { if (!node || !chain) return; const ChannelNode *ch = chain; while (ch && ch != node) ch = ch->_next; if (!ch) return; if (node->_next) node->_next->_prev = node->_prev; if (node->_prev) node->_prev->_next = node->_next; else chain = node->_next; node->_next = node->_prev = nullptr; } IMuseChannel_Midi::IMuseChannel_Midi(IMuseDriver_GMidi *drv, int number) :MidiChannel(), _drv(drv), _number(number), _allocated(false), _sustain(false), _pitchBend(0x2000), _polyphony(1), _channelUsage(0), _exhaust(false), _prio(0x80), _detune(0), _transpose(0), _pitchBendTemp(0), _pitchBendSensitivity(2), _activeChain(drv ? _drv->_activeChain : _dummyNode), _idleChain(drv ? _drv->_idleChain : _dummyNode), _dummyNode(nullptr), _newSystem(drv ? drv->_newSystem : false) { assert(_drv); } bool IMuseChannel_Midi::allocate() { if (_allocated) return false; _channelUsage = 0; _exhaust = false; return (_allocated = true); } void IMuseChannel_Midi::noteOff(byte note) { if (_newSystem) { if (!isNotePlaying(note)) return; clearNotePlaying(note); if (_sustain) { setNoteSustained(note); return; } } #ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION noteOffIntern(note); #else if (_newSystem) noteOffIntern(note); else sendMidi(0x80, note, 0x40); #endif } void IMuseChannel_Midi::noteOn(byte note, byte velocity) { if (_newSystem) { if (isNotePlaying(note)) { noteOffIntern(note); } else if (isNoteSustained(note)) { setNotePlaying(note); clearNoteSustained(note); noteOffIntern(note); } else { setNotePlaying(note); } } #ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION noteOnIntern(note, velocity); #else if (_newSystem) noteOnIntern(note, velocity); else sendMidi(0x90, note, velocity); #endif } void IMuseChannel_Midi::controlChange(byte control, byte value) { switch (control) { case 1: case 7: case 10: case 91: case 93: sendMidi(0xB0, control, value); break; case 17: if (_newSystem) _polyphony = value; else detune(value); break; case 18: priority(value); break; case 123: allNotesOff(); break; default: // The original SAMNMAX driver does not pass through "blindly". The // only controls that get sent are 1, 7, 10, 91 and 93. if (_newSystem) warning("Unhandled Control: %d", control); else sendMidi(0xB0, control, value); break; } } void IMuseChannel_Midi::programChange(byte program) { sendMidi(0xC0, program, 0); } void IMuseChannel_Midi::pitchBend(int16 bend) { _pitchBendTemp = bend; if (_newSystem) { // SAMNMAX formula (same for Roland MT-32 and GM) bend = (((bend * _pitchBendSensitivity) >> 5) + _detune + (_transpose << 8)) << 1; } else { // DOTT, INDY4 and MI2 formula (same for Roland MT-32 and GM) bend = CLIP(((bend * _pitchBendSensitivity) >> 6) + _detune + (_transpose << 7), -2048, 2047) << 2; } _pitchBend = bend + 0x2000; sendMidi(0xE0, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F); } void IMuseChannel_Midi::sustain(bool value) { _sustain = value; if (_newSystem) { // For SAMNMAX, this is fully software controlled. No control change message gets sent. if (_sustain) return; for (int i = 0; i < 128; ++i) { if (isNoteSustained(i)) noteOffIntern(i); } } else { sendMidi(0xB0, 0x40, value); } } void IMuseChannel_Midi::allNotesOff() { if (_newSystem) { // For SAMNMAX, this is fully software controlled. No control change message gets sent. if (_sustain) return; for (int i = 0; i < 128; ++i) { if (isNotePlaying(i)) { noteOffIntern(i); clearNotePlaying(i); } else if (isNoteSustained(i)) { noteOffIntern(i); clearNoteSustained(i); } } } else { sendMidi(0xB0, 0x7B, 0); } } void IMuseChannel_Midi::noteOffIntern(byte note) { if (!_activeChain || !validateTransmission(note)) return; ChannelNode *node = nullptr; for (ChannelNode *i = _activeChain; i; i = i->_next) { if (i->_number == _number && i->_note == note) { node = i; break; } } if (!node) return; sendNoteOff(note); if (_newSystem) _exhaust = (--_channelUsage > _polyphony); disconnect(_activeChain, node); connect(_idleChain, node); } void IMuseChannel_Midi::noteOnIntern(byte note, byte velocity) { if (!validateTransmission(note)) return; ChannelNode *node = nullptr; if (_idleChain) { node = _idleChain; disconnect(_idleChain, node); } else { IMuseChannel_Midi *best = this; for (ChannelNode *i = _activeChain; i; i = i->_next) { assert (i->_in); if ((best->_exhaust == i->_in->_exhaust && best->_prio >= i->_in->_prio) || (!best->_exhaust && i->_in->_exhaust)) { best = i->_in; node = i; } } if (!node) return; IMuseChannel_Midi *prt = _drv->getPart(node->_number); if (prt) prt->sendMidi(0x80, node->_note, 0x40); if (_newSystem && prt) prt->_exhaust = (--prt->_channelUsage > prt->_polyphony); disconnect(_activeChain, node); } assert(node); node->_in = this; node->_number = _number; node->_note = note; connect(_activeChain, node); if (_newSystem) _exhaust = (++_channelUsage > _polyphony); sendNoteOn(note, velocity); } void IMuseChannel_Midi::sendNoteOff(byte note) { sendMidi(0x80, note, 0x40); } void IMuseChannel_Midi::sendNoteOn(byte note, byte velocity) { sendMidi(0x90, note, velocity); } } // End of namespace IMSMidi namespace Scumm { using namespace IMSMidi; IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode), _noProgramTracking(false), _imsParts(nullptr), _newSystem(newSystem), _numChannels(16), _notesPlaying(nullptr), _notesSustained(nullptr), _midiRegState(nullptr), _idleChain(nullptr), _activeChain(nullptr), _numVoices(12) { _drv = MidiDriver::createMidi(dev); assert(_drv); _midiRegState = new byte[160]; assert(_midiRegState); memset(_midiRegState, 0xFF, 160); } IMuseDriver_GMidi::~IMuseDriver_GMidi() { close(); delete _drv; delete[] _midiRegState; _midiRegState = nullptr; } int IMuseDriver_GMidi::open() { if (!_drv) return MERR_CANNOT_CONNECT; int res = _drv->open(); if (res) return res; createChannels(); if (_gsMode) initRolandGSMode(); initDevice(); return res; } void IMuseDriver_GMidi::close() { if (isOpen() && _drv) { deinitDevice(); _drv->close(); } releaseChannels(); } MidiChannel *IMuseDriver_GMidi::allocateChannel() { if (!isOpen()) return nullptr; for (int i = 0; i < _numChannels; ++i) { IMuseChannel_Midi *ch = _imsParts[i]; if (ch && ch->getNumber() != 9 && ch->allocate()) return ch; } return nullptr; } MidiChannel *IMuseDriver_GMidi::getPercussionChannel() { if (!isOpen()) return nullptr; IMuseChannel_Midi *ch = getPart(9); return ch; } IMuseChannel_Midi *IMuseDriver_GMidi::getPart(int number) { for (int i = 0; i < _numChannels; ++i) if (_imsParts[i]->getNumber() == number) return _imsParts[i]; return nullptr; } void IMuseDriver_GMidi::createChannels() { releaseChannels(); createParts(); for (int i = 0; i < _numVoices; ++i) { ChannelNode *node = new ChannelNode(); assert(node); connect(_idleChain, node); } if (_newSystem) { _notesPlaying = new uint16[128](); _notesSustained = new uint16[128](); } } void IMuseDriver_GMidi::createParts() { _imsParts = new IMuseChannel_Midi*[_numChannels]; assert(_imsParts); for (int i = 0; i < _numChannels; ++i) _imsParts[i] = new IMuseChannel_Midi(this, i); } void IMuseDriver_GMidi::releaseChannels() { if (_imsParts) { for (int i = 0; i < _numChannels; ++i) delete _imsParts[i]; delete[] _imsParts; _imsParts = nullptr; } int released = 0; while (_idleChain) { ChannelNode *node = _idleChain; disconnect(_idleChain, node); delete node; released++; } while (_activeChain) { ChannelNode *node = _activeChain; disconnect(_activeChain, node); delete node; released++; } assert(released == 0 || released == _numVoices); delete[] _notesPlaying; _notesPlaying = nullptr; delete[] _notesSustained; _notesSustained = nullptr; } void IMuseDriver_GMidi::initDevice() { // These are the init messages from the DOTT General Midi driver. This is the major part of the bug fix for bug // no. 13460 ("DOTT: Incorrect MIDI pitch bending"). SAMNMAX has some less of the default settings (since // the driver works a bit different), but it uses the same values for the pitch bend range. for (int i = 0; i < 16; ++i) { send(0x0064B0 | i); send(0x0065B0 | i); send(0x1006B0 | i); send(0x7F07B0 | i); send(0x3F0AB0 | i); send(0x0000C0 | i); send(0x4000E0 | i); send(0x0001B0 | i); send(0x0040B0 | i); send(0x405BB0 | i); send(0x005DB0 | i); send(0x0000B0 | i); send(0x007BB0 | i); } } void IMuseDriver_GMidi::initRolandGSMode() { byte buffer[12]; int i; // General MIDI System On message // Resets all GM devices to default settings memcpy(&buffer[0], "\x7E\x7F\x09\x01", 4); sysEx(buffer, 4); debug(2, "GM SysEx: GM System On"); g_system->delayMillis(200); // All GS devices recognize the GS Reset command, // even using Roland's ID. It is impractical to // support other manufacturers' devices for // further GS settings, as there are limitless // numbers of them out there that would each // require individual SysEx commands with unique IDs. // Roland GS SysEx ID memcpy(&buffer[0], "\x41\x10\x42\x12", 4); // GS Reset memcpy(&buffer[4], "\x40\x00\x7F\x00\x41", 5); sysEx(buffer, 9); debug(2, "GS SysEx: GS Reset"); g_system->delayMillis(200); // Set global Master Tune to 442.0kHz, as on the MT-32 memcpy(&buffer[4], "\x40\x00\x00\x00\x04\x04\x0F\x29", 8); sysEx(buffer, 12); debug(2, "GS SysEx: Master Tune set to 442.0kHz"); // Note: All Roland GS devices support CM-64/32L maps // Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation for (i = 0; i < 16; ++i) { _drv->send((127 << 16) | (0 << 8) | (0xB0 | i)); _drv->send((1 << 16) | (32 << 8) | (0xB0 | i)); _drv->send((0 << 16) | (0 << 8) | (0xC0 | i)); } debug(2, "GS Program Change: CM-64/32L Map Selected"); // Set Percussion Channel to SC-55 Map (CC#32, 01H), then // Switch Drum Map to CM-64/32L (MT-32 Compatible Drums) getPercussionChannel()->controlChange(0, 0); getPercussionChannel()->controlChange(32, 1); send(127 << 8 | 0xC0 | 9); debug(2, "GS Program Change: Drum Map is CM-64/32L"); // Set Master Chorus to 0. The MT-32 has no chorus capability. memcpy(&buffer[4], "\x40\x01\x3A\x00\x05", 5); sysEx(buffer, 9); debug(2, "GS SysEx: Master Chorus Level is 0"); // Set Channels 1-16 Reverb to 64, which is the // equivalent of MT-32 default Reverb Level 5 for (i = 0; i < 16; ++i) send((64 << 16) | (91 << 8) | (0xB0 | i)); debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64"); // Set Channels 1-16 Pitch Bend Sensitivity to // 12 semitones; then lock the RPN by setting null. for (i = 0; i < 16; ++i) setPitchBendRange(i, 12); debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones"); // Set channels 1-16 Mod. LFO1 Pitch Depth to 4 memcpy(&buffer[4], "\x40\x20\x04\x04\x18", 5); for (i = 0; i < 16; ++i) { buffer[5] = 0x20 + i; buffer[8] = 0x18 - i; sysEx(buffer, 9); } debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4"); // Set Percussion Channel Expression to 80 getPercussionChannel()->controlChange(11, 80); debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80"); // Turn off Percussion Channel Rx. Expression so that // Expression cannot be modified. I don't know why, but // Roland does it this way. memcpy(&buffer[4], "\x40\x10\x0E\x00\x22", 5); sysEx(buffer, 9); debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF"); // Change Reverb Character to 0. I don't think this // sounds most like MT-32, but apparently Roland does. memcpy(&buffer[4], "\x40\x01\x31\x00\x0E", 5); sysEx(buffer, 9); debug(2, "GS SysEx: Reverb Character is 0"); // Change Reverb Pre-LF to 4, which is similar to // what MT-32 reverb does. memcpy(&buffer[4], "\x40\x01\x32\x04\x09", 5); sysEx(buffer, 9); debug(2, "GS SysEx: Reverb Pre-LF is 4"); // Change Reverb Time to 106; the decay on Hall 2 // Reverb is too fast compared to the MT-32's memcpy(&buffer[4], "\x40\x01\x34\x6A\x21", 5); sysEx(buffer, 9); debug(2, "GS SysEx: Reverb Time is 106"); } void IMuseDriver_GMidi::deinitDevice() { for (int i = 0; i < 16; ++i) { send(0x0040B0 | i); send(0x007BB0 | i); } } bool IMuseDriver_GMidi::trackMidiState(uint32 b) { // The purpose of this function is to reduce the amount of data sent to hardware devices and // thus avoid possible lag. It tracks certain midi messages with a table and allows skipping of // messages which just repeat the already present device state. byte evt = ((b & 0xF0) - 0xB0) >> 4; if (evt > 3) // Only track the state of program change, control change and pitch bend events return true; // Skip program change tracking for old MT-32 tracks, since their sysex based program changing // method requires resending the program change message, even with the same program. if (evt == 1 && _noProgramTracking) return true; byte part = b & 0x0F; b >>= 8; // Switch to para 1 assert(_midiRegState); byte *var = &_midiRegState[part]; switch (evt) { case 0: { // Control Change // Only track the state of these controllers. The first entry is an invalid placeholder, // since the first part of the reg state table is reserved for the program change state. static const byte regOrder[] = { 0xFF, 0, 1, 7, 10, 64, 91, 93 }; int srchReslt = Common::find(regOrder, ®Order[ARRAYSIZE(regOrder)], b & 0xFF) - regOrder; if (srchReslt > 0 && srchReslt < ARRAYSIZE(regOrder)) var += (srchReslt * 0x10); else return true; b >>= 8; // Switch to para 2 } // fall through case 1: // Program change if (*var == (b & 0xFF)) return false; else *var = b & 0xFF; break; case 3: // Pitch bend var += (0x80 + part); if (*reinterpret_cast(var) == (b & 0xFFFF)) return false; else *reinterpret_cast(var) = b & 0xFFFF; break; default: break; } return true; } } // End of namespace Scumm namespace IMSMidi { /************************** * MT-32 driver ***************************/ class IMuseChannel_MT32 : public IMuseChannel_Midi { public: IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number); ~IMuseChannel_MT32() override {} bool allocate() override; void reset(); // Regular messages void programChange(byte program) override; // Control Change and SCUMM specific functions void volume(byte value) override; void panPosition(byte value) override; void modulationWheel(byte value) override; void effectLevel(byte value) override; void chorusLevel(byte value) override {} void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override; void setOutput(ChannelNode *out) override { _out = out; } private: bool validateTransmission(byte note) const override { return _program < 128 && (!_newSystem || !(_number == 9 && note > 75)); } void sendMidi(byte stat, byte par1, byte par2) override { if (_drv && (_out || _number == 9)) _drv->send(((par2) << 16) | ((par1) << 8) | ((stat & 0xF0) | (_out ? _out->_number : 9))); } void sendNoteOff(byte note) override; void sendNoteOn(byte note, byte velocity) override; void sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const; void sendSysexTimbreData(const byte *data, uint32 dataSize) const; IMuseDriver_MT32 *_mt32Drv; ChannelNode *_out; byte _program; byte _timbre; byte _volume; byte _panPos; byte _reverbSwitch; ChannelNode *&_hwRealChain; const byte *_programsMapping; const uint32 _sysexPatchAddrBase; const uint32 _sysexTimbreAddrBase; enum SysexMessageSize { kSysexLengthTimbre = 254 }; }; IMuseChannel_MT32::IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number) : IMuseChannel_Midi(drv, number), _out(nullptr), _program(0), _timbre(0xFF), _volume(0x7F), _panPos(0x40), _reverbSwitch(1), _sysexPatchAddrBase(0x14000 + (number << 3)), _sysexTimbreAddrBase(0x20000 + (number << 8)), _mt32Drv(drv), _programsMapping(drv ? drv->_programsMapping : nullptr), _hwRealChain(drv ? drv->_hwRealChain : _dummyNode) { } bool IMuseChannel_MT32::allocate() { bool res = IMuseChannel_Midi::allocate(); if (res && !_newSystem) _program = _number; return res; } void IMuseChannel_MT32::reset() { if (_newSystem) return; byte msg[] = { (byte)(_timbre >> 6), (byte)(_timbre & 0x3F), 0x18, 0x32, 0x10, 0x00, _reverbSwitch}; sendSysexPatchData(0, msg, sizeof(msg)); } void IMuseChannel_MT32::programChange(byte program) { if (program > 127) return; if (_newSystem) { if (_programsMapping) program = _programsMapping[program]; _program = program; } else if (_timbre != program) { _timbre = program; byte msg[2] = { (byte)(program >> 6), (byte)(program & 0x3F) }; sendSysexPatchData(0, msg, sizeof(msg)); } if (_program < 128) sendMidi(0xC0, _program, 0); } void IMuseChannel_MT32::volume(byte value) { _volume = value; #ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION if (_number != 9) #else if (!_newSystem || _number != 9) #endif sendMidi(0xB0, 0x07, value); } void IMuseChannel_MT32::panPosition(byte value) { _panPos = value; sendMidi(0xB0, 0x0A, value); } void IMuseChannel_MT32::modulationWheel(byte value) { if (!_newSystem) sendMidi(0xB0, 0x01, value); } void IMuseChannel_MT32::effectLevel(byte value) { // The SAMNMAX Roland MT-32 driver ignores this (same with most of the other // sysex magic that the older drivers did in several places). if (_newSystem) return; value = value ? 1 : 0; if (_reverbSwitch == value) return; _reverbSwitch = value; sendSysexPatchData(6, &_reverbSwitch, 1); if (_out) _mt32Drv->sendMT32Sysex(_out->_addr + 6, &_reverbSwitch, 1); } void IMuseChannel_MT32::sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) { if (type != 'ROL ') { warning("IMuseChannel_MT32: Receiving '%c%c%c%c' instrument data. Probably loading a savegame with that sound setting", (type >> 24) & 0xFF, (type >> 16) & 0xFF, (type >> 8) & 0xFF, type & 0xFF); return; } if (*instr++ != 0x41 || dataSize < 6) { warning("IMuseChannel_MT32::sysEx_customInstrument(): Invalid sysex message received"); return; } byte partNo = *instr; uint32 addr = (instr[3] << 14) | (instr[4] << 7) | instr[5]; if (dataSize == kSysexLengthTimbre) { if (!(addr & 0xFFFF) || partNo < 16) { sendSysexTimbreData(instr + 6, 246); _timbre = 0xFF; byte msg[2] = { 0x02, _program }; sendSysexPatchData(0, msg, sizeof(msg)); if (_out) sendMidi(0xC0, _program, 0); } else { _mt32Drv->sendMT32Sysex(0x22000 + (partNo << 8), instr + 6, 246); } } else { // We cannot arrive here, since our imuse code calls this function only for instruments. // So this is just a reminder that the original driver handles more things than we do, // (but these things are apparently never used and thus not needed). warning("IMuseChannel_MT32::sysEx_customInstrument(): Unsupported sysex message received"); } } void IMuseChannel_MT32::sendNoteOff(byte note) { sendMidi(0x80, note, 0x40); } void IMuseChannel_MT32::sendNoteOn(byte note, byte velocity) { if (_number == 9) { sendMidi(0xB0, 0x07, _volume); sendMidi(0x90, note, velocity); return; } if (!_out) { ChannelNode *nodeReal = _hwRealChain; while (nodeReal && nodeReal->_next) nodeReal = nodeReal->_next; assert(nodeReal); assert(nodeReal->_in); nodeReal->_in->setOutput(nullptr); nodeReal->_in = this; _out = nodeReal; sendMidi(0xB0, 0x7B, 0); sendMidi(0xB0, 0x07, _volume); sendMidi(0xB0, 0x0A, _panPos); sendMidi(0xC0, _program, 0); sendMidi(0xE0, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F); } disconnect(_hwRealChain, _out); connect(_hwRealChain, _out); sendMidi(0x90, note, velocity); } void IMuseChannel_MT32::sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const { assert(!_newSystem); _mt32Drv->sendMT32Sysex(_sysexPatchAddrBase + offset, data, dataSize); } void IMuseChannel_MT32::sendSysexTimbreData(const byte *data, uint32 dataSize) const { assert(!_newSystem); _mt32Drv->sendMT32Sysex(_sysexTimbreAddrBase, data, dataSize); } } // End of namespace IMSMidi namespace Scumm { IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : IMuseDriver_GMidi(dev, false, newSystem), _programsMapping(nullptr), _hwRealChain(nullptr) { #ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION _numChannels = 16; #else _numChannels = newSystem ? 16 : 9; #endif _numVoices = 9; assert(_drv); _drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); if (_newSystem) _programsMapping = MidiDriver::_gmToMt32; else _noProgramTracking = true; } void IMuseDriver_MT32::initDevice() { // Display a welcome message on MT-32 displays. Compute version string (truncated to 20 chars max.) Common::String infoStr = gScummVMVersion; infoStr = "ScummVM " + infoStr.substr(0, MIN(infoStr.findFirstNotOf("0123456789."), 12)); for (int i = (20 - (int)infoStr.size()) >> 1; i > 0; --i) infoStr = ' ' + infoStr + ' '; sendMT32Sysex(0x80000, (const byte*)infoStr.c_str(), MIN(infoStr.size(), 20)); // Reset the MT-32 sendMT32Sysex(0x1FC000, 0, 0); g_system->delayMillis(250); // Setup master tune, reverb mode, reverb time, reverb level, channel mapping, partial reserve and master volume static const char initSysex1[] = "\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64"; sendMT32Sysex(0x40000, (const byte*)initSysex1, sizeof(initSysex1) - 1); g_system->delayMillis(40); if (!_newSystem) { // Map percussion to notes 24 - 34 without reverb. It still happens in the DOTT driver, but not in the SAMNMAX one. static const char initSysex2[] = "\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64" "\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00"; sendMT32Sysex(0xC090, (const byte*)initSysex2, sizeof(initSysex2) - 1); g_system->delayMillis(40); } const byte pbRange = 0x10; for (int i = 0; i < 128; ++i) { sendMT32Sysex(0x014004 + (i << 3), &pbRange, 1); g_system->delayMillis(5); } for (int i = 0; i < 16; ++i) { send(0x0000C0 | i); send(0x0040B0 | i); send(0x007BB0 | i); send(0x3F0AB0 | i); send(0x4000E0 | i); } for (int i = 0; i < _numChannels; ++i) { static_cast(_imsParts[i])->reset(); g_system->delayMillis(5); } } void IMuseDriver_MT32::deinitDevice() { for (int i = 0; i < 16; ++i) { send(0x0040B0 | i); send(0x007BB0 | i); } // Reset the MT-32 sendMT32Sysex(0x1FC000, 0, 0); } void IMuseDriver_MT32::createChannels() { releaseChannels(); IMuseDriver_GMidi::createChannels(); for (int i = 1; i < 9; ++i) { ChannelNode *node = new ChannelNode(); assert(node); node->_number = i; node->_in = getPart(i); assert(node->_in); node->_in->setOutput(node); node->_addr = 0xC000 + (i << 4); connect(_hwRealChain, node); } } void IMuseDriver_MT32::createParts() { _imsParts = new IMuseChannel_Midi*[_numChannels]; assert(_imsParts); for (int i = 0; i < _numChannels; ++i) { IMuseChannel_MT32 *prt = new IMuseChannel_MT32(this, (i + 1) & 0x0F); _imsParts[i] = prt; } } void IMuseDriver_MT32::releaseChannels() { IMuseDriver_GMidi::releaseChannels(); int released = 0; while (_hwRealChain) { ChannelNode *node = _hwRealChain; disconnect(_hwRealChain, node); delete node; released++; } assert(released == 0 || released == 8); } void IMuseDriver_MT32::sendMT32Sysex(uint32 addr, const byte *data, uint32 dataSize) { static const byte header[] = { 0x41, 0x10, 0x16, 0x12 }; byte *msg = new byte[sizeof(header) + 4 + dataSize]; memcpy(msg, header, sizeof(header)); byte *dst = msg + sizeof(header); const byte *src = dst; *dst++ = (addr >> 14) & 0x7F; *dst++ = (addr >> 7) & 0x7F; *dst++ = addr & 0x7F; while (dataSize) { *dst++ = *data++; --dataSize; } uint8 checkSum = 0; while (src < dst) checkSum -= *src++; *dst++ = checkSum & 0x7F; dataSize = dst - msg; sysEx(msg, dataSize); delete[] msg; } #undef FORCE_NEWSTYLE_CHANNEL_ALLOCATION } // End of namespace Scumm