[Scummvm-cvs-logs] scummvm master -> ce3ceb9296205d16f63135e98fdb729e2abd60ad

m-kiewitz m_kiewitz at users.sourceforge.net
Sat Jun 27 17:05:30 CEST 2015


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
ce3ceb9296 SHERLOCK: RT: Miles Audio 3 MT32 driver


Commit: ce3ceb9296205d16f63135e98fdb729e2abd60ad
    https://github.com/scummvm/scummvm/commit/ce3ceb9296205d16f63135e98fdb729e2abd60ad
Author: Martin Kiewitz (m_kiewitz at users.sourceforge.net)
Date: 2015-06-27T17:04:26+02:00

Commit Message:
SHERLOCK: RT: Miles Audio 3 MT32 driver

- implement Miles Audio 3 MT32 driver
- work in progress
- Miles Audio timbre files not supported atm b/c RT does not use them

Changed paths:
  A engines/sherlock/tattoo/drivers/tattoo_mt32.cpp
    engines/sherlock/module.mk
    engines/sherlock/music.cpp
    engines/sherlock/tattoo/drivers/tattoo_mididriver.h



diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk
index 2a5cff6..704b8d2 100644
--- a/engines/sherlock/module.mk
+++ b/engines/sherlock/module.mk
@@ -20,6 +20,7 @@ MODULE_OBJS = \
 	scalpel/scalpel_user_interface.o \
 	scalpel/settings.o \
 	tattoo/drivers/tattoo_adlib.o \
+	tattoo/drivers/tattoo_mt32.o \
 	tattoo/tattoo.o \
 	tattoo/tattoo_fixed_text.o \
 	tattoo/tattoo_journal.o \
diff --git a/engines/sherlock/music.cpp b/engines/sherlock/music.cpp
index c8ca49c..0ee6cda 100644
--- a/engines/sherlock/music.cpp
+++ b/engines/sherlock/music.cpp
@@ -276,10 +276,17 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
 			// although in case of Rose Tattoo both files are exactly the same
 			_midiDriver = MidiDriver_Miles_AdLib_create("SAMPLE.AD", "SAMPLE.OPL");
 			break;
+		case MT_MT32:
+			_midiDriver = MidiDriver_Miles_MT32_create("SAMPLE.MT");
+			break;
+		case MT_GM:
+			if (ConfMan.getBool("native_mt32")) {
+				_midiDriver = MidiDriver_Miles_MT32_create("SAMPLE.MT");
+				_musicType = MT_MT32;
+			}
+			break;
 		default:
-			// HACK
-			_musicType = MT_GM;
-			_midiDriver = MidiDriver::createMidi(dev);
+			// Do not create anything
 			break;
 		}
 	}
@@ -293,23 +300,28 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
 		_midiParser->setMidiDriver(_midiDriver);
 		_midiParser->setTimerRate(_midiDriver->getBaseTempo());
 
-		if (_musicType == MT_MT32) {
-			// Upload patches
-			Common::SeekableReadStream *MT32driverStream = _vm->_res->load("MTHOM.DRV", "MUSIC.LIB");
+		if (IS_SERRATED_SCALPEL) {
+			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);
+				if (!MT32driverStream)
+					error("Music: could not load MTHOM.DRV, critical");
 
-			MT32driverStream->read(MT32driverData, MT32driverDataSize);
-			delete MT32driverStream;
+				byte *MT32driverData = new byte[MT32driverStream->size()];
+				int32 MT32driverDataSize = MT32driverStream->size();
+				assert(MT32driverData);
 
-			assert(MT32driverDataSize > 12);
-			byte *MT32driverDataPtr = MT32driverData + 12;
-			MT32driverDataSize -= 12;
+				MT32driverStream->read(MT32driverData, MT32driverDataSize);
+				delete MT32driverStream;
 
-			MidiDriver_MT32_uploadPatches(_midiDriver, MT32driverDataPtr, MT32driverDataSize);
-			delete[] MT32driverData;
+				assert(MT32driverDataSize > 12);
+				byte *MT32driverDataPtr = MT32driverData + 12;
+				MT32driverDataSize -= 12;
+
+				MidiDriver_MT32_uploadPatches(_midiDriver, MT32driverDataPtr, MT32driverDataSize);
+				delete[] MT32driverData;
+			}
 		}
 
 		_musicOn = true;
