From 61a9fdac3295c9ca2a176cc8d647d2d8ee76c52b Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Wed, 8 Jul 2026 10:51:12 +0800 Subject: [PATCH] feat(audio): add underwater low-pass filter effect --- include/Cubed/audio/audio_engine.hpp | 6 +++- include/Cubed/audio/audio_filter.hpp | 19 +++++++++++ include/Cubed/audio/audio_source.hpp | 4 +++ include/Cubed/audio/source_pool.hpp | 2 ++ src/CMakeLists.txt | 1 + src/audio/audio_engine.cpp | 47 ++++++++++++++++++++++++++++ src/audio/audio_filter.cpp | 22 +++++++++++++ src/audio/audio_source.cpp | 10 ++++++ src/audio/source_pool.cpp | 2 +- src/camera.cpp | 5 +-- 10 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 include/Cubed/audio/audio_filter.hpp create mode 100644 src/audio/audio_filter.cpp diff --git a/include/Cubed/audio/audio_engine.hpp b/include/Cubed/audio/audio_engine.hpp index dd9a388..98d8b5e 100644 --- a/include/Cubed/audio/audio_engine.hpp +++ b/include/Cubed/audio/audio_engine.hpp @@ -35,6 +35,8 @@ public: void update(); void reload_config(); + void underwater_change(bool underwater); + float& bgm_target_volume(); private: @@ -47,8 +49,10 @@ private: FadeMap m_fade_map; SoundManager m_sounds; std::shared_ptr m_pool; - + bool m_efx_supported = false; + bool m_underwater = false; float m_music_volume = 1.0f; float m_sfx_volume = 1.0f; + std::unique_ptr m_low_pass_filter; }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/audio/audio_filter.hpp b/include/Cubed/audio/audio_filter.hpp new file mode 100644 index 0000000..95ec970 --- /dev/null +++ b/include/Cubed/audio/audio_filter.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +namespace Cubed { +class AudioFilter { +public: + AudioFilter(); + AudioFilter(const AudioFilter&) = delete; + AudioFilter(AudioFilter&&) = delete; + AudioFilter& operator=(const AudioFilter&) = delete; + AudioFilter& operator=(AudioFilter&&) = delete; + ~AudioFilter(); + + void set_lowpass(float gain, float gain_hf); + ALuint filter() const; + +private: + ALuint m_filter = 0; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/audio/audio_source.hpp b/include/Cubed/audio/audio_source.hpp index 045cac1..b95b5b0 100644 --- a/include/Cubed/audio/audio_source.hpp +++ b/include/Cubed/audio/audio_source.hpp @@ -1,5 +1,6 @@ #pragma once #include "Cubed/audio/audio_buffer.hpp" +#include "Cubed/audio/audio_filter.hpp" #include #include @@ -34,6 +35,9 @@ public: bool in_use() const; void reset(); + void set_filter(const AudioFilter& filter); + void clear_filter(); + private: ALuint m_source = 0; float m_target_volume = 1.0f; diff --git a/include/Cubed/audio/source_pool.hpp b/include/Cubed/audio/source_pool.hpp index df1e9c7..577f086 100644 --- a/include/Cubed/audio/source_pool.hpp +++ b/include/Cubed/audio/source_pool.hpp @@ -16,6 +16,8 @@ public: void update(); AudioSource* acquire(); + + std::vector& sources(); }; } // namespace Cubed \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00649e9..1216bb9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,4 +51,5 @@ target_sources(${PROJECT_NAME} audio/sound_manager.cpp audio/source_pool.cpp audio/audio_fade.cpp + audio/audio_filter.cpp ) \ No newline at end of file diff --git a/src/audio/audio_engine.cpp b/src/audio/audio_engine.cpp index fc79eab..9dd8040 100644 --- a/src/audio/audio_engine.cpp +++ b/src/audio/audio_engine.cpp @@ -35,6 +35,21 @@ void AudioEngine::init() { } alcMakeContextCurrent(context); + if (!alcIsExtensionPresent(device, "ALC_EXT_EFX")) { + Logger::error("EFX not supported!"); + m_efx_supported = false; + } else { + Logger::info("EFX supported!"); + m_efx_supported = true; + } + + if (m_efx_supported) { + + m_low_pass_filter = std::make_unique(); + + m_low_pass_filter->set_lowpass(1.0f, 0.15f); + } + alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); check_al_error(); @@ -78,6 +93,9 @@ void AudioEngine::change_bgm(const std::string& sound) { m_bgm->set_buffer_2d(m_sounds.get_buffer(sound)); m_bgm->set_target_volume(m_music_volume); m_bgm->set_volume(m_music_volume); + if (m_efx_supported && m_underwater) { + m_bgm->set_filter(*m_low_pass_filter); + } auto it = m_fade_map.find("bgm"); if (it != m_fade_map.end()) { it->second.reset(); @@ -99,6 +117,9 @@ void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos, try { auto& buffer = m_sounds.get_buffer(sound); + if (m_efx_supported && m_underwater) { + source->set_filter(*m_low_pass_filter); + } source->play_3d(buffer, pos); } catch (const std::exception& e) { if (check) { @@ -121,6 +142,9 @@ void AudioEngine::play_2d(const std::string& sound, bool check) { try { auto& buffer = m_sounds.get_buffer(sound); + if (m_efx_supported && m_underwater) { + source->set_filter(*m_low_pass_filter); + } source->play_2d(buffer); } catch (const std::exception& e) { if (check) { @@ -159,6 +183,29 @@ void AudioEngine::reload_config() { } } +void AudioEngine::underwater_change(bool underwater) { + m_underwater = underwater; + if (!m_efx_supported) { + return; + } + if (!m_pool) { + return; + } + for (auto& source : m_pool->sources()) { + if (m_underwater) { + source.set_filter(*m_low_pass_filter); + } else { + source.clear_filter(); + } + } + if (underwater) { + m_bgm->set_filter(*m_low_pass_filter); + } else { + m_bgm->clear_filter(); + } + Logger::info("Under Water Change {}", m_underwater); +} + float& AudioEngine::bgm_target_volume() { return m_bgm->target_volume(); } } // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_filter.cpp b/src/audio/audio_filter.cpp new file mode 100644 index 0000000..6eed12e --- /dev/null +++ b/src/audio/audio_filter.cpp @@ -0,0 +1,22 @@ +#include "Cubed/audio/audio_filter.hpp" +#ifndef AL_ALEXT_PROTOTYPES +#define AL_ALEXT_PROTOTYPES +#endif +#include +#include +namespace Cubed { +AudioFilter::AudioFilter() { alGenFilters(1, &m_filter); } +AudioFilter::~AudioFilter() { + if (m_filter != 0) { + alDeleteFilters(1, &m_filter); + } +} +void AudioFilter::set_lowpass(float gain, float gain_hf) { + alFilteri(m_filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); + alFilterf(m_filter, AL_LOWPASS_GAIN, gain); + alFilterf(m_filter, AL_LOWPASS_GAINHF, gain_hf); +} + +ALuint AudioFilter::filter() const { return m_filter; } + +} // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_source.cpp b/src/audio/audio_source.cpp index 09cef49..e869312 100644 --- a/src/audio/audio_source.cpp +++ b/src/audio/audio_source.cpp @@ -3,6 +3,7 @@ #include "Cubed/audio/audio_error.hpp" #include "Cubed/tools/log.hpp" +#include #include #include @@ -149,8 +150,17 @@ void AudioSource::reset() { alSourcef(m_source, AL_SEC_OFFSET, 0.0f); + clear_filter(); + m_duration = 0.0f; m_target_volume = 1.0f; m_using = false; } + +void AudioSource::set_filter(const AudioFilter& filter) { + alSourcei(m_source, AL_DIRECT_FILTER, filter.filter()); +} +void AudioSource::clear_filter() { + alSourcei(m_source, AL_DIRECT_FILTER, AL_FILTER_NULL); +} } // namespace Cubed \ No newline at end of file diff --git a/src/audio/source_pool.cpp b/src/audio/source_pool.cpp index 29c0f52..c266a03 100644 --- a/src/audio/source_pool.cpp +++ b/src/audio/source_pool.cpp @@ -20,5 +20,5 @@ AudioSource* SourcePool::acquire() { } return nullptr; } - +std::vector& SourcePool::sources() { return m_sources; } } // namespace Cubed \ No newline at end of file diff --git a/src/camera.cpp b/src/camera.cpp index 1813b06..aa4c9f0 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -49,8 +49,9 @@ void Camera::update_move_camera() { if (m_under_water != change) { m_under_water = change; m_player->set_underwater(m_under_water); - m_player->get_world().get_audio().play_2d( - "ambient/water/in_and_out_of_water.flac", true); + auto& audio = m_player->get_world().get_audio(); + audio.play_2d("ambient/water/in_and_out_of_water.flac", true); + audio.underwater_change(m_under_water); } }