[Scummvm-git-logs] scummvm master -> c853d0c8a22024f55aa6adcf9587bf6ebc0d1309

dreammaster noreply at scummvm.org
Sat Feb 17 23:16:56 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:
7c8ed5b574 MM: MM1: Support for non-vanilla version
d1a10e38ca MM: XEEN: Fix sound effects regression
f19ac248bd MM: XEEN: Initial MT32/LAPC-1 support
44477f22f1 MM: XEEN: MidiDriver close and cleanup
a026371fe8 NEWS: Add news about Xeen engine improvements
13d9d5f9ab MM: XEEN: Better wording in some comments
c853d0c8a2 MM: XEEN: Added postprocessing in MT32 driver


Commit: 7c8ed5b5748c3f491cbf4282cde2f61db42eb5b0
    https://github.com/scummvm/scummvm/commit/7c8ed5b5748c3f491cbf4282cde2f61db42eb5b0
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T13:16:51-10: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 56c5c7d90b6..9ec4db90b9d 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: d1a10e38caabf8638819d29897db26c149ff126e
    https://github.com/scummvm/scummvm/commit/d1a10e38caabf8638819d29897db26c149ff126e
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T13:16:51-10: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 42fda150313..ac096afefe6 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 bf169d5984a..134f143d433 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: f19ac248bd25007d9c55df5fc908c3bdbf0bc31c
    https://github.com/scummvm/scummvm/commit/f19ac248bd25007d9c55df5fc908c3bdbf0bc31c
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T13:16:51-10: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 ac096afefe6..e227469021d 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::Path &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 134f143d433..cd6689bf9da 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::Path _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: 44477f22f116884dee93d3a8e89b4df6cee8ca92
    https://github.com/scummvm/scummvm/commit/44477f22f116884dee93d3a8e89b4df6cee8ca92
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T13:16:51-10: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: a026371fe81ac66a8f0eed292bd4d1f2deb06a95
    https://github.com/scummvm/scummvm/commit/a026371fe81ac66a8f0eed292bd4d1f2deb06a95
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T13:16:51-10:00

Commit Message:
NEWS: Add news about Xeen engine improvements

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 7fb1f9dd88f..85f08b50972 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -9,6 +9,10 @@ For a more comprehensive changelog of the latest experimental code, see:
  General:
   - Fixed GLSL version parsing on some OpenGL ES2 platforms
 
+ MM:
+   - Added MT32/LAPC-1 support for Xeen engine.
+   - Fixed Xeen regression which caused some sound effects to stop abruptly.
+
  TsAGE:
    - Added support for Russian CD fan-translation for Ringworld.
 


Commit: 13d9d5f9abab2d08cb9d92ab4537ac752caa314b
    https://github.com/scummvm/scummvm/commit/13d9d5f9abab2d08cb9d92ab4537ac752caa314b
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T13:16:51-10: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 e227469021d..a9869e597a1 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: c853d0c8a22024f55aa6adcf9587bf6ebc0d1309
    https://github.com/scummvm/scummvm/commit/c853d0c8a22024f55aa6adcf9587bf6ebc0d1309
Author: NoSFeRaTU (master.nosferatu at gmail.com)
Date: 2024-02-17T13:16:51-10: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