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

elasota noreply at scummvm.org
Sat Mar 11 04:38:13 UTC 2023


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

Summary:
427302b48e VCRUISE: Fix wraparound gyros
d439140d58 VCRUISE: Add a bunch of sound support
a340dd803f VCRUISE: Fix playlists being based incorrectly when playing a gyro animation


Commit: 427302b48edcb9cb78bef67548bba8373c590550
    https://github.com/scummvm/scummvm/commit/427302b48edcb9cb78bef67548bba8373c590550
Author: elasota (ejlasota at gmail.com)
Date: 2023-03-10T20:51:38-05:00

Commit Message:
VCRUISE: Fix wraparound gyros

Changed paths:
    engines/vcruise/runtime.cpp
    engines/vcruise/runtime.h


diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 45baac7bcda..e6346c07bf1 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -137,6 +137,7 @@ void Runtime::GyroState::reset() {
 
 	dragBasePoint = Common::Point(0, 0);
 	dragBaseState = 0;
+	dragCurrentState = 0;
 	isWaitingForAnimation = false;
 }
 
@@ -633,15 +634,20 @@ bool Runtime::runGyroIdle() {
 	int32 deltaState = deltaCoordinate / static_cast<int32>(_gyros.dragMargin);
 	int32 targetStateInitial = static_cast<int32>(_gyros.dragBaseState) + deltaState;
 
+	Gyro &gyro = _gyros.gyros[_gyros.activeGyro];
+
 	int32 targetState = 0;
-	if (targetStateInitial > 0) {
+	if (gyro.wrapAround) {
 		targetState = targetStateInitial;
-		if (static_cast<uint>(targetState) > _gyros.maxValue)
-			targetState = _gyros.maxValue;
+	} else {
+		if (targetStateInitial > 0) {
+			targetState = targetStateInitial;
+			if (static_cast<uint>(targetState) > _gyros.maxValue)
+				targetState = _gyros.maxValue;
+		}
 	}
 
-	Gyro &gyro = _gyros.gyros[_gyros.activeGyro];
-	if (targetState < gyro.currentState) {
+	if (targetState < _gyros.dragCurrentState) {
 		AnimationDef animDef = _gyros.negAnim;
 
 		animDef.firstFrame += ((_gyros.maxValue - gyro.currentState) * _gyros.frameSeparation);
@@ -651,10 +657,15 @@ bool Runtime::runGyroIdle() {
 
 		gyro.logState();
 		gyro.currentState--;
+		_gyros.dragCurrentState--;
+
+		if (gyro.currentState < 0)
+			gyro.currentState = _gyros.maxValue;
+
 		_gameState = kGameStateGyroAnimation;
 		_havePendingCompletionCheck = true;
 		return true;
-	} else if (targetState > gyro.currentState) {
+	} else if (targetState > _gyros.dragCurrentState) {
 		AnimationDef animDef = _gyros.posAnim;
 
 		animDef.firstFrame += gyro.currentState * _gyros.frameSeparation;
@@ -664,6 +675,11 @@ bool Runtime::runGyroIdle() {
 
 		gyro.logState();
 		gyro.currentState++;
+		_gyros.dragCurrentState++;
+
+		if (static_cast<uint>(gyro.currentState) > _gyros.maxValue)
+			gyro.currentState = 0;
+
 		_gameState = kGameStateGyroAnimation;
 		_havePendingCompletionCheck = true;
 		return true;
@@ -2089,6 +2105,7 @@ void Runtime::scriptOpAnimG(ScriptArg_t arg) {
 
 	_gyros.dragBasePoint = _mousePos;
 	_gyros.dragBaseState = _gyros.gyros[_gyros.activeGyro].currentState;
+	_gyros.dragCurrentState = _gyros.dragBaseState;
 
 	_gameState = kGameStateGyroIdle;
 }
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 36e11dbb67a..a89307aa873 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -267,6 +267,7 @@ private:
 
 		Common::Point dragBasePoint;
 		uint dragBaseState;
+		int32 dragCurrentState;
 		bool isWaitingForAnimation;
 	};
 


Commit: d439140d58bd79fefb7a01514d334efdb727a0b2
    https://github.com/scummvm/scummvm/commit/d439140d58bd79fefb7a01514d334efdb727a0b2
Author: elasota (ejlasota at gmail.com)
Date: 2023-03-10T23:25:05-05:00

Commit Message:
VCRUISE: Add a bunch of sound support

Changed paths:
    engines/vcruise/audio_player.cpp
    engines/vcruise/audio_player.h
    engines/vcruise/runtime.cpp
    engines/vcruise/runtime.h


diff --git a/engines/vcruise/audio_player.cpp b/engines/vcruise/audio_player.cpp
index 876a1dc019e..92ecd64aa9c 100644
--- a/engines/vcruise/audio_player.cpp
+++ b/engines/vcruise/audio_player.cpp
@@ -67,6 +67,14 @@ void AudioPlayer::play(byte volume, int8 balance) {
 
 }
 
+void AudioPlayer::setVolume(byte volume) {
+	_mixer->setChannelVolume(_handle, volume);
+}
+
+void AudioPlayer::setBalance(int8 balance) {
+	_mixer->setChannelBalance(_handle, balance);
+}
+
 void AudioPlayer::stop() {
 	if (_isPlaying) {
 		_mixer->stopHandle(_handle);
diff --git a/engines/vcruise/audio_player.h b/engines/vcruise/audio_player.h
index 4447bcd1eea..59ae343172d 100644
--- a/engines/vcruise/audio_player.h
+++ b/engines/vcruise/audio_player.h
@@ -45,6 +45,9 @@ public:
 	void play(byte volume, int8 balance);
 	void stop();
 
+	void setVolume(byte volume);
+	void setBalance(int8 balance);
+
 private:
 	Common::Mutex _mutex;
 
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index e6346c07bf1..b57a69074fd 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -282,6 +282,19 @@ void SfxData::load(Common::SeekableReadStream &stream, Audio::Mixer *mixer) {
 	}
 }
 
+CachedSound::CachedSound() : rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false), volume(0), balance(0) {
+}
+
+CachedSound::~CachedSound() {
+	// Dispose player first so playback stops
+	this->player.reset();
+
+	// Dispose loopingStream before stream because stream is not refcounted by loopingStream so we need to avoid late free
+	this->loopingStream.reset();
+
+	this->stream.reset();
+}
+
 Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID)
 	: _system(system), _mixer(mixer), _roomNumber(1), _screenNumber(0), _direction(0), _havePanAnimations(0), _loadedRoomNumber(0), _activeScreenNumber(0),
 	  _gameState(kGameStateBoot), _gameID(gameID), _havePendingScreenChange(false), _havePendingReturnToIdleState(false), _havePendingCompletionCheck(false),
@@ -426,6 +439,10 @@ bool Runtime::runFrame() {
 		// Do nothing
 	}
 
+	uint32 timestamp = g_system->getMillis();
+
+	updateSounds(timestamp);
+
 	return true;
 }
 
@@ -435,6 +452,8 @@ bool Runtime::bootGame(bool newGame) {
 	debug(1, "Booting V-Cruise game...");
 	loadIndex();
 	debug(1, "Index loaded OK");
+	findWaves();
+	debug(1, "Waves indexed OK");
 
 	_gameState = kGameStateIdle;
 
@@ -1172,6 +1191,43 @@ void Runtime::loadIndex() {
 	}
 }
 
+void Runtime::findWaves() {
+	Common::ArchiveMemberList waves;
+	SearchMan.listMatchingMembers(waves, "Sfx/Waves-##/####*.wav", true);
+
+	for (const Common::ArchiveMemberPtr &wave : waves) {
+		Common::String name = wave->getName();
+
+		// Strip .wav extension
+		name = name.substr(0, name.size() - 4);
+
+		// Make case-insensitive
+		name.toLowercase();
+
+		_waves[name] = wave;
+	}
+}
+
+void Runtime::loadWave(uint soundID, const Common::String &soundName, const Common::ArchiveMemberPtr &archiveMemberPtr) {
+	Common::SeekableReadStream *stream = archiveMemberPtr->createReadStream();
+	if (!stream) {
+		warning("Couldn't open read stream for sound '%s'", soundName.c_str());
+		return;
+	}
+
+	Audio::SeekableAudioStream *audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+	if (!audioStream) {
+		warning("Couldn't open audio stream for sound '%s'", soundName.c_str());
+		return;
+	}
+
+	Common::SharedPtr<CachedSound> cachedSound(new CachedSound());
+	_cachedSounds[soundID] = cachedSound;
+
+	cachedSound->stream.reset(audioStream);
+	cachedSound->name = soundName;
+}
+
 void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
 	bool changedRoom = (roomNumber != _loadedRoomNumber);
 	bool changedScreen = (screenNumber != _activeScreenNumber) || changedRoom;
@@ -1509,6 +1565,97 @@ void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bo
 	debug(1, "Animation last frame set to %u", animDef.lastFrame);
 }
 
+void Runtime::triggerSound(bool looping, uint soundID, uint volume, int32 balance) {
+	Common::HashMap<uint, Common::SharedPtr<CachedSound> >::iterator it = _cachedSounds.find(soundID);
+	if (it == _cachedSounds.end()) {
+		warning("Couldn't trigger sound ID %u, the sound wasn't loaded", soundID);
+		return;
+	}
+
+	CachedSound &snd = *it->_value;
+
+	snd.volume = volume;
+	snd.balance = balance;
+
+	// Reset if looping state changes
+	if (snd.loopingStream && !looping) {
+		snd.player.reset();
+		snd.loopingStream.reset();
+		snd.stream->rewind();
+	}
+
+	if (!snd.loopingStream && looping)
+		snd.player.reset();
+
+	// Construct looping stream if needed and none exists
+	if (looping && !snd.loopingStream)
+		snd.loopingStream.reset(new Audio::LoopingAudioStream(snd.stream.get(), 0, DisposeAfterUse::NO, true));
+
+	if (snd.player) {
+		// If there is already a player and this is non-looping, start over
+		if (!looping) {
+			snd.player->stop();
+			snd.stream->rewind();
+			snd.player->play(volume, balance);
+		} else {
+			// Adjust volume and balance at least
+			snd.player->setVolume(volume);
+			snd.player->setBalance(balance);
+		}
+	} else {
+		snd.player.reset(new AudioPlayer(_mixer, looping ? snd.loopingStream.staticCast<Audio::AudioStream>() : snd.stream.staticCast<Audio::AudioStream>()));
+		snd.player->play(volume, balance);
+	}
+}
+
+void Runtime::triggerSoundRamp(uint soundID, uint durationMSec, uint newVolume, bool terminateOnCompletion) {
+	Common::HashMap<uint, Common::SharedPtr<CachedSound> >::const_iterator it = _cachedSounds.find(soundID);
+	if (it == _cachedSounds.end())
+		return;
+
+	CachedSound &snd = *it->_value;
+
+	snd.rampStartVolume = snd.volume;
+	snd.rampEndVolume = newVolume;
+	snd.rampTerminateOnCompletion = terminateOnCompletion;
+	snd.rampStartTime = g_system->getMillis();
+	snd.rampRatePerMSec = 65536;
+
+	if (durationMSec)
+		snd.rampRatePerMSec = 65536 / durationMSec;
+}
+
+void Runtime::updateSounds(uint32 timestamp) {
+	Common::Array<uint> condemnedSounds;
+
+	for (const Common::HashMap<uint, Common::SharedPtr<CachedSound> >::Node &node : _cachedSounds) {
+		CachedSound &snd = *node._value;
+
+		if (snd.rampRatePerMSec) {
+			uint ramp = snd.rampRatePerMSec * (timestamp - snd.rampStartTime);
+			uint newVolume = snd.volume;
+			if (ramp >= 65536) {
+				snd.rampRatePerMSec = 0;
+				newVolume = snd.rampEndVolume;
+				if (snd.rampTerminateOnCompletion)
+					condemnedSounds.push_back(node._key);
+			} else {
+				uint rampedVolume = (snd.rampStartVolume * (65536u - ramp)) + (snd.rampEndVolume * ramp);
+				newVolume = rampedVolume >> 16;
+			}
+
+			if (snd.volume != newVolume) {
+				snd.volume = newVolume;
+				if (snd.player)
+					snd.player->setVolume(snd.volume);
+			}
+		}
+	}
+
+	for (uint id : condemnedSounds)
+		_cachedSounds.erase(id);
+}
+
 AnimationDef Runtime::stackArgsToAnimDef(const StackValue_t *args) const {
 	AnimationDef def;
 	def.animNum = args[0];
@@ -2217,43 +2364,37 @@ void Runtime::scriptOpLMB1(ScriptArg_t arg) {
 void Runtime::scriptOpSoundS1(ScriptArg_t arg) {
 	TAKE_STACK(1);
 
-	warning("Sound play 1 not implemented yet");
-	(void)stackArgs;
+	triggerSound(false, stackArgs[0], 100, 0);
 }
 
 void Runtime::scriptOpSoundS2(ScriptArg_t arg) {
 	TAKE_STACK(2);
 
-	warning("Sound play 2 not implemented yet");
-	(void)stackArgs;
+	triggerSound(false, stackArgs[0], stackArgs[1], 0);
 }
 
 void Runtime::scriptOpSoundS3(ScriptArg_t arg) {
 	TAKE_STACK(3);
 
-	warning("Sound play 3 not implemented yet");
-	(void)stackArgs;
+	triggerSound(false, stackArgs[0], stackArgs[1], stackArgs[2]);
 }
 
 void Runtime::scriptOpSoundL1(ScriptArg_t arg) {
 	TAKE_STACK(1);
 
-	warning("Sound loop 1 not implemented yet");
-	(void)stackArgs;
+	triggerSound(true, stackArgs[0], 100, 0);
 }
 
 void Runtime::scriptOpSoundL2(ScriptArg_t arg) {
 	TAKE_STACK(2);
 
-	warning("Sound loop 2 not implemented yet");
-	(void)stackArgs;
+	triggerSound(true, stackArgs[0], stackArgs[1], 0);
 }
 
 void Runtime::scriptOpSoundL3(ScriptArg_t arg) {
 	TAKE_STACK(3);
 
-	warning("Sound loop 3 not implemented yet");
-	(void)stackArgs;
+	triggerSound(true, stackArgs[0], stackArgs[1], stackArgs[2]);
 }
 
 void Runtime::scriptOp3DSoundL2(ScriptArg_t arg) {
@@ -2385,24 +2526,13 @@ OPCODE_STUB(SAnimX)
 void Runtime::scriptOpVolumeUp3(ScriptArg_t arg) {
 	TAKE_STACK(3);
 
-	// stackArgs[0] = sound ID
-	// stackArgs[1] = duration (in 10ths of second)
-	// stackArgs[2] = new volume
-
-	warning("FX volume ramp up is not implemented");
-	(void)stackArgs;
+	triggerSoundRamp(stackArgs[0], stackArgs[1] * 100, stackArgs[2], false);
 }
 
 void Runtime::scriptOpVolumeDn4(ScriptArg_t arg) {
 	TAKE_STACK(4);
 
-	// stackArgs[0] = sound ID
-	// stackArgs[1] = duration (in 10ths of second)
-	// stackArgs[2] = new volume
-	// stackArgs[3] = stop sound ramp-down completes
-
-	warning("FX volume ramp down is not implemented");
-	(void)stackArgs;
+	triggerSoundRamp(stackArgs[0], stackArgs[1] * 100, stackArgs[2], stackArgs[3] != 0);
 }
 
 void Runtime::scriptOpRandom(ScriptArg_t arg) {
@@ -2677,13 +2807,28 @@ void Runtime::scriptOpVarName(ScriptArg_t arg) {
 }
 
 void Runtime::scriptOpSoundName(ScriptArg_t arg) {
-	const Common::String sndName = _scriptSet->strings[arg];
+	Common::String sndName = _scriptSet->strings[arg];
 
 	warning("Sound IDs are not implemented yet");
 
-	int32 soundID = 0;
+	uint soundID = 0;
 	for (uint i = 0; i < 4; i++)
-		soundID = soundID * 10 + (sndName[i] - '0');
+		soundID = soundID * 10u + (sndName[i] - '0');
+
+	sndName.toLowercase();
+
+	Common::HashMap<uint, Common::SharedPtr<CachedSound> >::const_iterator cachedSoundIt = _cachedSounds.find(soundID);
+	if (cachedSoundIt != _cachedSounds.end() && cachedSoundIt->_value->name != sndName) {
+		_cachedSounds.erase(cachedSoundIt);
+		cachedSoundIt = _cachedSounds.end();
+	}
+
+	if (cachedSoundIt == _cachedSounds.end()) {
+		Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator waveIt = _waves.find(sndName);
+
+		if (waveIt != _waves.end())
+			loadWave(soundID, sndName, waveIt->_value);
+	}
 
 	_scriptStack.push_back(soundID);
 }
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index a89307aa873..4e217a0fd29 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -40,6 +40,7 @@ class WriteStream;
 
 namespace Audio {
 
+class AudioStream;
 class SeekableAudioStream;
 
 } // End of namespace Audio
@@ -169,6 +170,25 @@ struct SfxData {
 	SoundMap_t sounds;
 };
 
+struct CachedSound {
+	CachedSound();
+	~CachedSound();
+
+	Common::String name;
+	Common::SharedPtr<Audio::SeekableAudioStream> stream;
+	Common::SharedPtr<Audio::AudioStream> loopingStream;
+	Common::SharedPtr<AudioPlayer> player;
+
+	uint rampStartVolume;
+	uint rampEndVolume;
+	uint32 rampRatePerMSec;
+	uint32 rampStartTime;
+	bool rampTerminateOnCompletion;
+
+	uint volume;
+	int32 balance;
+};
+
 class Runtime {
 public:
 	Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID);
@@ -337,6 +357,8 @@ private:
 	void queueOSEvent(const OSEvent &evt);
 
 	void loadIndex();
+	void findWaves();
+	void loadWave(uint soundID, const Common::String &soundName, const Common::ArchiveMemberPtr &archiveMemberPtr);
 	void changeToScreen(uint roomNumber, uint screenNumber);
 	void returnToIdleState();
 	void changeToCursor(const Common::SharedPtr<Graphics::WinCursorGroup> &cursor);
@@ -349,6 +371,10 @@ private:
 	void changeAnimation(const AnimationDef &animDef, bool consumeFPSOverride);
 	void changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride);
 
+	void triggerSound(bool looping, uint soundID, uint volume, int32 balance);
+	void triggerSoundRamp(uint soundID, uint durationMSec, uint newVolume, bool terminateOnCompletion);
+	void updateSounds(uint32 timestamp);
+
 	AnimationDef stackArgsToAnimDef(const StackValue_t *args) const;
 	void pushAnimDef(const AnimationDef &animDef);
 
@@ -584,6 +610,9 @@ private:
 
 	Common::Array<OSEvent> _pendingEvents;
 
+	Common::HashMap<Common::String, Common::ArchiveMemberPtr> _waves;
+	Common::HashMap<uint, Common::SharedPtr<CachedSound> > _cachedSounds;
+
 	static const uint kAnimDefStackArgs = 8;
 
 	static const uint kCursorArrow = 0;


Commit: a340dd803f42b2a4153c6c04eef304622999ca9c
    https://github.com/scummvm/scummvm/commit/a340dd803f42b2a4153c6c04eef304622999ca9c
Author: elasota (ejlasota at gmail.com)
Date: 2023-03-10T23:37:39-05:00

Commit Message:
VCRUISE: Fix playlists being based incorrectly when playing a gyro animation

Changed paths:
    engines/vcruise/runtime.cpp


diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index b57a69074fd..a7fe14e6a0e 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -669,10 +669,13 @@ bool Runtime::runGyroIdle() {
 	if (targetState < _gyros.dragCurrentState) {
 		AnimationDef animDef = _gyros.negAnim;
 
-		animDef.firstFrame += ((_gyros.maxValue - gyro.currentState) * _gyros.frameSeparation);
-		animDef.lastFrame = animDef.firstFrame + _gyros.frameSeparation;
+		uint initialFrame = animDef.firstFrame + ((_gyros.maxValue - gyro.currentState) * _gyros.frameSeparation);
 
-		changeAnimation(animDef, false);
+		// This is intentional instead of setting the stop frame, V-Cruise can overrun the end of the animation.
+		// firstFrame is left alone so playlists are based correctly.
+		animDef.lastFrame = initialFrame + _gyros.frameSeparation;
+
+		changeAnimation(animDef, initialFrame, false);
 
 		gyro.logState();
 		gyro.currentState--;
@@ -687,10 +690,13 @@ bool Runtime::runGyroIdle() {
 	} else if (targetState > _gyros.dragCurrentState) {
 		AnimationDef animDef = _gyros.posAnim;
 
-		animDef.firstFrame += gyro.currentState * _gyros.frameSeparation;
-		animDef.lastFrame = animDef.firstFrame + _gyros.frameSeparation;
+		uint initialFrame = animDef.firstFrame + gyro.currentState * _gyros.frameSeparation;
+
+		// This is intentional instead of setting the stop frame, V-Cruise can overrun the end of the animation.
+		// firstFrame is left alone so playlists are based correctly.
+		animDef.lastFrame = initialFrame + _gyros.frameSeparation;
 
-		changeAnimation(animDef, false);
+		changeAnimation(animDef, initialFrame, false);
 
 		gyro.logState();
 		gyro.currentState++;




More information about the Scummvm-git-logs mailing list