diff --git a/engines/sherlock/tattoo/drivers/tattoo_mididriver.h b/engines/sherlock/tattoo/drivers/tattoo_mididriver.h
index a2335cf..d140135 100644
--- a/engines/sherlock/tattoo/drivers/tattoo_mididriver.h
+++ b/engines/sherlock/tattoo/drivers/tattoo_mididriver.h
@@ -43,13 +43,26 @@ namespace Sherlock {
 #define MILES_CONTROLLER_PITCH_RANGE 6
 #define MILES_CONTROLLER_RESET_ALL 121
 #define MILES_CONTROLLER_ALL_NOTES_OFF 123
+#define MILES_CONTROLLER_PATCH_REVERB 59
+#define MILES_CONTROLLER_PATCH_BENDER 60
+#define MILES_CONTROLLER_REVERB_MODE 61
+#define MILES_CONTROLLER_REVERB_TIME 62
+#define MILES_CONTROLLER_REVERB_LEVEL 63
+#define MILES_CONTROLLER_RHYTHM_KEY_TIMBRE 58
+
+// 3 SysEx controllers, each range 14
+#define MILES_CONTROLLER_SYSEX_RANGE_BEGIN 32
+#define MILES_CONTROLLER_SYSEX_RANGE_END 64
+
+#define MILES_CONTROLLER_XMIDI_RANGE_BEGIN 110
+#define MILES_CONTROLLER_XMIDI_RANGE_END 120
 
 // Miles Audio actually used 0x4000, because they didn't shift the 2 bytes properly
 #define MILES_PITCHBENDER_DEFAULT 0x2000
 
 extern MidiDriver *MidiDriver_Miles_AdLib_create(const Common::String instrumentDataFilename, const Common::String instrumentDataFilenameOPL3);
 
-//extern MidiDriver *MidiDriver_Tattoo_MT32_create();
+extern MidiDriver *MidiDriver_Miles_MT32_create(const Common::String instrumentDataFilename);
 
 } // End of namespace Sherlock
 
