feat(audio): add spatial voice source with underwater effects

Update voice source to support spatial positioning, low-pass filter, and reverb effect for underwater immersion. Adjust source pool size and low-pass filter parameter. Correct receive_voice parameter type.
This commit is contained in:
2026-07-20 16:58:59 +08:00
parent 021ae1045a
commit 4f2f439d83
6 changed files with 96 additions and 8 deletions

View File

@@ -48,7 +48,7 @@ public:
void set_client(std::weak_ptr<NetworkClient> client); void set_client(std::weak_ptr<NetworkClient> client);
void send_voice(const std::array<int16_t, AudioRecording::FRAME_SAMPLES>&); void send_voice(const std::array<int16_t, AudioRecording::FRAME_SAMPLES>&);
void receive_voice(std::span<char> opus, const glm::vec3& pos); void receive_voice(std::span<const char> opus, const glm::vec3& pos);
AudioRecording& audio_recording(); AudioRecording& audio_recording();

View File

@@ -1,5 +1,9 @@
#pragma once #pragma once
#include "Cubed/audio/audio_effect_slot.hpp"
#include "Cubed/audio/audio_filter.hpp"
#include "glm/ext/vector_float3.hpp"
#include <al.h> #include <al.h>
#include <cstdint> #include <cstdint>
#include <span> #include <span>
@@ -19,6 +23,15 @@ public:
void stop(); void stop();
bool is_playing() const; bool is_playing() const;
void reset();
void set_volume(float volume);
void set_pitch(float pitch);
void set_pos(glm::vec3 pos);
void set_filter(const AudioFilter& filter);
void clear_filter();
void set_effect_slot(const AudioEffectSlot& slot);
void clear_effect_slot();
private: private:
ALuint m_source = 0; ALuint m_source = 0;

View File

