feat(audio): add per-channel volume control and config reload

This commit is contained in:
2026-07-07 13:29:28 +08:00
parent a5bc4e238d
commit 5264755a7e
11 changed files with 102 additions and 19 deletions

View File

@@ -49,6 +49,7 @@ public:
ClientWorld& client_world();
ServerWorld& server_world();
const Argument& argument() const;
AudioEngine& audio();
private:
Camera m_camera;

View File

@@ -30,7 +30,10 @@ public:
bool check = false);
void update_listener(const glm::vec3& pos, const glm::vec3& forward,
const glm::vec3& up);
void update(float dt);
void update();
void reload_config();
float& bgm_target_volume();
private:
using FadeMap = std::unordered_map<std::string, AudioFade>;
@@ -42,5 +45,8 @@ private:
FadeMap m_fade_map;
SoundManager m_sounds;
std::shared_ptr<SourcePool> m_pool;
float m_music_volume = 1.0f;
float m_sfx_volume = 1.0f;
};
} // namespace Cubed

View File

@@ -12,7 +12,6 @@ private:
float m_in_duration = 0.0f;
float m_out_duration = 0.0f;
float m_start_gain = 0.0f;
float m_end_gain = 0.0f;
bool m_fade_in = true;
bool m_active = true;
};

View File

@@ -9,7 +9,7 @@ enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED };
class AudioSource {
public:
AudioSource();
AudioSource(float volume = 1.0f);
~AudioSource();
void set_buffer_2d(const AudioBuffer& buffer);
@@ -25,7 +25,9 @@ public:
void pause();
float duration() const;
float current_time() const;
float volume() const;
float target_volume() const;
float& target_volume();
void set_target_volume(float volume);
AudioState state() const;
void mark_in_use();
@@ -34,7 +36,7 @@ public:
private:
ALuint m_source = 0;
float m_volume = 1.0f;
float m_target_volume = 1.0f;
float m_duration = 0.0f;
bool m_using;
};

View File

@@ -20,6 +20,8 @@ class DevPanel {
bool is_enable_aniso = false;
bool is_support_aniso = true;
bool is_reload = true;
float volume_music = 1.0f;
float volume_sfx = 1.0f;
};
struct PlayerProfile {
int game_mode = 0;

View File

@@ -361,7 +361,7 @@ void App::update() {
}
m_audio.update_listener(m_camera.get_camera_pos(),
m_camera.get_camera_front(), glm::vec3(0, 1, 0));
m_audio.update(dt);
m_audio.update();
m_renderer.update(dt);
}
@@ -396,4 +396,5 @@ Window& App::window() { return m_window; }
ClientWorld& App::client_world() { return m_client_world; }
ServerWorld& App::server_world() { return m_server.server_world(); }
const App::Argument& App::argument() const { return m_argument; }
AudioEngine& App::audio() { return m_audio; }
} // namespace Cubed

View File

@@ -1,5 +1,6 @@
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/config.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
@@ -33,8 +34,15 @@ void AudioEngine::init() {
}
alcMakeContextCurrent(context);
auto& config = Config::get();
m_music_volume = static_cast<float>(config.get<double>("volume.music"));
m_sfx_volume = static_cast<float>(config.get<double>("volume.SFX"));
m_sounds.init();
m_bgm = std::make_unique<AudioSource>();
m_bgm = std::make_unique<AudioSource>(m_music_volume);
m_bgm->set_buffer_2d(m_sounds.get_buffer("bgm/bgm001.mp3"));
m_fade_map.try_emplace("bgm", m_bgm.get(), 5.0f, 2.0f);
@@ -66,6 +74,7 @@ void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos,
return;
}
auto* source = m_pool->acquire();
source->set_volume(m_sfx_volume);
if (!source) {
Logger::error("Source is Full");
}
@@ -91,11 +100,24 @@ void AudioEngine::update_listener(const glm::vec3& pos,
alListenerfv(AL_ORIENTATION, orientation);
}
void AudioEngine::update(float dt) {
void AudioEngine::update() {
for (auto& [key, fade] : m_fade_map) {
fade.update();
}
m_pool->update();
}
void AudioEngine::reload_config() {
auto& config = Config::get();
m_music_volume = static_cast<float>(config.get<double>("volume.music"));
m_sfx_volume = static_cast<float>(config.get<double>("volume.SFX"));
if (m_bgm) {
m_bgm->set_target_volume(m_music_volume);
}
}
float& AudioEngine::bgm_target_volume() { return m_bgm->target_volume(); }
} // namespace Cubed

View File

