/* 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. * */ #include "agos/drivers/accolade/mididriver.h" #include "audio/mididrv.h" #include "common/config-manager.h" #include "common/file.h" #include "common/mutex.h" #include "common/system.h" #include "common/textconsole.h" namespace AGOS { class MidiDriver_Accolade_MT32 : public MidiDriver { public: MidiDriver_Accolade_MT32(); virtual ~MidiDriver_Accolade_MT32(); // MidiDriver int open(); void close(); bool isOpen() const { return _isOpen; } void send(uint32 b); MidiChannel *allocateChannel() { if (_driver) return _driver->allocateChannel(); return NULL; } MidiChannel *getPercussionChannel() { if (_driver) return _driver->getPercussionChannel(); return NULL; } void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { if (_driver) _driver->setTimerCallback(timer_param, timer_proc); } uint32 getBaseTempo() { if (_driver) { return _driver->getBaseTempo(); } return 1000000 / _baseFreq; } protected: Common::Mutex _mutex; MidiDriver *_driver; bool _nativeMT32; // native MT32, may also be our MUNT, or MUNT over MIDI bool _isOpen; int _baseFreq; private: // simple mapping between MIDI channel and MT32 channel byte _channelMapping[AGOS_MIDI_CHANNEL_COUNT]; // simple mapping between MIDI instruments and MT32 instruments byte _instrumentMapping[AGOS_MIDI_INSTRUMENT_COUNT]; public: bool setupInstruments(byte *instrumentData, uint16 instrumentDataSize, bool useMusicDrvFile); }; MidiDriver_Accolade_MT32::MidiDriver_Accolade_MT32() { _driver = NULL; _isOpen = false; _nativeMT32 = false; _baseFreq = 250; memset(_channelMapping, 0, sizeof(_channelMapping)); memset(_instrumentMapping, 0, sizeof(_instrumentMapping)); } MidiDriver_Accolade_MT32::~MidiDriver_Accolade_MT32() { Common::StackLock lock(_mutex); if (_driver) { _driver->setTimerCallback(0, 0); _driver->close(); delete _driver; } _driver = NULL; } int MidiDriver_Accolade_MT32::open() { assert(!_driver); // debugC(kDebugLevelMT32Driver, "MT32: starting driver"); // Setup midi driver MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32); MusicType musicType = MidiDriver::getMusicType(dev); // check, if we got a real MT32 (or MUNT, or MUNT over MIDI) switch (musicType) { case MT_MT32: _nativeMT32 = true; break; case MT_GM: if (ConfMan.getBool("native_mt32")) { _nativeMT32 = true; } break; default: break; } _driver = MidiDriver::createMidi(dev); if (!_driver) return 255; int ret = _driver->open(); if (ret) return ret; if (_nativeMT32) _driver->sendMT32Reset(); else _driver->sendGMReset(); return 0; } void MidiDriver_Accolade_MT32::close() { if (_driver) { _driver->close(); } } // MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php void MidiDriver_Accolade_MT32::send(uint32 b) { byte command = b & 0xf0; byte channel = b & 0xf; if (command == 0xF0) { if (_driver) { _driver->send(b); } return; } byte mappedChannel = _channelMapping[channel]; if (mappedChannel < AGOS_MIDI_CHANNEL_COUNT) { // channel mapped to an actual MIDI channel, so use that one b = (b & 0xFFFFFFF0) | mappedChannel; if (command == 0xC0) { // Program change // Figure out the requested instrument byte midiInstrument = (b >> 8) & 0xFF; byte mappedInstrument = _instrumentMapping[midiInstrument]; // If there is no actual MT32 (or MUNT), we make a second mapping to General MIDI instruments if (!_nativeMT32) { mappedInstrument = (MidiDriver::_mt32ToGm[mappedInstrument]); } // And replace it b = (b & 0xFFFF00FF) | (mappedInstrument << 8); } if (_driver) { _driver->send(b); } } } // Called right at the start, we get an INSTR.DAT entry bool MidiDriver_Accolade_MT32::setupInstruments(byte *driverData, uint16 driverDataSize, bool useMusicDrvFile) { uint16 channelMappingOffset = 0; uint16 channelMappingSize = 0; uint16 instrumentMappingOffset = 0; uint16 instrumentMappingSize = 0; if (!useMusicDrvFile) { // INSTR.DAT: we expect at least 354 bytes if (driverDataSize < 354) return false; // Data is like this: // 128 bytes instrument mapping // 128 bytes instrument volume adjust (signed!) (not used for MT32) // 16 bytes unknown // 16 bytes channel mapping // 64 bytes key note mapping (not really used for MT32) // 1 byte instrument count // 1 byte bytes per instrument // x bytes no instruments used for MT32 channelMappingOffset = 256 + 16; channelMappingSize = 16; instrumentMappingOffset = 0; instrumentMappingSize = 128; } else { // MUSIC.DRV: we expect at least 468 bytes if (driverDataSize < 468) return false; channelMappingOffset = 396; channelMappingSize = 16; instrumentMappingOffset = 140; instrumentMappingSize = 128; } // Channel mapping if (channelMappingSize) { // Get these 16 bytes for MIDI channel mapping if (channelMappingSize != sizeof(_channelMapping)) return false; memcpy(_channelMapping, driverData + channelMappingOffset, sizeof(_channelMapping)); } else { // Set up straight mapping for (uint16 channelNr = 0; channelNr < sizeof(_channelMapping); channelNr++) { _channelMapping[channelNr] = channelNr; } } if (instrumentMappingSize) { // And these for instrument mapping if (instrumentMappingSize > sizeof(_instrumentMapping)) return false; memcpy(_instrumentMapping, driverData + instrumentMappingOffset, instrumentMappingSize); } // Set up straight mapping for the remaining data for (uint16 instrumentNr = instrumentMappingSize; instrumentNr < sizeof(_instrumentMapping); instrumentNr++) { _instrumentMapping[instrumentNr] = instrumentNr; } return true; } MidiDriver *MidiDriver_Accolade_MT32_create(Common::String driverFilename) { byte *driverData = NULL; uint16 driverDataSize = 0; bool isMusicDrvFile = false; MidiDriver_Accolade_readDriver(driverFilename, MT_MT32, driverData, driverDataSize, isMusicDrvFile); if (!driverData) error("ACCOLADE-ADLIB: error during readDriver()"); MidiDriver_Accolade_MT32 *driver = new MidiDriver_Accolade_MT32(); if (driver) { if (!driver->setupInstruments(driverData, driverDataSize, isMusicDrvFile)) { delete driver; driver = nullptr; } } delete[] driverData; return driver; } } // End of namespace AGOS