@@ -80,7 +80,7 @@ void AudioEngine::init() {
m_low_pass_filter = std::make_unique<AudioFilter>(); m_low_pass_filter = std::make_unique<AudioFilter>();
m_low_pass_filter->set_lowpass(1.0f, 0.15f); m_low_pass_filter->set_lowpass(1.0f, 0.1f);
m_underwater_effect = std::make_unique<AudioEffect>(); m_underwater_effect = std::make_unique<AudioEffect>();
m_underwater_effect->set_reverb(0.8f, 0.3162f, 0.01f); m_underwater_effect->set_reverb(0.8f, 0.3162f, 0.01f);
@@ -102,7 +102,7 @@ void AudioEngine::init() {
m_bgm->set_buffer_2d(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); m_fade_map.try_emplace("bgm", m_bgm.get(), 5.0f, 2.0f);
m_voice_source = std::make_unique<AudioStreamSource>();
ALCint max_mono = 0; ALCint max_mono = 0;
alcGetIntegerv(device, ALC_MONO_SOURCES, 1, &max_mono); alcGetIntegerv(device, ALC_MONO_SOURCES, 1, &max_mono);
@@ -113,14 +113,12 @@ void AudioEngine::init() {
} }
Logger::info("Set Source Pool Size {}", static_cast<int>(max_mono)); Logger::info("Set Source Pool Size {}", static_cast<int>(max_mono));
// Reserve a source for BGM // Reserve two sources for BGM and voice
m_pool = std::make_shared<SourcePool>(max_mono - 1); m_pool = std::make_shared<SourcePool>(max_mono - 2);
Logger::info("Audio Engine Init Success"); Logger::info("Audio Engine Init Success");
m_init = true; m_init = true;
m_voice_source = std::make_unique<AudioStreamSource>();
} }
void AudioEngine::play_bgm() { m_bgm->play(); } void AudioEngine::play_bgm() { m_bgm->play(); }
@@ -242,9 +240,13 @@ void AudioEngine::underwater_change(bool underwater) {
} }
if (underwater) { if (underwater) {
m_bgm->set_filter(*m_low_pass_filter); m_bgm->set_filter(*m_low_pass_filter);
m_voice_source->set_filter(*m_low_pass_filter);
m_voice_source->set_effect_slot(*m_underwater_slot);
} else { } else {
m_bgm->clear_filter(); m_bgm->clear_filter();
m_voice_source->clear_filter();
m_voice_source->clear_effect_slot();
} }
Logger::info("Under Water Change {}", m_underwater); Logger::info("Under Water Change {}", m_underwater);
} }
@@ -279,7 +281,8 @@ void AudioEngine::send_voice(
c->send(make_packet(*msg)); c->send(make_packet(*msg));
} }
} }
void AudioEngine::receive_voice(std::span<char> opus, const glm::vec3& pos) { void AudioEngine::receive_voice(std::span<const char> opus,
const glm::vec3& pos) {
std::array<int16_t, AudioRecording::FRAME_SAMPLES> pcm; std::array<int16_t, AudioRecording::FRAME_SAMPLES> pcm;
int len = int len =
opus_decode(m_decoder, reinterpret_cast<const uint8_t*>(opus.data()), opus_decode(m_decoder, reinterpret_cast<const uint8_t*>(opus.data()),
@@ -288,6 +291,7 @@ void AudioEngine::receive_voice(std::span<char> opus, const glm::vec3& pos) {
Logger::error("Opus decode failed: {}", opus_strerror(len)); Logger::error("Opus decode failed: {}", opus_strerror(len));
return; return;
} }
m_voice_source->set_pos(pos);
m_voice_source->push_pcm(std::span(pcm.data(), len), m_voice_source->push_pcm(std::span(pcm.data(), len),
AudioRecording::SAMPLE_RATE); AudioRecording::SAMPLE_RATE);
} }

View File

@@ -1,6 +1,10 @@
#include "Cubed/audio/audio_stream_source.hpp" #include "Cubed/audio/audio_stream_source.hpp"
#include "Cubed/audio/audio_error.hpp"
#include "Cubed/tools/log.hpp" #include "Cubed/tools/log.hpp"
#include <cfloat>
#include <efx.h>
namespace Cubed { namespace Cubed {
AudioStreamSource::AudioStreamSource() { AudioStreamSource::AudioStreamSource() {
alGenBuffers(NUM_BUFFERS, m_buffers.data()); alGenBuffers(NUM_BUFFERS, m_buffers.data());
@@ -68,4 +72,71 @@ void AudioStreamSource::unqueue_processed() {
} }
} }
void AudioStreamSource::set_volume(float volume) {
volume = std::clamp(volume, 0.0f, 1.0f);
alSourcef(m_source, AL_GAIN, volume);
}
void AudioStreamSource::set_pitch(float pitch) {
pitch = std::clamp(pitch, 0.0f, 1.0f);
alSourcef(m_source, AL_PITCH, pitch);
}
void AudioStreamSource::set_pos(glm::vec3 pos) {
alSourcei(m_source, AL_SOURCE_RELATIVE, AL_FALSE);
alSource3f(m_source, AL_POSITION, pos.x, pos.y, pos.z);
check_al_error();
alSourcef(m_source, AL_REFERENCE_DISTANCE, 4.0f);
alSourcef(m_source, AL_ROLLOFF_FACTOR, 1.0f);
alSourcef(m_source, AL_MAX_DISTANCE, 48.0f);
}
void AudioStreamSource::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);
clear_filter();
clear_effect_slot();
}
void AudioStreamSource::set_filter(const AudioFilter& filter) {
alSourcei(m_source, AL_DIRECT_FILTER, filter.filter());
}
void AudioStreamSource::clear_filter() {
alSourcei(m_source, AL_DIRECT_FILTER, AL_FILTER_NULL);
}
void AudioStreamSource::set_effect_slot(const AudioEffectSlot& slot) {
alSource3i(m_source, AL_AUXILIARY_SEND_FILTER, slot.slot(), 0,
AL_FILTER_NULL);
}
void AudioStreamSource::clear_effect_slot() {
alSource3i(m_source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0,
AL_FILTER_NULL);
}
} // namespace Cubed } // namespace Cubed