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

bluegr noreply at scummvm.org
Sat May 21 19:44:11 UTC 2022


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

Summary:
9fc868c7ed SCUMM: Implement quarter frame precision timing
95894ae9fe SCUMM: Implement accurate PIT timings for DOS versions
caae9399f5 SCUMM: Add PIT behavior for ZAK v1
9cf60930b9 SCUMM: Fix the timings update for the script variable
6bc2df1709 SCUMM: Correctly notify the music timer of the current ticks
e42ad88690 SCUMM: Implement original CD Audio timing handling
ecdbc25624 SCUMM: Properly reset CD Audio timers
f3eee39c9d SCUMM: Properly implement speech timer for v5-7 talkie games
b90609a95a SCUMM: Clean-up transitionEffect()
23afa3bb88 SCUMM: WIP rework for dissolveEffect()
6b838fd53a SCUMM: GFX: Update comments and kPictureDelay
6eef97375e SCUMM: GFX: Add clarificatory comment to scrollEffect()
9d236fa13b SCUMM: GFX: Finish implementation for dissolveEffect()
aa8c5285eb SCUMM: Fix comment
38ac6fbadc SCUMM: GFX: Fix bool comparison
07eafca398 SCUMM: INSANE: Set correct video framerate
2d12acec5a SCUMM: Add v7 quirk to IsMouthSyncOff()
ebb3f2a842 SCUMM: Implement Amiga timings
ca527c8390 SCUMM: Implement a better approximation of some Amiga quirks


Commit: 9fc868c7ed15cefd2a241d7a9a99fcd74b5c377a
    https://github.com/scummvm/scummvm/commit/9fc868c7ed15cefd2a241d7a9a99fcd74b5c377a
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Implement quarter frame precision timing

