[Scummvm-git-logs] scummvm branch-2-8 -> ad3209378333a67a0dc794f2fe1d6d8518c7e27b

dreammaster noreply at scummvm.org
Sat Feb 17 23:37:35 UTC 2024


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

Summary:
8730b53ed4 MM: MM1: Support for non-vanilla version
d3f601cc6e MM: XEEN: Fix sound effects regression
54a8a4eacb MM: XEEN: Initial MT32/LAPC-1 support
defd3742be MM: XEEN: MidiDriver close and cleanup
33873e6ad4 NEWS: Add news about Xeen engine improvements
bc48dd680d MM: XEEN: Better wording in some comments
ad32093783 MM: XEEN: Added postprocessing in MT32 driver


Commit: 8730b53ed4419333574cf07adbb52e21e9fda3ef
    https://github.com/scummvm/scummvm/commit/8730b53ed4419333574cf07adbb52e21e9fda3ef
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T15:20:09-08:00

Commit Message:
MM: MM1: Support for non-vanilla version

There is version of MM1 in the wild which have some differences with floppy version of vanilla. Support resources from it too.

Changed paths:
    engines/mm/mm1/maps/map.cpp


diff --git a/engines/mm/mm1/maps/map.cpp b/engines/mm/mm1/maps/map.cpp
index 5ada7d22ad4..ddb892e3844 100644
--- a/engines/mm/mm1/maps/map.cpp
+++ b/engines/mm/mm1/maps/map.cpp
@@ -75,7 +75,7 @@ void Map::loadOverlay() {
 	f.readUint16LE();	// extras size
 	f.readUint16LE();	// code entry-point
 
-	if (magicId != 0xF2 || codePtr != 0xF48F)
+	if (magicId != 0xF2 || (codePtr != 0xF48F && codePtr != 0xF47C))
 		error("Invalid map overlay header");
 
 	// Skip over code segment, since each map's


Commit: d3f601cc6ee7e820a51d1b891567c8cc3e0f7c9f
    https://github.com/scummvm/scummvm/commit/d3f601cc6ee7e820a51d1b891567c8cc3e0f7c9f
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T15:20:25-08:00

Commit Message:
MM: XEEN: Fix sound effects regression

There is a regression introduced in commit 94cee9d which affects effects with countdown, like screaking door effect, and cause them to stop abruptly.

Changed paths:
    engines/mm/shared/xeen/sound.cpp
    engines/mm/shared/xeen/sound.h
    engines/mm/shared/xeen/sound_driver.cpp
    engines/mm/shared/xeen/sound_driver.h


diff --git a/engines/mm/shared/xeen/sound.cpp b/engines/mm/shared/xeen/sound.cpp
index 5ca36ee7aa3..3adc6af733d 100644
--- a/engines/mm/shared/xeen/sound.cpp
+++ b/engines/mm/shared/xeen/sound.cpp
@@ -113,7 +113,7 @@ bool Sound::isSoundPlaying() const {
 
 void Sound::stopAllAudio() {
 	stopSong();
-	stopFX();
+	stopFX(true);
 	stopSound();
 	setMusicPercent(100);
 }
@@ -172,8 +172,8 @@ void Sound::playFX(uint effectId) {
 	}
 }
 
-void Sound::stopFX() {
-	_SoundDriver->stopFX();
+void Sound::stopFX(bool force) {
+	_SoundDriver->stopFX(force);
 }
 
 int Sound::songCommand(uint commandId, byte musicVolume, byte sfxVolume) {
diff --git a/engines/mm/shared/xeen/sound.h b/engines/mm/shared/xeen/sound.h
index 7543fc12cab..3ebcaf3516d 100644
--- a/engines/mm/shared/xeen/sound.h
+++ b/engines/mm/shared/xeen/sound.h
@@ -74,7 +74,7 @@ public:
 	/**
 	 * Stops any currently playing FX
 	 */
-	void stopFX();
+	void stopFX(bool force = false);
 
 	/**
 	 * Executes special music command
diff --git a/engines/mm/shared/xeen/sound_driver.cpp b/engines/mm/shared/xeen/sound_driver.cpp
index c7ebd7f1b6f..98b4ed3198a 100644
--- a/engines/mm/shared/xeen/sound_driver.cpp
+++ b/engines/mm/shared/xeen/sound_driver.cpp
@@ -192,10 +192,12 @@ void SoundDriver::playFX(uint effectId, const byte *data) {
 	debugC(1, kDebugSound, "Starting FX %d", effectId);
 }
 
-void SoundDriver::stopFX() {
-	resetFX();
-	_streams[stFX]._playing = false;
-	_streams[stFX]._startPtr = _streams[stFX]._dataPtr = nullptr;
+void SoundDriver::stopFX(bool force) {
+	if (force || !_streams[stFX]._playing) {
+		resetFX();
+		_streams[stFX]._playing = false;
+		_streams[stFX]._startPtr = _streams[stFX]._dataPtr = nullptr;
+	}
 }
 
 void SoundDriver::playSong(const byte *data) {
diff --git a/engines/mm/shared/xeen/sound_driver.h b/engines/mm/shared/xeen/sound_driver.h
index 7ffbebd4cd7..c43ea178bfd 100644
--- a/engines/mm/shared/xeen/sound_driver.h
+++ b/engines/mm/shared/xeen/sound_driver.h
@@ -180,7 +180,7 @@ public:
 	/**
 	 * Stop any playing FX
 	 */
-	void stopFX();
+	void stopFX(bool force = false);
 
 	/**
 	 * Plays a song


Commit: 54a8a4eacb1a55e86137e1b1e1ae084cd3672947
    https://github.com/scummvm/scummvm/commit/54a8a4eacb1a55e86137e1b1e1ae084cd3672947
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T15:20:37-08:00

Commit Message:
MM: XEEN: Initial MT32/LAPC-1 support

Some non-essential subroutines in fxEndSubroutine is not implemented.
Support is currently the same as original game, so Roland GS and there are no conversion to GM.
Music tracks requires at least MT32-compatible device with support of capital tone fallback in ROM.
Game is also extensively uses extended sounds found in CM-32L for sound effects, MT-32 will produce silence for those.
So using in combination with real CM-32L or emulated one is the best for now.

Changed paths:
  A engines/mm/shared/xeen/sound_driver_mt32.cpp
  A engines/mm/shared/xeen/sound_driver_mt32.h
    audio/mididrv.h
    backends/midi/alsa.cpp
    engines/mm/module.mk
    engines/mm/shared/xeen/sound.cpp
    engines/mm/shared/xeen/sound.h
    engines/mm/shared/xeen/sound_driver.cpp
    engines/mm/shared/xeen/sound_driver.h
    engines/mm/shared/xeen/sound_driver_adlib.cpp
    engines/mm/shared/xeen/sound_driver_adlib.h


diff --git a/audio/mididrv.h b/audio/mididrv.h
index ed1f84d5eef..8166f8dadb3 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -200,7 +200,7 @@ public:
 	 * do NOT include the leading 0xF0 and the trailing 0xF7.
 	 *
 	 * Furthermore, the maximal supported length of a SysEx
-	 * is 264 bytes. Passing longer buffers can lead to
+	 * is 268 bytes. Passing longer buffers can lead to
 	 * undefined behavior (most likely, a crash).
 	 */
 	virtual void sysEx(const byte *msg, uint16 length) { }
diff --git a/backends/midi/alsa.cpp b/backends/midi/alsa.cpp
index f2591b60332..43953ea8ded 100644
--- a/backends/midi/alsa.cpp
+++ b/backends/midi/alsa.cpp
@@ -35,6 +35,8 @@
 
 #include <alsa/asoundlib.h>
 
+#define MIDI_USB_HACK
+
 /*
  *     ALSA sequencer driver
  * Mostly cut'n'pasted from Virtual Tiny Keyboard (vkeybd) by Takashi Iwai
@@ -217,10 +219,12 @@ void MidiDriver_ALSA::send(uint32 b) {
 		/* is it this simple ? Wow... */
 		snd_seq_ev_set_controller(&ev, chanID, midiCmd[1], midiCmd[2]);
 
+#ifdef MIDI_USB_HACK
 		// We save the volume of the first MIDI channel here to utilize it in
 		// our workaround for broken USB-MIDI cables.
 		if (chanID == 0 && midiCmd[1] == 0x07)
 			_channel0Volume = midiCmd[2];
+#endif
 
 		send_event(1);
 		break;
@@ -228,21 +232,25 @@ void MidiDriver_ALSA::send(uint32 b) {
 		snd_seq_ev_set_pgmchange(&ev, chanID, midiCmd[1]);
 		send_event(0);
 
+#ifdef MIDI_USB_HACK
 		// Send a volume change command to work around a firmware bug in common
 		// USB-MIDI cables. If the first MIDI command in a USB packet is a
 		// Cx or Dx command, the second command in the packet is dropped
 		// somewhere.
 		send(0x07B0 | (_channel0Volume << 16));
+#endif
 		break;
 	case 0xD0:
 		snd_seq_ev_set_chanpress(&ev, chanID, midiCmd[1]);
 		send_event(1);
 
+#ifdef MIDI_USB_HACK
 		// Send a volume change command to work around a firmware bug in common
 		// USB-MIDI cables. If the first MIDI command in a USB packet is a
 		// Cx or Dx command, the second command in the packet is dropped
 		// somewhere.
 		send(0x07B0 | (_channel0Volume << 16));
+#endif
 		break;
 	case 0xE0: {
 		// long theBend = ((((long)midiCmd[1] + (long)(midiCmd[2] << 7))) - 0x2000) / 4;
@@ -266,7 +274,7 @@ void MidiDriver_ALSA::sysEx(const byte *msg, uint16 length) {
 		return;
 	}
 
-	unsigned char buf[266];
+	unsigned char buf[270];
 
 	assert(length + 2 <= ARRAYSIZE(buf));
 
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index f0bd4f7827e..2d483ea3b82 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -13,6 +13,7 @@ MODULE_OBJS := \
 	shared/xeen/sound.o \
 	shared/xeen/sound_driver.o \
 	shared/xeen/sound_driver_adlib.o \
+	shared/xeen/sound_driver_mt32.o \
 	shared/xeen/sprites.o \
 	shared/xeen/xsurface.o
 
diff --git a/engines/mm/shared/xeen/sound.cpp b/engines/mm/shared/xeen/sound.cpp
index 3adc6af733d..445a6a45e80 100644
--- a/engines/mm/shared/xeen/sound.cpp
+++ b/engines/mm/shared/xeen/sound.cpp
@@ -18,13 +18,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-
 #include "audio/decoders/raw.h"
 #include "audio/decoders/voc.h"
 #include "backends/audiocd/audiocd.h"
 #include "common/config-manager.h"
 #include "mm/shared/xeen/sound.h"
 #include "mm/shared/xeen/sound_driver_adlib.h"
+#include "mm/shared/xeen/sound_driver_mt32.h"
 #include "mm/xeen/xeen.h"
 #include "mm/mm.h"
 
@@ -33,9 +33,28 @@ namespace Shared {
 namespace Xeen {
 
 Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer), _fxOn(true), _musicOn(true), _subtitles(false),
-_songData(nullptr), _effectsData(nullptr), _musicSide(0), _musicPercent(100),
+_songData(nullptr), _SoundDriver(nullptr), _effectsData(nullptr), _musicSide(0), _musicPercent(100),
 _musicVolume(0), _sfxVolume(0) {
-	_SoundDriver = new SoundDriverAdlib();
+	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32);
+	musicType = MidiDriver::getMusicType(dev);
+
+	switch (musicType) {
+	case MT_MT32:
+		_SoundDriver = new SoundDriverMT32();
+		debugC(1, "Selected mt32 sound driver\n");
+		break;
+	case MT_ADLIB:
+	default:
+		_SoundDriver = new SoundDriverAdlib();
+		debugC(1, "Selected adlib sound driver\n");
+		break;
+	}
+
+	// force load effects early so custom instruments for mt32 are loaded before sound is played.
+	loadEffectsData();
+
+	assert(_SoundDriver);
+
 	if (g_engine->getIsCD())
 		g_system->getAudioCDManager()->open();
 }
@@ -131,8 +150,13 @@ void Sound::loadEffectsData() {
 	// Stop any prior FX
 	stopFX();
 
-	if (!_effectsData) {
+	// Skip if we doesn't loaded sound driver or already loaded effects
+	if (!_SoundDriver || _effectsData)
+		return;
+
+	if (musicType == MT_ADLIB) {
 		// Load in an entire driver so we have quick access to the effects data that's hardcoded within it
+
 		const char *name = "blastmus";
 		File file(name);
 		size_t size = file.size();
@@ -157,6 +181,50 @@ void Sound::loadEffectsData() {
 		_effectsOffsets.resize(numEffects);
 		for (uint idx = 0; idx < numEffects; ++idx)
 			_effectsOffsets[idx] = READ_LE_UINT16(&table[idx * 2]);
+	} else if (musicType == MT_MT32) {
+		// Load in an entire driver so we have quick access to the effects data that's hardcoded within it
+		const char *name = "rolmus";
+		File file(name);
+		size_t size = file.size();
+		byte *effectsData = new byte[size];
+
+		if (file.read(effectsData, size) != size) {
+			delete[] effectsData;
+			error("Failed to read %zu bytes from '%s'", size, name);
+		}
+
+		_effectsData = effectsData;
+		// Locate the playFX routine
+		const byte *fx = effectsData + READ_LE_UINT16(effectsData + 10) + 12;
+		assert(READ_BE_UINT16(fx + 36) == 0x81FB);
+		// TODO: investigate, there are seems additional 11 effects in the table beside base 180
+		uint numEffects = READ_LE_UINT16(fx + 38);
+
+		assert(READ_BE_UINT16(fx + 80) == 0x8B87);
+		const byte *table = effectsData + READ_LE_UINT16(fx + 82);
+
+		// Extract the effects offsets
+		_effectsOffsets.resize(numEffects);
+		for (uint idx = 0; idx < numEffects; ++idx)
+			_effectsOffsets[idx] = READ_LE_UINT16(&table[idx * 2]);
+
+		// rolmus in intro.cc
+		if (effectsData[1] == 0xBD) {
+			_patchesOffsetsMT32 = {0x0A86, 0x0ABC, 0x10A3, 0x0BC1, 0x10AF, 0x0CBB, 0x10BB, 0x0DB5, 0x10C7, 0x0EAF, 0x10D3, 0x0FA9, 0x10DF};
+			debugC(3, "intro rolmus");
+		// rolmus in xeen.cc
+		} else if (effectsData[1] == 0xB9) {
+			_patchesOffsetsMT32 = {0x09A7, 0x09DD, 0x0FC4, 0x0AE2, 0x0FD0, 0x0BDC, 0x0FDC, 0x0CD6, 0x0FE8, 0x0DD0, 0x0FF4, 0x0ECA, 0x1000};
+			debugC(3, "xeen rolmus");
+		}
+
+		assert(_patchesOffsetsMT32.size() == 13);
+
+		for (uint idx = 0; idx < 13; idx++) {
+			const byte *ptr = &effectsData[_patchesOffsetsMT32[idx]];
+
+			_SoundDriver->sysExMessage(ptr);
+		}
 	}
 }
 
@@ -166,18 +234,23 @@ void Sound::playFX(uint effectId) {
 		return;
 	loadEffectsData();
 
-	if (effectId < _effectsOffsets.size()) {
+	if (_SoundDriver && effectId < _effectsOffsets.size()) {
 		const byte *dataP = &_effectsData[_effectsOffsets[effectId]];
 		_SoundDriver->playFX(effectId, dataP);
 	}
 }
 
 void Sound::stopFX(bool force) {
-	_SoundDriver->stopFX(force);
+	if (_SoundDriver)
+		_SoundDriver->stopFX(force);
 }
 
 int Sound::songCommand(uint commandId, byte musicVolume, byte sfxVolume) {
-	int result = _SoundDriver->songCommand(commandId, musicVolume, sfxVolume);
+	int result = 0;
+
+	if (_SoundDriver)
+		result = _SoundDriver->songCommand(commandId, musicVolume, sfxVolume);
+
 	if (commandId == STOP_SONG) {
 		delete[] _songData;
 		_songData = nullptr;
@@ -205,7 +278,8 @@ void Sound::playSong(Common::SeekableReadStream &stream) {
 	assert(!_songData);
 	_songData = songData;
 
-	_SoundDriver->playSong(_songData);
+	if (_SoundDriver)
+		_SoundDriver->playSong(_songData);
 }
 
 void Sound::playSong(const Common::String &name, int param) {
@@ -237,7 +311,7 @@ void Sound::setMusicOn(bool isOn) {
 }
 
 bool Sound::isMusicPlaying() const {
-	return _SoundDriver->isPlaying();
+	return _SoundDriver && _SoundDriver->isPlaying();
 }
 
 void Sound::setMusicPercent(byte percent) {
@@ -259,8 +333,13 @@ void Sound::updateSoundSettings() {
 		playSong(_currentMusic);
 
 	_subtitles = ConfMan.hasKey("subtitles") ? ConfMan.getBool("subtitles") : true;
-	_musicVolume = CLIP(ConfMan.getInt("music_volume"), 0, 255);
-	_sfxVolume = CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
+	if (ConfMan.getBool("mute")) {
+		_musicVolume = 0;
+		_sfxVolume = 0;
+	} else {
+		_musicVolume = CLIP(ConfMan.getInt("music_volume"), 0, 255);
+		_sfxVolume = CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
+	}
 	updateVolume();
 }
 
diff --git a/engines/mm/shared/xeen/sound.h b/engines/mm/shared/xeen/sound.h
index 3ebcaf3516d..505ff2cf361 100644
--- a/engines/mm/shared/xeen/sound.h
+++ b/engines/mm/shared/xeen/sound.h
@@ -22,6 +22,7 @@
 #ifndef MM_SHARED_XEEN_SOUND_H
 #define MM_SHARED_XEEN_SOUND_H
 
+#include "audio/mididrv.h"
 #include "audio/mixer.h"
 #include "audio/audiostream.h"
 #include "mm/shared/xeen/file.h"
@@ -36,6 +37,7 @@ private:
 	SoundDriver *_SoundDriver;
 	const byte *_effectsData;
 	Common::Array<uint16> _effectsOffsets;
+	Common::Array<uint16> _patchesOffsetsMT32;
 	const byte *_songData;
 	Audio::Mixer *_mixer;
 	Audio::SoundHandle _soundHandle;
@@ -62,6 +64,7 @@ public:
 	Common::String _currentMusic;
 	int _musicSide;
 	bool _subtitles;
+	MusicType musicType;
 public:
 	Sound(Audio::Mixer *mixer);
 	virtual ~Sound();
diff --git a/engines/mm/shared/xeen/sound_driver.cpp b/engines/mm/shared/xeen/sound_driver.cpp
index 98b4ed3198a..5e66cc02bf3 100644
--- a/engines/mm/shared/xeen/sound_driver.cpp
+++ b/engines/mm/shared/xeen/sound_driver.cpp
@@ -62,6 +62,8 @@ void SoundDriver::execute() {
 	// Main loop
 	bool breakFlag = false;
 	while (!breakFlag) {
+		if (!stream->_dataPtr || !stream->_startPtr)
+			break;
 		debugCN(3, kDebugSound, "MUSCODE %.4x - %.2x  ", (uint)(stream->_dataPtr - stream->_startPtr), (uint)*stream->_dataPtr);
 		byte nextByte = *stream->_dataPtr++;
 		int cmd = (nextByte >> 4) & 15;
@@ -109,24 +111,6 @@ bool SoundDriver::musSkipWord(const byte *&srcP, byte param) {
 	return false;
 }
 
-bool SoundDriver::cmdFreezeFrequency(const byte *&srcP, byte param) {
-	debugC(3, kDebugSound, "cmdFreezeFrequency %d", param);
-	_channels[param]._changeFrequency = false;
-	return false;
-}
-
-bool SoundDriver::cmdChangeFrequency(const byte *&srcP, byte param) {
-	debugC(3, kDebugSound, "cmdChangeFrequency %d", param);
-
-	_channels[param]._freqCtrChange = (int8)*srcP++;
-	_channels[param]._freqCtr = 0xFF;
-	_channels[param]._changeFrequency = true;
-	_channels[param]._freqChange = (int16)READ_BE_UINT16(srcP);
-	srcP += 2;
-
-	return false;
-}
-
 bool SoundDriver::musEndSubroutine(const byte *&srcP, byte param) {
 	debugC(3, kDebugSound, "musEndSubroutine %d", param);
 
diff --git a/engines/mm/shared/xeen/sound_driver.h b/engines/mm/shared/xeen/sound_driver.h
index c43ea178bfd..fcc2d3f57f3 100644
--- a/engines/mm/shared/xeen/sound_driver.h
+++ b/engines/mm/shared/xeen/sound_driver.h
@@ -31,6 +31,8 @@
 #include "mm/shared/xeen/file.h"
 
 #define CHANNEL_COUNT 9
+// interrupt is every ~13.736ms, which is ~72.8 times a second
+#define CALLBACKS_PER_SECOND 72.8f
 
 namespace OPL {
 	class OPL;
@@ -133,8 +135,8 @@ protected:
 	virtual bool musSetVolume(const byte *&srcP, byte param) = 0;
 	virtual bool musInjectMidi(const byte *&srcP, byte param) = 0;
 	virtual bool musPlayInstrument(const byte *&srcP, byte param) = 0;
-	virtual bool cmdFreezeFrequency(const byte *&srcP, byte param);
-	virtual bool cmdChangeFrequency(const byte *&srcP, byte param);
+	virtual bool cmdFreezeFrequency(const byte *&srcP, byte param) = 0;
+	virtual bool cmdChangeFrequency(const byte *&srcP, byte param) = 0;
 	virtual bool musEndSubroutine(const byte *&srcP, byte param);
 
 	// FX commands
@@ -198,6 +200,11 @@ public:
 	bool isPlaying() const {
 		return _streams[stMUSIC]._playing;
 	}
+
+	/**
+	 * Sends SysEx message
+	 */
+	virtual void sysExMessage(const byte *&data) = 0;
 };
 
 } // namespace Xeen
diff --git a/engines/mm/shared/xeen/sound_driver_adlib.cpp b/engines/mm/shared/xeen/sound_driver_adlib.cpp
index 240a4477c12..0726988810f 100644
--- a/engines/mm/shared/xeen/sound_driver_adlib.cpp
+++ b/engines/mm/shared/xeen/sound_driver_adlib.cpp
@@ -27,8 +27,6 @@ namespace MM {
 namespace Shared {
 namespace Xeen {
 
-#define CALLBACKS_PER_SECOND 73
-
 const byte SoundDriverAdlib::OPERATOR1_INDEXES[CHANNEL_COUNT] = {
 	0, 1, 2, 8, 9, 0xA, 0x10, 0x11, 0x12
 };
@@ -426,6 +424,29 @@ byte SoundDriverAdlib::calculateLevel(byte level, bool isFx) {
 	return scaling | (0x3f - totalLevel);
 }
 
+bool SoundDriverAdlib::cmdFreezeFrequency(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "cmdFreezeFrequency %d", param);
+	_channels[param]._changeFrequency = false;
+	return false;
+}
+
+bool SoundDriverAdlib::cmdChangeFrequency(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "cmdChangeFrequency %d", param);
+
+	_channels[param]._freqCtrChange = (int8)*srcP++;
+	_channels[param]._freqCtr = 0xFF;
+	_channels[param]._changeFrequency = true;
+	_channels[param]._freqChange = (int16)READ_BE_UINT16(srcP);
+	srcP += 2;
+
+	return false;
+}
+
+void SoundDriverAdlib::sysExMessage(const byte *&data) {
+	// not used in ad adlib
+}
+
+
 } // namespace Xeen
 } // namespace Shared
 } // namespace MM
diff --git a/engines/mm/shared/xeen/sound_driver_adlib.h b/engines/mm/shared/xeen/sound_driver_adlib.h
index 5d51c57914f..bc4be657405 100644
--- a/engines/mm/shared/xeen/sound_driver_adlib.h
+++ b/engines/mm/shared/xeen/sound_driver_adlib.h
@@ -22,6 +22,7 @@
 #ifndef MM_SHARED_XEEN_SOUND_DRIVER_ADLIB_H
 #define MM_SHARED_XEEN_SOUND_DRIVER_ADLIB_H
 
+#include "audio/mididrv.h"
 #include "mm/shared/xeen/sound_driver.h"
 
 namespace OPL {
@@ -106,6 +107,7 @@ private:
 	 * Calculates the scaling/volume level to output based on sfx or music master volume
 	 */
 	byte calculateLevel(byte level, bool isFx);
+
 protected:
 	bool musSetInstrument(const byte *&srcP, byte param) override;
 	bool musSetPitchWheel(const byte *&srcP, byte param) override;
@@ -115,6 +117,8 @@ protected:
 	bool musSetVolume(const byte *&srcP, byte param) override;
 	bool musInjectMidi(const byte *&srcP, byte param) override;
 	bool musPlayInstrument(const byte *&srcP, byte param) override;
+	bool cmdFreezeFrequency(const byte *&srcP, byte param) override;
+	bool cmdChangeFrequency(const byte *&srcP, byte param) override;
 
 	bool fxSetInstrument(const byte *&srcP, byte param) override;
 	bool fxSetVolume(const byte *&srcP, byte param) override;
@@ -136,6 +140,11 @@ protected:
 	 * Does a reset of any sound effect
 	 */
 	void resetFX() override;
+
+	/**
+	 * Sends SysEx message
+	 */
+	void sysExMessage(const byte *&data) override;
 public:
 	/**
 	 * Constructor
diff --git a/engines/mm/shared/xeen/sound_driver_mt32.cpp b/engines/mm/shared/xeen/sound_driver_mt32.cpp
new file mode 100644
index 00000000000..aa6d0f84161
--- /dev/null
+++ b/engines/mm/shared/xeen/sound_driver_mt32.cpp
@@ -0,0 +1,474 @@
+/* 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 "mm/shared/xeen/sound_driver_mt32.h"
+#include "mm/mm.h"
+
+namespace MM {
+namespace Shared {
+namespace Xeen {
+
+const uint8 SoundDriverMT32::MIDI_NOTE_MAP[24] = {
+    0x00, 0x0C, 0x0E, 0x10, 0x11, 0x13, 0x15, 0x17,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+    0x00, 0x0B, 0x0D, 0x0F, 0x10, 0x12, 0x14, 0x16
+};
+
+static byte last_notes[16] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#define MT32_ADJUST_VOLUME
+
+/*------------------------------------------------------------------------*/
+static void timerCallback(void *param) {
+	SoundDriverMT32 *_driver = (SoundDriverMT32*)param;
+	_driver->_timerCount += _driver->_midiDriver->getBaseTempo();
+	if (_driver->_timerCount > ((float)1000000 / CALLBACKS_PER_SECOND)) {
+		_driver->_timerCount -= (float)1000000 / CALLBACKS_PER_SECOND;
+		_driver->onTimer();
+	}
+}
+
+SoundDriverMT32::SoundDriverMT32() : _field180(0), _field181(0), _field182(0),
+_musicVolume(0), _sfxVolume(0), _timerCount(0) {
+	Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr);
+	Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr);
+
+	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32);
+	_midiDriver = MidiDriver::createMidi(dev);
+	int ret = _midiDriver->open();
+	if (ret) {
+		error("Can't open midi device. Errorcode: %d", ret);
+	} else {
+		_midiDriver->setTimerCallback(this, timerCallback);
+		initialize();
+	}
+}
+
+SoundDriverMT32::~SoundDriverMT32() {
+}
+
+void SoundDriverMT32::onTimer() {
+	Common::StackLock slock(_driverMutex);
+	execute();
+	flush();
+}
+
+void SoundDriverMT32::initialize() {
+	_midiDriver->sendMT32Reset();
+	for (int idx = 0; idx < CHANNEL_COUNT; idx++)
+	 	write(0xB1 + idx, 0x07, idx == 8 ? 0x7F : 0x4F);
+
+	resetFrequencies();
+
+	SoundDriverMT32::resetFX();
+}
+
+void SoundDriverMT32::playFX(uint effectId, const byte *data) {
+	Common::StackLock slock(_driverMutex);
+
+	// notes off on 8ch
+	write(0xB8, 0x7B, 0x00);
+	// pitch bend on 8 ch
+	write(0xE8, 0x00, 0x40);
+
+	SoundDriver::playFX(effectId, data);
+}
+
+void SoundDriverMT32::playSong(const byte *data) {
+	Common::StackLock slock(_driverMutex);
+	SoundDriver::playSong(data);
+	_field180 = 0;
+	resetFrequencies();
+}
+
+int SoundDriverMT32::songCommand(uint commandId, byte musicVolume, byte sfxVolume) {
+	Common::StackLock slock(_driverMutex);
+	SoundDriver::songCommand(commandId, musicVolume, sfxVolume);
+
+	if (commandId == STOP_SONG) {
+		_field180 = 0;
+		resetFrequencies();
+	} else if (commandId == RESTART_SONG) {
+		_field180 = 0;
+		_streams[stMUSIC]._playing = true;
+	} else if (commandId < 0x100) {
+		if (_streams[stMUSIC]._playing) {
+			_field180 = commandId;
+			_field182 = 63;
+		}
+	} else if (commandId == SET_VOLUME) {
+		_musicVolume = musicVolume;
+		_sfxVolume = sfxVolume;
+	} else if (commandId == GET_STATUS) {
+		return _field180;
+	}
+
+	return 0;
+}
+
+void SoundDriverMT32::write(uint8 command, uint8 op1, uint8 op2) {
+	MidiValue v(command, op1, op2);
+	_queue.push(v);
+	debugC(9, kDebugSound, "push %08x", v._val);
+}
+
+void SoundDriverMT32::flush() {
+	Common::StackLock slock(_driverMutex);
+
+	while (!_queue.empty()) {
+		MidiValue v = _queue.pop();
+		debugC(9, kDebugSound, "pop %08x", v._val);
+		_midiDriver->send(v._val);
+	}
+}
+
+byte SoundDriverMT32::noteMap(byte note) {
+	assert((note & 0x1F) < sizeof(MIDI_NOTE_MAP));
+	uint8 freq = (note & 0xE0) >> 2;
+	uint8 result = MIDI_NOTE_MAP[note & 0x1F];
+	result+= freq + (freq >> 1);
+	return result;
+}
+
+void SoundDriverMT32::pausePostProcess() {
+	if (_field180 && ((_field181 += _field180) < 0)) {
+		if (--_field182 < 0) {
+			_streams[stMUSIC]._playing = false;
+			_field180 = 0;
+			resetFrequencies();
+		}
+	}
+}
+
+void SoundDriverMT32::resetFX() {
+	// notes off on 8ch
+	write(0xB8, 0x7B, 0x00);
+	// pitch bend on 8ch
+	write(0xE8, 0x00, 0x40);
+}
+
+void SoundDriverMT32::resetFrequencies() {
+	// set pitch
+	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--) {
+		write(0xE1 + idx, 0x00, 0x40);
+	}
+	// set pan
+	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--) {
+		write(0xB1 + idx, 0x0A, 0x3F);
+	}
+	// notes off
+	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--) {
+		write(0xB1 + idx, 0x7B, 0x00);
+	}
+}
+
+void SoundDriverMT32::playInstrument(byte channelNum, const byte *data, bool isFx) {
+	debugC(2, kDebugSound, "---START-playInstrument - %d", channelNum);
+
+	write(0xC1 + channelNum, *data, 0x00);
+
+	debugC(2, kDebugSound, "---END-playInstrument");
+}
+
+bool SoundDriverMT32::musSetInstrument(const byte *&srcP, byte param) {
+	srcP += 24;
+	debugC(3, kDebugSound, "musSetInstrument %d -> %x", param, *srcP);
+	_musInstrumentPtrs[param] = srcP;
+	srcP += 2;
+
+	return false;
+}
+
+bool SoundDriverMT32::musSetPitchWheel(const byte *&srcP, byte param) {
+	byte pitch1 = *srcP++;
+	byte pitch2 = *srcP++;
+	debugC(3, kDebugSound, "musSetPitchWheel");
+	write(0xE1 + param, pitch1, pitch2);
+
+	return false;
+}
+
+bool SoundDriverMT32::musSetPanning(const byte *&srcP, byte param) {
+	byte pan = *srcP++;
+	debugC(3, kDebugSound, "mmusSetPanning");
+	write(0xB1 + param, 0x0A, pan);
+
+	return false;
+}
+
+bool SoundDriverMT32::musFade(const byte *&srcP, byte param) {
+	byte note = *srcP++;
+	debugC(3, kDebugSound, "musFade: %x", note);
+	if (param != 8)
+		note = noteMap(note);
+
+	write(0x81 + param, note & 0x7F, 0x40);
+	last_notes[param] = 0xFF;
+
+	return false;
+}
+
+bool SoundDriverMT32::musStartNote(const byte *&srcP, byte param) {
+	byte note = *srcP++;
+	byte fade = *srcP++;
+
+	debugC(3, kDebugSound, "musStartNote %x, %x", note, fade);
+	if (param != 8) {
+		note = noteMap(note);
+	}
+
+	if (param != 8) {
+		if (param != 7)
+			write(0x81 + param, last_notes[param] & 0x7F, 0x7F);
+		else
+			write(0x81 + param, note & 0x7F, 0x7F);
+	}
+
+#if defined(MT32_ADJUST_VOLUME)
+	byte level = calculateLevel(fade, false);
+	if (level > 0)
+		write(0x91 + param, note & 0x7F, level);
+#else
+	write(0x91 + param, note & 0x7F, fade);
+#endif
+	last_notes[param] = note & 0x7F;
+
+	return false;
+}
+
+bool SoundDriverMT32::musSetVolume(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "musSetVolume %d", (int)*srcP);
+
+	uint8 status = *srcP++;
+	uint8 volume = *srcP++;
+
+	if (status == 0 && !_field180) {
+		// TODO: this is used in fxEndSubroutine in rolmus
+		_channels[param]._volume = volume;
+#if defined(MT32_ADJUST_VOLUME)
+		byte level = calculateLevel(volume, true);
+		write(0xB1 + param, 0x0B, level);
+#else
+		write(0xB1 + param, 0x0B, volume);
+#endif
+	}
+
+	return false;
+}
+
+bool SoundDriverMT32::musInjectMidi(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "musInjectMidi");
+	
+	// TODO: When this happens? Wrap it in sysex as rolmus do
+	sysExMessage(srcP);
+
+	return false;
+}
+
+bool SoundDriverMT32::musPlayInstrument(const byte *&srcP, byte param) {
+	byte instrument = *srcP++;
+	debugC(3, kDebugSound, "musPlayInstrument %d -> %d", param, instrument);
+
+	// TODO: rolmus also have condition to use _fxInstrumentPtrs instead if isFx is set in timer, ignore for now
+	playInstrument(param, _musInstrumentPtrs[instrument], false);
+
+	return false;
+}
+
+bool SoundDriverMT32::fxSetInstrument(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "fxSetInstrument %d -> %x", param, *srcP);
+	_fxInstrumentPtrs[param] = srcP;
+	++srcP;
+
+	return false;
+}
+
+bool SoundDriverMT32::fxSetVolume(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "fxSetVolume %d", (int)*srcP);
+
+	uint8 volume = *srcP++;
+
+	if (!_field180) {
+		// TODO: this is used in fxEndSubroutine in rolmus
+		_channels[param]._volume = volume;
+#if defined(MT32_ADJUST_VOLUME)
+		byte level = calculateLevel(volume, true);
+		write(0xB1 + param, 0x0B, level);
+#else
+		write(0xB1 + param, 0x0B, volume);
+#endif
+	}
+
+	return false;
+}
+
+bool SoundDriverMT32::fxMidiReset(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "fxMidiReset");
+
+	return false;
+}
+
+bool SoundDriverMT32::fxMidiDword(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "fxMidiDword");
+
+ 	// First two bytes seems used in rolmus
+	srcP += 2;
+	// TODO: this is used in fxEndSubroutine in rolmus
+	_channels[param]._freqCtrChange = READ_LE_UINT16(*&srcP);
+	srcP += 2;
+
+	return false;
+}
+
+bool SoundDriverMT32::fxSetPanning(const byte *&srcP, byte param) {
+	byte pan = *srcP++;
+	debugC(3, kDebugSound, "fxSetPanning - %x", pan);
+
+	write(0xB1 + param, 0x0A, pan);
+
+	return false;
+}
+
+bool SoundDriverMT32::fxChannelOff(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "fxChannelOff %d", param);
+
+	return false;
+}
+
+bool SoundDriverMT32::fxFade(const byte *&srcP, byte param) {
+	byte note = *srcP++;
+
+	debugC(3, kDebugSound, "fxFade %d %x", param, note);
+	note = noteMap(note);
+
+	if (param == 7)
+		write(0x81 + param, last_notes[param] & 0x7F, 0x7F);
+	else
+		write(0x81 + param, note & 0x7F, 0x7F);
+	
+	last_notes[param] = 0xFF;
+
+	return false;
+}
+
+bool SoundDriverMT32::fxStartNote(const byte *&srcP, byte param) {
+	byte note = *srcP++;
+	byte fade = *srcP++;
+
+	debugC(3, kDebugSound, "fxStartNote %x, %x", note, fade);
+
+	if (param != 8)
+		note = noteMap(note);
+
+#if defined(MT32_ADJUST_VOLUME)
+	byte level = calculateLevel(fade, true);
+	if (level > 0)
+		write(0x91 + param, note & 0x7F, level);
+	else
+		write(0x81 + param, note & 0x7F, 0x7f);
+#else
+		write(0x91 + param, note & 0x7F, fade);
+#endif
+	last_notes[param] = note & 0x7F;
+	
+	return false;
+}
+
+bool SoundDriverMT32::fxInjectMidi(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "fxInjectMidi");
+
+	return false;
+}
+
+bool SoundDriverMT32::fxPlayInstrument(const byte *&srcP, byte param) {
+	byte instrument = *srcP++;
+	debugC(3, kDebugSound, "fxPlayInstrument %d, %d", param, instrument);
+
+	playInstrument(param, _fxInstrumentPtrs[instrument], true);
+
+	return false;
+}
+
+byte SoundDriverMT32::calculateLevel(byte level, bool isFx) {
+	uint volume = isFx ? _sfxVolume : _musicVolume;
+	float scaling = 127.0f / 255.0f;
+	uint totalLevel = volume * scaling;
+
+	return totalLevel;
+}
+
+bool SoundDriverMT32::cmdFreezeFrequency(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "cmdNoOperation");
+
+	return false;
+}
+
+bool SoundDriverMT32::cmdChangeFrequency(const byte *&srcP, byte param) {
+	debugC(3, kDebugSound, "cmdChangeFrequency");
+
+	srcP += 3;
+
+	return false;
+}
+
+void SoundDriverMT32::sysExMessage(const byte *&data) {
+	byte   sysExMessage[270];
+	uint16 sysExPos      = 0;
+	byte   sysExByte     = 0;
+	uint16 sysExChecksum = 0;
+
+	memset(&sysExMessage, 0, sizeof(sysExMessage));
+
+	sysExMessage[0] = 0x41; // Roland
+	sysExMessage[1] = 0x10;
+	sysExMessage[2] = 0x16; // Model MT32
+	sysExMessage[3] = 0x12; // Command DT1
+
+	sysExPos      = 4;
+	sysExChecksum = 0;
+	while (1) {
+		sysExByte = *data++;
+		if (sysExByte == 0xF7)
+			break; // Message done
+
+		// if we need bigger buffer then buffer in MidiDriver_ALSA::sysEx should also be increased!
+		assert(sysExPos < sizeof(sysExMessage));
+		sysExMessage[sysExPos++] = sysExByte;
+		sysExChecksum -= sysExByte;
+	}
+
+	// Calculate checksum
+	assert(sysExPos < sizeof(sysExMessage));
+	sysExMessage[sysExPos++] = sysExChecksum & 0x7f;
+
+	debugC(3, "sending sysex message, size %d", sysExPos);
+
+	// Send SysEx message
+	_midiDriver->sysEx(sysExMessage, sysExPos);
+}
+
+} // namespace Xeen
+} // namespace Shared
+} // namespace MM
diff --git a/engines/mm/shared/xeen/sound_driver_mt32.h b/engines/mm/shared/xeen/sound_driver_mt32.h
new file mode 100644
index 00000000000..76fc4a34583
--- /dev/null
+++ b/engines/mm/shared/xeen/sound_driver_mt32.h
@@ -0,0 +1,166 @@
+/* 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 MM_SHARED_XEEN_SOUND_DRIVER_MT32_H
+#define MM_SHARED_XEEN_SOUND_DRIVER_MT32_H
+
+
+#include "audio/mididrv.h"
+#include "mm/shared/xeen/sound_driver.h"
+
+namespace MM {
+namespace Shared {
+namespace Xeen {
+
+class SoundDriverMT32 : public SoundDriver {
+	struct MidiValue {
+		uint32 _val;
+
+		MidiValue(uint8 command, uint8 op1, uint8 op2) {
+			_val = (command) | ((uint32)op2 << 16) | ((uint32)op1 << 8);
+		}
+	};
+public:
+	MidiDriver *_midiDriver;
+	uint32 _timerCount;
+private:
+	static const uint8 MIDI_NOTE_MAP[24];
+private:
+	MidiChannel *_midiChannels[CHANNEL_COUNT];
+	Common::Queue<MidiValue> _queue;
+	Common::Mutex _driverMutex;
+	const byte *_musInstrumentPtrs[16];
+	const byte *_fxInstrumentPtrs[16];
+	byte _expressions[16];
+	int _field180;
+	int _field181;
+	int _field182;
+	int _musicVolume, _sfxVolume;
+private:
+	/**
+	 * Initializes the state of the MT32 driver
+	 */
+	void initialize();
+
+	/**
+	 * Adds a register write to the pending queue that will be flushed
+	 * out to the MT32 on the next timer call
+	 */
+	void write(uint8 command, uint8 op1, uint8 op2);
+
+	/**
+	 * Flushes any pending writes to the OPL
+	 */
+	void flush();
+
+	/**
+	 * Resets all the output frequencies
+	 */
+	void resetFrequencies();
+
+	/**
+	 * Starts playing an instrument
+	 */
+	void playInstrument(byte channelNum, const byte *data, bool isFx);
+
+	/**
+	 * Calculates the scaling/volume level to output based on sfx or music master volume
+	 */
+	byte calculateLevel(byte level, bool isFx);
+
+	/**
+	 * Maps note using hardcoded notes table
+	 */
+	byte noteMap(byte note);
+protected:
+	bool musSetInstrument(const byte *&srcP, byte param) override;
+	bool musSetPitchWheel(const byte *&srcP, byte param) override;
+	bool musSetPanning(const byte *&srcP, byte param) override;
+	bool musFade(const byte *&srcP, byte param) override;
+	bool musStartNote(const byte *&srcP, byte param) override;
+	bool musSetVolume(const byte *&srcP, byte param) override;
+	bool musInjectMidi(const byte *&srcP, byte param) override;
+	bool musPlayInstrument(const byte *&srcP, byte param) override;
+	bool cmdFreezeFrequency(const byte *&srcP, byte param) override;
+	bool cmdChangeFrequency(const byte *&srcP, byte param) override;
+
+	bool fxSetInstrument(const byte *&srcP, byte param) override;
+	bool fxSetVolume(const byte *&srcP, byte param) override;
+	bool fxMidiReset(const byte *&srcP, byte param) override;
+	bool fxMidiDword(const byte *&srcP, byte param) override;
+	bool fxSetPanning(const byte *&srcP, byte param) override;
+	bool fxChannelOff(const byte *&srcP, byte param) override;
+	bool fxFade(const byte *&srcP, byte param) override;
+	bool fxStartNote(const byte *&srcP, byte param) override;
+	bool fxInjectMidi(const byte *&srcP, byte param) override;
+	bool fxPlayInstrument(const byte *&srcP, byte param) override;
+
+	/**
+	 * Post-processing done when a pause countdown starts or is in progress
+	 */
+	void pausePostProcess() override;
+
+	/**
+	 * Does a reset of any sound effect
+	 */
+	void resetFX() override;
+
+	/**
+	 * Sends SysEx message
+	 */
+	void sysExMessage(const byte *&data) override;
+public:
+	/**
+	 * Constructor
+	 */
+	SoundDriverMT32();
+
+	/**
+	 * Destructor
+	 */
+	~SoundDriverMT32() override;
+
+	/**
+	 * Starts a special effect playing
+	 */
+	void playFX(uint effectId, const byte *data) override;
+
+	/**
+	 * Plays a song
+	 */
+	void playSong(const byte *data) override;
+
+	/**
+	 * Executes special music command
+	 */
+	int songCommand(uint commandId, byte musicVolume = 0, byte sfxVolume = 0) override;
+
+	/**
+	 * Timer function for MT32
+	 */
+	void onTimer();
+};
+
+} // namespace Xeen
+} // namespace Shared
+} // namespace MM
+
+#endif


