[Scummvm-git-logs] scummvm master -> 4a0c6a93a2e1d12ae1d57e0928f7af6c5f3e8841

NMIError 60350957+NMIError at users.noreply.github.com
Thu Aug 12 12:35:59 UTC 2021


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

Summary:
dfb8db2ae2 AUDIO/MIDI: Add dual OPL2 support to Miles AdLib driver
9ab34c4166 ULTIMA8: Fix music transitions playing incorrectly
4a0c6a93a2 ULTIMA8: Fix overlaid music transitions


Commit: dfb8db2ae2ddb6dca368fe5a7b3b819be0951c11
    https://github.com/scummvm/scummvm/commit/dfb8db2ae2ddb6dca368fe5a7b3b819be0951c11
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-08-12T14:30:52+02:00

Commit Message:
AUDIO/MIDI: Add dual OPL2 support to Miles AdLib driver

This adds support for the dual OPL2 chip configuration to the Miles AdLib MIDI
driver.

Changed paths:
    audio/miles_adlib.cpp


diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp
index 74311289f6..daf4ab2500 100644
--- a/audio/miles_adlib.cpp
+++ b/audio/miles_adlib.cpp
@@ -116,6 +116,18 @@ uint16 milesAdLibVolumeSensitivityTable[] = {
 	82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127
 };
 
+// MIDI panning to register volume table for dual OPL2
+// hardcoded, dumped from ADLIB.MDI
+uint8 milesAdLibPanningVolumeLookUpTable[] = {
+	0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+	32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
+	64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94,
+	96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 127,
+	127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+	127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+	127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+	127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
+};
 
 class MidiDriver_Miles_AdLib : public MidiDriver_Multisource {
 public:
@@ -251,6 +263,7 @@ private:
 	void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value);
 
 	void setRegister(int reg, int value);
+	void setRegisterStereo(uint8 reg, uint8 valueLeft, uint8 valueRight);
 
 	int16 searchFreeVirtualFmVoiceChannel();
 	int16 searchFreePhysicalFmVoiceChannel();
@@ -308,14 +321,14 @@ int MidiDriver_Miles_AdLib::open() {
 		// Try to create OPL3 first
 		_opl = OPL::Config::create(OPL::Config::kOpl3);
 	}
-	// TODO Add support for dual OPL2
+	if (!_opl) {
+		// not created yet, downgrade to dual OPL2
+		_oplType = OPL::Config::kDualOpl2;
+		_opl = OPL::Config::create(OPL::Config::kDualOpl2);
+	}
 	if (!_opl) {
 		// not created yet, downgrade to OPL2
 		_oplType = OPL::Config::kOpl2;
-		_modeVirtualFmVoicesCount = 16;
-		_modePhysicalFmVoicesCount = 9;
-		_modeStereo = false;
-
 		_opl = OPL::Config::create(OPL::Config::kOpl2);
 	}
 
@@ -324,6 +337,12 @@ int MidiDriver_Miles_AdLib::open() {
 		return -1;
 	}
 
+	if (_oplType != OPL::Config::kOpl3) {
+		_modeVirtualFmVoicesCount = 16;
+		_modePhysicalFmVoicesCount = 9;
+		_modeStereo = false;
+	}
+
 	_opl->init();
 
 	_isOpen = true;
@@ -778,6 +797,8 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
 	uint16 channelReg = milesAdLibChannelRegister[physicalFmVoice];
 
 	uint16 compositeVolume = 0;
+	uint8 leftVolume = 0;
+	uint8 rightVolume = 0;
 
 	if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_40) {
 		// Calculate new volume
@@ -807,6 +828,19 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
 		}
 		// Source volume scaling might clip volume, so reduce to maximum.
 		compositeVolume = MIN(compositeVolume, (uint16)0x7F);
