Opened 2 years ago

Closed 21 months ago

#13378 closed defect (fixed)

SCUMM: SAMNMAX: Timing Problems in German CD Intro

Reported by: GermanTribun Owned by: eriktorbjorn
Priority: normal Component: Engine: SCUMM
Version: Keywords:
Cc: Game: Sam and Max

Description

ScummVM: 2.5.1.
System: Windows 10 Pro
Game: Sam & Max CD, German

Now, this already was a problem in the original interpreter. The intro of the German CD version has broken animation timing, causing some animations to not be in synch with the audio.

This Let's Play shows the English into: https://www.youtube.com/watch?v=pdU0Njkek5s&t=3240s
This Let's Play shows the German intro: https://www.youtube.com/watch?v=ByC0rswsjEA

The timing problems in the German intro are especially obvious the first time the scene shifts from the doctor to the lab (his talking animation is way too fast and finishes way before the audio) and when Sam is attempting to free the hostage (his animations are not timed correctly with the audio and thus he freezes at the end to let the audio catch up).

Since the English intro does have the correct timing, something must have been broken in the translation process. Can this be fixed?

Change History (20)

comment:1 by AndywinXp, 2 years ago

Summary: SCUMM: Timing Problems in German CD IntroSCUMM: SAMNMAX: Timing Problems in German CD Intro

The italian version has the same identical issue; i would say that it happens because of the lack of synchronization point which were absent in translated versions. But I frankly know nothing about SCUMM v6 audio, so that's only a guess

comment:2 by eriktorbjorn, 2 years ago

i would say that it happens because of the lack of synchronization point which were absent in translated versions

It may be much simpler than that, at least in the German version. (I thought I didn't have that one, but then I realized GOG included a number of localized versions.)

Here's part of the English script-65:

[0377] (CA) delayFrames(3)
[037B] (B4) printLine.begin()
[037D] (B4) printLine.color(10)
[0382] (B4) printLine.XY(240,20)
[038A] (B4) printLine.msg(sound(0x39C91C, 0xA) + "^You'll fry like a pork sausage.")

And here's the German one:

[0381] (CA) delayFrames(3)
[0385] (43) VAR_TIMER_NEXT = 1
[038B] (B4) printLine.begin()
[038D] (B4) printLine.color(10)
[0392] (B4) printLine.XY(240,20)
[039A] (B4) printLine.msg(sound(0x28428, 0xA) + "Wirst Du brutzeln, wie eine grobe Bratwurst!")

Earlier, both scripts set VAR_TIMER_NEXT to 6, and the German version sets it back to 6 again afterwards. The only way this makes sense to me is if the localization was developed on some horribly slow computer, and even that seems like a stretch.

So anyway, something like this should fix it. I just need to check the other localized versions I have to see what their scripts do.

diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index f763b967056..f6f79d50ec4 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -648,6 +648,17 @@ void ScummEngine::writeVar(uint var, int value) {
                        }
                }
 
+               // WORKAROUND bug #13378: For whatever reason, some localized
+               // versions set the game to run really fast for the part of the
+               // Sam & Max intro where the scientist says "You'll fry like a
+               // pork sausage." It doesn't look accidental, because the speed
+               // is set back to normal afterwards. We don't want that in
+               // ScummVM, but surely it would have run at ludicrous speed
+               // in the original interpreter, on any reasonable machine?
+
+               if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 65 && value == 1 && _enableEnhancements)
+                       return;
+
                _scummVars[var] = value;
 
                // Unlike the PC version, the Macintosh version of Loom appears

comment:3 by eriktorbjorn, 2 years ago

French:

[039A] (CA) delayFrames(3)
[039E] (B4) printLine.begin()
[03A0] (B4) printLine.color(10)
[03A5] (B4) printLine.XY(240,20)
[03AD] (B4) printLine.msg(sound(0x163C660, 0xA) + "Je vais vous faire frire comme un vulgaire saucisse!")

Italian:

[0373] (CA) delayFrames(3)
[0377] (B4) printLine.begin()
[0379] (B4) printLine.color(10)
[037E] (B4) printLine.XY(240,20)
[0386] (B4) printLine.msg(sound(0x7153DC2, 0xA) + "^Friggerai come una salsiccia.")

Spanish:

[037C] (CA) delayFrames(3)
[0380] (B4) printLine.begin()
[0382] (B4) printLine.color(10)
[0387] (B4) printLine.XY(240,20)
[038F] (B4) printLine.msg(sound(0x39C91C, 0xA) + "Te freir\xA0s como una salchicha de cerdo.")