Commit: defd3742bee16ef81e2900963321376d3f3ba105
    https://github.com/scummvm/scummvm/commit/defd3742bee16ef81e2900963321376d3f3ba105
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T15:20:48-08:00

Commit Message:
MM: XEEN: MidiDriver close and cleanup

Changed paths:
    engines/mm/shared/xeen/sound_driver_mt32.cpp


diff --git a/engines/mm/shared/xeen/sound_driver_mt32.cpp b/engines/mm/shared/xeen/sound_driver_mt32.cpp
index aa6d0f84161..8a19af2c583 100644
--- a/engines/mm/shared/xeen/sound_driver_mt32.cpp
+++ b/engines/mm/shared/xeen/sound_driver_mt32.cpp
@@ -28,9 +28,9 @@ namespace Shared {
 namespace Xeen {
 
 const uint8 SoundDriverMT32::MIDI_NOTE_MAP[24] = {
-    0x00, 0x0C, 0x0E, 0x10, 0x11, 0x13, 0x15, 0x17,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
-    0x00, 0x0B, 0x0D, 0x0F, 0x10, 0x12, 0x14, 0x16
+	0x00, 0x0C, 0x0E, 0x10, 0x11, 0x13, 0x15, 0x17,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+	0x00, 0x0B, 0x0D, 0x0F, 0x10, 0x12, 0x14, 0x16
 };
 
 static byte last_notes[16] = {
@@ -43,6 +43,9 @@ static byte last_notes[16] = {
 /*------------------------------------------------------------------------*/
 static void timerCallback(void *param) {
 	SoundDriverMT32 *_driver = (SoundDriverMT32*)param;
+	if (!_driver || !_driver->_midiDriver)
+		return;
+
 	_driver->_timerCount += _driver->_midiDriver->getBaseTempo();
 	if (_driver->_timerCount > ((float)1000000 / CALLBACKS_PER_SECOND)) {
 		_driver->_timerCount -= (float)1000000 / CALLBACKS_PER_SECOND;
@@ -51,7 +54,7 @@ static void timerCallback(void *param) {
 }
 
 SoundDriverMT32::SoundDriverMT32() : _field180(0), _field181(0), _field182(0),
-_musicVolume(0), _sfxVolume(0), _timerCount(0) {
+_musicVolume(0), _sfxVolume(0), _timerCount(0), _midiDriver(nullptr) {
 	Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr);
 	Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr);
 
@@ -67,6 +70,11 @@ _musicVolume(0), _sfxVolume(0), _timerCount(0) {
 }
 
 SoundDriverMT32::~SoundDriverMT32() {
+	if (_midiDriver) {
+		_midiDriver->close();
+		delete _midiDriver;
+		_midiDriver = nullptr;
+	}
 }
 
 void SoundDriverMT32::onTimer() {
@@ -88,11 +96,6 @@ void SoundDriverMT32::initialize() {
 void SoundDriverMT32::playFX(uint effectId, const byte *data) {
 	Common::StackLock slock(_driverMutex);
 
-	// notes off on 8ch
-	write(0xB8, 0x7B, 0x00);
-	// pitch bend on 8 ch
-	write(0xE8, 0x00, 0x40);
-
 	SoundDriver::playFX(effectId, data);
 }
 
@@ -442,7 +445,7 @@ void SoundDriverMT32::sysExMessage(const byte *&data) {
 	memset(&sysExMessage, 0, sizeof(sysExMessage));
 
 	sysExMessage[0] = 0x41; // Roland
-	sysExMessage[1] = 0x10;
+	sysExMessage[1] = 0x10; // Device ID
 	sysExMessage[2] = 0x16; // Model MT32
 	sysExMessage[3] = 0x12; // Command DT1
 


Commit: 33873e6ad41fd715ee62fa74e57d29ac3f314b95
    https://github.com/scummvm/scummvm/commit/33873e6ad41fd715ee62fa74e57d29ac3f314b95
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T15:22:22-08:00

Commit Message:
NEWS: Add news about Xeen engine improvements

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index a6f48187ffc..13b23d276a0 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -12,6 +12,8 @@ For a more comprehensive changelog of the latest experimental code, see:
 
  MM:
    - Enabled engine, allowing MM1 and Xeen to be compiled.
+   - Added MT32/LAPC-1 support for Xeen engine.
+   - Fixed Xeen regression which caused some sound effects to stop abruptly.
 
  MTROPOLIS:
    - Fixed crash in Muppet Treasure Island on some platforms.


Commit: bc48dd680dcff16a8acc8ff637c257d5a62ebe5a
    https://github.com/scummvm/scummvm/commit/bc48dd680dcff16a8acc8ff637c257d5a62ebe5a
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T15:22:48-08:00

Commit Message:
MM: XEEN: Better wording in some comments

Changed paths:
    engines/mm/shared/xeen/sound.cpp


diff --git a/engines/mm/shared/xeen/sound.cpp b/engines/mm/shared/xeen/sound.cpp
index 445a6a45e80..d68d0a2af37 100644
--- a/engines/mm/shared/xeen/sound.cpp
+++ b/engines/mm/shared/xeen/sound.cpp
@@ -150,7 +150,7 @@ void Sound::loadEffectsData() {
 	// Stop any prior FX
 	stopFX();
 
-	// Skip if we doesn't loaded sound driver or already loaded effects
+	// Skip if the sound driver hasn't been loaded, or effects have already been loaded
 	if (!_SoundDriver || _effectsData)
 		return;
 
@@ -197,7 +197,7 @@ void Sound::loadEffectsData() {
 		// Locate the playFX routine
 		const byte *fx = effectsData + READ_LE_UINT16(effectsData + 10) + 12;
 		assert(READ_BE_UINT16(fx + 36) == 0x81FB);
-		// TODO: investigate, there are seems additional 11 effects in the table beside base 180
+		// TODO: Investigate, additional 10 effects seem to exist in the table beyond the base 180. Not unique to rolmus as at least admus, blastmus and canmus have them too.
 		uint numEffects = READ_LE_UINT16(fx + 38);
 
 		assert(READ_BE_UINT16(fx + 80) == 0x8B87);


Commit: ad3209378333a67a0dc794f2fe1d6d8518c7e27b
    https://github.com/scummvm/scummvm/commit/ad3209378333a67a0dc794f2fe1d6d8518c7e27b
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T15:23:01-08:00

Commit Message:
MM: XEEN: Added postprocessing in MT32 driver

Added missing postprocessing functionallity and so resolved relevant TODOs.
Make sure last_note table is always initialized with 0xFF.
Added previously missing sound effect when Efx is enabled in control panel.

Changed paths:
    engines/mm/shared/xeen/sound_driver_mt32.cpp
    engines/mm/shared/xeen/sound_driver_mt32.h
    engines/mm/xeen/dialogs/dialogs_control_panel.cpp


diff --git a/engines/mm/shared/xeen/sound_driver_mt32.cpp b/engines/mm/shared/xeen/sound_driver_mt32.cpp
index 8a19af2c583..ae1fc603469 100644
--- a/engines/mm/shared/xeen/sound_driver_mt32.cpp
+++ b/engines/mm/shared/xeen/sound_driver_mt32.cpp
@@ -33,11 +33,6 @@ const uint8 SoundDriverMT32::MIDI_NOTE_MAP[24] = {
 	0x00, 0x0B, 0x0D, 0x0F, 0x10, 0x12, 0x14, 0x16
 };
 
-static byte last_notes[16] = {
-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-};
-
 #define MT32_ADJUST_VOLUME
 
 /*------------------------------------------------------------------------*/
@@ -57,6 +52,7 @@ SoundDriverMT32::SoundDriverMT32() : _field180(0), _field181(0), _field182(0),
 _musicVolume(0), _sfxVolume(0), _timerCount(0), _midiDriver(nullptr) {
 	Common::fill(&_musInstrumentPtrs[0], &_musInstrumentPtrs[16], (const byte *)nullptr);
 	Common::fill(&_fxInstrumentPtrs[0], &_fxInstrumentPtrs[16], (const byte *)nullptr);
+	Common::fill(&_last_notes[0], &_last_notes[16], 0xFF);
 
 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32);
 	_midiDriver = MidiDriver::createMidi(dev);
@@ -85,6 +81,8 @@ void SoundDriverMT32::onTimer() {
 
 void SoundDriverMT32::initialize() {
 	_midiDriver->sendMT32Reset();
+
+	// set volume
 	for (int idx = 0; idx < CHANNEL_COUNT; idx++)
 	 	write(0xB1 + idx, 0x07, idx == 8 ? 0x7F : 0x4F);
 
@@ -119,7 +117,7 @@ int SoundDriverMT32::songCommand(uint commandId, byte musicVolume, byte sfxVolum
 	} else if (commandId < 0x100) {
 		if (_streams[stMUSIC]._playing) {
 			_field180 = commandId;
-			_field182 = 63;
+			_field182 = 0x7F;
 		}
 	} else if (commandId == SET_VOLUME) {
 		_musicVolume = musicVolume;
@@ -161,8 +159,26 @@ void SoundDriverMT32::pausePostProcess() {
 			_streams[stMUSIC]._playing = false;
 			_field180 = 0;
 			resetFrequencies();
+		} else {
+			for (int channelNum = 8; channelNum >= 0; --channelNum) {
+				if (channelNum == 7)
+					return;
+
+				if (_channels[channelNum]._volume >= 40) {
+					_channels[channelNum]._volume--;
+					// set volume expression
+					write(0xB1 + channelNum, 0x0B, _channels[channelNum]._volume);
+				}
+			}
 		}
 	}
+
+	byte channelNum = 7;
+	if (_channels[channelNum]._freqChange) {
+		_channels[channelNum]._frequency += _channels[channelNum]._freqChange;
+		// pitch bend
+		write(0xE1 + channelNum, _channels[channelNum]._frequency & 0x7F, (_channels[channelNum]._frequency >> 8) & 0x7F);
+	}
 }
 
 void SoundDriverMT32::resetFX() {
@@ -174,17 +190,14 @@ void SoundDriverMT32::resetFX() {
 
 void SoundDriverMT32::resetFrequencies() {
 	// set pitch
-	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--) {
+	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--)
 		write(0xE1 + idx, 0x00, 0x40);
-	}
 	// set pan
-	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--) {
+	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--)
 		write(0xB1 + idx, 0x0A, 0x3F);
-	}
 	// notes off
-	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--) {
+	for (int idx = CHANNEL_COUNT - 1; idx >= 0; idx--)
 		write(0xB1 + idx, 0x7B, 0x00);
-	}
 }
 
 void SoundDriverMT32::playInstrument(byte channelNum, const byte *data, bool isFx) {
@@ -228,7 +241,7 @@ bool SoundDriverMT32::musFade(const byte *&srcP, byte param) {
 		note = noteMap(note);
 
 	write(0x81 + param, note & 0x7F, 0x40);
-	last_notes[param] = 0xFF;
+	_last_notes[param] = 0xFF;
 
 	return false;
 }
@@ -244,7 +257,7 @@ bool SoundDriverMT32::musStartNote(const byte *&srcP, byte param) {
 
 	if (param != 8) {
 		if (param != 7)
-			write(0x81 + param, last_notes[param] & 0x7F, 0x7F);
+			write(0x81 + param, _last_notes[param] & 0x7F, 0x7F);
 		else
 			write(0x81 + param, note & 0x7F, 0x7F);
 	}
@@ -256,7 +269,7 @@ bool SoundDriverMT32::musStartNote(const byte *&srcP, byte param) {
 #else
 	write(0x91 + param, note & 0x7F, fade);
 #endif
-	last_notes[param] = note & 0x7F;
+	_last_notes[param] = note & 0x7F;
 
 	return false;
 }
@@ -268,7 +281,6 @@ bool SoundDriverMT32::musSetVolume(const byte *&srcP, byte param) {
 	uint8 volume = *srcP++;
 
 	if (status == 0 && !_field180) {
-		// TODO: this is used in fxEndSubroutine in rolmus
 		_channels[param]._volume = volume;
 #if defined(MT32_ADJUST_VOLUME)
 		byte level = calculateLevel(volume, true);
@@ -314,7 +326,6 @@ bool SoundDriverMT32::fxSetVolume(const byte *&srcP, byte param) {
 	uint8 volume = *srcP++;
 
 	if (!_field180) {
-		// TODO: this is used in fxEndSubroutine in rolmus
 		_channels[param]._volume = volume;
 #if defined(MT32_ADJUST_VOLUME)
 		byte level = calculateLevel(volume, true);
@@ -330,16 +341,17 @@ bool SoundDriverMT32::fxSetVolume(const byte *&srcP, byte param) {
 bool SoundDriverMT32::fxMidiReset(const byte *&srcP, byte param) {
 	debugC(3, kDebugSound, "fxMidiReset");
 
+	_channels[param]._freqChange = 0;
+
 	return false;
 }
 
 bool SoundDriverMT32::fxMidiDword(const byte *&srcP, byte param) {
 	debugC(3, kDebugSound, "fxMidiDword");
 
- 	// First two bytes seems used in rolmus
+	_channels[param]._freqChange = READ_LE_UINT16(*&srcP);
 	srcP += 2;
-	// TODO: this is used in fxEndSubroutine in rolmus
-	_channels[param]._freqCtrChange = READ_LE_UINT16(*&srcP);
+	_channels[param]._frequency = READ_LE_UINT16(*&srcP);
 	srcP += 2;
 
 	return false;
@@ -367,11 +379,11 @@ bool SoundDriverMT32::fxFade(const byte *&srcP, byte param) {
 	note = noteMap(note);
 
 	if (param == 7)
-		write(0x81 + param, last_notes[param] & 0x7F, 0x7F);
+		write(0x81 + param, _last_notes[param] & 0x7F, 0x7F);
 	else
 		write(0x81 + param, note & 0x7F, 0x7F);
 	
-	last_notes[param] = 0xFF;
+	_last_notes[param] = 0xFF;
 
 	return false;
 }
@@ -394,7 +406,7 @@ bool SoundDriverMT32::fxStartNote(const byte *&srcP, byte param) {
 #else
 		write(0x91 + param, note & 0x7F, fade);
 #endif
-	last_notes[param] = note & 0x7F;
+	_last_notes[param] = note & 0x7F;
 	
 	return false;
 }
diff --git a/engines/mm/shared/xeen/sound_driver_mt32.h b/engines/mm/shared/xeen/sound_driver_mt32.h
index 76fc4a34583..280f42f9b94 100644
--- a/engines/mm/shared/xeen/sound_driver_mt32.h
+++ b/engines/mm/shared/xeen/sound_driver_mt32.h
@@ -49,7 +49,7 @@ private:
 	Common::Mutex _driverMutex;
 	const byte *_musInstrumentPtrs[16];
 	const byte *_fxInstrumentPtrs[16];
-	byte _expressions[16];
+	byte _last_notes[16];
 	int _field180;
 	int _field181;
 	int _field182;
diff --git a/engines/mm/xeen/dialogs/dialogs_control_panel.cpp b/engines/mm/xeen/dialogs/dialogs_control_panel.cpp
index 8ad487730cc..a6474b008e0 100644
--- a/engines/mm/xeen/dialogs/dialogs_control_panel.cpp
+++ b/engines/mm/xeen/dialogs/dialogs_control_panel.cpp
@@ -129,6 +129,8 @@ int ControlPanel::execute() {
 
 		} else if (Res.KeyConstants.DialogsControlPanel.KEY_FXON == _buttonValue) {
 			sound.setFxOn(!sound._fxOn);
+			if (sound._fxOn)
+				sound.playFX(20);
 
 		} else if (Res.KeyConstants.DialogsControlPanel.KEY_MUSICON == _buttonValue) {
 			sound.setMusicOn(!sound._musicOn);




More information about the Scummvm-git-logs mailing list