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

sev- noreply at scummvm.org
Mon Aug 18 10:40:22 UTC 2025


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

Summary:
01ba459d9c MOHAWK: Fix sound discontinuity.
50926fec90 MOHAWK: Refactor discontinuity check.
d5077a13b3 MOHAWK: Add option to toggle discontinuity check.


Commit: 01ba459d9c9fdc9b5289d53f81351213f3a580b7
    https://github.com/scummvm/scummvm/commit/01ba459d9c9fdc9b5289d53f81351213f3a580b7
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2025-08-18T12:40:18+02:00

Commit Message:
MOHAWK: Fix sound discontinuity.

Changed paths:
    engines/mohawk/sound.cpp


diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index e0a674f2112..c7e36cb6eb1 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -137,6 +137,57 @@ Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *s
 				dataChunk.loopStart = stream->readUint32BE();
 				dataChunk.loopEnd = stream->readUint32BE();
 
+				// This fix applies a balanced heuristic to detect and prevent audible pops/clicks
+				// at the end of 8-bit unsigned PCM samples, which were present in the original game assets.
+				if (dataChunk.encoding == kCodecRaw && dataChunk.bitsPerSample == 8 && dataChunk.sampleCount >= 4) {
+					const int PCM8_U_SILENCE = 0x80;
+					const int SQUELCH = 32; // Threshold of discontinuity before removing. Lower values = increased sensitivity.
+					bool is_safe = false;
+
+					// Peek at the last 4 samples without permanently moving the stream pointer.
+					uint32 current_pos = stream->pos();
+					stream->seek(current_pos + dataSize - 4, SEEK_SET);
+					byte s[4];
+					stream->read(s, 4);
+					stream->seek(current_pos, SEEK_SET); // Return to original position
+
+					// Path 1: Check for sustained quietness. If all samples are very close to silence,
+					// any minor fluctuation is inaudible and the sound is considered safe.
+					bool is_stable_and_quiet = true;
+					for (int i = 0; i < 4; i++) {
+						if (abs(s[i] - PCM8_U_SILENCE) > SQUELCH) {
+							is_stable_and_quiet = false;
+							break;
+						}
+					}
+					if (is_stable_and_quiet) {
+						is_safe = true;
+					}
+
+					// Path 2: If not stable/quiet, check for a consistent fade-out trend.
+					if (!is_safe) {
+						int dist_last = abs(s[3] - PCM8_U_SILENCE);
+						int dist_prev = abs(s[2] - PCM8_U_SILENCE);
+						int dist_ante = abs(s[1] - PCM8_U_SILENCE);
+
+						if (dist_last < dist_prev && dist_prev < dist_ante) {
+							is_safe = true;
+						}
+					}
+
+					// If the ending is neither stable nor fading, apply the fix.
+					if (!is_safe) {
+						debug(0, "MOHAWK: Pop/click detected at sample %u. Final samples: %02x %02x %02x %02x. Truncating one sample.",
+							dataChunk.sampleCount, s[0], s[1], s[2], s[3]);
+
+						dataChunk.sampleCount--;
+						dataSize--; // Also decrement the total data size to be read.
+						if (dataChunk.loopCount == 0xFFFF && dataChunk.loopEnd > dataChunk.sampleCount) {
+							dataChunk.loopEnd = dataChunk.sampleCount;
+						}
+					}
+				}
+
 				// NOTE: We currently ignore all of the loop parameters here. Myst uses the
 				// loopCount variable but the loopStart and loopEnd are always 0 and the size of
 				// the sample. Myst ME doesn't use the Mohawk Sound format and just standard WAVE


Commit: 50926fec90c62b8fc8503e6445e63cd9fed9b8d6
    https://github.com/scummvm/scummvm/commit/50926fec90c62b8fc8503e6445e63cd9fed9b8d6
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2025-08-18T12:40:18+02:00

Commit Message:
MOHAWK: Refactor discontinuity check.

Apply suggestions.

Changed paths:
    engines/mohawk/sound.cpp


diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index c7e36cb6eb1..a1fa6d44ebc 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -35,6 +35,65 @@
 
 namespace Mohawk {
 
+/**
+ * Applies a heuristic to detect and address discontinuity
+ * at the end of 8-bit unsigned PCM samples, which were present in the
+ * original game assets. The function modifies the DataChunk and dataSize
+ * directly if a fix is applied.
+ *
+ * @param dataChunk The DataChunk containing sample metadata.
+ * @param dataSize The total size of the data.
+ * @param stream The stream to look for samples.
+ */
+void scanAndFixAudioPops(DataChunk &dataChunk, uint32 &dataSize, Common::SeekableReadStream *stream) {
+	const int PCM8_U_SILENCE = 0x80;
+	const int SQUELCH = 32; // Threshold of discontinuity before removing. Lower values = increased sensitivity.
+	bool is_safe = false;
+
+	// Peek at the last 4 samples without permanently moving the stream pointer.
+	uint32 current_pos = stream->pos();
+	stream->seek(current_pos + dataSize - 4, SEEK_SET);
+	byte s[4];
+	stream->read(s, 4);
+	stream->seek(current_pos, SEEK_SET); // Return to original position
+
+	// Path 1: Check for sustained quietness. If all samples are very close to silence,
+	// any minor fluctuation is inaudible and the sound is considered safe.
+	bool is_stable_and_quiet = true;
+	for (int i = 0; i < 4; i++) {
+		if (abs(s[i] - PCM8_U_SILENCE) > SQUELCH) {
+			is_stable_and_quiet = false;
+			break;
+		}
+	}
+	if (is_stable_and_quiet) {
+		is_safe = true;
+	}
+
+	// Path 2: If not stable/quiet, check for a consistent fade-out trend.
+	if (!is_safe) {
+		int dist_last = abs(s[3] - PCM8_U_SILENCE);
+		int dist_prev = abs(s[2] - PCM8_U_SILENCE);
+		int dist_ante = abs(s[1] - PCM8_U_SILENCE);
+
+		if (dist_last < dist_prev && dist_prev < dist_ante) {
+			is_safe = true;
+		}
+	}
+
+	// If the ending is neither stable nor fading, apply the fix.
+	if (!is_safe) {
+		debug(0, "MOHAWK: Pop/click detected at sample %u. Final samples: %02x %02x %02x %02x. Truncating one sample.",
+			dataChunk.sampleCount, s[0], s[1], s[2], s[3]);
+
+		dataChunk.sampleCount--;
+		dataSize--; // Also decrement the total data size to be read.
+		if (dataChunk.loopCount == 0xFFFF && dataChunk.loopEnd > dataChunk.sampleCount) {
+			dataChunk.loopEnd = dataChunk.sampleCount;
+		}
+	}
+}
+
 Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *stream, CueList *cueList) {
 	uint32 tag = 0;
 	ADPCMStatus adpcmStatus;
@@ -137,56 +196,10 @@ Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *s
 				dataChunk.loopStart = stream->readUint32BE();
 				dataChunk.loopEnd = stream->readUint32BE();
 
-				// This fix applies a balanced heuristic to detect and prevent audible pops/clicks
-				// at the end of 8-bit unsigned PCM samples, which were present in the original game assets.
-				if (dataChunk.encoding == kCodecRaw && dataChunk.bitsPerSample == 8 && dataChunk.sampleCount >= 4) {
-					const int PCM8_U_SILENCE = 0x80;
-					const int SQUELCH = 32; // Threshold of discontinuity before removing. Lower values = increased sensitivity.
-					bool is_safe = false;
-
-					// Peek at the last 4 samples without permanently moving the stream pointer.
-					uint32 current_pos = stream->pos();
-					stream->seek(current_pos + dataSize - 4, SEEK_SET);
-					byte s[4];
-					stream->read(s, 4);
-					stream->seek(current_pos, SEEK_SET); // Return to original position
-
-					// Path 1: Check for sustained quietness. If all samples are very close to silence,
-					// any minor fluctuation is inaudible and the sound is considered safe.
-					bool is_stable_and_quiet = true;
-					for (int i = 0; i < 4; i++) {
-						if (abs(s[i] - PCM8_U_SILENCE) > SQUELCH) {
-							is_stable_and_quiet = false;
-							break;
-						}
-					}
-					if (is_stable_and_quiet) {
-						is_safe = true;
-					}
-
-					// Path 2: If not stable/quiet, check for a consistent fade-out trend.
-					if (!is_safe) {
-						int dist_last = abs(s[3] - PCM8_U_SILENCE);
-						int dist_prev = abs(s[2] - PCM8_U_SILENCE);
-						int dist_ante = abs(s[1] - PCM8_U_SILENCE);
-
-						if (dist_last < dist_prev && dist_prev < dist_ante) {
-							is_safe = true;
-						}
-					}
-
-					// If the ending is neither stable nor fading, apply the fix.
-					if (!is_safe) {
-						debug(0, "MOHAWK: Pop/click detected at sample %u. Final samples: %02x %02x %02x %02x. Truncating one sample.",
-							dataChunk.sampleCount, s[0], s[1], s[2], s[3]);
-
-						dataChunk.sampleCount--;
-						dataSize--; // Also decrement the total data size to be read.
-						if (dataChunk.loopCount == 0xFFFF && dataChunk.loopEnd > dataChunk.sampleCount) {
-							dataChunk.loopEnd = dataChunk.sampleCount;
-						}
-					}
-				}
+			// For unsigned 8-bit PCM, check for and fix a potential pop/click at the end of the sample.
+			if (dataChunk.encoding == kCodecRaw && dataChunk.bitsPerSample == 8 && dataChunk.sampleCount >= 4) {
+				scanAndFixAudioPops(dataChunk, dataSize, stream);
+			}
 
 				// NOTE: We currently ignore all of the loop parameters here. Myst uses the
 				// loopCount variable but the loopStart and loopEnd are always 0 and the size of


Commit: d5077a13b3480fc730e4f660b8e85cd882e842fa
    https://github.com/scummvm/scummvm/commit/d5077a13b3480fc730e4f660b8e85cd882e842fa
Author: Alstruit (34786806+Alstruit at users.noreply.github.com)
Date: 2025-08-18T12:40:18+02:00

Commit Message:
MOHAWK: Add option to toggle discontinuity check.

Applies to non Myst/Riven games.

- Myst does not have audio discontinuity.
- Riven does not use 8 bit unsigned PCM.

Changed paths:
    engines/mohawk/dialogs.cpp
    engines/mohawk/dialogs.h
    engines/mohawk/metaengine.cpp
    engines/mohawk/sound.cpp


diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index 00fd90dd3be..e9a12327cec 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -449,4 +449,33 @@ bool RivenOptionsWidget::save() {
 
 #endif
 
+MohawkDefaultOptionsWidget::MohawkDefaultOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
+	OptionsContainerWidget(boss, name, "MohawkEngineOptionsDialog", domain) {
+
+	_audioPopFixCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "MohawkEngineOptionsDialog.AudioDiscontinuityFix",
+		_("Fix audio pops/clicks"),
+		_("Reduces audible pops at the end of some sound effects (Non Myst/Riven only)."));
+}
+
+MohawkDefaultOptionsWidget::~MohawkDefaultOptionsWidget() {
+}
+
+void MohawkDefaultOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
+	layouts.addDialog(layoutName, overlayedLayout)
+		.addLayout(GUI::ThemeLayout::kLayoutVertical)
+			.addPadding(0, 0, 0, 0)
+			.addWidget("AudioDiscontinuityFix", "Checkbox")
+		.closeLayout()
+	.closeDialog();
+}
+
+void MohawkDefaultOptionsWidget::load() {
+	_audioPopFixCheckbox->setState(ConfMan.getBool("fix_audio_pops", _domain));
+}
+
+bool MohawkDefaultOptionsWidget::save() {
+	ConfMan.setBool("fix_audio_pops", _audioPopFixCheckbox->getState(), _domain);
+	return true;
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/dialogs.h b/engines/mohawk/dialogs.h
index 8452788af43..e846bf0a85b 100644
--- a/engines/mohawk/dialogs.h
+++ b/engines/mohawk/dialogs.h
@@ -143,6 +143,22 @@ private:
 
 #endif
 
+class MohawkDefaultOptionsWidget : public GUI::OptionsContainerWidget {
+public:
+	MohawkDefaultOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
+	~MohawkDefaultOptionsWidget() override;
+
+	// OptionsContainerWidget API
+	void load() override;
+	bool save() override;
+
+private:
+	// OptionsContainerWidget API
+	void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
+
+	GUI::CheckboxWidget *_audioPopFixCheckbox;
+};
+
 } // End of namespace Mohawk
 
 #endif