@@ -10,8 +10,9 @@ AudioFade::AudioFade(AudioSource* source, float fade_in, float fade_out)
m_active = false;
return;
}
m_start_gain = 0.0f;
m_end_gain = m_source->volume();
m_source->set_volume(0.0f);
}
AudioFade::~AudioFade() {}
@@ -27,13 +28,12 @@ void AudioFade::update() {
// Ease Out Sine
t = std::sin(t * std::numbers::pi_v<float> * 0.5f);
float gain = std::lerp(m_start_gain, m_end_gain, t);
float gain = std::lerp(m_start_gain, m_source->target_volume(), t);
m_source->set_volume(gain);
if (t >= 1.0f) {
m_source->set_volume(m_end_gain);
m_source->set_volume(m_source->target_volume());
m_fade_in = false;
m_start_gain = m_end_gain;
m_end_gain = 0.0f;
m_start_gain = m_source->target_volume();
}
} else {
float start_time =
@@ -50,11 +50,11 @@ void AudioFade::update() {
// Smoothstep
t = t * t * (3.0f - 2.0f * t);
float gain = std::lerp(m_start_gain, m_end_gain, t);
float gain = std::lerp(m_start_gain, 0.0f, t);
m_source->set_volume(gain);
if (t >= 1.0f) {
m_source->set_volume(m_end_gain);
m_source->set_volume(0.0f);
m_active = false;
}
}

View File

@@ -7,7 +7,10 @@
#include <stdexcept>
namespace Cubed {
AudioSource::AudioSource() { alGenSources(1, &m_source); }
AudioSource::AudioSource(float volume) {
alGenSources(1, &m_source);
set_target_volume(volume);
}
AudioSource::~AudioSource() {
if (m_source != 0) {
stop();
@@ -62,7 +65,6 @@ void AudioSource::set_loop(bool on) {
void AudioSource::set_volume(float volume) {
volume = std::clamp(volume, 0.0f, 1.0f);
m_volume = volume;
alSourcef(m_source, AL_GAIN, volume);
}
@@ -76,7 +78,6 @@ void AudioSource::play_2d(const AudioBuffer& buffer) {
void AudioSource::play_3d(const AudioBuffer& buffer, const glm::vec3& pos) {
set_buffer_3d(buffer, pos);
play();
check_al_error();
}
void AudioSource::stop() { alSourceStop(m_source); }
@@ -88,7 +89,14 @@ float AudioSource::current_time() const {
alGetSourcef(m_source, AL_SEC_OFFSET, &sec);
return static_cast<float>(sec);
}
float AudioSource::volume() const { return m_volume; }
float AudioSource::target_volume() const { return m_target_volume; }
float& AudioSource::target_volume() { return m_target_volume; }
void AudioSource::set_target_volume(float volume) {
m_target_volume = volume;
set_volume(m_target_volume);
}
AudioState AudioSource::state() const {
ALint state;
@@ -111,11 +119,33 @@ AudioState AudioSource::state() const {
void AudioSource::mark_in_use() { m_using = true; }
bool AudioSource::in_use() const { return m_using; }
void AudioSource::reset() {
alSourceStop(m_source);
alSourcei(m_source, AL_BUFFER, 0);
alSourcef(m_source, AL_GAIN, 1.0f);
alSourcef(m_source, AL_PITCH, 1.0f);
alSource3f(m_source, AL_POSITION, 0.0f, 0.0f, 0.0f);
alSource3f(m_source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSource3f(m_source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSourcef(m_source, AL_REFERENCE_DISTANCE, 1.0f);
alSourcef(m_source, AL_ROLLOFF_FACTOR, 1.0f);
alSourcef(m_source, AL_MAX_DISTANCE, FLT_MAX);
alSourcef(m_source, AL_CONE_INNER_ANGLE, 360.0f);
alSourcef(m_source, AL_CONE_OUTER_ANGLE, 360.0f);
alSourcef(m_source, AL_CONE_OUTER_GAIN, 0.0f);
alSourcei(m_source, AL_LOOPING, AL_FALSE);
alSourcei(m_source, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcef(m_source, AL_SEC_OFFSET, 0.0f);
m_duration = 0.0f;
m_target_volume = 1.0f;
m_using = false;
}
} // namespace Cubed

View File

@@ -44,6 +44,11 @@ void Config::create_config() {
[texture]
aniso = 1 # i is the minimun value, indicating off
[volume]
music = 1.0
SFX = 1.0
)"sv;
try {

View File

@@ -436,6 +436,17 @@ void DevPanel::show_settings_tab_item() {
ImGui::SameLine();
ImGui::Text("Your need to click this button to apply config\n");
}
if (ImGui::SliderFloat("Music", &m_config.volume_music, 0.0f, 1.0f)) {
Config::get().set("volume.music",
static_cast<double>(m_config.volume_music));
m_app.audio().reload_config();
}
if (ImGui::SliderFloat("SFX", &m_config.volume_sfx, 0.0f, 1.0f)) {
Config::get().set("volume.SFX",
static_cast<double>(m_config.volume_sfx));
m_app.audio().reload_config();
}
if (ImGui::Combo("Theme", &m_theme, THEMES, IM_ARRAYSIZE(THEMES))) {
if (m_theme == 0) {
ImGui::StyleColorsDark();
@@ -752,6 +763,10 @@ void DevPanel::update_config_view() {
} else {
m_config.is_enable_aniso = true;
}
m_config.volume_music =
static_cast<float>(config.val_view("volume.music").value_or(1.0));
m_config.volume_sfx =
static_cast<float>(config.val_view("volume.SFX").value_or(1.0));
}
void DevPanel::update_player_profile() {
if (!m_player) {