[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