So at least with the talkie versions GOG provides, it only seems to happen in the German version of the game.

comment:4 by eriktorbjorn, 2 years ago

I wonder if https://www.youtube.com/watch?v=utCR_GfMyLk&list=PLLAIg3KRKON6eOpfjG0_XGaqryup_892E&index=1 was done with DOSBox or ScummVM. It could be DOSBox. It doesn't run unnaturally fast (for whatever reason), and it has the graphics glitch in the Hall of Oddities that ScummVM works around.

If the original doesn't run that fast, ScummVM probably shouldn't flag it as en enhancement...

comment:5 by AndywinXp, 2 years ago

Phew I'm glad that's easier than I thought, thanks for finding the scripts.

The italian version goes berserk when Max is punching the mad scientists, maybe there's the exact same script issue there.

I guess they put those VAR_TIMER_NEXT statements in order to account for different phrase lengths but I suppose they didn't do too good of a job there.

comment:6 by AndywinXp, 2 years ago

Oh yes, they did change VAR_TIMER_NEXT. For each. Single. Close-up. Phrase.

(Italian script below, with the english translation next to each printLine.msg() line)

[...]
[01A3] (43) VAR_TIMER_NEXT = 4
[01A9] (B4) printLine.begin()
[01AB] (B4) printLine.color(10)
[01B0] (B4) printLine.msg(sound(0x712BAD5, 0xA) + "Siamo usciti solo tre volte insieme e mi dici gi\x85 che vuoi che restiamo solo amici?") // "We've only gone out together three times, and already you're telling me you just want to be friends?"
[...]
[0279] (43) VAR_TIMER_NEXT = 5
[027F] (B4) printLine.begin()
[0281] (B4) printLine.color(10)
[0286] (B4) printLine.msg(sound(0x71443F9, 0xA) + "Non mi hai mai dato una possibilit\x85!" + wait() + "E per questo^") // "You've never gave me a chance, and for that..."
[...]
[0321] (43) VAR_TIMER_NEXT = 6
[...]
[0386] (B4) printLine.msg(sound(0x7153DC2, 0xA) + "^Friggerai come una salsiccia.") // "You'll fry like a pork sausage"
[...]
[0739] (43) VAR_TIMER_NEXT = 3
[...]
[07A6] (B4) printLine.msg(sound(0x719C486, 0xA) + "Questo non sembra il Lincoln Tunnel, Sam.") // "This doesn't look like the Lincoln Tunnel, Sam"
[...]
[081A] (43) VAR_TIMER_NEXT = 4
[...]
[0827] (B4) printLine.msg(sound(0x71A7543, 0xA) + "Sembra una situazione di stallo marginalmente instabile, Max.") // "Looks to me like a marginally volatile hostage situation, Max"
[...]
[08AA] (43) VAR_TIMER_NEXT = 4
[08B0] (43) VAR_TIMER_NEXT = 5
[...]
[08BD] (B4) printLine.msg(sound(0x71BBA45, 0xA) + "Ooh! Ci\x95 significa una pedata nel didietro bianco e paffuto di qualche scienziato pazzo?") // "Ooh! Does this mean we get to kick some puffy white mad scientist butt?"
[...]
[0971] (43) VAR_TIMER_NEXT = 6
[...]
[0985] (B4) printLine.msg(sound(0x71D6CFF, 0xA) + "Non vedo una ragione per il contrario!") // "Can't think of a reason not to"
[...]
[09CB] (43) VAR_TIMER_NEXT = 6
[...]
[09DB] (43) VAR_TIMER_NEXT = 6
[...]
[0A61] (B4) printLine.msg(sound(0x71E0531, 0xA) + "Sarete inutili, Poliziotti Privati!") // "You'll be of no use, freelance police!"
[...]
[0AB5] (43) VAR_TIMER_NEXT = 9
[...]
[0AC2] (B4) printLine.msg(sound(0x71EDD30, 0xA) + "Con un colpo di questa leva, la mia ingrata invitata a pranzo sar\x85 ridotta in mezza tazzina di materia atomica disorientata!") // "With a flip of a lever" blah blah blah, too long to transcribe :-P
[...]
[0B5B] (43) VAR_TIMER_NEXT = 6
[...]

