[Scummvm-git-logs] scummvm branch-3-0 -> 021792ffcf3f979ec4025142e8acf3306a1cc2c3

sluicebox noreply at scummvm.org
Wed Dec 17 08:19:34 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:
8020f66841 PRIVATE: Move subtitle destruction into function
977b7403a7 PRIVATE: Allow blocking sounds to be interrupted
021792ffcf PRIVATE: Allow multiple foreground sounds


Commit: 8020f6684196e6f820d6913611f41d9c7edb9950
    https://github.com/scummvm/scummvm/commit/8020f6684196e6f820d6913611f41d9c7edb9950
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-12-17T00:17:55-08:00

Commit Message:
PRIVATE: Move subtitle destruction into function

Changed paths:
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index caed5bb27e2..3be8f925177 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -461,11 +461,7 @@ Common::Error PrivateEngine::run() {
 			if (_videoDecoder->endOfVideo()) {
 				delete _videoDecoder;
 				_videoDecoder = nullptr;
-				if (_subtitles != nullptr) {
-					delete _subtitles;
-					_subtitles = nullptr;
-					_system->hideOverlay();
-				}
+				destroySubtitles();
 				_currentMovie = "";
 			} else if (!_videoDecoder->needsUpdate() && mouseMoved) {
 				_system->updateScreen();
@@ -507,9 +503,7 @@ Common::Error PrivateEngine::run() {
 			if (_mixer->isSoundHandleActive(_fgSoundHandle)) {
 				_subtitles->drawSubtitle(_mixer->getElapsedTime(_fgSoundHandle).msecs(), false, _sfxSubtitles);
 			} else {
-				delete _subtitles;
-				_subtitles = nullptr;
-				_system->hideOverlay();
+				destroySubtitles();
 			}
 		}
 	}
@@ -2401,11 +2395,7 @@ void PrivateEngine::loadSubtitles(const Common::Path &path) {
 	subPath = subPath.appendComponent(subPathStr);
 	debugC(1, kPrivateDebugFunction, "Loading subtitles from %s", subPath.toString().c_str());
 
-	if (_subtitles != nullptr) {
-		delete _subtitles;
-		_subtitles = nullptr;
-		_system->hideOverlay();
-	}
+	destroySubtitles();
 
 	_subtitles = new Video::Subtitles();
 	_subtitles->loadSRTFile(subPath);
@@ -2419,6 +2409,15 @@ void PrivateEngine::loadSubtitles(const Common::Path &path) {
 	_system->clearOverlay();
 	adjustSubtitleSize();
 }
+
+void PrivateEngine::destroySubtitles() {
+	if (_subtitles != nullptr) {
+		delete _subtitles;
+		_subtitles = nullptr;
+		_system->hideOverlay();
+	}
+}
+
 void PrivateEngine::playVideo(const Common::String &name) {
 	debugC(1, kPrivateDebugFunction, "%s(%s)", __FUNCTION__, name.c_str());
 	//stopSound(true);
@@ -2495,11 +2494,7 @@ void PrivateEngine::skipVideo() {
 
 	delete _videoDecoder;
 	_videoDecoder = nullptr;
-	if (_subtitles != nullptr) {
-		delete _subtitles;
-		_subtitles = nullptr;
-		_system->hideOverlay();
-	}
+	destroySubtitles();
 	_currentMovie = "";
 }
 
@@ -2510,11 +2505,7 @@ void PrivateEngine::destroyVideo() {
 	delete _videoDecoder;
 	_videoDecoder = nullptr;
 	_pausedVideo = nullptr;
-	if (_subtitles != nullptr) {
-		delete _subtitles;
-		_subtitles = nullptr;
-		_system->hideOverlay();
-	}
+	destroySubtitles();
 }
 
 void PrivateEngine::stopSound(bool all) {
diff --git a/engines/private/private.h b/engines/private/private.h
index 24539fd2633..cd8581b9898 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -299,6 +299,7 @@ public:
 	void destroyVideo();
 
 	void loadSubtitles(const Common::Path &path);
+	void destroySubtitles();
 	void adjustSubtitleSize();
 	Video::Subtitles *_subtitles;
 	bool _useSubtitles;


Commit: 977b7403a74b30978940619e1b751acb83d22afc
    https://github.com/scummvm/scummvm/commit/977b7403a74b30978940619e1b751acb83d22afc
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-12-17T00:18:18-08:00

Commit Message:
PRIVATE: Allow blocking sounds to be interrupted

Blocking sounds can now be interrupted by the engine quitting or the
user pressing Escape (kActionSkip)

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index 0139cf9375c..e07257e975c 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -182,7 +182,7 @@ static void fSyncSound(ArgArray args) {
 		g_private->drawScreen();
 
 		g_private->playSound(s, 1, true, false);
-		g_private->waitForSoundToStop();
+		g_private->waitForSoundsToStop();
 	}
 }
 
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 3be8f925177..9ff25bd54c1 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -510,13 +510,6 @@ Common::Error PrivateEngine::run() {
 	return Common::kNoError;
 }
 
-void PrivateEngine::ignoreEvents() {
-	Common::Event event;
-	_system->getEventManager()->pollEvent(event);
-	_system->updateScreen();
-	_system->delayMillis(10);
-}
-
 void PrivateEngine::initFuncs() {
 	for (const Private::FuncTable *fnc = funcTable; fnc->name; fnc++) {
 		Common::String name(fnc->name);
@@ -642,7 +635,7 @@ void PrivateEngine::completePoliceBust() {
 		Common::String s("global/transiti/audio/spoc02VO.wav");
 		playSound(s, 1, true, false);
 		changeCursor("default");
-		waitForSoundToStop();
+		waitForSoundsToStop();
 	}
 
 	// Cycle to the next movie and wrap around
@@ -2322,13 +2315,50 @@ bool PrivateEngine::isSoundActive() {
 	return _mixer->isSoundIDActive(-1);
 }
 
-void PrivateEngine::waitForSoundToStop() {
-	while (isSoundActive())
-		ignoreEvents();
+void PrivateEngine::waitForSoundsToStop() {
+	while (isSoundActive()) {
+		if (consumeEvents()) {
+			stopSound(true);
+			return;
+		}
+	}
 
 	uint32 i = 100;
-	while (i--) // one second extra
-		ignoreEvents();
+	while (i--) { // one second extra
+		if (consumeEvents()) {
+			stopSound(true);
+			return;
+		}
+	}
+}
+
+// returns true if interrupted by user or engine quitting
+bool PrivateEngine::consumeEvents() {
+	if (shouldQuit()) {
+		return true;
+	}
+
+	Common::Event event;
+	while (_system->getEventManager()->pollEvent(event)) {
+		switch (event.type) {
+		case Common::EVENT_RETURN_TO_LAUNCHER:
+		case Common::EVENT_QUIT:
+			return true;
+
+		case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+			if (event.customType == kActionSkip) {
+				return true;
+			}
+			break;
+
+		default:
+			break;;
+		}
+	}
+
+	_system->updateScreen();
+	_system->delayMillis(10);
+	return false;
 }
 
 void PrivateEngine::adjustSubtitleSize() {
diff --git a/engines/private/private.h b/engines/private/private.h
index cd8581b9898..6bdff2268b4 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -288,7 +288,6 @@ public:
 		return true;
 	}
 
-	void ignoreEvents();
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 
@@ -444,7 +443,8 @@ public:
 	void playPhoneCallSound();
 	void stopSound(bool);
 	bool isSoundActive();
-	void waitForSoundToStop();
+	void waitForSoundsToStop();
+	bool consumeEvents();
 	bool _noStopSounds;
 	Common::String _backgroundSound;
 	Common::String _pausedBackgroundSound;


Commit: 021792ffcf3f979ec4025142e8acf3306a1cc2c3
    https://github.com/scummvm/scummvm/commit/021792ffcf3f979ec4025142e8acf3306a1cc2c3
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-12-17T00:19:05-08:00

Commit Message:
PRIVATE: Allow multiple foreground sounds

- Police siren no longer interrupted by other sounds
- Wall safe alarm no longer interrupted by other sounds
- Marlowe's speech now plays when taking bag of drugs
- Radios can now be turned off when playing, as in the original
- Many misc sounds are no longer interrupted

Changed paths:
    engines/private/funcs.cpp
    engines/private/private.cpp
    engines/private/private.h
    engines/private/savegame.h


diff --git a/engines/private/funcs.cpp b/engines/private/funcs.cpp
index e07257e975c..5b0f99712b4 100644
--- a/engines/private/funcs.cpp
+++ b/engines/private/funcs.cpp
@@ -70,7 +70,7 @@ static void fChgMode(ArgArray args) {
 		if (g_private->_noStopSounds) {
 			g_private->_noStopSounds = false;
 		} else {
-			g_private->stopSound(true);
+			g_private->stopSounds();
 		}
 	}
 }
@@ -181,7 +181,8 @@ static void fSyncSound(ArgArray args) {
 	if (s != "\"\"") {
 		g_private->drawScreen();
 
-		g_private->playSound(s, 1, true, false);
+		g_private->stopSounds();
+		g_private->playForegroundSound(s);
 		g_private->waitForSoundsToStop();
 	}
 }
@@ -433,11 +434,10 @@ static void fInventory(ArgArray args) {
 		g_private->_toTake = true;
 		Common::String sound(snd.u.str);
 
-		if (sound != "\"\"") {
-			g_private->playSound(sound, 1, false, false);
-		} else {
-			g_private->playSound(g_private->getTakeLeaveSound(), 1, false, false);
+		if (sound == "\"\"") {
+			sound = g_private->getTakeLeaveSound();
 		}
+		g_private->playForegroundSound(g_private->_takeLeaveSound, sound);
 	} else {
 		Common::String flag;
 		if (v1.type == NAME) {
@@ -500,7 +500,7 @@ static void fSetModifiedFlag(ArgArray args) {
 static void fPaperShuffleSound(ArgArray args) {
 	assert(args.size() == 0);
 	debugC(1, kPrivateDebugScript, "PaperShuffleSound()");
-	g_private->playSound(g_private->getPaperShuffleSound(), 1, false, false);
+	g_private->playForegroundSound(g_private->getPaperShuffleSound());
 }
 
 static void fSoundEffect(ArgArray args) {
@@ -508,9 +508,9 @@ static void fSoundEffect(ArgArray args) {
 	debugC(1, kPrivateDebugScript, "SoundEffect(%s)", args[0].u.str);
 	Common::String s(args[0].u.str);
 	if (s != "\"\"") {
-		g_private->playSound(s, 1, false, false);
+		g_private->playForegroundSound(s);
 	} else {
-		g_private->stopSound(true);
+		g_private->stopSounds();
 	}
 }
 
@@ -523,18 +523,18 @@ static void fSound(ArgArray args) {
 		int c = args[3].u.val;
 
 		if (!b1 && !b2 && c == 1) {
-			g_private->stopSound(true);
+			g_private->stopSounds();
 		} else if (!b1 && !b2 && c == 2) {
-			g_private->stopSound(false);
+			g_private->stopForegroundSounds();
 		} else
 			assert(0);
 	}
 
 	Common::String s(args[0].u.str);
 	if (s != "\"\"") {
-		g_private->playSound(s, 1, false, false);
+		g_private->playForegroundSound(s);
 	} else {
-		g_private->stopSound(true);
+		g_private->stopSounds();
 	}
 }
 
@@ -545,9 +545,9 @@ static void fLoopedSound(ArgArray args) {
 	Common::String s(args[0].u.str);
 
 	if (s != "\"\"") {
-		g_private->playSound(s, 0, false, true);
+		g_private->playBackgroundSound(s);
 	} else {
-		g_private->stopSound(true);
+		g_private->stopSounds();
 	}
 }
 
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 9ff25bd54c1..535bbd02fc9 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -55,7 +55,7 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	: Engine(syst), _gameDescription(gd), _image(nullptr), _videoDecoder(nullptr),
 	  _compositeSurface(nullptr), _transparentColor(0), _frameImage(nullptr),
 	  _framePalette(nullptr),
-	  _subtitles(nullptr), _sfxSubtitles(false), _useSubtitles(false),
+	  _subtitles(nullptr), _subtitledSound(nullptr), _sfxSubtitles(false), _useSubtitles(false),
 	  _defaultCursor(nullptr),
 	  _screenW(640), _screenH(480) {
 	_highlightMasks = false;
@@ -99,9 +99,10 @@ PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	_AMRadioArea.clear();
 	_phoneArea.clear();
 	_AMRadio.path = "inface/radio/comm_/";
+	_AMRadio.sound = &_AMRadioSound;
 	_policeRadio.path = "inface/radio/police/";
+	_policeRadio.sound = &_policeRadioSound;
 	_phonePrefix = "inface/telephon/";
-	_phoneCallSound = "phone.wav";
 
 	// Dossiers
 	_dossierPage = 0;
@@ -455,7 +456,7 @@ Common::Error PrivateEngine::run() {
 
 		if (_videoDecoder && !_videoDecoder->isPaused()) {
 			if (_videoDecoder->getCurFrame() == 0) {
-				stopSound(true);
+				stopSounds();
 			}
 
 			if (_videoDecoder->endOfVideo()) {
@@ -500,8 +501,8 @@ Common::Error PrivateEngine::run() {
 		_system->updateScreen();
 		_system->delayMillis(10);
 		if (_subtitles != nullptr) {
-			if (_mixer->isSoundHandleActive(_fgSoundHandle)) {
-				_subtitles->drawSubtitle(_mixer->getElapsedTime(_fgSoundHandle).msecs(), false, _sfxSubtitles);
+			if (_subtitledSound != nullptr && isSoundPlaying(*_subtitledSound)) {
+				_subtitles->drawSubtitle(_mixer->getElapsedTime(_subtitledSound->handle).msecs(), false, _sfxSubtitles);
 			} else {
 				destroySubtitles();
 			}
@@ -633,7 +634,8 @@ void PrivateEngine::completePoliceBust() {
 	// Play audio on the second bust movie
 	if (kPoliceBustVideos[_policeBustMovieIndex] == 2) {
 		Common::String s("global/transiti/audio/spoc02VO.wav");
-		playSound(s, 1, true, false);
+		stopSounds();
+		playForegroundSound(s);
 		changeCursor("default");
 		waitForSoundsToStop();
 	}
@@ -659,8 +661,7 @@ void PrivateEngine::checkPoliceBust() {
 
 	if (!_policeSirenPlayed) {
 		// Play siren
-		stopSound(true);
-		playSound(_sirenSound, 1, false, false);
+		playForegroundSound(_sirenSound);
 
 		_policeSirenPlayed = true;
 		_numberOfClicks = _numberClicksAfterSiren;
@@ -901,7 +902,7 @@ void PrivateEngine::selectPauseGame(Common::Point mousePos) {
 					_pausedVideo = _videoDecoder;
 				}
 
-				_pausedBackgroundSound = _backgroundSound;
+				_pausedBackgroundSoundName = _bgSound.name;
 
 				_compositeSurface->fillRect(_screenRect, 0);
 				_compositeSurface->setPalette(_framePalette, 0, 256);
@@ -929,9 +930,9 @@ void PrivateEngine::resumeGame() {
 		_needToDrawScreenFrame = true;
 	}
 
-	if (!_pausedBackgroundSound.empty()) {
-		playSound(_pausedBackgroundSound, 0, false, true);
-		_pausedBackgroundSound.clear();
+	if (!_pausedBackgroundSoundName.empty()) {
+		playBackgroundSound(_pausedBackgroundSoundName);
+		_pausedBackgroundSoundName.clear();
 	}
 }
 
@@ -950,7 +951,7 @@ bool PrivateEngine::selectExit(Common::Point mousePos) {
 			if (cs < rs && !e.nextSetting.empty()) { // TODO: check this
 				// an item was not taken
 				if (_toTake) {
-					playSound(getLeaveSound(), 1, false, false);
+					playForegroundSound(_takeLeaveSound, getLeaveSound());
 					_toTake = false;
 				}
 
@@ -987,7 +988,7 @@ bool PrivateEngine::selectMask(Common::Point mousePos) {
 				// an item was taken
 				if (_toTake) {
 					addInventory(m.inventoryItem, *(m.flag1->name));
-					playSound(getTakeSound(), 1, false, false);
+					playForegroundSound(_takeLeaveSound, getTakeSound());
 					_toTake = false;
 					_haveTakenItem = true;
 				}
@@ -1056,7 +1057,7 @@ bool PrivateEngine::selectDiaryNextPage(Common::Point mousePos) {
 		_currentDiaryPage++;
 		_nextSetting = _diaryNextPageExit.nextSetting;
 
-		playSound(getPaperShuffleSound(), 1, false, false);
+		playForegroundSound(getPaperShuffleSound());
 
 		return true;
 	}
@@ -1073,7 +1074,7 @@ bool PrivateEngine::selectDiaryPrevPage(Common::Point mousePos) {
 		_currentDiaryPage--;
 		_nextSetting = _diaryPrevPageExit.nextSetting;
 
-		playSound(getPaperShuffleSound(), 1, false, false);
+		playForegroundSound(getPaperShuffleSound());
 
 		return true;
 	}
@@ -1338,7 +1339,7 @@ bool PrivateEngine::selectDossierNextSuspect(Common::Point mousePos) {
 
 	if (inMask(_dossierNextSuspectMask.surf, mousePos)) {
 		if ((_dossierSuspect + 1) < _dossiers.size()) {
-			playSound(getPaperShuffleSound(), 1, false, false);
+			playForegroundSound(getPaperShuffleSound());
 			_dossierSuspect++;
 			_dossierPage = 0;
 			
@@ -1356,7 +1357,7 @@ bool PrivateEngine::selectDossierPrevSheet(Common::Point mousePos) {
 
 	if (inMask(_dossierPrevSheetMask.surf, mousePos)) {
 		if (_dossierPage == 1) {
-			playSound(getPaperShuffleSound(), 1, false, false);
+			playForegroundSound(getPaperShuffleSound());
 			_dossierPage = 0;
 			
 			// reload kDossierOpen
@@ -1374,7 +1375,7 @@ bool PrivateEngine::selectDossierNextSheet(Common::Point mousePos) {
 	if (inMask(_dossierNextSheetMask.surf, mousePos)) {
 		DossierInfo m = _dossiers[_dossierSuspect];
 		if (_dossierPage == 0 && !m.page2.empty()) {
-			playSound(getPaperShuffleSound(), 1, false, false);
+			playForegroundSound(getPaperShuffleSound());
 			_dossierPage = 1;
 			
 			// reload kDossierOpen
@@ -1391,7 +1392,7 @@ bool PrivateEngine::selectDossierPrevSuspect(Common::Point mousePos) {
 
 	if (inMask(_dossierPrevSuspectMask.surf, mousePos)) {
 		if (_dossierSuspect > 0) {
-			playSound(getPaperShuffleSound(), 1, false, false);
+			playForegroundSound(getPaperShuffleSound());
 			_dossierSuspect--;
 			_dossierPage = 0;
 			
@@ -1581,6 +1582,12 @@ void PrivateEngine::disableRadioClips(Radio &radio, int priority) {
 }
 
 void PrivateEngine::playRadio(Radio &radio, bool randomlyDisableClips) {
+	// if radio is already playing then turn it off
+	if (isSoundPlaying(*(radio.sound))) {
+		stopForegroundSounds();
+		return;
+	}
+
 	// search channels for first available clip
 	for (uint i = 0; i < ARRAYSIZE(radio.channels); i++) {
 		// skip empty channels
@@ -1606,7 +1613,8 @@ void PrivateEngine::playRadio(Radio &radio, bool randomlyDisableClips) {
 
 		// play the clip
 		Common::String sound = radio.path + clip.name + ".wav";
-		playSound(sound, 1, false, false);
+		stopForegroundSounds();
+		playForegroundSound(*(radio.sound), sound);
 		clip.played = true;
 		if (!clip.flagName.empty()) {
 			Symbol *flag = maps.lookupVariable(&(clip.flagName));
@@ -1616,7 +1624,8 @@ void PrivateEngine::playRadio(Radio &radio, bool randomlyDisableClips) {
 	}
 
 	// play default radio sound
-	playSound("inface/radio/radio.wav", 1, false, false);
+	stopForegroundSounds();
+	playForegroundSound(*(radio.sound), "inface/radio/radio.wav");
 }
 
 void PrivateEngine::addPhone(const Common::String &name, bool once, int startIndex, int endIndex, const Common::String &flagName, int flagValue) {
@@ -1691,7 +1700,7 @@ void PrivateEngine::checkPhoneCall() {
 		return;
 	}
 
-	if (isSoundActive()) {
+	if (isSoundPlaying()) {
 		return;
 	}
 
@@ -1718,7 +1727,7 @@ void PrivateEngine::checkPhoneCall() {
 
 	phone->status = kPhoneStatusCalling;
 	phone->callCount++;
-	playPhoneCallSound();
+	playForegroundSound(_phoneCallSound, _phonePrefix + "phone.wav");
 }
 
 bool PrivateEngine::cursorPhoneArea(Common::Point mousePos) {
@@ -1726,7 +1735,7 @@ bool PrivateEngine::cursorPhoneArea(Common::Point mousePos) {
 		return false;
 	}
 
-	if (!_mixer->isSoundHandleActive(_phoneCallSoundHandle)) {
+	if (!isSoundPlaying(_phoneCallSound)) {
 		return false;
 	}
 
@@ -1743,7 +1752,7 @@ bool PrivateEngine::selectPhoneArea(Common::Point mousePos) {
 		return false;
 	}
 
-	if (!_mixer->isSoundHandleActive(_phoneCallSoundHandle)) {
+	if (!isSoundPlaying(_phoneCallSound)) {
 		return false;
 	}
 
@@ -1773,8 +1782,10 @@ bool PrivateEngine::selectPhoneArea(Common::Point mousePos) {
 			setSymbol(flag, phone->flagValue);
 		}
 
-		playSound(sound, 1, true, false);
+		stopForegroundSounds(); // stop phone ringing
+		playForegroundSound(sound);
 		_nextSetting = getListenToPhoneSetting();
+		changeCursor("default");
 		return true;
 	}
 	return false;
@@ -1909,8 +1920,7 @@ void PrivateEngine::restartGame() {
 	_AMRadio.clear();
 	_policeRadio.clear();
 	_phones.clear();
-	_backgroundSound.clear();
-	_pausedBackgroundSound.clear();
+	_pausedBackgroundSoundName.clear();
 
 	// Movies
 	_repeatedMovieExit = "";
@@ -1932,7 +1942,7 @@ void PrivateEngine::restartGame() {
 
 Common::Error PrivateEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	// We don't want to continue with any sound or videos from a previous game
-	stopSound(true);
+	stopSounds();
 	destroyVideo();
 
 	debugC(1, kPrivateDebugFunction, "loadGameStream");
@@ -2093,9 +2103,9 @@ Common::Error PrivateEngine::loadGameStream(Common::SeekableReadStream *stream)
 
 	// Sounds
 	if (meta.version >= 4) {
-		_pausedBackgroundSound = stream->readString();
+		_pausedBackgroundSoundName = stream->readString();
 	} else {
-		_pausedBackgroundSound.clear();
+		_pausedBackgroundSoundName.clear();
 	}
 
 	return Common::kNoError;
@@ -2245,7 +2255,7 @@ Common::Error PrivateEngine::saveGameStream(Common::WriteStream *stream, bool is
 		stream->writeUint32LE(0);
 
 	// Sounds
-	stream->writeString(_pausedBackgroundSound);
+	stream->writeString(_pausedBackgroundSoundName);
 	stream->writeByte(0);
 
 	return Common::kNoError;
@@ -2269,56 +2279,84 @@ Common::Path PrivateEngine::convertPath(const Common::String &name) {
 	return Common::Path(path);
 }
 
-void PrivateEngine::playSound(const Common::String &name, uint loops, bool stopOthers, bool background) {
-	debugC(1, kPrivateDebugFunction, "%s(%s,%d,%d,%d)", __FUNCTION__, name.c_str(), loops, stopOthers, background);
-
-	Common::Path path = convertPath(name);
-	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(path);
-
-	if (!file)
-		error("unable to find sound file %s", path.toString().c_str());
+void PrivateEngine::playBackgroundSound(const Common::String &name) {
+	playSound(_bgSound, name, true);
+}
 
-	Audio::LoopingAudioStream *stream;
-	stream = new Audio::LoopingAudioStream(Audio::makeWAVStream(file, DisposeAfterUse::YES), loops);
-	if (stopOthers) {
-		stopSound(true);
+void PrivateEngine::playForegroundSound(const Common::String &name) {
+	// stop sound if already playing. for example, the wall safe alarm.
+	for (uint i = 0; i < ARRAYSIZE(_fgSounds); i++) {
+		if (_fgSounds[i].name == name) {
+			if (isSoundPlaying(_fgSounds[i])) {
+				stopSound(_fgSounds[i]);
+				break;
+			}
+		}
 	}
 
-	Audio::SoundHandle *sh = nullptr;
-	if (background) {
-		_mixer->stopHandle(_bgSoundHandle);
-		sh = &_bgSoundHandle;
-		_backgroundSound = name;
-	} else {
-		_mixer->stopHandle(_fgSoundHandle);
-		_mixer->stopHandle(_phoneCallSoundHandle);
-		sh = &_fgSoundHandle;
+	// play using the first available sound
+	for (uint i = 0; i < ARRAYSIZE(_fgSounds); i++) {
+		if (!isSoundPlaying(_fgSounds[i])) {
+			playSound(_fgSounds[i], name, false);
+			break;
+		}
 	}
+}
 
-	_mixer->playStream(Audio::Mixer::kSFXSoundType, sh, stream, -1, Audio::Mixer::kMaxChannelVolume);
-	loadSubtitles(path);
+void PrivateEngine::playForegroundSound(Sound &sound, const Common::String &name) {
+	playSound(sound, name, false);
 }
 
-void PrivateEngine::playPhoneCallSound() {
-	debugC(1, kPrivateDebugFunction, "%s()", __FUNCTION__);
+void PrivateEngine::playSound(Sound &sound, const Common::String &name, bool loop) {
+	sound.name = name;
 
-	Common::Path path = convertPath(_phonePrefix + _phoneCallSound);
+	Common::Path path = convertPath(name);
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(path);
-	if (!file) {
+
+	if (file == nullptr) {
 		error("unable to find sound file %s", path.toString().c_str());
 	}
-	Audio::SeekableAudioStream *audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
-	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_phoneCallSoundHandle, audioStream, -1, Audio::Mixer::kMaxChannelVolume);
+
+	Audio::LoopingAudioStream *stream = new Audio::LoopingAudioStream(Audio::makeWAVStream(file, DisposeAfterUse::YES), loop ? 0 : 1);
+	
+	_mixer->stopHandle(sound.handle);
+	_mixer->playStream(Audio::Mixer::kSFXSoundType, &sound.handle, stream, -1, Audio::Mixer::kMaxChannelVolume);
+
+	loadSubtitles(path, &sound);
+}
+
+void PrivateEngine::stopForegroundSounds() {
+	for (uint i = 0; i < ARRAYSIZE(_fgSounds); i++) {
+		stopSound(_fgSounds[i]);
+	}
+	stopSound(_phoneCallSound);
+	stopSound(_AMRadioSound);
+	stopSound(_policeRadioSound);
+	stopSound(_takeLeaveSound);
+}
+
+void PrivateEngine::stopSounds() {
+	stopSound(_bgSound);
+	stopForegroundSounds();
+}
+
+void PrivateEngine::stopSound(Sound &sound) {
+	_mixer->stopHandle(sound.handle);
+	sound.name.clear();
 }
 
-bool PrivateEngine::isSoundActive() {
+bool PrivateEngine::isSoundPlaying() {
 	return _mixer->isSoundIDActive(-1);
 }
 
+bool PrivateEngine::isSoundPlaying(Sound &sound) {
+	return _mixer->isSoundHandleActive(sound.handle);
+}
+
 void PrivateEngine::waitForSoundsToStop() {
-	while (isSoundActive()) {
+	while (isSoundPlaying()) {
 		if (consumeEvents()) {
-			stopSound(true);
+			stopSounds();
 			return;
 		}
 	}
@@ -2326,7 +2364,7 @@ void PrivateEngine::waitForSoundsToStop() {
 	uint32 i = 100;
 	while (i--) { // one second extra
 		if (consumeEvents()) {
-			stopSound(true);
+			stopSounds();
 			return;
 		}
 	}
@@ -2408,7 +2446,7 @@ void PrivateEngine::adjustSubtitleSize() {
 	}
 }
 
-void PrivateEngine::loadSubtitles(const Common::Path &path) {
+void PrivateEngine::loadSubtitles(const Common::Path &path, Sound *sound) {
 	debugC(1, kPrivateDebugFunction, "%s(%s)", __FUNCTION__, path.toString().c_str());
 	if (!_useSubtitles)
 		return;
@@ -2435,6 +2473,8 @@ void PrivateEngine::loadSubtitles(const Common::Path &path) {
 		return;
 	}
 
+	_subtitledSound = sound;
+
 	_system->showOverlay(false);
 	_system->clearOverlay();
 	adjustSubtitleSize();
@@ -2445,12 +2485,13 @@ void PrivateEngine::destroySubtitles() {
 		delete _subtitles;
 		_subtitles = nullptr;
 		_system->hideOverlay();
+		_subtitledSound = nullptr;
 	}
 }
 
 void PrivateEngine::playVideo(const Common::String &name) {
 	debugC(1, kPrivateDebugFunction, "%s(%s)", __FUNCTION__, name.c_str());
-	//stopSound(true);
+
 	Common::Path path = convertPath(name);
 	Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(path);
 
@@ -2538,18 +2579,6 @@ void PrivateEngine::destroyVideo() {
 	destroySubtitles();
 }
 
-void PrivateEngine::stopSound(bool all) {
-	debugC(1, kPrivateDebugFunction, "%s(%d)", __FUNCTION__, all);
-
-	_mixer->stopHandle(_fgSoundHandle);
-	_mixer->stopHandle(_phoneCallSoundHandle);
-
-	if (all) {
-		_mixer->stopHandle(_bgSoundHandle);
-		_backgroundSound.clear();
-	}
-}
-
 Graphics::Surface *PrivateEngine::decodeImage(const Common::String &name, byte **palette, bool *isNewPalette) {
 	debugC(1, kPrivateDebugFunction, "%s(%s)", __FUNCTION__, name.c_str());
 	Common::Path path = convertPath(name);
diff --git a/engines/private/private.h b/engines/private/private.h
index 6bdff2268b4..adc95c61988 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -126,6 +126,11 @@ enum PhoneStatus : byte {
 	kPhoneStatusAnswered
 };
 
+typedef struct Sound {
+	Common::String name;
+	Audio::SoundHandle handle;
+} Sound;
+
 typedef struct PhoneInfo {
 	Common::String name;
 	bool once;
@@ -153,10 +158,11 @@ typedef struct RadioClip {
 
 typedef struct Radio {
 	Common::String path;
+	Sound *sound;
 	Common::Array<RadioClip> clips;
 	int channels[3];
 
-	Radio() {
+	Radio() : sound(nullptr) {
 		clear();
 	}
 
@@ -244,9 +250,6 @@ public:
 
 	SymbolMaps maps;
 
-	Audio::SoundHandle _fgSoundHandle;
-	Audio::SoundHandle _bgSoundHandle;
-	Audio::SoundHandle _phoneCallSoundHandle;
 	Video::SmackerDecoder *_videoDecoder;
 	Video::SmackerDecoder *_pausedVideo;
 
@@ -297,10 +300,11 @@ public:
 	void skipVideo();
 	void destroyVideo();
 
-	void loadSubtitles(const Common::Path &path);
+	void loadSubtitles(const Common::Path &path, Sound *sound = nullptr);
 	void destroySubtitles();
 	void adjustSubtitleSize();
 	Video::Subtitles *_subtitles;
+	Sound *_subtitledSound;
 	bool _useSubtitles;
 	bool _sfxSubtitles;
 
@@ -439,15 +443,25 @@ public:
 	MaskList _masks;
 
 	// Sounds
-	void playSound(const Common::String &, uint, bool, bool);
-	void playPhoneCallSound();
-	void stopSound(bool);
-	bool isSoundActive();
+	void playBackgroundSound(const Common::String &name);
+	void playForegroundSound(const Common::String &name);
+	void playForegroundSound(Sound &sound, const Common::String &name);
+	void playSound(Sound &sound, const Common::String &name, bool loop);
+	void stopForegroundSounds();
+	void stopSounds();
+	void stopSound(Sound &sound);
+	bool isSoundPlaying();
+	bool isSoundPlaying(Sound &sound);
 	void waitForSoundsToStop();
 	bool consumeEvents();
+	Sound _bgSound;
+	Sound _fgSounds[4];
+	Sound _phoneCallSound;
+	Sound _AMRadioSound;
+	Sound _policeRadioSound;
+	Sound _takeLeaveSound;
 	bool _noStopSounds;
-	Common::String _backgroundSound;
-	Common::String _pausedBackgroundSound;
+	Common::String _pausedBackgroundSoundName;
 
 	Common::String getPaperShuffleSound();
 	Common::String _globalAudioPath;
@@ -477,7 +491,6 @@ public:
 	// Phone
 	MaskInfo _phoneArea;
 	Common::String _phonePrefix;
-	Common::String _phoneCallSound;
 	PhoneList _phones;
 	void addPhone(const Common::String &name, bool once, int startIndex, int endIndex, const Common::String &flagName, int flagValue);
 	void initializePhoneOnDesktop();
diff --git a/engines/private/savegame.h b/engines/private/savegame.h
index a6f59d437d3..eeccd27b82c 100644
--- a/engines/private/savegame.h
+++ b/engines/private/savegame.h
@@ -36,7 +36,7 @@ namespace Private {
 //
 // Version - new/changed feature
 // =============================
-//       4 - _pausedBackgroundSound (December 2025)
+//       4 - _pausedBackgroundSoundName (December 2025)
 //       3 - Radio detailed state (December 2025)
 //       2 - Phone clip detailed state (December 2025)
 //       1 - Metadata header and more game state (November 2025)




More information about the Scummvm-git-logs mailing list