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.