// This is where the speed goes so high that the Sam sequence of words goes totally out of sync
[0E51] (43) VAR_TIMER_NEXT = 2
[0E57] (B4) printLine.begin()
[0E59] (B4) printLine.color(6)
[0E5E] (B4) printLine.XY(220,20)
[0E66] (B4) printLine.msg(sound(0x723E2C5, 0xA) + "Ooh." + wait() + "Oh." + wait() + "Ehi, bel colpo." + wait() + "Yikes!" + wait() + "Huh?")
[...]
[0EA4] (43) VAR_TIMER_NEXT = 6
[...]
[0F0B] (43) VAR_TIMER_NEXT = 4
[0F11] (BA) talkActor(sound(0x7250FB2, 0xA) + "Non \x8A un uomo vero, Sam!" + wait() + "Posso tenere la sua testa come ricordo?" + wait() + "Perch\x82 pensi che stia ticchettando?",3) // "It's not a real guy Sam! Can I keep his head for a souvenir? Why do you think it's ticking?"
[...]

Last edited 2 years ago by AndywinXp (previous) (diff)

comment:7 by eriktorbjorn, 2 years ago

The scripts have a lot of differences between the localized versions, probably to get the animation to sync better with the speech.

That said, the lowest the English version ever sets VAR_TIMER_NEXT to is 3, and that's for when Max says "This doesn't look like the Lincoln Tunnel, Sam."

The outliers are

  • The German version sets it to 1 for "You'll fry like a pork sausage!" Suppressing this would leave it at 6, same as the English version
  • The Italian version sets it to 2 when Max beats up the scientist. I guess it was sped up to match the subtitles better, but the animation looks weird. Suppressing this would leave it at 6, same as the English version.

So that gives the following revised workaround:

diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index f763b967056..0bc6931ccdc 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -648,6 +648,24 @@ void ScummEngine::writeVar(uint var, int value) {
                        }
                }
 
+               // WORKAROUND bug #13378: For whatever reason, the German and
+               // Italian talkie versions (I can't check the floppy versions)
+               // set the game to run much too fast in some parts of the intro.
+               // Some differences are natural because of the different lengths
+               // of the spoken lines, but 1 or 2 is too fast.
+
+               if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 65 && _enableEnhancements) {
+                       // "Wirst Du brutzeln, wie eine grobe Bratwurst!"
+                       if (value == 1 && _language == Common::DE_DEU)
+                               return;
+
+                       // Max beats up the scientist. This was probably to
+                       // match the subtitles to the speech better, but it
+                       // makes the animation look strange.
+                       if (value == 2 && _language == Common::IT_ITA)
+                               return;
+               }
+

comment:8 by AndywinXp, 2 years ago

I like it; I think setting value to 3 or 4 for the italian version would better match the intention, even though the subtitles seem to stay for longer than they need to (I guess _talkDelay would be the culprit here?), but that's an entirely different matter. Either way, personally I like this workaround.

comment:9 by GermanTribun, 2 years ago

How is the German timing broken when Max beats up the scientist? In the German version that scene quickly goes out of synch as well.

comment:10 by eriktorbjorn, 2 years ago

How is the German timing broken when Max beats up the scientist? In the German version that scene quickly goes out of synch as well.

If you mean how the speech isn't syncing up to the subtitles/mouth movements here:

[0F10] (B4) printLine.msg(sound(0x11DEF4, 0xA) + "Ohh!" + wait() + "Hey." + wait() + "Klasse Schlag!" + wait() + "Uiii!" + wait() + "H\x84h?")

That's probably a lot harder to do anything about. If I understand correctly, the script uses two different tools to adjust the sync:

  • VAR_TIMER_NEXT, which affects the overall speed of the game. The Italian version sets this to 2 to speed up the scene, but though I guess it syncs up better to the subtitles the animation looks a bit ridiculous.
  • VAR_CHARINC, which determines how long a message should be visible on screen, based on the number of characters in it. Which probably works a lot better for long messages than really short ones.

(At least, I think that's how it works, and that the embedded wait() instructions are equivalent to waitForMessage().)

Apparently the whole string of exclamations is a single sound. You'd think that if they had been serious about getting them to sync up, there would have been one sound for each exclamation, and they'd have used delayFrames() or something like that to specify the exact delay between them, rather than depending on a calculated guess.

Last edited 2 years ago by eriktorbjorn (previous) (diff)

comment:11 by eriktorbjorn, 2 years ago

Oops, I just realized my workaround doesn't actually check which variable the script tries to set. This should be better:

diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index f763b967056..f07218786e9 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -648,6 +648,27 @@ void ScummEngine::writeVar(uint var, int value) {
                        }
                }
 