diff --git a/engines/sherlock/tattoo/drivers/tattoo_mt32.cpp b/engines/sherlock/tattoo/drivers/tattoo_mt32.cpp
new file mode 100644
index 0000000..73bc1b9
--- /dev/null
+++ b/engines/sherlock/tattoo/drivers/tattoo_mt32.cpp
@@ -0,0 +1,437 @@
+/* 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/tattoo/drivers/tattoo_mididriver.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+namespace Sherlock {
+
+#define MILES_MT32_PATCH_COUNT 128
+
+const byte milesMT32SysExResetParameters[] = {
+	0x01, 0xFF
+};
+
+const byte milesMT32SysExChansSetup[] = {
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF
+};
+
+const byte milesMT32SysExPartialReserveTable[] = {
+	0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, 0xFF
+};
+
+const byte milesMT32SysExInitReverb[] = {
+	0x00, 0x03, 0x02, 0xFF // Reverb mode 0, reverb time 3, reverb level 2
+};
+
+class MidiDriver_Miles_MT32 : public MidiDriver {
+public:
+	MidiDriver_Miles_MT32();
+	virtual ~MidiDriver_Miles_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 _MT32;
+	bool _nativeMT32;
+
+	bool _isOpen;
+	int _baseFreq;
+
+public:
+
+private:
+	void resetMT32();
+
+	void MT32SysEx(const uint32 targetAddress, const byte *dataPtr);
+
+	void writePatchTimbre(byte patchId, byte timbreGroup, byte timbreId);
+	void writePatchByte(byte patchId, byte index, byte patchValue);
+	void writeToSystemArea(byte index, byte value);
+
+	void controlChange(byte midiChannel, byte controllerNumber, byte controllerValue);
+	void programChange(byte midiChannel, byte patchId);
+
+	void setupPatch(byte patchId, byte patchBank);
+
+private:
+	struct MidiChannelEntry {
+		byte   currentPatchBank;
+		byte   currentPatchId;
+		bool   patchIdSet;
+
+		MidiChannelEntry() : currentPatchBank(0),
+							currentPatchId(0),
+							patchIdSet(false) { }
+	};
+
+	// stores information about all MIDI channels
+	MidiChannelEntry _midiChannels[MILES_MIDI_CHANNEL_COUNT];
+
+	byte _patchesBank[MILES_MT32_PATCH_COUNT];
+};
+
+MidiDriver_Miles_MT32::MidiDriver_Miles_MT32() {
+	_driver = NULL;
+	_isOpen = false;
+	_MT32 = false;
+	_nativeMT32 = false;
+	_baseFreq = 250;
+
+	memset(_patchesBank, 0, sizeof(_patchesBank));
+}
+
+MidiDriver_Miles_MT32::~MidiDriver_Miles_MT32() {
+	Common::StackLock lock(_mutex);
+	if (_driver) {
+		_driver->setTimerCallback(0, 0);
+		_driver->close();
+		delete _driver;
+	}
+	_driver = NULL;
+}
+
+int MidiDriver_Miles_MT32::open() {
+	assert(!_driver);
+
+	// Setup midi driver
+	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32);
+	MusicType musicType = MidiDriver::getMusicType(dev);
+
+	switch (musicType) {
+	case MT_MT32:
+		_nativeMT32 = true;
+		break;
+	case MT_GM:
+		if (ConfMan.getBool("native_mt32")) {
+			_nativeMT32 = true;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (!_nativeMT32) {
+		error("MILES-MT32: non-mt32 currently not supported!");
+	}
+
+	_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 (_nativeMT32) {
+		_driver->sendMT32Reset();
+
+		resetMT32();
+	}
+
+	return 0;
+}
+
+void MidiDriver_Miles_MT32::close() {
+	if (_driver) {
+		_driver->close();
+	}
+}
+
+void MidiDriver_Miles_MT32::resetMT32() {
+	// reset all internal parameters / patches
+	MT32SysEx(0x7F0000, milesMT32SysExResetParameters);
+
+	// init part/channel assignments
+	MT32SysEx(0x10000D, milesMT32SysExChansSetup);
+
+	// partial reserve table
+	MT32SysEx(0x100004, milesMT32SysExPartialReserveTable);
+
+	// init reverb
+	MT32SysEx(0x100001, milesMT32SysExInitReverb);
+}
+
+void MidiDriver_Miles_MT32::MT32SysEx(const uint32 targetAddress, const byte *dataPtr) {
+	byte   sysExMessage[270];
+	uint16 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
+
+	sysExChecksum = 0;
+
+	sysExMessage[4] = (targetAddress >> 16) & 0xFF;
+	sysExMessage[5] = (targetAddress >> 8) & 0xFF;
+	sysExMessage[6] = targetAddress & 0xFF;
+
+	sysExChecksum -= sysExMessage[4];
+	sysExChecksum -= sysExMessage[5];
+	sysExChecksum -= sysExMessage[6];
+
+	sysExPos      = 7;
+	while (1) {
+		sysExByte = *dataPtr++;
+		if (sysExByte == 0xff)
+			break; // Message done
+
+		assert(sysExPos < sizeof(sysExMessage));
+		sysExMessage[sysExPos++] = sysExByte;
+		sysExChecksum -= sysExByte;
+	}
+
+	// Calculate checksum
+	assert(sysExPos < sizeof(sysExMessage));
+	sysExMessage[sysExPos++] = sysExChecksum & 0x7f;
+
+	// 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_Miles_MT32::send(uint32 b) {
+	byte command = b & 0xf0;
+	byte channel = b & 0xf;
+	byte op1 = (b >> 8) & 0xff;
+	byte op2 = (b >> 16) & 0xff;
+
+	switch (command) {
+	case 0x80: // note off
+	case 0x90: // note on
+	case 0xe0: // pitch bend change
+		_driver->send(b);
+		break;
+	case 0xb0: // Control change
+		controlChange(channel, op1, op2);
+		break;
+	case 0xc0: // Program Change
+		programChange(channel, op1);
+		break;
+	case 0xa0: // Polyphonic key pressure (aftertouch)
+	case 0xd0: // Channel pressure (aftertouch)
+		// Aftertouch doesn't seem to be implemented in the Sherlock Holmes adlib driver
+		break;
+	case 0xf0: // SysEx
+		warning("MILES-MT32: SysEx: %x", b);
+		break;
+	default:
+		warning("MILES-MT32: Unknown event %02x", command);
+	}
+}
+
+void MidiDriver_Miles_MT32::controlChange(byte midiChannel, byte controllerNumber, byte controllerValue) {
+	byte channelPatchId = 0;
+
+	switch (controllerNumber) {
+	case MILES_CONTROLLER_SELECT_PATCH_BANK:
+		_midiChannels[midiChannel].currentPatchBank = controllerValue;
+		return;
+
+	case MILES_CONTROLLER_PATCH_REVERB:
+		channelPatchId = _midiChannels[midiChannel].currentPatchId;
+
+		writePatchByte(channelPatchId, 6, controllerValue);
+		_driver->send(0xC0 | midiChannel | (channelPatchId << 8)); // execute program change
+		return;
+
+	case MILES_CONTROLLER_PATCH_BENDER:
+		channelPatchId = _midiChannels[midiChannel].currentPatchId;
+
+		writePatchByte(channelPatchId, 4, controllerValue);
+		_driver->send(0xC0 | midiChannel | (channelPatchId << 8)); // execute program change
+		return;
+
+	case MILES_CONTROLLER_REVERB_MODE:
+		writeToSystemArea(1, controllerValue);
+		return;
+
+	case MILES_CONTROLLER_REVERB_TIME:
+		writeToSystemArea(2, controllerValue);
+		return;
+
+	case MILES_CONTROLLER_REVERB_LEVEL:
+		writeToSystemArea(3, controllerValue);
+		return;
+
+	case MILES_CONTROLLER_RHYTHM_KEY_TIMBRE:
+		// uses .MT data, cannot implement atm
+		return;
+
+	case MILES_CONTROLLER_PROTECT_TIMBRE:
+		// timbre .MT data, cannot implement atm
+		return;
+
+	default:
+		break;
+	}
+
+	if ((controllerNumber >= MILES_CONTROLLER_SYSEX_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_SYSEX_RANGE_END)) {
+		// send SysEx
+		warning("MILES-MT32: embedded SysEx controller %2x, value %2x", controllerNumber, controllerValue);
+		return;
+	}
+
+	if ((controllerNumber >= MILES_CONTROLLER_XMIDI_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_XMIDI_RANGE_END)) {
+		// XMIDI controllers? ignore those
+		return;
+	}
+
+	_driver->send(0xB0 | midiChannel | (controllerNumber << 8) | (controllerValue << 16));
+}
+
+void MidiDriver_Miles_MT32::programChange(byte midiChannel, byte patchId) {
+	byte channelPatchBank = _midiChannels[midiChannel].currentPatchBank;
+	byte activePatchBank = _patchesBank[patchId];
+
+	// remember patch id for the current MIDI-channel
+	_midiChannels[midiChannel].currentPatchId = patchId;
+
+	if (channelPatchBank != activePatchBank) {
+		// associate patch with timbre
+		setupPatch(patchId, channelPatchBank);
+		warning("setup patch");
+	}
+
+	// Search timbre and remember it (only used when timbre file is available)
+	// TODO
+
+	// Finally send to MT32
+	_driver->send(0xC0 | midiChannel | (patchId << 8));
+}
+
+void MidiDriver_Miles_MT32::setupPatch(byte patchId, byte patchBank) {
+	byte timbreId = 0;
+
+	_patchesBank[patchId] = patchBank;
+
+	if (patchBank) {
+		// non-built-in bank
+		// TODO: search timbre
+	}
+
+	// for built-in bank (or timbres, that are not available) use default MT32 timbres
+	timbreId = patchId & 0x3F;
+	if (!(patchId & 0x40)) {
+		writePatchTimbre(patchId, 0, timbreId); // Group A
+	} else {
+		writePatchTimbre(patchId, 1, timbreId); // Group B
+	}
+}
+
+void MidiDriver_Miles_MT32::writePatchTimbre(byte patchId, byte timbreGroup, byte timbreId) {
+	byte   sysExData[3];
+	uint32 targetAddress = 0;
+
+	targetAddress = ((patchId << 3) << 16) | 0x000500;
+
+	sysExData[0] = timbreGroup;
+	sysExData[1] = timbreId;
+	sysExData[2] = 0xFF; // terminator
+
+	MT32SysEx(targetAddress, sysExData);
+}
+
+void MidiDriver_Miles_MT32::writePatchByte(byte patchId, byte index, byte patchValue) {
+	byte   sysExData[2];
+	uint32 targetAddress = 0;
+
+	targetAddress = (((patchId << 3) + index ) << 16) | 0x000500;
+
+	sysExData[0] = patchValue;
+	sysExData[1] = 0xFF; // terminator
+
+	MT32SysEx(targetAddress, sysExData);
+}
+
+void MidiDriver_Miles_MT32::writeToSystemArea(byte index, byte value) {
+	byte   sysExData[2];
+	uint32 targetAddress = 0;
+
+	targetAddress = 0x100000 | index;
+
+	sysExData[0] = value;
+	sysExData[1] = 0xFF; // terminator
+
+	MT32SysEx(targetAddress, sysExData);
+}
+
+MidiDriver *MidiDriver_Miles_MT32_create(const Common::String instrumentDataFilename) {
+	// For some games there are timbre files called [something].MT
+	// Sherlock Holmes 2 doesn't have one of those
+	// so I can't implement them
+	return new MidiDriver_Miles_MT32();
+}
+
+} // End of namespace Sherlock






More information about the Scummvm-git-logs mailing list