diff --git a/engines/mohawk/metaengine.cpp b/engines/mohawk/metaengine.cpp
index 85752ece985..b9f9ce30c55 100644
--- a/engines/mohawk/metaengine.cpp
+++ b/engines/mohawk/metaengine.cpp
@@ -334,6 +334,14 @@ void MohawkMetaEngine::registerDefaultSettings(const Common::String &target) con
 		return Mohawk::MohawkMetaEngine_Riven::registerDefaultSettings();
 	}
 
+	// Rest of the Mohawk games.
+	ConfMan.registerDefault("fix_audio_pops", true);
+
+	if (!ConfMan.hasKey("fix_audio_pops", target)) {
+		ConfMan.setBool("fix_audio_pops", true, target);
+		ConfMan.flushToDisk();
+	}
+
 	return MetaEngine::registerDefaultSettings(target);
 }
 
@@ -351,7 +359,7 @@ GUI::OptionsContainerWidget *MohawkMetaEngine::buildEngineOptionsWidget(GUI::Gui
 	}
 #endif
 
-	return MetaEngine::buildEngineOptionsWidget(boss, name, target);
+	return new Mohawk::MohawkDefaultOptionsWidget(boss, name, target);
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(MOHAWK)
diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index a1fa6d44ebc..8c104e0e17b 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/debug.h"
+#include "common/config-manager.h"
 
 #include "audio/mididrv.h"
 #include "audio/midiparser.h"
@@ -198,7 +199,12 @@ Audio::RewindableAudioStream *makeMohawkWaveStream(Common::SeekableReadStream *s
 
 			// For unsigned 8-bit PCM, check for and fix a potential pop/click at the end of the sample.
 			if (dataChunk.encoding == kCodecRaw && dataChunk.bitsPerSample == 8 && dataChunk.sampleCount >= 4) {
-				scanAndFixAudioPops(dataChunk, dataSize, stream);
+				MohawkEngine *mohawkEngine = static_cast<MohawkEngine *>(g_engine);
+				const char *gameId = mohawkEngine->getGameId();
+				// Myst does not have pops and Riven does not have unsigned 8-bit PCM and so is ignored.
+				if (strcmp(gameId, "myst") != 0 && strcmp(gameId, "riven") != 0 && ConfMan.getBool("fix_audio_pops")) {
+					scanAndFixAudioPops(dataChunk, dataSize, stream);
+				}
 			}
 
 				// NOTE: We currently ignore all of the loop parameters here. Myst uses the




More information about the Scummvm-git-logs mailing list