+
+		if (_oplType == OPL::Config::kDualOpl2) {
+			// For dual OPL2, Miles pans the notes by playing the same note on
+			// the left and right OPL2 chips at different volume levels.
+			// Calculate the volume for each chip based on the panning value.
+			leftVolume = (milesAdLibPanningVolumeLookUpTable[_midiChannels[midiChannel].currentPanning] * compositeVolume) >> 7;
+			if (leftVolume)
+				leftVolume++; // round up in case result wasn't 0
+			uint8 invertedPanning = 0 - (_midiChannels[midiChannel].currentPanning - 127);
+			rightVolume = (milesAdLibPanningVolumeLookUpTable[invertedPanning] * compositeVolume) >> 7;
+			if (rightVolume)
+				rightVolume++; // round up in case result wasn't 0
+		}
 	}
 
 	if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_20) {
@@ -831,22 +865,52 @@ void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool key
 		uint16 volumeOp1 = (~reg40op1) & 0x3F;
 		uint16 volumeOp2 = (~reg40op2) & 0x3F;
 
-		if (instrumentPtr->regC0 & 1) {
-			// operator 2 enabled
-			// scale volume factor
-			volumeOp1 = (volumeOp1 * compositeVolume) / 127;
-			// 2nd operator always scaled
-		}
+		if (_oplType != OPL::Config::kDualOpl2) {
+			if (instrumentPtr->regC0 & 1) {
+				// operator 2 enabled
+				// scale volume factor
+				volumeOp1 = (volumeOp1 * compositeVolume) / 127;
+				// 2nd operator always scaled
+			}
+
+			volumeOp2 = (volumeOp2 * compositeVolume) / 127;
+
+			volumeOp1 = (~volumeOp1) & 0x3F; // negate it, so we get the proper value for the register
+			volumeOp2 = (~volumeOp2) & 0x3F; // ditto
+			reg40op1  = (reg40op1 & 0xC0) | volumeOp1; // keep "scaling level" and merge in our volume
+			reg40op2  = (reg40op2 & 0xC0) | volumeOp2;
 
-		volumeOp2 = (volumeOp2 * compositeVolume) / 127;
+			setRegister(0x40 + op1Reg, reg40op1);
+			setRegister(0x40 + op2Reg, reg40op2);
+		} else {
+			// For dual OPL2, separate register values are calculated for the
+			// left and right OPL2 chip.
+			uint8 volumeLeftOp1 = volumeOp1;
+			uint8 volumeRightOp1 = volumeOp1;
+
+			if (instrumentPtr->regC0 & 1) {
+				// operator 2 enabled
+				// scale volume factor
+				volumeLeftOp1 = (volumeLeftOp1 * leftVolume) / 127;
+				volumeRightOp1 = (volumeRightOp1 * rightVolume) / 127;
+				// 2nd operator always scaled
+			}
+
+			uint8 volumeLeftOp2 = (volumeOp2 * leftVolume) / 127;
+			uint8 volumeRightOp2 = (volumeOp2 * rightVolume) / 127;
 
-		volumeOp1 = (~volumeOp1) & 0x3F; // negate it, so we get the proper value for the register
-		volumeOp2 = (~volumeOp2) & 0x3F; // ditto
-		reg40op1  = (reg40op1 & 0xC0) | volumeOp1; // keep "scaling level" and merge in our volume
-		reg40op2  = (reg40op2 & 0xC0) | volumeOp2;
+			volumeLeftOp1 = (~volumeLeftOp1) & 0x3F; // negate it, so we get the proper value for the register
+			volumeRightOp1 = (~volumeRightOp1) & 0x3F;
+			volumeLeftOp2 = (~volumeLeftOp2) & 0x3F; // ditto
+			volumeRightOp2 = (~volumeRightOp2) & 0x3F;
+			uint8 reg40op1left = (reg40op1 & 0xC0) | volumeLeftOp1; // keep "scaling level" and merge in our volume
+			uint8 reg40op1right = (reg40op1 & 0xC0) | volumeRightOp1;
+			uint8 reg40op2left = (reg40op2 & 0xC0) | volumeLeftOp2;
+			uint8 reg40op2right = (reg40op2 & 0xC0) | volumeRightOp2;
 
-		setRegister(0x40 + op1Reg, reg40op1);
-		setRegister(0x40 + op2Reg, reg40op2);
+			setRegisterStereo(0x40 + op1Reg, reg40op1left, reg40op1right);
+			setRegisterStereo(0x40 + op2Reg, reg40op2left, reg40op2right);
+		}
 	}
 
 	if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_60) {
@@ -1157,6 +1221,13 @@ void MidiDriver_Miles_AdLib::setRegister(int reg, int value) {
 	}
 }
 
