mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
feat(audio): add 3D audio playback with source pool and auto-loading
This commit is contained in:
@@ -13,10 +13,12 @@ public:
|
|||||||
~AudioBuffer();
|
~AudioBuffer();
|
||||||
ALuint buffer() const;
|
ALuint buffer() const;
|
||||||
float duration() const;
|
float duration() const;
|
||||||
|
uint32_t channels() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ALuint m_buffer = 0;
|
ALuint m_buffer = 0;
|
||||||
float m_duration = 0.0f;
|
float m_duration = 0.0f;
|
||||||
|
uint32_t m_channels = 0;
|
||||||
void set_data(const AudioData& data);
|
void set_data(const AudioData& data);
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "Cubed/audio/audio_fade.hpp"
|
#include "Cubed/audio/audio_fade.hpp"
|
||||||
#include "Cubed/audio/audio_source.hpp"
|
#include "Cubed/audio/audio_source.hpp"
|
||||||
#include "Cubed/audio/sound_manager.hpp"
|
#include "Cubed/audio/sound_manager.hpp"
|
||||||
|
#include "Cubed/audio/source_pool.hpp"
|
||||||
|
|
||||||
#include <AL/al.h>
|
#include <AL/al.h>
|
||||||
#include <AL/alc.h>
|
#include <AL/alc.h>
|
||||||
@@ -25,11 +26,11 @@ public:
|
|||||||
|
|
||||||
void init();
|
void init();
|
||||||
void play_bgm();
|
void play_bgm();
|
||||||
|
void play_3d(const std::string& sound, const glm::vec3& pos);
|
||||||
void update_listener(glm::vec3 listener_pos);
|
void update_listener(glm::vec3 listener_pos);
|
||||||
void update(float dt);
|
void update(float dt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SourcePool = std::unordered_map<std::string, ALuint>;
|
|
||||||
using FadeMap = std::unordered_map<std::string, AudioFade>;
|
using FadeMap = std::unordered_map<std::string, AudioFade>;
|
||||||
bool m_init{false};
|
bool m_init{false};
|
||||||
ALCdevice* device{nullptr};
|
ALCdevice* device{nullptr};
|
||||||
@@ -38,5 +39,6 @@ private:
|
|||||||
std::unique_ptr<AudioSource> m_bgm;
|
std::unique_ptr<AudioSource> m_bgm;
|
||||||
FadeMap m_fade_map;
|
FadeMap m_fade_map;
|
||||||
SoundManager m_sounds;
|
SoundManager m_sounds;
|
||||||
|
std::shared_ptr<SourcePool> m_pool;
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "Cubed/audio/audio_buffer.hpp"
|
#include "Cubed/audio/audio_buffer.hpp"
|
||||||
|
|
||||||
#include <AL/al.h>
|
#include <AL/al.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
|
|
||||||
enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED };
|
enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED };
|
||||||
@@ -12,9 +13,14 @@ public:
|
|||||||
~AudioSource();
|
~AudioSource();
|
||||||
|
|
||||||
void set_buffer_2d(const AudioBuffer& buffer);
|
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_loop(bool on = true);
|
||||||
void set_volume(float volume);
|
void set_volume(float volume);
|
||||||
|
|
||||||
void play();
|
void play();
|
||||||
|
void play_2d(const AudioBuffer& buffer);
|
||||||
|
void play_3d(const AudioBuffer& buffer, const glm::vec3& pos);
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
void pause();
|
void pause();
|
||||||
float duration() const;
|
float duration() const;
|
||||||
@@ -22,9 +28,14 @@ public:
|
|||||||
float volume() const;
|
float volume() const;
|
||||||
AudioState state() const;
|
AudioState state() const;
|
||||||
|
|
||||||
|
void mark_in_use();
|
||||||
|
bool in_use() const;
|
||||||
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ALuint m_source = 0;
|
ALuint m_source = 0;
|
||||||
float m_volume = 1.0f;
|
float m_volume = 1.0f;
|
||||||
float m_duration = 0.0f;
|
float m_duration = 0.0f;
|
||||||
|
bool m_using;
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ class SoundManager {
|
|||||||
public:
|
public:
|
||||||
SoundManager();
|
SoundManager();
|
||||||
~SoundManager();
|
~SoundManager();
|
||||||
void load(const std::string& name);
|
const AudioBuffer& load(const std::string& name);
|
||||||
const AudioBuffer& get_buffer(const std::string& name) const;
|
const AudioBuffer& get_buffer(const std::string& name);
|
||||||
void init();
|
void init();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Cubed/audio/audio_source.hpp"
|
||||||
|
|
||||||
|
namespace Cubed {
|
||||||
|
class SourcePool {
|
||||||
|
private:
|
||||||
|
std::vector<AudioSource> 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
|
||||||
@@ -9,6 +9,7 @@ AudioBuffer::~AudioBuffer() { alDeleteBuffers(1, &m_buffer); }
|
|||||||
|
|
||||||
ALuint AudioBuffer::buffer() const { return m_buffer; }
|
ALuint AudioBuffer::buffer() const { return m_buffer; }
|
||||||
float AudioBuffer::duration() const { return m_duration; }
|
float AudioBuffer::duration() const { return m_duration; }
|
||||||
|
uint32_t AudioBuffer::channels() const { return m_channels; }
|
||||||
|
|
||||||
void AudioBuffer::set_data(const AudioData& data) {
|
void AudioBuffer::set_data(const AudioData& data) {
|
||||||
ALenum format;
|
ALenum format;
|
||||||
@@ -17,6 +18,7 @@ void AudioBuffer::set_data(const AudioData& data) {
|
|||||||
} else {
|
} else {
|
||||||
format = AL_FORMAT_STEREO16;
|
format = AL_FORMAT_STEREO16;
|
||||||
}
|
}
|
||||||
|
m_channels = data.channels;
|
||||||
alBufferData(m_buffer, format, data.pcm.data(), data.pcm.size(),
|
alBufferData(m_buffer, format, data.pcm.data(), data.pcm.size(),
|
||||||
data.sample_rate);
|
data.sample_rate);
|
||||||
m_duration = static_cast<float>(data.pcm.size()) /
|
m_duration = static_cast<float>(data.pcm.size()) /
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ AudioEngine::~AudioEngine() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_bgm.reset();
|
m_bgm.reset();
|
||||||
|
m_pool.reset();
|
||||||
m_sounds.clear();
|
m_sounds.clear();
|
||||||
|
|
||||||
alcMakeContextCurrent(nullptr);
|
alcMakeContextCurrent(nullptr);
|
||||||
@@ -37,6 +38,19 @@ void AudioEngine::init() {
|
|||||||
|
|
||||||
m_fade_map.try_emplace("bgm", m_bgm.get(), 5.0f, 2.0f);
|
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<int>(max_mono));
|
||||||
|
// Reserve a source for BGM
|
||||||
|
m_pool = std::make_shared<SourcePool>(max_mono - 1);
|
||||||
|
|
||||||
Logger::info("Audio Engine Init Success");
|
Logger::info("Audio Engine Init Success");
|
||||||
|
|
||||||
m_init = true;
|
m_init = true;
|
||||||
@@ -44,11 +58,25 @@ void AudioEngine::init() {
|
|||||||
|
|
||||||
void AudioEngine::play_bgm() { m_bgm->play(); }
|
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_listener(glm::vec3 listener_pos) {}
|
||||||
void AudioEngine::update(float dt) {
|
void AudioEngine::update(float dt) {
|
||||||
|
|
||||||
for (auto& [key, fade] : m_fade_map) {
|
for (auto& [key, fade] : m_fade_map) {
|
||||||
fade.update();
|
fade.update();
|
||||||
}
|
}
|
||||||
|
m_pool->update();
|
||||||
}
|
}
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "Cubed/audio/audio_source.hpp"
|
#include "Cubed/audio/audio_source.hpp"
|
||||||
|
|
||||||
|
#include "Cubed/tools/log.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -22,6 +24,22 @@ void AudioSource::set_buffer_2d(const AudioBuffer& buffer) {
|
|||||||
alSource3f(m_source, AL_POSITION, 0, 0, 0);
|
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) {
|
void AudioSource::set_loop(bool on) {
|
||||||
if (on) {
|
if (on) {
|
||||||
alSourcei(m_source, AL_LOOPING, AL_TRUE);
|
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() { 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::stop() { alSourceStop(m_source); }
|
||||||
void AudioSource::pause() { alSourcePause(m_source); }
|
void AudioSource::pause() { alSourcePause(m_source); }
|
||||||
|
|
||||||
@@ -67,4 +95,14 @@ AudioState AudioSource::state() const {
|
|||||||
throw std::runtime_error("Invaild state");
|
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
|
} // namespace Cubed
|
||||||
@@ -9,28 +9,38 @@ namespace Cubed {
|
|||||||
SoundManager::SoundManager() {}
|
SoundManager::SoundManager() {}
|
||||||
SoundManager::~SoundManager() { clear(); }
|
SoundManager::~SoundManager() { clear(); }
|
||||||
void SoundManager::clear() { m_buffers.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) {
|
const AudioBuffer& SoundManager::load(const std::string& name) {
|
||||||
fs::path sound_path{std::string(ASSETS_PATH) + "sound/" + name};
|
fs::path sound_path{fs::path(ASSETS_PATH) / "sound" / name};
|
||||||
try {
|
try {
|
||||||
|
|
||||||
AudioData data = AudioLoader::load(sound_path);
|
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) {
|
if (!inserted) {
|
||||||
Logger::error("Key Already exist, check the sound name {}", name);
|
Logger::error("Key Already exist, check the sound name {}", name);
|
||||||
}
|
}
|
||||||
|
return pos->second;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
Logger::error("Load Sound Error {}", e.what());
|
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);
|
auto it = m_buffers.find(name);
|
||||||
if (it == m_buffers.end()) {
|
if (it == m_buffers.end()) {
|
||||||
std::string err = std::format("Can't Find Buffer {}", name);
|
try {
|
||||||
ASSERT_MSG(false, err);
|
return load(name);
|
||||||
throw std::runtime_error(err);
|
} 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;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user