feat(audio): add underwater low-pass filter effect

This commit is contained in:
2026-07-08 10:51:12 +08:00
parent 85761dee8f
commit 61a9fdac32
10 changed files with 114 additions and 4 deletions

View File

@@ -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<SourcePool> 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<AudioFilter> m_low_pass_filter;
};
} // namespace Cubed

View File

@@ -0,0 +1,19 @@
#pragma once
#include <AL/al.h>
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

View File

@@ -1,5 +1,6 @@
#pragma once
#include "Cubed/audio/audio_buffer.hpp"
#include "Cubed/audio/audio_filter.hpp"
#include <AL/al.h>
#include <glm/glm.hpp>
@@ -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;

View File

@@ -16,6 +16,8 @@ public:
void update();
AudioSource* acquire();
std::vector<AudioSource>& sources();
};
} // namespace Cubed

View File

@@ -51,4 +51,5 @@ target_sources(${PROJECT_NAME}
audio/sound_manager.cpp
audio/source_pool.cpp
audio/audio_fade.cpp
audio/audio_filter.cpp
)

View File

@@ -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<AudioFilter>();
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

View File

@@ -0,0 +1,22 @@
#include "Cubed/audio/audio_filter.hpp"
#ifndef AL_ALEXT_PROTOTYPES
#define AL_ALEXT_PROTOTYPES
#endif
#include <AL/alc.h>
#include <AL/efx.h>
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

View File

@@ -3,6 +3,7 @@
#include "Cubed/audio/audio_error.hpp"
#include "Cubed/tools/log.hpp"
#include <AL/efx.h>
#include <algorithm>
#include <stdexcept>
@@ -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

View File

@@ -20,5 +20,5 @@ AudioSource* SourcePool::acquire() {
}
return nullptr;
}
std::vector<AudioSource>& SourcePool::sources() { return m_sources; }
} // namespace Cubed

View File

@@ -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);
}
}