+               // WORKAROUND bug #13378: For whatever reason, the German and
+               // Italian talkie versions (I can't check the floppy versions)
+               // set the game to run much too fast in some parts of the intro.
+               // Some differences are natural because of the different lengths
+               // of the spoken lines, but 1 or 2 is too fast.
+               //
+               // Any modifications here depend on knowing if the script will
+               // set the timer value back to something sensible afterwards.
+
+               if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 65 && var == VAR_TIMER_NEXT && _enableEnhancements) {
+                       // "Wirst Du brutzeln, wie eine grobe Bratwurst!"
+                       if (value == 1 && _language == Common::DE_DEU)
+                               value = 4;
+
+                       // Max beats up the scientist. This was probably to
+                       // match the subtitles to the speech better, but it
+                       // makes the animation look strange.
+                       if (value == 2 && _language == Common::IT_ITA)
+                               return;
+               }
+
                _scummVars[var] = value;
 
                // Unlike the PC version, the Macintosh version of Loom appears

(This also adjusts the timing for the German "Wirst Du brutzeln" line a little bit.)

I have an idea for how to possibly fix the timing while Max is beating up the scientist a bit, but I'm not sure if it will work out yet.

comment:12 by eriktorbjorn, 2 years ago

So it is possible to adjust the timing of the intro further, though working out the exact details is a slow and tedious process. Which is why I only have it for the German intro so far. I think this is about as good as that one will ever get. It works by:

  • Ignoring when the script sets the main timer to be unnecessary fast.
  • Hard-coding talk delays for broken up sentences, when the calculated ones aren't good enough.
  • Slowing down Sam's mouth animation while Max is beating up the scientist.

I need to clean things up, e.g. to make it more obvious exactly which sentences are being adjusted. But I probably won't have any more time for this today.

diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index f763b967056..f07218786e9 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -648,6 +648,27 @@ void ScummEngine::writeVar(uint var, int value) {
 			}
 		}
 
+		// WORKAROUND bug #13378: For whatever reason, the German and
+		// Italian talkie versions (I can't check the floppy versions)
+		// set the game to run much too fast in some parts of the intro.
+		// Some differences are natural because of the different lengths
+		// of the spoken lines, but 1 or 2 is too fast.
+		//
+		// Any modifications here depend on knowing if the script will
+		// set the timer value back to something sensible afterwards.
+
+		if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 65 && var == VAR_TIMER_NEXT && _enableEnhancements) {
+			// "Wirst Du brutzeln, wie eine grobe Bratwurst!"
+			if (value == 1 && _language == Common::DE_DEU)
+				value = 4;
+
+			// Max beats up the scientist. This was probably to
+			// match the subtitles to the speech better, but it
+			// makes the animation look strange.
+			if (value == 2 && _language == Common::IT_ITA)
+				return;
+		}
+
 		_scummVars[var] = value;
 
 		// Unlike the PC version, the Macintosh version of Loom appears
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 84c98944ba1..a3e15eb1316 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -72,6 +72,22 @@ void ScummEngine::printString(int m, const byte *msg) {
 			return;
 		}
 
+		// WORKAROUND bug #13378: Sam's reactions during the intro run
+		// much too quick for the subtitles in some localizations. We
+		// get around this by slowing down that entire animation, while
+		// leaving the reset of the animations unchanged.
+		//
+		// The animation speed is not very fine grained, though.
+		if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 65 && _enableEnhancements) {
+			if (_language == Common::DE_DEU) {
+				if (memcmp(msg + 16, "Ohh!", 4) == 0) {
+					Actor *a = derefActorSafe(2, "printString");
+					if (a)
+						a->setAnimSpeed(3);
+				}
+			}
+		}
+
 		actorTalk(msg);
 		break;
 	case 1:
@@ -354,6 +370,44 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
 			_keepText = false;
 			_msgCount = 0;
 			endLoop = true;
