From 25c0e7ec333c40c4a7feac9eb639d2631743071c Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Tue, 2 Jun 2015 16:42:31 +0200 Subject: SHERLOCK: add mt32 music driver --- engines/sherlock/module.mk | 1 + engines/sherlock/music.cpp | 78 ++++--- engines/sherlock/scalpel/drivers/midi.cpp | 287 ++++++++++++++++++++++++++ engines/sherlock/scalpel/drivers/mididriver.h | 4 + engines/sherlock/sherlock.h | 3 +- 5 files changed, 341 insertions(+), 32 deletions(-) create mode 100644 engines/sherlock/scalpel/drivers/midi.cpp diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk index 1a38d56511..32ea862533 100644 --- a/engines/sherlock/module.mk +++ b/engines/sherlock/module.mk @@ -4,6 +4,7 @@ MODULE_OBJS = \ scalpel/darts.o \ scalpel/scalpel.o \ scalpel/drivers/adlib.o \ + scalpel/drivers/midi.o \ scalpel/tsage/logo.o \ scalpel/tsage/resources.o \ scalpel/scalpel_scene.o \ diff --git a/engines/sherlock/music.cpp b/engines/sherlock/music.cpp index a6f1e5b9b4..d165d924fd 100644 --- a/engines/sherlock/music.cpp +++ b/engines/sherlock/music.cpp @@ -62,28 +62,6 @@ MidiParser_SH::MidiParser_SH() { void MidiParser_SH::parseNextEvent(EventInfo &info) { // warning("parseNextEvent"); - // An attempt to remap MT32 instruments to GMIDI. Only partially successful, it still - // does not sound even close to the real MT32. Oddly enough, on the actual hardware MT32 - // and SB sound very differently. - static const byte mt32Map[128] = { - 0, 1, 0, 2, 4, 4, 5, 3, /* 0-7 */ - 16, 17, 18, 16, 16, 19, 20, 21, /* 8-15 */ - 6, 6, 6, 7, 7, 7, 8, 112, /* 16-23 */ - 62, 62, 63, 63 , 38, 38, 39, 39, /* 24-31 */ - 88, 95, 52, 98, 97, 99, 14, 54, /* 32-39 */ - 102, 96, 53, 102, 81, 100, 14, 80, /* 40-47 */ - 48, 48, 49, 45, 41, 40, 42, 42, /* 48-55 */ - 43, 46, 45, 24, 25, 28, 27, 104, /* 56-63 */ - 32, 32, 34, 33, 36, 37, 35, 35, /* 64-71 */ - 79, 73, 72, 72, 74, 75, 64, 65, /* 72-79 */ - 66, 67, 71, 71, 68, 69, 70, 22, /* 80-87 */ - 56, 59, 57, 57, 60, 60, 58, 61, /* 88-95 */ - 61, 11, 11, 98, 14, 9, 14, 13, /* 96-103 */ - 12, 107, 107, 77, 78, 78, 76, 76, /* 104-111 */ - 47, 117, 127, 118, 118, 116, 115, 119, /* 112-119 */ - 115, 112, 55, 124, 123, 0, 14, 117 /* 120-127 */ - }; - // there is no delta right at the start of the music data // this order is essential, otherwise notes will get delayed or even go missing if (_position._playPos != _tracks[0]) { @@ -207,11 +185,13 @@ bool MidiParser_SH::loadMusic(byte *data, uint32 size) { /*----------------------------------------------------------------*/ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) { + _musicPlaying = false; + _musicOn = true; + if (_vm->_interactiveFl) _vm->_res->addToCache("MUSIC.LIB"); - MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM); - + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32); _musicType = MidiDriver::getMusicType(dev); _driver = NULL; @@ -220,8 +200,18 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) { case MT_ADLIB: _driver = MidiDriver_AdLib_create(); break; + case MT_MT32: + _driver = MidiDriver_MIDI_create(); + break; + case MT_GM: + if (ConfMan.getBool("native_mt32")) { + _driver = MidiDriver_MIDI_create(); + _musicType = MT_MT32; + } default: - _driver = MidiDriver::createMidi(dev); + // Create default one + // I guess we shouldn't do this anymore + //_driver = MidiDriver::createMidi(dev); break; } @@ -230,15 +220,34 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) { int ret = _driver->open(); if (ret == 0) { - _driver->sendGMReset(); + // Reset is done inside our MIDI driver _driver->setTimerCallback(&_midiParser, &_midiParser.timerCallback); } _midiParser.setMidiDriver(_driver); _midiParser.setTimerRate(_driver->getBaseTempo()); - } - _musicPlaying = false; - _musicOn = true; + if (_musicType == MT_MT32) { + // Upload patches + Common::SeekableReadStream *MT32driverStream = _vm->_res->load("MTHOM.DRV", "MUSIC.LIB"); + + byte *MT32driverData = new byte[MT32driverStream->size()]; + int32 MT32driverDataSize = MT32driverStream->size(); + assert(MT32driverData); + + MT32driverStream->read(MT32driverData, MT32driverDataSize); + delete MT32driverStream; + + assert(MT32driverDataSize > 12); + byte *MT32driverDataPtr = MT32driverData + 12; + MT32driverDataSize -= 12; + + MidiDriver_MIDI_uploadMT32Patches(_driver, MT32driverDataPtr, MT32driverDataSize); + delete[] MT32driverData; + } + } else { + // no driver, bye bye music + _musicOn = false; + } } bool Music::loadSong(int songNumber) { @@ -323,9 +332,16 @@ bool Music::playMusic(const Common::String &name) { return false; } - if (_musicType == MT_ADLIB) { - if (_driver) + if (_driver) { + switch (_musicType) { + case MT_ADLIB: MidiDriver_AdLib_newMusicData(_driver, dataPos, dataSize); + break; + + case MT_MT32: + MidiDriver_MIDI_newMusicData(_driver, dataPos, dataSize); + break; + } } _midiParser.loadMusic(dataPos, dataSize); diff --git a/engines/sherlock/scalpel/drivers/midi.cpp b/engines/sherlock/scalpel/drivers/midi.cpp new file mode 100644 index 0000000000..3a9fd9e722 --- /dev/null +++ b/engines/sherlock/scalpel/drivers/midi.cpp @@ -0,0 +1,287 @@ +/* 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 "sherlock/sherlock.h" +#include "sherlock/scalpel/drivers/mididriver.h" + +#include "common/config-manager.h" +#include "common/file.h" +#include "common/system.h" +#include "common/textconsole.h" + +//#include "audio/mididrv.h" + +namespace Sherlock { + +#define SHERLOCK_MT32_CHANNEL_COUNT 16 + +const byte mt32_reverbDataSysEx[] = { + 0x10, 0x00, 0x01, 0x01, 0x05, 0x05, 0xFF +}; + +class MidiDriver_MIDI : public MidiDriver { +public: + MidiDriver_MIDI() { + _driver = NULL; + _isOpen = false; + _MT32 = false; + _nativeMT32 = false; + _baseFreq = 250; + + memset(_MIDIchannelActive, 1, sizeof(_MIDIchannelActive)); + } + virtual ~MidiDriver_MIDI(); + + // MidiDriver + int open(); + void close(); + bool isOpen() const { return _isOpen; } + + void send(uint32 b); + + void newMusicData(byte *musicData, int32 musicDataSize); + + 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; + } + +private: + Common::TimerManager::TimerProc _timerProc; + void *_timerParam; + +protected: + Common::Mutex _mutex; + MidiDriver *_driver; + bool _MT32; + bool _nativeMT32; + + bool _isOpen; + int _baseFreq; + +private: + // points to a MIDI channel for each of the new voice channels + byte _MIDIchannelActive[SHERLOCK_MT32_CHANNEL_COUNT]; + +public: + void uploadMT32Patches(byte *driverData, int32 driverSize); + + void MT32SysEx(const byte *&dataPtr, int32 &bytesLeft); +}; + +MidiDriver_MIDI::~MidiDriver_MIDI() { + Common::StackLock lock(_mutex); + if (_driver) { + _driver->setTimerCallback(0, 0); + _driver->close(); + delete _driver; + } + _driver = NULL; +} + +int MidiDriver_MIDI::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); + + switch (musicType) { + case MT_MT32: + _MT32 = true; + _nativeMT32 = false; + break; + case MT_GM: + if (ConfMan.getBool("native_mt32")) { + _MT32 = true; + _nativeMT32 = true; + } + break; + default: + break; + } + + _driver = MidiDriver::createMidi(dev); + if (!_driver) + return 255; + + if (_nativeMT32) + _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + + int ret = _driver->open(); + if (ret) + return ret; + + if (_MT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + return 0; +} + +void MidiDriver_MIDI::close() { + if (_driver) { + _driver->close(); + } +} + +// Called when a music track got loaded into memory +void MidiDriver_MIDI::newMusicData(byte *musicData, int32 musicDataSize) { + assert(musicDataSize >= 0x7F); + // MIDI Channel Enable/Disable bytes at offset 0x2 of music data + memcpy(&_MIDIchannelActive, musicData + 0x2, SHERLOCK_MT32_CHANNEL_COUNT); + + // Send 16 bytes from offset 0x12 to MT32 + // TODO + // Although at least the Prologue music doesn't use this at all + + // Also send these bytes to MT32 (SysEx) - seems to be reverb configuration + if (_MT32) { + const byte *reverbData = mt32_reverbDataSysEx; + int32 reverbDataSize = sizeof(mt32_reverbDataSysEx); + MT32SysEx(reverbData, reverbDataSize); + } +} + +void MidiDriver_MIDI::uploadMT32Patches(byte *driverData, int32 driverSize) { + if (!_driver) + return; + + if (!_MT32) + return; + + // patch data starts at offset 0x863 + assert(driverSize == 0x13B9); // Security check + assert(driverData[0x863] == 0x7F); // another security check + + const byte *patchPtr = driverData + 0x863; + int32 bytesLeft = driverSize - 0x863; + + while(1) { + MT32SysEx(patchPtr, bytesLeft); + + assert(bytesLeft); + if (*patchPtr == 0x80) // List terminator + break; + } +} + +void MidiDriver_MIDI::MT32SysEx(const byte *&dataPtr, int32 &bytesLeft) { + byte sysExMessage[270]; + byte sysExPos = 0; + byte sysExByte = 0; + uint16 sysExChecksum = 0; + + memset(&sysExMessage, 0, sizeof(sysExMessage)); + + sysExMessage[0] = 0x41; // Roland + sysExMessage[1] = 0x10; + sysExMessage[2] = 0x16; // Model MT32 + sysExMessage[3] = 0x12; // Command DT1 + + sysExPos = 4; + sysExChecksum = 0; + while (1) { + assert(bytesLeft); + + sysExByte = *dataPtr++; + bytesLeft--; + if (sysExByte == 0xff) + break; // Message done + + assert(sysExPos < 260); + sysExMessage[sysExPos++] = sysExByte; + sysExChecksum -= sysExByte; + } + + // Calculate checksum + sysExMessage[sysExPos++] = sysExChecksum & 0x7f; + + debugC(kDebugLevelMT32Driver, "MT32: uploading patch data, size %d", sysExPos); + + // Send SysEx + _driver->sysEx(sysExMessage, sysExPos); + + // Wait the time it takes to send the SysEx data + uint32 delay = (sysExPos + 2) * 1000 / 3125; + + // Plus an additional delay for the MT-32 rev00 + if (_nativeMT32) + delay += 40; + + g_system->delayMillis(delay); +} + +// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php +void MidiDriver_MIDI::send(uint32 b) { + byte command = b & 0xf0; + byte channel = b & 0xf; + + if (command == 0xF0) { + if (_driver) { + _driver->send(b); + } + return; + } + + if (_MIDIchannelActive[channel]) { + // Only forward MIDI-data in case the channel is currently enabled via music-data + if (_driver) { + _driver->send(b); + } + } +} + +MidiDriver *MidiDriver_MIDI_create() { + return new MidiDriver_MIDI(); +} + +void MidiDriver_MIDI_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize) { + static_cast(driver)->newMusicData(musicData, musicDataSize); +} + +void MidiDriver_MIDI_uploadMT32Patches(MidiDriver *driver, byte *driverData, int32 driverSize) { + static_cast(driver)->uploadMT32Patches(driverData, driverSize); +} + +} // End of namespace Sci diff --git a/engines/sherlock/scalpel/drivers/mididriver.h b/engines/sherlock/scalpel/drivers/mididriver.h index 64213315e8..e38f27dc4e 100644 --- a/engines/sherlock/scalpel/drivers/mididriver.h +++ b/engines/sherlock/scalpel/drivers/mididriver.h @@ -32,6 +32,10 @@ namespace Sherlock { extern MidiDriver *MidiDriver_AdLib_create(); extern void MidiDriver_AdLib_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize); +extern MidiDriver *MidiDriver_MIDI_create(); +extern void MidiDriver_MIDI_uploadMT32Patches(MidiDriver *driver, byte *driverData, int32 driverSize); +extern void MidiDriver_MIDI_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize); + } // End of namespace Sci #endif // SHERLOCK_SOFTSEQ_MIDIDRIVER_H diff --git a/engines/sherlock/sherlock.h b/engines/sherlock/sherlock.h index c233373dbc..75c8498e4e 100644 --- a/engines/sherlock/sherlock.h +++ b/engines/sherlock/sherlock.h @@ -53,7 +53,8 @@ namespace Sherlock { enum { kDebugLevelScript = 1 << 0, kDebugLevelAdLibDriver = 2 << 0, - kDebugLevelMusic = 3 << 0 + kDebugLevelMT32Driver = 3 << 0, + kDebugLevelMusic = 4 << 0 }; enum GameType { -- cgit v1.2.3