Changed paths:
    engines/scumm/gfx.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index cf0ab5c0cb1..423c694aa98 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -66,8 +66,7 @@ struct StripTable {
 
 enum {
 	kScrolltime = 500,  // ms scrolling is supposed to take
-	kPictureDelay = 20,
-	kFadeDelay = 4 // 1/4th of a jiffie
+	kPictureDelay = 5
 };
 
 #define NUM_SHAKE_POSITIONS 8
@@ -3920,7 +3919,7 @@ void ScummEngine::transitionEffect(int a) {
 	int bottom;
 	int l, t, r, b;
 	const int height = MIN((int)_virtscr[kMainVirtScreen].h, _screenHeight);
-	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) * kFadeDelay : kPictureDelay;
+	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
 
 	for (i = 0; i < 16; i++) {
 		delta[i] = transitionEffects[a].deltaTable[i];
@@ -3979,7 +3978,7 @@ void ScummEngine::transitionEffect(int a) {
 void ScummEngine::dissolveEffect(int width, int height) {
 	VirtScreen *vs = &_virtscr[kMainVirtScreen];
 	int *offsets;
-	int blits_before_refresh, blits;
+	int blitsBeforeRefresh, blits;
 	int x, y;
 	int w, h;
 	int i;
@@ -4049,7 +4048,7 @@ void ScummEngine::dissolveEffect(int width, int height) {
 	// but might still need some tuning.
 
 	blits = 0;
-	blits_before_refresh = (3 * w * h) / 25;
+	blitsBeforeRefresh = (3 * w * h) / 25; // TODO: Check
 
 	// Speed up the effect for CD Loom since it uses it so often. I don't
 	// think the original had any delay at all, so on modern hardware it
@@ -4072,17 +4071,17 @@ void ScummEngine::dissolveEffect(int width, int height) {
 			_system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height);
 
 
-		if (++blits >= blits_before_refresh) {
+		if (++blits >= blitsBeforeRefresh) {
 			blits = 0;
-			waitForTimer(30);
+			waitForTimer(4);
 		}
 	}
 
-	free(offsets);
-
 	if (blits != 0) {
-		waitForTimer(30);
+		waitForTimer(4);
 	}
+
+	free(offsets);
 }
 
 void ScummEngine::scrollEffect(int dir) {
@@ -4097,7 +4096,7 @@ void ScummEngine::scrollEffect(int dir) {
 
 	int x, y;
 	int step;
-	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) * kFadeDelay : kPictureDelay;
+	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
 
 	if ((dir == 0) || (dir == 1))
 		step = vs->h;
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index db0ac280b8d..c64f3e6a662 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2101,6 +2101,7 @@ Common::Error ScummEngine::go() {
 		_rnd.getRandomNumber(2);
 
 		// Notify the script about how much time has passed, in ticks (60 ticks per second)
+		uint32 diff = _system->getMillis() - _lastWaitTime;
 		if (VAR_TIMER != 0xFF)
 			VAR(VAR_TIMER) = diff * 60 / 1000;
 		if (VAR_TIMER_TOTAL != 0xFF)
@@ -2142,19 +2143,16 @@ Common::Error ScummEngine::go() {
 			delta = 6;
 		}
 
-		// Wait and start the stop watch at the time the wait is assumed
+		// Wait, start and stop the stop watch at the time the wait is assumed
 		// to end. There is no guarantee that the wait is that exact,
 		// but this way if it overshoots that time will count as part
 		// of the main loop.
 
-		diff = waitForTimer(delta * 1000 / 60 - diff);
+		waitForTimer(delta * 4);
 
 		// Run the main loop
 		scummLoop(delta);
 
-		// Halt the stop watch and compute how much time this iteration took.
-		diff = _system->getMillis() - diff;
-
 		if (shouldQuit()) {
 			// TODO: Maybe perform an autosave on exit?
 			runQuitScript();
@@ -2164,17 +2162,19 @@ Common::Error ScummEngine::go() {
 	return Common::kNoError;
 }
 
-int ScummEngine::waitForTimer(int msec_delay) {
-	uint32 end_time;
+void ScummEngine::waitForTimer(int quarterFrames) {
+	uint32 endTime, cur;
+	uint32 msecDelay = getIntegralTime(quarterFrames * (1000 / 60.0) / 4);
 
 	if (_fastMode & 2)
-		msec_delay = 0;
+		msecDelay = 0;
 	else if (_fastMode & 1)
-		msec_delay = 10;
-
-	uint32 cur = _system->getMillis();;
+		msecDelay = 10;
 
-	end_time = cur + msec_delay;
+	cur = _system->getMillis();
+	uint32 diff = cur - _lastWaitTime;
+	msecDelay = (msecDelay > diff) ? msecDelay - diff : 0;
+	endTime = cur + msecDelay;
 
 	while (!shouldQuit()) {
 		_sound->updateCD(); // Loop CD Audio if needed
@@ -2196,20 +2196,31 @@ int ScummEngine::waitForTimer(int msec_delay) {
 		_refreshDuration[_refreshArrayPos] = (int)(cur - screenUpdateTimerStart);
 		_refreshArrayPos = (_refreshArrayPos + 1) % ARRAYSIZE(_refreshDuration);
 #endif
-		if (cur >= end_time)
+		if (cur >= endTime)
 			break;
-		_system->delayMillis(MIN<uint32>(10, end_time - cur));
+		_system->delayMillis(MIN<uint32>(10, endTime - cur));
 	}
 
-	// Return the expected end time, which may be different from the actual
-	// time. This helps the main loop maintain consistent timing.
+	// Set the last wait time as the expected end time, which may be different
+	// from the actual time. This helps the main loop maintain consistent timing.
 	//
 	// If it's lagging too far behind, we probably resumed from pausing, or
 	// the process was suspended, or any such thing. We probably can't
 	// sensibly detect all of them from within ScummVM, so in that case we
 	// simply return the current time to catch up.
 
-	return (cur > end_time + 50) ? cur : end_time;
+	_lastWaitTime = (cur > endTime + 50) ? cur : endTime;
+}
+
+uint32 ScummEngine::getIntegralTime(double fMsecs) {
+	double msecIntPart;
+	_msecFractParts += modf(fMsecs, &msecIntPart);
+	if (_msecFractParts >= 1) {
+		_msecFractParts--;
+		msecIntPart++;
+	}
+
+	return msecIntPart;
 }
 
 void ScummEngine_v0::scummLoop(int delta) {
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index fe1973b0686..40bbdb359e3 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -383,7 +383,17 @@ public:
 protected:
 	virtual void parseEvent(Common::Event event);
 
-	int waitForTimer(int msec_delay);
+	void waitForTimer(int quarterFrames);
+	uint32 _lastWaitTime;
+
+	/**
+	 * Represents fractional milliseconds by decomposing the passed
+	 * value into integral and fractional parts, then incrementing the
+	 * integer part as needed on subsequent function calls.
+	 */
+	uint32 getIntegralTime(double fMsecs);
+	double _msecFractParts;
+
 	virtual void processInput();
 	virtual void processKeyboard(Common::KeyState lastKeyHit);
 	virtual void clearClickedStatus();


Commit: 95894ae9fe06bb073d1f9f4c28c2a7f887072da8
    https://github.com/scummvm/scummvm/commit/95894ae9fe06bb073d1f9f4c28c2a7f887072da8
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Implement accurate PIT timings for DOS versions

Changed paths:
    engines/scumm/gfx.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 423c694aa98..17f58251247 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -65,7 +65,6 @@ struct StripTable {
 };
 
 enum {
-	kScrolltime = 500,  // ms scrolling is supposed to take
 	kPictureDelay = 5
 };
 
@@ -4095,16 +4094,9 @@ void ScummEngine::scrollEffect(int dir) {
 	VirtScreen *vs = &_virtscr[kMainVirtScreen];
 
 	int x, y;
-	int step;
+	const int step = 8;
 	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
 
-	if ((dir == 0) || (dir == 1))
-		step = vs->h;
-	else
-		step = vs->w;
-
-	step = (step * delay) / kScrolltime;
-
 	byte *src;
 	int m = _textSurfaceMultiplier;
 	int vsPitch = vs->pitch;
@@ -4227,7 +4219,7 @@ void ScummEngine::updateScreenShakeEffect() {
 		// but inside each respective ims driver during the driver load/init process. The screen shakes update every 8 ticks.
 		// LOOM uses either 236.696 Hz at 8 ticks delay or 473.297 Hz at 16 ticks delay, depending on the sound card selection.
 		// The outcome is the same...
-		_shakeTickCounter += ((1000000000 / _shakeTimerRate) * 8);
+		_shakeTickCounter += ((1000000 / _shakeTimerRate) * 8);
 		_shakeNextTick += (_shakeTickCounter / 1000);
 		_shakeTickCounter %= 1000;
 	}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index c64f3e6a662..b500053e18c 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -106,9 +106,9 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
 	  _game(dr.game),
 	  _filenamePattern(dr.fp),
 	  _language(dr.language),
-	  _rnd("scumm"),
-	  _shakeTimerRate(dr.game.version <= 3 ? 236696 : 291304)
+	  _rnd("scumm")
 {
+
 #ifdef USE_RGB_COLOR
 	if (_game.features & GF_16BIT_COLOR) {
 		if (_game.platform == Common::kPlatformPCEngine)
@@ -152,6 +152,9 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
 	for (uint i = 0; i < ARRAYSIZE(_virtscr); i++) {
 		_virtscr[i].clear();
 	}
+
+	setTimerAndShakeFrequency();
+
 	camera.reset();
 	memset(_colorCycle, 0, sizeof(_colorCycle));
 	memset(_colorUsedByCycle, 0, sizeof(_colorUsedByCycle));
@@ -2093,8 +2096,6 @@ Common::Error ScummEngine::go() {
 		_saveLoadFlag = 0;
 	}
 
-	int diff = 0;	// Duration of one loop iteration
-
 	while (!shouldQuit()) {
 		// Randomize the PRNG by calling it at regular intervals. This ensures
 		// that it will be in a different state each time you run the program.
@@ -2135,12 +2136,13 @@ Common::Error ScummEngine::go() {
 			delta += ((ScummEngine_v0 *)this)->DelayCalculateDelta();
 		}
 
-		// WORKAROUND: walking speed in the original v1 interpreter
-		// is sometimes slower (e.g. during scrolling) than in ScummVM.
-		// This is important for the door-closing action in the dungeon,
-		// otherwise (delta < 6) a single kid is able to escape.
-		if (_game.version == 1 && isScriptRunning(137)) {
-			delta = 6;
+		// In MANIAC V1, the workings of the wait loop will increment the
+		// timer past the comparison, producing a longer wait loop than
+		// expected. The timer resolution is lower than the frame-time
+		// derived from it, i.e., one tick represents three frames. We need
+		// to round up VAR_TIMER_NEXT to the nearest multiple of three.
+		if (_game.id == GID_MANIAC && _game.version == 1) {
+			delta = ceil(delta / 3.0) * 3;
 		}
 
 		// Wait, start and stop the stop watch at the time the wait is assumed
@@ -2164,7 +2166,7 @@ Common::Error ScummEngine::go() {
 
 void ScummEngine::waitForTimer(int quarterFrames) {
 	uint32 endTime, cur;
-	uint32 msecDelay = getIntegralTime(quarterFrames * (1000 / 60.0) / 4);
+	uint32 msecDelay = getIntegralTime(quarterFrames * (1000 / _timerFrequency));
 
 	if (_fastMode & 2)
 		msecDelay = 0;
@@ -2223,6 +2225,45 @@ uint32 ScummEngine::getIntegralTime(double fMsecs) {
 	return msecIntPart;
 }
 
+void ScummEngine::setTimerAndShakeFrequency() {
+	_shakeTimerRate = _timerFrequency = 240.0;
+
+	if (_game.platform == Common::kPlatformDOS || _game.platform == Common::kPlatformUnknown) {
+		switch (_game.version) {
+		case 1:
+			if (_game.id == GID_MANIAC) {
+				// In MANIAC V1, one tick represents three frames,
+				// i.e., 12 quarter-frames.
+				_shakeTimerRate = _timerFrequency = PIT_BASE_FREQUENCY / PIT_V1_DIVISOR * 12;
+			}
+			break;
+		case 2:
+		case 3:
+		case 4:
+			_shakeTimerRate = _timerFrequency = PIT_BASE_FREQUENCY / PIT_V2_4_DIVISOR;
+			break;
+		case 5:
+			_shakeTimerRate = _timerFrequency = PIT_BASE_FREQUENCY / PIT_V5_6_ORCHESTRATOR_DIVISOR;
+			_timerFrequency *= PIT_V5_6_SUBTIMER_INC / PIT_V5_SUBTIMER_THRESH;
+			break;
+		case 6:
+			_shakeTimerRate = _timerFrequency = PIT_BASE_FREQUENCY / PIT_V5_6_ORCHESTRATOR_DIVISOR;
+			if (_game.id == GID_TENTACLE) {
+				_timerFrequency *= PIT_V5_6_SUBTIMER_INC / PIT_V6_DOTT_SUBTIMER_THRESH;
+			} else {
+				_timerFrequency *= PIT_V5_6_SUBTIMER_INC / PIT_V6_SAMNMAX_SUBTIMER_THRESH;
+			}
+			break;
+		case 7:
+			_shakeTimerRate = _timerFrequency = PIT_BASE_FREQUENCY / PIT_V7_ORCHESTRATOR_DIVISOR;
+			_timerFrequency *= PIT_V7_SUBTIMER_INC / PIT_V7_SUBTIMER_THRESH;
+			break;
+		default:
+			_shakeTimerRate = _timerFrequency = 240.0;
+		}
+	}
+}
+
 void ScummEngine_v0::scummLoop(int delta) {
 	VAR(VAR_IS_SOUND_RUNNING) = (_sound->_lastSound && _sound->isSoundRunning(_sound->_lastSound) != 0);
 
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 40bbdb359e3..187aaaa8411 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -280,6 +280,58 @@ typedef uint16 ResId;
 
 class ResourceManager;
 
+/**
+ * DOS Programmable Interrupt Timer constants.
+ *
+ * The SCUMM engine (v1-v7, DOS) timer ticks are based on the jiffy unit (roughly 60Hz).
+ * Well, if we want to be pedantic about it, it operates on quarter jiffies (240Hz),
+ * a rate at which several screen effects are updated; but still, this value is divided
+ * by 4 in the main game loop in order for it to operate on whole jiffies.
+ * In order to obtain this behavior, the PIT is programmed to operate at roughly 240Hz,
+ * though these timings change from version to version (or game by game, for v6).
+ *
+ * Glossary:
+ * - Base frequency: this is the frequency at which the Intel 8253/54 PIT
+ *                   operates, namely obtained with the formula 105/88, which
+ *                   yields 1.193181818... MHz. We are storing it in Hz;
+ *
+ * - Divisor:        the base frequency in DOS is not used as-is, but it is instead
+ *                   divided by a customizable divisor which can range between
+ *                   0 and (2^16-1), where 0 is a shortcut for 2^16. This operation
+ *                   yields the custom frequency at which the custom assigned interrupt
+ *                   gets called (hence "Programmable");
+ *
+ * - Orchestrator:   starting from SCUMM v5, games started using iMUSE, and apparently
+ *                   needed a more precise timing handling; this led to the introduction
+ *                   of a main orchestrator timer (which then handled the execution of
+                     other sub-timers), whose divisor (4096) was set up in the IMS
+ *                   drivers up until v7, in which the divisor (3977) was set up in the
+ *                   executable as a part of the INSANE orchestration;
+ *
+ * - Sub-timer:      custom made timers, operating under an orchestrator; in the macros
+ *                   below, "INC" refers to the increment of an accumulator which gets
+ *                   updated at each iteration of the orchestrator interrupt; "THRESH"
+ *                   refers to a threshold value of the aforementioned accumulator,
+ *                   beyond which the accumulator is decremented by the threshold value,
+ *                   and the interrupt of the sub-timer gets executed (e.g. the values
+ *                   below mainly refer to the interrupt which increments the SCUMM
+ *                   quarter frame counter.
+ *
+ * All the values below are presented as doubles, so to safely yield fractional results.
+ */
+
+#define PIT_BASE_FREQUENCY             1193182.0 // In Hz
+#define PIT_V1_DIVISOR                 65536.0
+#define PIT_V2_4_DIVISOR               5041.0
+#define PIT_V5_6_ORCHESTRATOR_DIVISOR  4096.0
+#define PIT_V5_6_SUBTIMER_INC          3433.0
+#define PIT_V5_SUBTIMER_THRESH         4167.0
+#define PIT_V6_SAMNMAX_SUBTIMER_THRESH 4167.0
+#define PIT_V6_DOTT_SUBTIMER_THRESH    4237.0
+#define PIT_V7_ORCHESTRATOR_DIVISOR    3977.0
+#define PIT_V7_SUBTIMER_INC            3977.0
+#define PIT_V7_SUBTIMER_THRESH         4971.0
+
 /**
  * Base class for all SCUMM engines.
  */
@@ -386,6 +438,8 @@ protected:
 	void waitForTimer(int quarterFrames);
 	uint32 _lastWaitTime;
 
+	void setTimerAndShakeFrequency();
+
 	/**
 	 * Represents fractional milliseconds by decomposing the passed
 	 * value into integral and fractional parts, then incrementing the
@@ -1018,7 +1072,8 @@ protected:
 	uint _shakeFrame = 0;
 	uint32 _shakeNextTick = 0;
 	uint32 _shakeTickCounter = 0;
-	const uint32 _shakeTimerRate;
+	double _shakeTimerRate;
+	double _timerFrequency;
 
 	void setShake(int mode);
 


Commit: caae9399f5c5c970d2f89cef097852ae5137eefa
    https://github.com/scummvm/scummvm/commit/caae9399f5c5c970d2f89cef097852ae5137eefa
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Add PIT behavior for ZAK v1

Changed paths:
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index b500053e18c..c677218805f 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2235,6 +2235,8 @@ void ScummEngine::setTimerAndShakeFrequency() {
 				// In MANIAC V1, one tick represents three frames,
 				// i.e., 12 quarter-frames.
 				_shakeTimerRate = _timerFrequency = PIT_BASE_FREQUENCY / PIT_V1_DIVISOR * 12;
+			} else {
+				_shakeTimerRate = _timerFrequency = PIT_BASE_FREQUENCY / PIT_V2_4_DIVISOR;
 			}
 			break;
 		case 2:


Commit: 9cf60930b9639b349d973edbb91e5bfa728f8f67
    https://github.com/scummvm/scummvm/commit/9cf60930b9639b349d973edbb91e5bfa728f8f67
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Fix the timings update for the script variable
This fixes music/game sync issues at the end of DOTT intro (before the main theme)

Changed paths:
    engines/scumm/gfx.cpp
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 17f58251247..d36eea2db57 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -4053,7 +4053,7 @@ void ScummEngine::dissolveEffect(int width, int height) {
 	// think the original had any delay at all, so on modern hardware it
 	// wasn't even noticeable.
 	if (_game.id == GID_LOOM && (_game.version == 4))
-		blits_before_refresh *= 2;
+		blitsBeforeRefresh *= 2;
 
 	for (i = 0; i < w * h; i++) {
 		x = offsets[i] % vs->pitch;
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index c677218805f..082cca71f16 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2101,12 +2101,13 @@ Common::Error ScummEngine::go() {
 		// that it will be in a different state each time you run the program.
 		_rnd.getRandomNumber(2);
 
-		// Notify the script about how much time has passed, in ticks (60 ticks per second)
+		// Notify the script about how much time has passed, in jiffies
+		// (the timing varies depending on the SCUMM version and the game)
 		uint32 diff = _system->getMillis() - _lastWaitTime;
 		if (VAR_TIMER != 0xFF)
-			VAR(VAR_TIMER) = diff * 60 / 1000;
+			VAR(VAR_TIMER) = diff * (_timerFrequency / 4) / 1000;
 		if (VAR_TIMER_TOTAL != 0xFF)
-			VAR(VAR_TIMER_TOTAL) += diff * 60 / 1000;
+			VAR(VAR_TIMER_TOTAL) += diff * (_timerFrequency / 4) / 1000;
 
 		// Determine how long to wait before the next loop iteration should start
 		int delta = (VAR_TIMER_NEXT != 0xFF) ? VAR(VAR_TIMER_NEXT) : 4;


Commit: 6bc2df17094186e4c23fb64e46685694d37c8563
    https://github.com/scummvm/scummvm/commit/6bc2df17094186e4c23fb64e46685694d37c8563
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Correctly notify the music timer of the current ticks

Changed paths:
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 082cca71f16..131994ac417 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2317,10 +2317,10 @@ void ScummEngine::scummLoop(int delta) {
 	} else if (VAR_MUSIC_TIMER != 0xFF) {
 		if (_sound->useReplacementAudioTracks() && _sound->getCurrentCDSound()) {
 			_sound->updateMusicTimer();
-			VAR(VAR_MUSIC_TIMER) = _sound->getMusicTimer();
+			VAR(VAR_MUSIC_TIMER) = _sound->getMusicTimer() * _timerFrequency / 240.0;
 		} else if (_musicEngine) {
 			// The music engine generates the timer data for us.
-			VAR(VAR_MUSIC_TIMER) = _musicEngine->getMusicTimer();
+			VAR(VAR_MUSIC_TIMER) = _musicEngine->getMusicTimer() * _timerFrequency / 240.0;
 		}
 	}
 


Commit: e42ad8869092273b3e4e0fb00542e5a3fdd7c7c7
    https://github.com/scummvm/scummvm/commit/e42ad8869092273b3e4e0fb00542e5a3fdd7c7c7
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Implement original CD Audio timing handling

Changed paths:
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h
    engines/scumm/sound.cpp
    engines/scumm/sound.h


diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 131994ac417..39347740aef 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2267,6 +2267,10 @@ void ScummEngine::setTimerAndShakeFrequency() {
 	}
 }
 
+double ScummEngine::getTimerFrequency() {
+	return _timerFrequency;
+}
+
 void ScummEngine_v0::scummLoop(int delta) {
 	VAR(VAR_IS_SOUND_RUNNING) = (_sound->_lastSound && _sound->isSoundRunning(_sound->_lastSound) != 0);
 
@@ -2313,7 +2317,7 @@ void ScummEngine::scummLoop(int delta) {
 	scummLoop_updateScummVars();
 
 	if (_game.features & GF_AUDIOTRACKS) {
-		// Covered automatically by the Sound class
+		VAR(VAR_MUSIC_TIMER) = _sound->getCDMusicTimer();
 	} else if (VAR_MUSIC_TIMER != 0xFF) {
 		if (_sound->useReplacementAudioTracks() && _sound->getCurrentCDSound()) {
 			_sound->updateMusicTimer();
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 187aaaa8411..48efbf5d58f 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1067,6 +1067,9 @@ protected:
 
 	void updateScreenShakeEffect();
 
+public:
+	double getTimerFrequency();
+
 protected:
 	bool _shakeEnabled = false;
 	uint _shakeFrame = 0;
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 86fb717c787..f8479b3c78b 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -62,6 +62,8 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
 	_replacementTrackStartTime(0),
 	_replacementTrackPauseTime(0),
 	_musicTimer(0),
+	_cdMusicTimerMod(0),
+	_cdMusicTimer(0),
 	_soundQuePos(0),
 	_soundQue2Pos(0),
 	_sfxFilename(),
@@ -1339,42 +1341,38 @@ bool Sound::isSfxFinished() const {
 	return !_mixer->hasActiveChannelOfType(Audio::Mixer::kSFXSoundType);
 }
 
-// We use a real timer in an attempt to get better sync with CD tracks. This is
-// necessary for games like Loom CD.
-
-static void cd_timer_handler(void *refCon) {
-	ScummEngine *scumm = (ScummEngine *)refCon;
+static void cdTimerHandler(void *refCon) {
+	Sound *snd = (Sound *)refCon;
 
 	// FIXME: Turn off the timer when it's no longer needed. In theory, it
 	// should be possible to check with pollCD(), but since CD sound isn't
 	// properly restarted when reloading a saved game, I don't dare to.
-
-	scumm->VAR(scumm->VAR_MUSIC_TIMER) += 6;
+	if ((snd->_cdMusicTimerMod++ & 3) == 0) {
+		snd->_cdMusicTimer++;
+	}
 }
 
 void Sound::startCDTimer() {
 	if (_useReplacementAudioTracks)
 		return;
 
-	// This timer interval is based on two scenes: The Monkey Island 1
-	// intro, and the scene in Loom CD where Chaos appears. In both cases
-	// the game plays the scene as two separate sounds, even though both
-	// halves are right next to each other in the CD track. Probably so
-	// that you can hit Escape to skip the first half.
+	// This CD timer implementation strictly follows the original interpreters for
+	// Monkey Island 1 CD and Loom CD: it works by incrementing _cdMusicTimerMod and _cdMusicTimer
+	// at each quarter frame (see ScummEngine::setTimerAndShakeFrequency() for what the exact
+	// frequency rate is for the particular game and engine version being ran).
 	//
-	// Make it too low, and the Monkey Island theme will be cut short. Make
-	// it too high, and there will be a nasty "hiccup" just as Chaos
-	// appears.
-
-	_vm->getTimerManager()->removeTimerProc(&cd_timer_handler);
-	_vm->getTimerManager()->installTimerProc(&cd_timer_handler, 100700, _vm, "scummCDtimer");
+	// Again as per the interpreters, VAR_MUSIC_TIMER is then updated inside the SCUMM main loop.
+	_vm->getTimerManager()->removeTimerProc(&cdTimerHandler);
+	_vm->getTimerManager()->installTimerProc(&cdTimerHandler, 1000000 / _vm->getTimerFrequency(), this, "scummCDtimer");
 }
 
 void Sound::stopCDTimer() {
 	if (_useReplacementAudioTracks)
 		return;
 
-	_vm->getTimerManager()->removeTimerProc(&cd_timer_handler);
+	_cdMusicTimerMod = 0;
+	_cdMusicTimer = 0;
+	_vm->getTimerManager()->removeTimerProc(&cdTimerHandler);
 }
 
 void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index 21e00e8e3bb..732871f287b 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -112,6 +112,8 @@ public:
 	bool _soundsPaused;
 	byte _sfxMode;
 	uint _lastSound;
+	uint32 _cdMusicTimerMod;
+	uint32 _cdMusicTimer;
 
 	MidiDriverFlags _musicType;
 
@@ -154,6 +156,7 @@ public:
 	bool useReplacementAudioTracks() const { return _useReplacementAudioTracks; }
 	void updateMusicTimer();
 	int getMusicTimer() const { return _musicTimer; }
+	int getCDMusicTimer() const { return _cdMusicTimer; }
 
 	void saveLoadWithSerializer(Common::Serializer &ser) override;
 	void restoreAfterLoad();


Commit: ecdbc256243116dd2d351f061ab1b8eb08742e0a
    https://github.com/scummvm/scummvm/commit/ecdbc256243116dd2d351f061ab1b8eb08742e0a
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Properly reset CD Audio timers

Changed paths:
    engines/scumm/sound.cpp


diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index f8479b3c78b..96f7736afc3 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1370,14 +1370,14 @@ void Sound::stopCDTimer() {
 	if (_useReplacementAudioTracks)
 		return;
 
-	_cdMusicTimerMod = 0;
-	_cdMusicTimer = 0;
 	_vm->getTimerManager()->removeTimerProc(&cdTimerHandler);
 }
 
 void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {
 	// Reset the music timer variable at the start of a new track
 	_vm->VAR(_vm->VAR_MUSIC_TIMER) = 0;
+	_cdMusicTimerMod = 0;
+	_cdMusicTimer = 0;
 
 	// Play it
 	if (!_soundsPaused)


Commit: f3eee39c9d4d64bdbb17349795064bd9ea887eac
    https://github.com/scummvm/scummvm/commit/f3eee39c9d4d64bdbb17349795064bd9ea887eac
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Properly implement speech timer for v5-7 talkie games

Changed paths:
    engines/scumm/sound.cpp
    engines/scumm/sound.h


diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 96f7736afc3..9ed8850db3a 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -64,6 +64,7 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
 	_musicTimer(0),
 	_cdMusicTimerMod(0),
 	_cdMusicTimer(0),
+	_speechTimerMod(0),
 	_soundQuePos(0),
 	_soundQue2Pos(0),
 	_sfxFilename(),
@@ -105,6 +106,13 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
 
 	_loomSteamCDAudioHandle = new Audio::SoundHandle();
 	_talkChannelHandle = new Audio::SoundHandle();
+
+	// This timer targets every talkie game, except for LOOM CD
+	// which is handled differently, and except for COMI which
+	// handles lipsync within Digital iMUSE.
+	if (_vm->_game.version >= 5 && _vm->_game.version <= 7) {
+		startSpeechTimer();
+	}
 }
 
 Sound::~Sound() {
@@ -113,6 +121,9 @@ Sound::~Sound() {
 	free(_offsetTable);
 	delete _loomSteamCDAudioHandle;
 	delete _talkChannelHandle;
+	if (_vm->_game.version >= 5 && _vm->_game.version <= 7) {
+		stopSpeechTimer();
+	}
 }
 
 bool Sound::isRolandLoom() const {
@@ -590,16 +601,17 @@ void Sound::processSfxQueues() {
 
 		if (_vm->_imuseDigital) {
 			finished = !isSoundRunning(kTalkSoundID);
+			if (_vm->_game.id == GID_CMI) {
 #if defined(ENABLE_SCUMM_7_8)
-			_curSoundPos = _vm->_imuseDigital->getSoundElapsedTimeInMs(kTalkSoundID) * 60 / 1000;
+				_curSoundPos = _vm->_imuseDigital->getSoundElapsedTimeInMs(kTalkSoundID) * 60 / 1000;
 #endif
+			}
 		} else if (_vm->_game.heversion >= 60) {
 			finished = !isSoundRunning(1);
 		} else {
 			finished = !_mixer->isSoundHandleActive(*_talkChannelHandle);
-			// calculate speech sound position simulating increment at 60FPS
-			_curSoundPos = (_mixer->getSoundElapsedTime(*_talkChannelHandle) * 60) / 1000;
 		}
+
 		if ((uint) act < 0x80 && ((_vm->_game.version == 8) || (_vm->_game.version <= 7 && !_vm->_string[0].no_talk_anim))) {
 			a = _vm->derefActor(act, "processSfxQueues");
 			if (a->isInCurrentRoom()) {
@@ -692,7 +704,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 		_sfxMode |= mode;
 
 		if (_vm->_game.id == GID_DIG)
-			_curSoundPos = 0;
+			resetSpeechTimer();
 
 		return;
 	} else if (_vm->_game.id == GID_DIG && (_vm->_game.features & GF_DEMO)) {
@@ -777,7 +789,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 
 			_mouthSyncTimes[i] = 0xFFFF;
 			_sfxMode |= mode;
-			_curSoundPos = 0;
+			resetSpeechTimer();
 			_mouthSyncMode = true;
 
 			totalOffset = offset + b;
@@ -877,7 +889,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
 
 		_mouthSyncTimes[i] = 0xFFFF;
 		_sfxMode |= mode;
-		_curSoundPos = 0;
+		resetSpeechTimer();
 		_mouthSyncMode = true;
 	}
 
@@ -958,6 +970,7 @@ bool Sound::isMouthSyncOff(uint pos) {
 	uint j;
 	bool val = true;
 	uint16 *ms = _mouthSyncTimes;
+	uint delay = (_vm->_game.version == 6) ? 10 : 0;
 
 	if (_vm->_game.id == GID_DIG && !(_vm->_game.features & GF_DEMO)) {
 		pos = 1000 * pos / 60;
@@ -972,7 +985,7 @@ bool Sound::isMouthSyncOff(uint pos) {
 			_endOfMouthSync = true;
 			break;
 		}
-	} while (pos > j);
+	} while (pos + delay > j);
 	return val;
 }
 
@@ -1341,6 +1354,30 @@ bool Sound::isSfxFinished() const {
 	return !_mixer->hasActiveChannelOfType(Audio::Mixer::kSFXSoundType);
 }
 
+void Sound::incrementSpeechTimer() {
+	if (!_soundsPaused)
+		_curSoundPos++;
+}
+
+void Sound::resetSpeechTimer() {
+	_curSoundPos = 0;
+}
+
+static void speechTimerHandler(void *refCon) {
+	Sound *snd = (Sound *)refCon;
+	if ((snd->_speechTimerMod++ & 3) == 0) {
+		snd->incrementSpeechTimer();
+	}
+}
+
+void Sound::startSpeechTimer() {
+	_vm->getTimerManager()->installTimerProc(&speechTimerHandler, 1000000 / _vm->getTimerFrequency(), this, "scummSpeechTimer");
+}
+
+void Sound::stopSpeechTimer() {
+	_vm->getTimerManager()->removeTimerProc(&speechTimerHandler);
+}
+
 static void cdTimerHandler(void *refCon) {
 	Sound *snd = (Sound *)refCon;
 
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index 732871f287b..11474dd3d10 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -114,6 +114,7 @@ public:
 	uint _lastSound;
 	uint32 _cdMusicTimerMod;
 	uint32 _cdMusicTimer;
+	uint32 _speechTimerMod;
 
 	MidiDriverFlags _musicType;
 
@@ -140,6 +141,10 @@ public:
 	bool hasSfxFile() const;
 	ScummFile *restoreDiMUSESpeechFile(const char *fileName);
 	void extractSyncsFromDiMUSEMarker(const char *marker);
+	void incrementSpeechTimer();
+	void resetSpeechTimer();
+	void startSpeechTimer();
+	void stopSpeechTimer();
 
 	void startCDTimer();
 	void stopCDTimer();


Commit: b90609a95a4d358ddc82556ca21abfccc8a15ffd
    https://github.com/scummvm/scummvm/commit/b90609a95a4d358ddc82556ca21abfccc8a15ffd
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Clean-up transitionEffect()

Changed paths:
    engines/scumm/gfx.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index d36eea2db57..1b119e7c915 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -65,6 +65,7 @@ struct StripTable {
 };
 
 enum {
+	kNoDelay = 0,
 	kPictureDelay = 5
 };
 
@@ -3813,7 +3814,7 @@ void ScummEngine::fadeIn(int effect) {
 		transitionEffect(effect - 1);
 		break;
 	case 128:
-		unkScreenEffect6();
+		dissolveEffectSelector();
 		break;
 	case 129:
 		break;
@@ -3875,7 +3876,7 @@ void ScummEngine::fadeOut(int effect) {
 			transitionEffect(effect - 1);
 			break;
 		case 128:
-			unkScreenEffect6();
+			dissolveEffectSelector();
 			break;
 		case 129:
 			// Just blit screen 0 to the display (i.e. display will be black)
@@ -3912,13 +3913,31 @@ void ScummEngine::fadeOut(int effect) {
  * @param a		the transition effect to perform
  */
 void ScummEngine::transitionEffect(int a) {
-	int delta[16];								// Offset applied during each iteration
+	int delta[16]; // Offset applied during each iteration
 	int tab_2[16];
 	int i, j;
 	int bottom;
 	int l, t, r, b;
+	int delay, numOfIterations;
 	const int height = MIN((int)_virtscr[kMainVirtScreen].h, _screenHeight);
-	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
+
+	if (VAR_FADE_DELAY == 0xFF) {
+		if (_game.version >= 2) {
+			delay = kPictureDelay;
+		} else {
+			delay = kNoDelay;
+		}
+	} else {
+		delay = VAR(VAR_FADE_DELAY);
+	}
+
+	// V3+ games have the number of iterations hardcoded; we also
+	// have that number hardcoded for MM NES, so let's target that too:
+	if (_game.version >= 3 || _game.platform == Common::kPlatformNES) {
+		numOfIterations = transitionEffects[a].numOfIterations;
+	} else {
+		numOfIterations = (a == 0 || a == 4) ? ceil((height / 8.0) / 2) : height / 8;
+	}
 
 	for (i = 0; i < 16; i++) {
 		delta[i] = transitionEffects[a].deltaTable[i];
@@ -3929,7 +3948,7 @@ void ScummEngine::transitionEffect(int a) {
 	}
 
 	bottom = height / 8;
-	for (j = 0; j < transitionEffects[a].numOfIterations; j++) {
+	for (j = 0; j < numOfIterations; j++) {
 		for (i = 0; i < 4; i++) {
 			l = tab_2[i * 4];
 			t = tab_2[i * 4 + 1];
@@ -3960,9 +3979,11 @@ void ScummEngine::transitionEffect(int a) {
 		for (i = 0; i < 16; i++)
 			tab_2[i] += delta[i];
 
-		// Draw the current state to the screen and wait a few secs so the
-		// user can watch the effect taking place.
-		waitForTimer(delay);
+		// Draw the current state to the screen and wait
+		// for the appropriate number of quarter frames
+		if (!_fastMode) {
+			waitForTimer(delay);
+		}
 	}
 }
 
@@ -4118,7 +4139,6 @@ void ScummEngine::scrollEffect(int dir) {
 					vsPitch,
 					0, (vs->h - step) * m,
 					vs->w * m, step * m);
-				_system->updateScreen();
 			}
 
 			waitForTimer(delay);
@@ -4141,7 +4161,6 @@ void ScummEngine::scrollEffect(int dir) {
 					vsPitch,
 					0, 0,
 					vs->w * m, step * m);
-				_system->updateScreen();
 			}
 
 			waitForTimer(delay);
@@ -4159,7 +4178,6 @@ void ScummEngine::scrollEffect(int dir) {
 				vsPitch,
 				(vs->w - step) * m, 0,
 				step * m, vs->h * m);
-			_system->updateScreen();
 
 			waitForTimer(delay);
 			x += step;
@@ -4176,7 +4194,6 @@ void ScummEngine::scrollEffect(int dir) {
 				vsPitch,
 				0, 0,
 				step, vs->h);
-			_system->updateScreen();
 
 			waitForTimer(delay);
 			x += step;
@@ -4187,7 +4204,7 @@ void ScummEngine::scrollEffect(int dir) {
 	}
 }
 
-void ScummEngine::unkScreenEffect6() {
+void ScummEngine::dissolveEffectSelector() {
 	// CD Loom (but not EGA Loom!) uses a more fine-grained dissolve
 	if (_game.id == GID_LOOM && _game.version == 4)
 		dissolveEffect(1, 1);
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 48efbf5d58f..8fcba794ee9 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1060,7 +1060,7 @@ protected:
 	void fadeOut(int effect);
 	void setScrollBuffer();
 
-	void unkScreenEffect6();
+	void dissolveEffectSelector();
 	void transitionEffect(int a);
 	void dissolveEffect(int width, int height);
 	void scrollEffect(int dir);


Commit: 23afa3bb88d036718efe31b44ba9ebaa4a41247b
    https://github.com/scummvm/scummvm/commit/23afa3bb88d036718efe31b44ba9ebaa4a41247b
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: WIP rework for dissolveEffect()

Changed paths:
    engines/scumm/gfx.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 1b119e7c915..a7fd2aed399 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -4068,13 +4068,13 @@ void ScummEngine::dissolveEffect(int width, int height) {
 	// but might still need some tuning.
 
 	blits = 0;
-	blitsBeforeRefresh = (3 * w * h) / 25; // TODO: Check
+	blitsBeforeRefresh = (w * h) / 8;
 
 	// Speed up the effect for CD Loom since it uses it so often. I don't
 	// think the original had any delay at all, so on modern hardware it
 	// wasn't even noticeable.
-	if (_game.id == GID_LOOM && (_game.version == 4))
-		blitsBeforeRefresh *= 2;
+	//if (_game.id == GID_LOOM && (_game.version == 4))
+	//	blitsBeforeRefresh *= 2;
 
 	for (i = 0; i < w * h; i++) {
 		x = offsets[i] % vs->pitch;
@@ -4097,10 +4097,6 @@ void ScummEngine::dissolveEffect(int width, int height) {
 		}
 	}
 
-	if (blits != 0) {
-		waitForTimer(4);
-	}
-
 	free(offsets);
 }
 


Commit: 6b838fd53acfda2c4f50ce60b38debb27f5faf26
    https://github.com/scummvm/scummvm/commit/6b838fd53acfda2c4f50ce60b38debb27f5faf26
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: GFX: Update comments and kPictureDelay

Changed paths:
    engines/scumm/gfx.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index a7fd2aed399..a5fab091f7e 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -66,7 +66,13 @@ struct StripTable {
 
 enum {
 	kNoDelay = 0,
-	kPictureDelay = 5
+	// This should actually be 3 in all games using it;
+	// every one of those games seems to accumulate some
+	// kind of internal random delay while performing
+	// the screen effect (like sound interrupts running,
+	// forcing the SCUMM timer to a lower frequency).
+	// I have added an extra quarter frame to emulate that.
+	kPictureDelay = 4
 };
 
 #define NUM_SHAKE_POSITIONS 8
@@ -81,11 +87,9 @@ static const int8 shake_positions[NUM_SHAKE_POSITIONS] = {
  * that the screen has 40 vertical strips (i.e. 320 pixel), and 25 horizontal
  * strips (i.e. 200 pixel). There is a hack in transitionEffect that
  * makes it work correctly in games which have a different screen height
- * (for example, 240 pixel), but nothing is done regarding the width, so this
- * code won't work correctly in COMI. Also, the number of iteration depends
- * on min(vertStrips, horizStrips}. So the 13 is derived from 25/2, rounded up.
- * And the 25 = min(25,40). Hence for Zak256 instead of 13 and 25, the values
- * 15 and 30 should be used, and for COMI probably 30 and 60.
+ * (for example, 240 pixel), but nothing is done regarding the width.
+ * The number of iterations is hardcoded from version 3 up to 7.
+ * Version 8 doesn't have any of these effects at all.
  */
 struct TransitionEffect {
 	byte numOfIterations;


Commit: 6eef97375eebc6c31f204b66e24cba4d41678347
    https://github.com/scummvm/scummvm/commit/6eef97375eebc6c31f204b66e24cba4d41678347
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: GFX: Add clarificatory comment to scrollEffect()

Changed paths:
    engines/scumm/gfx.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index a5fab091f7e..ffc924500e2 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -4116,6 +4116,9 @@ void ScummEngine::scrollEffect(int dir) {
 
 	int x, y;
 	const int step = 8;
+
+	// Keep in mind: this effect is only present in v5 and v6, so VAR_FADE_DELAY is
+	// never uninitialized. The following check is here for good measure only.
 	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
 
 	byte *src;


Commit: 9d236fa13b175bb5d0f2b261c7549d65e3c5b683
    https://github.com/scummvm/scummvm/commit/9d236fa13b175bb5d0f2b261c7549d65e3c5b683
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: GFX: Finish implementation for dissolveEffect()

Changed paths:
    engines/scumm/gfx.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index ffc924500e2..c1ff5d8f733 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -4002,11 +4002,12 @@ void ScummEngine::transitionEffect(int a) {
 void ScummEngine::dissolveEffect(int width, int height) {
 	VirtScreen *vs = &_virtscr[kMainVirtScreen];
 	int *offsets;
-	int blitsBeforeRefresh, blits;
+	int blitsBeforeRefresh, blits, blitsToFreeze;
 	int x, y;
 	int w, h;
 	int i;
-
+	bool canHalt = false;
+	bool is1x1Pattern = (width == height == 1);
 	// There's probably some less memory-hungry way of doing this. But
 	// since we're only dealing with relatively small images, it shouldn't
 	// be too bad.
@@ -4066,19 +4067,31 @@ void ScummEngine::dissolveEffect(int width, int height) {
 		free(offsets2);
 	}
 
-	// Blit the image piece by piece to the screen. The idea here is that
-	// the whole update should take about a quarter of a second, assuming
-	// most of the time is spent in waitForTimer(). It looks good to me,
-	// but might still need some tuning.
+	// The whole effect has a variable duration, depending on
+	// the host machine speed. We want to be accurate, but we
+	// also want to actually *see* the effect, given that in modern
+	// machines this code would run at lightspeed, so let's declare
+	// a blitsBeforeRefresh variable, which serves as a threshold
+	// value allowing us to pause rendering every N blits.
+	//
+	// Pattern 1x1:
+	//   The original construct the image piece by piece but blits it
+	//   every 8 iterations of a loop with duration h. We try to imitate
+	//   this behavior by using blitsBeforeRefresh and by assigning it a value
+	//   which is a factor of blitsToFreeze.
+	//
+	// Pattern NxM:
+	//   The original construct the image piece by piece but blits it
+	//   every time it finds an offset smaller than the height of the virtual
+	//   screen. This is trivial to do in our code, so we just sleep for a
+	//   quarter frame everytime the condition above is met.
+	//
+	// If we ever get a blitsToFreeze == 0, we will use 18 in its place
+	// since it's the most typical value got out of the calculations.
 
 	blits = 0;
-	blitsBeforeRefresh = (w * h) / 8;
-
-	// Speed up the effect for CD Loom since it uses it so often. I don't
-	// think the original had any delay at all, so on modern hardware it
-	// wasn't even noticeable.
-	//if (_game.id == GID_LOOM && (_game.version == 4))
-	//	blitsBeforeRefresh *= 2;
+	blitsToFreeze = (h / 8); // The number of blits between which we delay rendering for pattern 1x1
+	blitsBeforeRefresh = (w * h) / (blitsToFreeze == 0 ? 18 : blitsToFreeze);
 
 	for (i = 0; i < w * h; i++) {
 		x = offsets[i] % vs->pitch;
@@ -4094,10 +4107,21 @@ void ScummEngine::dissolveEffect(int width, int height) {
 		else
 			_system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height);
 
+		// Test for 1x1 pattern...
+		canHalt |= is1x1Pattern && ++blits >= blitsBeforeRefresh;
 
-		if (++blits >= blitsBeforeRefresh) {
+		// If that is true, then reset the blits var...
+		if (canHalt) {
 			blits = 0;
-			waitForTimer(4);
+		}
+
+		// Test for NxM pattern...
+		canHalt |= !is1x1Pattern && (offsets[i] < vs->h);
+
+		// Halt rendering for a quarter frame...
+		if (canHalt) {
+			canHalt = false;
+			waitForTimer(1);
 		}
 	}
 


Commit: aa8c5285ebee4179b53404ff937987c6410dda11
    https://github.com/scummvm/scummvm/commit/aa8c5285ebee4179b53404ff937987c6410dda11
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Fix comment

Changed paths:
    engines/scumm/scumm.h


diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 8fcba794ee9..391501c849e 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -281,7 +281,7 @@ typedef uint16 ResId;
 class ResourceManager;
 
 /**
- * DOS Programmable Interrupt Timer constants.
+ * DOS Programmable Interval Timer constants.
  *
  * The SCUMM engine (v1-v7, DOS) timer ticks are based on the jiffy unit (roughly 60Hz).
  * Well, if we want to be pedantic about it, it operates on quarter jiffies (240Hz),
@@ -298,7 +298,7 @@ class ResourceManager;
  * - Divisor:        the base frequency in DOS is not used as-is, but it is instead
  *                   divided by a customizable divisor which can range between
  *                   0 and (2^16-1), where 0 is a shortcut for 2^16. This operation
- *                   yields the custom frequency at which the custom assigned interrupt
+ *                   yields the custom frequency at which our custom interrupt
  *                   gets called (hence "Programmable");
  *
  * - Orchestrator:   starting from SCUMM v5, games started using iMUSE, and apparently


Commit: 38ac6fbadcb8f5c46c4173ea04dba5b4977b06f8
    https://github.com/scummvm/scummvm/commit/38ac6fbadcb8f5c46c4173ea04dba5b4977b06f8
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: GFX: Fix bool comparison

Changed paths:
    engines/scumm/gfx.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index c1ff5d8f733..101b3202f66 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -4007,7 +4007,7 @@ void ScummEngine::dissolveEffect(int width, int height) {
 	int w, h;
 	int i;
 	bool canHalt = false;
-	bool is1x1Pattern = (width == height == 1);
+	bool is1x1Pattern = (width == 1 && height == 1);
 	// There's probably some less memory-hungry way of doing this. But
 	// since we're only dealing with relatively small images, it shouldn't
 	// be too bad.


Commit: 07eafca398854f0fe13feef7a7d9baf434e3683b
    https://github.com/scummvm/scummvm/commit/07eafca398854f0fe13feef7a7d9baf434e3683b
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: INSANE: Set correct video framerate

Changed paths:
    engines/scumm/insane/insane.cpp


diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
index 37997f0c968..f4f0444d03f 100644
--- a/engines/scumm/insane/insane.cpp
+++ b/engines/scumm/insane/insane.cpp
@@ -629,7 +629,7 @@ void Insane::startVideo(const char *filename, int num, int argC, int frameRate,
 		smush_setupSanFromStart(filename, 0, -1, -1, 0);
 	}
 
-	_player->play(filename, _speed, offset, startFrame);
+	_player->play(filename, frameRate, offset, startFrame);
 }
 
 void Insane::smush_warpMouse(int x, int y, int buttons) {


Commit: 2d12acec5ab912559bc870844030c1dff9fdc763
    https://github.com/scummvm/scummvm/commit/2d12acec5ab912559bc870844030c1dff9fdc763
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Add v7 quirk to IsMouthSyncOff()

Changed paths:
    engines/scumm/sound.cpp


diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 9ed8850db3a..edd388bcfcb 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -986,7 +986,12 @@ bool Sound::isMouthSyncOff(uint pos) {
 			break;
 		}
 	} while (pos + delay > j);
-	return val;
+
+	if (_vm->_game.version < 7) {
+		return val;
+	} else {
+		return (j != 0xFFFF) ? val : false;
+	}
 }
 
 int Sound::isSoundRunning(int sound) const {


Commit: ebb3f2a8426737fed67df0a579cb9f640f8e1834
    https://github.com/scummvm/scummvm/commit/ebb3f2a8426737fed67df0a579cb9f640f8e1834
Author: AndywinXp (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Implement Amiga timings

Changed paths:
    engines/scumm/gfx.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 101b3202f66..78c1089565f 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -3935,6 +3935,10 @@ void ScummEngine::transitionEffect(int a) {
 		delay = VAR(VAR_FADE_DELAY);
 	}
 
+	if (_game.platform == Common::kPlatformAmiga) {
+		delay *= 4;
+	}
+
 	// V3+ games have the number of iterations hardcoded; we also
 	// have that number hardcoded for MM NES, so let's target that too:
 	if (_game.version >= 3 || _game.platform == Common::kPlatformNES) {
@@ -4118,10 +4122,14 @@ void ScummEngine::dissolveEffect(int width, int height) {
 		// Test for NxM pattern...
 		canHalt |= !is1x1Pattern && (offsets[i] < vs->h);
 
-		// Halt rendering for a quarter frame...
+		// Halt rendering for a quarter frame (or a whole frame in case of Amiga)...
 		if (canHalt) {
 			canHalt = false;
-			waitForTimer(1);
+			if (_game.platform == Common::kPlatformAmiga) {
+				waitForTimer(4);
+			} else {
+				waitForTimer(1);
+			}
 		}
 	}
 
@@ -4143,7 +4151,11 @@ void ScummEngine::scrollEffect(int dir) {
 
 	// Keep in mind: this effect is only present in v5 and v6, so VAR_FADE_DELAY is
 	// never uninitialized. The following check is here for good measure only.
-	const int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
+	int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
+
+	if (_game.platform == Common::kPlatformAmiga) {
+		delay *= 4;
+	}
 
 	byte *src;
 	int m = _textSurfaceMultiplier;
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 39347740aef..46d15a2792c 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2264,6 +2264,8 @@ void ScummEngine::setTimerAndShakeFrequency() {
 		default:
 			_shakeTimerRate = _timerFrequency = 240.0;
 		}
+	} else if (_game.platform == Common::kPlatformAmiga) {
+		_shakeTimerRate = _timerFrequency = AMIGA_NTSC_VBLANK_RATE;
 	}
 }
 
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 391501c849e..7c3400caa1b 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -332,6 +332,22 @@ class ResourceManager;
 #define PIT_V7_SUBTIMER_INC            3977.0
 #define PIT_V7_SUBTIMER_THRESH         4971.0
 
+/**
+ * Amiga timing constants.
+ *
+ * Amiga versions of SCUMM games update the game timer at every
+ * V-Blank interrupt, incrementing it by 4 each time (which means
+ * a full frame/jiffie). The shake timer is instead updated every
+ * other V-Blank interrupt, so 8 quarter frames (2 frames/jiffies)
+ * at a time.
+ *
+ * The base rate is 50Hz for PAL systems and 60Hz for NTSC systems.
+ * We're going to target the latter in here, converting it in a quarter
+ * frame frequency.
+ */
+
+#define AMIGA_NTSC_VBLANK_RATE 240.0
+
 /**
  * Base class for all SCUMM engines.
  */


Commit: ca527c8390022f54bf3863c4819d5ec1627f166d
    https://github.com/scummvm/scummvm/commit/ca527c8390022f54bf3863c4819d5ec1627f166d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2022-05-21T22:44:01+03:00

Commit Message:
SCUMM: Implement a better approximation of some Amiga quirks

Changed paths:
    engines/scumm/gfx.cpp


diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 78c1089565f..fce188bc18d 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -3935,8 +3935,17 @@ void ScummEngine::transitionEffect(int a) {
 		delay = VAR(VAR_FADE_DELAY);
 	}
 
+	// Amiga handles timing a whole frame at a time
+	// instead of using quarter frames; the following
+	// code gives my best approximation of that behavior
+	// and the resulting timing
 	if (_game.platform == Common::kPlatformAmiga) {
-		delay *= 4;
+		int amigaRest = (delay % 4);
+		delay = (delay / 4);
+		if (amigaRest > 0) {
+			delay += 1;
+		}
+		delay *= 10;
 	}
 
 	// V3+ games have the number of iterations hardcoded; we also
@@ -4153,8 +4162,17 @@ void ScummEngine::scrollEffect(int dir) {
 	// never uninitialized. The following check is here for good measure only.
 	int delay = (VAR_FADE_DELAY != 0xFF) ? VAR(VAR_FADE_DELAY) : kPictureDelay;
 
+	// Amiga handles timing a whole frame at a time
+	// instead of using quarter frames; the following
+	// code gives my best approximation of that behavior
+	// and the resulting timing
 	if (_game.platform == Common::kPlatformAmiga) {
-		delay *= 4;
+		int amigaRest = (delay % 4);
+		delay = (delay / 4);
+		if (amigaRest > 0) {
+			delay += 1;
+		}
+		delay *= 10;
 	}
 
 	byte *src;




More information about the Scummvm-git-logs mailing list