From 7f3dda59252c0abe2f2437cc79a2f86a0382167a Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:27:30 +0200 Subject: [PATCH 01/20] Made working concept of proper RTS attenuation Cleaned up logs --- rts/System/Sound/OpenAL/Sound.cpp | 26 ++- rts/System/Sound/OpenAL/Sound.h | 2 + rts/System/Sound/OpenAL/SoundSource.cpp | 241 ++++++++++++++++++++---- rts/System/Sound/OpenAL/SoundSource.h | 26 ++- 4 files changed, 253 insertions(+), 42 deletions(-) diff --git a/rts/System/Sound/OpenAL/Sound.cpp b/rts/System/Sound/OpenAL/Sound.cpp index 832f506bfcc..c8c58692627 100644 --- a/rts/System/Sound/OpenAL/Sound.cpp +++ b/rts/System/Sound/OpenAL/Sound.cpp @@ -44,6 +44,8 @@ #include "System/float3.h" +#include "Rendering/GL/glExtra.h" + spring::recursive_mutex soundMutex; @@ -358,7 +360,7 @@ void CSound::DeviceChanged(uint32_t sdlDeviceIndex) // In these cases, no event is emitted — SDL2 switches the active audio device internally through the OS-specific audio backend (WASAPI, PulseAudio, etc.). // However, with certain device changes a short dropout may occur, and SDL2 will emit the SDL_AUDIODEVICEREMOVED event. // Shortly afterwards, the default device can usually be reinitialized. - + // This behavior can be reproduced on several test systems, for example when switching the Windows default device from monitor audio over HDMI to a sound card (HDMI->analog) std::lock_guard lck(soundMutex); @@ -513,7 +515,7 @@ bool CSound::OpenSdlDevice(const std::string& deviceName, SDL_AudioSpec& obtaine desiredSpec.samples = 4096; desiredSpec.callback = RenderSDLSamples; desiredSpec.userdata = this; - + /* SDL bug: can return devices with >2 channels (3D surround), even if we ask for just 2. * This causes the 2 "primary" channels to be moved in the 3D space compared to their "normal" state * and directional sound doesn't work anymore (though volume change with distance still does). @@ -1108,3 +1110,23 @@ std::vector CSound::GetSoundDevices() } return devices; } + +void CSound::DrawDebug() const +{ + // Only draw 3D sound sources that are currently playing + for (const CSoundSource& source: soundSources) { + // Use your extracted data functions instead of OpenAL calls + if (source.IsPlaying(false) && source.IsIn3D()) { + // Get position using your accessor methods instead of OpenAL calls + float3 pos = source.GetPosition(); // Use your implemented accessor + + // Draw a wireframe sphere at the sound source position + float color[4] = {1.0f, 0.0f, 0.0f, 0.7f}; // Red with some transparency + CMatrix44f matrix; + matrix.Translate(pos.x, pos.y, pos.z); + matrix.Scale(CSoundSource::REFERENCE_DIST * ELMOS_TO_METERS); // Adjusted scale for better visibility + GL::shapes.DrawWireSphere(8, 8, matrix, color); + } + } +} + diff --git a/rts/System/Sound/OpenAL/Sound.h b/rts/System/Sound/OpenAL/Sound.h index c3fa476160e..6085a73a7c9 100644 --- a/rts/System/Sound/OpenAL/Sound.h +++ b/rts/System/Sound/OpenAL/Sound.h @@ -25,6 +25,8 @@ class SoundItem; /// Default sound system implementation (OpenAL) class CSound : public ISound { +public: + void DrawDebug() const; // Debug visualization method public: CSound(); ~CSound(); diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index a66ba495a3c..21ea8787b08 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -2,27 +2,37 @@ #include "SoundSource.h" +#include +#include #include #include +#include +#include +#include #include "ALShared.h" +#include "Game/Camera.h" #include "EFX.h" +#include "Game/Camera.h" +#include "Game/CameraHandler.h" +#include "Game/TraceRay.h" +#include "Rendering/GlobalRendering.h" +#include "System/Misc/SpringTime.h" +#include "System/Sound/OpenAL/EFXfuncs.h" +#include "System/Sound/SoundLog.h" #include "System/Sound/IAudioChannel.h" #include "MusicStream.h" -#include "System/Sound/SoundLog.h" #include "SoundBuffer.h" #include "SoundItem.h" +#include +#include + + #include "Sound.h" //remove when unified ElmoInMeters #include "Sim/Misc/GlobalConstants.h" #include "System/float3.h" -#include "System/StringUtil.h" -#include "System/SpringMath.h" - - -static constexpr float ROLLOFF_FACTOR = 5.0f; -static constexpr float REFERENCE_DIST = 200.0f; // used to adjust the pitch to the GameSpeed (optional) @@ -54,11 +64,29 @@ CSoundSource::CSoundSource() if (!CheckError("CSoundSource::CSoundSource")) { id = 0; } else { - alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); + // alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); CheckError("CSoundSource::CSoundSource"); } } +/// Distance in front of the camera at which volume attenuation reaches its maximum +static constexpr float FORWARD_ATTENUATION_RANGE = 8000.0f; + +/// Distance behind the camera at which volume attenuation reaches its maximum +static constexpr float BACKWARD_ATTENUATION_RANGE = 300.0f; + +/// Distance outside the viewport at which off-screen attenuation reaches its maximum +static constexpr float OUTER_ATTENUATION_RANGE = 1000.0f; + +/// Percentage (0–1) of the viewport half-extents that is exempt from off-center attenuation +static constexpr float OFFCENTER_SAFE_ZONE_RATIO = 0.3f; + +/// Maximum volume reduction applied when a sound is fully off-center +static constexpr float OFFCENTER_ATTENUATION_STRENGTH = 0.3f; + +/// Scales how strongly camera zoom influences off-center attenuation +/// (zoomed out = stronger effect, zoomed in = weaker effect) +static constexpr float ZOOM_ATTENUATION_INFLUENCE = 0.2f; CSoundSource::CSoundSource(CSoundSource&& src) { @@ -76,26 +104,133 @@ CSoundSource::~CSoundSource() Delete(); } +float SmoothTowards(float current, float target, float speed, float dt) +{ + const float t = 1.0f - std::exp(-speed * dt); + return current + (target - current) * t; +} + +float Curve(float t, float min, float max, float k) { + return min + (max - min) * std::pow(t, k); +} + +void CSoundSource::ComputeCameraSpaceData() { + if (!in3D) return; + + CCamera* playerCamera = CCameraHandler::GetCamera(CCamera::CAMTYPE_PLAYER); + + float3 toSound = currentPosition - playerCamera->GetPos(); + + float camRight = playerCamera->GetRight().dot(toSound); + float camUp = playerCamera->GetUp().dot(toSound); + float camForward = playerCamera->GetForward().dot(toSound); + + innerDistance = sqrt(camRight * camRight + camUp * camUp); + forwardDistance = camForward; + + float hfov = playerCamera->GetHFOV() * math::DEG_TO_RAD; + float vfov = playerCamera->GetVFOV() * math::DEG_TO_RAD; + + float distance = std::abs(camForward); + if (distance <= 0.0f) distance = 1.0f; + + float frustumWidth = distance * std::tan(hfov * 0.5f); + float frustumHeight = distance * std::tan(vfov * 0.5f); + + const CUnit* hitUnit = nullptr; + const CFeature* hitFeature = nullptr; + + terrainDistance = TraceRay::GuiTraceRay( + playerCamera->GetPos(), + playerCamera->GetForward(), + FORWARD_ATTENUATION_RANGE, + nullptr, + hitUnit, + hitFeature, + true, + true + ); + + viewportHalfExtents = float2(frustumWidth, frustumHeight); + + float outsideRight = std::max(0.0f, std::abs(camRight) - frustumWidth); + float outsideUp = std::max(0.0f, std::abs(camUp) - frustumHeight); + + outerDistance = std::sqrt(outsideRight * outsideRight + outsideUp * outsideUp); +} + +void CSoundSource::ApplyGainBasedOnVisiblity(bool smooth) { + if (!in3D) return; + + float forwardValue = forwardDistance >= 0 ? + forwardValue = std::clamp(1 - forwardDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f): + forwardValue = std::clamp(1 - (-forwardDistance) / BACKWARD_ATTENUATION_RANGE, 0.0f, 1.0f); + + float outerValue = std::clamp(1 - outerDistance / OUTER_ATTENUATION_RANGE, 0.0f, 1.0f); + + float innerMaxRadius = std::min(viewportHalfExtents.x, viewportHalfExtents.y); + float innerMinRadius = innerMaxRadius * OFFCENTER_SAFE_ZONE_RATIO; + + float t = std::clamp((innerDistance - innerMinRadius) / (innerMaxRadius - innerMinRadius), 0.0f, 1.0f); + + float zoomFactor = std::clamp(terrainDistance == -1 ? 1.0f : terrainDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f); + + float innerValue = 1.0f - t * (OFFCENTER_ATTENUATION_STRENGTH * zoomFactor); + + float totalValue = forwardValue * outerValue * innerValue; + + if (smooth) + curViewportVolumeMultiplier = SmoothTowards( + curViewportVolumeMultiplier, + totalValue, + VIEWPORT_VOLUME_REDUCTION_SPEED, + globalRendering->lastFrameTime); + else + curViewportVolumeMultiplier = totalValue; + + float vol = curVolume; + + vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3); + + alSourcef(id, AL_GAIN, vol); + + float filter = 1; + + if (curViewportVolumeMultiplier <= 1.0) { + float factor = std::min(curViewportVolumeMultiplier / 1.0f, 1.0f); + filter = Curve(factor, 0.1f, 1.0f, 0.75f); + } + + alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); +} + void CSoundSource::Update() { + ComputeCameraSpaceData(); + ApplyGainBasedOnVisiblity(true); + if (asyncPlayItem.id != 0) { // Sound::Update() holds mutex, soundItems can not be accessed concurrently Play(asyncPlayItem.channel, sound->GetSoundItem(asyncPlayItem.id), asyncPlayItem.position, asyncPlayItem.velocity, asyncPlayItem.volume, asyncPlayItem.relative); asyncPlayItem = AsyncSoundItemData(); } - if (curPlayingItem.id != 0) { - if (in3D && (efxEnabled != efx.Enabled())) { - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); - alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); - efxEnabled = efx.Enabled(); - efxUpdates = efx.updates; - } + // LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); + + if (curPlayingItem.id != 0) { + // if (in3D && (efxEnabled != efx.Enabled())) { + // alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); + // alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + // alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); + // efxEnabled = efx.Enabled(); + // efxUpdates = efx.updates; + // } if (heightRolloffModifier != curHeightRolloffModifier) { curHeightRolloffModifier = heightRolloffModifier; - alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * curPlayingItem.rolloff * heightRolloffModifier); + // LOG_L(L_WARNING, "[AUDIO] Tried to set rollof factor"); + // alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * curPlayingItem.rolloff * heightRolloffModifier); } if (!IsPlaying(true) || ((curPlayingItem.loopTime > 0) && (spring_gettime() > loopStop))) @@ -113,9 +248,9 @@ void CSoundSource::Update() if (efxEnabled && (efxUpdates != efx.updates)) { // airAbsorption & LowPass aren't auto updated by OpenAL on change, so we need to do it per source - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); - alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); - efxUpdates = efx.updates; + // alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); + // alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); + // efxUpdates = efx.updates; } } @@ -131,7 +266,6 @@ void CSoundSource::Delete() CheckError("CSoundSource::Delete"); } - int CSoundSource::GetCurrentPriority() const { if (asyncPlayItem.id != 0) @@ -168,7 +302,6 @@ bool CSoundSource::IsPlaying(const bool checkOpenAl) const return (state == AL_PLAYING); } - void CSoundSource::Stop() { alSourceStop(id); @@ -208,26 +341,32 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo if (!item->PlayNow()) return; + in3D = !relative && item->in3D; + + currentPosition = pos; + + name = item->name; + const SoundBuffer& itemBuffer = SoundBuffer::GetById(item->GetSoundBufferID()); Stop(); - curVolume = volume; + name = item->name; + curVolume = volume * item->GetGain() * channel->volume; curPlayingItem = {item->soundItemID, item->loopTime, item->priority, item->GetGain(), item->rolloff}; curChannel = channel; alSourcei(id, AL_BUFFER, itemBuffer.GetId()); - alSourcef(id, AL_GAIN, volume * item->GetGain() * channel->volume); + alSourcef(id, AL_GAIN, curVolume); alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch); velocity *= item->dopplerScale * ELMOS_TO_METERS; - alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z); + // alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z); alSourcei(id, AL_LOOPING, (item->loopTime > 0) ? AL_TRUE : AL_FALSE); loopStop = spring_gettime() + spring_msecs(item->loopTime); - if (relative || !item->in3D) { - in3D = false; + if (!in3D) { if (efxEnabled) { alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); @@ -243,21 +382,44 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo if (itemBuffer.GetChannels() > 1) LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); - in3D = true; - if (efx.Enabled()) { - efxEnabled = true; - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); - alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); - efxUpdates = efx.updates; - } + // if (efx.Enabled()) { + // efxEnabled = true; + // alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); + // alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); + // alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); + // efxUpdates = efx.updates; + // } + + alDopplerFactor(0); - alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); pos *= ELMOS_TO_METERS; + + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); - curHeightRolloffModifier = heightRolloffModifier; - alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier); + alSourcef(id, AL_ROLLOFF_FACTOR, 0); + + efx.Enable(); + if (attenuationFilter == 0) { + alGenFilters(1, &attenuationFilter); + alFilteri(attenuationFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + } + + // need to set this here or else the filter state is incorrect when reusing a source + alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, 1); + ComputeCameraSpaceData(); + ApplyGainBasedOnVisiblity(false); + + alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); + + //we should not use attenuation features as they will fight against the nature of an rts game + //it's necessary to calclulate the gain/filtering based on custom viewport related logic, not raw distance + + // curHeightRolloffModifier = heightRolloffModifier; + // alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier); + // alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR); + // alSourcef(id, AL_MAX_DISTANCE, MAX_DISTANCE * ELMOS_TO_METERS); #if defined(__APPLE__) || defined(__OpenBSD__) alSourcef(id, AL_MAX_DISTANCE, 1000000.0f); @@ -280,7 +442,8 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo #endif } - alSourcePlay(id); + + alSourcePlay(id); if (itemBuffer.GetId() == 0) LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); diff --git a/rts/System/Sound/OpenAL/SoundSource.h b/rts/System/Sound/OpenAL/SoundSource.h index ecf61527552..81ce706f41f 100644 --- a/rts/System/Sound/OpenAL/SoundSource.h +++ b/rts/System/Sound/OpenAL/SoundSource.h @@ -8,7 +8,6 @@ #include -#include "System/Misc/NonCopyable.h" #include "System/Misc/SpringTime.h" #include "System/float3.h" @@ -36,6 +35,8 @@ class CSoundSource void Update(); void Delete(); + void ApplyGainBasedOnVisiblity(const bool smooth); + void UpdateVolume(); bool IsValid() const { return (id != 0); }; @@ -51,10 +52,17 @@ class CSoundSource void StreamPause(); float GetStreamTime(); float GetStreamPlayTime(); + void ComputeCameraSpaceData(); static void SetPitch(const float& newPitch) { globalPitch = newPitch; } static void SetHeightRolloffModifer(const float& mod) { heightRolloffModifier = mod; } + bool IsIn3D() const { return in3D; } + float3 GetPosition() const { return currentPosition; } + ALuint GetId() const { return id; } + + static constexpr float VIEWPORT_VOLUME_REDUCTION_SPEED = 0.02f; + private: void swap(CSoundSource& other); @@ -93,6 +101,17 @@ class CSoundSource private: ALuint id = 0; + ALuint attenuationFilter = 0; + float outerDistance = 0; + float innerDistance = 0; + float forwardDistance = 0; + float2 viewportHalfExtents = float2(); + float terrainDistance; + + float cameraZoom; + + float3 currentPosition = float3(0.0f, 0.0f, 0.0f); + SoundItemData curPlayingItem; AsyncSoundItemData asyncPlayItem; @@ -100,11 +119,16 @@ class CSoundSource std::unique_ptr curStream; float curVolume = 1.0f; + float curViewportVolumeMultiplier = 0; + spring_time loopStop {1e9}; + spring_time lastUpdate = spring_gettime(); bool in3D = false; bool efxEnabled = false; int efxUpdates = 0; + std::string name; + ALfloat curHeightRolloffModifier = 1.0f; }; From 7668712e3310616cba0e6d89b4cf977e1e6eb804 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:10:18 +0200 Subject: [PATCH 02/20] Added ISoundAttenuationModel.h --- rts/System/Sound/ISoundAttenuationModel.h | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 rts/System/Sound/ISoundAttenuationModel.h diff --git a/rts/System/Sound/ISoundAttenuationModel.h b/rts/System/Sound/ISoundAttenuationModel.h new file mode 100644 index 00000000000..20f10317bcc --- /dev/null +++ b/rts/System/Sound/ISoundAttenuationModel.h @@ -0,0 +1,30 @@ +#pragma once + +struct SoundAttenuationInput { + // float3 soundPosition; + + float forwardDistance; + float innerDistance; + float outerDistance; + + float viewportHalfWidth; + float viewportHalfHeight; + + float zoomFactor; + + //maybe some sound specific settings like should it be resistant to attenuation +}; + +struct SoundAttenuationOutput { + float totalFactor; +}; + +class ISoundAttenuationModel { +public: + virtual ~ISoundAttenuationModel() = default; + + virtual SoundAttenuationOutput Evaluate( + const SoundAttenuationInput& in + ) const = 0; +}; + From b9fbe8286bf3e04327f764c545f41c6b81a71e94 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Mon, 22 Dec 2025 21:42:27 +0200 Subject: [PATCH 03/20] First attempt trying to make AttenuationModel --- rts/Game/Camera.cpp | 21 +- rts/Game/Camera.h | 4 + .../AttenuationModels/RtsAttenuationModel.cpp | 78 +++++ .../AttenuationModels/RtsAttenuationModel.h | 22 ++ rts/System/Sound/CMakeLists.txt | 2 + rts/System/Sound/ISound.cpp | 4 +- rts/System/Sound/ISound.h | 6 + rts/System/Sound/ISoundAttenuationModel.h | 18 +- rts/System/Sound/OpenAL/Sound.cpp | 2 + rts/System/Sound/OpenAL/Sound.h | 6 + rts/System/Sound/OpenAL/SoundSource.cpp | 324 +++++++----------- rts/System/Sound/OpenAL/SoundSource.h | 29 +- 12 files changed, 300 insertions(+), 216 deletions(-) create mode 100644 rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp create mode 100644 rts/System/Sound/AttenuationModels/RtsAttenuationModel.h diff --git a/rts/Game/Camera.cpp b/rts/Game/Camera.cpp index 8c5ea462a4b..7e59ca14e7a 100644 --- a/rts/Game/Camera.cpp +++ b/rts/Game/Camera.cpp @@ -4,6 +4,7 @@ #include "Camera.h" #include "CameraHandler.h" +#include "Game/TraceRay.h" #include "UI/MouseHandler.h" #include "Map/Ground.h" #include "Map/ReadMap.h" @@ -708,6 +709,24 @@ void CCamera::ClipFrustumLines(const float zmin, const float zmax, bool neg) } } +void CCamera::TraceToTerrain() { + if (camType != CAMTYPE_PLAYER) + return; + + const CUnit* hitUnit = nullptr; + const CFeature* hitFeature = nullptr; + + terrainDistance = TraceRay::GuiTraceRay( + GetPos(), + GetForward(), + 30000, + nullptr, + hitUnit, + hitFeature, + true, + true + ); +} float3 CCamera::GetMoveVectorFromState(bool fromKeyState) const { @@ -716,7 +735,7 @@ float3 CCamera::GetMoveVectorFromState(bool fromKeyState) const if (useInterpolate > 0) camDeltaTime = 1000.0f / std::fmax(globalRendering->FPS, 1.0f); - + float camMoveSpeed = 1.0f; camMoveSpeed *= movState[MOVE_STATE_SLW] ? moveSlowMult : 1.0f; diff --git a/rts/Game/Camera.h b/rts/Game/Camera.h index 7901f2218e2..541fe7be4a7 100644 --- a/rts/Game/Camera.h +++ b/rts/Game/Camera.h @@ -202,6 +202,7 @@ class CCamera { float GetNearPlaneDist() const { return frustum.scales.z; } float GetFarPlaneDist() const { return frustum.scales.w; } float GetAspectRatio() const { return aspectRatio; } + float GetTerrainDistance() const { return terrainDistance; } float3 GetMoveVectorFromState(bool fromKeyState) const; @@ -253,6 +254,7 @@ class CCamera { void UpdateDirsFromRot(const float3& r); + void TraceToTerrain(); public: float3 pos; float3 rot; ///< x = inclination, y = azimuth (to the -z axis!), z = roll @@ -309,6 +311,8 @@ class CCamera { uint8_t inViewPlanesMask; + float terrainDistance; + bool movState[10]; // fwd, back, left, right, up, down, fast, slow, tilt, reset bool rotState[4]; // unused }; diff --git a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp new file mode 100644 index 00000000000..85fb352de0e --- /dev/null +++ b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp @@ -0,0 +1,78 @@ +/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ + +#include "RtsAttenuationModel.h" +#include "Game/Camera.h" +#include "Game/CameraHandler.h" +#include "System/Sound/ISoundAttenuationModel.h" +#include "Game/TraceRay.h" +#include +#include + +SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput& in) const { + + SoundAttenuationOutput out; + + // ----------------------------------------------------------------- + // Convert the soundPosition in to camera space and calculate necessary data + // ----------------------------------------------------------------- + + { + CCamera* playerCamera = CCameraHandler::GetCamera(CCamera::CAMTYPE_PLAYER); + + float3 toSound = in.soundPosition - playerCamera->GetPos(); + + float camForward = playerCamera->GetForward().dot(toSound); + float camRight = playerCamera->GetRight().dot(toSound); + float camUp = playerCamera->GetUp().dot(toSound); + + out.innerDistance = sqrt(camRight * camRight + camUp * camUp); + out.forwardDistance = camForward; + + float hfov = playerCamera->GetHFOV() * math::DEG_TO_RAD; + float vfov = playerCamera->GetVFOV() * math::DEG_TO_RAD; + + float distance = std::abs(camForward); + if (distance <= 0.0f) distance = 1.0f; + + out.frustumWidth = distance * std::tan(hfov * 0.5f); + out.frustumHeight = distance * std::tan(vfov * 0.5f); + + float outsideRight = std::max(0.0f, std::abs(camRight) - out.frustumWidth); + float outsideUp = std::max(0.0f, std::abs(camUp) - out.frustumHeight); + + out.outerDistance = std::sqrt(outsideRight * outsideRight + outsideUp * outsideUp); + + float terrainDistance = playerCamera->GetTerrainDistance(); + out.zoomFactor = std::clamp(terrainDistance == -1 ? 1.0f : terrainDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f); + } + + // ----------------------------------------------------------------- + // Convert all the positional data to normalized ranges and calculate the final range + // ----------------------------------------------------------------- + + float totalFactor; + + { + // Calculate forward attenuation + float forwardValue = out.forwardDistance >= 0 ? + std::clamp(1.0f - out.forwardDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f) : + std::clamp(1.0f - (-out.forwardDistance) / BACKWARD_ATTENUATION_RANGE, 0.0f, 1.0f); + + // Calculate outer attenuation + float outerValue = std::clamp(1.0f - out.outerDistance / OUTER_ATTENUATION_RANGE, 0.0f, 1.0f); + + // Calculate inner attenuation + float innerMaxRadius = std::min(out.frustumWidth, out.frustumHeight); + float innerMinRadius = innerMaxRadius * OFFCENTER_SAFE_ZONE_RATIO; + float t = std::clamp((out.innerDistance - innerMinRadius) / (innerMaxRadius - innerMinRadius), 0.0f, 1.0f); + + // Apply zoom factor to inner attenuation + float innerValue = 1.0f - t * (OFFCENTER_ATTENUATION_STRENGTH * out.zoomFactor); + + // Combine all attenuation factors + totalFactor = forwardValue * outerValue * innerValue; + } + + return out; +} + diff --git a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.h b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.h new file mode 100644 index 00000000000..d63c2f51f87 --- /dev/null +++ b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.h @@ -0,0 +1,22 @@ +/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ + +#pragma once + +#include "System/Sound/ISoundAttenuationModel.h" + +class RtsAttenuationModel : public ISoundAttenuationModel +{ +public: + SoundAttenuationOutput Evaluate(const SoundAttenuationInput& in) const override; + +private: + //TODO make these settings in the game when using the rts attenuation + static constexpr float FORWARD_ATTENUATION_RANGE = 8000.0f; + static constexpr float BACKWARD_ATTENUATION_RANGE = 300.0f; + static constexpr float OUTER_ATTENUATION_RANGE = 1000.0f; + static constexpr float OFFCENTER_SAFE_ZONE_RATIO = 0.3f; + static constexpr float OFFCENTER_ATTENUATION_STRENGTH = 0.3f; +}; + +// "RTS Model: Designed for RTS games with viewport-based attenuation, forward/backward distance handling, off-screen attenuation, and zoom-aware center attenuation. Seen in games like \"Planetary Annihilation\" or \"Supreme Commander 2\"" + diff --git a/rts/System/Sound/CMakeLists.txt b/rts/System/Sound/CMakeLists.txt index a574323e378..8b2c529cbb3 100644 --- a/rts/System/Sound/CMakeLists.txt +++ b/rts/System/Sound/CMakeLists.txt @@ -8,6 +8,7 @@ set(noSoundSources ISound.cpp Null/SoundChannels.cpp Null/NullSound.cpp + AttenuationModels/RtsAttenuationModel.cpp ) add_library(no-sound STATIC EXCLUDE_FROM_ALL ${noSoundSources}) @@ -42,6 +43,7 @@ if (NOT NO_SOUND) OpenAL/SoundItem.cpp OpenAL/SoundSource.cpp OpenAL/VorbisShared.cpp + AttenuationModels/RtsAttenuationModel.cpp ) find_package_static(OpenAL 1.18.2 REQUIRED) diff --git a/rts/System/Sound/ISound.cpp b/rts/System/Sound/ISound.cpp index a8f14595442..9d64d25b586 100644 --- a/rts/System/Sound/ISound.cpp +++ b/rts/System/Sound/ISound.cpp @@ -40,7 +40,9 @@ CONFIG(float, snd_airAbsorption).defaultValue(0.1f); CONFIG(std::string, snd_device).defaultValue("").description("Sets the used output device. See \"Available Devices\" section in infolog.txt."); - +CONFIG(bool, snd_useAttenuationModel) + .defaultValue(false) + .description("Use the new AttenuationModel system. Affects how you hear 3D sounds in the game. Recommended: ON"); #ifndef NO_SOUND // [0] := Music, [1] := General, [2] := Battle, [3] := UnitReply, [4] := UserInterface diff --git a/rts/System/Sound/ISound.h b/rts/System/Sound/ISound.h index 6638a4469f1..b554e6a15d5 100644 --- a/rts/System/Sound/ISound.h +++ b/rts/System/Sound/ISound.h @@ -3,6 +3,7 @@ #ifndef _I_SOUND_H_ #define _I_SOUND_H_ +#include "System/Sound/ISoundAttenuationModel.h" #include #include #include @@ -79,6 +80,11 @@ class ISound { private: virtual bool LoadSoundDefsImpl(LuaParser* defsParser) = 0; static bool IsNullAudio(); + +public: + ISoundAttenuationModel* GetAttenuationModel() { return attenuationModel; } +private: + ISoundAttenuationModel* attenuationModel; }; #define sound ISound::GetInstance() diff --git a/rts/System/Sound/ISoundAttenuationModel.h b/rts/System/Sound/ISoundAttenuationModel.h index 20f10317bcc..77239a6d4a0 100644 --- a/rts/System/Sound/ISoundAttenuationModel.h +++ b/rts/System/Sound/ISoundAttenuationModel.h @@ -1,21 +1,19 @@ #pragma once +#include "System/float3.h" struct SoundAttenuationInput { - // float3 soundPosition; + float3 soundPosition; + //maybe some sound specific settings like should it be resistant to attenuation +}; +/// All of the data used to calculate the totalFactor as well as the final totalFactor +struct SoundAttenuationOutput { float forwardDistance; float innerDistance; float outerDistance; - - float viewportHalfWidth; - float viewportHalfHeight; - + float frustumHeight; + float frustumWidth; float zoomFactor; - - //maybe some sound specific settings like should it be resistant to attenuation -}; - -struct SoundAttenuationOutput { float totalFactor; }; diff --git a/rts/System/Sound/OpenAL/Sound.cpp b/rts/System/Sound/OpenAL/Sound.cpp index c8c58692627..9a340aff848 100644 --- a/rts/System/Sound/OpenAL/Sound.cpp +++ b/rts/System/Sound/OpenAL/Sound.cpp @@ -53,11 +53,13 @@ spring::recursive_mutex soundMutex; CSound::CSound() { configHandler->NotifyOnChange(this, {"snd_volmaster", "snd_eaxpreset", "snd_filter", "UseEFX", "snd_volgeneral", "snd_volunitreply", "snd_volbattle", "snd_volui", "snd_volmusic", "PitchAdjust"}); + attenuationModel = new RtsAttenuationModel(); } CSound::~CSound() { configHandler->RemoveObserver(this); + delete attenuationModel; } diff --git a/rts/System/Sound/OpenAL/Sound.h b/rts/System/Sound/OpenAL/Sound.h index 6085a73a7c9..f9f3e00d994 100644 --- a/rts/System/Sound/OpenAL/Sound.h +++ b/rts/System/Sound/OpenAL/Sound.h @@ -10,6 +10,7 @@ #include #include +#include "System/Sound/AttenuationModels/RtsAttenuationModel.h" #include "System/Sound/ISound.h" #include "System/float3.h" #include "System/UnorderedMap.hpp" @@ -75,6 +76,9 @@ class CSound : public ISound int GetFrameSize() const { return frameSize; } std::vector GetSoundDevices() override; + + ISoundAttenuationModel* GetAttenuationModel() { return attenuationModel; } + private: typedef spring::unordered_map SoundItemNameMap; typedef spring::unordered_map SoundItemDefsMap; @@ -105,6 +109,8 @@ class CSound : public ISound std::string selectedDeviceName = ""; + ISoundAttenuationModel* attenuationModel; + spring::thread soundThread; spring::unordered_map soundMap; // spring::unordered_set preloadSet; diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 21ea8787b08..5df97b69791 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -17,6 +17,7 @@ #include "Game/CameraHandler.h" #include "Game/TraceRay.h" #include "Rendering/GlobalRendering.h" +#include "System/Config/ConfigHandler.h" #include "System/Misc/SpringTime.h" #include "System/Sound/OpenAL/EFXfuncs.h" #include "System/Sound/SoundLog.h" @@ -34,7 +35,6 @@ #include "Sim/Misc/GlobalConstants.h" #include "System/float3.h" - // used to adjust the pitch to the GameSpeed (optional) float CSoundSource::globalPitch = 1.0f; @@ -43,17 +43,18 @@ float CSoundSource::heightRolloffModifier = 1.0f; void CSoundSource::swap(CSoundSource& r) { + //TODO makesure all data here is swapped std::swap(id, r.id); - std::swap(curChannel, r.curChannel); + std::swap(currentChannel, r.currentChannel); std::swap(curStream, r.curStream); - std::swap(curVolume, r.curVolume); + std::swap(currentVolume, r.currentVolume); std::swap(loopStop, r.loopStop); std::swap(in3D, r.in3D); std::swap(efxEnabled, r.efxEnabled); std::swap(efxUpdates, r.efxUpdates); std::swap(curHeightRolloffModifier, r.curHeightRolloffModifier); - std::swap(curPlayingItem, r.curPlayingItem); + std::swap(currentSoundItem, r.currentSoundItem); std::swap(asyncPlayItem, r.asyncPlayItem); } @@ -69,25 +70,6 @@ CSoundSource::CSoundSource() } } -/// Distance in front of the camera at which volume attenuation reaches its maximum -static constexpr float FORWARD_ATTENUATION_RANGE = 8000.0f; - -/// Distance behind the camera at which volume attenuation reaches its maximum -static constexpr float BACKWARD_ATTENUATION_RANGE = 300.0f; - -/// Distance outside the viewport at which off-screen attenuation reaches its maximum -static constexpr float OUTER_ATTENUATION_RANGE = 1000.0f; - -/// Percentage (0–1) of the viewport half-extents that is exempt from off-center attenuation -static constexpr float OFFCENTER_SAFE_ZONE_RATIO = 0.3f; - -/// Maximum volume reduction applied when a sound is fully off-center -static constexpr float OFFCENTER_ATTENUATION_STRENGTH = 0.3f; - -/// Scales how strongly camera zoom influences off-center attenuation -/// (zoomed out = stronger effect, zoomed in = weaker effect) -static constexpr float ZOOM_ATTENUATION_INFLUENCE = 0.2f; - CSoundSource::CSoundSource(CSoundSource&& src) { // can't use naive/default move because `id` member has to be unique @@ -114,70 +96,13 @@ float Curve(float t, float min, float max, float k) { return min + (max - min) * std::pow(t, k); } -void CSoundSource::ComputeCameraSpaceData() { - if (!in3D) return; - - CCamera* playerCamera = CCameraHandler::GetCamera(CCamera::CAMTYPE_PLAYER); - - float3 toSound = currentPosition - playerCamera->GetPos(); - - float camRight = playerCamera->GetRight().dot(toSound); - float camUp = playerCamera->GetUp().dot(toSound); - float camForward = playerCamera->GetForward().dot(toSound); - - innerDistance = sqrt(camRight * camRight + camUp * camUp); - forwardDistance = camForward; - - float hfov = playerCamera->GetHFOV() * math::DEG_TO_RAD; - float vfov = playerCamera->GetVFOV() * math::DEG_TO_RAD; - - float distance = std::abs(camForward); - if (distance <= 0.0f) distance = 1.0f; - - float frustumWidth = distance * std::tan(hfov * 0.5f); - float frustumHeight = distance * std::tan(vfov * 0.5f); - - const CUnit* hitUnit = nullptr; - const CFeature* hitFeature = nullptr; - - terrainDistance = TraceRay::GuiTraceRay( - playerCamera->GetPos(), - playerCamera->GetForward(), - FORWARD_ATTENUATION_RANGE, - nullptr, - hitUnit, - hitFeature, - true, - true - ); - - viewportHalfExtents = float2(frustumWidth, frustumHeight); - - float outsideRight = std::max(0.0f, std::abs(camRight) - frustumWidth); - float outsideUp = std::max(0.0f, std::abs(camUp) - frustumHeight); - - outerDistance = std::sqrt(outsideRight * outsideRight + outsideUp * outsideUp); -} - -void CSoundSource::ApplyGainBasedOnVisiblity(bool smooth) { - if (!in3D) return; - - float forwardValue = forwardDistance >= 0 ? - forwardValue = std::clamp(1 - forwardDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f): - forwardValue = std::clamp(1 - (-forwardDistance) / BACKWARD_ATTENUATION_RANGE, 0.0f, 1.0f); - - float outerValue = std::clamp(1 - outerDistance / OUTER_ATTENUATION_RANGE, 0.0f, 1.0f); +void CSoundSource::ApplyAttenuationModel(bool smooth) { - float innerMaxRadius = std::min(viewportHalfExtents.x, viewportHalfExtents.y); - float innerMinRadius = innerMaxRadius * OFFCENTER_SAFE_ZONE_RATIO; + if (sound != nullptr) + return; - float t = std::clamp((innerDistance - innerMinRadius) / (innerMaxRadius - innerMinRadius), 0.0f, 1.0f); - - float zoomFactor = std::clamp(terrainDistance == -1 ? 1.0f : terrainDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f); - - float innerValue = 1.0f - t * (OFFCENTER_ATTENUATION_STRENGTH * zoomFactor); - - float totalValue = forwardValue * outerValue * innerValue; + attenuationOutput = sound->GetAttenuationModel()->Evaluate({ currentPosition }); + float totalValue = attenuationOutput.totalFactor; if (smooth) curViewportVolumeMultiplier = SmoothTowards( @@ -188,7 +113,7 @@ void CSoundSource::ApplyGainBasedOnVisiblity(bool smooth) { else curViewportVolumeMultiplier = totalValue; - float vol = curVolume; + float vol = currentVolume; vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3); @@ -203,37 +128,37 @@ void CSoundSource::ApplyGainBasedOnVisiblity(bool smooth) { alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); + + alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); } void CSoundSource::Update() { - ComputeCameraSpaceData(); - ApplyGainBasedOnVisiblity(true); - if (asyncPlayItem.id != 0) { // Sound::Update() holds mutex, soundItems can not be accessed concurrently Play(asyncPlayItem.channel, sound->GetSoundItem(asyncPlayItem.id), asyncPlayItem.position, asyncPlayItem.velocity, asyncPlayItem.volume, asyncPlayItem.relative); asyncPlayItem = AsyncSoundItemData(); } - // LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); - - if (curPlayingItem.id != 0) { - // if (in3D && (efxEnabled != efx.Enabled())) { - // alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); - // alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); - // alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); - // efxEnabled = efx.Enabled(); - // efxUpdates = efx.updates; - // } - - if (heightRolloffModifier != curHeightRolloffModifier) { - curHeightRolloffModifier = heightRolloffModifier; - // LOG_L(L_WARNING, "[AUDIO] Tried to set rollof factor"); - // alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * curPlayingItem.rolloff * heightRolloffModifier); - } + if (currentSoundItem.id != 0) { + if (configHandler->GetBool("snd_useAttenuationModel")) { + ApplyAttenuationModel(true); + } else { + if (in3D && (efxEnabled != efx.Enabled())) { + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); + efxEnabled = efx.Enabled(); + efxUpdates = efx.updates; + } + + if (heightRolloffModifier != curHeightRolloffModifier) { + curHeightRolloffModifier = heightRolloffModifier; + alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); + } + } - if (!IsPlaying(true) || ((curPlayingItem.loopTime > 0) && (spring_gettime() > loopStop))) + if (!IsPlaying(true) || ((currentSoundItem.loopTime > 0) && (spring_gettime() > loopStop))) Stop(); } @@ -246,11 +171,11 @@ void CSoundSource::Update() } } - if (efxEnabled && (efxUpdates != efx.updates)) { + if (!configHandler->GetBool("snd_useAttenuationModel") && efxEnabled && (efxUpdates != efx.updates)) { // airAbsorption & LowPass aren't auto updated by OpenAL on change, so we need to do it per source - // alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); - // alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); - // efxUpdates = efx.updates; + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); + alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); + efxUpdates = efx.updates; } } @@ -274,10 +199,10 @@ int CSoundSource::GetCurrentPriority() const if (curStream) return INT_MAX; - if (curPlayingItem.id == 0) + if (currentSoundItem.id == 0) return INT_MIN; - return (curPlayingItem.priority); + return (currentSoundItem.priority); } bool CSoundSource::IsPlaying(const bool checkOpenAl) const @@ -288,7 +213,7 @@ bool CSoundSource::IsPlaying(const bool checkOpenAl) const if (asyncPlayItem.id != 0) return true; - if (curPlayingItem.id == 0) + if (currentSoundItem.id == 0) return false; // calling OpenAL has a high chance of generating a L2 cache miss, avoid if possible @@ -316,119 +241,96 @@ void CSoundSource::Stop() // ::StreamStop via AudioChannel::StreamStop (*) // AudioChannel::FindSourceAndPlay (*) if (sound != nullptr) - item = sound->GetSoundItem(curPlayingItem.id); + item = sound->GetSoundItem(currentSoundItem.id); if (item != nullptr) item->StopPlay(); - curPlayingItem = {}; + currentSoundItem = {}; } curStream.reset(); - if (curChannel != nullptr) { - IAudioChannel* oldChannel = curChannel; - curChannel = nullptr; + if (currentChannel != nullptr) { + IAudioChannel* oldChannel = currentChannel; + currentChannel = nullptr; oldChannel->SoundSourceFinished(this); } CheckError("CSoundSource::Stop"); } -void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) -{ - assert(!curStream); - assert(channel); +void CSoundSource::Initialize(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) { - if (!item->PlayNow()) - return; + Stop(); + currentVolume = volume; + currentSoundItem = { item->soundItemID, item->loopTime, item->priority, item->GetGain(), item->rolloff }; + currentChannel = channel; in3D = !relative && item->in3D; + bufferId = item->GetSoundBufferID(); - currentPosition = pos; - - name = item->name; + const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); + alSourcei(id, AL_BUFFER, itemBuffer.GetId()); - const SoundBuffer& itemBuffer = SoundBuffer::GetById(item->GetSoundBufferID()); + alSourcef(id, AL_GAIN, volume * item->GetGain() * channel->volume); + alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch); - Stop(); - - name = item->name; - curVolume = volume * item->GetGain() * channel->volume; - curPlayingItem = {item->soundItemID, item->loopTime, item->priority, item->GetGain(), item->rolloff}; - curChannel = channel; + velocity *= item->dopplerScale * ELMOS_TO_METERS; + alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z); + alSourcei(id, AL_LOOPING, (item->loopTime > 0) ? AL_TRUE : AL_FALSE); - alSourcei(id, AL_BUFFER, itemBuffer.GetId()); - alSourcef(id, AL_GAIN, curVolume); - alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch); + loopStop = spring_gettime() + spring_msecs(item->loopTime); - velocity *= item->dopplerScale * ELMOS_TO_METERS; - // alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z); - alSourcei(id, AL_LOOPING, (item->loopTime > 0) ? AL_TRUE : AL_FALSE); - - loopStop = spring_gettime() + spring_msecs(item->loopTime); + if (in3D) { + EnableSpatialization(); + } else { + DisableSpatialization(); + } +} - if (!in3D) { - if (efxEnabled) { - alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); - efxEnabled = false; - } - alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcef(id, AL_ROLLOFF_FACTOR, 0.f); - alSource3f(id, AL_POSITION, 0.0f, 0.0f, -1.0f * ELMOS_TO_METERS); -#if defined(__APPLE__) || defined(__OpenBSD__) - alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); -#endif - } else { +void CSoundSource::EnableSpatialization() { + const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); + // GENERIC if (itemBuffer.GetChannels() > 1) LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); - // if (efx.Enabled()) { - // efxEnabled = true; - // alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); - // alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); - // alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); - // efxUpdates = efx.updates; - // } - - alDopplerFactor(0); - - pos *= ELMOS_TO_METERS; - + // GENERIC alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + float3 pos = currentPosition * ELMOS_TO_METERS; alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); - alSourcef(id, AL_ROLLOFF_FACTOR, 0); - efx.Enable(); - if (attenuationFilter == 0) { - alGenFilters(1, &attenuationFilter); - alFilteri(attenuationFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); - } + if (configHandler->GetBool("snd_useAttenuationModel")) { - // need to set this here or else the filter state is incorrect when reusing a source - alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); - alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, 1); + if (attenuationFilter == 0) { + alGenFilters(1, &attenuationFilter); + alFilteri(attenuationFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + } - ComputeCameraSpaceData(); - ApplyGainBasedOnVisiblity(false); + alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, 1); - alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); + ApplyAttenuationModel(false); - //we should not use attenuation features as they will fight against the nature of an rts game - //it's necessary to calclulate the gain/filtering based on custom viewport related logic, not raw distance + } else { + if (efx.Enabled()) { + efxEnabled = true; + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); + efxUpdates = efx.updates; + } - // curHeightRolloffModifier = heightRolloffModifier; - // alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier); - // alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR); - // alSourcef(id, AL_MAX_DISTANCE, MAX_DISTANCE * ELMOS_TO_METERS); + curHeightRolloffModifier = heightRolloffModifier; + alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); + } #if defined(__APPLE__) || defined(__OpenBSD__) alSourcef(id, AL_MAX_DISTANCE, 1000000.0f); // Max distance is too small by default on my Mac... - ALfloat gain = channel->volume * item->GetGain() * volume; + ALfloat gain = GetSummedVolume(); if (gain > 1.0f) { // OpenAL on Mac cannot handle AL_GAIN > 1 well, so we will adjust settings to get the same output with AL_GAIN = 1. const ALint model = alGetInteger(AL_DISTANCE_MODEL); - const ALfloat rolloff = ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier; + const ALfloat rolloff = ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier; const ALfloat refDist = REFERENCE_DIST * ELMOS_TO_METERS; if ((model == AL_INVERSE_DISTANCE_CLAMPED) || (model == AL_INVERSE_DISTANCE)) { @@ -440,17 +342,43 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); } #endif +} - } +void CSoundSource::DisableSpatialization() { + alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcef(id, AL_ROLLOFF_FACTOR, 0.f); + alSource3f(id, AL_POSITION, 0.0f, 0.0f, -1.0f * ELMOS_TO_METERS); - alSourcePlay(id); +#if defined(__APPLE__) || defined(__OpenBSD__) + alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); +#endif - if (itemBuffer.GetId() == 0) - LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); + if (!efxEnabled) + return; - CheckError("CSoundSource::Play"); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); + efxEnabled = false; } +void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) +{ + assert(!curStream); + assert(channel); + + if (!item->PlayNow()) + return; + + Initialize(channel, item, pos, velocity, volume, relative); + + alSourcePlay(id); + + const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); + if (bufferId == 0) + LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); + + CheckError("CSoundSource::Play"); +} void CSoundSource::PlayAsync(IAudioChannel* channel, size_t id, float3 pos, float3 velocity, float volume, float priority, bool relative) { @@ -476,8 +404,8 @@ void CSoundSource::PlayStream(IAudioChannel* channel, const std::string& file, f curStream = std::make_unique (); // OpenAL params - curChannel = channel; - curVolume = volume; + currentChannel = channel; + currentVolume = volume; in3D = false; if (efxEnabled) { @@ -538,15 +466,15 @@ float CSoundSource::GetStreamPlayTime() void CSoundSource::UpdateVolume() { - if (curChannel == nullptr) + if (currentChannel == nullptr) return; if (curStream) { - alSourcef(id, AL_GAIN, curVolume * curChannel->volume); + alSourcef(id, AL_GAIN, currentVolume * currentChannel->volume); return; } - if (curPlayingItem.id != 0) { - alSourcef(id, AL_GAIN, curVolume * curPlayingItem.rndGain * curChannel->volume); + if (currentSoundItem.id != 0) { + alSourcef(id, AL_GAIN, currentVolume * currentSoundItem.volume * currentChannel->volume); return; } } diff --git a/rts/System/Sound/OpenAL/SoundSource.h b/rts/System/Sound/OpenAL/SoundSource.h index 81ce706f41f..966a42f0c2a 100644 --- a/rts/System/Sound/OpenAL/SoundSource.h +++ b/rts/System/Sound/OpenAL/SoundSource.h @@ -9,9 +9,10 @@ #include #include "System/Misc/SpringTime.h" +#include "System/Sound/IAudioChannel.h" +#include "System/Sound/ISoundAttenuationModel.h" #include "System/float3.h" -class IAudioChannel; class SoundItem; class MusicStream; @@ -32,10 +33,13 @@ class CSoundSource CSoundSource& operator = (CSoundSource&& src); CSoundSource& operator = (const CSoundSource& src) = delete; + static constexpr float ROLLOFF_FACTOR = 5.0f; + static constexpr float REFERENCE_DIST = 200.0f; + void Update(); void Delete(); - void ApplyGainBasedOnVisiblity(const bool smooth); + void ApplyAttenuationModel(const bool smooth); void UpdateVolume(); bool IsValid() const { return (id != 0); }; @@ -66,6 +70,14 @@ class CSoundSource private: void swap(CSoundSource& other); + void Initialize(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative); + void EnableSpatialization(); + void DisableSpatialization(); + + float GetSummedVolume() { + return (currentChannel ? currentChannel->volume : 1.0f) * currentSoundItem.volume * currentVolume; + } + struct AsyncSoundItemData { IAudioChannel* channel = nullptr; @@ -87,7 +99,7 @@ class CSoundSource unsigned int loopTime = 0; int priority = 0; - float rndGain = 0.0f; + float volume = 0.0f; float rolloff = 0.0f; }; @@ -108,17 +120,21 @@ class CSoundSource float2 viewportHalfExtents = float2(); float terrainDistance; + SoundAttenuationOutput attenuationOutput; + + float bufferId = 0; + float cameraZoom; float3 currentPosition = float3(0.0f, 0.0f, 0.0f); - SoundItemData curPlayingItem; + SoundItemData currentSoundItem; AsyncSoundItemData asyncPlayItem; - IAudioChannel* curChannel = nullptr; + IAudioChannel* currentChannel = nullptr; std::unique_ptr curStream; - float curVolume = 1.0f; + float currentVolume = 1.0f; float curViewportVolumeMultiplier = 0; spring_time loopStop {1e9}; @@ -133,3 +149,4 @@ class CSoundSource }; #endif + From e076320bad3bfb2753a225eb3561cd8f78456246 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:27:09 +0200 Subject: [PATCH 04/20] Small fixes --- rts/Game/Camera.cpp | 2 + .../AttenuationModels/RtsAttenuationModel.cpp | 3 + rts/System/Sound/ISound.h | 4 +- rts/System/Sound/OpenAL/Sound.cpp | 5 +- rts/System/Sound/OpenAL/Sound.h | 2 +- rts/System/Sound/OpenAL/SoundSource.cpp | 138 +++++++++++------- 6 files changed, 91 insertions(+), 63 deletions(-) diff --git a/rts/Game/Camera.cpp b/rts/Game/Camera.cpp index 7e59ca14e7a..b803215738e 100644 --- a/rts/Game/Camera.cpp +++ b/rts/Game/Camera.cpp @@ -157,6 +157,8 @@ void CCamera::Update(const UpdateParams& p) if (p.updateFrustum) UpdateFrustum(); + TraceToTerrain(); + LoadMatrices(); // not done here // LoadViewPort(); diff --git a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp index 85fb352de0e..3653a835928 100644 --- a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp +++ b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp @@ -3,6 +3,7 @@ #include "RtsAttenuationModel.h" #include "Game/Camera.h" #include "Game/CameraHandler.h" +#include "System/Log/ILog.h" #include "System/Sound/ISoundAttenuationModel.h" #include "Game/TraceRay.h" #include @@ -12,6 +13,8 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput SoundAttenuationOutput out; + // LOG_L(L_NOTICE, "Evaluating %s", __FILE_NAME__); + // ----------------------------------------------------------------- // Convert the soundPosition in to camera space and calculate necessary data // ----------------------------------------------------------------- diff --git a/rts/System/Sound/ISound.h b/rts/System/Sound/ISound.h index b554e6a15d5..1ca0900b84b 100644 --- a/rts/System/Sound/ISound.h +++ b/rts/System/Sound/ISound.h @@ -82,9 +82,7 @@ class ISound { static bool IsNullAudio(); public: - ISoundAttenuationModel* GetAttenuationModel() { return attenuationModel; } -private: - ISoundAttenuationModel* attenuationModel; + virtual ISoundAttenuationModel* GetAttenuationModel() { return nullptr; } }; #define sound ISound::GetInstance() diff --git a/rts/System/Sound/OpenAL/Sound.cpp b/rts/System/Sound/OpenAL/Sound.cpp index 9a340aff848..68476f27f2a 100644 --- a/rts/System/Sound/OpenAL/Sound.cpp +++ b/rts/System/Sound/OpenAL/Sound.cpp @@ -53,16 +53,13 @@ spring::recursive_mutex soundMutex; CSound::CSound() { configHandler->NotifyOnChange(this, {"snd_volmaster", "snd_eaxpreset", "snd_filter", "UseEFX", "snd_volgeneral", "snd_volunitreply", "snd_volbattle", "snd_volui", "snd_volmusic", "PitchAdjust"}); - attenuationModel = new RtsAttenuationModel(); } CSound::~CSound() { configHandler->RemoveObserver(this); - delete attenuationModel; } - void CSound::Init() { std::lock_guard lck(soundMutex); @@ -83,6 +80,8 @@ void CSound::Init() updateListener = false; soundThreadQuit = false; canLoadDefs = false; + + attenuationModel = new RtsAttenuationModel(); } { Channels::General->SetVolume(configHandler->GetInt("snd_volgeneral") * 0.01f); diff --git a/rts/System/Sound/OpenAL/Sound.h b/rts/System/Sound/OpenAL/Sound.h index f9f3e00d994..4e8d7d888b9 100644 --- a/rts/System/Sound/OpenAL/Sound.h +++ b/rts/System/Sound/OpenAL/Sound.h @@ -77,7 +77,7 @@ class CSound : public ISound std::vector GetSoundDevices() override; - ISoundAttenuationModel* GetAttenuationModel() { return attenuationModel; } + ISoundAttenuationModel* GetAttenuationModel() override { return attenuationModel; } private: typedef spring::unordered_map SoundItemNameMap; diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 5df97b69791..bd2e75ee972 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -18,6 +18,7 @@ #include "Game/TraceRay.h" #include "Rendering/GlobalRendering.h" #include "System/Config/ConfigHandler.h" +#include "System/Log/ILog.h" #include "System/Misc/SpringTime.h" #include "System/Sound/OpenAL/EFXfuncs.h" #include "System/Sound/SoundLog.h" @@ -76,7 +77,8 @@ CSoundSource::CSoundSource(CSoundSource&& src) this->swap(src); } -CSoundSource& CSoundSource::operator = (CSoundSource&& src) { +CSoundSource& CSoundSource::operator = (CSoundSource&& src) +{ this->swap(src); return *this; } @@ -92,16 +94,28 @@ float SmoothTowards(float current, float target, float speed, float dt) return current + (target - current) * t; } -float Curve(float t, float min, float max, float k) { +float Curve(float t, float min, float max, float k) +{ return min + (max - min) * std::pow(t, k); } -void CSoundSource::ApplyAttenuationModel(bool smooth) { +void CSoundSource::ApplyAttenuationModel(bool smooth) +{ + if (!in3D) + return; - if (sound != nullptr) + if (sound == nullptr) { + LOG_L(L_ERROR, "Could not get sound singleton"); return; + } + + if (sound->GetAttenuationModel() == nullptr) { + LOG_L(L_ERROR, "Could not get attenuation model"); + return; + } attenuationOutput = sound->GetAttenuationModel()->Evaluate({ currentPosition }); + float totalValue = attenuationOutput.totalFactor; if (smooth) @@ -117,7 +131,10 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) { vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3); - alSourcef(id, AL_GAIN, vol); + if (name.contains("sizzle")) + LOG_L(L_NOTICE, "val: %f, vol: %f", totalValue, vol); + + // alSourcef(id, AL_GAIN, vol); float filter = 1; @@ -126,10 +143,10 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) { filter = Curve(factor, 0.1f, 1.0f, 0.75f); } - alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); - alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); - - alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); + // alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); + // alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); + // + // alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); } void CSoundSource::Update() @@ -258,8 +275,8 @@ void CSoundSource::Stop() CheckError("CSoundSource::Stop"); } -void CSoundSource::Initialize(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) { - +void CSoundSource::Initialize(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) +{ Stop(); currentVolume = volume; @@ -271,7 +288,11 @@ void CSoundSource::Initialize(IAudioChannel* channel, SoundItem* item, float3 po const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); alSourcei(id, AL_BUFFER, itemBuffer.GetId()); - alSourcef(id, AL_GAIN, volume * item->GetGain() * channel->volume); + float vol = GetSummedVolume(); + + LOG_L(L_NOTICE, "name: %s, vol: %f", item->name.c_str(), vol); + + alSourcef(id, AL_GAIN, vol); alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch); velocity *= item->dopplerScale * ELMOS_TO_METERS; @@ -287,64 +308,69 @@ void CSoundSource::Initialize(IAudioChannel* channel, SoundItem* item, float3 po } } -void CSoundSource::EnableSpatialization() { +void CSoundSource::EnableSpatialization() +{ const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); - // GENERIC - if (itemBuffer.GetChannels() > 1) - LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); - // GENERIC - alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); - float3 pos = currentPosition * ELMOS_TO_METERS; - alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + if (itemBuffer.GetChannels() > 1) + LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); - if (configHandler->GetBool("snd_useAttenuationModel")) { + // alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + // float3 pos = currentPosition * ELMOS_TO_METERS; + // alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); - if (attenuationFilter == 0) { - alGenFilters(1, &attenuationFilter); - alFilteri(attenuationFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); - } + if (configHandler->GetBool("snd_useAttenuationModel")) { - alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); - alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, 1); + if (attenuationFilter == 0) { + alGenFilters(1, &attenuationFilter); + alFilteri(attenuationFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + } - ApplyAttenuationModel(false); + alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, 1); - } else { - if (efx.Enabled()) { - efxEnabled = true; - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); - alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); - efxUpdates = efx.updates; - } + ApplyAttenuationModel(false); - curHeightRolloffModifier = heightRolloffModifier; - alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); + } else { + if (efx.Enabled()) { + efxEnabled = true; + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); + efxUpdates = efx.updates; } + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + float3 pos = currentPosition * ELMOS_TO_METERS; + alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + + curHeightRolloffModifier = heightRolloffModifier; + alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); + } + #if defined(__APPLE__) || defined(__OpenBSD__) - alSourcef(id, AL_MAX_DISTANCE, 1000000.0f); - // Max distance is too small by default on my Mac... - ALfloat gain = GetSummedVolume(); - if (gain > 1.0f) { - // OpenAL on Mac cannot handle AL_GAIN > 1 well, so we will adjust settings to get the same output with AL_GAIN = 1. - const ALint model = alGetInteger(AL_DISTANCE_MODEL); - const ALfloat rolloff = ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier; - const ALfloat refDist = REFERENCE_DIST * ELMOS_TO_METERS; - - if ((model == AL_INVERSE_DISTANCE_CLAMPED) || (model == AL_INVERSE_DISTANCE)) { - alSourcef(id, AL_REFERENCE_DISTANCE, ((gain - 1.0f) * refDist / rolloff) + refDist); - alSourcef(id, AL_ROLLOFF_FACTOR, (gain + rolloff - 1.0f) / gain); - alSourcef(id, AL_GAIN, 1.0f); - } - } else { - alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); - } + alSourcef(id, AL_MAX_DISTANCE, 1000000.0f); + // Max distance is too small by default on my Mac... + ALfloat gain = GetSummedVolume(); + if (gain > 1.0f) { + // OpenAL on Mac cannot handle AL_GAIN > 1 well, so we will adjust settings to get the same output with AL_GAIN = 1. + const ALint model = alGetInteger(AL_DISTANCE_MODEL); + const ALfloat rolloff = ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier; + const ALfloat refDist = REFERENCE_DIST * ELMOS_TO_METERS; + + if ((model == AL_INVERSE_DISTANCE_CLAMPED) || (model == AL_INVERSE_DISTANCE)) { + alSourcef(id, AL_REFERENCE_DISTANCE, ((gain - 1.0f) * refDist / rolloff) + refDist); + alSourcef(id, AL_ROLLOFF_FACTOR, (gain + rolloff - 1.0f) / gain); + alSourcef(id, AL_GAIN, 1.0f); + } + } else { + alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); + } #endif } -void CSoundSource::DisableSpatialization() { +void CSoundSource::DisableSpatialization() +{ alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); alSourcef(id, AL_ROLLOFF_FACTOR, 0.f); alSource3f(id, AL_POSITION, 0.0f, 0.0f, -1.0f * ELMOS_TO_METERS); From 3197b482d3058ee5ec0c5acb73e91094faf6c010 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:50:45 +0200 Subject: [PATCH 05/20] Fixed missing position assignment --- rts/System/Sound/OpenAL/SoundSource.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index bd2e75ee972..dc20add2dff 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -284,6 +284,7 @@ void CSoundSource::Initialize(IAudioChannel* channel, SoundItem* item, float3 po currentChannel = channel; in3D = !relative && item->in3D; bufferId = item->GetSoundBufferID(); + currentPosition = pos; const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); alSourcei(id, AL_BUFFER, itemBuffer.GetId()); From 280fe12f7a55b364f0fe90acb30939a6b9a864d3 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:51:07 +0200 Subject: [PATCH 06/20] Moved some source sets --- rts/System/Sound/OpenAL/SoundSource.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index dc20add2dff..99fee1bc688 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -316,9 +316,9 @@ void CSoundSource::EnableSpatialization() if (itemBuffer.GetChannels() > 1) LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); - // alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); - // float3 pos = currentPosition * ELMOS_TO_METERS; - // alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + float3 pos = currentPosition * ELMOS_TO_METERS; + alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); if (configHandler->GetBool("snd_useAttenuationModel")) { @@ -341,10 +341,6 @@ void CSoundSource::EnableSpatialization() efxUpdates = efx.updates; } - alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); - float3 pos = currentPosition * ELMOS_TO_METERS; - alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); - curHeightRolloffModifier = heightRolloffModifier; alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); } From ec6435e7f01405ad5a9552f5803fde4169edae59 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 10:21:39 +0200 Subject: [PATCH 07/20] Reverted SoundSource to original working state Will use this to add the new incremental changes --- rts/System/Sound/OpenAL/SoundSource.cpp | 301 ++++++++---------------- 1 file changed, 93 insertions(+), 208 deletions(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 99fee1bc688..9f40517c334 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -2,39 +2,28 @@ #include "SoundSource.h" -#include -#include #include #include -#include -#include -#include #include "ALShared.h" -#include "Game/Camera.h" #include "EFX.h" -#include "Game/Camera.h" -#include "Game/CameraHandler.h" -#include "Game/TraceRay.h" -#include "Rendering/GlobalRendering.h" -#include "System/Config/ConfigHandler.h" -#include "System/Log/ILog.h" -#include "System/Misc/SpringTime.h" -#include "System/Sound/OpenAL/EFXfuncs.h" -#include "System/Sound/SoundLog.h" #include "System/Sound/IAudioChannel.h" #include "MusicStream.h" +#include "System/Sound/SoundLog.h" #include "SoundBuffer.h" #include "SoundItem.h" -#include -#include - - #include "Sound.h" //remove when unified ElmoInMeters #include "Sim/Misc/GlobalConstants.h" #include "System/float3.h" +#include "System/StringUtil.h" +#include "System/SpringMath.h" + + +static constexpr float ROLLOFF_FACTOR = 5.0f; +static constexpr float REFERENCE_DIST = 200.0f; + // used to adjust the pitch to the GameSpeed (optional) float CSoundSource::globalPitch = 1.0f; @@ -44,7 +33,6 @@ float CSoundSource::heightRolloffModifier = 1.0f; void CSoundSource::swap(CSoundSource& r) { - //TODO makesure all data here is swapped std::swap(id, r.id); std::swap(currentChannel, r.currentChannel); std::swap(curStream, r.curStream); @@ -66,19 +54,19 @@ CSoundSource::CSoundSource() if (!CheckError("CSoundSource::CSoundSource")) { id = 0; } else { - // alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); + alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); CheckError("CSoundSource::CSoundSource"); } } + CSoundSource::CSoundSource(CSoundSource&& src) { // can't use naive/default move because `id` member has to be unique this->swap(src); } -CSoundSource& CSoundSource::operator = (CSoundSource&& src) -{ +CSoundSource& CSoundSource::operator = (CSoundSource&& src) { this->swap(src); return *this; } @@ -88,67 +76,6 @@ CSoundSource::~CSoundSource() Delete(); } -float SmoothTowards(float current, float target, float speed, float dt) -{ - const float t = 1.0f - std::exp(-speed * dt); - return current + (target - current) * t; -} - -float Curve(float t, float min, float max, float k) -{ - return min + (max - min) * std::pow(t, k); -} - -void CSoundSource::ApplyAttenuationModel(bool smooth) -{ - if (!in3D) - return; - - if (sound == nullptr) { - LOG_L(L_ERROR, "Could not get sound singleton"); - return; - } - - if (sound->GetAttenuationModel() == nullptr) { - LOG_L(L_ERROR, "Could not get attenuation model"); - return; - } - - attenuationOutput = sound->GetAttenuationModel()->Evaluate({ currentPosition }); - - float totalValue = attenuationOutput.totalFactor; - - if (smooth) - curViewportVolumeMultiplier = SmoothTowards( - curViewportVolumeMultiplier, - totalValue, - VIEWPORT_VOLUME_REDUCTION_SPEED, - globalRendering->lastFrameTime); - else - curViewportVolumeMultiplier = totalValue; - - float vol = currentVolume; - - vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3); - - if (name.contains("sizzle")) - LOG_L(L_NOTICE, "val: %f, vol: %f", totalValue, vol); - - // alSourcef(id, AL_GAIN, vol); - - float filter = 1; - - if (curViewportVolumeMultiplier <= 1.0) { - float factor = std::min(curViewportVolumeMultiplier / 1.0f, 1.0f); - filter = Curve(factor, 0.1f, 1.0f, 0.75f); - } - - // alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); - // alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); - // - // alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); -} - void CSoundSource::Update() { if (asyncPlayItem.id != 0) { @@ -158,22 +85,18 @@ void CSoundSource::Update() } if (currentSoundItem.id != 0) { - if (configHandler->GetBool("snd_useAttenuationModel")) { - ApplyAttenuationModel(true); - } else { - if (in3D && (efxEnabled != efx.Enabled())) { - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); - alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); - efxEnabled = efx.Enabled(); - efxUpdates = efx.updates; - } - - if (heightRolloffModifier != curHeightRolloffModifier) { - curHeightRolloffModifier = heightRolloffModifier; - alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); - } - } + if (in3D && (efxEnabled != efx.Enabled())) { + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); + efxEnabled = efx.Enabled(); + efxUpdates = efx.updates; + } + + if (heightRolloffModifier != curHeightRolloffModifier) { + curHeightRolloffModifier = heightRolloffModifier; + alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); + } if (!IsPlaying(true) || ((currentSoundItem.loopTime > 0) && (spring_gettime() > loopStop))) Stop(); @@ -188,7 +111,7 @@ void CSoundSource::Update() } } - if (!configHandler->GetBool("snd_useAttenuationModel") && efxEnabled && (efxUpdates != efx.updates)) { + if (efxEnabled && (efxUpdates != efx.updates)) { // airAbsorption & LowPass aren't auto updated by OpenAL on change, so we need to do it per source alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); @@ -244,6 +167,7 @@ bool CSoundSource::IsPlaying(const bool checkOpenAl) const return (state == AL_PLAYING); } + void CSoundSource::Stop() { alSourceStop(id); @@ -275,134 +199,95 @@ void CSoundSource::Stop() CheckError("CSoundSource::Stop"); } -void CSoundSource::Initialize(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) -{ - Stop(); - - currentVolume = volume; - currentSoundItem = { item->soundItemID, item->loopTime, item->priority, item->GetGain(), item->rolloff }; - currentChannel = channel; - in3D = !relative && item->in3D; - bufferId = item->GetSoundBufferID(); - currentPosition = pos; - - const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); - alSourcei(id, AL_BUFFER, itemBuffer.GetId()); - - float vol = GetSummedVolume(); - - LOG_L(L_NOTICE, "name: %s, vol: %f", item->name.c_str(), vol); - - alSourcef(id, AL_GAIN, vol); - alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch); - - velocity *= item->dopplerScale * ELMOS_TO_METERS; - alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z); - alSourcei(id, AL_LOOPING, (item->loopTime > 0) ? AL_TRUE : AL_FALSE); - - loopStop = spring_gettime() + spring_msecs(item->loopTime); - - if (in3D) { - EnableSpatialization(); - } else { - DisableSpatialization(); - } -} - -void CSoundSource::EnableSpatialization() +void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) { - const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); + assert(!curStream); + assert(channel); - if (itemBuffer.GetChannels() > 1) - LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); - - alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); - float3 pos = currentPosition * ELMOS_TO_METERS; - alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + if (!item->PlayNow()) + return; - if (configHandler->GetBool("snd_useAttenuationModel")) { + const SoundBuffer& itemBuffer = SoundBuffer::GetById(item->GetSoundBufferID()); - if (attenuationFilter == 0) { - alGenFilters(1, &attenuationFilter); - alFilteri(attenuationFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); - } + Stop(); - alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); - alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, 1); + currentVolume = volume; + currentSoundItem = {item->soundItemID, item->loopTime, item->priority, item->GetGain(), item->rolloff}; + currentChannel = channel; - ApplyAttenuationModel(false); + alSourcei(id, AL_BUFFER, itemBuffer.GetId()); + alSourcef(id, AL_GAIN, volume * item->GetGain() * channel->volume); + alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch); - } else { - if (efx.Enabled()) { - efxEnabled = true; - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); - alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); - efxUpdates = efx.updates; - } + velocity *= item->dopplerScale * ELMOS_TO_METERS; + alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z); + alSourcei(id, AL_LOOPING, (item->loopTime > 0) ? AL_TRUE : AL_FALSE); - curHeightRolloffModifier = heightRolloffModifier; - alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); - } + loopStop = spring_gettime() + spring_msecs(item->loopTime); + if (relative || !item->in3D) { + in3D = false; + if (efxEnabled) { + alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); + efxEnabled = false; + } + alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcef(id, AL_ROLLOFF_FACTOR, 0.f); + alSource3f(id, AL_POSITION, 0.0f, 0.0f, -1.0f * ELMOS_TO_METERS); #if defined(__APPLE__) || defined(__OpenBSD__) - alSourcef(id, AL_MAX_DISTANCE, 1000000.0f); - // Max distance is too small by default on my Mac... - ALfloat gain = GetSummedVolume(); - if (gain > 1.0f) { - // OpenAL on Mac cannot handle AL_GAIN > 1 well, so we will adjust settings to get the same output with AL_GAIN = 1. - const ALint model = alGetInteger(AL_DISTANCE_MODEL); - const ALfloat rolloff = ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier; - const ALfloat refDist = REFERENCE_DIST * ELMOS_TO_METERS; - - if ((model == AL_INVERSE_DISTANCE_CLAMPED) || (model == AL_INVERSE_DISTANCE)) { - alSourcef(id, AL_REFERENCE_DISTANCE, ((gain - 1.0f) * refDist / rolloff) + refDist); - alSourcef(id, AL_ROLLOFF_FACTOR, (gain + rolloff - 1.0f) / gain); - alSourcef(id, AL_GAIN, 1.0f); - } - } else { - alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); - } + alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); #endif -} + } else { + if (itemBuffer.GetChannels() > 1) + LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); + + in3D = true; + if (efx.Enabled()) { + efxEnabled = true; + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); + efxUpdates = efx.updates; + } + + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + pos *= ELMOS_TO_METERS; + alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + curHeightRolloffModifier = heightRolloffModifier; + alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier); -void CSoundSource::DisableSpatialization() -{ - alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcef(id, AL_ROLLOFF_FACTOR, 0.f); - alSource3f(id, AL_POSITION, 0.0f, 0.0f, -1.0f * ELMOS_TO_METERS); #if defined(__APPLE__) || defined(__OpenBSD__) - alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); + alSourcef(id, AL_MAX_DISTANCE, 1000000.0f); + // Max distance is too small by default on my Mac... + ALfloat gain = channel->volume * item->GetGain() * volume; + if (gain > 1.0f) { + // OpenAL on Mac cannot handle AL_GAIN > 1 well, so we will adjust settings to get the same output with AL_GAIN = 1. + const ALint model = alGetInteger(AL_DISTANCE_MODEL); + const ALfloat rolloff = ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier; + const ALfloat refDist = REFERENCE_DIST * ELMOS_TO_METERS; + + if ((model == AL_INVERSE_DISTANCE_CLAMPED) || (model == AL_INVERSE_DISTANCE)) { + alSourcef(id, AL_REFERENCE_DISTANCE, ((gain - 1.0f) * refDist / rolloff) + refDist); + alSourcef(id, AL_ROLLOFF_FACTOR, (gain + rolloff - 1.0f) / gain); + alSourcef(id, AL_GAIN, 1.0f); + } + } else { + alSourcef(id, AL_REFERENCE_DISTANCE, REFERENCE_DIST * ELMOS_TO_METERS); + } #endif - if (!efxEnabled) - return; - - alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); - efxEnabled = false; -} - -void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) -{ - assert(!curStream); - assert(channel); - - if (!item->PlayNow()) - return; - - Initialize(channel, item, pos, velocity, volume, relative); - + } alSourcePlay(id); - const SoundBuffer& itemBuffer = SoundBuffer::GetById(bufferId); - if (bufferId == 0) - LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); + if (itemBuffer.GetId() == 0) + LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); - CheckError("CSoundSource::Play"); + CheckError("CSoundSource::Play"); } + void CSoundSource::PlayAsync(IAudioChannel* channel, size_t id, float3 pos, float3 velocity, float volume, float priority, bool relative) { asyncPlayItem.channel = channel; From 6f492d311b79fe54e63c710e5550334e1c42a185 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 10:42:38 +0200 Subject: [PATCH 08/20] Impemented useAttenuationModel --- rts/System/Sound/OpenAL/SoundSource.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rts/System/Sound/OpenAL/SoundSource.h b/rts/System/Sound/OpenAL/SoundSource.h index 966a42f0c2a..2b623513e94 100644 --- a/rts/System/Sound/OpenAL/SoundSource.h +++ b/rts/System/Sound/OpenAL/SoundSource.h @@ -8,6 +8,7 @@ #include +#include "System/Config/ConfigHandler.h" #include "System/Misc/SpringTime.h" #include "System/Sound/IAudioChannel.h" #include "System/Sound/ISoundAttenuationModel.h" @@ -146,6 +147,8 @@ class CSoundSource std::string name; ALfloat curHeightRolloffModifier = 1.0f; + + bool useAttenuationModel = configHandler->GetBool("snd_useAttenuationModel"); }; #endif From b2f58d5b3afcffaeaef7409d838f790b8a8080b6 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 10:42:47 +0200 Subject: [PATCH 09/20] Moved position setting --- rts/System/Sound/OpenAL/SoundSource.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 9f40517c334..ba5508c3b96 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -242,6 +242,10 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo if (itemBuffer.GetChannels() > 1) LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); + pos *= ELMOS_TO_METERS; + alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); + in3D = true; if (efx.Enabled()) { efxEnabled = true; @@ -251,9 +255,6 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo efxUpdates = efx.updates; } - alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); - pos *= ELMOS_TO_METERS; - alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); curHeightRolloffModifier = heightRolloffModifier; alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier); From 84f402910b17c6dc564b0b01b617884afaf84b79 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 12:01:23 +0200 Subject: [PATCH 10/20] Fixed missing assignement of model output --- rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp index 3653a835928..8f32e259827 100644 --- a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp +++ b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp @@ -13,8 +13,6 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput SoundAttenuationOutput out; - // LOG_L(L_NOTICE, "Evaluating %s", __FILE_NAME__); - // ----------------------------------------------------------------- // Convert the soundPosition in to camera space and calculate necessary data // ----------------------------------------------------------------- @@ -53,8 +51,6 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput // Convert all the positional data to normalized ranges and calculate the final range // ----------------------------------------------------------------- - float totalFactor; - { // Calculate forward attenuation float forwardValue = out.forwardDistance >= 0 ? @@ -73,7 +69,7 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput float innerValue = 1.0f - t * (OFFCENTER_ATTENUATION_STRENGTH * out.zoomFactor); // Combine all attenuation factors - totalFactor = forwardValue * outerValue * innerValue; + out.totalFactor = forwardValue * outerValue * innerValue; } return out; From 2b782d32af3daadfaa6319942c31a58108c10aed Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 12:01:49 +0200 Subject: [PATCH 11/20] Fixed incorrect usage of useAttenuationModel --- rts/System/Sound/OpenAL/SoundSource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.h b/rts/System/Sound/OpenAL/SoundSource.h index 2b623513e94..8038bb82052 100644 --- a/rts/System/Sound/OpenAL/SoundSource.h +++ b/rts/System/Sound/OpenAL/SoundSource.h @@ -148,7 +148,7 @@ class CSoundSource ALfloat curHeightRolloffModifier = 1.0f; - bool useAttenuationModel = configHandler->GetBool("snd_useAttenuationModel"); + bool UseAttenuationModel() { return configHandler->GetBool("snd_useAttenuationModel"); } }; #endif From da822deffb832c7042dd1a072e8e71beb93dfe48 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 12:02:10 +0200 Subject: [PATCH 12/20] Began reimplementing the model incrementally --- rts/System/Sound/OpenAL/SoundSource.cpp | 140 ++++++++++++++++++------ 1 file changed, 108 insertions(+), 32 deletions(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index ba5508c3b96..576c8814de6 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -7,8 +7,10 @@ #include "ALShared.h" #include "EFX.h" +#include "Rendering/GlobalRendering.h" #include "System/Sound/IAudioChannel.h" #include "MusicStream.h" +#include "System/Sound/OpenAL/EFXfuncs.h" #include "System/Sound/SoundLog.h" #include "SoundBuffer.h" #include "SoundItem.h" @@ -20,11 +22,9 @@ #include "System/StringUtil.h" #include "System/SpringMath.h" - static constexpr float ROLLOFF_FACTOR = 5.0f; static constexpr float REFERENCE_DIST = 200.0f; - // used to adjust the pitch to the GameSpeed (optional) float CSoundSource::globalPitch = 1.0f; @@ -59,7 +59,6 @@ CSoundSource::CSoundSource() } } - CSoundSource::CSoundSource(CSoundSource&& src) { // can't use naive/default move because `id` member has to be unique @@ -76,6 +75,69 @@ CSoundSource::~CSoundSource() Delete(); } +float SmoothTowards(float current, float target, float speed, float dt) +{ + const float t = 1.0f - std::exp(-speed * dt); + return current + (target - current) * t; +} + +float Curve(float t, float min, float max, float k) +{ + return min + (max - min) * std::pow(t, k); +} + +void CSoundSource::ApplyAttenuationModel(bool smooth) +{ + if (!in3D) + return; + + if (sound == nullptr) { + LOG_L(L_ERROR, "Could not get sound singleton"); + return; + } + + if (sound->GetAttenuationModel() == nullptr) { + LOG_L(L_ERROR, "Could not get attenuation model"); + return; + } + + attenuationOutput = sound->GetAttenuationModel()->Evaluate({ currentPosition }); + + float totalValue = attenuationOutput.totalFactor; + + if (smooth) + curViewportVolumeMultiplier = SmoothTowards( + curViewportVolumeMultiplier, + totalValue, + VIEWPORT_VOLUME_REDUCTION_SPEED, + globalRendering->lastFrameTime); + else + curViewportVolumeMultiplier = totalValue; + + float vol = GetSummedVolume(); + + vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3); + + if (name.contains("sizzle")) + LOG_L(L_NOTICE, "val: %f, vol: %f", totalValue, vol); + + alSourcef(id, AL_GAIN, vol); + + float filter = 1; + + if (curViewportVolumeMultiplier <= 1.0) { + float factor = std::min(curViewportVolumeMultiplier / 1.0f, 1.0f); + filter = Curve(factor, 0.1f, 1.0f, 0.75f); + } + + LOG_L(L_NOTICE, "name: %s, output: %f, vol: %f", name.c_str(), totalValue, vol); + + alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); + + alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); +} + void CSoundSource::Update() { if (asyncPlayItem.id != 0) { @@ -85,18 +147,22 @@ void CSoundSource::Update() } if (currentSoundItem.id != 0) { - if (in3D && (efxEnabled != efx.Enabled())) { - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); - alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); - efxEnabled = efx.Enabled(); - efxUpdates = efx.updates; - } - - if (heightRolloffModifier != curHeightRolloffModifier) { - curHeightRolloffModifier = heightRolloffModifier; - alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); - } + if (UseAttenuationModel()) { + ApplyAttenuationModel(true); + } else { + if (in3D && (efxEnabled != efx.Enabled())) { + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx.Enabled()) ? efx.GetAirAbsorptionFactor() : 0); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx.Enabled()) ? efx.sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, (efx.Enabled()) ? efx.sfxFilter : AL_FILTER_NULL); + efxEnabled = efx.Enabled(); + efxUpdates = efx.updates; + } + + if (heightRolloffModifier != curHeightRolloffModifier) { + curHeightRolloffModifier = heightRolloffModifier; + alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * currentSoundItem.rolloff * heightRolloffModifier); + } + } if (!IsPlaying(true) || ((currentSoundItem.loopTime > 0) && (spring_gettime() > loopStop))) Stop(); @@ -111,7 +177,7 @@ void CSoundSource::Update() } } - if (efxEnabled && (efxUpdates != efx.updates)) { + if (!UseAttenuationModel() && efxEnabled && (efxUpdates != efx.updates)) { // airAbsorption & LowPass aren't auto updated by OpenAL on change, so we need to do it per source alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); @@ -167,7 +233,6 @@ bool CSoundSource::IsPlaying(const bool checkOpenAl) const return (state == AL_PLAYING); } - void CSoundSource::Stop() { alSourceStop(id); @@ -212,8 +277,17 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo Stop(); currentVolume = volume; - currentSoundItem = {item->soundItemID, item->loopTime, item->priority, item->GetGain(), item->rolloff}; + currentSoundItem = { + item->soundItemID, + item->loopTime, + item->priority, + item->GetGain(), + item->rolloff + }; currentChannel = channel; + currentPosition = pos; + + name = item->name; alSourcei(id, AL_BUFFER, itemBuffer.GetId()); alSourcef(id, AL_GAIN, volume * item->GetGain() * channel->volume); @@ -242,22 +316,26 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo if (itemBuffer.GetChannels() > 1) LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.", itemBuffer.GetFilename().c_str()); + in3D = true; + alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE); pos *= ELMOS_TO_METERS; alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); - in3D = true; - if (efx.Enabled()) { - efxEnabled = true; - alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); - alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); - efxUpdates = efx.updates; - } - - curHeightRolloffModifier = heightRolloffModifier; - alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier); - + if (UseAttenuationModel()) { + ApplyAttenuationModel(false); + } else { + if (efx.Enabled()) { + efxEnabled = true; + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx.GetAirAbsorptionFactor()); + alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx.sfxSlot, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, efx.sfxFilter); + efxUpdates = efx.updates; + } + + curHeightRolloffModifier = heightRolloffModifier; + alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier); + } #if defined(__APPLE__) || defined(__OpenBSD__) alSourcef(id, AL_MAX_DISTANCE, 1000000.0f); @@ -288,7 +366,6 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo CheckError("CSoundSource::Play"); } - void CSoundSource::PlayAsync(IAudioChannel* channel, size_t id, float3 pos, float3 velocity, float volume, float priority, bool relative) { asyncPlayItem.channel = channel; @@ -303,7 +380,6 @@ void CSoundSource::PlayAsync(IAudioChannel* channel, size_t id, float3 pos, floa asyncPlayItem.relative = relative; } - void CSoundSource::PlayStream(IAudioChannel* channel, const std::string& file, float volume) { // stop any current playback From 4f37bdfcd20832da026a0e3757d855c88ed481a2 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:49:15 +0200 Subject: [PATCH 13/20] Fixed physics based attenuation fighting new one --- rts/System/Sound/OpenAL/SoundSource.cpp | 201 +++++++++++++----------- rts/System/Sound/OpenAL/SoundSource.h | 4 +- 2 files changed, 114 insertions(+), 91 deletions(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 576c8814de6..03a96cbabb5 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -2,6 +2,7 @@ #include "SoundSource.h" +#include #include #include @@ -19,8 +20,6 @@ #include "Sim/Misc/GlobalConstants.h" #include "System/float3.h" -#include "System/StringUtil.h" -#include "System/SpringMath.h" static constexpr float ROLLOFF_FACTOR = 5.0f; static constexpr float REFERENCE_DIST = 200.0f; @@ -33,6 +32,7 @@ float CSoundSource::heightRolloffModifier = 1.0f; void CSoundSource::swap(CSoundSource& r) { + LOG_L(L_WARNING, "SWAP"); std::swap(id, r.id); std::swap(currentChannel, r.currentChannel); std::swap(curStream, r.curStream); @@ -114,13 +114,13 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) else curViewportVolumeMultiplier = totalValue; - float vol = GetSummedVolume(); + //TODO currentSoundItem.randomVolume is quite overtuned on many things (0.35 for example) + // new system will treat volume as a normalized range (0-1) so 0.35 is like 35% variable volume which is ridiculous + // for now, just ignore the randomVolume in the calculations - vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3); - - if (name.contains("sizzle")) - LOG_L(L_NOTICE, "val: %f, vol: %f", totalValue, vol); + float vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3) * currentChannel->GetVolume() * currentVolume; + efx.Enabled(); alSourcef(id, AL_GAIN, vol); float filter = 1; @@ -130,8 +130,6 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) filter = Curve(factor, 0.1f, 1.0f, 0.75f); } - LOG_L(L_NOTICE, "name: %s, output: %f, vol: %f", name.c_str(), totalValue, vol); - alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); @@ -185,85 +183,6 @@ void CSoundSource::Update() } } -void CSoundSource::Delete() -{ - if (efxEnabled) { - alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); - alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); - } - - Stop(); - alDeleteSources(1, &id); - CheckError("CSoundSource::Delete"); -} - -int CSoundSource::GetCurrentPriority() const -{ - if (asyncPlayItem.id != 0) - return asyncPlayItem.priority; - - if (curStream) - return INT_MAX; - - if (currentSoundItem.id == 0) - return INT_MIN; - - return (currentSoundItem.priority); -} - -bool CSoundSource::IsPlaying(const bool checkOpenAl) const -{ - if (curStream) - return true; - - if (asyncPlayItem.id != 0) - return true; - - if (currentSoundItem.id == 0) - return false; - - // calling OpenAL has a high chance of generating a L2 cache miss, avoid if possible - if (!checkOpenAl) - return true; - - CheckError("CSoundSource::IsPlaying"); - ALint state; - alGetSourcei(id, AL_SOURCE_STATE, &state); - CheckError("CSoundSource::IsPlaying"); - return (state == AL_PLAYING); -} - -void CSoundSource::Stop() -{ - alSourceStop(id); - - { - SoundItem* item = nullptr; - - // callers marked * are mutex-guarded - // ::Delete via ~CSoundSource via CSound::Kill - // ::Play via ::Update (*) - // ::PlayStream via AudioChannel::StreamPlay (*) - // ::StreamStop via AudioChannel::StreamStop (*) - // AudioChannel::FindSourceAndPlay (*) - if (sound != nullptr) - item = sound->GetSoundItem(currentSoundItem.id); - if (item != nullptr) - item->StopPlay(); - - currentSoundItem = {}; - } - - curStream.reset(); - - if (currentChannel != nullptr) { - IAudioChannel* oldChannel = currentChannel; - currentChannel = nullptr; - oldChannel->SoundSourceFinished(this); - } - CheckError("CSoundSource::Stop"); -} - void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative) { assert(!curStream); @@ -323,7 +242,25 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); if (UseAttenuationModel()) { + + efx.Enable(); + if (attenuationFilter == 0) { + alGenFilters(1, &attenuationFilter); + alFilteri(attenuationFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + } + + // Need to disable physics based attenuation + alSourcef(id, AL_AIR_ABSORPTION_FACTOR, 0); + alSourcef(id, AL_REFERENCE_DISTANCE, 0); + alSourcef(id, AL_ROLLOFF_FACTOR, 0); + + alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, 1); + + alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); + ApplyAttenuationModel(false); + } else { if (efx.Enabled()) { efxEnabled = true; @@ -358,14 +295,100 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo #endif } + alSourcePlay(id); + if (name.contains("lasrfir")) { + float finalVol = 0; + alGetSourcefv(id, AL_GAIN, &finalVol); + LOG_L(L_NOTICE, "finalVol: %.2f", finalVol); + } + if (itemBuffer.GetId() == 0) LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); CheckError("CSoundSource::Play"); } +void CSoundSource::Delete() +{ + if (efxEnabled) { + alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); + alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); + } + + Stop(); + alDeleteSources(1, &id); + CheckError("CSoundSource::Delete"); +} + +int CSoundSource::GetCurrentPriority() const +{ + if (asyncPlayItem.id != 0) + return asyncPlayItem.priority; + + if (curStream) + return INT_MAX; + + if (currentSoundItem.id == 0) + return INT_MIN; + + return (currentSoundItem.priority); +} + +bool CSoundSource::IsPlaying(const bool checkOpenAl) const +{ + if (curStream) + return true; + + if (asyncPlayItem.id != 0) + return true; + + if (currentSoundItem.id == 0) + return false; + + // calling OpenAL has a high chance of generating a L2 cache miss, avoid if possible + if (!checkOpenAl) + return true; + + CheckError("CSoundSource::IsPlaying"); + ALint state; + alGetSourcei(id, AL_SOURCE_STATE, &state); + CheckError("CSoundSource::IsPlaying"); + return (state == AL_PLAYING); +} + +void CSoundSource::Stop() +{ + alSourceStop(id); + + { + SoundItem* item = nullptr; + + // callers marked * are mutex-guarded + // ::Delete via ~CSoundSource via CSound::Kill + // ::Play via ::Update (*) + // ::PlayStream via AudioChannel::StreamPlay (*) + // ::StreamStop via AudioChannel::StreamStop (*) + // AudioChannel::FindSourceAndPlay (*) + if (sound != nullptr) + item = sound->GetSoundItem(currentSoundItem.id); + if (item != nullptr) + item->StopPlay(); + + currentSoundItem = {}; + } + + curStream.reset(); + + if (currentChannel != nullptr) { + IAudioChannel* oldChannel = currentChannel; + currentChannel = nullptr; + oldChannel->SoundSourceFinished(this); + } + CheckError("CSoundSource::Stop"); +} + void CSoundSource::PlayAsync(IAudioChannel* channel, size_t id, float3 pos, float3 velocity, float volume, float priority, bool relative) { asyncPlayItem.channel = channel; @@ -459,7 +482,7 @@ void CSoundSource::UpdateVolume() return; } if (currentSoundItem.id != 0) { - alSourcef(id, AL_GAIN, currentVolume * currentSoundItem.volume * currentChannel->volume); + alSourcef(id, AL_GAIN, currentVolume * currentSoundItem.randomVolume * currentChannel->volume); return; } } diff --git a/rts/System/Sound/OpenAL/SoundSource.h b/rts/System/Sound/OpenAL/SoundSource.h index 8038bb82052..32dabf650a8 100644 --- a/rts/System/Sound/OpenAL/SoundSource.h +++ b/rts/System/Sound/OpenAL/SoundSource.h @@ -76,7 +76,7 @@ class CSoundSource void DisableSpatialization(); float GetSummedVolume() { - return (currentChannel ? currentChannel->volume : 1.0f) * currentSoundItem.volume * currentVolume; + return (currentChannel ? currentChannel->volume : 1.0f) * currentSoundItem.randomVolume * currentVolume; } struct AsyncSoundItemData { @@ -100,7 +100,7 @@ class CSoundSource unsigned int loopTime = 0; int priority = 0; - float volume = 0.0f; + float randomVolume = 0.0f; float rolloff = 0.0f; }; From c6a457a42b083e561ecbae911443fa7a6601417f Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Thu, 25 Dec 2025 17:01:14 +0200 Subject: [PATCH 14/20] Converted many consts to config, fixed some issues --- .../AttenuationModels/RtsAttenuationModel.cpp | 29 +++++++------------ .../AttenuationModels/RtsAttenuationModel.h | 12 ++++---- rts/System/Sound/ISound.cpp | 10 +++++-- rts/System/Sound/ISoundAttenuationModel.h | 4 ++- rts/System/Sound/OpenAL/SoundSource.cpp | 27 ++++++++++------- 5 files changed, 44 insertions(+), 38 deletions(-) diff --git a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp index 8f32e259827..575201d295f 100644 --- a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp +++ b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp @@ -20,13 +20,17 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput { CCamera* playerCamera = CCameraHandler::GetCamera(CCamera::CAMTYPE_PLAYER); + float terrainDistance = playerCamera->GetTerrainDistance(); + + out.zoomFactor = 1 - std::clamp(terrainDistance == -1 ? 1.0f : terrainDistance / GetForwardAttenuationRange(), 0.0f, 1.0f); + out.tiltFactor = playerCamera->GetForward().dot(float3(0.0f, -1.0f, 0.0f)); + float3 toSound = in.soundPosition - playerCamera->GetPos(); float camForward = playerCamera->GetForward().dot(toSound); float camRight = playerCamera->GetRight().dot(toSound); float camUp = playerCamera->GetUp().dot(toSound); - out.innerDistance = sqrt(camRight * camRight + camUp * camUp); out.forwardDistance = camForward; float hfov = playerCamera->GetHFOV() * math::DEG_TO_RAD; @@ -42,9 +46,6 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput float outsideUp = std::max(0.0f, std::abs(camUp) - out.frustumHeight); out.outerDistance = std::sqrt(outsideRight * outsideRight + outsideUp * outsideUp); - - float terrainDistance = playerCamera->GetTerrainDistance(); - out.zoomFactor = std::clamp(terrainDistance == -1 ? 1.0f : terrainDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f); } // ----------------------------------------------------------------- @@ -53,23 +54,15 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput { // Calculate forward attenuation - float forwardValue = out.forwardDistance >= 0 ? - std::clamp(1.0f - out.forwardDistance / FORWARD_ATTENUATION_RANGE, 0.0f, 1.0f) : - std::clamp(1.0f - (-out.forwardDistance) / BACKWARD_ATTENUATION_RANGE, 0.0f, 1.0f); + float forwardValue = std::clamp(out.forwardDistance >= 0 ? + std::lerp(GetMinVolumeAttenuation(), 1.0f, 1.0f - out.forwardDistance / GetForwardAttenuationRange()) : + std::clamp(1.0f - (-out.forwardDistance) / GetBackwardAttenuationRange(), 0.0f, 1.0f), GetMinVolumeAttenuation(), 1.0f); // Calculate outer attenuation - float outerValue = std::clamp(1.0f - out.outerDistance / OUTER_ATTENUATION_RANGE, 0.0f, 1.0f); - - // Calculate inner attenuation - float innerMaxRadius = std::min(out.frustumWidth, out.frustumHeight); - float innerMinRadius = innerMaxRadius * OFFCENTER_SAFE_ZONE_RATIO; - float t = std::clamp((out.innerDistance - innerMinRadius) / (innerMaxRadius - innerMinRadius), 0.0f, 1.0f); - - // Apply zoom factor to inner attenuation - float innerValue = 1.0f - t * (OFFCENTER_ATTENUATION_STRENGTH * out.zoomFactor); + float outerValue = std::clamp(std::lerp(GetMinFilterAttenuation(), 1.0f, 1.0f - out.outerDistance / GetOuterAttenuationRange()), GetMinFilterAttenuation(), 1.0f); - // Combine all attenuation factors - out.totalFactor = forwardValue * outerValue * innerValue; + out.volumeFactor = std::pow(forwardValue, 6) * outerValue; + out.filterFactor = std::pow(forwardValue, 1) * outerValue; } return out; diff --git a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.h b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.h index d63c2f51f87..3597ef03de2 100644 --- a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.h +++ b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.h @@ -2,6 +2,7 @@ #pragma once +#include "System/Config/ConfigHandler.h" #include "System/Sound/ISoundAttenuationModel.h" class RtsAttenuationModel : public ISoundAttenuationModel @@ -10,12 +11,11 @@ class RtsAttenuationModel : public ISoundAttenuationModel SoundAttenuationOutput Evaluate(const SoundAttenuationInput& in) const override; private: - //TODO make these settings in the game when using the rts attenuation - static constexpr float FORWARD_ATTENUATION_RANGE = 8000.0f; - static constexpr float BACKWARD_ATTENUATION_RANGE = 300.0f; - static constexpr float OUTER_ATTENUATION_RANGE = 1000.0f; - static constexpr float OFFCENTER_SAFE_ZONE_RATIO = 0.3f; - static constexpr float OFFCENTER_ATTENUATION_STRENGTH = 0.3f; + float GetForwardAttenuationRange() const { return configHandler->GetFloat("snd_forwardAttenuationRange"); } + float GetBackwardAttenuationRange() const { return configHandler->GetFloat("snd_backwardAttenuationRange"); } + float GetOuterAttenuationRange() const { return configHandler->GetFloat("snd_outerAttenuationRange"); } + float GetMinVolumeAttenuation() const { return configHandler->GetFloat("snd_minVolumeAttenuation"); } + float GetMinFilterAttenuation() const { return configHandler->GetFloat("snd_minFilterAttenuation"); } }; // "RTS Model: Designed for RTS games with viewport-based attenuation, forward/backward distance handling, off-screen attenuation, and zoom-aware center attenuation. Seen in games like \"Planetary Annihilation\" or \"Supreme Commander 2\"" diff --git a/rts/System/Sound/ISound.cpp b/rts/System/Sound/ISound.cpp index 9d64d25b586..f9c0b6eab32 100644 --- a/rts/System/Sound/ISound.cpp +++ b/rts/System/Sound/ISound.cpp @@ -40,9 +40,13 @@ CONFIG(float, snd_airAbsorption).defaultValue(0.1f); CONFIG(std::string, snd_device).defaultValue("").description("Sets the used output device. See \"Available Devices\" section in infolog.txt."); -CONFIG(bool, snd_useAttenuationModel) - .defaultValue(false) - .description("Use the new AttenuationModel system. Affects how you hear 3D sounds in the game. Recommended: ON"); +CONFIG(bool, snd_useAttenuationModel).defaultValue(false).description("Use the new AttenuationModel system. Affects how you hear 3D sounds in the game. Recommended: ON"); +CONFIG(float, snd_forwardAttenuationRange).defaultValue(8000).description("Distance in front of the camera over which sound volume and clarity gradually fade."); +CONFIG(float, snd_backwardAttenuationRange).defaultValue(300).description("Distance behind the camera over which sounds are attenuated."); +CONFIG(float, snd_outerAttenuationRange).defaultValue(500).description("How far sounds can be outside the camera view before being strongly attenuated."); +CONFIG(float, snd_minVolumeAttenuation).defaultValue(0.3f).description("Minimum volume multiplier for attenuated sounds to prevent them from becoming silent. Smaller is quiter"); +CONFIG(float, snd_minFilterAttenuation).defaultValue(0.05f).description("Minimum clarity level for attenuated sounds, controlling how muffled distant sounds become. Smaller is more filtered"); + #ifndef NO_SOUND // [0] := Music, [1] := General, [2] := Battle, [3] := UnitReply, [4] := UserInterface diff --git a/rts/System/Sound/ISoundAttenuationModel.h b/rts/System/Sound/ISoundAttenuationModel.h index 77239a6d4a0..09d17dc272c 100644 --- a/rts/System/Sound/ISoundAttenuationModel.h +++ b/rts/System/Sound/ISoundAttenuationModel.h @@ -13,8 +13,10 @@ struct SoundAttenuationOutput { float outerDistance; float frustumHeight; float frustumWidth; + float volumeFactor; + float filterFactor; + float tiltFactor; float zoomFactor; - float totalFactor; }; class ISoundAttenuationModel { diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 03a96cbabb5..8a19a8a4fb6 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -103,7 +103,7 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) attenuationOutput = sound->GetAttenuationModel()->Evaluate({ currentPosition }); - float totalValue = attenuationOutput.totalFactor; + float totalValue = attenuationOutput.volumeFactor; if (smooth) curViewportVolumeMultiplier = SmoothTowards( @@ -118,20 +118,27 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) // new system will treat volume as a normalized range (0-1) so 0.35 is like 35% variable volume which is ridiculous // for now, just ignore the randomVolume in the calculations - float vol = Curve(curViewportVolumeMultiplier, 0.0f, 1.0f, 3) * currentChannel->GetVolume() * currentVolume; + //TODO need way more focus on the center of the camera when it comes to sound, maybe the cursor? + + // float vol = Curve(attenuationOutput.volumeFactor, 0.0f, 1.0f, 2) * currentChannel->GetVolume() * currentVolume; efx.Enabled(); - alSourcef(id, AL_GAIN, vol); + // alSourcef(id, AL_GAIN, vol); - float filter = 1; + // alSourcef(id, AL_GAIN, attenuationOutput.volumeFactor * currentChannel->GetVolume() * currentVolume); + alSourcef(id, AL_GAIN, attenuationOutput.volumeFactor * currentChannel->GetVolume()); - if (curViewportVolumeMultiplier <= 1.0) { - float factor = std::min(curViewportVolumeMultiplier / 1.0f, 1.0f); - filter = Curve(factor, 0.1f, 1.0f, 0.75f); - } + // float filter = 1; + + // if (curViewportVolumeMultiplier <= 1.0) { + // float factor = std::min(curViewportVolumeMultiplier / 1.0f, 1.0f); + // filter = Curve(factor, 0.1f, 1.0f, 0.75f); + // } + + // filter = Curve(attenuationOutput.filterFactor, 0.1f, 1.0f, 0.75f); alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); - alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, filter); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, attenuationOutput.filterFactor); alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); } @@ -209,7 +216,7 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo name = item->name; alSourcei(id, AL_BUFFER, itemBuffer.GetId()); - alSourcef(id, AL_GAIN, volume * item->GetGain() * channel->volume); + alSourcef(id, AL_GAIN, volume * channel->volume); alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch); velocity *= item->dopplerScale * ELMOS_TO_METERS; From 093fcbcf537058d5686548c1b9d13d57895833a8 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Thu, 25 Dec 2025 17:10:09 +0200 Subject: [PATCH 15/20] Fixed resused sources having filters (maybe) --- rts/System/Sound/OpenAL/SoundSource.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 8a19a8a4fb6..b5280c094ef 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -123,6 +123,7 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) // float vol = Curve(attenuationOutput.volumeFactor, 0.0f, 1.0f, 2) * currentChannel->GetVolume() * currentVolume; efx.Enabled(); + efxEnabled = true; // alSourcef(id, AL_GAIN, vol); // alSourcef(id, AL_GAIN, attenuationOutput.volumeFactor * currentChannel->GetVolume() * currentVolume); From 310deaf541532456a7531086ba0766d1f0450030 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Sat, 27 Dec 2025 13:00:05 +0200 Subject: [PATCH 16/20] Added some clamps --- rts/System/Sound/OpenAL/SoundSource.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index b5280c094ef..412db92fdde 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -3,6 +3,7 @@ #include "SoundSource.h" #include +#include #include #include @@ -126,15 +127,15 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) efxEnabled = true; // alSourcef(id, AL_GAIN, vol); + //FIXME sometimes some audio comes in loud and leaves, could be related to not reseting or swaping sound sources right + //FIXME 2D events are being affected by filters like the vo + + //FIXME can't just this as many volumes passed in Play are over 1 // alSourcef(id, AL_GAIN, attenuationOutput.volumeFactor * currentChannel->GetVolume() * currentVolume); - alSourcef(id, AL_GAIN, attenuationOutput.volumeFactor * currentChannel->GetVolume()); - // float filter = 1; + alSourcef(id, AL_GAIN, std::clamp(attenuationOutput.volumeFactor * currentChannel->GetVolume(), 0.0f, 1.0f)); - // if (curViewportVolumeMultiplier <= 1.0) { - // float factor = std::min(curViewportVolumeMultiplier / 1.0f, 1.0f); - // filter = Curve(factor, 0.1f, 1.0f, 0.75f); - // } + // float filter = 1; // filter = Curve(attenuationOutput.filterFactor, 0.1f, 1.0f, 0.75f); From 7e0d0153e403d0140a9faf4a14fb1113e90ae9fc Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Sat, 27 Dec 2025 18:14:33 +0200 Subject: [PATCH 17/20] Small cleanup --- .../AttenuationModels/RtsAttenuationModel.cpp | 3 +- rts/System/Sound/OpenAL/SoundSource.cpp | 60 +++++++++++-------- rts/System/Sound/OpenAL/SoundSource.h | 3 +- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp index 575201d295f..2623973b650 100644 --- a/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp +++ b/rts/System/Sound/AttenuationModels/RtsAttenuationModel.cpp @@ -3,7 +3,6 @@ #include "RtsAttenuationModel.h" #include "Game/Camera.h" #include "Game/CameraHandler.h" -#include "System/Log/ILog.h" #include "System/Sound/ISoundAttenuationModel.h" #include "Game/TraceRay.h" #include @@ -49,7 +48,7 @@ SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput } // ----------------------------------------------------------------- - // Convert all the positional data to normalized ranges and calculate the final range + // Convert all the positional data to normalized ranges and calculate the final ranges // ----------------------------------------------------------------- { diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 412db92fdde..4c1c9d2ce66 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -104,43 +104,47 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) attenuationOutput = sound->GetAttenuationModel()->Evaluate({ currentPosition }); - float totalValue = attenuationOutput.volumeFactor; + if (smooth) { + currentVolumeValue = SmoothTowards( + currentVolumeValue, + attenuationOutput.volumeFactor, + VIEWPORT_VOLUME_REDUCTION_SPEED, + globalRendering->lastFrameTime); - if (smooth) - curViewportVolumeMultiplier = SmoothTowards( - curViewportVolumeMultiplier, - totalValue, + currentFilterValue = SmoothTowards( + currentFilterValue, + attenuationOutput.filterFactor, VIEWPORT_VOLUME_REDUCTION_SPEED, globalRendering->lastFrameTime); - else - curViewportVolumeMultiplier = totalValue; + } + else { + currentVolumeValue = attenuationOutput.volumeFactor; + currentFilterValue = attenuationOutput.filterFactor; + } + + efx.Enabled(); + efxEnabled = true; //TODO currentSoundItem.randomVolume is quite overtuned on many things (0.35 for example) // new system will treat volume as a normalized range (0-1) so 0.35 is like 35% variable volume which is ridiculous // for now, just ignore the randomVolume in the calculations - //TODO need way more focus on the center of the camera when it comes to sound, maybe the cursor? - - // float vol = Curve(attenuationOutput.volumeFactor, 0.0f, 1.0f, 2) * currentChannel->GetVolume() * currentVolume; - - efx.Enabled(); - efxEnabled = true; - // alSourcef(id, AL_GAIN, vol); - - //FIXME sometimes some audio comes in loud and leaves, could be related to not reseting or swaping sound sources right //FIXME 2D events are being affected by filters like the vo - //FIXME can't just this as many volumes passed in Play are over 1 + //FIXME can't just do this as many volumes passed in Play are over 1 + // need to figure out what the best solution is for converting existing values to new system + // + // alSourcef(id, AL_GAIN, attenuationOutput.volumeFactor * currentChannel->GetVolume() * currentVolume); alSourcef(id, AL_GAIN, std::clamp(attenuationOutput.volumeFactor * currentChannel->GetVolume(), 0.0f, 1.0f)); - // float filter = 1; + float gain; - // filter = Curve(attenuationOutput.filterFactor, 0.1f, 1.0f, 0.75f); + alGetSourcef(id, AL_GAIN, &gain); alFilterf(attenuationFilter, AL_LOWPASS_GAIN, 1); - alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, attenuationOutput.filterFactor); + alFilterf(attenuationFilter, AL_LOWPASS_GAINHF, currentFilterValue); alSourcei(id, AL_DIRECT_FILTER, attenuationFilter); } @@ -307,11 +311,11 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo alSourcePlay(id); - if (name.contains("lasrfir")) { - float finalVol = 0; - alGetSourcefv(id, AL_GAIN, &finalVol); - LOG_L(L_NOTICE, "finalVol: %.2f", finalVol); - } + float gain; + alGetSourcef(id, AL_GAIN, &gain); + + if (gain >= 0.9) + LOG_L(L_WARNING, "Gain was suspiciously high: %s, at %f", name.c_str(), gain); if (itemBuffer.GetId() == 0) LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); @@ -491,7 +495,11 @@ void CSoundSource::UpdateVolume() return; } if (currentSoundItem.id != 0) { - alSourcef(id, AL_GAIN, currentVolume * currentSoundItem.randomVolume * currentChannel->volume); + if (UseAttenuationModel()) { + ApplyAttenuationModel(false); + return; + } + alSourcef(id, AL_GAIN, currentVolume * currentSoundItem.randomVolume * currentChannel->volume); return; } } diff --git a/rts/System/Sound/OpenAL/SoundSource.h b/rts/System/Sound/OpenAL/SoundSource.h index 32dabf650a8..a2d7ee27b02 100644 --- a/rts/System/Sound/OpenAL/SoundSource.h +++ b/rts/System/Sound/OpenAL/SoundSource.h @@ -136,7 +136,8 @@ class CSoundSource std::unique_ptr curStream; float currentVolume = 1.0f; - float curViewportVolumeMultiplier = 0; + float currentVolumeValue = 0; + float currentFilterValue = 0; spring_time loopStop {1e9}; spring_time lastUpdate = spring_gettime(); From e09b9697b60ebbe1d6512533f93b2985565216fd Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Sun, 28 Dec 2025 12:31:08 +0200 Subject: [PATCH 18/20] Various fixed to initialization of values and small things --- rts/Game/Camera.h | 2 +- rts/System/Sound/ISound.cpp | 4 ++-- rts/System/Sound/ISoundAttenuationModel.h | 1 - rts/System/Sound/OpenAL/Sound.cpp | 3 +++ rts/System/Sound/OpenAL/Sound.h | 2 +- rts/System/Sound/OpenAL/SoundSource.cpp | 7 +------ rts/System/Sound/OpenAL/SoundSource.h | 7 ------- 7 files changed, 8 insertions(+), 18 deletions(-) diff --git a/rts/Game/Camera.h b/rts/Game/Camera.h index 541fe7be4a7..c0b563490b1 100644 --- a/rts/Game/Camera.h +++ b/rts/Game/Camera.h @@ -311,7 +311,7 @@ class CCamera { uint8_t inViewPlanesMask; - float terrainDistance; + float terrainDistance = 1000; bool movState[10]; // fwd, back, left, right, up, down, fast, slow, tilt, reset bool rotState[4]; // unused diff --git a/rts/System/Sound/ISound.cpp b/rts/System/Sound/ISound.cpp index f9c0b6eab32..ead450c8bd3 100644 --- a/rts/System/Sound/ISound.cpp +++ b/rts/System/Sound/ISound.cpp @@ -44,8 +44,8 @@ CONFIG(bool, snd_useAttenuationModel).defaultValue(false).description("Use the n CONFIG(float, snd_forwardAttenuationRange).defaultValue(8000).description("Distance in front of the camera over which sound volume and clarity gradually fade."); CONFIG(float, snd_backwardAttenuationRange).defaultValue(300).description("Distance behind the camera over which sounds are attenuated."); CONFIG(float, snd_outerAttenuationRange).defaultValue(500).description("How far sounds can be outside the camera view before being strongly attenuated."); -CONFIG(float, snd_minVolumeAttenuation).defaultValue(0.3f).description("Minimum volume multiplier for attenuated sounds to prevent them from becoming silent. Smaller is quiter"); -CONFIG(float, snd_minFilterAttenuation).defaultValue(0.05f).description("Minimum clarity level for attenuated sounds, controlling how muffled distant sounds become. Smaller is more filtered"); +CONFIG(float, snd_minVolumeAttenuation).defaultValue(0.0f).minimumValue(0).maximumValue(1).description("Minimum volume multiplier for attenuated sounds to prevent them from becoming silent. Smaller is quieter"); +CONFIG(float, snd_minFilterAttenuation).defaultValue(0.05f).minimumValue(0).maximumValue(1).description("Minimum clarity level for attenuated sounds, controlling how muffled distant sounds become. Smaller numbers meaning more filtered"); #ifndef NO_SOUND diff --git a/rts/System/Sound/ISoundAttenuationModel.h b/rts/System/Sound/ISoundAttenuationModel.h index 09d17dc272c..2e5be57a3c0 100644 --- a/rts/System/Sound/ISoundAttenuationModel.h +++ b/rts/System/Sound/ISoundAttenuationModel.h @@ -9,7 +9,6 @@ struct SoundAttenuationInput { /// All of the data used to calculate the totalFactor as well as the final totalFactor struct SoundAttenuationOutput { float forwardDistance; - float innerDistance; float outerDistance; float frustumHeight; float frustumWidth; diff --git a/rts/System/Sound/OpenAL/Sound.cpp b/rts/System/Sound/OpenAL/Sound.cpp index 68476f27f2a..9659f6baf0a 100644 --- a/rts/System/Sound/OpenAL/Sound.cpp +++ b/rts/System/Sound/OpenAL/Sound.cpp @@ -124,6 +124,9 @@ void CSound::Kill() soundThread.join(); } + delete attenuationModel; + attenuationModel = nullptr; + SoundBuffer::Deinitialise(); } diff --git a/rts/System/Sound/OpenAL/Sound.h b/rts/System/Sound/OpenAL/Sound.h index 4e8d7d888b9..db84a466514 100644 --- a/rts/System/Sound/OpenAL/Sound.h +++ b/rts/System/Sound/OpenAL/Sound.h @@ -109,7 +109,7 @@ class CSound : public ISound std::string selectedDeviceName = ""; - ISoundAttenuationModel* attenuationModel; + ISoundAttenuationModel* attenuationModel = nullptr; spring::thread soundThread; spring::unordered_map soundMap; // diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 4c1c9d2ce66..8b2ad0896e2 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -22,9 +22,6 @@ #include "Sim/Misc/GlobalConstants.h" #include "System/float3.h" -static constexpr float ROLLOFF_FACTOR = 5.0f; -static constexpr float REFERENCE_DIST = 200.0f; - // used to adjust the pitch to the GameSpeed (optional) float CSoundSource::globalPitch = 1.0f; @@ -33,7 +30,6 @@ float CSoundSource::heightRolloffModifier = 1.0f; void CSoundSource::swap(CSoundSource& r) { - LOG_L(L_WARNING, "SWAP"); std::swap(id, r.id); std::swap(currentChannel, r.currentChannel); std::swap(curStream, r.curStream); @@ -122,7 +118,7 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) currentFilterValue = attenuationOutput.filterFactor; } - efx.Enabled(); + efx.Enable(); efxEnabled = true; //TODO currentSoundItem.randomVolume is quite overtuned on many things (0.35 for example) @@ -133,7 +129,6 @@ void CSoundSource::ApplyAttenuationModel(bool smooth) //FIXME can't just do this as many volumes passed in Play are over 1 // need to figure out what the best solution is for converting existing values to new system - // // alSourcef(id, AL_GAIN, attenuationOutput.volumeFactor * currentChannel->GetVolume() * currentVolume); diff --git a/rts/System/Sound/OpenAL/SoundSource.h b/rts/System/Sound/OpenAL/SoundSource.h index a2d7ee27b02..b68c993d9f8 100644 --- a/rts/System/Sound/OpenAL/SoundSource.h +++ b/rts/System/Sound/OpenAL/SoundSource.h @@ -115,16 +115,9 @@ class CSoundSource ALuint id = 0; ALuint attenuationFilter = 0; - float outerDistance = 0; - float innerDistance = 0; - float forwardDistance = 0; - float2 viewportHalfExtents = float2(); - float terrainDistance; SoundAttenuationOutput attenuationOutput; - float bufferId = 0; - float cameraZoom; float3 currentPosition = float3(0.0f, 0.0f, 0.0f); From b09b239156128e1a00f43544371799c8cb8f7b0f Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Sun, 28 Dec 2025 14:51:08 +0200 Subject: [PATCH 19/20] Small cleanup --- rts/System/Sound/OpenAL/SoundSource.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index 8b2ad0896e2..dd11f96409f 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -309,9 +309,6 @@ void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, flo float gain; alGetSourcef(id, AL_GAIN, &gain); - if (gain >= 0.9) - LOG_L(L_WARNING, "Gain was suspiciously high: %s, at %f", name.c_str(), gain); - if (itemBuffer.GetId() == 0) LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), itemBuffer.GetFilename().c_str()); @@ -325,6 +322,9 @@ void CSoundSource::Delete() alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL); } + if (attenuationFilter != 0) + alDeleteFilters(id, &attenuationFilter); + Stop(); alDeleteSources(1, &id); CheckError("CSoundSource::Delete"); From d73248c54eba51ae810e6f9c59fb9d8eae3e4ae9 Mon Sep 17 00:00:00 2001 From: Majavaa <249626292+Majavaa@users.noreply.github.com> Date: Sun, 28 Dec 2025 14:55:02 +0200 Subject: [PATCH 20/20] Fixed inccorrect usage of function --- rts/System/Sound/OpenAL/SoundSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rts/System/Sound/OpenAL/SoundSource.cpp b/rts/System/Sound/OpenAL/SoundSource.cpp index dd11f96409f..10220395612 100644 --- a/rts/System/Sound/OpenAL/SoundSource.cpp +++ b/rts/System/Sound/OpenAL/SoundSource.cpp @@ -323,7 +323,7 @@ void CSoundSource::Delete() } if (attenuationFilter != 0) - alDeleteFilters(id, &attenuationFilter); + alDeleteFilters(1, &attenuationFilter); Stop(); alDeleteSources(1, &id);