From 765f5ca714659e05de525c13cac22960d63ed57e Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Mon, 6 Jul 2026 20:26:20 +0800 Subject: [PATCH] feat(audio): add 3D audio playback with source pool and auto-loading --- include/Cubed/audio/audio_buffer.hpp | 2 ++ include/Cubed/audio/audio_engine.hpp | 4 ++- include/Cubed/audio/audio_source.hpp | 11 ++++++++ include/Cubed/audio/sound_manager.hpp | 4 +-- include/Cubed/audio/source_pool.hpp | 21 +++++++++++++++ src/audio/audio_buffer.cpp | 2 ++ src/audio/audio_engine.cpp | 28 ++++++++++++++++++++ src/audio/audio_source.cpp | 38 +++++++++++++++++++++++++++ src/audio/sound_manager.cpp | 28 +++++++++++++------- src/audio/source_pool.cpp | 24 +++++++++++++++++ 10 files changed, 150 insertions(+), 12 deletions(-) diff --git a/include/Cubed/audio/audio_buffer.hpp b/include/Cubed/audio/audio_buffer.hpp index 9b7dcfc..e22d827 100644 --- a/include/Cubed/audio/audio_buffer.hpp +++ b/include/Cubed/audio/audio_buffer.hpp @@ -13,10 +13,12 @@ public: ~AudioBuffer(); ALuint buffer() const; float duration() const; + uint32_t channels() const; private: ALuint m_buffer = 0; float m_duration = 0.0f; + uint32_t m_channels = 0; 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 b4e8e5c..f17fd9c 100644 --- a/include/Cubed/audio/audio_engine.hpp +++ b/include/Cubed/audio/audio_engine.hpp @@ -3,6 +3,7 @@ #include "Cubed/audio/audio_fade.hpp" #include "Cubed/audio/audio_source.hpp" #include "Cubed/audio/sound_manager.hpp" +#include "Cubed/audio/source_pool.hpp" #include #include @@ -25,11 +26,11 @@ public: void init(); void play_bgm(); + void play_3d(const std::string& sound, const glm::vec3& 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}; @@ -38,5 +39,6 @@ private: std::unique_ptr m_bgm; FadeMap m_fade_map; SoundManager m_sounds; + std::shared_ptr m_pool; }; } // 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 08a1979..34ff76d 100644 --- a/include/Cubed/audio/audio_source.hpp +++ b/include/Cubed/audio/audio_source.hpp @@ -2,6 +2,7 @@ #include "Cubed/audio/audio_buffer.hpp" #include +#include namespace Cubed { enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED }; @@ -12,9 +13,14 @@ public: ~AudioSource(); void set_buffer_2d(const AudioBuffer& buffer); + void set_buffer_3d(const AudioBuffer& buffer, const glm::vec3& vec3); void set_loop(bool on = true); void set_volume(float volume); + void play(); + void play_2d(const AudioBuffer& buffer); + void play_3d(const AudioBuffer& buffer, const glm::vec3& pos); + void stop(); void pause(); float duration() const; @@ -22,9 +28,14 @@ public: float volume() const; AudioState state() const; + void mark_in_use(); + bool in_use() const; + void reset(); + private: ALuint m_source = 0; float m_volume = 1.0f; float m_duration = 0.0f; + bool m_using; }; } // namespace Cubed diff --git a/include/Cubed/audio/sound_manager.hpp b/include/Cubed/audio/sound_manager.hpp index af1b282..b5460d6 100644 --- a/include/Cubed/audio/sound_manager.hpp +++ b/include/Cubed/audio/sound_manager.hpp @@ -8,8 +8,8 @@ class SoundManager { public: SoundManager(); ~SoundManager(); - void load(const std::string& name); - const AudioBuffer& get_buffer(const std::string& name) const; + const AudioBuffer& load(const std::string& name); + const AudioBuffer& get_buffer(const std::string& name); void init(); void clear(); diff --git a/include/Cubed/audio/source_pool.hpp b/include/Cubed/audio/source_pool.hpp index e69de29..df1e9c7 100644 --- a/include/Cubed/audio/source_pool.hpp +++ b/include/Cubed/audio/source_pool.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "Cubed/audio/audio_source.hpp" + +namespace Cubed { +class SourcePool { +private: + std::vector m_sources; + +public: + explicit SourcePool(size_t size); + ~SourcePool(); + SourcePool(const SourcePool&) = delete; + SourcePool(SourcePool&&) = delete; + SourcePool& operator=(const SourcePool&) = delete; + SourcePool& operator=(SourcePool&&) = delete; + + void update(); + AudioSource* acquire(); +}; + +} // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_buffer.cpp b/src/audio/audio_buffer.cpp index 6b97a96..269759d 100644 --- a/src/audio/audio_buffer.cpp +++ b/src/audio/audio_buffer.cpp @@ -9,6 +9,7 @@ AudioBuffer::~AudioBuffer() { alDeleteBuffers(1, &m_buffer); } ALuint AudioBuffer::buffer() const { return m_buffer; } float AudioBuffer::duration() const { return m_duration; } +uint32_t AudioBuffer::channels() const { return m_channels; } void AudioBuffer::set_data(const AudioData& data) { ALenum format; @@ -17,6 +18,7 @@ void AudioBuffer::set_data(const AudioData& data) { } else { format = AL_FORMAT_STEREO16; } + m_channels = data.channels; alBufferData(m_buffer, format, data.pcm.data(), data.pcm.size(), data.sample_rate); m_duration = static_cast(data.pcm.size()) / diff --git a/src/audio/audio_engine.cpp b/src/audio/audio_engine.cpp index 0e455b2..79531fa 100644 --- a/src/audio/audio_engine.cpp +++ b/src/audio/audio_engine.cpp @@ -12,6 +12,7 @@ AudioEngine::~AudioEngine() { } m_bgm.reset(); + m_pool.reset(); m_sounds.clear(); alcMakeContextCurrent(nullptr); @@ -37,6 +38,19 @@ void AudioEngine::init() { m_fade_map.try_emplace("bgm", m_bgm.get(), 5.0f, 2.0f); + ALCint max_mono = 0; + + alcGetIntegerv(device, ALC_MONO_SOURCES, 1, &max_mono); + + if (max_mono <= 1) { + Logger::error("Can't get max mono"); + max_mono = 4; + } + + Logger::info("Set Source Pool Size {}", static_cast(max_mono)); + // Reserve a source for BGM + m_pool = std::make_shared(max_mono - 1); + Logger::info("Audio Engine Init Success"); m_init = true; @@ -44,11 +58,25 @@ void AudioEngine::init() { void AudioEngine::play_bgm() { m_bgm->play(); } +void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos) { + if (!m_pool) { + Logger::error("Source Pool is nullptr"); + return; + } + auto* source = m_pool->acquire(); + if (!source) { + Logger::error("Source is Full"); + } + auto& buffer = m_sounds.get_buffer(sound); + source->play_3d(buffer, pos); +} + void AudioEngine::update_listener(glm::vec3 listener_pos) {} void AudioEngine::update(float dt) { for (auto& [key, fade] : m_fade_map) { fade.update(); } + m_pool->update(); } } // namespace Cubed \ No newline at end of file diff --git a/src/audio/audio_source.cpp b/src/audio/audio_source.cpp index 3c10cb7..5a0766a 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 #include @@ -22,6 +24,22 @@ void AudioSource::set_buffer_2d(const AudioBuffer& buffer) { alSource3f(m_source, AL_POSITION, 0, 0, 0); } +void AudioSource::set_buffer_3d(const AudioBuffer& buffer, + const glm::vec3& pos) { + + if (buffer.channels() != 1) { + Logger::warn("3D sound should use mono audio."); + } + + if (state() != AudioState::STOPPED && state() != AudioState::INITIAL) { + stop(); + } + m_duration = buffer.duration(); + + alSourcei(m_source, AL_BUFFER, buffer.buffer()); + alSource3f(m_source, AL_POSITION, pos.x, pos.y, pos.z); +} + void AudioSource::set_loop(bool on) { if (on) { alSourcei(m_source, AL_LOOPING, AL_TRUE); @@ -38,6 +56,16 @@ void AudioSource::set_volume(float volume) { void AudioSource::play() { alSourcePlay(m_source); } +void AudioSource::play_2d(const AudioBuffer& buffer) { + set_buffer_2d(buffer); + play(); +} + +void AudioSource::play_3d(const AudioBuffer& buffer, const glm::vec3& pos) { + set_buffer_3d(buffer, pos); + play(); +} + void AudioSource::stop() { alSourceStop(m_source); } void AudioSource::pause() { alSourcePause(m_source); } @@ -67,4 +95,14 @@ AudioState AudioSource::state() const { throw std::runtime_error("Invaild state"); } +void AudioSource::mark_in_use() { m_using = true; } +bool AudioSource::in_use() const { return m_using; } +void AudioSource::reset() { + 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); + alSourcei(m_source, AL_LOOPING, AL_FALSE); + m_using = false; +} } // namespace Cubed \ No newline at end of file diff --git a/src/audio/sound_manager.cpp b/src/audio/sound_manager.cpp index 2073f9e..29eaf51 100644 --- a/src/audio/sound_manager.cpp +++ b/src/audio/sound_manager.cpp @@ -9,28 +9,38 @@ namespace Cubed { SoundManager::SoundManager() {} SoundManager::~SoundManager() { clear(); } void SoundManager::clear() { m_buffers.clear(); } -void SoundManager::init() { load("bgm/bgm001.mp3"); } +void SoundManager::init() { + try { + // load("bgm/bgm001.mp3"); + } catch (const std::exception& e) { + } +} -void SoundManager::load(const std::string& name) { - fs::path sound_path{std::string(ASSETS_PATH) + "sound/" + name}; +const AudioBuffer& SoundManager::load(const std::string& name) { + fs::path sound_path{fs::path(ASSETS_PATH) / "sound" / name}; try { AudioData data = AudioLoader::load(sound_path); - auto [_, inserted] = m_buffers.try_emplace(name, data); + auto [pos, inserted] = m_buffers.try_emplace(name, data); if (!inserted) { Logger::error("Key Already exist, check the sound name {}", name); } - + return pos->second; } catch (const std::exception& e) { Logger::error("Load Sound Error {}", e.what()); + throw; } } -const AudioBuffer& SoundManager::get_buffer(const std::string& name) const { +const AudioBuffer& SoundManager::get_buffer(const std::string& name) { auto it = m_buffers.find(name); if (it == m_buffers.end()) { - std::string err = std::format("Can't Find Buffer {}", name); - ASSERT_MSG(false, err); - throw std::runtime_error(err); + try { + return load(name); + } catch (const std::exception& e) { + std::string err = std::format("Can't Find Buffer {}", name); + ASSERT_MSG(false, err); + throw std::runtime_error(err); + } } return it->second; } diff --git a/src/audio/source_pool.cpp b/src/audio/source_pool.cpp index e69de29..29c0f52 100644 --- a/src/audio/source_pool.cpp +++ b/src/audio/source_pool.cpp @@ -0,0 +1,24 @@ +#include "Cubed/audio/source_pool.hpp" + +namespace Cubed { +SourcePool::SourcePool(size_t size) { m_sources.resize(size); } +SourcePool::~SourcePool() { m_sources.clear(); } +void SourcePool::update() { + for (auto& source : m_sources) { + if (source.in_use() && source.state() == AudioState::STOPPED) { + source.reset(); + } + } +} + +AudioSource* SourcePool::acquire() { + for (auto& source : m_sources) { + if (!source.in_use()) { + source.mark_in_use(); + return &source; + } + } + return nullptr; +} + +} // namespace Cubed \ No newline at end of file