From 3013d07e8182c422612d6685b4871f12ffa460db Mon Sep 17 00:00:00 2001 From: Kaloyan Chehlarski Date: Fri, 7 May 2021 10:47:31 +0300 Subject: [PATCH] NANCY: Implement PlaySoundPanFrameAnchorAndDie action record Implemented the PlaySoundPanFrameAnchorAndDie action record type and added support for panning audio to the sound manager. --- engines/nancy/action/recordtypes.cpp | 10 ++++++- engines/nancy/action/recordtypes.h | 4 ++- engines/nancy/commontypes.cpp | 4 ++- engines/nancy/commontypes.h | 1 + engines/nancy/sound.cpp | 39 +++++++++++++++++++++++----- engines/nancy/sound.h | 6 ++++- engines/nancy/state/scene.cpp | 18 +++++++++---- engines/nancy/state/scene.h | 5 +++- 8 files changed, 71 insertions(+), 16 deletions(-) diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp index ea6c61f1ed4..6030527c925 100644 --- a/engines/nancy/action/recordtypes.cpp +++ b/engines/nancy/action/recordtypes.cpp @@ -516,7 +516,15 @@ void PlayDigiSoundAndDie::execute() { } void PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) { - stream.skip(0x20); + _sound.read(stream, SoundDescription::kDIGI); + stream.skip(2); +} + +void PlaySoundPanFrameAnchorAndDie::execute() { + g_nancy->_sound->loadSound(_sound, true); + g_nancy->_sound->playSound(_sound); + g_nancy->_sound->calculatePanForAllSounds(); + _isDone = true; } void PlaySoundMultiHS::readData(Common::SeekableReadStream &stream) { diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h index 219d6106774..a095bed7f9e 100644 --- a/engines/nancy/action/recordtypes.h +++ b/engines/nancy/action/recordtypes.h @@ -457,10 +457,12 @@ protected: virtual Common::String getRecordTypeName() const override { return "PlayDigiSoundAndDie"; } }; -class PlaySoundPanFrameAnchorAndDie : public Unimplemented { +class PlaySoundPanFrameAnchorAndDie : public ActionRecord { public: virtual void readData(Common::SeekableReadStream &stream) override; + virtual void execute() override; + SoundDescription _sound; protected: virtual Common::String getRecordTypeName() const override { return "PlaySoundPanFrameAnchorAndDie"; } }; diff --git a/engines/nancy/commontypes.cpp b/engines/nancy/commontypes.cpp index 3708e4b6cb0..a07b7a962ce 100644 --- a/engines/nancy/commontypes.cpp +++ b/engines/nancy/commontypes.cpp @@ -97,7 +97,9 @@ void SoundDescription::read(Common::SeekableReadStream &stream, Type type) { } stream.skip(2); volume = stream.readUint16LE(); - stream.skip(6); + stream.skip(2); + panAnchorFrame = stream.readUint16LE(); + stream.skip(2); } } // End of namespace Nancy diff --git a/engines/nancy/commontypes.h b/engines/nancy/commontypes.h index 09b185701ac..fb895493bcb 100644 --- a/engines/nancy/commontypes.h +++ b/engines/nancy/commontypes.h @@ -118,6 +118,7 @@ struct SoundDescription { uint16 channelID = 0; uint16 numLoops = 0; uint16 volume = 0; + uint16 panAnchorFrame = 0; void read(Common::SeekableReadStream &stream, Type type); }; diff --git a/engines/nancy/sound.cpp b/engines/nancy/sound.cpp index 21bca8b87c7..1dfaa9981cb 100644 --- a/engines/nancy/sound.cpp +++ b/engines/nancy/sound.cpp @@ -30,6 +30,8 @@ #include "engines/nancy/nancy.h" #include "engines/nancy/sound.h" +#include "engines/nancy/state/scene.h" + namespace Nancy { enum SoundType { @@ -284,7 +286,7 @@ SoundManager::~SoundManager() { stopAllSounds(); } -void SoundManager::loadSound(const SoundDescription &description) { +void SoundManager::loadSound(const SoundDescription &description, bool panning) { if (description.name == "NO SOUND") { return; } @@ -293,13 +295,16 @@ void SoundManager::loadSound(const SoundDescription &description) { _mixer->stopHandle(_channels[description.channelID].handle); } - delete _channels[description.channelID].stream; - _channels[description.channelID].stream = nullptr; + Channel &chan = _channels[description.channelID]; - _channels[description.channelID].name = description.name; - _channels[description.channelID].numLoops = description.numLoops; - _channels[description.channelID].volume = description.volume; + delete chan.stream; + chan.stream = nullptr; + chan.name = description.name; + chan.numLoops = description.numLoops; + chan.volume = description.volume; + chan.panAnchorFrame = description.panAnchorFrame; + chan.isPanning = panning; Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(description.name + (g_nancy->getGameType() == kGameTypeVampire ? ".dwd" : ".his")); if (file) { @@ -404,6 +409,28 @@ void SoundManager::stopAllSounds() { } } +void SoundManager::calculatePanForAllSounds() { + uint16 viewportFrameID = NancySceneState.getSceneInfo().frameID; + const State::Scene::SceneSummary &sceneSummary = NancySceneState.getSceneSummary(); + for (uint i = 0; i < 31; ++i) { + Channel &chan = _channels[i]; + if (chan.isPanning) { + switch (sceneSummary.totalViewAngle) { + case 180: + _mixer->setChannelBalance(chan.handle, CLIP((viewportFrameID - chan.panAnchorFrame) * sceneSummary.soundPanPerFrame * 364, -32768, 32767) / 256); + break; + case 360: + // TODO + _mixer->setChannelBalance(chan.handle, 0); + break; + default: + _mixer->setChannelBalance(chan.handle, 0); + break; + } + } + } +} + void SoundManager::stopAndUnloadSpecificSounds() { // TODO missing if diff --git a/engines/nancy/sound.h b/engines/nancy/sound.h index 21c34d4f47c..64c8db88473 100644 --- a/engines/nancy/sound.h +++ b/engines/nancy/sound.h @@ -47,7 +47,7 @@ public: void loadCommonSounds(); // Load a sound into a channel without starting it - void loadSound(const SoundDescription &description); + void loadSound(const SoundDescription &description, bool panning = false); void playSound(uint16 channelID); void playSound(const SoundDescription &description); @@ -65,6 +65,8 @@ public: void stopSound(const SoundDescription &description); void stopSound(const Common::String &chunkName); void stopAllSounds(); + + void calculatePanForAllSounds(); // Used when changing scenes void stopAndUnloadSpecificSounds(); @@ -77,6 +79,8 @@ protected: Audio::Mixer::SoundType type; uint16 numLoops = 0; uint volume = 0; + uint16 panAnchorFrame = 0; + bool isPanning = false; Audio::SeekableAudioStream *stream = nullptr; Audio::SoundHandle handle; }; diff --git a/engines/nancy/state/scene.cpp b/engines/nancy/state/scene.cpp index 937fc8a3831..b5d86f322a2 100644 --- a/engines/nancy/state/scene.cpp +++ b/engines/nancy/state/scene.cpp @@ -70,8 +70,11 @@ void Scene::SceneSummary::read(Common::SeekableReadStream &stream) { sound.read(stream, SoundDescription::kScene); ser.skip(6); - ser.syncAsByte(dontWrap); - ser.skip(9); + ser.syncAsUint16LE(dontWrap); + ser.syncAsUint16LE(soundWrapAroundPan); + ser.syncAsUint16LE(soundPanPerFrame); + ser.syncAsUint16LE(totalViewAngle); + ser.syncAsUint16LE(horizontalScrollDelta); ser.syncAsUint16LE(verticalScrollDelta); ser.syncAsUint16LE(horizontalEdgeSize); ser.syncAsUint16LE(verticalEdgeSize); @@ -567,6 +570,14 @@ void Scene::run() { // Update the UI elements and handle input NancyInput input = g_nancy->_input->getInput(); _viewport.handleInput(input); + + _sceneState.currentScene.verticalOffset = _viewport.getCurVerticalScroll(); + + if (_sceneState.currentScene.frameID != _viewport.getCurFrame()) { + _sceneState.currentScene.frameID = _viewport.getCurFrame(); + g_nancy->_sound->calculatePanForAllSounds(); + } + _actionManager.handleInput(input); _menuButton->handleInput(input); _helpButton->handleInput(input); @@ -585,9 +596,6 @@ void Scene::run() { requestStateChange(NancyState::kHelp); } - _sceneState.currentScene.frameID = _viewport.getCurFrame(); - _sceneState.currentScene.verticalOffset = _viewport.getCurVerticalScroll(); - // Handle invisible map button for (uint i = 0; i < ARRAYSIZE(g_nancy->getConstants().mapAccessSceneIDs); ++i) { if (g_nancy->getConstants().mapAccessSceneIDs[i] == -1) { diff --git a/engines/nancy/state/scene.h b/engines/nancy/state/scene.h index 197e32ea9b3..90cca8acae9 100644 --- a/engines/nancy/state/scene.h +++ b/engines/nancy/state/scene.h @@ -95,7 +95,10 @@ public: SoundDescription sound; // NancyFlag dontWrap; - // + uint16 soundWrapAroundPan; + uint16 soundPanPerFrame; + uint16 totalViewAngle; + uint16 horizontalScrollDelta; uint16 verticalScrollDelta; uint16 horizontalEdgeSize; uint16 verticalEdgeSize;