+void MidiDriver_Miles_AdLib::setRegisterStereo(uint8 reg, uint8 valueLeft, uint8 valueRight) {
+	_opl->write(0x220, reg);
+	_opl->write(0x221, valueLeft);
+	_opl->write(0x222, reg);
+	_opl->write(0x223, valueRight);
+}
+
 MidiDriver_Multisource *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, const Common::String &filenameOPL3, Common::SeekableReadStream *streamAdLib, Common::SeekableReadStream *streamOPL3) {
 	// Load adlib instrument data from file SAMPLE.AD (OPL3: SAMPLE.OPL)
 	Common::String              timbreFilename;


Commit: 9ab34c4166e1bbaa269f13389d5376a4b6b5e4c8
    https://github.com/scummvm/scummvm/commit/9ab34c4166e1bbaa269f13389d5376a4b6b5e4c8
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-08-12T14:30:52+02:00

Commit Message:
ULTIMA8: Fix music transitions playing incorrectly

The music transition numbers from the song info data were incorrectly parsed.
For overlaid transition numbers starting with !, the first character is
skipped when parsing the number, but this was also done for instant transition
numbers which do not start with !, resulting in incorrect numbers. Also, the
overlaid transition numbers were 1 lower than what the music process was
expecting.
After a transition was started, the music process' run method would immediately
stop the transition and start the next track. This is fixed by waiting for the
transition to finish before starting the next track.

Changed paths:
    engines/ultima/ultima8/audio/music_flex.cpp
    engines/ultima/ultima8/audio/u8_music_process.cpp


diff --git a/engines/ultima/ultima8/audio/music_flex.cpp b/engines/ultima/ultima8/audio/music_flex.cpp
index 3670ec355e..1a11a8b0be 100644
--- a/engines/ultima/ultima8/audio/music_flex.cpp
+++ b/engines/ultima/ultima8/audio/music_flex.cpp
@@ -227,9 +227,9 @@ void MusicFlex::loadSongInfo() {
 
 			// Overlayed
 			if (*str == '!')
-				num = -1 - atoi(str + 1);
+				num = 0 - atoi(str + 1);
 			else
-				num = atoi(str + 1);
+				num = atoi(str);
 
 			_info[fi]->_transitions[ti][m] = num;
 		}
diff --git a/engines/ultima/ultima8/audio/u8_music_process.cpp b/engines/ultima/ultima8/audio/u8_music_process.cpp
index a164cd8859..159506925b 100644
--- a/engines/ultima/ultima8/audio/u8_music_process.cpp
+++ b/engines/ultima/ultima8/audio/u8_music_process.cpp
@@ -203,11 +203,9 @@ void U8MusicProcess::run() {
 		break;
 
 	case PLAYBACK_TRANSITION:
-		if (!_midiPlayer) {
+		if (!_midiPlayer || !_midiPlayer->isPlaying()) {
+			// Transition has finished. Play the next track.
 			_state = PLAYBACK_PLAY_WANTED;
-		} else {
-			_state = PLAYBACK_PLAY_WANTED;
-			_midiPlayer->stop();
 		}
 		break;
 


Commit: 4a0c6a93a2e1d12ae1d57e0928f7af6c5f3e8841
    https://github.com/scummvm/scummvm/commit/4a0c6a93a2e1d12ae1d57e0928f7af6c5f3e8841
Author: Coen Rampen (crampen at gmail.com)
Date: 2021-08-12T14:30:52+02:00

Commit Message:
ULTIMA8: Fix overlaid music transitions

Some music transitions are intended to play on top of the currently playing
track (mostly fade-outs). ScummVM would stop the current track and then play
the transition. This commit changes this so it overlays the transitions like
the original interpreter does.

Changed paths:
    engines/ultima/ultima8/audio/midi_player.cpp
    engines/ultima/ultima8/audio/midi_player.h
    engines/ultima/ultima8/audio/u8_music_process.cpp


diff --git a/engines/ultima/ultima8/audio/midi_player.cpp b/engines/ultima/ultima8/audio/midi_player.cpp
index 632798b559..3f7b2656ff 100644
--- a/engines/ultima/ultima8/audio/midi_player.cpp
+++ b/engines/ultima/ultima8/audio/midi_player.cpp
@@ -34,7 +34,7 @@ namespace Ultima8 {
 
 byte MidiPlayer::_callbackData[2];
 
-MidiPlayer::MidiPlayer() : _parser(nullptr) {
+MidiPlayer::MidiPlayer() : _parser(nullptr), _transitionParser(nullptr), _playingTransition(false) {
 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
 	MusicType musicType = MidiDriver::getMusicType(dev);
 
@@ -74,6 +74,12 @@ MidiPlayer::~MidiPlayer() {
 		_parser = 0;
 	}
 
+	if (_transitionParser) {
+		_transitionParser->unloadMusic();
+		delete _transitionParser;
+		_transitionParser = 0;
+	}
+
 	if (_driver) {
 		_driver->close();
 		delete _driver;
@@ -81,7 +87,7 @@ MidiPlayer::~MidiPlayer() {
 	}
 }
 
-void MidiPlayer::load(byte *data, size_t size, int seqNo, bool speedHack) {
+void MidiPlayer::load(byte *data, size_t size, int seqNo) {
 	if (!_driver)
 		return;
 
@@ -103,8 +109,6 @@ void MidiPlayer::load(byte *data, size_t size, int seqNo, bool speedHack) {
 
 		_parser->setMidiDriver(_driver);
 		_parser->setTimerRate(_driver->getBaseTempo());
-		if (speedHack)
-			_parser->setTempo(_driver->getBaseTempo() * 2);
 		_parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
 		_parser->property(MidiParser::mpDisableAutoStartPlayback, 1);
 
@@ -113,6 +117,22 @@ void MidiPlayer::load(byte *data, size_t size, int seqNo, bool speedHack) {
 	}
 }
 
+void MidiPlayer::loadTransitionData(byte* data, size_t size) {
+	if (size < 4)
+		error("loadTransitionData() wrong music resource size");
+
+	if (READ_BE_UINT32(data) != MKTAG('F', 'O', 'R', 'M'))
+		error("loadTransitionData() Unexpected signature");
+
+	_transitionParser = MidiParser::createParser_XMIDI(nullptr, nullptr, 0);
+	_transitionParser->setMidiDriver(_driver);
+	_transitionParser->setTimerRate(_driver->getBaseTempo());
+	_transitionParser->property(MidiParser::mpDisableAutoStartPlayback, 1);
+
+	if (!_transitionParser->loadMusic(data, size))
+		error("loadTransitionData() wrong music resource");
+}
+
 void MidiPlayer::play(int trackNo, int branchIndex) {
 	if (!_parser || !_driver)
 		return;
@@ -133,29 +153,61 @@ void MidiPlayer::play(int trackNo, int branchIndex) {
 	if (_driver->isFading(0))
 		_driver->abortFade(0);
 	_driver->resetSourceVolume(0);
+	if (_transitionParser) {
+		_transitionParser->stopPlaying();
+		_playingTransition = false;
+	}
 
 	if (!_parser->startPlaying()) {
 		warning("play() failed to start playing");
 	}
 }
 
+void MidiPlayer::playTransition(int trackNo, bool overlay) {
+	if (!overlay && _parser)
+		_parser->stopPlaying();
+
+	if (!_transitionParser) {
+		warning("playTransition() transition data not loaded");
+		if (_parser)
+			_parser->stopPlaying();
+		return;
+	}
+
+	_transitionParser->setTrack(trackNo);
+	if (overlay)
+		_transitionParser->setTempo(_driver->getBaseTempo() * 2);
+	_transitionParser->property(MidiParser::mpDisableAllNotesOffMidiEvents, overlay);
+
+	_transitionParser->startPlaying();
+	_playingTransition = true;
+}
+
 void MidiPlayer::stop() {
 	if (_parser)
 		_parser->stopPlaying();
+	if (_transitionParser) {
+		_transitionParser->stopPlaying();
+		_playingTransition = false;
+	}
 }
 
 void MidiPlayer::pause(bool pause) {
-	if (_parser) {
-		if (pause) {
+	if (pause) {
+		if (_parser)
 			_parser->pausePlaying();
-		} else {
+		if (_transitionParser)
+			_transitionParser->pausePlaying();
+	} else {
+		if (_parser)
 			_parser->resumePlaying();
-		}
+		if (_transitionParser)
+			_transitionParser->resumePlaying();
 	}
 }
 
 bool MidiPlayer::isPlaying() {
-	return _parser && _parser->isPlaying();
+	return (_parser && _parser->isPlaying()) || _playingTransition;
 }
 
 void MidiPlayer::startFadeOut(uint16 length) {
@@ -191,6 +243,17 @@ void MidiPlayer::xmidiCallback(byte eventData, void *data) {
 void MidiPlayer::onTimer() {
 	if (_parser)
 		_parser->onTimer();
+	if (_transitionParser) {
+		_transitionParser->onTimer();
+		if (_playingTransition && !_transitionParser->isPlaying()) {
+			// Transition has finished.
+			if (_parser)
+				// Stop the main track (which is still playing if the
+				// transition was overlaid).
+				_parser->stopPlaying();
+			_playingTransition = false;
+		}
+	}
 }
 
 void MidiPlayer::timerCallback(void *data) {
diff --git a/engines/ultima/ultima8/audio/midi_player.h b/engines/ultima/ultima8/audio/midi_player.h
index 08fd8cd28f..30ee6578d2 100644
--- a/engines/ultima/ultima8/audio/midi_player.h
+++ b/engines/ultima/ultima8/audio/midi_player.h
@@ -38,7 +38,13 @@ public:
 	/**
 	 * Load the specified music data
 	 */
-	void load(byte *data, size_t size, int seqNo, bool speedHack);
+	void load(byte *data, size_t size, int seqNo);
+
+	/**
+	 * Load the XMIDI data containing the transition tracks.
+	 * Call this function before calling playTransition.
+	 */
+	void loadTransitionData(byte *data, size_t size);
 
 	/**
 	 * Play the specified music track, starting at the
@@ -47,6 +53,15 @@ public:
 	 */
 	void play(int trackNo, int branchNo);
 
+	/**
+	 * Plays the specified transition track. If overlay is specified, the
+	 * transition is overlaid on the currently playing music track and this
+	 * track is stopped when the transition ends. If overlay is not specified,
+	 * the currently playing music track is stopped before the transition is
+	 * started.
+	 */
+	void playTransition(int trackNo, bool overlay);
+
 	/**
 	 * Stop the currently playing track.
 	 */
@@ -105,8 +120,10 @@ public:
 private:
 	MidiDriver_Multisource *_driver;
 	MidiParser *_parser;
+	MidiParser *_transitionParser;
 
 	bool _isFMSynth;
+	bool _playingTransition;
 	static byte _callbackData[2];
 };
 
diff --git a/engines/ultima/ultima8/audio/u8_music_process.cpp b/engines/ultima/ultima8/audio/u8_music_process.cpp
index 159506925b..5992c186b1 100644
--- a/engines/ultima/ultima8/audio/u8_music_process.cpp
+++ b/engines/ultima/ultima8/audio/u8_music_process.cpp
@@ -45,6 +45,12 @@ U8MusicProcess::U8MusicProcess(MidiPlayer *player) : _midiPlayer(player),
 	_theMusicProcess = this;
 	_type = 1; // persistent
 	setRunPaused();
+
+	// Now get the transition midi
+	MusicFlex *musicflex = GameData::get_instance()->getMusic();
+	int xmidi_index = _midiPlayer->isFMSynth() ? 260 : 258;
+	MusicFlex::XMidiData *xmidi = musicflex->getXMidi(xmidi_index);
+	_midiPlayer->loadTransitionData(xmidi->_data, xmidi->_size);
 }
 
 U8MusicProcess::~U8MusicProcess() {
@@ -163,28 +169,18 @@ void U8MusicProcess::playMusic_internal(int track) {
 
 		// Get transition info
 		int trans = info->_transitions[track][measure];
-		bool speed_hack = false;
+		bool overlay = false;
 
 		if (trans < 0) {
 			trans = (-trans) - 1;
-			speed_hack = true;
+			overlay = true;
 		} else {
-			_midiPlayer->stop();
 			trans = trans - 1;
 		}
 
-		// Now get the transition midi
-		int xmidi_index = _midiPlayer->isFMSynth() ? 260 : 258;
-		MusicFlex::XMidiData *xmidi = musicflex->getXMidi(xmidi_index);
-
-		warning("Doing a MIDI transition! trans: %d xmidi: %d speedhack: %d", trans, xmidi_index, speed_hack);
+		warning("Doing a MIDI transition! trans: %d overlay: %d", trans, overlay);
 
-		if (xmidi && xmidi->_data) {
-			_midiPlayer->load(xmidi->_data, xmidi->_size, 1, speed_hack);
-			_midiPlayer->play(trans, -1);
-		} else {
-			_midiPlayer->stop();
-		}
+		_midiPlayer->playTransition(trans, overlay);
 
 		_trackState._wanted = track;
 		_state = PLAYBACK_TRANSITION;
@@ -228,7 +224,7 @@ void U8MusicProcess::run() {
 			if (_midiPlayer) {
 				// if there's a track queued, only play this one once
 				bool repeat = (_trackState._queued == 0);
-				_midiPlayer->load(xmidi->_data, xmidi->_size, 0, false);
+				_midiPlayer->load(xmidi->_data, xmidi->_size, 0);
 				_midiPlayer->setLooping(repeat);
 				if (_songBranches[_trackState._wanted] >= 0 && !_midiPlayer->hasBranchIndex(_songBranches[_trackState._wanted])) {
 					if (_songBranches[_trackState._wanted] == 0) {




More information about the Scummvm-git-logs mailing list