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();
|
||||
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
|
||||
@@ -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 <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
@@ -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<std::string, ALuint>;
|
||||
using FadeMap = std::unordered_map<std::string, AudioFade>;
|
||||
bool m_init{false};
|
||||
ALCdevice* device{nullptr};
|
||||
@@ -38,5 +39,6 @@ private:
|
||||
std::unique_ptr<AudioSource> m_bgm;
|
||||
FadeMap m_fade_map;
|
||||
SoundManager m_sounds;
|
||||
std::shared_ptr<SourcePool> m_pool;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Cubed/audio/audio_buffer.hpp"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <glm/glm.hpp>
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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; }
|
||||
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<float>(data.pcm.size()) /
|
||||
|
||||
@@ -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<int>(max_mono));
|
||||
// Reserve a source for BGM
|
||||
m_pool = std::make_shared<SourcePool>(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
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "Cubed/audio/audio_source.hpp"
|
||||
|
||||
#include "Cubed/tools/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -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
|
||||
@@ -9,29 +9,39 @@ 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()) {
|
||||
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;
|
||||
}
|
||||
} // namespace Cubed
|
||||
@@ -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