diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index dfddc45bc5..f7000ab5dd 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -293,6 +293,7 @@ bool Launcher::SettingsPage::loadSettings() } } loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox); + dopplerSpinBox->setValue(Settings::sound().mDopplerFactor); } // Interface Changes @@ -486,6 +487,8 @@ void Launcher::SettingsPage::saveSettings() const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked; Settings::sound().mCameraListener.set(cCameraListener); + + Settings::sound().mDopplerFactor.set(dopplerSpinBox->value()); } // Interface Changes diff --git a/apps/launcher/ui/settingspage.ui b/apps/launcher/ui/settingspage.ui index e792ac2843..c383a6abbf 100644 --- a/apps/launcher/ui/settingspage.ui +++ b/apps/launcher/ui/settingspage.ui @@ -1235,6 +1235,50 @@ + + + + + Controls the strength of the doppler effect. Zero means it is completely disabled. + + + Doppler Factor + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 2 + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.010000000000000 + + + 0.250000000000000 + + + + + Qt::Vertical diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 532bc771ba..5591b7205c 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -234,6 +234,8 @@ namespace MWBase const osg::Vec3f& pos, const osg::Vec3f& dir, const osg::Vec3f& up, bool underwater) = 0; + virtual void setListenerVel(const osg::Vec3f& vel) = 0; + virtual void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) = 0; void setSimulationTimeScale(float scale) { mSimulationTimeScale = scale; } diff --git a/apps/openmw/mwsound/openaloutput.cpp b/apps/openmw/mwsound/openaloutput.cpp index 0f27524912..a8bc166c94 100644 --- a/apps/openmw/mwsound/openaloutput.cpp +++ b/apps/openmw/mwsound/openaloutput.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "efxpresets.h" @@ -1137,12 +1138,13 @@ namespace MWSound alSourcef(source, AL_GAIN, gain); alSourcef(source, AL_PITCH, pitch); + alSourcef(source, AL_DOPPLER_FACTOR, 0.0f); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); } - void OpenALOutput::initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, + void OpenALOutput::initCommon3D(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv) { alSourcef(source, AL_REFERENCE_DISTANCE, mindist); @@ -1177,13 +1179,14 @@ namespace MWSound alSourcef(source, AL_GAIN, gain); alSourcef(source, AL_PITCH, pitch); + alSourcef(source, AL_DOPPLER_FACTOR, Settings::sound().mDopplerFactor); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSourcefv(source, AL_VELOCITY, vel.ptr()); } void OpenALOutput::updateCommon( - ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv) + ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv) { if (useenv && mListenerEnv == Env_Underwater && !mWaterFilter) { @@ -1195,7 +1198,7 @@ namespace MWSound alSourcef(source, AL_PITCH, pitch); alSourcefv(source, AL_POSITION, pos.ptr()); alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSourcefv(source, AL_VELOCITY, vel.ptr()); } bool OpenALOutput::playSound(Sound* sound, Sound_Handle data, float offset) @@ -1248,7 +1251,7 @@ namespace MWSound } source = mFreeSources.front(); - initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), + initCommon3D(source, sound->getPosition(), sound->getVelocity(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), sound->getIsLooping(), sound->getUseEnv()); alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); @@ -1312,7 +1315,7 @@ namespace MWSound return; ALuint source = GET_PTRID(sound->mHandle); - updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), + updateCommon(source, sound->getPosition(), sound->getVelocity(), sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), sound->getUseEnv()); getALError(); } @@ -1360,7 +1363,7 @@ namespace MWSound if (sound->getIsLooping()) Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; - initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), + initCommon3D(source, sound->getPosition(), sound->getVelocity(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), false, sound->getUseEnv()); if (getALError() != AL_NO_ERROR) return false; @@ -1443,7 +1446,7 @@ namespace MWSound OpenAL_SoundStream* stream = reinterpret_cast(sound->mHandle); ALuint source = stream->mSource; - updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), + updateCommon(source, sound->getPosition(), sound->getVelocity(), sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), sound->getUseEnv()); getALError(); } @@ -1459,12 +1462,13 @@ namespace MWSound } void OpenALOutput::updateListener( - const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) + const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, const osg::Vec3f& vel, Environment env) { if (mContext) { ALfloat orient[6] = { atdir.x(), atdir.y(), atdir.z(), updir.x(), updir.y(), updir.z() }; alListenerfv(AL_POSITION, pos.ptr()); + alListenerfv(AL_VELOCITY, vel.ptr()); alListenerfv(AL_ORIENTATION, orient); if (env != mListenerEnv) @@ -1497,6 +1501,7 @@ namespace MWSound } mListenerPos = pos; + mListenerVel = vel; mListenerEnv = env; } @@ -1583,6 +1588,7 @@ namespace MWSound , mDevice(nullptr) , mContext(nullptr) , mListenerPos(0.0f, 0.0f, 0.0f) + , mListenerVel(0.0f, 0.0f, 0.0f) , mListenerEnv(Env_Normal) , mWaterFilter(0) , mWaterEffect(0) diff --git a/apps/openmw/mwsound/openaloutput.hpp b/apps/openmw/mwsound/openaloutput.hpp index 4e96dd1627..e7089388c3 100644 --- a/apps/openmw/mwsound/openaloutput.hpp +++ b/apps/openmw/mwsound/openaloutput.hpp @@ -46,6 +46,7 @@ namespace MWSound StreamVec mActiveStreams; osg::Vec3f mListenerPos; + osg::Vec3f mListenerVel; Environment mListenerEnv; ALuint mWaterFilter; @@ -64,11 +65,11 @@ namespace MWSound std::unique_ptr mDefaultDeviceThread; void initCommon2D(ALuint source, const osg::Vec3f& pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv); - void initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, + void initCommon3D(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv); void updateCommon( - ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv); + ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv); float getTimeScaledPitch(SoundBase* sound); @@ -109,7 +110,7 @@ namespace MWSound void finishUpdate() override; void updateListener( - const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) override; + const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, const osg::Vec3f& vel, Environment env) override; void pauseSounds(int types) override; void resumeSounds(int types) override; diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 5160b2934f..0ac30d0ccd 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -31,6 +31,8 @@ namespace MWSound struct SoundParams { osg::Vec3f mPos; + osg::Vec3f mLastPos; + osg::Vec3f mVel; float mVolume = 1.0f; float mBaseVolume = 1.0f; float mPitch = 1.0f; @@ -57,6 +59,8 @@ namespace MWSound public: void setPosition(const osg::Vec3f& pos) { mParams.mPos = pos; } + void setLastPosition(const osg::Vec3f& lastpos) { mParams.mLastPos = lastpos; } + void setVelocity(const osg::Vec3f& vel) { mParams.mVel = vel; } void setVolume(float volume) { mParams.mVolume = volume; } void setBaseVolume(float volume) { mParams.mBaseVolume = volume; } void setFadeout(float duration) { setFade(duration, 0.0, Play_StopAtFadeEnd); } @@ -150,6 +154,8 @@ namespace MWSound } const osg::Vec3f& getPosition() const { return mParams.mPos; } + const osg::Vec3f& getLastPosition() const { return mParams.mLastPos; } + const osg::Vec3f& getVelocity() const {return mParams.mVel; } float getRealVolume() const { return mParams.mVolume * mParams.mBaseVolume * mParams.mFadeVolume; } float getPitch() const { return mParams.mPitch; } float getMinDistance() const { return mParams.mMinDistance; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 66bdfbdbfa..f7dff94d1c 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -119,6 +120,7 @@ namespace MWSound , mListenerPos(0, 0, 0) , mListenerDir(1, 0, 0) , mListenerUp(0, 0, 1) + , mListenerVel(0, 0, 0) , mUnderwaterSound(nullptr) , mNearWaterSound(nullptr) , mPlaybackPaused(false) @@ -959,8 +961,16 @@ namespace MWSound mUnderwaterSound = nullptr; } + float physicsFramerate = 60.f; + if (const char* env = getenv("OPENMW_PHYSICS_FPS")) + { + if (const auto physFramerate = Misc::StringUtils::toNumeric(env); + physFramerate.has_value() && *physFramerate > 0) + physicsFramerate = *physFramerate; + } + mOutput->startUpdate(); - mOutput->updateListener(mListenerPos, mListenerDir, mListenerUp, env); + mOutput->updateListener(mListenerPos, mListenerDir, mListenerUp, mListenerVel, env); updateMusic(duration); @@ -977,7 +987,11 @@ namespace MWSound if (sound->getIs3D()) { if (!ptr.isEmpty()) + { + sound->setLastPosition(sound->getPosition()); sound->setPosition(ptr.getRefData().getPosition().asVec3()); + sound->setVelocity((sound->getPosition() - sound->getLastPosition()) * physicsFramerate); + } cull3DSound(sound); } @@ -1013,8 +1027,10 @@ namespace MWSound { if (!ptr.isEmpty()) { + sound->setLastPosition(sound->getPosition()); MWBase::World* world = MWBase::Environment::get().getWorld(); sound->setPosition(world->getActorHeadTransform(ptr).getTrans()); + sound->setVelocity((sound->getPosition() - sound->getLastPosition()) * physicsFramerate); } cull3DSound(sound); @@ -1153,6 +1169,11 @@ namespace MWSound mWaterSoundUpdater.setUnderwater(underwater); } + void SoundManager::setListenerVel(const osg::Vec3f& vel) + { + mListenerVel = vel; + } + void SoundManager::updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) { SoundMap::iterator snditer = mActiveSounds.find(old.mRef); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 8fc7e6701f..f1b79557a0 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -92,6 +92,7 @@ namespace MWSound osg::Vec3f mListenerPos; osg::Vec3f mListenerDir; osg::Vec3f mListenerUp; + osg::Vec3f mListenerVel; int mPausedSoundTypes[BlockerType::MaxCount] = {}; @@ -283,6 +284,8 @@ namespace MWSound void setListenerPosDir( const osg::Vec3f& pos, const osg::Vec3f& dir, const osg::Vec3f& up, bool underwater) override; + void setListenerVel(const osg::Vec3f& vel) override; + void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) override; void clear() override; diff --git a/apps/openmw/mwsound/soundoutput.hpp b/apps/openmw/mwsound/soundoutput.hpp index e5c266b204..d0024e9a7c 100644 --- a/apps/openmw/mwsound/soundoutput.hpp +++ b/apps/openmw/mwsound/soundoutput.hpp @@ -62,7 +62,7 @@ namespace MWSound virtual void finishUpdate() = 0; virtual void updateListener( - const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) + const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, const osg::Vec3f& vel, Environment env) = 0; virtual void pauseSounds(int types) = 0; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 6ea510e1e2..c436f86df5 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -461,6 +461,11 @@ namespace MWWorld update(magicBoltState, duration); + for (const auto& sound : magicBoltState.mSounds) + { + sound->setVelocity(direction * speed); + } + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit // result. std::vector targetActors; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f608b8c781..cf9a0d7b07 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1467,6 +1467,8 @@ namespace MWWorld void World::queueMovement(const Ptr& ptr, const osg::Vec3f& velocity) { mPhysics->queueObjectMovement(ptr, velocity); + if(ptr == MWMechanics::getPlayer()) + MWBase::Environment::get().getSoundManager()->setListenerVel(velocity); } void World::updateAnimatedCollisionShape(const Ptr& ptr) diff --git a/components/settings/categories/sound.hpp b/components/settings/categories/sound.hpp index 8398a38c55..99e70fcdb2 100644 --- a/components/settings/categories/sound.hpp +++ b/components/settings/categories/sound.hpp @@ -24,6 +24,7 @@ namespace Settings SettingValue mHrtfEnable{ mIndex, "Sound", "hrtf enable" }; SettingValue mHrtf{ mIndex, "Sound", "hrtf" }; SettingValue mCameraListener{ mIndex, "Sound", "camera listener" }; + SettingValue mDopplerFactor{ mIndex, "Sound", "doppler factor", makeClampSanitizerFloat(0, 1) }; }; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index e5654794cc..33c5c60ce5 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -627,6 +627,9 @@ hrtf = # Specifies whether to use camera as audio listener camera listener = false +# Specifies strength of doppler effect +doppler factor = 0.25 + [Video] # Resolution of the OpenMW window or screen.