[Scummvm-git-logs] scummvm master -> 70c2d07f198461d96bdf727495295a2590ae785e

athrxx noreply at scummvm.org
Thu Oct 6 16:48:20 UTC 2022


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

Summary:
99dd6cb248 SCUMM: (IMS) - minor restructuring and cleanup
40b459164b SCUMM: (IMS) - add dedicated MT-32 sound driver
6984faf958 AUDIO: add data size arg to MidiChannel::sysEx_customInstrument()
ead51cb45e SCUMM: (IMS) - add general midi driver
caa96a50f0 SCUMM: (IMS) - refactor MT32/GM drivers
70c2d07f19 SCUMM: (IMS) - minor fixes


Commit: 99dd6cb248a89460b973f7f29b259b34d9e84e8a
    https://github.com/scummvm/scummvm/commit/99dd6cb248a89460b973f7f29b259b34d9e84e8a
Author: athrxx (athrxx at scummvm.org)
Date: 2022-10-06T18:47:40+02:00

Commit Message:
SCUMM: (IMS) - minor restructuring and cleanup

Currently, Roland MT-32 sound is broken for
SAMNMAX. Just try the intro song with shut off
speech. It is very obvious that it plays with a quite
reduced number of channels compared to the
original interpreter.

Now, due to the not-so-helpful code design (much
of the iMuse code has been drawn into the common
code) it has become increasingly difficult to fix Midi
related thing in iMuse. I have added more and more
crude hacks over time.

SAMNMAX requires more elaborate channel
allocation. To make it happen I have added driver
wrappers for Midi to the iMuse code.

Other than that, I have done only minor cleanup here.

Actually, I would have liked to withdraw much more
of the iMuse code from the common code and move
it to SCUMM (basically all the MidiChannel
stuff which is exclusively used by iMuse. But it turns
out that it is so thoroughly intertwined (the major
blocker here being the AdLib driver) that it requires
more thought and effort and would just distract me
from fixing the SAMNMAX sound.

Changed paths:
  A engines/scumm/imuse/drivers/gmidi.cpp
  A engines/scumm/imuse/drivers/gmidi.h
  A engines/scumm/imuse/drivers/mt32.cpp
  A engines/scumm/imuse/drivers/mt32.h
    audio/adlib.cpp
    audio/mididrv.h
    audio/mpu401.cpp
    audio/mpu401.h
    engines/scumm/imuse/drivers/fmtowns.cpp
    engines/scumm/imuse/drivers/fmtowns.h
    engines/scumm/imuse/drivers/mac_m68k.cpp
    engines/scumm/imuse/drivers/mac_m68k.h
    engines/scumm/imuse/drivers/pcspk.cpp
    engines/scumm/imuse/drivers/pcspk.h
    engines/scumm/imuse/imuse.cpp
    engines/scumm/imuse/imuse_internal.h
    engines/scumm/imuse/imuse_part.cpp
    engines/scumm/imuse/imuse_player.cpp
    engines/scumm/module.mk
    engines/scumm/scumm.cpp


diff --git a/audio/adlib.cpp b/audio/adlib.cpp
index b4099128a8f..196e7feed89 100644
--- a/audio/adlib.cpp
+++ b/audio/adlib.cpp
@@ -942,7 +942,6 @@ public:
 	uint32 getBaseTempo() override { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
 
 	void setPitchBendRange(byte channel, uint range) override;
-	void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override;
 
 	MidiChannel *allocateChannel() override;
 	MidiChannel *getPercussionChannel() override { return &_percussion; } // Percussion partially supported
@@ -1585,10 +1584,6 @@ void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) {
 	}
 }
 
-void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
-	_parts[channel].sysEx_customInstrument(type, instr);
-}
-
 MidiChannel *MidiDriver_ADLIB::allocateChannel() {
 	AdLibPart *part;
 	uint i;
diff --git a/audio/mididrv.h b/audio/mididrv.h
index adc85b6e905..49a279f6246 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -499,8 +499,6 @@ public:
 	 */
 	void sendGMReset();
 
-	virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { }
-
 	// Timing functions - MidiDriver now operates timers
 	virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) = 0;
 
diff --git a/audio/mpu401.cpp b/audio/mpu401.cpp
index 76ae76fd8f0..06ab6ebd355 100644
--- a/audio/mpu401.cpp
+++ b/audio/mpu401.cpp
@@ -71,9 +71,6 @@ void MidiChannel_MPU401::pitchBendFactor(byte value) {
 	_owner->setPitchBendRange(_channel, value);
 }
 