+
+			// WORKAROUND bug #13378: Some of the speech is badly
+			// synced to the subtitles, particularly in the
+			// localized versions. This happens because a single
+			// speech line is used for a text that's broken up by
+			// one or more embedded "wait" codes. Rather than
+			// relying on the calculated talk delay, hard-code
+			// better ones.
+			if (_game.id == GID_SAMNMAX && _enableEnhancements && isScriptRunning(65)) {
+ 				if (_language == Common::DE_DEU) {
+					if (memcmp(buffer - 10, "gegeben!", 8) == 0) {
+						_talkDelay = 110;
+					} else if (memcmp(buffer - 12, "nicht mag^", 10) == 0) {
+						_talkDelay = 120;
+					} else if (memcmp(buffer - 6, "Ohh!", 4) == 0) {
+						_talkDelay = 130;
+					} else if (memcmp(buffer - 6, "Hey.", 4) == 0) {
+						_talkDelay = 150;
+					} else if (memcmp(buffer - 9, "Schlag!", 7) == 0) {
+						_talkDelay = 185;
+
+						Actor *a = derefActorSafe(2, "handleNextCharsetCode");
+						if (a)
+							a->setAnimSpeed(2);
+					} else if (memcmp(buffer - 7, "Uiii!", 5) == 0) {
+						_talkDelay = 150;
+					} else if (memcmp(buffer - 6, "Sam!", 4) == 0) {
+						_talkDelay = 110;
+					} else if (memcmp(buffer - 11, "behalten?", 9) == 0) {
+						_talkDelay = 120;
+					} else if (memcmp(buffer - 6, "Sam.", 4) == 0) {
+						_talkDelay = 90;
+					} else if (memcmp(buffer - 9, "laufen.", 7) == 0) {
+						_talkDelay = 240;
+					}
+				}
+			}
+
 			break;
 		case 8:
 			// Ignore this code here. Occurs e.g. in MI2 when you
Last edited 2 years ago by eriktorbjorn (previous) (diff)

comment:13 by eriktorbjorn, 2 years ago

To make things easier to keep track of, I'm making a branch for this. I've only committed the German bits to it yet, though:

https://github.com/eriktorbjorn/scummvm/commits/samnmax-intro-timing

comment:14 by eriktorbjorn, 2 years ago

I've committed adjustments for the Italian intro to my branch. I've cleaned up the code a lot, but it's still slow, tedious work.

comment:15 by eriktorbjorn, 2 years ago

The final (?) version of the changes can be found in https://github.com/scummvm/scummvm/pull/3777

I'm a bit concerned that the French CD and floppy versions don't use the same voices. I don't own any of the floppy versions, so I don't know if that's true for other languages as well.

comment:16 by GermanTribun, 2 years ago

Well, I do know that the voices were completely re-done for the German CD version and that the Floppy version had horrible placeholder voices (no wonder it was redone). The Floppy version has become extinct, but in case... see below.

I did collect tons of old stuff over the years and I do have the files for various floppy versions of the game. Any way I can make these available to you for testing purposes?

in reply to:  16 comment:17 by eriktorbjorn, 2 years ago

Replying to GermanTribun:

I did collect tons of old stuff over the years and I do have the files for various floppy versions of the game. Any way I can make these available to you for testing purposes?

Thanks for the offer! I was able to borrow the German floppy files to test with, and have updated the pull request for that one. (One scene has to be slowed down, just like in the CD version, but there's no need to slow down Sam's speech animation during the fighting.)

So this is where we're at at the moment:

LanguageCDFloppy
EnglishDoneDone
GermanDoneDone
ItalianDoneDone
FrenchDoneDone
SpanishDoneUntested

The English floppy version uses the same voices as the CD version.

The German floppy version uses different voices for Sam and Max. There's no need to slow down Sam's talk animation during the fight scene.

The Italian floppy version uses English voices, and doesn't speed up the fight scene.

The French floppy version uses different voices.

https://www.youtube.com/watch?v=2m38adNzhbo suggests that the Spanish floppy version is just like the CD version, i.e. uses the English voices. Since the subtitles appear identical to me, it probably works fine.

Last edited 2 years ago by eriktorbjorn (previous) (diff)

comment:18 by GermanTribun, 2 years ago

The Spanish and Italian Floppy versions both have the English audio, meaning the Italian CD version was dubbed after that. That English audio in turn sounds the same as the one on CD, just of lower quality (probably for size reasons). I also saw no problem with the beat-up scene in the Italian floppy version.

in reply to:  18 comment:19 by eriktorbjorn, 2 years ago

Replying to GermanTribun:

The Spanish and Italian Floppy versions both have the English audio, meaning the Italian CD version was dubbed after that. That English audio in turn sounds the same as the one on CD, just of lower quality (probably for size reasons). I also saw no problem with the beat-up scene in the Italian floppy version.

Thanks for confirming. I think everything should work in that pull request now, except the Spanish floppy version hasn't actually been tested. But since the speech is the same (English) and the subtitles appear to be identical, the timing changes that work for the CD version should also work for the floppy version.

Since everything was done by trial and error, you can imagine I'm getting pretty sick of the Sam & Max intro. So if any other versions turn up I may scream. :-)

comment:20 by AndywinXp, 21 months ago

Owner: set to eriktorbjorn
Resolution: fixed
Status: newclosed

The improvements made by eriktorbjorn appear to work beautifully on every version, thanks!
Closing this

Note: See TracTickets for help on using tickets.