[Scummvm-git-logs] scummvm master -> 832791407cd4a1a0ebd067a4b879eb680cc53b9e

NMIError 60350957+NMIError at users.noreply.github.com
Thu Jul 29 18:26:08 UTC 2021


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:
12974b29e4 SAGA: Limit end of track resets to IHNM
e63b556015 SAGA: Add ITE MT-32 display message
aae00f50b1 AUDIO/MIDI: Added support for Miles v3 initialization
72c8622927 SAGA: Set Miles version on MIDI driver
9f0c28b751 SAGA2: Set Miles version on MIDI driver
2edc3773bc AUDIO/MIDI: Fix Miles AdLib fades
832791407c SAGA: Fix music fade-outs


Commit: 12974b29e4a3711662ce903841b59d029ac7e6e2
    https://github.com/scummvm/scummvm/commit/12974b29e4a3711662ce903841b59d029ac7e6e2
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-07-29T20:24:42+02:00

Commit Message:
SAGA: Limit end of track resets to IHNM

This limits the pitch bend and sustain reset messages at the end of MIDI tracks
to I Have No Mouth, And I Must Scream. Inherit The Earth does not use sustain
and pitch bend is already reset at the start of each track. IHNM does use
sustain and does not reset pitch bend at the start of each track.

Changed paths:
    engines/saga/music.cpp


diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index 507dd77ec7..f24a8e3f22 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -445,8 +445,12 @@ void Music::playMidi(uint32 resourceId, MusicFlags flags) {
 
 		_parser->setMidiDriver(_driver);
 		_parser->setTimerRate(_driver->getBaseTempo());
-		_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
-		_parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
+		if (_vm->getGameId() == GID_IHNM) {
+			// IHNM XMIDI uses sustain and does not reset pitch bend at the
+			// start of a new track.
+			_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+			_parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
+		}
 
 		// Handle music looping
 		_parser->property(MidiParser::mpAutoLoop, flags & MUSIC_LOOP);


Commit: e63b556015f8c5a69cb44c9ba2012bc6fe858f64
    https://github.com/scummvm/scummvm/commit/e63b556015f8c5a69cb44c9ba2012bc6fe858f64
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-07-29T20:24:42+02:00

Commit Message:
SAGA: Add ITE MT-32 display message

This adds the message that the DOS version of Inherit The Earth displays on the
MT-32 when quitting the game.

Changed paths:
    audio/mt32gm.cpp
    audio/mt32gm.h
    engines/saga/music.cpp
    engines/saga/music.h
    engines/saga/saga.cpp


diff --git a/audio/mt32gm.cpp b/audio/mt32gm.cpp
index c5df819ba1..98e416c21f 100644
--- a/audio/mt32gm.cpp
+++ b/audio/mt32gm.cpp
@@ -75,6 +75,9 @@ const uint8 MidiDriver_MT32GM::GS_DRUMKIT_FALLBACK_MAP[128] = {
 	 0,  0,  0,  0,  0,  0,  0, 127 // No drumkit defined; CM-64/32L (127)
 };
 
+const uint8 MidiDriver_MT32GM::MT32_DISPLAY_NUM_CHARS;
+const uint32 MidiDriver_MT32GM::MT32_DISPLAY_MEMORY_ADDRESS;
+
 // Callback hooked up to the driver wrapped by the MIDI driver
 // object. Executes onTimer and the external callback set by
 // the setTimerCallback function.
diff --git a/audio/mt32gm.h b/audio/mt32gm.h
index 0bb471de64..5410be3491 100644
--- a/audio/mt32gm.h
+++ b/audio/mt32gm.h
@@ -118,6 +118,9 @@ public:
 	// Map for correcting Roland GS drumkit numbers.
 	static const uint8 GS_DRUMKIT_FALLBACK_MAP[128];
 
+	static const uint8 MT32_DISPLAY_NUM_CHARS = 20;
+	static const uint32 MT32_DISPLAY_MEMORY_ADDRESS = 0x20 << 14;
+
 protected:
 	static const uint8 MAXIMUM_MT32_ACTIVE_NOTES = 48;
 	static const uint8 MAXIMUM_GM_ACTIVE_NOTES = 96;
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index f24a8e3f22..2da968c5b6 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -44,6 +44,8 @@
 
 namespace Saga {
 
+const uint8 Music::MT32_GOODBYE_MSG[] = { 0x47, 0x6F, 0x6F, 0x64, 0x62, 0x79, 0x65, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
+
 Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer), _parser(0), _driver(0), _driverPC98(0), _musicContext(0) {
 	_currentVolume = 0;
 	_currentMusicBuffer = NULL;
@@ -173,6 +175,18 @@ Music::~Music() {
 	}
 }
 
+void Music::close() {
+	if (_parser)
+		_parser->stopPlaying();
+
+	if (_vm->getGameId() == GID_ITE && _vm->getPlatform() == Common::kPlatformDOS && _driver) {
+		MidiDriver_MT32GM *mt32Driver = dynamic_cast<MidiDriver_MT32GM *>(_driver);
+		if (mt32Driver)
+			mt32Driver->sysExMT32(MT32_GOODBYE_MSG, MidiDriver_MT32GM::MT32_DISPLAY_NUM_CHARS,
+				MidiDriver_MT32GM::MT32_DISPLAY_MEMORY_ADDRESS, false, false);
+	}
+}
+
 void Music::musicVolumeGaugeCallback(void *refCon) {
 	((Music *)refCon)->musicVolumeGauge();
 }
diff --git a/engines/saga/music.h b/engines/saga/music.h
index 29e10c4a2b..c888541dad 100644
--- a/engines/saga/music.h
+++ b/engines/saga/music.h
@@ -27,6 +27,7 @@
 
 #include "audio/mididrv.h"
 #include "audio/mididrv_ms.h"
+#include "audio/mt32gm.h"
 #include "audio/midiparser.h"
 #include "audio/mixer.h"
 #include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
@@ -43,10 +44,12 @@ enum MusicFlags {
 class Music {
 private:
 	static const uint8 MUSIC_SUNSPOT = 26;
+	static const uint8 MT32_GOODBYE_MSG[MidiDriver_MT32GM::MT32_DISPLAY_NUM_CHARS];
 
 public:
 	Music(SagaEngine *vm, Audio::Mixer *mixer);
 	~Music();
+	void close();
 	bool isPlaying();
 	bool hasDigitalMusic() { return _digitalMusic; }
 
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 070173ce0a..ddc2eb066e 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -366,6 +366,8 @@ Common::Error SagaEngine::run() {
 		_system->delayMillis(10);
 	}
 
+	_music->close();
+
 	return Common::kNoError;
 }
 


Commit: aae00f50b1d103edab4e5b2e601c158398e88c3b
    https://github.com/scummvm/scummvm/commit/aae00f50b1d103edab4e5b2e601c158398e88c3b
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-07-29T20:24:42+02:00

Commit Message:
AUDIO/MIDI: Added support for Miles v3 initialization

This adds a MidiDriver property which controls the Miles AIL/MSS version to
emulate. This controls the initialization of MT-32 and GM devices. MSS v3 added
GM support, which caused some differences between v2 and v3 in how MIDI devices
are initialized. Some games might rely on certain default settings for correct
playback.

Changed paths:
    audio/mididrv.h
    audio/miles.h
    audio/miles_midi.cpp


diff --git a/audio/mididrv.h b/audio/mididrv.h
index 49df1e8e9e..8f9eb2ea90 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -421,7 +421,18 @@ public:
 		 * requires MIDI channels to be monophonic (i.e. only play one
 		 * note at a time).
 		 */
-		PROP_OPL_CHANNEL_ALLOCATION_MODE = 8
+		PROP_OPL_CHANNEL_ALLOCATION_MODE = 8,
+		/**
+		 * Set this property to specify the Miles AIL/MSS version that the
+		 * Miles drivers should emulate.
+		 * 
+		 * MILES_VERSION_2: behavior matches Miles AIL versions 1 and 2.
+		 * Specifically, GM devices are initialized like the MT-32 because
+		 * these versions do not yet support GM.
+		 * MILES_VERSION_3: behavior matches Miles Sound System version 3 and
+		 * higher. GM devices are initialized according to the GM standard.
+		 */
+		PROP_MILES_VERSION = 9
 	};
 
 	/**
diff --git a/audio/miles.h b/audio/miles.h
index dc2f117211..7a1d689f42 100644
--- a/audio/miles.h
+++ b/audio/miles.h
@@ -118,6 +118,11 @@ public:
 
 class MidiDriver_Miles_Midi : public MidiDriver_MT32GM, public MidiDriver_Miles_Xmidi_Timbres {
 public:
+	enum MilesVersion {
+		MILES_VERSION_2 = 2,
+		MILES_VERSION_3
+	};
+
 	MidiDriver_Miles_Midi(MusicType midiType, MilesMT32InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount);
 	~MidiDriver_Miles_Midi();
 
@@ -135,6 +140,8 @@ public:
 
 	void stopAllNotes(bool stopSustainedNotes = false) override;
 
+	uint32 property(int prop, uint32 param) override;
+
 	void processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize) override;
 
 protected:
@@ -277,6 +284,9 @@ private:
 		}
 	};
 
+	// the version of Miles AIL/MSS to emulate
+	MilesVersion _milesVersion;
+
 	// stores information about all MIDI channels
 	MidiChannelEntry _midiChannels[MIDI_CHANNEL_COUNT];
 
diff --git a/audio/miles_midi.cpp b/audio/miles_midi.cpp
index 17df8ff2d5..8c841f4c0e 100644
--- a/audio/miles_midi.cpp
+++ b/audio/miles_midi.cpp
@@ -49,7 +49,7 @@ const byte milesMT32SysExInitReverb[] = {
 };
 
 MidiDriver_Miles_Midi::MidiDriver_Miles_Midi(MusicType midiType, MilesMT32InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount) :
-		MidiDriver_MT32GM(midiType), _noteCounter(0) {
+		MidiDriver_MT32GM(midiType), _noteCounter(0), _milesVersion(MILES_VERSION_2) {
 	memset(_patchesBank, 0, sizeof(_patchesBank));
 
 	_instrumentTablePtr = instrumentTablePtr;
@@ -98,35 +98,75 @@ void MidiDriver_Miles_Midi::initMidiDevice() {
 	}
 
 	// Set Miles default controller values
-	// Note that AIL/MSS apparently did not get full support for GM until
-	// version 3.00 in 09/1994. Many games used the MT-32 driver to
-	// implement GM support. As a result, default parameters were only sent
-	// out on the MT-32 channels (2-10). Also, the default MT-32 instrument
-	// numbers were set on GM devices, even though they map to different
-	// instruments. This is reproduced here to prevent possible issues with
-	// games that depend on this behavior.
-
-	for (int i = 1; i < 10; ++i) {
-		// Volume 7F (max)
-		send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_VOLUME, 0x7F);
-		if (_midiType == MT_MT32) {
-			// Panning center - not the MT-32 default for all channels
-			send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_PANNING, 0x40);
-		}
-		// Patch
-		if (i != MIDI_RHYTHM_CHANNEL) {
-			if (_midiType == MT_GM) {
+	if (_milesVersion == MILES_VERSION_2) {
+		// Note that AIL/MSS apparently did not get full support for GM until
+		// version 3.00 in 09/1994. Many games used the MT-32 driver to
+		// implement GM support. As a result, default parameters were only sent
+		// out on the MT-32 channels (2-10). Also, the default MT-32 instrument
+		// numbers were set on GM devices, even though they map to different
+		// instruments. This is reproduced here to prevent possible issues with
+		// games that depend on this behavior.
+
+		for (int i = 1; i < 10; ++i) {
+			// Volume 7F (max)
+			send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_VOLUME, 0x7F);
+			if (_midiType == MT_MT32) {
+				// Panning center - not the MT-32 default for all channels
+				send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_PANNING, 0x40);
+			}
+			// Patch
+			if (_midiType == MT_GM && i != MIDI_RHYTHM_CHANNEL) {
 				// Send the MT-32 default instrument numbers out to GM devices.
 				send(-1, MIDI_COMMAND_PROGRAM_CHANGE | i, MT32_DEFAULT_INSTRUMENTS[i - 1], 0);
 			}
+			// The following settings are also sent out by the AIL driver:
+			// - Modulation 0
+			// - Expression 7F (max)
+			// - Sustain off
+			// - Pitch bend neutral
+			// These are the default MT-32 and GM settings, so it is not
+			// necessary to send these.
+		}
+	} else {
+		// MSS 3 initialization
+		for (int i = (_midiType == MT_GM ? 0 : 1); i < (_midiType == MT_GM ? MIDI_CHANNEL_COUNT : 10); ++i) {
+			// Patch
+			if (_midiType == MT_MT32 && i != MIDI_RHYTHM_CHANNEL) {
+				// Set instrument to 0.
+				send(-1, MIDI_COMMAND_PROGRAM_CHANGE | i, 0, 0);
+			}
+			// Volume 7F (max)
+			send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_VOLUME, 0x7F);
+			if (_midiType == MT_MT32) {
+				// Panning center - not the MT-32 default for all channels
+				send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_PANNING, 0x40);
+			}
+			if (_midiType == MT_GM) {
+				// Reverb 28h
+				send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_REVERB, 0x28);
+			}
+			// Pitch bend range 2 semitones
+			// TODO Some games seem to initialize this to a different value, so
+			// this might need to be configurable.
+			send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_RPN_LSB, 0x00);
+			send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_RPN_MSB, 0x00);
+			if (_midiType == MT_GM) {
+				// MT-32 does not respond to the LSB, so only send it out for GM
+				send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_DATA_ENTRY_LSB, 0x00);
+			}
+			send(-1, MIDI_COMMAND_CONTROL_CHANGE | i, MIDI_CONTROLLER_DATA_ENTRY_MSB, 0x02);
+
+			// MSS 3 also sets the following settings:
+			// - Program 0 (also for GM)
+			// - Pitch bend neutral
+			// - Modulation 0
+			// - Panning center (also for GM)
+			// - Expression 7F
+			// - Sustain off
+			// - Chorus 0
+			// These are the default settings, so it is not necessary to send
+			// these.
 		}
-		// The following settings are also sent out by the AIL driver:
-		// - Modulation 0
-		// - Expression 7F (max)
-		// - Sustain off
-		// - Pitch bend neutral
-		// These are the default MT-32 and GM settings, so it is not
-		// necessary to send these.
 	}
 }
 
@@ -892,4 +932,26 @@ void MidiDriver_Miles_Midi::applySourceVolume(uint8 source) {
 	}
 }
 
+uint32 MidiDriver_Miles_Midi::property(int prop, uint32 param) {
+	switch (prop) {
+	case PROP_MILES_VERSION:
+		if (param == 0xFFFF)
+			return _milesVersion;
+
+		switch (param) {
+		case MILES_VERSION_3:
+			_milesVersion = MILES_VERSION_3;
+			break;
+		case MILES_VERSION_2:
+		default:
+			_milesVersion = MILES_VERSION_2;
+		}
+
+		break;
+	default:
+		return MidiDriver_Multisource::property(prop, param);
+	}
+	return 0;
+}
+
 } // End of namespace Audio


Commit: 72c862292761a18884fb8d09b6159739a6b7f96e
    https://github.com/scummvm/scummvm/commit/72c862292761a18884fb8d09b6159739a6b7f96e
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-07-29T20:24:42+02:00

Commit Message:
SAGA: Set Miles version on MIDI driver

This sets the version to emulate on the Miles MIDI driver so the MIDI devices
are initialized in the same way as the original interpreter.

Changed paths:
    engines/saga/music.cpp


diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index 2da968c5b6..d7ab5b6df2 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -87,6 +87,8 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer), _par
 		case MT_GM:
 			if (_vm->getPlatform() == Common::kPlatformDOS) {
 				_driver = Audio::MidiDriver_Miles_MIDI_create(_musicType, "");
+				_driver->property(MidiDriver::PROP_MILES_VERSION, _vm->getGameId() == GID_ITE ?
+					Audio::MidiDriver_Miles_Midi::MILES_VERSION_2 : Audio::MidiDriver_Miles_Midi::MILES_VERSION_3);
 			} else {
 				_driver = new MidiDriver_MT32GM(_musicType);
 			}


Commit: 9f0c28b751b758aaf4720eb8f359f791af520ccf
    https://github.com/scummvm/scummvm/commit/9f0c28b751b758aaf4720eb8f359f791af520ccf
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-07-29T20:24:42+02:00

Commit Message:
SAGA2: Set Miles version on MIDI driver

This sets the version to emulate on the Miles MIDI driver so the MIDI devices
are initialized in the same way as the original interpreter.

Changed paths:
    engines/saga2/music.cpp


diff --git a/engines/saga2/music.cpp b/engines/saga2/music.cpp
index 2c4d1e2e72..1a92f62c8e 100644
--- a/engines/saga2/music.cpp
+++ b/engines/saga2/music.cpp
@@ -59,6 +59,7 @@ Music::Music(hResContext *musicRes) : _musicContext(musicRes), _parser(0) {
 	case MT_MT32:
 	case MT_GM:
 		_driver = Audio::MidiDriver_Miles_MIDI_create(_musicType, "");
+		_driver->property(MidiDriver::PROP_MILES_VERSION, Audio::MidiDriver_Miles_Midi::MILES_VERSION_3);
 		break;
 	default:
 		_driver = new MidiDriver_NULL_Multisource();


Commit: 2edc3773bc6e645edb2c0f818b864ebc075beee6
    https://github.com/scummvm/scummvm/commit/2edc3773bc6e645edb2c0f818b864ebc075beee6
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-07-29T20:24:42+02:00

Commit Message:
AUDIO/MIDI: Fix Miles AdLib fades

This fixes some issues which caused the fades in the Miles AdLib driver to not
work properly:
- _timerRate was not set
- end of track meta event was not handled, so active fades were not aborted by
deinitializing the source on end of track
The timerProc fields and onTimer function were removed because they duplicate
functionality from the MidiDriver_Multisource base class.

Changed paths:
    audio/miles_adlib.cpp


diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp
index 6b8127a0fd..23eddb9c39 100644
--- a/audio/miles_adlib.cpp
+++ b/audio/miles_adlib.cpp
@@ -127,6 +127,7 @@ public:
 	void close() override;
 	void send(uint32 b) override;
 	void send(int8 source, uint32 b) override;
+	void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
 	MidiChannel *allocateChannel() override { return NULL; }
 	MidiChannel *getPercussionChannel() override { return NULL; }
 
@@ -135,11 +136,10 @@ public:
 
 	void stopAllNotes(uint8 source, uint8 channel) override;
 	void applySourceVolume(uint8 source) override;
+	void deinitSource(uint8 source) override;
 
 	void setVolume(byte volume);
 
-	void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override;
-
 private:
 	bool _modeOPL3;
 	byte _modePhysicalFmVoicesCount;
@@ -222,9 +222,6 @@ private:
 	OPL::OPL *_opl;
 	int _masterVolume;
 
-	Common::TimerManager::TimerProc _adlibTimerProc;
-	void *_adlibTimerParam;
-
 	bool _isOpen;
 
 	// stores information about all MIDI channels (not the actual OPL FM voice channels!)
@@ -243,8 +240,6 @@ private:
 	bool circularPhysicalAssignment;
 	byte circularPhysicalAssignmentFmVoice;
 
-	void onTimer();
-
 	void resetData();
 	void resetAdLib();
 	void resetAdLibOperatorRegisters(byte baseRegister, byte value);
@@ -275,8 +270,7 @@ private:
 };
 
 MidiDriver_Miles_AdLib::MidiDriver_Miles_AdLib(InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount)
-	: _masterVolume(15), _opl(0),
-	  _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) {
+	: _masterVolume(15), _opl(0), _isOpen(false) {
 
 	_instrumentTablePtr = instrumentTablePtr;
 	_instrumentTableCount = instrumentTableCount;
@@ -325,6 +319,7 @@ int MidiDriver_Miles_AdLib::open() {
 
 	_isOpen = true;
 
+	_timerRate = getBaseTempo();
 	_opl->start(new Common::Functor0Mem<void, MidiDriver_Miles_AdLib>(this, &MidiDriver_Miles_AdLib::onTimer));
 
 	resetAdLib();
@@ -342,11 +337,6 @@ void MidiDriver_Miles_AdLib::setVolume(byte volume) {
 	//renewNotes(-1, true);
 }
 
-void MidiDriver_Miles_AdLib::onTimer() {
-	if (_adlibTimerProc)
-		(*_adlibTimerProc)(_adlibTimerParam);
-}
-
 void MidiDriver_Miles_AdLib::resetData() {
 	ARRAYCLEAR(_midiChannels);
 	ARRAYCLEAR(_virtualFmVoices);
@@ -469,11 +459,6 @@ void MidiDriver_Miles_AdLib::applySourceVolume(uint8 source) {
 	}
 }
 
-void MidiDriver_Miles_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
-	_adlibTimerProc = timerProc;
-	_adlibTimerParam = timerParam;
-}
-
 int16 MidiDriver_Miles_AdLib::searchFreeVirtualFmVoiceChannel() {
 	for (byte virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) {
 		if (!_virtualFmVoices[virtualFmVoice].inUse)
@@ -1093,6 +1078,25 @@ void MidiDriver_Miles_AdLib::pitchBendChange(byte midiChannel, byte parameter1,
 	}
 }
 
+void MidiDriver_Miles_AdLib::metaEvent(int8 source, byte type, byte *data, uint16 length) {
+	if (type == MIDI_META_END_OF_TRACK && source >= 0)
+		// Stop hanging notes and release resources used by this source.
+		deinitSource(source);
+}
+
+void MidiDriver_Miles_AdLib::deinitSource(uint8 source) {
+	if (!(source == 0 || source == 0xFF))
+		return;
+
+	// Turn off sustained notes.
+	for (int i = 0; i < MIDI_CHANNEL_COUNT; i++) {
+		controlChange(i, MIDI_CONTROLLER_SUSTAIN, 0);
+	}
+
+	// Stop fades and turn off non-sustained notes.
+	MidiDriver_Multisource::deinitSource(source);
+}
+
 void MidiDriver_Miles_AdLib::setRegister(int reg, int value) {
 	if (!(reg & 0x100)) {
 		_opl->write(0x220, reg);


Commit: 832791407cd4a1a0ebd067a4b879eb680cc53b9e
    https://github.com/scummvm/scummvm/commit/832791407cd4a1a0ebd067a4b879eb680cc53b9e
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-07-29T20:24:43+02:00

Commit Message:
SAGA: Fix music fade-outs

This fixes the fade-outs in IHNM, most notably at the end of the title screen
when using GM. Fades would take 30 seconds instead of the intended 3, and the
engine did not wait for the fade to finish but immediately played the next
track, which aborted the fade.
Note that the engine still does not always wait for a fade to finish when it is
triggered from a script. For example after AM's speech when dying in
Gorrister's chapter, in the original interpreter the music fades out while the
screen fades to black. In ScummVM, the screen does not fade out, so the music
fade is immediately aborted by playing the next track.

Changed paths:
    engines/saga/introproc_ihnm.cpp
    engines/saga/music.cpp
    engines/saga/music.h
    engines/saga/scene.h


diff --git a/engines/saga/introproc_ihnm.cpp b/engines/saga/introproc_ihnm.cpp
index c7a9357f59..eda32fadc8 100644
--- a/engines/saga/introproc_ihnm.cpp
+++ b/engines/saga/introproc_ihnm.cpp
@@ -68,7 +68,7 @@ int Scene::IHNMStartProc() {
 				// Play the title music
 				_vm->_music->play(1, MUSIC_NORMAL);
 				// Play title screen
-				playTitle(2, _vm->_music->isAdlib() ? 20 : 27);
+				playTitle(2, 20);
 			}
 		}
 	} else {
@@ -79,7 +79,10 @@ int Scene::IHNMStartProc() {
 		playTitle(2, 12);
 	}
 
-	_vm->_music->setVolume(0, 1000);
+	fadeMusic();
+	if (_vm->shouldQuit())
+		return !SUCCESS;
+
 	_vm->_anim->clearCutawayList();
 
 	// Queue first scene
@@ -109,7 +112,7 @@ int Scene::IHNMCreditsProc() {
 		playTitle(3, 60, true);
 	}
 
-	_vm->_music->setVolume(0, 1000);
+	fadeMusic();
 	_vm->_anim->clearCutawayList();
 
 	return SUCCESS;
@@ -163,6 +166,19 @@ bool Scene::checkKey() {
 	return res;
 }
 
+void Scene::fadeMusic() {
+	if (!_vm->_music->isPlaying())
+		return;
+
+	_vm->_music->setVolume(0, 1000);
+	while (!_vm->shouldQuit() && _vm->_music->isFading()) {
+		_vm->_system->delayMillis(10);
+		if (checkKey()) {
+			_vm->_music->setVolume(0);
+		}
+	}
+}
+
 bool Scene::playTitle(int title, int time, int mode) {
 	bool interrupted = false;
 	int startTime = _vm->_system->getMillis();
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index d7ab5b6df2..d45194fe37 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -154,7 +154,7 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer), _par
 	_userVolume = 0;
 	_userMute = false;
 	_targetVolume = 0;
-	_currentVolumePercent = 0;
+	_currentVolumePercent = 100;
 
 	_digitalMusic = false;
 }
@@ -229,7 +229,6 @@ void Music::musicVolumeGauge() {
 
 void Music::setVolume(int volume, int time) {
 	_targetVolume = volume;
-	_currentVolumePercent = 0;
 
 	if (volume == -1) // Set Full volume
 		volume = 255;
@@ -241,6 +240,7 @@ void Music::setVolume(int volume, int time) {
 			_driver->setSourceVolume(0, volume);
 		}
 
+		_currentVolumePercent = 100;
 		_vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback);
 
 		int scaledVolume;
@@ -259,12 +259,10 @@ void Music::setVolume(int volume, int time) {
 	}
 
 	if (_driver)
-		_driver->startFade(0, time * 30, volume);
-	// TODO When doing a fade-out, this function is called with time 1000.
-	// Each callback decreases volume by 10%, so it takes 10 * 1000 * 3000 us =
-	// 30 seconds. That doesn't seem right. Also, the game does not wait for
-	// the fade-out to complete, but immediately plays the next track.
-	_vm->getTimerManager()->installTimerProc(&musicVolumeGaugeCallback, time * 3000L, this, "sagaMusicVolume");
+		_driver->startFade(0, time * 3, volume);
+
+	_currentVolumePercent = 0;
+	_vm->getTimerManager()->installTimerProc(&musicVolumeGaugeCallback, time * 300L, this, "sagaMusicVolume");
 }
 
 void Music::resetVolume() {
@@ -272,6 +270,15 @@ void Music::resetVolume() {
 	setVolume(255);
 }
 
+bool Music::isFading() {
+	bool isFading = false;
+	if (_driver)
+		isFading = _driver->isFading(0);
+	isFading = isFading || (_currentVolumePercent < 100);
+
+	return isFading;
+}
+
 bool Music::isPlaying() {
 
 	return _mixer->isSoundHandleActive(_musicHandle) || (_parser ? _parser->isPlaying() : false) || (_driverPC98 ? _driverPC98->musicPlaying() : false);
diff --git a/engines/saga/music.h b/engines/saga/music.h
index c888541dad..aa8b68ff0c 100644
--- a/engines/saga/music.h
+++ b/engines/saga/music.h
@@ -61,6 +61,7 @@ public:
 	void setVolume(int volume, int time = 1);
 	int getVolume() { return _currentVolume; }
 	void resetVolume();
+	bool isFading();
 
 	bool isAdlib() const { return  _driverType == MT_ADLIB; }
 
diff --git a/engines/saga/scene.h b/engines/saga/scene.h
index a3700004df..e51cebf2d7 100644
--- a/engines/saga/scene.h
+++ b/engines/saga/scene.h
@@ -358,6 +358,7 @@ class Scene {
 
 	void IHNMLoadCutaways();
 	bool checkKey();
+	void fadeMusic();
 
 	bool playTitle(int title, int time, int mode = kPanelVideo);
 	bool playLoopingTitle(int title, int seconds);




More information about the Scummvm-git-logs mailing list