-void MidiChannel_MPU401::sysEx_customInstrument(uint32 type, const byte *instr) {
-	_owner->sysEx_customInstrument(_channel, type, instr);
-}
 
 const char *MidiDriver::getErrorName(int error_code) {
 	static const char *const midi_errors[] = {
diff --git a/audio/mpu401.h b/audio/mpu401.h
index 9d91a144bd4..13857d852ab 100644
--- a/audio/mpu401.h
+++ b/audio/mpu401.h
@@ -55,7 +55,7 @@ public:
 	virtual void pitchBendFactor(byte value);
 
 	// SysEx messages
-	virtual void sysEx_customInstrument(uint32 type, const byte *instr);
+	virtual void sysEx_customInstrument(uint32 type, const byte *instr) {}
 
 	// Only to be called by the owner
 	void init(MidiDriver *owner, byte channel);
diff --git a/engines/scumm/imuse/drivers/fmtowns.cpp b/engines/scumm/imuse/drivers/fmtowns.cpp
index 787e787a5cf..6161bc8c82a 100644
--- a/engines/scumm/imuse/drivers/fmtowns.cpp
+++ b/engines/scumm/imuse/drivers/fmtowns.cpp
@@ -19,15 +19,17 @@
  *
  */
 
-#include "engines/scumm/imuse/drivers/fmtowns.h"
+#include "scumm/imuse/drivers/fmtowns.h"
 #include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h"
 #include "common/textconsole.h"
 #include "common/system.h"
 
+namespace Scumm {
+
 class TownsMidiOutputChannel {
 friend class TownsMidiInputChannel;
 public:
-	TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanId);
+	TownsMidiOutputChannel(IMuseDriver_FMTowns *driver, int chanId);
 	~TownsMidiOutputChannel();
 
 	void noteOn(uint8 msb, uint16 lsb);
@@ -103,7 +105,7 @@ private:
 	uint16 _freq;
 	int16 _freqAdjust;
 
-	MidiDriver_TOWNS *_driver;
+	IMuseDriver_FMTowns *_driver;
 
 	static const uint8 _chanMap[];
 	static const uint8 _chanMap2[];
@@ -116,11 +118,12 @@ private:
 class TownsMidiInputChannel : public MidiChannel {
 friend class TownsMidiOutputChannel;
 public:
-	TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex);
+	TownsMidiInputChannel(IMuseDriver_FMTowns *driver, int16 number);
 	~TownsMidiInputChannel() override;
 
 	MidiDriver *device() override { return _driver; }
-	byte getNumber() override { return _chanIndex; }
+	byte getNumber() override { return _number; }
+
 	bool allocate();
 	void release() override;
 
@@ -147,8 +150,8 @@ private:
 
 	TownsMidiOutputChannel *_out;
 
+	const byte _number;
 	uint8 *_instrument;
-	uint8 _chanIndex;
 	uint8 _priority;
 	uint8 _tl;
 	int8 _transpose;
@@ -161,7 +164,7 @@ private:
 
 	bool _allocated;
 
-	MidiDriver_TOWNS *_driver;
+	IMuseDriver_FMTowns *_driver;
 
 	static const uint8 _programAdjustLevel[];
 };
@@ -207,7 +210,7 @@ uint8 TownsMidiChanState::get(uint8 type) {
 	return 0;
 }
 
-TownsMidiOutputChannel::TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanIndex) : _driver(driver), _chan(chanIndex),
+TownsMidiOutputChannel::TownsMidiOutputChannel(IMuseDriver_FMTowns *driver, int number) : _driver(driver), _chan(number),
 	_in(nullptr), _prev(nullptr), _next(nullptr), _adjustModTl(0), _operator2Tl(0), _note(0), _operator1Tl(0), _sustainNoteOff(0), _duration(0), _freq(0), _freqAdjust(0) {
 	_effectEnvelopes = new EffectEnvelope[2]();
 	_effectDefs = new EffectDef[2]();
@@ -651,7 +654,7 @@ const uint16 TownsMidiOutputChannel::_freqLSB[] = {
 	0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B
 };
 
-TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(nullptr), _chanIndex(chanIndex),
+TownsMidiInputChannel::TownsMidiInputChannel(IMuseDriver_FMTowns *driver, int16 number) : MidiChannel(), _driver(driver), _number(number), _out(nullptr),
 	_priority(0), _tl(0), _transpose(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), _detune(0), _modWheel(0), _allocated(false) {
 	_instrument = new uint8[30]();
 }
@@ -672,7 +675,7 @@ void TownsMidiInputChannel::release() {
 }
 
 void TownsMidiInputChannel::send(uint32 b) {
-	_driver->send(b | _chanIndex);
+	device()->send(b | _number);
 }
 
 void TownsMidiInputChannel::noteOff(byte note) {
@@ -836,7 +839,7 @@ const uint8 TownsMidiInputChannel::_programAdjustLevel[] = {
 	0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F
 };
 
-MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(nullptr), _timerProcPara(nullptr), _channels(nullptr), _out(nullptr),
+IMuseDriver_FMTowns::IMuseDriver_FMTowns(Audio::Mixer *mixer) : _timerProc(nullptr), _timerProcPara(nullptr), _channels(nullptr), _out(nullptr),
 	_baseTempo(10080), _chanState(nullptr), _operatorLevelTable(nullptr), _tickCounter(0), _rand(1), _allocCurPos(0), _isOpen(false) {
 	_intf = new TownsAudioInterface(mixer, this, true);
 
@@ -859,7 +862,7 @@ MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(nullptr), _
 		_operatorLevelTable[i << 5] = 0;
 }
 
-MidiDriver_TOWNS::~MidiDriver_TOWNS() {
+IMuseDriver_FMTowns::~IMuseDriver_FMTowns() {
 	close();
 	delete _intf;
 
@@ -883,7 +886,7 @@ MidiDriver_TOWNS::~MidiDriver_TOWNS() {
 	_operatorLevelTable = nullptr;
 }
 
-int MidiDriver_TOWNS::open() {
+int IMuseDriver_FMTowns::open() {
 	if (_isOpen)
 		return MERR_ALREADY_OPEN;
 
@@ -906,7 +909,7 @@ int MidiDriver_TOWNS::open() {
 	return 0;
 }
 
-void MidiDriver_TOWNS::close() {
+void IMuseDriver_FMTowns::close() {
 	if (!_isOpen)
 		return;
 
@@ -916,7 +919,7 @@ void MidiDriver_TOWNS::close() {
 	g_system->delayMillis(20);
 }
 
-void MidiDriver_TOWNS::send(uint32 b) {
+void IMuseDriver_FMTowns::send(uint32 b) {
 	if (!_isOpen)
 		return;
 
@@ -954,16 +957,16 @@ void MidiDriver_TOWNS::send(uint32 b) {
 	}
 }
 
-void MidiDriver_TOWNS::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
+void IMuseDriver_FMTowns::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
 	_timerProc = timer_proc;
 	_timerProcPara = timer_param;
 }
 
-uint32 MidiDriver_TOWNS::getBaseTempo() {
+uint32 IMuseDriver_FMTowns::getBaseTempo() {
 	return _baseTempo;
 }
 
-MidiChannel *MidiDriver_TOWNS::allocateChannel() {
+MidiChannel *IMuseDriver_FMTowns::allocateChannel() {
 	if (!_isOpen)
 		return nullptr;
 
@@ -976,11 +979,11 @@ MidiChannel *MidiDriver_TOWNS::allocateChannel() {
 	return nullptr;
 }
 
-MidiChannel *MidiDriver_TOWNS::getPercussionChannel() {
+MidiChannel *IMuseDriver_FMTowns::getPercussionChannel() {
 	return nullptr;
 }
 
-void MidiDriver_TOWNS::timerCallback(int timerId) {
+void IMuseDriver_FMTowns::timerCallback(int timerId) {
 	if (!_isOpen)
 		return;
 
@@ -994,12 +997,12 @@ void MidiDriver_TOWNS::timerCallback(int timerId) {
 	}
 }
 
-void MidiDriver_TOWNS::updateParser() {
+void IMuseDriver_FMTowns::updateParser() {
 	if (_timerProc)
 		_timerProc(_timerProcPara);
 }
 
-void MidiDriver_TOWNS::updateOutputChannels() {
+void IMuseDriver_FMTowns::updateOutputChannels() {
 	_tickCounter += _baseTempo;
 	while (_tickCounter >= 16667) {
 		_tickCounter -= 16667;
@@ -1010,7 +1013,7 @@ void MidiDriver_TOWNS::updateOutputChannels() {
 	}
 }
 
-TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(uint8 pri) {
+TownsMidiOutputChannel *IMuseDriver_FMTowns::allocateOutputChannel(uint8 pri) {
 	TownsMidiOutputChannel *res = nullptr;
 
 	for (int i = 0; i < 6; i++) {
@@ -1033,7 +1036,9 @@ TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(uint8 pri) {
 	return res;
 }
 
-int MidiDriver_TOWNS::randomValue(int para) {
+int IMuseDriver_FMTowns::randomValue(int para) {
 	_rand = (_rand & 1) ? (_rand >> 1) ^ 0xb8 : (_rand >> 1);
 	return (_rand * para) >> 8;
 }
+
+} // end of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/fmtowns.h b/engines/scumm/imuse/drivers/fmtowns.h
index 5ece0182c91..aa8a8cedd02 100644
--- a/engines/scumm/imuse/drivers/fmtowns.h
+++ b/engines/scumm/imuse/drivers/fmtowns.h
@@ -25,17 +25,18 @@
 #include "audio/softsynth/fmtowns_pc98/towns_audio.h"
 #include "audio/mididrv.h"
 
+namespace Scumm {
 
 class TownsMidiOutputChannel;
 class TownsMidiInputChannel;
 class TownsMidiChanState;
 
-class MidiDriver_TOWNS : public MidiDriver, public TownsAudioInterfacePluginDriver {
+class IMuseDriver_FMTowns : public MidiDriver, public TownsAudioInterfacePluginDriver {
 friend class TownsMidiInputChannel;
 friend class TownsMidiOutputChannel;
 public:
-	MidiDriver_TOWNS(Audio::Mixer *mixer);
-	~MidiDriver_TOWNS() override;
+	IMuseDriver_FMTowns(Audio::Mixer *mixer);
+	~IMuseDriver_FMTowns() override;
 
 	int open() override;
 	bool isOpen() const override { return _isOpen; }
@@ -79,4 +80,6 @@ private:
 	const uint16 _baseTempo;
 };
 
+} // end of namespace Scumm
+
 #endif
diff --git a/engines/scumm/imuse/drivers/gmidi.cpp b/engines/scumm/imuse/drivers/gmidi.cpp
new file mode 100644
index 00000000000..eaa322cb981
--- /dev/null
+++ b/engines/scumm/imuse/drivers/gmidi.cpp
@@ -0,0 +1,195 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/system.h"
+#include "scumm/imuse/drivers/gmidi.h"
+
+namespace Scumm {
+
+IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode), _newSystem(newSystem) {
+	_drv = MidiDriver::createMidi(dev);
+	assert(_drv);
+}
+
+IMuseDriver_GMidi::~IMuseDriver_GMidi() {
+	delete _drv;
+}
+
+int IMuseDriver_GMidi::open() {
+	if (!_drv)
+		return MERR_CANNOT_CONNECT;
+
+	int res = _drv->open();
+
+	if (_gsMode)
+		initDeviceAsRolandGS();
+	else
+		initDevice();
+
+	return res;
+}
+
+MidiChannel *IMuseDriver_GMidi::allocateChannel() {
+	if (!_newSystem)
+		return _drv->allocateChannel();
+
+	return nullptr;
+}
+
+MidiChannel *IMuseDriver_GMidi::getPercussionChannel() {
+	if (!_newSystem)
+		return _drv->getPercussionChannel();
+
+	return 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::initDeviceAsRolandGS() {
+	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);
+	_drv->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);
+	_drv->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);
+	_drv->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);
+	_drv->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);
+	_drv->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)
+		_drv->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)
+		_drv->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;
+		_drv->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);
+	_drv->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);
+	_drv->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);
+	_drv->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);
+	_drv->sysEx(buffer, 9);
+	debug(2, "GS SysEx: Reverb Time is 106");
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/gmidi.h b/engines/scumm/imuse/drivers/gmidi.h
new file mode 100644
index 00000000000..311c8d9a45e
--- /dev/null
+++ b/engines/scumm/imuse/drivers/gmidi.h
@@ -0,0 +1,60 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_IMUSE_DRV_GMIDI_H
+#define SCUMM_IMUSE_DRV_GMIDI_H
+
+#include "audio/mididrv.h"
+
+namespace Scumm {
+
+class IMuseDriver_GMidi : public MidiDriver {
+public:
+	IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem);
+	~IMuseDriver_GMidi() override;
+
+	int open() override;
+
+	// Just pass these through...
+	bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
+	void close() override { if (_drv) _drv->close(); }
+	uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
+	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
+	uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
+	void send(uint32 b) override { if (_drv) _drv->send(b); };
+	void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } 
+
+	// Channel allocation functions
+	MidiChannel *allocateChannel() override;
+	MidiChannel *getPercussionChannel() override;
+
+private:
+	void initDevice();
+	void initDeviceAsRolandGS();
+
+	MidiDriver *_drv;
+	const bool _gsMode;
+	const bool _newSystem;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse/drivers/mac_m68k.cpp b/engines/scumm/imuse/drivers/mac_m68k.cpp
index 33e5993ff56..600c07032ce 100644
--- a/engines/scumm/imuse/drivers/mac_m68k.cpp
+++ b/engines/scumm/imuse/drivers/mac_m68k.cpp
@@ -27,14 +27,19 @@
 
 namespace Scumm {
 
-MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer)
-	: MidiDriver_Emulated(mixer) {
+IMuseDriver_MacM68k::IMuseDriver_MacM68k(Audio::Mixer *mixer)
+	: MidiDriver_Emulated(mixer), _mixBuffer(nullptr), _mixBufferLength(0), _volumeTable(nullptr), _lastUsedVoiceChannel(0) {
+	memset(_channels, 0, sizeof(_channels));
+	memset(_pitchTable, 0, sizeof(_pitchTable));
+	memset(_voiceChannels, 0, sizeof(_voiceChannels));
 }
 
-MacM68kDriver::~MacM68kDriver() {
+IMuseDriver_MacM68k::~IMuseDriver_MacM68k() {
+	for (uint i = 0; i < ARRAYSIZE(_channels); ++i)
+		delete _channels[i];
 }
 
-int MacM68kDriver::open() {
+int IMuseDriver_MacM68k::open() {
 	if (_isOpen) {
 		return MERR_ALREADY_OPEN;
 	}
@@ -45,7 +50,8 @@ int MacM68kDriver::open() {
 	}
 
 	for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
-		_channels[i].init(this, i);
+		delete _channels[i];
+		_channels[i] = new MidiChannel_MacM68k(this, i);
 	}
 
 	memset(_voiceChannels, 0, sizeof(_voiceChannels));
@@ -88,7 +94,7 @@ int MacM68kDriver::open() {
 	return 0;
 }
 
-void MacM68kDriver::close() {
+void IMuseDriver_MacM68k::close() {
 	if (!_isOpen) {
 		return;
 	}
@@ -106,25 +112,21 @@ void MacM68kDriver::close() {
 	_mixBufferLength = 0;
 }
 
-void MacM68kDriver::send(uint32 d) {
-	assert(false);
-}
-
-void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
+void IMuseDriver_MacM68k::send(uint32 d) {
 	assert(false);
 }
 
-MidiChannel *MacM68kDriver::allocateChannel() {
+MidiChannel *IMuseDriver_MacM68k::allocateChannel() {
 	for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
-		if (_channels[i].allocate()) {
-			return &_channels[i];
+		if (_channels[i]->allocate()) {
+			return _channels[i];
 		}
 	}
 
 	return nullptr;
 }
 
-MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const {
+IMuseDriver_MacM68k::Instrument IMuseDriver_MacM68k::getInstrument(int idx) const {
 	InstrumentMap::const_iterator i = _instruments.find(idx);
 	if (i != _instruments.end()) {
 		return i->_value;
@@ -133,7 +135,7 @@ MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const {
 	}
 }
 
-void MacM68kDriver::generateSamples(int16 *buf, int len) {
+void IMuseDriver_MacM68k::generateSamples(int16 *buf, int len) {
 	int silentChannels = 0;
 
 	if (_mixBufferLength < len) {
@@ -191,7 +193,7 @@ void MacM68kDriver::generateSamples(int16 *buf, int len) {
 	}
 }
 
-void MacM68kDriver::loadAllInstruments() {
+void IMuseDriver_MacM68k::loadAllInstruments() {
 	Common::MacResManager resource;
 	if (resource.open("iMUSE Setups")) {
 		if (!resource.hasResFork()) {
@@ -225,7 +227,7 @@ void MacM68kDriver::loadAllInstruments() {
 	}
 }
 
-void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) {
+void IMuseDriver_MacM68k::addInstrument(int idx, Common::SeekableReadStream *data) {
 	// We parse the "SND" files manually here, since we need special data
 	// from their header and need to work on them raw while mixing.
 	data->skip(2);
@@ -251,7 +253,7 @@ void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) {
 	_instruments[idx] = inst;
 }
 
-void MacM68kDriver::setPitch(OutputChannel *out, int frequency) {
+void IMuseDriver_MacM68k::setPitch(OutputChannel *out, int frequency) {
 	out->frequency = frequency;
 	out->isFinished = false;
 
@@ -266,7 +268,7 @@ void MacM68kDriver::setPitch(OutputChannel *out, int frequency) {
 	}
 }
 
-void MacM68kDriver::VoiceChannel::off() {
+void IMuseDriver_MacM68k::VoiceChannel::off() {
 	if (out.start) {
 		out.isFinished = true;
 	}
@@ -275,14 +277,14 @@ void MacM68kDriver::VoiceChannel::off() {
 	part = nullptr;
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::release() {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::release() {
 	_allocated = false;
 	while (_voice) {
 		_voice->off();
 	}
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::send(uint32 b) {
 	uint8 type = b & 0xF0;
 	uint8 p1 = (b >> 8) & 0xFF;
 	uint8 p2 = (b >> 16) & 0xFF;
@@ -313,7 +315,7 @@ void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) {
 	}
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::noteOff(byte note) {
 	for (VoiceChannel *i = _voice; i; i = i->next) {
 		if (i->note == note) {
 			if (_sustain) {
@@ -325,7 +327,7 @@ void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) {
 	}
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
 	// Do not start a not unless there is an instrument set up
 	if (!_instrument.data) {
 		return;
@@ -366,18 +368,18 @@ void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
 	voice->out.subPos = 0;
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::programChange(byte program) {
 	_instrument = _owner->getInstrument(program + kProgramChangeBase);
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::pitchBend(int16 bend) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBend(int16 bend) {
 	_pitchBend = (bend * _pitchBendFactor) >> 6;
 	for (VoiceChannel *i = _voice; i; i = i->next) {
 		_owner->setPitch(&i->out, (i->note << 7) + _pitchBend);
 	}
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::controlChange(byte control, byte value) {
 	switch (control) {
 	// volume change
 	case 7:
@@ -412,28 +414,22 @@ void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value)
 	}
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBendFactor(byte value) {
 	_pitchBendFactor = value;
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::priority(byte value) {
 	_priority = value;
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) {
 	assert(instr);
 	if (type == 'MAC ') {
 		_instrument = _owner->getInstrument(*instr + kSysExBase);
 	}
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::init(MacM68kDriver *owner, byte channel) {
-	_owner = owner;
-	_number = channel;
-	_allocated = false;
-}
-
-bool MacM68kDriver::MidiChannel_MacM68k::allocate() {
+bool IMuseDriver_MacM68k::MidiChannel_MacM68k::allocate() {
 	if (_allocated) {
 		return false;
 	}
@@ -448,7 +444,7 @@ bool MacM68kDriver::MidiChannel_MacM68k::allocate() {
 	return true;
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
 	voice->next = _voice;
 	voice->prev = nullptr;
 	voice->part = this;
@@ -458,7 +454,7 @@ void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
 	_voice = voice;
 }
 
-void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
 	VoiceChannel *i = _voice;
 	while (i && i != voice) {
 		i = i->next;
@@ -477,7 +473,7 @@ void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
 	}
 }
 
-MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) {
+IMuseDriver_MacM68k::VoiceChannel *IMuseDriver_MacM68k::allocateVoice(int priority) {
 	VoiceChannel *channel = nullptr;
 	for (int i = 0; i < kChannelCount; ++i) {
 		if (++_lastUsedVoiceChannel == kChannelCount) {
@@ -504,7 +500,7 @@ MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) {
 	return channel;
 }
 
-const int MacM68kDriver::_volumeBaseTable[32] = {
+const int IMuseDriver_MacM68k::_volumeBaseTable[32] = {
 	  0,   0,   1,   1,   2,   3,   5,   6,
 	  8,  11,  13,  16,  19,  22,  26,  30,
 	 34,  38,  43,  48,  53,  58,  64,  70,
diff --git a/engines/scumm/imuse/drivers/mac_m68k.h b/engines/scumm/imuse/drivers/mac_m68k.h
index d04e769da5f..cf51c5d90d6 100644
--- a/engines/scumm/imuse/drivers/mac_m68k.h
+++ b/engines/scumm/imuse/drivers/mac_m68k.h
@@ -32,20 +32,19 @@ class SeekableReadStream;
 
 namespace Scumm {
 
-class MacM68kDriver : public MidiDriver_Emulated {
+class IMuseDriver_MacM68k : public MidiDriver_Emulated {
 	friend class MidiChannel_MacM68k;
 public:
-	MacM68kDriver(Audio::Mixer *mixer);
-	~MacM68kDriver() override;
+	IMuseDriver_MacM68k(Audio::Mixer *mixer);
+	~IMuseDriver_MacM68k() override;
 
 	int open() override;
 	void close() override;
 
 	void send(uint32 d) override;
-	void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override;
 
 	MidiChannel *allocateChannel() override;
-	MidiChannel *getPercussionChannel() override { return 0; }
+	MidiChannel *getPercussionChannel() override { return nullptr; }
 
 	bool isStereo() const override { return false; }
 	int getRate() const override {
@@ -127,8 +126,9 @@ private:
 	};
 
 	class MidiChannel_MacM68k : public MidiChannel {
-		friend class MacM68kDriver;
+		friend class IMuseDriver_MacM68k;
 	public:
+		MidiChannel_MacM68k(IMuseDriver_MacM68k *driver, byte number) : MidiChannel(), _owner(driver), _number(number), _allocated(false) {}
 		MidiDriver *device() override { return _owner; }
 		byte getNumber() override { return _number; }
 		void release() override;
@@ -143,15 +143,14 @@ private:
 		void priority(byte value) override;
 		void sysEx_customInstrument(uint32 type, const byte *instr) override;
 
-		void init(MacM68kDriver *owner, byte channel);
 		bool allocate();
 
 		void addVoice(VoiceChannel *voice);
 		void removeVoice(VoiceChannel *voice);
 	private:
-		MacM68kDriver *_owner;
+		IMuseDriver_MacM68k *_owner;
+		const byte _number;
 		bool _allocated;
-		int _number;
 
 		VoiceChannel *_voice;
 		int _priority;
@@ -162,7 +161,7 @@ private:
 		int _volume;
 	};
 
-	MidiChannel_MacM68k _channels[32];
+	MidiChannel_MacM68k *_channels[32];
 
 	enum {
 		kChannelCount = 8
diff --git a/engines/scumm/imuse/drivers/mt32.cpp b/engines/scumm/imuse/drivers/mt32.cpp
new file mode 100644
index 00000000000..e65001834ac
--- /dev/null
+++ b/engines/scumm/imuse/drivers/mt32.cpp
@@ -0,0 +1,103 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/str.h"
+#include "common/system.h"
+#include "base/version.h"
+#include "scumm/imuse/drivers/mt32.h"
+
+namespace Scumm {
+
+IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _drv(nullptr) {
+	_drv = MidiDriver::createMidi(dev);
+	assert(_drv);
+
+	if (!_newSystem)
+		_drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+}
+
+IMuseDriver_MT32::~IMuseDriver_MT32() {
+	delete _drv;
+}
+
+int IMuseDriver_MT32::open() {
+	if (!_drv)
+		return MERR_CANNOT_CONNECT;
+
+	int res = _drv->open();
+
+	initDevice();
+
+	return res;
+}
+
+MidiChannel *IMuseDriver_MT32::allocateChannel() {
+	if (!_newSystem)
+		return _drv->allocateChannel();
+
+	return nullptr;
+}
+
+MidiChannel *IMuseDriver_MT32::getPercussionChannel() {
+	if (!_newSystem)
+		return _drv->getPercussionChannel();
+
+	return nullptr;
+}
+
+void IMuseDriver_MT32::initDevice() {
+	byte buffer[52];
+
+	// Reset the MT-32
+	_drv->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
+	g_system->delayMillis(250);
+
+	// Setup master tune, reverb mode, reverb time, reverb level,
+	// channel mapping, partial reserve and master volume
+	_drv->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31);
+	g_system->delayMillis(250);
+
+	// Map percussion to notes 24 - 34 without reverb
+	if (!_newSystem) {
+		_drv->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\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\x44", 52);
+		g_system->delayMillis(250);
+	}
+
+	// Compute version string (truncated to 20 chars max.)
+	Common::String infoStr = "ScummVM ";
+	infoStr += gScummVMVersion;
+	int len = infoStr.size();
+	if (len > 20)
+		len = 20;
+
+	// Display a welcome message on MT-32 displays.
+	memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
+	memcpy(&buffer[7], "                    ", 20);
+	memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len);
+	byte checksum = 0;
+	for (int i = 4; i < 27; ++i)
+		checksum -= buffer[i];
+	buffer[27] = checksum & 0x7F;
+	_drv->sysEx(buffer, 28);
+	g_system->delayMillis(1000);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/mt32.h b/engines/scumm/imuse/drivers/mt32.h
new file mode 100644
index 00000000000..9f6cf7b2687
--- /dev/null
+++ b/engines/scumm/imuse/drivers/mt32.h
@@ -0,0 +1,59 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_IMUSE_DRV_MT32_H
+#define SCUMM_IMUSE_DRV_MT32_H
+
+#include "audio/mididrv.h"
+
+namespace Scumm {
+
+class IMuseDriver_MT32 : public MidiDriver {
+public:
+	IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem);
+	~IMuseDriver_MT32() override;
+
+	int open() override;
+
+	// Just pass these through...
+	bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
+	void close() override { if (_drv) _drv->close(); }
+	uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
+	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
+	uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
+	void send(uint32 b) override { if (_drv) _drv->send(b); };
+	void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } 
+	void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
+
+	// Channel allocation functions
+	MidiChannel *allocateChannel() override;
+	MidiChannel *getPercussionChannel() override;
+
+private:
+	void initDevice();
+
+	MidiDriver *_drv;
+	const bool _newSystem;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse/drivers/pcspk.cpp b/engines/scumm/imuse/drivers/pcspk.cpp
index 4ccfa8e0c3f..546d674321a 100644
--- a/engines/scumm/imuse/drivers/pcspk.cpp
+++ b/engines/scumm/imuse/drivers/pcspk.cpp
@@ -25,22 +25,26 @@
 
 namespace Scumm {
 
-PcSpkDriver::PcSpkDriver(Audio::Mixer *mixer)
-	: MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) {
+IMuseDriver_PCSpk::IMuseDriver_PCSpk(Audio::Mixer *mixer)
+	: MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) , _activeChannel(nullptr), _lastActiveChannel(nullptr), _lastActiveOut(0), _effectTimer(0), _randBase(1) {
+	memset(_channels, 0, sizeof(_channels));
 }
 
-PcSpkDriver::~PcSpkDriver() {
+IMuseDriver_PCSpk::~IMuseDriver_PCSpk() {
 	close();
 }
 
-int PcSpkDriver::open() {
+int IMuseDriver_PCSpk::open() {
 	if (_isOpen)
 		return MERR_ALREADY_OPEN;
 
 	MidiDriver_Emulated::open();
 
-	for (uint i = 0; i < 6; ++i)
-		_channels[i].init(this, i);
+	for (uint i = 0; i < 6; ++i) {
+		delete _channels[i];
+		_channels[i] = new MidiChannel_PcSpk(this, i);
+	}
+
 	_activeChannel = nullptr;
 	_effectTimer = 0;
 	_randBase = 1;
@@ -59,44 +63,41 @@ int PcSpkDriver::open() {
 	return 0;
 }
 
-void PcSpkDriver::close() {
+void IMuseDriver_PCSpk::close() {
 	if (!_isOpen)
 		return;
 	_isOpen = false;
 
 	_mixer->stopHandle(_mixerSoundHandle);
-}
 
-void PcSpkDriver::send(uint32 d) {
-	assert((d & 0x0F) < 6);
-	_channels[(d & 0x0F)].send(d);
+	for (uint i = 0; i < 6; ++i)
+		delete _channels[i];
 }
 
-void PcSpkDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
-	assert(channel < 6);
-	if (type == 'SPK ')
-		_channels[channel].sysEx_customInstrument(type, instr);
+void IMuseDriver_PCSpk::send(uint32 d) {
+	assert((d & 0x0F) < 6);
+	_channels[(d & 0x0F)]->send(d);
 }
 
-MidiChannel *PcSpkDriver::allocateChannel() {
+MidiChannel *IMuseDriver_PCSpk::allocateChannel() {
 	for (uint i = 0; i < 6; ++i) {
-		if (_channels[i].allocate())
-			return &_channels[i];
+		if (_channels[i]->allocate())
+			return _channels[i];
 	}
 
 	return nullptr;
 }
 
-void PcSpkDriver::generateSamples(int16 *buf, int len) {
+void IMuseDriver_PCSpk::generateSamples(int16 *buf, int len) {
 	_pcSpk.readBuffer(buf, len);
 }
 
-void PcSpkDriver::onTimer() {
+void IMuseDriver_PCSpk::onTimer() {
 	if (!_activeChannel)
 		return;
 
 	for (uint i = 0; i < 6; ++i) {
-		OutputChannel &out = _channels[i]._out;
+		OutputChannel &out = _channels[i]->_out;
 
 		if (!out.active)
 			continue;
@@ -113,9 +114,9 @@ void PcSpkDriver::onTimer() {
 				_effectTimer = 0;
 
 				if (out.effectEnvelopeA.state)
-					updateEffectGenerator(_channels[i], out.effectEnvelopeA, out.effectDefA);
+					updateEffectGenerator(*_channels[i], out.effectEnvelopeA, out.effectDefA);
 				if (out.effectEnvelopeB.state)
-					updateEffectGenerator(_channels[i], out.effectEnvelopeB, out.effectDefB);
+					updateEffectGenerator(*_channels[i], out.effectEnvelopeB, out.effectDefB);
 			}
 		} else {
 			out.active = 0;
@@ -133,13 +134,13 @@ void PcSpkDriver::onTimer() {
 	}
 }
 
-void PcSpkDriver::updateNote() {
+void IMuseDriver_PCSpk::updateNote() {
 	uint8 priority = 0;
 	_activeChannel = nullptr;
 	for (uint i = 0; i < 6; ++i) {
-		if (_channels[i]._allocated && _channels[i]._out.active && _channels[i]._priority >= priority) {
-			priority = _channels[i]._priority;
-			_activeChannel = &_channels[i];
+		if (_channels[i]->_allocated && _channels[i]->_out.active && _channels[i]->_priority >= priority) {
+			priority = _channels[i]->_priority;
+			_activeChannel = _channels[i];
 		}
 	}
 
@@ -152,7 +153,7 @@ void PcSpkDriver::updateNote() {
 	}
 }
 
-void PcSpkDriver::output(uint16 out) {
+void IMuseDriver_PCSpk::output(uint16 out) {
 	byte v1 = (out >> 7) & 0xFF;
 	byte v2 = (out >> 2) & 0x1E;
 
@@ -170,14 +171,11 @@ void PcSpkDriver::output(uint16 out) {
 	}
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::init(PcSpkDriver *owner, byte channel) {
-	_owner = owner;
-	_channel = channel;
-	_allocated = false;
+IMuseDriver_PCSpk::MidiChannel_PcSpk::MidiChannel_PcSpk(IMuseDriver_PCSpk *owner, byte number) : MidiChannel(), _owner(owner), _number(number), _allocated(false) {
 	memset(&_out, 0, sizeof(_out));
 }
 
-bool PcSpkDriver::MidiChannel_PcSpk::allocate() {
+bool IMuseDriver_PCSpk::MidiChannel_PcSpk::allocate() {
 	if (_allocated)
 		return false;
 
@@ -190,21 +188,13 @@ bool PcSpkDriver::MidiChannel_PcSpk::allocate() {
 	return true;
 }
 
-MidiDriver *PcSpkDriver::MidiChannel_PcSpk::device() {
-	return _owner;
-}
-
-byte PcSpkDriver::MidiChannel_PcSpk::getNumber() {
-	return _channel;
-}
-
-void PcSpkDriver::MidiChannel_PcSpk::release() {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::release() {
 	_out.active = 0;
 	_allocated = false;
 	_owner->updateNote();
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::send(uint32 b) {
 	uint8 type = b & 0xF0;
 	uint8 p1 = (b >> 8) & 0xFF;
 	uint8 p2 = (b >> 16) & 0xFF;
@@ -234,7 +224,7 @@ void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) {
 	}
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::noteOff(byte note) {
 	if (!_allocated)
 		return;
 
@@ -249,7 +239,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) {
 	}
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
 	if (!_allocated)
 		return;
 
@@ -257,7 +247,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
 	_out.sustainNoteOff = 0;
 	_out.length = _instrument[0];
 
-	if (_instrument[4] * 256 < ARRAYSIZE(PcSpkDriver::_outInstrumentData))
+	if (_instrument[4] * 256 < ARRAYSIZE(IMuseDriver_PCSpk::_outInstrumentData))
 		_out.instrument = _owner->_outInstrumentData + _instrument[4] * 256;
 	else
 		_out.instrument = nullptr;
@@ -278,7 +268,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
 	}
 	_owner->updateNote();
 
-	_out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4));
+	_out.unkC += IMuseDriver_PCSpk::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4));
 	if (_out.unkC > 63)
 		_out.unkC = 63;
 
@@ -289,16 +279,16 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
 		_owner->setupEffects(*this, _out.effectEnvelopeB, _out.effectDefB, _instrument[14], _instrument + 15);
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::programChange(byte program) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::programChange(byte program) {
 	// Nothing to implement here, the iMuse code takes care of passing us the
 	// instrument data.
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::pitchBend(int16 bend) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::pitchBend(int16 bend) {
 	_pitchBend = (bend * _pitchBendFactor) >> 6;
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::controlChange(byte control, byte value) {
 	switch (control) {
 	case 1:
 		if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel)
@@ -338,19 +328,19 @@ void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) {
 	}
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::pitchBendFactor(byte value) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::pitchBendFactor(byte value) {
 	_pitchBendFactor = value;
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::priority(byte value) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::priority(byte value) {
 	_priority = value;
 }
 
-void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) {
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) {
 	memcpy(_instrument, instr, sizeof(_instrument));
 }
 
-uint8 PcSpkDriver::getEffectModifier(uint16 level) {
+uint8 IMuseDriver_PCSpk::getEffectModifier(uint16 level) {
 	uint8 base = level / 32;
 	uint8 index = level % 32;
 
@@ -360,7 +350,7 @@ uint8 PcSpkDriver::getEffectModifier(uint16 level) {
 	return (base * (index + 1)) >> 5;
 }
 
-int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) {
+int16 IMuseDriver_PCSpk::getEffectModLevel(int16 level, int8 mod) {
 	if (!mod) {
 		return 0;
 	} else if (mod == 31) {
@@ -380,7 +370,7 @@ int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) {
 	}
 }
 
-int16 PcSpkDriver::getRandScale(int16 input) {
+int16 IMuseDriver_PCSpk::getRandScale(int16 input) {
 	if (_randBase & 1)
 		_randBase = (_randBase >> 1) ^ 0xB8;
 	else
@@ -389,7 +379,7 @@ int16 PcSpkDriver::getRandScale(int16 input) {
 	return (_randBase * input) >> 8;
 }
 
-void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) {
+void IMuseDriver_PCSpk::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) {
 	def.phase = 0;
 	def.useModWheel = flags & 0x40;
 	env.loop = flags & 0x20;
@@ -446,7 +436,7 @@ void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, Eff
 	startEffect(env, data);
 }
 
-void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) {
+void IMuseDriver_PCSpk::startEffect(EffectEnvelope &env, const byte *data) {
 	env.state = 1;
 	env.currentLevel = 0;
 	env.modWheelLast = 31;
@@ -465,7 +455,7 @@ void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) {
 	initNextEnvelopeState(env);
 }
 
-void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) {
+void IMuseDriver_PCSpk::initNextEnvelopeState(EffectEnvelope &env) {
 	uint8 lastState = env.state - 1;
 
 	uint16 stepCount = _effectEnvStepTable[getEffectModifier(((env.stateTargetLevels[lastState] & 0x7F) << 5) + env.modWheelSensitivity)];
@@ -501,7 +491,7 @@ void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) {
 	env.changeCountRem = 0;
 }
 
-void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) {
+void IMuseDriver_PCSpk::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) {
 	if (advanceEffectEnvelope(env, def) & 1) {
 		switch (def.type) {
 		case 0: case 1:
@@ -537,7 +527,7 @@ void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope
 	}
 }
 
-uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) {
+uint8 IMuseDriver_PCSpk::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) {
 	if (env.duration != 0) {
 		env.duration -= 17;
 		if (env.duration <= 0) {
@@ -584,7 +574,7 @@ uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &
 	return changedFlags;
 }
 
-const byte PcSpkDriver::_outInstrumentData[1024] = {
+const byte IMuseDriver_PCSpk::_outInstrumentData[1024] = {
 	0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15,
 	0x18, 0x1B, 0x1E, 0x21, 0x24, 0x27, 0x2A, 0x2D,
 	0x30, 0x33, 0x36, 0x39, 0x3B, 0x3E, 0x41, 0x43,
@@ -715,7 +705,7 @@ const byte PcSpkDriver::_outInstrumentData[1024] = {
 	0xD4, 0xF7, 0x4A, 0x4A, 0xD0, 0x57, 0x68, 0x76
 };
 
-const byte PcSpkDriver::_outputTable1[] = {
+const byte IMuseDriver_PCSpk::_outputTable1[] = {
 	0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0,
@@ -734,7 +724,7 @@ const byte PcSpkDriver::_outputTable1[] = {
 	7, 7, 7, 7, 7, 7, 7, 7
 };
 
-const byte PcSpkDriver::_outputTable2[] = {
+const byte IMuseDriver_PCSpk::_outputTable2[] = {
 	0,  1,  2,  3,
 	4,  5,  6,  7,
 	8,  9, 10, 11,
@@ -769,7 +759,7 @@ const byte PcSpkDriver::_outputTable2[] = {
 	4,  5,  6,  7
 };
 
-const uint16 PcSpkDriver::_effectEnvStepTable[] = {
+const uint16 IMuseDriver_PCSpk::_effectEnvStepTable[] = {
 	  1,    2,    4,    5,
 	  6,    7,    8,    9,
 	 10,   12,   14,   16,
@@ -780,7 +770,7 @@ const uint16 PcSpkDriver::_effectEnvStepTable[] = {
 	600,  860, 1200, 1600
 };
 
-const uint16 PcSpkDriver::_frequencyTable[] = {
+const uint16 IMuseDriver_PCSpk::_frequencyTable[] = {
 	0x8E84, 0x8E00, 0x8D7D, 0x8CFA,
 	0x8C78, 0x8BF7, 0x8B76, 0x8AF5,
 	0x8A75, 0x89F5, 0x8976, 0x88F7,
diff --git a/engines/scumm/imuse/drivers/pcspk.h b/engines/scumm/imuse/drivers/pcspk.h
index 8d4c23521d6..bad9f1269ff 100644
--- a/engines/scumm/imuse/drivers/pcspk.h
+++ b/engines/scumm/imuse/drivers/pcspk.h
@@ -27,19 +27,18 @@
 
 namespace Scumm {
 
-class PcSpkDriver : public MidiDriver_Emulated {
+class IMuseDriver_PCSpk : public MidiDriver_Emulated {
 public:
-	PcSpkDriver(Audio::Mixer *mixer);
-	~PcSpkDriver() override;
+	IMuseDriver_PCSpk(Audio::Mixer *mixer);
+	~IMuseDriver_PCSpk() override;
 
 	int open() override;
 	void close() override;
 
 	void send(uint32 d) override;
-	void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override;
 
 	MidiChannel *allocateChannel() override;
-	MidiChannel *getPercussionChannel() override { return 0; }
+	MidiChannel *getPercussionChannel() override { return nullptr; }
 
 	bool isStereo() const override { return _pcSpk.isStereo(); }
 	int getRate() const override { return _pcSpk.getRate(); }
@@ -103,9 +102,11 @@ private:
 		int16 unk60;
 	};
 
-	struct MidiChannel_PcSpk : public MidiChannel {
-		MidiDriver *device() override;
-		byte getNumber() override;
+	class MidiChannel_PcSpk: public MidiChannel {
+	public:
+		MidiChannel_PcSpk(IMuseDriver_PCSpk *owner, byte number);
+		MidiDriver *device() override { return _owner; }
+		byte getNumber() override { return _number; }
 		void release() override;
 
 		void send(uint32 b) override;
@@ -118,22 +119,23 @@ private:
 		void priority(byte value) override;
 		void sysEx_customInstrument(uint32 type, const byte *instr) override;
 
-		void init(PcSpkDriver *owner, byte channel);
 		bool allocate();
 
-		PcSpkDriver *_owner;
 		bool _allocated;
-		byte _channel;
-
 		OutputChannel _out;
 		uint8 _instrument[23];
-		uint8 _programNr;
 		uint8 _priority;
 		uint8 _tl;
 		uint8 _modWheel;
+		int16 _pitchBend;
+
+	private:
+		IMuseDriver_PCSpk *_owner;
+		const byte _number;
+		uint8 _programNr;
 		uint8 _sustain;
 		uint8 _pitchBendFactor;
-		int16 _pitchBend;
+		
 	};
 
 	void setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data);
@@ -142,7 +144,7 @@ private:
 	void updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def);
 	uint8 advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def);
 
-	MidiChannel_PcSpk _channels[6];
+	MidiChannel_PcSpk *_channels[6];
 	MidiChannel_PcSpk *_activeChannel;
 
 	MidiChannel_PcSpk *_lastActiveChannel;
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 93699c49311..8b071e2f838 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -1437,17 +1437,6 @@ int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver
 	init_queue();
 	init_parts();
 
-	if (_midi_native && _soundType != MDT_MACINTOSH && _soundType != MDT_AMIGA) {
-		if (_native_mt32 && !_enable_gs) {
-			initMT32(_midi_native);
-		} else if (_enable_gs) {
-			initGS(_midi_native);
-		} else if (!_native_mt32) {
-			// If GS is disabled we do the "normal" init from the original GM drivers.
-			initGM();
-		}
-	}
-
 	_initialized = true;
 
 	return 0;
@@ -1463,174 +1452,6 @@ void IMuseInternal::initMidiDriver(TimerCallbackInfo *info) {
 	info->driver->setTimerCallback(info, &IMuseInternal::midiTimerCallback);
 }
 
-void IMuseInternal::initMT32(MidiDriver *midi) {
-	byte buffer[52];
-
-	// Reset the MT-32
-	midi->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
-	_system->delayMillis(250);
-
-	// Setup master tune, reverb mode, reverb time, reverb level,
-	// channel mapping, partial reserve and master volume
-	midi->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31);
-	_system->delayMillis(250);
-
-	// Map percussion to notes 24 - 34 without reverb
-	midi->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\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\x44", 52);
-	_system->delayMillis(250);
-
-	// Compute version string (truncated to 20 chars max.)
-	Common::String infoStr = "ScummVM ";
-	infoStr += gScummVMVersion;
-	int len = infoStr.size();
-	if (len > 20)
-		len = 20;
-
-	// Display a welcome message on MT-32 displays.
-	memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
-	memcpy(&buffer[7], "                    ", 20);
-	memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len);
-	byte checksum = 0;
-	for (int i = 4; i < 27; ++i)
-		checksum -= buffer[i];
-	buffer[27] = checksum & 0x7F;
-	midi->sysEx(buffer, 28);
-	_system->delayMillis(1000);
-}
-
-void IMuseInternal::initGS(MidiDriver *midi) {
-	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);
-	midi->sysEx(buffer, 4);
-	debug(2, "GM SysEx: GM System On");
-	_system->delayMillis(200);
-
-	if (_enable_gs) {
-		// 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);
-		midi->sysEx(buffer, 9);
-		debug(2, "GS SysEx: GS Reset");
-		_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);
-		midi->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) {
-			midi->send((127 << 16) | (0  << 8) | (0xB0 | i));
-			midi->send((1   << 16) | (32 << 8) | (0xB0 | i));
-			midi->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)
-		midi->getPercussionChannel()->controlChange(0, 0);
-		midi->getPercussionChannel()->controlChange(32, 1);
-		midi->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);
-		midi->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)
-			midi->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) {
-			midi->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;
-			midi->sysEx(buffer, 9);
-		}
-		debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
-
-		// Set Percussion Channel Expression to 80
-		midi->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);
-		midi->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);
-		midi->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);
-		midi->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);
-		midi->sysEx(buffer, 9);
-		debug(2, "GS SysEx: Reverb Time is 106");
-	}
-}
-
-void IMuseInternal::initGM() {
-	// 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
-	// value for the pitch bend range.
-	MidiDriver *m = _midi_native;
-	for (int i = 0; i < 16; ++i) {
-		m->send(0x0064B0 | i);
-		m->send(0x0065B0 | i);
-		m->send(0x1006B0 | i);
-		m->send(0x7F07B0 | i);
-		m->send(0x3F0AB0 | i);
-		m->send(0x0000C0 | i);
-		m->send(0x4000E0 | i);
-		m->send(0x0001B0 | i);
-		m->send(0x0040B0 | i);
-		m->send(0x405BB0 | i);
-		m->send(0x005DB0 | i);
-		m->send(0x0000B0 | i);
-		m->send(0x007BB0 | i);
-	}
-}
-
 void IMuseInternal::init_queue() {
 	_queue_adding = false;
 	_queue_pos = 0;
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index c7d883b83c3..7539eb55423 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -367,6 +367,7 @@ struct Part : public Common::Serializable {
 	void set_pri(int8 pri);
 	void set_pan(int8 pan);
 
+	void set_sm17(int8 val);
 	void set_onoff(bool on);
 	void fix_after_load();
 
@@ -481,9 +482,6 @@ protected:
 	void handle_marker(uint id, byte data);
 	int get_channel_volume(uint a);
 	void initMidiDriver(TimerCallbackInfo *info);
-	void initGS(MidiDriver *midi);
-	void initMT32(MidiDriver *midi);
-	void initGM();
 	void init_players();
 	void init_parts();
 	void init_queue();
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 46e69f1c492..28f3e345e80 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -24,8 +24,8 @@
 #include "common/debug.h"
 #include "common/textconsole.h"
 #include "common/util.h"
-#include "scumm/imuse/imuse_internal.h"
 #include "scumm/scumm.h"
+#include "scumm/imuse/imuse_internal.h"
 
 namespace Scumm {
 
@@ -146,6 +146,10 @@ void Part::set_pan(int8 pan) {
 	sendPanPosition(_pan_eff + 0x40);
 }
 
+void Part::set_sm17(int8 val) {
+
+}
+
 void Part::set_transpose(int8 transpose, int8 clipRangeLow, int8 clipRangeHi)  {
 	if (_se->_game_id == GID_TENTACLE && (transpose > 24 || transpose < -24))
 		return;
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 73a4c75dbbf..4837f960c76 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -223,7 +223,7 @@ int Player::start_seq_sound(int sound, bool reset_vars) {
 }
 
 void Player::loadStartParameters(int sound) {
-	_priority = 0x80;
+	_priority = (_se->_game_id != GID_SAMNMAX) ? 0x80 : 0;
 	_volume = 0x7F;
 	_vol_chan = 0xFFFF;
 	_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
@@ -319,10 +319,15 @@ void Player::send(uint32 b) {
 			part->pitchBendFactor(param2);
 			break;
 		case 17: // GP Slider 2
-			part->set_detune(param2 - 0x40);
+			if (_se->_game_id == GID_SAMNMAX)
+				part->set_sm17(param2);
+			else
+				part->set_detune(param2 - 0x40);
 			break;
 		case 18: // GP Slider 3
-			part->set_pri(param2 - 0x40);
+			if (_se->_game_id != GID_SAMNMAX)
+				param2 -= 0x40;
+			part->set_pri(param2);
 			_se->reallocateMidiChannels(_midi);
 			break;
 		case 64: // Sustain Pedal
@@ -401,9 +406,6 @@ void Player::sysEx(const byte *p, uint16 len) {
 						part->_instrument.send(part->_mc);
 				}
 			}
-		} else if (a == YM2612_SYSEX_ID) {
-			// FM-TOWNS custom instrument definition
-			_midi->sysEx_customInstrument(p[0], 'EUP ', p + 1);
 		} else {
 			// SysEx manufacturer 0x97 has been spotted in the
 			// Monkey Island 2 AdLib music, so don't make this a
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index d8686d2c672..49f12e3e241 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -33,7 +33,9 @@ MODULE_OBJS := \
 	imuse/sysex_scumm.o \
 	imuse/drivers/amiga.o \
 	imuse/drivers/fmtowns.o \
+	imuse/drivers/gmidi.o \
 	imuse/drivers/mac_m68k.o \
+	imuse/drivers/mt32.o \
 	imuse/drivers/pcspk.o \
 	input.o \
 	ks_check.o \
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 2da35837065..3ddf5b5a295 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -81,6 +81,8 @@
 #include "scumm/imuse/drivers/mac_m68k.h"
 #include "scumm/imuse/drivers/amiga.h"
 #include "scumm/imuse/drivers/fmtowns.h"
+#include "scumm/imuse/drivers/gmidi.h"
+#include "scumm/imuse/drivers/mt32.h"
 #include "scumm/detection_steam.h"
 
 #include "backends/audiocd/audiocd.h"
@@ -2044,10 +2046,16 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
 		bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
 		bool useOnlyNative = false;
 
+		uint32 imsFlags = 0;
+		if (_native_mt32)
+			imsFlags |= IMuse::kFlagNativeMT32;
+		if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32)
+			imsFlags |= IMuse::kFlagRolandGS;
+
 		if (isMacM68kIMuse()) {
 			// We setup this driver as native MIDI driver to avoid playback
 			// of the Mac music via a selected MIDI device.
-			nativeMidiDriver = new MacM68kDriver(_mixer);
+			nativeMidiDriver = new IMuseDriver_MacM68k(_mixer);
 			// The Mac driver is never MT-32.
 			_native_mt32 = false;
 			// Ignore non-native drivers. This also ignores the multi MIDI setting.
@@ -2057,31 +2065,25 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
 			_native_mt32 = enable_gs = false;
 			useOnlyNative = true;
 		} else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
-			nativeMidiDriver = MidiDriver::createMidi(dev);
+			if (_native_mt32)
+				nativeMidiDriver = new IMuseDriver_MT32(dev, _game.id == GID_SAMNMAX);
+			else
+				nativeMidiDriver = new IMuseDriver_GMidi(dev, imsFlags & IMuse::kFlagRolandGS, _game.id == GID_SAMNMAX);
 		}
 
-		if (nativeMidiDriver != nullptr && _native_mt32)
-			nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
-
 		if (!useOnlyNative) {
-			if (_sound->_musicType == MDT_TOWNS) {
-				adlibMidiDriver = new MidiDriver_TOWNS(_mixer);
-			} else if (_sound->_musicType == MDT_ADLIB || multi_midi) {
-				adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
+			if (_sound->_musicType == MDT_ADLIB || multi_midi) {
+				adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB));
 				adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
 				// Try to use OPL3 mode for Sam&Max when possible.
 				adlibMidiDriver->property(MidiDriver::PROP_SCUMM_OPL3, (_game.id == GID_SAMNMAX) ? 1 : 0);
+			} else if (_sound->_musicType == MDT_TOWNS) {
+				adlibMidiDriver = new IMuseDriver_FMTowns(_mixer);
 			} else if (_sound->_musicType == MDT_PCSPK) {
-				adlibMidiDriver = new PcSpkDriver(_mixer);
+				adlibMidiDriver = new IMuseDriver_PCSpk(_mixer);
 			}
 		}
 
-		uint32 imsFlags = 0;
-		if (_native_mt32)
-			imsFlags |= IMuse::kFlagNativeMT32;
-		if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32)
-			imsFlags |= IMuse::kFlagRolandGS;
-
 		_imuse = IMuse::create(this, nativeMidiDriver, adlibMidiDriver, isMacM68kIMuse() ? MDT_MACINTOSH : _sound->_musicType, imsFlags);
 
 		if (_game.platform == Common::kPlatformFMTowns) {


Commit: 40b459164b8971591f3debab2f88c34efff7c0dd
    https://github.com/scummvm/scummvm/commit/40b459164b8971591f3debab2f88c34efff7c0dd
Author: athrxx (athrxx at scummvm.org)
Date: 2022-10-06T18:47:46+02:00

Commit Message:
SCUMM: (IMS) - add dedicated MT-32 sound driver

(fixes bug no. 1248 - "SAM: some MIDI channels missing on MT-32")

This is a very obvious bug if the speech is turned off (otherwise the speech will
kind of cover it up). In the SAMNMAX intro it is hardly playing anything.

The reason is that the newer sound system has an extra driver layer between the
imuse parts and the actual hardware which mananges the channels (similar to
what we already do for AdLib, Amiga, FM-Towns...). Whenever the songs are
attempting to use a Midi part > 9 our current system will fail.

I have written the MT-32 player to also handle the older games music and sound
effects. This will allow cleaning up various more hacks in the common sound code.
I have done only little cleanup here, since it is not the main purpose of the commit.

I will also write a GM driver. This is going to be easier. Currently, there is a only
some init and skeleton code for it, the rest is just pass-through...

Changed paths:
    engines/scumm/imuse/drivers/gmidi.cpp
    engines/scumm/imuse/drivers/gmidi.h
    engines/scumm/imuse/drivers/mt32.cpp
    engines/scumm/imuse/drivers/mt32.h
    engines/scumm/imuse/imuse.cpp
    engines/scumm/imuse/imuse.h
    engines/scumm/imuse/imuse_internal.h
    engines/scumm/imuse/imuse_part.cpp
    engines/scumm/imuse/imuse_player.cpp
    engines/scumm/imuse/instrument.cpp
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/imuse/drivers/gmidi.cpp b/engines/scumm/imuse/drivers/gmidi.cpp
index eaa322cb981..d6e8749ab0e 100644
--- a/engines/scumm/imuse/drivers/gmidi.cpp
+++ b/engines/scumm/imuse/drivers/gmidi.cpp
@@ -39,6 +39,8 @@ int IMuseDriver_GMidi::open() {
 		return MERR_CANNOT_CONNECT;
 
 	int res = _drv->open();
+	if (res)
+		return res;
 
 	if (_gsMode)
 		initDeviceAsRolandGS();
@@ -48,15 +50,30 @@ int IMuseDriver_GMidi::open() {
 	return res;
 }
 
+void IMuseDriver_GMidi::close() {
+	if (isOpen() && _drv)
+		_drv->close();
+
+	//releaseChannels();
+}
+
 MidiChannel *IMuseDriver_GMidi::allocateChannel() {
-	if (!_newSystem)
+	if (!isOpen())
+		return nullptr;
+
+	// Pass through everything for now.
+	//if (!_newSystem)
 		return _drv->allocateChannel();
 
 	return nullptr;
 }
 
 MidiChannel *IMuseDriver_GMidi::getPercussionChannel() {
-	if (!_newSystem)
+	if (!isOpen())
+		return nullptr;
+
+	// Pass through everything for now.
+	//if (!_newSystem)
 		return _drv->getPercussionChannel();
 
 	return nullptr;
@@ -93,7 +110,7 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
 	// General MIDI System On message
 	// Resets all GM devices to default settings
 	memcpy(&buffer[0], "\x7E\x7F\x09\x01", 4);
-	_drv->sysEx(buffer, 4);
+	sysEx(buffer, 4);
 	debug(2, "GM SysEx: GM System On");
 	g_system->delayMillis(200);
 
@@ -109,13 +126,13 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
 
 	// GS Reset
 	memcpy(&buffer[4], "\x40\x00\x7F\x00\x41", 5);
-	_drv->sysEx(buffer, 9);
+	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);
-	_drv->sysEx(buffer, 12);
+	sysEx(buffer, 12);
 	debug(2, "GS SysEx: Master Tune set to 442.0kHz");
 
 	// Note: All Roland GS devices support CM-64/32L maps
@@ -132,24 +149,24 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
 	// Switch Drum Map to CM-64/32L (MT-32 Compatible Drums)
 	getPercussionChannel()->controlChange(0, 0);
 	getPercussionChannel()->controlChange(32, 1);
-	_drv->send(127 << 8 | 0xC0 | 9);
+	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);
-	_drv->sysEx(buffer, 9);
+	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)
-		_drv->send((64 << 16) | (91 << 8) | (0xB0 | 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)
-		_drv->setPitchBendRange(i, 12);
+		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
@@ -157,7 +174,7 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
 	for (i = 0; i < 16; ++i) {
 		buffer[5] = 0x20 + i;
 		buffer[8] = 0x18 - i;
-		_drv->sysEx(buffer, 9);
+		sysEx(buffer, 9);
 	}
 
 	debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
@@ -170,25 +187,25 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
 	// Expression cannot be modified. I don't know why, but
 	// Roland does it this way.
 	memcpy(&buffer[4], "\x40\x10\x0E\x00\x22", 5);
-	_drv->sysEx(buffer, 9);
+	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);
-	_drv->sysEx(buffer, 9);
+	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);
-	_drv->sysEx(buffer, 9);
+	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);
-	_drv->sysEx(buffer, 9);
+	sysEx(buffer, 9);
 	debug(2, "GS SysEx: Reverb Time is 106");
 }
 
diff --git a/engines/scumm/imuse/drivers/gmidi.h b/engines/scumm/imuse/drivers/gmidi.h
index 311c8d9a45e..7f7dca52a5a 100644
--- a/engines/scumm/imuse/drivers/gmidi.h
+++ b/engines/scumm/imuse/drivers/gmidi.h
@@ -32,10 +32,10 @@ public:
 	~IMuseDriver_GMidi() override;
 
 	int open() override;
+	void close() override;
 
 	// Just pass these through...
 	bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
-	void close() override { if (_drv) _drv->close(); }
 	uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
 	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
 	uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
diff --git a/engines/scumm/imuse/drivers/mt32.cpp b/engines/scumm/imuse/drivers/mt32.cpp
index e65001834ac..4be56514df6 100644
--- a/engines/scumm/imuse/drivers/mt32.cpp
+++ b/engines/scumm/imuse/drivers/mt32.cpp
@@ -24,17 +24,501 @@
 #include "base/version.h"
 #include "scumm/imuse/drivers/mt32.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 Scumm {
 
-IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _drv(nullptr) {
+class IMuseChannel_MT32 : public MidiChannel {
+public:
+	IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number);
+	~IMuseChannel_MT32() override {}
+
+	MidiDriver *device() override { return _drv; }
+	byte getNumber() override {	return _number; }
+
+	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;
+	void programChange(byte program) override;
+	void pitchBend(int16 bend) override;
+
+	// Control Change and SCUMM specific functions
+	void volume(byte value) override;
+	void panPosition(byte value) override;
+	void pitchBendFactor(byte value) override { _pitchBendSensitivity = value; }
+	void transpose(int8 value) override { _transpose = value; }
+	void detune(byte value) override { _detune = value; }
+	void priority(byte value) override { _prio = value; }
+	void modulationWheel(byte value) override;
+	void sustain(bool value) override;
+	void effectLevel(byte value) override;
+	void chorusLevel(byte value) override {}
+	void allNotesOff() override;
+	void sysEx_customInstrument(uint32 type, const byte *instr) override;
+
+	void setOutput(MT32RealChan *out) { _out = out; }
+
+private:
+	void noteOffIntern(byte note);
+	void noteOnIntern(byte note, byte velocity);
+
+	void sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const;
+	void sendSysexTimbreData(const byte *data, uint32 dataSize) const;
+
+	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); }
+
+	IMuseDriver_MT32 *_drv;
+	const byte _number;
+	const bool _newSystem;
+	bool _allocated;
+
+	MT32RealChan *_out;
+	byte _program;
+	byte _timbre;
+	byte _volume;
+	byte _panPos;
+	byte _polyphony;
+	byte _channelUsage;
+	bool _exhaust;
+	byte _prio;
+	int8 _detune;
+	int8 _transpose;
+	byte _reverbSwitch;
+	byte _pitchBendSensitivity;
+	int16 _pitchBend;
+	bool _sustain;
+
+	MT32ControlChan *&_idleChain;
+	MT32RealChan *&_availHwChain;
+	MT32ControlChan *&_activeChain;
+
+	const byte *_programsMapping;
+	const uint32 _sysexPatchAddrBase;
+	const uint32 _sysexTimbreAddrBase;
+};
+
+class MT32Chan {
+public:
+	MT32Chan(int number, IMuseChannel_MT32 *in) : _prev(nullptr), _next(nullptr), _in(in), _number(number) {}
+	MT32Chan *_prev;
+	MT32Chan *_next;
+	IMuseChannel_MT32 *_in;
+	byte _number;
+};
+
+class MT32RealChan : public MT32Chan {
+public:
+	MT32RealChan(int number, IMuseChannel_MT32 *in, MidiChannel *out) : MT32Chan(number, in), _out(out), _sysexTempAddrBase(0xC000 + (number << 4)) {
+		assert(_out && _number == out->getNumber());
+		if (in)
+			in->setOutput(this);
+	}
+	MidiChannel *_out;
+	const uint32 _sysexTempAddrBase;
+};
+
+class MT32ControlChan : public MT32Chan{
+public:
+	MT32ControlChan() : MT32Chan(0, nullptr), _note(0) {}
+	byte _note;
+};
+
+template <typename MT32ChanTmpl>
+void connect(MT32ChanTmpl *&chain, MT32Chan *node) {
+	if (!node || node->_prev || node->_next)
+		return;
+	if ((node->_next = chain))
+		chain->_prev = node;
+	chain = static_cast<MT32ChanTmpl*>(node);
+}
+
+template <typename MT32ChanTmpl>
+void disconnect(MT32ChanTmpl *&chain, MT32Chan *node) {
+	if (!node || !chain)
+		return;
+
+	const MT32Chan *ch = static_cast<MT32Chan*>(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 = static_cast<MT32ChanTmpl*>(node->_next);
+
+	node->_next = node->_prev = nullptr;
+}
+
+#define sendMidi(stat, par1, par2)	_drv->send(((par2) << 16) | ((par1) << 8) | (stat))
+
+IMuseChannel_MT32::IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number) :MidiChannel(), _drv(drv), _number(number), _allocated(false), _out(nullptr), _sustain(false),
+	_program(0), _timbre(0xFF),	_volume(0x7F), _panPos(0x40), _pitchBend(0x2000), _polyphony(1), _channelUsage(0), _exhaust(false), _prio(0), _detune(0), _transpose(0),
+	_pitchBendSensitivity(2), _reverbSwitch(1), _idleChain(_drv->_idleChain), _availHwChain(_drv->_hwChain), _activeChain(_drv->_activeChain),
+	_sysexPatchAddrBase(0x14000 + (number << 3)), _sysexTimbreAddrBase(0x20000 + (number << 8)), _programsMapping(_drv->_programsMapping), _newSystem(_drv->_newSystem) {
+	assert(_drv);
+}
+
+bool IMuseChannel_MT32::allocate() {
+	if (_allocated)
+		return false;
+
+	if (!_newSystem) {
+		byte msg[] = { (byte)(_timbre >> 6), (byte)(_timbre & 0x3F), 0x18, 0x32, 0x10, 0x00, _reverbSwitch};
+		sendSysexPatchData(0, msg, sizeof(msg));
+		_program = _number;
+		_prio = 0x80;
+	}
+
+	return (_allocated = true);
+}
+
+void IMuseChannel_MT32::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 if (_out || _number == 9)
+		sendMidi(0x80 | _number, note, 0x40);
+#endif
+}
+
+void IMuseChannel_MT32::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 if (_out) {
+		sendMidi(0x90 | _out->_number, note, velocity);
+	} else if (_number == 9) {
+		sendMidi(0xB9, 0x07, _volume);
+		sendMidi(0x99, note, velocity);
+	}
+#endif
+}
+
+void IMuseChannel_MT32::controlChange(byte control, byte value)  {
+	switch (control) {
+	case 7:
+		volume(value);
+		break;
+	case 10:
+		panPosition(value);
+		break;
+	case 17:
+		if (_newSystem)
+			_polyphony = value;
+		else
+			detune(value);
+		break;
+	case 18:
+		priority(value);
+		break;
+	case 123:
+		allNotesOff();
+		break;
+	default:
+		warning("Unhandled Control: %d", control);
+		break;
+	}
+}
+
+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 (_out && _program < 128)
+		sendMidi(0xC0 | _out->_number, _program, 0);
+}
+
+void IMuseChannel_MT32::pitchBend(int16 bend)  {
+	_pitchBend = bend + 0x2000;
+	if (_out)
+		sendMidi(0xE0 | _out->_number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
+}
+
+void IMuseChannel_MT32::volume(byte value) {
+	_volume = value;
+	if (_out)
+		sendMidi(0xB0 | _out->_number, 0x07, value);
+}
+
+void IMuseChannel_MT32::panPosition(byte value) {
+	_panPos = value;
+	if (_out)
+		sendMidi(0xB0 | _out->_number, 0x0A, value);
+}
+
+void IMuseChannel_MT32::modulationWheel(byte value) {
+	if (_out && !_newSystem)
+		sendMidi(0xB0 | _out->_number, 0x01, value);
+}
+
+void IMuseChannel_MT32::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 if (_out) {
+		sendMidi(0xB0 | _out->_number, 0x40, 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)
+		_drv->sendMT32Sysex(_out->_sysexTempAddrBase + 6, &_reverbSwitch, 1);
+}
+
+void IMuseChannel_MT32::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 if (_out) {
+		sendMidi(0xB0 | _out->_number, 0x7B, 0);
+	}
+}
+
+void IMuseChannel_MT32::sysEx_customInstrument(uint32 type, const byte *instr)  {
+	if (*instr++ != 0x41) {
+		warning("IMuseChannel_MT32::sysEx_customInstrument(): Invalid (non-Roland) sysex message received");
+		return;
+	}
+
+	byte partNo = *instr;
+	uint32 addr = (instr[3] << 14) | (instr[4] << 7) | instr[5];
+
+	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 | _out->_number, _program, 0);
+	} else {
+		_drv->sendMT32Sysex(0x22000 + (partNo << 8), instr + 6, 246);
+	}
+}
+
+void IMuseChannel_MT32::noteOffIntern(byte note) {
+	if (_program > 127 || _activeChain == nullptr)
+		return;
+
+	MT32Chan *node = nullptr;
+	for (MT32ControlChan *i = _activeChain; i; i = static_cast<MT32ControlChan*>(i->_next)) {
+		if (i->_number == _number && i->_note == note) {
+			node = i;
+			break;
+		}
+	}
+
+	if (!node)
+		return;
+
+	if (_out)
+		sendMidi(0x80 | _out->_number, note, 0x40);
+	else
+		sendMidi(0x89, note, 0x40);
+
+	if (_newSystem)
+		_exhaust = (--_channelUsage > _polyphony);
+
+	disconnect(_activeChain, node);
+	connect(_idleChain, node);
+}
+
+void IMuseChannel_MT32::noteOnIntern(byte note, byte velocity) {
+	if (_program > 127 || (_newSystem && _number == 9 && note > 75))
+		return;
+
+	MT32ControlChan *node = nullptr;
+
+	if (_idleChain) {
+		node = _idleChain;
+		disconnect(_idleChain, node);
+	} else {
+		MT32Chan *foundChan = nullptr;
+		IMuseChannel_MT32 *best = this;
+		for (MT32Chan *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;
+				foundChan = i;
+			}
+		}
+
+		if (!foundChan)
+			return;
+
+		node = static_cast<MT32ControlChan*>(foundChan);
+
+		IMuseChannel_MT32 *prt = _drv->getPart(node->_number);
+		if (prt && prt->_out)
+			sendMidi(0x80 | prt->_out->_number, node->_note, 0x40);
+		else if (node->_number == 9)
+			sendMidi(0x89, 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);
+
+	if (_number == 9) {
+		sendMidi(0xB9, 0x07, _volume);
+		sendMidi(0x99, note, velocity);
+		return;
+	}
+
+	if (!_out) {
+		MT32RealChan *nodeReal = _availHwChain;
+		while (nodeReal && nodeReal->_next)
+			nodeReal = static_cast<MT32RealChan*>(nodeReal->_next);
+
+		assert(nodeReal);
+		assert(nodeReal->_in);
+
+		nodeReal->_in->_out = nullptr;
+		nodeReal->_in = this;
+		_out = nodeReal;
+
+		sendMidi(0xB0 | _out->_number, 0x7B, 0);
+		sendMidi(0xB0 | _out->_number, 0x07, _volume);
+		sendMidi(0xB0 | _out->_number, 0x0A, _panPos);
+		sendMidi(0xC0 | _out->_number, _program, 0);
+		sendMidi(0xE0 | _out->_number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
+	}
+
+	disconnect(_availHwChain, _out);
+	connect(_availHwChain, _out);
+
+	sendMidi(0x90 | _out->_number, note, velocity);
+}
+
+void IMuseChannel_MT32::sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const {
+	assert(!_newSystem);
+	_drv->sendMT32Sysex(_sysexPatchAddrBase + offset, data, dataSize);
+}
+
+void IMuseChannel_MT32::sendSysexTimbreData(const byte *data, uint32 dataSize) const {
+	assert(!_newSystem);
+	_drv->sendMT32Sysex(_sysexTimbreAddrBase, data, dataSize);
+}
+
+#undef sendMidi
+
+IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _programsMapping(nullptr), _notesPlaying(nullptr),
+	_notesSustained(nullptr), _drv(nullptr), _imsParts(nullptr), _hwOutputChan(nullptr), _controlChan(nullptr), _idleChain(nullptr), _hwChain(nullptr), _activeChain(nullptr),
+#ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
+	_numChannels(16) {
+#else
+	_numChannels(newSystem ? 16 : 9) {
+#endif
 	_drv = MidiDriver::createMidi(dev);
 	assert(_drv);
 
-	if (!_newSystem)
-		_drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+	_drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+
+	if (newSystem)
+		_programsMapping = MidiDriver::_gmToMt32;
 }
 
 IMuseDriver_MT32::~IMuseDriver_MT32() {
+	close();
 	delete _drv;
 }
 
@@ -43,44 +527,84 @@ int IMuseDriver_MT32::open() {
 		return MERR_CANNOT_CONNECT;
 
 	int res = _drv->open();
+	if (res)
+		return res;
 
 	initDevice();
+	createChannels();
 
 	return res;
 }
 
+void IMuseDriver_MT32::close() {
+	if (isOpen()) {
+		// Reset the MT-32
+		sendMT32Sysex(0x1FC000, 0, 0);
+		g_system->delayMillis(250);
+
+		if (_drv)
+			_drv->close();
+	}
+
+	releaseChannels();
+}
+
 MidiChannel *IMuseDriver_MT32::allocateChannel() {
-	if (!_newSystem)
-		return _drv->allocateChannel();
+	if (!isOpen())
+		return nullptr;
+
+	for (int i = 0; i < _numChannels; ++i) {
+		IMuseChannel_MT32 *ch = _imsParts[i];
+		if (ch && ch->getNumber() != 9 && ch->allocate())
+			return ch;
+	}
 
 	return nullptr;
 }
 
 MidiChannel *IMuseDriver_MT32::getPercussionChannel() {
-	if (!_newSystem)
-		return _drv->getPercussionChannel();
+	if (!isOpen())
+		return nullptr;
 
-	return nullptr;
+	IMuseChannel_MT32 *ch = getPart(9);
+	if (ch) {
+		ch->release();
+		ch->allocate();
+	}
+
+	return ch;
 }
 
 void IMuseDriver_MT32::initDevice() {
-	byte buffer[52];
-
 	// Reset the MT-32
-	_drv->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
+	sendMT32Sysex(0x1FC000, 0, 0);
 	g_system->delayMillis(250);
 
 	// Setup master tune, reverb mode, reverb time, reverb level,
 	// channel mapping, partial reserve and master volume
-	_drv->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31);
+	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(250);
 
-	// Map percussion to notes 24 - 34 without reverb
 	if (!_newSystem) {
-		_drv->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\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\x44", 52);
+		// 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(250);
 	}
 
+	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);
+	}
+
 	// Compute version string (truncated to 20 chars max.)
 	Common::String infoStr = "ScummVM ";
 	infoStr += gScummVMVersion;
@@ -89,6 +613,7 @@ void IMuseDriver_MT32::initDevice() {
 		len = 20;
 
 	// Display a welcome message on MT-32 displays.
+	byte buffer[28];
 	memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
 	memcpy(&buffer[7], "                    ", 20);
 	memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len);
@@ -96,8 +621,120 @@ void IMuseDriver_MT32::initDevice() {
 	for (int i = 4; i < 27; ++i)
 		checksum -= buffer[i];
 	buffer[27] = checksum & 0x7F;
-	_drv->sysEx(buffer, 28);
+	sysEx(buffer, 28);
 	g_system->delayMillis(1000);
 }
 
+void IMuseDriver_MT32::createChannels() {
+	releaseChannels();
+
+	_imsParts = new IMuseChannel_MT32*[_numChannels];
+	_hwOutputChan = new MT32RealChan*[8];
+	_controlChan = new MT32ControlChan*[9];
+
+	assert(_imsParts);
+	assert(_hwOutputChan);
+	assert(_controlChan);
+
+	for (int i = 0; i < _numChannels; ++i)
+		_imsParts[i] = new IMuseChannel_MT32(this, (i + 1) & 0x0F);
+
+	MidiChannel *driverChannels[16];
+	memset(driverChannels, 0, sizeof(driverChannels));
+	for (int i = 0; i < ARRAYSIZE(driverChannels); ++i)
+		driverChannels[i] = _drv->allocateChannel();
+
+	for (int i = 1; i < 9; ++i) {
+		MidiChannel *m = nullptr;
+		for (int ii = 0; m == nullptr && ii < ARRAYSIZE(driverChannels); ++ii) {
+			if (driverChannels[ii] && driverChannels[ii]->getNumber() == i)
+				SWAP(m, driverChannels[ii]);
+		}
+		if (!m)
+			error("IMuseDriver_MT32::createChannels(): Failed to create channels.");
+		_hwOutputChan[i - 1] = new MT32RealChan(i, getPart(i), m);
+		connect(_hwChain, _hwOutputChan[i - 1]);
+	}
+
+	for (int i = 0; i < ARRAYSIZE(driverChannels); ++i) {
+		if (driverChannels[i])
+			driverChannels[i]->release();
+	}
+
+	for (int i = 0; i < 9; ++i) {
+		_controlChan[i] = new MT32ControlChan();
+		connect(_idleChain, _controlChan[i]);
+	}
+
+	if (_newSystem) {
+		_notesPlaying = new uint16[128]();
+		_notesSustained = new uint16[128]();
+	}
+}
+
+void IMuseDriver_MT32::releaseChannels() {
+	if (_imsParts) {
+		for (int i = 0; i < _numChannels; ++i)
+			delete _imsParts[i];
+		delete[] _imsParts;
+		_imsParts = nullptr;
+	}
+
+	if (_hwOutputChan) {
+		for (int i = 0; i < 8; ++i)
+			delete _hwOutputChan[i];
+		delete[] _hwOutputChan;
+		_hwOutputChan = nullptr;
+	}
+
+	if (_controlChan) {
+		for (int i = 0; i < 9; ++i)
+			delete _controlChan[i];
+		delete[] _controlChan;
+		_controlChan = nullptr;
+	}
+
+	delete[] _notesPlaying;
+	_notesPlaying = nullptr;
+	delete[] _notesSustained;
+	_notesSustained = nullptr;
+}
+
+IMuseChannel_MT32 *IMuseDriver_MT32::getPart(int number) const {
+	for (int i = 0; i < _numChannels; ++i)
+		if (_imsParts[i]->getNumber() == number)
+			return _imsParts[i];
+	return nullptr;
+}
+
+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;
+
+	sysEx(msg, dst - msg);
+
+	delete[] msg;
+}
+
+#undef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
+
 } // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/mt32.h b/engines/scumm/imuse/drivers/mt32.h
index 9f6cf7b2687..6f0bf32de78 100644
--- a/engines/scumm/imuse/drivers/mt32.h
+++ b/engines/scumm/imuse/drivers/mt32.h
@@ -26,22 +26,26 @@
 
 namespace Scumm {
 
+class IMuseChannel_MT32;
+class MT32RealChan;
+class MT32ControlChan;
+
 class IMuseDriver_MT32 : public MidiDriver {
+	friend class IMuseChannel_MT32;
 public:
 	IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem);
 	~IMuseDriver_MT32() override;
 
 	int open() override;
+	void close() override;
 
 	// Just pass these through...
 	bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
-	void close() override { if (_drv) _drv->close(); }
 	uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
 	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
 	uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
 	void send(uint32 b) override { if (_drv) _drv->send(b); };
 	void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } 
-	void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
 
 	// Channel allocation functions
 	MidiChannel *allocateChannel() override;
@@ -49,9 +53,37 @@ public:
 
 private:
 	void initDevice();
+	void createChannels();
+	void releaseChannels();
+
+	IMuseChannel_MT32 *getPart(int number) const;
+
+	// Convenience function that allows to send the sysex message with the exact same arguments as they are used in the original drivers.
+	void sendMT32Sysex(uint32 addr, const byte *data, uint32 dataSize);
+
+	void setNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] |= (1 << chan); }
+	void clearNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] &= ~(1 << chan); }
+	bool queryNoteFlag(byte chan, byte note) const { return (_notesPlaying && chan < 16 && note < 128) ? _notesPlaying[note] & (1 << chan) : false; }
+	void setSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] |= (1 << chan); }
+	void clearSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] &= ~(1 << chan); }
+	bool querySustainFlag(byte chan, byte note) const { return (_notesSustained && chan < 16 && note < 128) ? _notesSustained[note] & (1 << chan) : false; }
 
 	MidiDriver *_drv;
 	const bool _newSystem;
+	const byte _numChannels;
+
+	IMuseChannel_MT32 **_imsParts;
+	MT32RealChan **_hwOutputChan;
+	MT32ControlChan **_controlChan;
+
+	MT32ControlChan *_idleChain;
+	MT32RealChan *_hwChain;
+	MT32ControlChan *_activeChain;
+
+	const byte *_programsMapping;
+
+	uint16 *_notesPlaying;
+	uint16 *_notesSustained;
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 8b071e2f838..187ead42f6e 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -46,6 +46,7 @@ namespace Scumm {
 IMuseInternal::IMuseInternal(ScummEngine *vm, MidiDriverFlags sndType, uint32 initFlags) :
 	_native_mt32((initFlags & kFlagNativeMT32) || (initFlags & kFlagRolandGS)), // GS Mode emulates MT-32 on a GS device, so _native_mt32 should always be true
 	_enable_gs(initFlags & kFlagRolandGS),
+	_newSystem(initFlags & kFlagNewSystem),
 	_midi_adlib(nullptr),
 	_midi_native(nullptr),
 	_sysex(nullptr),
@@ -90,12 +91,6 @@ IMuseInternal::~IMuseInternal() {
 	}
 
 	if (_midi_native) {
-		if (_native_mt32) {
-			// Reset the MT-32
-			_midi_native->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
-			_system->delayMillis(250);
-		}
-
 		_midi_native->close();
 		delete _midi_native;
 		_midi_native = nullptr;
diff --git a/engines/scumm/imuse/imuse.h b/engines/scumm/imuse/imuse.h
index 7924bcf409b..a711d5dd9fa 100644
--- a/engines/scumm/imuse/imuse.h
+++ b/engines/scumm/imuse/imuse.h
@@ -56,8 +56,9 @@ public:
 	};
 
 	enum {
-		kFlagNativeMT32 =	1 << 0,
-		kFlagRolandGS =		1 << 1
+		kFlagNewSystem	=	1 << 0,
+		kFlagNativeMT32 =	1 << 1,
+		kFlagRolandGS =		1 << 2
 	};
 
 public:
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index 7539eb55423..6e395ad52ac 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -323,6 +323,7 @@ struct Part : public Common::Serializable {
 	byte _vol, _vol_eff;
 	int8 _detune, _detune_eff;
 	int8 _pan, _pan_eff;
+	byte _polyphony;
 	bool _on;
 	byte _modwheel;
 	bool _pedal;
@@ -367,7 +368,7 @@ struct Part : public Common::Serializable {
 	void set_pri(int8 pri);
 	void set_pan(int8 pan);
 
-	void set_sm17(int8 val);
+	void set_polyphony(byte val);
 	void set_onoff(bool on);
 	void fix_after_load();
 
@@ -384,6 +385,7 @@ private:
 	void sendDetune();
 	void sendPanPosition(uint8 value);
 	void sendEffectLevel(uint8 value);
+	void sendPolyphony();
 };
 
 
@@ -409,6 +411,7 @@ class IMuseInternal : public IMuse {
 protected:
 	const bool _native_mt32;
 	const bool _enable_gs;
+	const bool _newSystem;
 	const MidiDriverFlags _soundType;
 	MidiDriver *_midi_adlib;
 	MidiDriver *_midi_native;
@@ -458,6 +461,15 @@ protected:
 	CommandQueue _cmd_queue[64];
 	DeferredCommand _deferredCommands[4];
 
+	// These are basically static vars in the original drivers
+	struct RhyState {
+		RhyState() : RhyState(127, 1, 0) {}
+		RhyState(byte volume, byte polyphony, byte priority) : vol(volume), poly(polyphony), prio(priority) {}
+		byte vol;
+		byte poly;
+		byte prio;
+	} _rhyState;
+
 protected:
 	IMuseInternal(ScummEngine *vm, MidiDriverFlags sndType, uint32 initFlags);
 	~IMuseInternal() override;
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 28f3e345e80..7a0a674d4d5 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -51,6 +51,7 @@ Part::Part() {
 	_detune_eff = 0;
 	_pan = 0;
 	_pan_eff = 0;
+	_polyphony = 0;
 	_on = false;
 	_modwheel = 0;
 	_pedal = false;
@@ -108,20 +109,15 @@ void Part::set_detune(int8 detune) {
 	// Sam&Max does not have detune, so we just ignore this here. We still get
 	// this called, since Sam&Max uses the same controller for a different
 	// purpose.
-	if (_se->_game_id == GID_SAMNMAX) {
-#if 0
-		if (_mc) {
-			_mc->controlChange(17, detune + 0x40);
-		}
-#endif
-	} else {
-		_detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
-		// Some drivers handle the transpose and the detune in pitchBend()...
-		if (_player->isAdLibOrFMTowns())
-			sendDetune();
-		else
-			sendPitchBend();
-	}
+	if (_se->_newSystem)
+		return;
+
+	_detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
+	// Some drivers handle the transpose and the detune in pitchBend()...
+	if (_player->isAdLibOrFMTowns())
+		sendDetune();
+	else
+		sendPitchBend();
 }
 
 void Part::pitchBend(int16 value) {
@@ -146,8 +142,12 @@ void Part::set_pan(int8 pan) {
 	sendPanPosition(_pan_eff + 0x40);
 }
 
-void Part::set_sm17(int8 val) {
-
+void Part::set_polyphony(byte val) {
+	if (!_se->_newSystem)
+		return;
+	_polyphony = val;
+	if (_mc)
+		_mc->controlChange(17, val);
 }
 
 void Part::set_transpose(int8 transpose, int8 clipRangeLow, int8 clipRangeHi)  {
@@ -255,19 +255,20 @@ void Part::noteOn(byte note, byte velocity) {
 		if (!mc)
 			return;
 
-		// FIXME: The following is evil, EVIL!!! Either prev_vol_eff is
-		// actually meant to be a member of the Part class (i.e. each
-		// instance of Part keeps a separate copy of it); or it really
-		// is supposed to be shared by all Part instances -- but then it
-		// should be implemented as a class static var. As it is, using
-		// a function level static var in most cases is arcane and evil.
-		static byte prev_vol_eff = 128;
-		if (_vol_eff != prev_vol_eff) {
+		if (_vol_eff != _se->_rhyState.vol)
 			mc->volume(_vol_eff);
-			prev_vol_eff = _vol_eff;
-		}
-		if ((note < 35) && (!_player->_se->isNativeMT32()))
+
+		if (_se->_newSystem) {
+			if (_pri_eff != _se->_rhyState.prio)
+				mc->priority(_pri_eff);
+			if (_polyphony != _se->_rhyState.poly)
+				mc->controlChange(17, _polyphony);
+
+		} else if ((note < 35) && (!_player->_se->isNativeMT32())) {
 			note = Instrument::_gmRhythmMap[note];
+		}
+
+		_se->_rhyState = IMuseInternal::RhyState(_vol_eff, _polyphony, _pri_eff);
 
 		mc->noteOn(note, velocity);
 	}
@@ -358,6 +359,7 @@ void Part::sendAll() {
 	_mc->sustain(_pedal);
 	_mc->modulationWheel(_modwheel);
 	sendPanPosition(_pan_eff + 0x40);
+	sendPolyphony();
 
 	if (_instrument.isValid())
 		_instrument.send(_mc);
@@ -483,29 +485,13 @@ void Part::sendPanPosition(uint8 value) {
 void Part::sendEffectLevel(uint8 value) {
 	if (!_mc)
 		return;
+	_mc->effectLevel(value);
+}
 
-	// As described in bug report #1849 "MI2: Minor problems in native MT-32 mode"
-	// for the MT-32 one has to use a sysEx event to change the effect level (rather
-	// the reverb setting).
-	if (_player->_se->isNativeMT32()) {
-		if (value != 127 && value != 0) {
-			warning("Trying to use unsupported effect level value %d in native MT-32 mode.", value);
-
-			if (value >= 64)
-				value = 127;
-			else
-				value = 0;
-		}
-
-		byte message[9];
-		memcpy(message, "\x41\x00\x16\x12\x00\x00\x06\x00\x00", 9);
-		message[1] = _mc->getNumber();
-		message[7] = (value == 127) ? 1 : 0;
-		message[8] = 128 - (6 + message[7]);
-		_player->getMidiDriver()->sysEx(message, 9);
-	} else {
-		_mc->effectLevel(value);
-	}
+void Part::sendPolyphony() {
+	if (!_mc || !_se->_newSystem)
+		return;
+	_mc->controlChange(17, _polyphony);
 }
 
 } // End of namespace Scumm
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 4837f960c76..50ab9f9b86e 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -223,7 +223,7 @@ int Player::start_seq_sound(int sound, bool reset_vars) {
 }
 
 void Player::loadStartParameters(int sound) {
-	_priority = (_se->_game_id != GID_SAMNMAX) ? 0x80 : 0;
+	_priority = _se->_newSystem ? 0 : 0x80;
 	_volume = 0x7F;
 	_vol_chan = 0xFFFF;
 	_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
@@ -319,13 +319,13 @@ void Player::send(uint32 b) {
 			part->pitchBendFactor(param2);
 			break;
 		case 17: // GP Slider 2
-			if (_se->_game_id == GID_SAMNMAX)
-				part->set_sm17(param2);
+			if (_se->_newSystem)
+				part->set_polyphony(param2);
 			else
 				part->set_detune(param2 - 0x40);
 			break;
 		case 18: // GP Slider 3
-			if (_se->_game_id != GID_SAMNMAX)
+			if (!_se->_newSystem)
 				param2 -= 0x40;
 			part->set_pri(param2);
 			_se->reallocateMidiChannels(_midi);
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index 476a6448045..57123698a23 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -414,8 +414,8 @@ void Instrument_Program::send(MidiChannel *mc) {
 		return;
 
 	byte program = _program;
-	if (_nativeMT32Device != _soundTypeMT32)
-		program = _nativeMT32Device ? MidiDriver::_gmToMt32[program] : MidiDriver::_mt32ToGm[program];
+	if (!_nativeMT32Device && _soundTypeMT32)
+		program =  MidiDriver::_mt32ToGm[program];
 	if (program < 128)
 		mc->programChange(program);
 }
@@ -483,25 +483,7 @@ void Instrument_Roland::saveLoadWithSerializer(Common::Serializer &s) {
 
 void Instrument_Roland::send(MidiChannel *mc) {
 	if (_nativeMT32Device) {
-		if (mc->getNumber() > 8)
-			return;
-		_instrument.device_id = mc->getNumber();
-
-		// Remap instrument to appropriate address space.
-		int address = 0x008000;
-		_instrument.address[0] = (address >> 14) & 0x7F;
-		_instrument.address[1] = (address >>  7) & 0x7F;
-		_instrument.address[2] = (address      ) & 0x7F;
-
-		// Recompute the checksum.
-		byte checksum = 0;
-		byte *ptr = (byte *)&_instrument + 4;
-		int i;
-		for (i = 4; i < (int)sizeof(_instrument) - 1; ++i)
-			checksum -= *ptr++;
-		_instrument.checksum = checksum & 0x7F;
-
-		mc->device()->sysEx((byte *)&_instrument, sizeof(_instrument));
+		mc->sysEx_customInstrument(0, (byte *)&_instrument);
 	} else {
 		// Convert to a GM program change.
 		byte program = getEquivalentGM();
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 3ddf5b5a295..e6c463a83a8 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1970,9 +1970,6 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
 		_sound->_musicType = MDT_MIDI;
 	}
 
-	// DOTT + SAM use General MIDI, so they shouldn't use GS settings
-	bool enable_gs = (_game.id == GID_TENTACLE || _game.id == GID_SAMNMAX) ? false : ConfMan.getBool("enable_gs");
-
 	/* Bind the mixer to the system => mixer will be invoked
 	 * automatically when samples need to be generated */
 	if (!_mixer->isReady()) {
@@ -2046,11 +2043,9 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
 		bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
 		bool useOnlyNative = false;
 
-		uint32 imsFlags = 0;
-		if (_native_mt32)
-			imsFlags |= IMuse::kFlagNativeMT32;
-		if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32)
-			imsFlags |= IMuse::kFlagRolandGS;
+		// DOTT + SAM use General MIDI, so they shouldn't use GS settings
+		bool enable_gs = (_game.id == GID_TENTACLE || _game.id == GID_SAMNMAX) ? false : (ConfMan.getBool("enable_gs") && MidiDriver::getMusicType(dev) != MT_MT32);
+		bool newSystem = (_game.id == GID_SAMNMAX);
 
 		if (isMacM68kIMuse()) {
 			// We setup this driver as native MIDI driver to avoid playback
@@ -2066,9 +2061,9 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
 			useOnlyNative = true;
 		} else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
 			if (_native_mt32)
-				nativeMidiDriver = new IMuseDriver_MT32(dev, _game.id == GID_SAMNMAX);
+				nativeMidiDriver = new IMuseDriver_MT32(dev, newSystem);
 			else
-				nativeMidiDriver = new IMuseDriver_GMidi(dev, imsFlags & IMuse::kFlagRolandGS, _game.id == GID_SAMNMAX);
+				nativeMidiDriver = new IMuseDriver_GMidi(dev, enable_gs, newSystem);
 		}
 
 		if (!useOnlyNative) {
@@ -2084,6 +2079,12 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
 			}
 		}
 
+		uint32 imsFlags =  newSystem ? IMuse::kFlagNewSystem : 0;
+		if (_native_mt32)
+			imsFlags |= IMuse::kFlagNativeMT32;
+		if (enable_gs)
+			imsFlags |= IMuse::kFlagRolandGS;
+
 		_imuse = IMuse::create(this, nativeMidiDriver, adlibMidiDriver, isMacM68kIMuse() ? MDT_MACINTOSH : _sound->_musicType, imsFlags);
 
 		if (_game.platform == Common::kPlatformFMTowns) {


Commit: 6984faf958db7184f516e7a7976b6667211e3172
    https://github.com/scummvm/scummvm/commit/6984faf958db7184f516e7a7976b6667211e3172
Author: athrxx (athrxx at scummvm.org)
Date: 2022-10-06T18:47:47+02:00

Commit Message:
AUDIO: add data size arg to MidiChannel::sysEx_customInstrument()

As a safety check...

Also throw out some warnings when loading savegames
with instrument data for a different device (the savegame
incompatibility would be very nice to be fixed, but it is not
trivial.

Changed paths:
    audio/adlib.cpp
    audio/mididrv.h
    audio/mpu401.h
    engines/scumm/imuse/drivers/amiga.cpp
    engines/scumm/imuse/drivers/fmtowns.cpp
    engines/scumm/imuse/drivers/mac_m68k.cpp
    engines/scumm/imuse/drivers/mac_m68k.h
    engines/scumm/imuse/drivers/mt32.cpp
    engines/scumm/imuse/drivers/pcspk.cpp
    engines/scumm/imuse/drivers/pcspk.h
    engines/scumm/imuse/instrument.cpp


diff --git a/audio/adlib.cpp b/audio/adlib.cpp
index 196e7feed89..83d557d6a9f 100644
--- a/audio/adlib.cpp
+++ b/audio/adlib.cpp
@@ -156,7 +156,7 @@ public:
 	void allNotesOff() override;
 
 	// SysEx messages
-	void sysEx_customInstrument(uint32 type, const byte *instr) override;
+	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override;
 };
 
 // FYI (Jamieson630)
@@ -185,7 +185,7 @@ public:
 	void sustain(bool value) override { }
 
 	// SysEx messages
-	void sysEx_customInstrument(uint32 type, const byte *instr) override;
+	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 datasize) override;
 
 private:
 	byte _notes[256];
@@ -1263,7 +1263,7 @@ void AdLibPart::allNotesOff() {
 		_owner->mcOff(_voice);
 }
 
-void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr) {
+void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) {
 	// Sam&Max allows for instrument overwrites, but we will not support it
 	// until we can find any track actually using it.
 #ifdef ENABLE_OPL3
@@ -1273,8 +1273,10 @@ void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr) {
 	}
 #endif
 
-	if (type == 'ADL ') {
+	if (type == 'ADL ' && instr && dataSize == sizeof(AdLibInstrument))
 		memcpy(&_partInstr, instr, sizeof(AdLibInstrument));
+	else if (type != 'ADL '){
+		warning("AdLibPart: 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);
 	}
 }
 
@@ -1350,7 +1352,7 @@ void AdLibPercussionChannel::noteOn(byte note, byte velocity) {
 	_owner->partKeyOn(this, inst, note, velocity, sec, _pan);
 }
 
-void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *instr) {
+void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) {
 	// We do not allow custom instruments in OPL3 mode right now.
 #ifdef ENABLE_OPL3
 	if (_owner->_opl3Mode) {
@@ -1359,7 +1361,7 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins
 	}
 #endif
 
-	if (type == 'ADLP') {
+	if (type == 'ADLP' && instr && dataSize) {
 		byte note = instr[0];
 		_notes[note] = instr[1];
 
@@ -1381,6 +1383,8 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins
 		_customInstruments[note]->carSustainRelease     = instr[10];
 		_customInstruments[note]->carWaveformSelect     = instr[11];
 		_customInstruments[note]->feedback               = instr[12];
+	} else if (type != 'ADLP'){
+		warning("AdLibPercussionChannel: 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);
 	}
 }
 
diff --git a/audio/mididrv.h b/audio/mididrv.h
index 49a279f6246..ed1f84d5eef 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -547,7 +547,7 @@ public:
 	virtual void allNotesOff() { controlChange(MidiDriver::MIDI_CONTROLLER_ALL_NOTES_OFF, 0); }
 
 	// SysEx messages
-	virtual void sysEx_customInstrument(uint32 type, const byte *instr) = 0;
+	virtual void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) = 0;
 };
 /** @} */
 #endif
diff --git a/audio/mpu401.h b/audio/mpu401.h
index 13857d852ab..6f014635a35 100644
--- a/audio/mpu401.h
+++ b/audio/mpu401.h
@@ -55,7 +55,7 @@ public:
 	virtual void pitchBendFactor(byte value);
 
 	// SysEx messages
-	virtual void sysEx_customInstrument(uint32 type, const byte *instr) {}
+	virtual void sysEx_customInstrument(uint32 type, const byte *instr, uint32 datasize) {}
 
 	// Only to be called by the owner
 	void init(MidiDriver *owner, byte channel);
diff --git a/engines/scumm/imuse/drivers/amiga.cpp b/engines/scumm/imuse/drivers/amiga.cpp
index 807269dd42d..d893e3fced7 100644
--- a/engines/scumm/imuse/drivers/amiga.cpp
+++ b/engines/scumm/imuse/drivers/amiga.cpp
@@ -149,7 +149,7 @@ public:
 	void transpose(int8 value) override;
 
 	void priority(byte value) override;
-	void sysEx_customInstrument(uint32 type, const byte *instr) override {}
+	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override {}
 
 	int getPriority() const { return _priority; }
 	SoundChannel_Amiga *getChannel() const { return _out; }
diff --git a/engines/scumm/imuse/drivers/fmtowns.cpp b/engines/scumm/imuse/drivers/fmtowns.cpp
index 6161bc8c82a..25062566980 100644
--- a/engines/scumm/imuse/drivers/fmtowns.cpp
+++ b/engines/scumm/imuse/drivers/fmtowns.cpp
@@ -138,7 +138,7 @@ public:
 	void detune(byte value) override;
 	void transpose(int8 value) override;
 	void priority(byte value) override;
-	void sysEx_customInstrument(uint32 type, const byte *instr) override;
+	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override;
 
 private:
 	void controlModulationWheel(byte value);
@@ -788,8 +788,12 @@ void TownsMidiInputChannel::priority(byte value) {
 	_priority = value;
 }
 
-void TownsMidiInputChannel::sysEx_customInstrument(uint32 type, const byte *instr) {
-	memcpy(_instrument, instr, 30);
+void TownsMidiInputChannel::sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) {
+	const uint8 instrSize = 30;
+	if (instr && dataSize == instrSize)
+		memcpy(_instrument, instr, instrSize);
+	else if (type != 'ADL ') // FM-Towns is actually supposed to get ADL type data.
+		warning("TownsMidiInputChannel: 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);
 }
 
 void TownsMidiInputChannel::controlModulationWheel(byte value) {
diff --git a/engines/scumm/imuse/drivers/mac_m68k.cpp b/engines/scumm/imuse/drivers/mac_m68k.cpp
index 600c07032ce..cc8669d5b69 100644
--- a/engines/scumm/imuse/drivers/mac_m68k.cpp
+++ b/engines/scumm/imuse/drivers/mac_m68k.cpp
@@ -422,11 +422,12 @@ void IMuseDriver_MacM68k::MidiChannel_MacM68k::priority(byte value) {
 	_priority = value;
 }
 
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) {
+void IMuseDriver_MacM68k::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) {
 	assert(instr);
-	if (type == 'MAC ') {
+	if (type == 'MAC ' && dataSize == sizeof(byte)) 
 		_instrument = _owner->getInstrument(*instr + kSysExBase);
-	}
+	else if (type != 'MAC ')
+		warning("MidiChannel_MacM68k: 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);
 }
 
 bool IMuseDriver_MacM68k::MidiChannel_MacM68k::allocate() {
diff --git a/engines/scumm/imuse/drivers/mac_m68k.h b/engines/scumm/imuse/drivers/mac_m68k.h
index cf51c5d90d6..b0fb064ba07 100644
--- a/engines/scumm/imuse/drivers/mac_m68k.h
+++ b/engines/scumm/imuse/drivers/mac_m68k.h
@@ -141,7 +141,7 @@ private:
 		void controlChange(byte control, byte value) override;
 		void pitchBendFactor(byte value) override;
 		void priority(byte value) override;
-		void sysEx_customInstrument(uint32 type, const byte *instr) override;
+		void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override;
 
 		bool allocate();
 
diff --git a/engines/scumm/imuse/drivers/mt32.cpp b/engines/scumm/imuse/drivers/mt32.cpp
index 4be56514df6..d1e02040a55 100644
--- a/engines/scumm/imuse/drivers/mt32.cpp
+++ b/engines/scumm/imuse/drivers/mt32.cpp
@@ -63,7 +63,7 @@ public:
 	void effectLevel(byte value) override;
 	void chorusLevel(byte value) override {}
 	void allNotesOff() override;
-	void sysEx_customInstrument(uint32 type, const byte *instr) override;
+	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override;
 
 	void setOutput(MT32RealChan *out) { _out = out; }
 
@@ -109,6 +109,10 @@ private:
 	const byte *_programsMapping;
 	const uint32 _sysexPatchAddrBase;
 	const uint32 _sysexTimbreAddrBase;
+
+	enum SysexMessageSize {
+		kSysexLengthTimbre = 254
+	};
 };
 
 class MT32Chan {
@@ -363,24 +367,36 @@ void IMuseChannel_MT32::allNotesOff() {
 	}
 }
 
-void IMuseChannel_MT32::sysEx_customInstrument(uint32 type, const byte *instr)  {
-	if (*instr++ != 0x41) {
-		warning("IMuseChannel_MT32::sysEx_customInstrument(): Invalid (non-Roland) sysex message received");
+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 (!(addr & 0xFFFF) || partNo < 16) {
-		sendSysexTimbreData(instr + 6, 246);
-		_timbre = 0xFF;
-		byte msg[2] = { 0x02, _program };
-		sendSysexPatchData(0, msg, sizeof(msg));
-		if (_out)
-			sendMidi(0xC0 | _out->_number, _program, 0);
+	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 | _out->_number, _program, 0);
+		} else {
+			_drv->sendMT32Sysex(0x22000 + (partNo << 8), instr + 6, 246);
+		}
 	} else {
-		_drv->sendMT32Sysex(0x22000 + (partNo << 8), instr + 6, 246);
+		// 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");
 	}
 }
 
diff --git a/engines/scumm/imuse/drivers/pcspk.cpp b/engines/scumm/imuse/drivers/pcspk.cpp
index 546d674321a..333ed30de5e 100644
--- a/engines/scumm/imuse/drivers/pcspk.cpp
+++ b/engines/scumm/imuse/drivers/pcspk.cpp
@@ -336,8 +336,11 @@ void IMuseDriver_PCSpk::MidiChannel_PcSpk::priority(byte value) {
 	_priority = value;
 }
 
-void IMuseDriver_PCSpk::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) {
-	memcpy(_instrument, instr, sizeof(_instrument));
+void IMuseDriver_PCSpk::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) {
+	if (type == 'SPK ' && instr && dataSize == sizeof(_instrument))
+		memcpy(_instrument, instr, sizeof(_instrument));
+	else if (type != 'SPK ')
+		warning("MidiChannel_PcSpk: 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);
 }
 
 uint8 IMuseDriver_PCSpk::getEffectModifier(uint16 level) {
diff --git a/engines/scumm/imuse/drivers/pcspk.h b/engines/scumm/imuse/drivers/pcspk.h
index bad9f1269ff..979bdf32567 100644
--- a/engines/scumm/imuse/drivers/pcspk.h
+++ b/engines/scumm/imuse/drivers/pcspk.h
@@ -117,7 +117,7 @@ private:
 		void controlChange(byte control, byte value) override;
 		void pitchBendFactor(byte value) override;
 		void priority(byte value) override;
-		void sysEx_customInstrument(uint32 type, const byte *instr) override;
+		void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override;
 
 		bool allocate();
 
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index 57123698a23..d96ca2dac18 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -442,7 +442,7 @@ void Instrument_AdLib::saveLoadWithSerializer(Common::Serializer &s) {
 }
 
 void Instrument_AdLib::send(MidiChannel *mc) {
-	mc->sysEx_customInstrument('ADL ', (byte *)&_instrument);
+	mc->sysEx_customInstrument('ADL ', (byte *)&_instrument, sizeof(_instrument));
 }
 
 ////////////////////////////////////////
@@ -483,7 +483,7 @@ void Instrument_Roland::saveLoadWithSerializer(Common::Serializer &s) {
 
 void Instrument_Roland::send(MidiChannel *mc) {
 	if (_nativeMT32Device) {
-		mc->sysEx_customInstrument(0, (byte *)&_instrument);
+		mc->sysEx_customInstrument('ROL ', (byte *)&_instrument, sizeof(_instrument));
 	} else {
 		// Convert to a GM program change.
 		byte program = getEquivalentGM();
@@ -523,7 +523,7 @@ void Instrument_PcSpk::saveLoadWithSerializer(Common::Serializer &s) {
 }
 
 void Instrument_PcSpk::send(MidiChannel *mc) {
-	mc->sysEx_customInstrument('SPK ', (byte *)&_instrument);
+	mc->sysEx_customInstrument('SPK ', (byte *)&_instrument, sizeof(_instrument));
 }
 
 ////////////////////////////////////////
@@ -554,6 +554,6 @@ void Instrument_MacSfx::send(MidiChannel *mc) {
 	if (_program > 127) {
 		return;
 	}
-	mc->sysEx_customInstrument('MAC ', &_program);
+	mc->sysEx_customInstrument('MAC ', &_program, sizeof(_program));
 }
 } // End of namespace Scumm


Commit: ead51cb45e93c27bce0c2c6b6608c13d7f1ea527
    https://github.com/scummvm/scummvm/commit/ead51cb45e93c27bce0c2c6b6608c13d7f1ea527
Author: athrxx (athrxx at scummvm.org)
Date: 2022-10-06T18:47:51+02:00

Commit Message:
SCUMM: (IMS) - add general midi driver

This is the GM counterpart for the MT32 driver, mostly based on
SAMNMAX, but I have also added the necessary things from the
DOTT driver.

Changed paths:
    engines/scumm/imuse/drivers/gmidi.cpp
    engines/scumm/imuse/drivers/gmidi.h


diff --git a/engines/scumm/imuse/drivers/gmidi.cpp b/engines/scumm/imuse/drivers/gmidi.cpp
index d6e8749ab0e..3a8b769029c 100644
--- a/engines/scumm/imuse/drivers/gmidi.cpp
+++ b/engines/scumm/imuse/drivers/gmidi.cpp
@@ -23,14 +23,328 @@
 #include "common/system.h"
 #include "scumm/imuse/drivers/gmidi.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 Scumm {
 
-IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode), _newSystem(newSystem) {
+class IMuseChannel_GMidi : public MidiChannel {
+public:
+	IMuseChannel_GMidi(IMuseDriver_GMidi *drv, int number);
+	~IMuseChannel_GMidi() override {}
+
+	MidiDriver *device() override { return _drv; }
+	byte getNumber() override {	return _number; }
+
+	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;
+	void programChange(byte program) override;
+	void pitchBend(int16 bend) override;
+
+	// Control Change and SCUMM specific functions
+	void pitchBendFactor(byte value) override { _pitchBendSensitivity = value; }
+	void transpose(int8 value) override { _transpose = value; }
+	void detune(byte value) override { _detune = value; }
+	void priority(byte value) override { _prio = value; }
+	void sustain(bool value) override;
+	void allNotesOff() override;
+	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override {}
+
+private:
+	void noteOffIntern(byte note);
+	void noteOnIntern(byte note, byte velocity);
+
+	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); }
+
+	IMuseDriver_GMidi *_drv;
+	const byte _number;
+	const bool _newSystem;
+	bool _allocated;
+
+	byte _polyphony;
+	byte _channelUsage;
+	bool _exhaust;
+	byte _prio;
+	int8 _detune;
+	int8 _transpose;
+	byte _pitchBendSensitivity;
+	int16 _pitchBend;
+	bool _sustain;
+
+	GMidiControlChan *&_idleChain;
+	GMidiControlChan *&_activeChain;
+};
+
+class GMidiControlChan {
+public:
+	GMidiControlChan() : _prev(nullptr), _next(nullptr), _in(nullptr), _number(0), _note(0) {}
+	GMidiControlChan *_prev;
+	GMidiControlChan *_next;
+	IMuseChannel_GMidi *_in;
+	byte _number;
+	byte _note;
+};
+
+void connect(GMidiControlChan *&chain, GMidiControlChan *node) {
+	if (!node || node->_prev || node->_next)
+		return;
+	if ((node->_next = chain))
+		chain->_prev = node;
+	chain = node;
+}
+
+void disconnect(GMidiControlChan *&chain, GMidiControlChan *node) {
+	if (!node || !chain)
+		return;
+
+	const GMidiControlChan *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;
+}
+
+#define sendMidi(stat, par1, par2)	_drv->send(((par2) << 16) | ((par1) << 8) | (stat))
+
+IMuseChannel_GMidi::IMuseChannel_GMidi(IMuseDriver_GMidi *drv, int number) :MidiChannel(), _drv(drv), _number(number), _allocated(false), _sustain(false),
+	_pitchBend(0x2000), _polyphony(1), _channelUsage(0), _exhaust(false), _prio(0), _detune(0), _transpose(0),
+	_pitchBendSensitivity(2), _idleChain(_drv->_idleChain), _activeChain(_drv->_activeChain), _newSystem(_drv->_newSystem) {
+	assert(_drv);
+}
+
+bool IMuseChannel_GMidi::allocate() {
+	if (_allocated)
+		return false;
+
+	if (!_newSystem) {
+		_prio = 0x80;
+	}
+
+	return (_allocated = true);
+}
+
+void IMuseChannel_GMidi::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 | _number, note, 0x40);
+#endif
+}
+
+void IMuseChannel_GMidi::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 | _number, note, velocity);
+#endif
+}
+
+void IMuseChannel_GMidi::controlChange(byte control, byte value)  {
+	switch (control) {
+	case 1:
+	case 7:
+	case 10:
+	case 91:
+	case 93:
+		sendMidi(0xB0 | _number, 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 driver does not pass through "blindly". The
+		// only controls that get sent are 1, 7, 10, 91 and 93.
+		warning("Unhandled Control: %d", control);
+		break;
+	}
+}
+
+void IMuseChannel_GMidi::programChange(byte program)  {
+	sendMidi(0xC0 | _number, program, 0);
+}
+
+void IMuseChannel_GMidi::pitchBend(int16 bend)  {
+	bend = bend + 0x2000;
+	sendMidi(0xE0 | _number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
+}
+
+void IMuseChannel_GMidi::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 | _number, 0x40, value);
+	}
+}
+
+void IMuseChannel_GMidi::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 | _number, 0x7B, 0);
+	}
+}
+
+void IMuseChannel_GMidi::noteOffIntern(byte note) {
+	if (_activeChain == nullptr)
+		return;
+
+	GMidiControlChan *node = nullptr;
+	for (GMidiControlChan *i = _activeChain; i; i = i->_next) {
+		if (i->_number == _number && i->_note == note) {
+			node = i;
+			break;
+		}
+	}
+
+	if (!node)
+		return;
+
+	sendMidi(0x80 | _number, note, 0x40);
+
+	if (_newSystem)
+		_exhaust = (--_channelUsage > _polyphony);
+
+	disconnect(_activeChain, node);
+	connect(_idleChain, node);
+}
+
+void IMuseChannel_GMidi::noteOnIntern(byte note, byte velocity) {
+	GMidiControlChan *node = nullptr;
+
+	if (_idleChain) {
+		node = _idleChain;
+		disconnect(_idleChain, node);
+	} else {
+		IMuseChannel_GMidi *best = this;
+		for (GMidiControlChan *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_GMidi *prt = _drv->_imsParts[node->_number];
+		if (prt) {
+			sendMidi(0x80 | prt->_number, node->_note, 0x40);
+			if (_newSystem)
+				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);
+
+	sendMidi(0x90 | _number, note, velocity);
+}
+
+#undef sendMidi
+
+IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode), _imsParts(nullptr),
+	_newSystem(newSystem), _numChannels(16), _notesPlaying(nullptr), _notesSustained(nullptr),  _controlChan(nullptr), _idleChain(nullptr), _activeChain(nullptr) {
 	_drv = MidiDriver::createMidi(dev);
 	assert(_drv);
 }
 
 IMuseDriver_GMidi::~IMuseDriver_GMidi() {
+	close();
 	delete _drv;
 }
 
@@ -42,28 +356,37 @@ int IMuseDriver_GMidi::open() {
 	if (res)
 		return res;
 
+	createChannels();
+
 	if (_gsMode)
 		initDeviceAsRolandGS();
 	else
-		initDevice();
+		initDevice();	
 
 	return res;
 }
 
 void IMuseDriver_GMidi::close() {
-	if (isOpen() && _drv)
+	if (isOpen() && _drv) {
+		for (int i = 0; i < 16; ++i) {
+			send(0x0040B0 | i);
+			send(0x007BB0 | i);
+		}
 		_drv->close();
+	}
 
-	//releaseChannels();
+	releaseChannels();
 }
 
 MidiChannel *IMuseDriver_GMidi::allocateChannel() {
 	if (!isOpen())
 		return nullptr;
 
-	// Pass through everything for now.
-	//if (!_newSystem)
-		return _drv->allocateChannel();
+	for (int i = 0; i < _numChannels; ++i) {
+		IMuseChannel_GMidi *ch = _imsParts[i];
+		if (ch && i != 9 && ch->allocate())
+			return ch;
+	}
 
 	return nullptr;
 }
@@ -72,20 +395,13 @@ MidiChannel *IMuseDriver_GMidi::getPercussionChannel() {
 	if (!isOpen())
 		return nullptr;
 
-	// Pass through everything for now.
-	//if (!_newSystem)
-		return _drv->getPercussionChannel();
-
-	return nullptr;
+	return _imsParts[9];
 }
 
 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.
+	// 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);
@@ -209,4 +525,50 @@ void IMuseDriver_GMidi::initDeviceAsRolandGS() {
 	debug(2, "GS SysEx: Reverb Time is 106");
 }
 
+void IMuseDriver_GMidi::createChannels() {
+	releaseChannels();
+
+	_imsParts = new IMuseChannel_GMidi*[_numChannels];
+	_controlChan = new GMidiControlChan*[12];
+
+	assert(_imsParts);
+	assert(_controlChan);
+
+	for (int i = 0; i < _numChannels; ++i)
+		_imsParts[i] = new IMuseChannel_GMidi(this, i);
+
+	for (int i = 0; i < 12; ++i) {
+		_controlChan[i] = new GMidiControlChan();
+		connect(_idleChain, _controlChan[i]);
+	}
+
+	if (_newSystem) {
+		_notesPlaying = new uint16[128]();
+		_notesSustained = new uint16[128]();
+	}
+}
+
+void IMuseDriver_GMidi::releaseChannels() {
+	if (_imsParts) {
+		for (int i = 0; i < _numChannels; ++i)
+			delete _imsParts[i];
+		delete[] _imsParts;
+		_imsParts = nullptr;
+	}
+
+	if (_controlChan) {
+		for (int i = 0; i < 12; ++i)
+			delete _controlChan[i];
+		delete[] _controlChan;
+		_controlChan = nullptr;
+	}
+
+	delete[] _notesPlaying;
+	_notesPlaying = nullptr;
+	delete[] _notesSustained;
+	_notesSustained = nullptr;
+}
+
+#undef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
+
 } // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/gmidi.h b/engines/scumm/imuse/drivers/gmidi.h
index 7f7dca52a5a..d2186b4725f 100644
--- a/engines/scumm/imuse/drivers/gmidi.h
+++ b/engines/scumm/imuse/drivers/gmidi.h
@@ -26,7 +26,11 @@
 
 namespace Scumm {
 
+class IMuseChannel_GMidi;
+class GMidiControlChan;
+
 class IMuseDriver_GMidi : public MidiDriver {
+	friend class IMuseChannel_GMidi;
 public:
 	IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem);
 	~IMuseDriver_GMidi() override;
@@ -41,6 +45,7 @@ public:
 	uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
 	void send(uint32 b) override { if (_drv) _drv->send(b); };
 	void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } 
+	void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
 
 	// Channel allocation functions
 	MidiChannel *allocateChannel() override;
@@ -49,10 +54,29 @@ public:
 private:
 	void initDevice();
 	void initDeviceAsRolandGS();
+	void createChannels();
+	void releaseChannels();
+
+	void setNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] |= (1 << chan); }
+	void clearNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] &= ~(1 << chan); }
+	bool queryNoteFlag(byte chan, byte note) const { return (_notesPlaying && chan < 16 && note < 128) ? _notesPlaying[note] & (1 << chan) : false; }
+	void setSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] |= (1 << chan); }
+	void clearSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] &= ~(1 << chan); }
+	bool querySustainFlag(byte chan, byte note) const { return (_notesSustained && chan < 16 && note < 128) ? _notesSustained[note] & (1 << chan) : false; }
 
 	MidiDriver *_drv;
-	const bool _gsMode;
 	const bool _newSystem;
+	const bool _gsMode;
+	const byte _numChannels;
+
+	IMuseChannel_GMidi **_imsParts;
+	GMidiControlChan **_controlChan;
+
+	GMidiControlChan *_idleChain;
+	GMidiControlChan *_activeChain;
+
+	uint16 *_notesPlaying;
+	uint16 *_notesSustained;
 };
 
 } // End of namespace Scumm


Commit: caa96a50f0c8337f43fdd5f6c80c304d7a7bf454
    https://github.com/scummvm/scummvm/commit/caa96a50f0c8337f43fdd5f6c80c304d7a7bf454
Author: athrxx (athrxx at scummvm.org)
Date: 2022-10-06T18:47:55+02:00

Commit Message:
SCUMM: (IMS) - refactor MT32/GM drivers

The MT32 driver could be based on the GM driver for the
most parts. Also minor fixes and cleanup.

Changed paths:
  A engines/scumm/imuse/drivers/midi.cpp
  A engines/scumm/imuse/drivers/midi.h
  R engines/scumm/imuse/drivers/gmidi.cpp
  R engines/scumm/imuse/drivers/gmidi.h
  R engines/scumm/imuse/drivers/mt32.cpp
  R engines/scumm/imuse/drivers/mt32.h
    engines/scumm/module.mk
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/imuse/drivers/gmidi.cpp b/engines/scumm/imuse/drivers/gmidi.cpp
deleted file mode 100644
index 3a8b769029c..00000000000
--- a/engines/scumm/imuse/drivers/gmidi.cpp
+++ /dev/null
@@ -1,574 +0,0 @@
-/* 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "common/debug.h"
-#include "common/system.h"
-#include "scumm/imuse/drivers/gmidi.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 Scumm {
-
-class IMuseChannel_GMidi : public MidiChannel {
-public:
-	IMuseChannel_GMidi(IMuseDriver_GMidi *drv, int number);
-	~IMuseChannel_GMidi() override {}
-
-	MidiDriver *device() override { return _drv; }
-	byte getNumber() override {	return _number; }
-
-	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;
-	void programChange(byte program) override;
-	void pitchBend(int16 bend) override;
-
-	// Control Change and SCUMM specific functions
-	void pitchBendFactor(byte value) override { _pitchBendSensitivity = value; }
-	void transpose(int8 value) override { _transpose = value; }
-	void detune(byte value) override { _detune = value; }
-	void priority(byte value) override { _prio = value; }
-	void sustain(bool value) override;
-	void allNotesOff() override;
-	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override {}
-
-private:
-	void noteOffIntern(byte note);
-	void noteOnIntern(byte note, byte velocity);
-
-	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); }
-
-	IMuseDriver_GMidi *_drv;
-	const byte _number;
-	const bool _newSystem;
-	bool _allocated;
-
-	byte _polyphony;
-	byte _channelUsage;
-	bool _exhaust;
-	byte _prio;
-	int8 _detune;
-	int8 _transpose;
-	byte _pitchBendSensitivity;
-	int16 _pitchBend;
-	bool _sustain;
-
-	GMidiControlChan *&_idleChain;
-	GMidiControlChan *&_activeChain;
-};
-
-class GMidiControlChan {
-public:
-	GMidiControlChan() : _prev(nullptr), _next(nullptr), _in(nullptr), _number(0), _note(0) {}
-	GMidiControlChan *_prev;
-	GMidiControlChan *_next;
-	IMuseChannel_GMidi *_in;
-	byte _number;
-	byte _note;
-};
-
-void connect(GMidiControlChan *&chain, GMidiControlChan *node) {
-	if (!node || node->_prev || node->_next)
-		return;
-	if ((node->_next = chain))
-		chain->_prev = node;
-	chain = node;
-}
-
-void disconnect(GMidiControlChan *&chain, GMidiControlChan *node) {
-	if (!node || !chain)
-		return;
-
-	const GMidiControlChan *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;
-}
-
-#define sendMidi(stat, par1, par2)	_drv->send(((par2) << 16) | ((par1) << 8) | (stat))
-
-IMuseChannel_GMidi::IMuseChannel_GMidi(IMuseDriver_GMidi *drv, int number) :MidiChannel(), _drv(drv), _number(number), _allocated(false), _sustain(false),
-	_pitchBend(0x2000), _polyphony(1), _channelUsage(0), _exhaust(false), _prio(0), _detune(0), _transpose(0),
-	_pitchBendSensitivity(2), _idleChain(_drv->_idleChain), _activeChain(_drv->_activeChain), _newSystem(_drv->_newSystem) {
-	assert(_drv);
-}
-
-bool IMuseChannel_GMidi::allocate() {
-	if (_allocated)
-		return false;
-
-	if (!_newSystem) {
-		_prio = 0x80;
-	}
-
-	return (_allocated = true);
-}
-
-void IMuseChannel_GMidi::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 | _number, note, 0x40);
-#endif
-}
-
-void IMuseChannel_GMidi::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 | _number, note, velocity);
-#endif
-}
-
-void IMuseChannel_GMidi::controlChange(byte control, byte value)  {
-	switch (control) {
-	case 1:
-	case 7:
-	case 10:
-	case 91:
-	case 93:
-		sendMidi(0xB0 | _number, 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 driver does not pass through "blindly". The
-		// only controls that get sent are 1, 7, 10, 91 and 93.
-		warning("Unhandled Control: %d", control);
-		break;
-	}
-}
-
-void IMuseChannel_GMidi::programChange(byte program)  {
-	sendMidi(0xC0 | _number, program, 0);
-}
-
-void IMuseChannel_GMidi::pitchBend(int16 bend)  {
-	bend = bend + 0x2000;
-	sendMidi(0xE0 | _number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
-}
-
-void IMuseChannel_GMidi::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 | _number, 0x40, value);
-	}
-}
-
-void IMuseChannel_GMidi::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 | _number, 0x7B, 0);
-	}
-}
-
-void IMuseChannel_GMidi::noteOffIntern(byte note) {
-	if (_activeChain == nullptr)
-		return;
-
-	GMidiControlChan *node = nullptr;
-	for (GMidiControlChan *i = _activeChain; i; i = i->_next) {
-		if (i->_number == _number && i->_note == note) {
-			node = i;
-			break;
-		}
-	}
-
-	if (!node)
-		return;
-
-	sendMidi(0x80 | _number, note, 0x40);
-
-	if (_newSystem)
-		_exhaust = (--_channelUsage > _polyphony);
-
-	disconnect(_activeChain, node);
-	connect(_idleChain, node);
-}
-
-void IMuseChannel_GMidi::noteOnIntern(byte note, byte velocity) {
-	GMidiControlChan *node = nullptr;
-
-	if (_idleChain) {
-		node = _idleChain;
-		disconnect(_idleChain, node);
-	} else {
-		IMuseChannel_GMidi *best = this;
-		for (GMidiControlChan *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_GMidi *prt = _drv->_imsParts[node->_number];
-		if (prt) {
-			sendMidi(0x80 | prt->_number, node->_note, 0x40);
-			if (_newSystem)
-				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);
-
-	sendMidi(0x90 | _number, note, velocity);
-}
-
-#undef sendMidi
-
-IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode), _imsParts(nullptr),
-	_newSystem(newSystem), _numChannels(16), _notesPlaying(nullptr), _notesSustained(nullptr),  _controlChan(nullptr), _idleChain(nullptr), _activeChain(nullptr) {
-	_drv = MidiDriver::createMidi(dev);
-	assert(_drv);
-}
-
-IMuseDriver_GMidi::~IMuseDriver_GMidi() {
-	close();
-	delete _drv;
-}
-
-int IMuseDriver_GMidi::open() {
-	if (!_drv)
-		return MERR_CANNOT_CONNECT;
-
-	int res = _drv->open();
-	if (res)
-		return res;
-
-	createChannels();
-
-	if (_gsMode)
-		initDeviceAsRolandGS();
-	else
-		initDevice();	
-
-	return res;
-}
-
-void IMuseDriver_GMidi::close() {
-	if (isOpen() && _drv) {
-		for (int i = 0; i < 16; ++i) {
-			send(0x0040B0 | i);
-			send(0x007BB0 | i);
-		}
-		_drv->close();
-	}
-
-	releaseChannels();
-}
-
-MidiChannel *IMuseDriver_GMidi::allocateChannel() {
-	if (!isOpen())
-		return nullptr;
-
-	for (int i = 0; i < _numChannels; ++i) {
-		IMuseChannel_GMidi *ch = _imsParts[i];
-		if (ch && i != 9 && ch->allocate())
-			return ch;
-	}
-
-	return nullptr;
-}
-
-MidiChannel *IMuseDriver_GMidi::getPercussionChannel() {
-	if (!isOpen())
-		return nullptr;
-
-	return _imsParts[9];
-}
-
-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::initDeviceAsRolandGS() {
-	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::createChannels() {
-	releaseChannels();
-
-	_imsParts = new IMuseChannel_GMidi*[_numChannels];
-	_controlChan = new GMidiControlChan*[12];
-
-	assert(_imsParts);
-	assert(_controlChan);
-
-	for (int i = 0; i < _numChannels; ++i)
-		_imsParts[i] = new IMuseChannel_GMidi(this, i);
-
-	for (int i = 0; i < 12; ++i) {
-		_controlChan[i] = new GMidiControlChan();
-		connect(_idleChain, _controlChan[i]);
-	}
-
-	if (_newSystem) {
-		_notesPlaying = new uint16[128]();
-		_notesSustained = new uint16[128]();
-	}
-}
-
-void IMuseDriver_GMidi::releaseChannels() {
-	if (_imsParts) {
-		for (int i = 0; i < _numChannels; ++i)
-			delete _imsParts[i];
-		delete[] _imsParts;
-		_imsParts = nullptr;
-	}
-
-	if (_controlChan) {
-		for (int i = 0; i < 12; ++i)
-			delete _controlChan[i];
-		delete[] _controlChan;
-		_controlChan = nullptr;
-	}
-
-	delete[] _notesPlaying;
-	_notesPlaying = nullptr;
-	delete[] _notesSustained;
-	_notesSustained = nullptr;
-}
-
-#undef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
-
-} // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/gmidi.h b/engines/scumm/imuse/drivers/gmidi.h
deleted file mode 100644
index d2186b4725f..00000000000
--- a/engines/scumm/imuse/drivers/gmidi.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef SCUMM_IMUSE_DRV_GMIDI_H
-#define SCUMM_IMUSE_DRV_GMIDI_H
-
-#include "audio/mididrv.h"
-
-namespace Scumm {
-
-class IMuseChannel_GMidi;
-class GMidiControlChan;
-
-class IMuseDriver_GMidi : public MidiDriver {
-	friend class IMuseChannel_GMidi;
-public:
-	IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem);
-	~IMuseDriver_GMidi() override;
-
-	int open() override;
-	void close() override;
-
-	// Just pass these through...
-	bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
-	uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
-	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
-	uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
-	void send(uint32 b) override { if (_drv) _drv->send(b); };
-	void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } 
-	void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
-
-	// Channel allocation functions
-	MidiChannel *allocateChannel() override;
-	MidiChannel *getPercussionChannel() override;
-
-private:
-	void initDevice();
-	void initDeviceAsRolandGS();
-	void createChannels();
-	void releaseChannels();
-
-	void setNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] |= (1 << chan); }
-	void clearNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] &= ~(1 << chan); }
-	bool queryNoteFlag(byte chan, byte note) const { return (_notesPlaying && chan < 16 && note < 128) ? _notesPlaying[note] & (1 << chan) : false; }
-	void setSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] |= (1 << chan); }
-	void clearSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] &= ~(1 << chan); }
-	bool querySustainFlag(byte chan, byte note) const { return (_notesSustained && chan < 16 && note < 128) ? _notesSustained[note] & (1 << chan) : false; }
-
-	MidiDriver *_drv;
-	const bool _newSystem;
-	const bool _gsMode;
-	const byte _numChannels;
-
-	IMuseChannel_GMidi **_imsParts;
-	GMidiControlChan **_controlChan;
-
-	GMidiControlChan *_idleChain;
-	GMidiControlChan *_activeChain;
-
-	uint16 *_notesPlaying;
-	uint16 *_notesSustained;
-};
-
-} // End of namespace Scumm
-
-#endif
diff --git a/engines/scumm/imuse/drivers/midi.cpp b/engines/scumm/imuse/drivers/midi.cpp
new file mode 100644
index 00000000000..8fdda1b8141
--- /dev/null
+++ b/engines/scumm/imuse/drivers/midi.cpp
@@ -0,0 +1,963 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 Scumm {
+
+/*******************************
+*		General Midi driver
+********************************/
+
+struct ChannelNode;
+class IMuseChannel_Midi : public MidiChannel {
+public:
+	IMuseChannel_Midi(IMuseDriver_GMidi *drv, int number);
+	~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 { _pitchBendSensitivity = value; }
+	void transpose(int8 value) override { _transpose = value; }
+	void detune(byte value) override { _detune = value; }
+	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;
+	int8 _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(0),
+	_activeChain(drv ? _drv->_activeChain : _dummyNode), _idleChain(drv ? _drv->_idleChain : _dummyNode), _newSystem(drv ? drv->_newSystem : false) {
+	assert(_drv);
+	if (!_newSystem)
+		_pitchBendSensitivity = 2;
+}
+
+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)  {
+	_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);
+}
+
+IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode),
+	_imsParts(nullptr), _newSystem(newSystem), _numChannels(16), _notesPlaying(nullptr), _notesSustained(nullptr), _idleChain(nullptr), _activeChain(nullptr), _numVoices(12) {
+	_drv = MidiDriver::createMidi(dev);
+	assert(_drv);
+}
+
+IMuseDriver_GMidi::~IMuseDriver_GMidi() {
+	close();
+	delete _drv;
+}
+
+int IMuseDriver_GMidi::open() {
+	if (!_drv)
+		return MERR_CANNOT_CONNECT;
+
+	int res = _drv->open();
+	if (res)
+		return res;
+
+	createChannels();
+
+	if (_gsMode)
+		initDeviceAsRolandGS();
+	else
+		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);
+	if (ch) {
+		ch->release();
+		ch->allocate();
+	}
+
+	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::initDeviceAsRolandGS() {
+	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);
+	}
+}
+
+/**************************
+*		MT-32 driver
+***************************/
+
+class IMuseChannel_MT32 : public IMuseChannel_Midi {
+public:
+	IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number);
+	~IMuseChannel_MT32() override {}
+
+	bool allocate() override;
+
+	// 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 { 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) {
+		byte msg[] = { (byte)(_timbre >> 6), (byte)(_timbre & 0x3F), 0x18, 0x32, 0x10, 0x00, _reverbSwitch};
+		sendSysexPatchData(0, msg, sizeof(msg));
+		_program = _number;
+	}
+
+	return res;
+}
+
+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);
+}
+
+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;
+}
+
+void IMuseDriver_MT32::initDevice() {
+	// 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(250);
+
+	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(250);
+	}
+
+	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);
+	}
+
+	// Display a welcome message on MT-32 displays. Compute version string (truncated to 20 chars max.)
+	Common::String infoStr = Common::String("ScummVM ") + gScummVMVersion;
+	for (int i = (20 - infoStr.size()) >> 1; i > 0; --i)
+		infoStr = ' ' + infoStr + ' ';
+	sendMT32Sysex(0x80000, (const byte*)infoStr.c_str(), MIN<uint32>(infoStr.size(), 20));
+	g_system->delayMillis(1000);
+}
+
+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;
+
+	sysEx(msg, dst - msg);
+
+	delete[] msg;
+}
+
+#undef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/mt32.h b/engines/scumm/imuse/drivers/midi.h
similarity index 73%
rename from engines/scumm/imuse/drivers/mt32.h
rename to engines/scumm/imuse/drivers/midi.h
index 6f0bf32de78..64c97f6e448 100644
--- a/engines/scumm/imuse/drivers/mt32.h
+++ b/engines/scumm/imuse/drivers/midi.h
@@ -19,22 +19,22 @@
  *
  */
 
-#ifndef SCUMM_IMUSE_DRV_MT32_H
-#define SCUMM_IMUSE_DRV_MT32_H
+#ifndef SCUMM_IMUSE_DRV_GMIDI_H
+#define SCUMM_IMUSE_DRV_GMIDI_H
 
 #include "audio/mididrv.h"
 
 namespace Scumm {
 
+class IMuseChannel_Midi;
 class IMuseChannel_MT32;
-class MT32RealChan;
-class MT32ControlChan;
+struct ChannelNode;
 
-class IMuseDriver_MT32 : public MidiDriver {
-	friend class IMuseChannel_MT32;
+class IMuseDriver_GMidi : public MidiDriver {
+	friend class IMuseChannel_Midi;
 public:
-	IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem);
-	~IMuseDriver_MT32() override;
+	IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem);
+	virtual ~IMuseDriver_GMidi() override;
 
 	int open() override;
 	void close() override;
@@ -46,20 +46,28 @@ public:
 	uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
 	void send(uint32 b) override { if (_drv) _drv->send(b); };
 	void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } 
+	virtual void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
 
 	// Channel allocation functions
 	MidiChannel *allocateChannel() override;
 	MidiChannel *getPercussionChannel() override;
 
-private:
-	void initDevice();
-	void createChannels();
-	void releaseChannels();
+protected:
+	IMuseChannel_Midi *getPart(int number);
+	virtual void createChannels();
+	virtual void createParts();
+	virtual void releaseChannels();
 
-	IMuseChannel_MT32 *getPart(int number) const;
+	MidiDriver *_drv;
+	const bool _newSystem;
+	byte _numChannels;
+	byte _numVoices;
+	IMuseChannel_Midi **_imsParts;
 
-	// Convenience function that allows to send the sysex message with the exact same arguments as they are used in the original drivers.
-	void sendMT32Sysex(uint32 addr, const byte *data, uint32 dataSize);
+private:
+	virtual void initDevice();
+	void initDeviceAsRolandGS();
+	virtual void deinitDevice();
 
 	void setNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] |= (1 << chan); }
 	void clearNoteFlag(byte chan, byte note) { if (_notesPlaying && chan < 16 && note < 128) _notesPlaying[note] &= ~(1 << chan); }
@@ -68,24 +76,35 @@ private:
 	void clearSustainFlag(byte chan, byte note) { if (_notesSustained && chan < 16 && note < 128) _notesSustained[note] &= ~(1 << chan); }
 	bool querySustainFlag(byte chan, byte note) const { return (_notesSustained && chan < 16 && note < 128) ? _notesSustained[note] & (1 << chan) : false; }
 
-	MidiDriver *_drv;
-	const bool _newSystem;
-	const byte _numChannels;
-
-	IMuseChannel_MT32 **_imsParts;
-	MT32RealChan **_hwOutputChan;
-	MT32ControlChan **_controlChan;
-
-	MT32ControlChan *_idleChain;
-	MT32RealChan *_hwChain;
-	MT32ControlChan *_activeChain;
+	const bool _gsMode;
 
-	const byte *_programsMapping;
+	ChannelNode *_idleChain;
+	ChannelNode *_activeChain;
 
 	uint16 *_notesPlaying;
 	uint16 *_notesSustained;
 };
 
+class IMuseDriver_MT32 final : public IMuseDriver_GMidi {
+	friend class IMuseChannel_MT32;
+public:
+	IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem);
+	~IMuseDriver_MT32() override {}
+
+private:
+	void initDevice() override;
+	void createChannels() override;
+	void createParts() override;
+	void releaseChannels() override;
+
+	// Convenience function that allows to send the sysex message with the exact same arguments as they are used in the original drivers.
+	void sendMT32Sysex(uint32 addr, const byte *data, uint32 dataSize);
+
+	ChannelNode *_hwRealChain;
+
+	const byte *_programsMapping;
+};
+
 } // End of namespace Scumm
 
 #endif
diff --git a/engines/scumm/imuse/drivers/mt32.cpp b/engines/scumm/imuse/drivers/mt32.cpp
deleted file mode 100644
index d1e02040a55..00000000000
--- a/engines/scumm/imuse/drivers/mt32.cpp
+++ /dev/null
@@ -1,756 +0,0 @@
-/* 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "common/str.h"
-#include "common/system.h"
-#include "base/version.h"
-#include "scumm/imuse/drivers/mt32.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 Scumm {
-
-class IMuseChannel_MT32 : public MidiChannel {
-public:
-	IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number);
-	~IMuseChannel_MT32() override {}
-
-	MidiDriver *device() override { return _drv; }
-	byte getNumber() override {	return _number; }
-
-	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;
-	void programChange(byte program) override;
-	void pitchBend(int16 bend) override;
-
-	// Control Change and SCUMM specific functions
-	void volume(byte value) override;
-	void panPosition(byte value) override;
-	void pitchBendFactor(byte value) override { _pitchBendSensitivity = value; }
-	void transpose(int8 value) override { _transpose = value; }
-	void detune(byte value) override { _detune = value; }
-	void priority(byte value) override { _prio = value; }
-	void modulationWheel(byte value) override;
-	void sustain(bool value) override;
-	void effectLevel(byte value) override;
-	void chorusLevel(byte value) override {}
-	void allNotesOff() override;
-	void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override;
-
-	void setOutput(MT32RealChan *out) { _out = out; }
-
-private:
-	void noteOffIntern(byte note);
-	void noteOnIntern(byte note, byte velocity);
-
-	void sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const;
-	void sendSysexTimbreData(const byte *data, uint32 dataSize) const;
-
-	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); }
-
-	IMuseDriver_MT32 *_drv;
-	const byte _number;
-	const bool _newSystem;
-	bool _allocated;
-
-	MT32RealChan *_out;
-	byte _program;
-	byte _timbre;
-	byte _volume;
-	byte _panPos;
-	byte _polyphony;
-	byte _channelUsage;
-	bool _exhaust;
-	byte _prio;
-	int8 _detune;
-	int8 _transpose;
-	byte _reverbSwitch;
-	byte _pitchBendSensitivity;
-	int16 _pitchBend;
-	bool _sustain;
-
-	MT32ControlChan *&_idleChain;
-	MT32RealChan *&_availHwChain;
-	MT32ControlChan *&_activeChain;
-
-	const byte *_programsMapping;
-	const uint32 _sysexPatchAddrBase;
-	const uint32 _sysexTimbreAddrBase;
-
-	enum SysexMessageSize {
-		kSysexLengthTimbre = 254
-	};
-};
-
-class MT32Chan {
-public:
-	MT32Chan(int number, IMuseChannel_MT32 *in) : _prev(nullptr), _next(nullptr), _in(in), _number(number) {}
-	MT32Chan *_prev;
-	MT32Chan *_next;
-	IMuseChannel_MT32 *_in;
-	byte _number;
-};
-
-class MT32RealChan : public MT32Chan {
-public:
-	MT32RealChan(int number, IMuseChannel_MT32 *in, MidiChannel *out) : MT32Chan(number, in), _out(out), _sysexTempAddrBase(0xC000 + (number << 4)) {
-		assert(_out && _number == out->getNumber());
-		if (in)
-			in->setOutput(this);
-	}
-	MidiChannel *_out;
-	const uint32 _sysexTempAddrBase;
-};
-
-class MT32ControlChan : public MT32Chan{
-public:
-	MT32ControlChan() : MT32Chan(0, nullptr), _note(0) {}
-	byte _note;
-};
-
-template <typename MT32ChanTmpl>
-void connect(MT32ChanTmpl *&chain, MT32Chan *node) {
-	if (!node || node->_prev || node->_next)
-		return;
-	if ((node->_next = chain))
-		chain->_prev = node;
-	chain = static_cast<MT32ChanTmpl*>(node);
-}
-
-template <typename MT32ChanTmpl>
-void disconnect(MT32ChanTmpl *&chain, MT32Chan *node) {
-	if (!node || !chain)
-		return;
-
-	const MT32Chan *ch = static_cast<MT32Chan*>(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 = static_cast<MT32ChanTmpl*>(node->_next);
-
-	node->_next = node->_prev = nullptr;
-}
-
-#define sendMidi(stat, par1, par2)	_drv->send(((par2) << 16) | ((par1) << 8) | (stat))
-
-IMuseChannel_MT32::IMuseChannel_MT32(IMuseDriver_MT32 *drv, int number) :MidiChannel(), _drv(drv), _number(number), _allocated(false), _out(nullptr), _sustain(false),
-	_program(0), _timbre(0xFF),	_volume(0x7F), _panPos(0x40), _pitchBend(0x2000), _polyphony(1), _channelUsage(0), _exhaust(false), _prio(0), _detune(0), _transpose(0),
-	_pitchBendSensitivity(2), _reverbSwitch(1), _idleChain(_drv->_idleChain), _availHwChain(_drv->_hwChain), _activeChain(_drv->_activeChain),
-	_sysexPatchAddrBase(0x14000 + (number << 3)), _sysexTimbreAddrBase(0x20000 + (number << 8)), _programsMapping(_drv->_programsMapping), _newSystem(_drv->_newSystem) {
-	assert(_drv);
-}
-
-bool IMuseChannel_MT32::allocate() {
-	if (_allocated)
-		return false;
-
-	if (!_newSystem) {
-		byte msg[] = { (byte)(_timbre >> 6), (byte)(_timbre & 0x3F), 0x18, 0x32, 0x10, 0x00, _reverbSwitch};
-		sendSysexPatchData(0, msg, sizeof(msg));
-		_program = _number;
-		_prio = 0x80;
-	}
-
-	return (_allocated = true);
-}
-
-void IMuseChannel_MT32::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 if (_out || _number == 9)
-		sendMidi(0x80 | _number, note, 0x40);
-#endif
-}
-
-void IMuseChannel_MT32::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 if (_out) {
-		sendMidi(0x90 | _out->_number, note, velocity);
-	} else if (_number == 9) {
-		sendMidi(0xB9, 0x07, _volume);
-		sendMidi(0x99, note, velocity);
-	}
-#endif
-}
-
-void IMuseChannel_MT32::controlChange(byte control, byte value)  {
-	switch (control) {
-	case 7:
-		volume(value);
-		break;
-	case 10:
-		panPosition(value);
-		break;
-	case 17:
-		if (_newSystem)
-			_polyphony = value;
-		else
-			detune(value);
-		break;
-	case 18:
-		priority(value);
-		break;
-	case 123:
-		allNotesOff();
-		break;
-	default:
-		warning("Unhandled Control: %d", control);
-		break;
-	}
-}
-
-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 (_out && _program < 128)
-		sendMidi(0xC0 | _out->_number, _program, 0);
-}
-
-void IMuseChannel_MT32::pitchBend(int16 bend)  {
-	_pitchBend = bend + 0x2000;
-	if (_out)
-		sendMidi(0xE0 | _out->_number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
-}
-
-void IMuseChannel_MT32::volume(byte value) {
-	_volume = value;
-	if (_out)
-		sendMidi(0xB0 | _out->_number, 0x07, value);
-}
-
-void IMuseChannel_MT32::panPosition(byte value) {
-	_panPos = value;
-	if (_out)
-		sendMidi(0xB0 | _out->_number, 0x0A, value);
-}
-
-void IMuseChannel_MT32::modulationWheel(byte value) {
-	if (_out && !_newSystem)
-		sendMidi(0xB0 | _out->_number, 0x01, value);
-}
-
-void IMuseChannel_MT32::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 if (_out) {
-		sendMidi(0xB0 | _out->_number, 0x40, 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)
-		_drv->sendMT32Sysex(_out->_sysexTempAddrBase + 6, &_reverbSwitch, 1);
-}
-
-void IMuseChannel_MT32::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 if (_out) {
-		sendMidi(0xB0 | _out->_number, 0x7B, 0);
-	}
-}
-
-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 | _out->_number, _program, 0);
-		} else {
-			_drv->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::noteOffIntern(byte note) {
-	if (_program > 127 || _activeChain == nullptr)
-		return;
-
-	MT32Chan *node = nullptr;
-	for (MT32ControlChan *i = _activeChain; i; i = static_cast<MT32ControlChan*>(i->_next)) {
-		if (i->_number == _number && i->_note == note) {
-			node = i;
-			break;
-		}
-	}
-
-	if (!node)
-		return;
-
-	if (_out)
-		sendMidi(0x80 | _out->_number, note, 0x40);
-	else
-		sendMidi(0x89, note, 0x40);
-
-	if (_newSystem)
-		_exhaust = (--_channelUsage > _polyphony);
-
-	disconnect(_activeChain, node);
-	connect(_idleChain, node);
-}
-
-void IMuseChannel_MT32::noteOnIntern(byte note, byte velocity) {
-	if (_program > 127 || (_newSystem && _number == 9 && note > 75))
-		return;
-
-	MT32ControlChan *node = nullptr;
-
-	if (_idleChain) {
-		node = _idleChain;
-		disconnect(_idleChain, node);
-	} else {
-		MT32Chan *foundChan = nullptr;
-		IMuseChannel_MT32 *best = this;
-		for (MT32Chan *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;
-				foundChan = i;
-			}
-		}
-
-		if (!foundChan)
-			return;
-
-		node = static_cast<MT32ControlChan*>(foundChan);
-
-		IMuseChannel_MT32 *prt = _drv->getPart(node->_number);
-		if (prt && prt->_out)
-			sendMidi(0x80 | prt->_out->_number, node->_note, 0x40);
-		else if (node->_number == 9)
-			sendMidi(0x89, 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);
-
-	if (_number == 9) {
-		sendMidi(0xB9, 0x07, _volume);
-		sendMidi(0x99, note, velocity);
-		return;
-	}
-
-	if (!_out) {
-		MT32RealChan *nodeReal = _availHwChain;
-		while (nodeReal && nodeReal->_next)
-			nodeReal = static_cast<MT32RealChan*>(nodeReal->_next);
-
-		assert(nodeReal);
-		assert(nodeReal->_in);
-
-		nodeReal->_in->_out = nullptr;
-		nodeReal->_in = this;
-		_out = nodeReal;
-
-		sendMidi(0xB0 | _out->_number, 0x7B, 0);
-		sendMidi(0xB0 | _out->_number, 0x07, _volume);
-		sendMidi(0xB0 | _out->_number, 0x0A, _panPos);
-		sendMidi(0xC0 | _out->_number, _program, 0);
-		sendMidi(0xE0 | _out->_number, _pitchBend & 0x7F, (_pitchBend >> 7) & 0x7F);
-	}
-
-	disconnect(_availHwChain, _out);
-	connect(_availHwChain, _out);
-
-	sendMidi(0x90 | _out->_number, note, velocity);
-}
-
-void IMuseChannel_MT32::sendSysexPatchData(byte offset, const byte *data, uint32 dataSize) const {
-	assert(!_newSystem);
-	_drv->sendMT32Sysex(_sysexPatchAddrBase + offset, data, dataSize);
-}
-
-void IMuseChannel_MT32::sendSysexTimbreData(const byte *data, uint32 dataSize) const {
-	assert(!_newSystem);
-	_drv->sendMT32Sysex(_sysexTimbreAddrBase, data, dataSize);
-}
-
-#undef sendMidi
-
-IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _programsMapping(nullptr), _notesPlaying(nullptr),
-	_notesSustained(nullptr), _drv(nullptr), _imsParts(nullptr), _hwOutputChan(nullptr), _controlChan(nullptr), _idleChain(nullptr), _hwChain(nullptr), _activeChain(nullptr),
-#ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
-	_numChannels(16) {
-#else
-	_numChannels(newSystem ? 16 : 9) {
-#endif
-	_drv = MidiDriver::createMidi(dev);
-	assert(_drv);
-
-	_drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
-
-	if (newSystem)
-		_programsMapping = MidiDriver::_gmToMt32;
-}
-
-IMuseDriver_MT32::~IMuseDriver_MT32() {
-	close();
-	delete _drv;
-}
-
-int IMuseDriver_MT32::open() {
-	if (!_drv)
-		return MERR_CANNOT_CONNECT;
-
-	int res = _drv->open();
-	if (res)
-		return res;
-
-	initDevice();
-	createChannels();
-
-	return res;
-}
-
-void IMuseDriver_MT32::close() {
-	if (isOpen()) {
-		// Reset the MT-32
-		sendMT32Sysex(0x1FC000, 0, 0);
-		g_system->delayMillis(250);
-
-		if (_drv)
-			_drv->close();
-	}
-
-	releaseChannels();
-}
-
-MidiChannel *IMuseDriver_MT32::allocateChannel() {
-	if (!isOpen())
-		return nullptr;
-
-	for (int i = 0; i < _numChannels; ++i) {
-		IMuseChannel_MT32 *ch = _imsParts[i];
-		if (ch && ch->getNumber() != 9 && ch->allocate())
-			return ch;
-	}
-
-	return nullptr;
-}
-
-MidiChannel *IMuseDriver_MT32::getPercussionChannel() {
-	if (!isOpen())
-		return nullptr;
-
-	IMuseChannel_MT32 *ch = getPart(9);
-	if (ch) {
-		ch->release();
-		ch->allocate();
-	}
-
-	return ch;
-}
-
-void IMuseDriver_MT32::initDevice() {
-	// 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(250);
-
-	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(250);
-	}
-
-	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);
-	}
-
-	// Compute version string (truncated to 20 chars max.)
-	Common::String infoStr = "ScummVM ";
-	infoStr += gScummVMVersion;
-	int len = infoStr.size();
-	if (len > 20)
-		len = 20;
-
-	// Display a welcome message on MT-32 displays.
-	byte buffer[28];
-	memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
-	memcpy(&buffer[7], "                    ", 20);
-	memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len);
-	byte checksum = 0;
-	for (int i = 4; i < 27; ++i)
-		checksum -= buffer[i];
-	buffer[27] = checksum & 0x7F;
-	sysEx(buffer, 28);
-	g_system->delayMillis(1000);
-}
-
-void IMuseDriver_MT32::createChannels() {
-	releaseChannels();
-
-	_imsParts = new IMuseChannel_MT32*[_numChannels];
-	_hwOutputChan = new MT32RealChan*[8];
-	_controlChan = new MT32ControlChan*[9];
-
-	assert(_imsParts);
-	assert(_hwOutputChan);
-	assert(_controlChan);
-
-	for (int i = 0; i < _numChannels; ++i)
-		_imsParts[i] = new IMuseChannel_MT32(this, (i + 1) & 0x0F);
-
-	MidiChannel *driverChannels[16];
-	memset(driverChannels, 0, sizeof(driverChannels));
-	for (int i = 0; i < ARRAYSIZE(driverChannels); ++i)
-		driverChannels[i] = _drv->allocateChannel();
-
-	for (int i = 1; i < 9; ++i) {
-		MidiChannel *m = nullptr;
-		for (int ii = 0; m == nullptr && ii < ARRAYSIZE(driverChannels); ++ii) {
-			if (driverChannels[ii] && driverChannels[ii]->getNumber() == i)
-				SWAP(m, driverChannels[ii]);
-		}
-		if (!m)
-			error("IMuseDriver_MT32::createChannels(): Failed to create channels.");
-		_hwOutputChan[i - 1] = new MT32RealChan(i, getPart(i), m);
-		connect(_hwChain, _hwOutputChan[i - 1]);
-	}
-
-	for (int i = 0; i < ARRAYSIZE(driverChannels); ++i) {
-		if (driverChannels[i])
-			driverChannels[i]->release();
-	}
-
-	for (int i = 0; i < 9; ++i) {
-		_controlChan[i] = new MT32ControlChan();
-		connect(_idleChain, _controlChan[i]);
-	}
-
-	if (_newSystem) {
-		_notesPlaying = new uint16[128]();
-		_notesSustained = new uint16[128]();
-	}
-}
-
-void IMuseDriver_MT32::releaseChannels() {
-	if (_imsParts) {
-		for (int i = 0; i < _numChannels; ++i)
-			delete _imsParts[i];
-		delete[] _imsParts;
-		_imsParts = nullptr;
-	}
-
-	if (_hwOutputChan) {
-		for (int i = 0; i < 8; ++i)
-			delete _hwOutputChan[i];
-		delete[] _hwOutputChan;
-		_hwOutputChan = nullptr;
-	}
-
-	if (_controlChan) {
-		for (int i = 0; i < 9; ++i)
-			delete _controlChan[i];
-		delete[] _controlChan;
-		_controlChan = nullptr;
-	}
-
-	delete[] _notesPlaying;
-	_notesPlaying = nullptr;
-	delete[] _notesSustained;
-	_notesSustained = nullptr;
-}
-
-IMuseChannel_MT32 *IMuseDriver_MT32::getPart(int number) const {
-	for (int i = 0; i < _numChannels; ++i)
-		if (_imsParts[i]->getNumber() == number)
-			return _imsParts[i];
-	return nullptr;
-}
-
-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;
-
-	sysEx(msg, dst - msg);
-
-	delete[] msg;
-}
-
-#undef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
-
-} // End of namespace Scumm
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 49f12e3e241..7ed34a42082 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -33,9 +33,8 @@ MODULE_OBJS := \
 	imuse/sysex_scumm.o \
 	imuse/drivers/amiga.o \
 	imuse/drivers/fmtowns.o \
-	imuse/drivers/gmidi.o \
+	imuse/drivers/midi.o \
 	imuse/drivers/mac_m68k.o \
-	imuse/drivers/mt32.o \
 	imuse/drivers/pcspk.o \
 	input.o \
 	ks_check.o \
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index e6c463a83a8..866cd98adeb 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -81,8 +81,7 @@
 #include "scumm/imuse/drivers/mac_m68k.h"
 #include "scumm/imuse/drivers/amiga.h"
 #include "scumm/imuse/drivers/fmtowns.h"
-#include "scumm/imuse/drivers/gmidi.h"
-#include "scumm/imuse/drivers/mt32.h"
+#include "scumm/imuse/drivers/midi.h"
 #include "scumm/detection_steam.h"
 
 #include "backends/audiocd/audiocd.h"


Commit: 70c2d07f198461d96bdf727495295a2590ae785e
    https://github.com/scummvm/scummvm/commit/70c2d07f198461d96bdf727495295a2590ae785e
Author: athrxx (athrxx at scummvm.org)
Date: 2022-10-06T18:47:56+02:00

Commit Message:
SCUMM: (IMS) - minor fixes

Changed paths:
    engines/scumm/imuse/imuse_part.cpp
    engines/scumm/imuse/imuse_player.cpp


diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 7a0a674d4d5..7882a5a015c 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -310,7 +310,8 @@ void Part::setup(Player *player) {
 	_transpose = 0;
 	_detune = 0;
 	_detune_eff = player->getDetune();
-	_pitchbend_factor = 2;
+	_pitchbend_factor = _se->_newSystem ? 0 : 2;
+	_polyphony = 1;
 	_pitchbend = 0;
 	_effect_level = player->_se->isNativeMT32() ? 127 : 64;
 	_instrument.clear();
@@ -350,8 +351,7 @@ void Part::sendAll() {
 	if (!clearToTransmit())
 		return;
 
-	if (!_player->isGM())
-		_mc->pitchBendFactor(_pitchbend_factor);
+	_mc->pitchBendFactor(_pitchbend_factor);
 	sendTranspose();
 	sendDetune();
 	sendPitchBend();
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 50ab9f9b86e..d2649f8207c 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -223,7 +223,7 @@ int Player::start_seq_sound(int sound, bool reset_vars) {
 }
 
 void Player::loadStartParameters(int sound) {
-	_priority = _se->_newSystem ? 0 : 0x80;
+	_priority = 0x80;
 	_volume = 0x7F;
 	_vol_chan = 0xFFFF;
 	_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;




More information about the Scummvm-git-logs mailing list