From 960df4a45952030a4fda4d14486a6d14af11e944 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Mon, 6 Jul 2026 17:25:18 +0800 Subject: [PATCH] feat(audio): add audio fade in/out and fix delta time naming --- include/Cubed/app.hpp | 4 +- include/Cubed/audio/audio_buffer.hpp | 2 + include/Cubed/audio/audio_engine.hpp | 6 ++- include/Cubed/audio/audio_fade.hpp | 19 +++++++++ include/Cubed/audio/audio_source.hpp | 10 +++-- include/Cubed/audio/source_pool.hpp | 0 include/Cubed/tools/math_tools.hpp | 2 + src/CMakeLists.txt | 1 + src/app.cpp | 13 +++--- src/audio/audio_buffer.cpp | 4 ++ src/audio/audio_engine.cpp | 12 +++++- src/audio/audio_fade.cpp | 62 ++++++++++++++++++++++++++++ src/audio/audio_source.cpp | 24 ++++++++++- src/audio/source_pool.cpp | 0 14 files changed, 144 insertions(+), 15 deletions(-) create mode 100644 include/Cubed/audio/audio_fade.hpp create mode 100644 include/Cubed/audio/source_pool.hpp create mode 100644 src/audio/audio_fade.cpp create mode 100644 src/audio/source_pool.cpp diff --git a/include/Cubed/app.hpp b/include/Cubed/app.hpp index ba0bd84..a65376c 100644 --- a/include/Cubed/app.hpp +++ b/include/Cubed/app.hpp @@ -38,7 +38,7 @@ public: static int start_cubed_application(int argc, char** argv); static unsigned int seed(); - static float delte_time(); + static float delta_time(); static float get_fps(); Camera& camera(); @@ -67,7 +67,7 @@ private: inline static double last_time = glfwGetTime(); inline static double current_time = glfwGetTime(); - inline static double delta_time = 0.0f; + inline static double dt = 0.0f; inline static double fps_time_count = 0.0f; inline static int frame_count = 0; inline static int fps = 0; diff --git a/include/Cubed/audio/audio_buffer.hpp b/include/Cubed/audio/audio_buffer.hpp index 074a5ce..9b7dcfc 100644 --- a/include/Cubed/audio/audio_buffer.hpp +++ b/include/Cubed/audio/audio_buffer.hpp @@ -12,9 +12,11 @@ public: AudioBuffer& operator=(AudioBuffer&&) = delete; ~AudioBuffer(); ALuint buffer() const; + float duration() const; private: ALuint m_buffer = 0; + float m_duration = 0.0f; void set_data(const AudioData& data); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/audio/audio_engine.hpp b/include/Cubed/audio/audio_engine.hpp index 5d75764..b4e8e5c 100644 --- a/include/Cubed/audio/audio_engine.hpp +++ b/include/Cubed/audio/audio_engine.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Cubed/audio/audio_fade.hpp" #include "Cubed/audio/audio_source.hpp" #include "Cubed/audio/sound_manager.hpp" @@ -24,15 +25,18 @@ public: void init(); void play_bgm(); - void update(glm::vec3 listener_pos); + void update_listener(glm::vec3 listener_pos); + void update(float dt); private: using SourcePool = std::unordered_map; + using FadeMap = std::unordered_map; bool m_init{false}; ALCdevice* device{nullptr}; ALCcontext* context{nullptr}; glm::vec3 listener_pos; std::unique_ptr m_bgm; + FadeMap m_fade_map; SoundManager m_sounds; }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/audio/audio_fade.hpp b/include/Cubed/audio/audio_fade.hpp new file mode 100644 index 0000000..39144c7 --- /dev/null +++ b/include/Cubed/audio/audio_fade.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "Cubed/audio/audio_source.hpp" +namespace Cubed { +class AudioFade { +public: + AudioFade(AudioSource* source, float fade_in = 0.0f, float fade_out = 0.0f); + ~AudioFade(); + void update(); + +private: + AudioSource* m_source = nullptr; + 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; +}; +} // 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 0eeb9a5..fd71720 100644 --- a/include/Cubed/audio/audio_source.hpp +++ b/include/Cubed/audio/audio_source.hpp @@ -11,16 +11,20 @@ public: AudioSource(); ~AudioSource(); - void set_buffer(const AudioBuffer& buffer); + void set_buffer_2d(const AudioBuffer& buffer); void set_loop(bool on = true); - + void set_volume(float volume); void play(); void stop(); void pause(); - + float duration() const; + float current_time() const; + float volume() const; AudioState state(); private: ALuint m_source = 0; + float m_volume = 1.0f; + float m_duration = 0.0f; }; } // namespace Cubed diff --git a/include/Cubed/audio/source_pool.hpp b/include/Cubed/audio/source_pool.hpp new file mode 100644 index 0000000..e69de29 diff --git a/include/Cubed/tools/math_tools.hpp b/include/Cubed/tools/math_tools.hpp index 9ca874a..a22c4c6 100644 --- a/include/Cubed/tools/math_tools.hpp +++ b/include/Cubed/tools/math_tools.hpp @@ -104,6 +104,8 @@ inline float distance2(const glm::vec3& a, const glm::vec3& b) { return glm::dot(diff, diff); } +inline float lerp(float a, float b, float t) { return a + t * (b - a); } + } // namespace Math } // namespace Cubed \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b38e744..00649e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,4 +50,5 @@ target_sources(${PROJECT_NAME} audio/audio_buffer.cpp audio/sound_manager.cpp audio/source_pool.cpp + audio/audio_fade.cpp ) \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index 3e8ada4..1d7d611 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -329,9 +329,9 @@ static Gait player_gait = Gait::WALK; void App::update() { glfwPollEvents(); current_time = glfwGetTime(); - delta_time = current_time - last_time; + dt = current_time - last_time; last_time = current_time; - fps_time_count += delta_time; + fps_time_count += dt; frame_count++; if (fps_time_count >= 1.0f) { fps = static_cast(frame_count / fps_time_count); @@ -346,7 +346,7 @@ void App::update() { std::format("RSS: {}mb", Tools::get_current_rss() / (1024 * 1024))); } m_texture_manager.update(); - m_client_world.update(delta_time); + m_client_world.update(dt); m_camera.update_move_camera(); const auto& player = m_client_world.get_player(); if (player_gait != player.get_gait()) { @@ -359,8 +359,9 @@ void App::update() { m_renderer.update_fov(fov + 5.0f); } } - m_audio.update(m_camera.get_camera_pos()); - m_renderer.update(delta_time); + m_audio.update_listener(m_camera.get_camera_pos()); + m_audio.update(dt); + m_renderer.update(dt); } int App::start_cubed_application(int argc, char** argv) { @@ -382,7 +383,7 @@ int App::start_cubed_application(int argc, char** argv) { return 1; } -float App::delte_time() { return delta_time; } +float App::delta_time() { return dt; } float App::get_fps() { return fps; } diff --git a/src/audio/audio_buffer.cpp b/src/audio/audio_buffer.cpp index 10db445..6b97a96 100644 --- a/src/audio/audio_buffer.cpp +++ b/src/audio/audio_buffer.cpp @@ -8,6 +8,8 @@ AudioBuffer::AudioBuffer(const AudioData& data) { AudioBuffer::~AudioBuffer() { alDeleteBuffers(1, &m_buffer); } ALuint AudioBuffer::buffer() const { return m_buffer; } +float AudioBuffer::duration() const { return m_duration; } + void AudioBuffer::set_data(const AudioData& data) { ALenum format; if (data.channels == 1) { @@ -17,6 +19,8 @@ void AudioBuffer::set_data(const AudioData& data) { } alBufferData(m_buffer, format, data.pcm.data(), data.pcm.size(), data.sample_rate); + m_duration = static_cast(data.pcm.size()) / + (data.channels * data.sample_rate); } } // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_engine.cpp b/src/audio/audio_engine.cpp index 27f56de..0e455b2 100644 --- a/src/audio/audio_engine.cpp +++ b/src/audio/audio_engine.cpp @@ -33,7 +33,10 @@ void AudioEngine::init() { m_sounds.init(); m_bgm = std::make_unique(); - m_bgm->set_buffer(m_sounds.get_buffer("bgm/bgm001.mp3")); + 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); + Logger::info("Audio Engine Init Success"); m_init = true; @@ -41,6 +44,11 @@ void AudioEngine::init() { void AudioEngine::play_bgm() { m_bgm->play(); } -void AudioEngine::update(glm::vec3 listener_pos) {} +void AudioEngine::update_listener(glm::vec3 listener_pos) {} +void AudioEngine::update(float dt) { + for (auto& [key, fade] : m_fade_map) { + fade.update(); + } +} } // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_fade.cpp b/src/audio/audio_fade.cpp new file mode 100644 index 0000000..6d3ee37 --- /dev/null +++ b/src/audio/audio_fade.cpp @@ -0,0 +1,62 @@ +#include "Cubed/audio/audio_fade.hpp" + +#include +#include + +namespace Cubed { +AudioFade::AudioFade(AudioSource* source, float fade_in, float fade_out) + : m_source(source), m_in_duration(fade_in), m_out_duration(fade_out) { + if (!m_source) { + m_active = false; + return; + } + m_start_gain = 0.0f; + m_end_gain = m_source->volume(); + m_source->set_volume(0.0f); +} +AudioFade::~AudioFade() {} +void AudioFade::update() { + if (!m_active || !m_source) { + return; + } + + if (m_fade_in) { + float t = + std::clamp(m_source->current_time() / m_in_duration, 0.0f, 1.0f); + + // Ease Out Sine + t = std::sin(t * std::numbers::pi_v * 0.5f); + + float gain = std::lerp(m_start_gain, m_end_gain, t); + m_source->set_volume(gain); + if (t >= 1.0f) { + m_source->set_volume(m_end_gain); + m_fade_in = false; + m_start_gain = m_end_gain; + m_end_gain = 0.0f; + } + } else { + float start_time = + std::max(0.0f, m_source->duration() - m_out_duration); + + if (m_source->current_time() < start_time) { + return; + } + + float elapsed = m_source->current_time() - start_time; + + float t = std::clamp(elapsed / m_out_duration, 0.0f, 1.0f); + + // Smoothstep + t = t * t * (3.0f - 2.0f * t); + + float gain = std::lerp(m_start_gain, m_end_gain, t); + m_source->set_volume(gain); + + if (t >= 1.0f) { + m_source->set_volume(m_end_gain); + m_active = false; + } + } +} +} // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_source.cpp b/src/audio/audio_source.cpp index daebcd7..4c4f5e9 100644 --- a/src/audio/audio_source.cpp +++ b/src/audio/audio_source.cpp @@ -1,5 +1,7 @@ #include "Cubed/audio/audio_source.hpp" +#include "Cubed/tools/log.hpp" + #include namespace Cubed { @@ -11,11 +13,14 @@ AudioSource::~AudioSource() { } } -void AudioSource::set_buffer(const AudioBuffer& buffer) { +void AudioSource::set_buffer_2d(const AudioBuffer& buffer) { if (state() == AudioState::PLAYING) { stop(); } + m_duration = buffer.duration(); alSourcei(m_source, AL_BUFFER, buffer.buffer()); + alSourcei(m_source, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(m_source, AL_POSITION, 0, 0, 0); } void AudioSource::set_loop(bool on) { @@ -26,11 +31,28 @@ void AudioSource::set_loop(bool on) { } } +void AudioSource::set_volume(float volume) { + if (volume > 1.0f) { + Logger::error("Volume {} is too large", volume); + return; + } + m_volume = volume; + alSourcef(m_source, AL_GAIN, volume); +} + void AudioSource::play() { alSourcePlay(m_source); } void AudioSource::stop() { alSourceStop(m_source); } void AudioSource::pause() { alSourcePause(m_source); } +float AudioSource::duration() const { return m_duration; } +float AudioSource::current_time() const { + ALfloat sec = 0.0f; + alGetSourcef(m_source, AL_SEC_OFFSET, &sec); + return static_cast(sec); +} +float AudioSource::volume() const { return m_volume; } + AudioState AudioSource::state() { ALint state; alGetSourcei(m_source, AL_SOURCE_STATE, &state); diff --git a/src/audio/source_pool.cpp b/src/audio/source_pool.cpp new file mode 100644 index 0000000..e69de29