feat(audio): add footstep sounds and fix audio listener orientation

This commit is contained in:
2026-07-07 11:20:17 +08:00
parent efd7af2c62
commit 68ddd78a6f
19 changed files with 137 additions and 27 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -26,8 +26,10 @@ 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 play_3d(const std::string& sound, const glm::vec3& pos,
bool check = false);
void update_listener(const glm::vec3& pos, const glm::vec3& forward,
const glm::vec3& up);
void update(float dt);
private:

View File

@@ -0,0 +1,15 @@
#pragma once
#include "Cubed/tools/log.hpp"
#include <AL/al.h>
namespace Cubed {
inline void check_al_error(
std::source_location loaction = std::source_location::current()) {
ALenum error = alGetError();
if (error != AL_NO_ERROR) {
Logger::error("File {} Line {} Function {} OpenAL Error {} ",
loaction.file_name(), loaction.line(),
loaction.function_name(), alGetString(error));
}
}
} // namespace Cubed

View File

@@ -123,6 +123,8 @@ private:
float m_angle{0.0f};
float m_walk_time{0.0f};
float m_footstep_timer = 0.0f;
mutable std::shared_mutex m_player_pos_mutex;
mutable std::shared_mutex m_chunk_pos_mutex;
ChunkPosSet m_player_chunk_pos_set;
@@ -134,6 +136,7 @@ private:
void update_y_move(glm::vec3& player_pos);
void update_z_move(glm::vec3& player_pos);
void update_player_chunk();
void play_walk_sound(float dt);
Gait compute_gait() const;
};
} // namespace Cubed

View File

@@ -90,7 +90,7 @@ public:
bool is_receive_exit();
int chunk_size() const;
static AABB get_block_aabb(const glm::ivec3& pos);
AudioEngine& get_audio();
template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
m_timers.emplace(std::piecewise_construct,

View File

@@ -359,7 +359,8 @@ void App::update() {
m_renderer.update_fov(fov + 5.0f);
}
}
m_audio.update_listener(m_camera.get_camera_pos());
m_audio.update_listener(m_camera.get_camera_pos(),
m_camera.get_camera_front(), glm::vec3(0, 1, 0));
m_audio.update(dt);
m_renderer.update(dt);
}

View File

@@ -1,5 +1,6 @@
#include "Cubed/audio/audio_buffer.hpp"
#include "Cubed/audio/audio_error.hpp"
namespace Cubed {
AudioBuffer::AudioBuffer(const AudioData& data) {
alGenBuffers(1, &m_buffer);
@@ -12,6 +13,9 @@ float AudioBuffer::duration() const { return m_duration; }
uint32_t AudioBuffer::channels() const { return m_channels; }
void AudioBuffer::set_data(const AudioData& data) {
Logger::info("buffer={} valid={}", m_buffer, (int)alIsBuffer(m_buffer));
Logger::info("buffer={} isBuffer={}", m_buffer, (int)alIsBuffer(m_buffer));
ALenum format;
if (data.channels == 1) {
format = AL_FORMAT_MONO16;
@@ -19,8 +23,19 @@ void AudioBuffer::set_data(const AudioData& data) {
format = AL_FORMAT_STEREO16;
}
m_channels = data.channels;
alBufferData(m_buffer, format, data.pcm.data(), data.pcm.size(),
data.sample_rate);
Logger::info("channels={} rate={} samples={} bytes={} format={}",
data.channels, data.sample_rate, data.pcm.size(),
data.pcm.size() * sizeof(int16_t), format);
alBufferData(m_buffer, format, data.pcm.data(),
static_cast<ALsizei>(data.pcm.size() * sizeof(int16_t)),
static_cast<ALsizei>(data.sample_rate));
check_al_error();
ALint size = 0;
alGetBufferi(m_buffer, AL_SIZE, &size);
check_al_error();
Logger::info("buffer size={}", size);
m_duration = static_cast<float>(data.pcm.size()) /
(data.channels * data.sample_rate);
}

View File

@@ -1,5 +1,6 @@
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
#include <stdexcept>
@@ -58,7 +59,8 @@ void AudioEngine::init() {
void AudioEngine::play_bgm() { m_bgm->play(); }
void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos) {
void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos,
bool check) {
if (!m_pool) {
Logger::error("Source Pool is nullptr");
return;
@@ -67,11 +69,28 @@ void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos) {
if (!source) {
Logger::error("Source is Full");
}
auto& buffer = m_sounds.get_buffer(sound);
source->play_3d(buffer, pos);
try {
auto& buffer = m_sounds.get_buffer(sound);
source->play_3d(buffer, pos);
} catch (const std::exception& e) {
if (check) {
ASSERT_MSG(false, e.what());
}
}
}
void AudioEngine::update_listener(glm::vec3 listener_pos) {}
void AudioEngine::update_listener(const glm::vec3& pos,
const glm::vec3& forward,
const glm::vec3& up) {
alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
float orientation[] = {forward.x, forward.y, forward.z,
up.x, up.y, up.z};
alListenerfv(AL_ORIENTATION, orientation);
}
void AudioEngine::update(float dt) {
for (auto& [key, fade] : m_fade_map) {

View File

@@ -1,6 +1,6 @@
#include "Cubed/audio/audio_loader.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
#define STB_VORBIS_IMPLEMENTATION
#include "stb/stb_vorbis.h"
@@ -14,7 +14,7 @@ namespace Cubed {
AudioData AudioLoader::load(const std::filesystem::path& path) {
if (!fs::is_regular_file(path)) {
std::string err = std::format("Path {} is not a file", path.string());
ASSERT_MSG(false, err);
throw std::runtime_error(err);
}
auto ext = path.extension().string();
@@ -46,6 +46,8 @@ AudioData AudioLoader::load_wav(const std::filesystem::path& path) {
drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, data.pcm.data());
drwav_uninit(&wav);
Logger::info("{} channels={} rate={} samples={}", path.filename().string(),
data.channels, data.sample_rate, data.pcm.size());
return data;
}
@@ -68,7 +70,8 @@ AudioData AudioLoader::load_mp3(const std::filesystem::path& path) {
data.pcm.assign(pcm, pcm + frame_count * config.channels);
drmp3_free(pcm, nullptr);
Logger::info("{} channels={} rate={} samples={}", path.filename().string(),
data.channels, data.sample_rate, data.pcm.size());
return data;
}
@@ -90,7 +93,8 @@ AudioData AudioLoader::load_flac(const std::filesystem::path& path) {
data.pcm.assign(pcm, pcm + frame_count * channels);
drflac_free(pcm, nullptr);
Logger::info("{} channels={} rate={} samples={}", path.filename().string(),
data.channels, data.sample_rate, data.pcm.size());
return data;
}
@@ -117,7 +121,7 @@ AudioData AudioLoader::load_ogg(const std::filesystem::path& path) {
std::vector<int16_t> pcm(total_frames * channels);
int frames_read = stb_vorbis_get_samples_short_interleaved(
vorbis, channels, pcm.data(), total_frames);
vorbis, channels, pcm.data(), pcm.size());
if (frames_read < total_frames) {
pcm.resize(frames_read * channels);
@@ -129,6 +133,10 @@ AudioData AudioLoader::load_ogg(const std::filesystem::path& path) {
data.channels = channels;
data.sample_rate = sample_rate;
data.pcm = std::move(pcm);
Logger::info("{} channels={} rate={} samples={}", path.filename().string(),
data.channels, data.sample_rate, data.pcm.size());
return data;
}

View File

@@ -1,5 +1,6 @@
#include "Cubed/audio/audio_source.hpp"
#include "Cubed/audio/audio_error.hpp"
#include "Cubed/tools/log.hpp"
#include <algorithm>
@@ -35,9 +36,20 @@ void AudioSource::set_buffer_3d(const AudioBuffer& buffer,
stop();
}
m_duration = buffer.duration();
Logger::info("buffer={}", buffer.buffer());
Logger::info("source={} isSource={}", m_source, alIsSource(m_source));
Logger::info("buffer={} isBuffer={}", buffer.buffer(),
alIsBuffer(buffer.buffer()));
alSourcei(m_source, AL_BUFFER, buffer.buffer());
check_al_error();
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, 100.0f);
}
void AudioSource::set_loop(bool on) {
@@ -64,6 +76,7 @@ void AudioSource::play_2d(const AudioBuffer& buffer) {
void AudioSource::play_3d(const AudioBuffer& buffer, const glm::vec3& pos) {
set_buffer_3d(buffer, pos);
play();
check_al_error();
}
void AudioSource::stop() { alSourceStop(m_source); }

View File

@@ -1,7 +1,7 @@
#include "Cubed/audio/sound_manager.hpp"
#include "Cubed/audio/audio_loader.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
#include <filesystem>
namespace fs = std::filesystem;
@@ -38,7 +38,7 @@ const AudioBuffer& SoundManager::get_buffer(const std::string& name) {
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);
}
}

View File

@@ -1,9 +1,9 @@
#include "Cubed/gameplay/client_player.hpp"
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/gameplay/client_world.hpp"
namespace Cubed {
ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {}
ClientPlayer::~ClientPlayer() {}
@@ -144,42 +144,35 @@ void ClientPlayer::update_player_move_state(int key, int action) {
case GLFW_KEY_W:
if (action == GLFW_PRESS) {
m_move_state.forward = true;
m_moving = true;
}
if (action == GLFW_RELEASE) {
m_move_state.forward = false;
m_moving = false;
m_sprinting = false;
}
break;
case GLFW_KEY_S:
if (action == GLFW_PRESS) {
m_move_state.back = true;
m_moving = true;
}
if (action == GLFW_RELEASE) {
m_move_state.back = false;
m_moving = false;
}
break;
case GLFW_KEY_A:
if (action == GLFW_PRESS) {
m_move_state.left = true;
m_moving = true;
}
if (action == GLFW_RELEASE) {
m_move_state.left = false;
m_moving = false;
}
break;
case GLFW_KEY_D:
if (action == GLFW_PRESS) {
m_move_state.right = true;
m_moving = true;
}
if (action == GLFW_RELEASE) {
m_move_state.right = false;
m_moving = false;
}
break;
case GLFW_KEY_SPACE:
@@ -223,6 +216,8 @@ void ClientPlayer::update_player_move_state(int key, int action) {
}
break;
}
m_moving = m_move_state.forward || m_move_state.back || m_move_state.left ||
m_move_state.right;
}
void ClientPlayer::update_front_vec(float offset_x, float offset_y) {
@@ -391,6 +386,7 @@ void ClientPlayer::update_move(float delta_time) {
m_player_pos = player_pos;
}
update_player_chunk();
play_walk_sound(delta_time);
}
void ClientPlayer::update_x_move(glm::vec3& player_pos) {
@@ -503,6 +499,42 @@ void ClientPlayer::update_player_chunk() {
}
}
void ClientPlayer::play_walk_sound(float dt) {
if (!m_moving || is_fly) {
return;
}
m_footstep_timer += dt;
if (m_sprinting) {
const float WALK_INTERVAL = 0.3f;
if (m_footstep_timer < WALK_INTERVAL) {
return;
}
} else {
const float WALK_INTERVAL = 0.45f;
if (m_footstep_timer < WALK_INTERVAL) {
return;
}
}
m_footstep_timer = 0.0f;
glm::ivec3 block = glm::floor(m_player_pos);
block.y -= 1;
BlockType id = m_world.get_block_tpye(block);
Logger::info("player Block {} Walk Sound", id);
if (id == 0) {
return;
}
std::string name = BlockManager::name_form_id(id);
std::string sound = "block/" + name + "/walk.ogg";
auto& audio = m_world.get_audio();
audio.play_3d(sound, m_player_pos);
Logger::info("Player block {} walk sound", name);
}
Gait ClientPlayer::compute_gait() const {
if (m_xz_speed < 0.01f)
return Gait::STOP;

View File

@@ -607,6 +607,8 @@ AABB ClientWorld::get_block_aabb(const glm::ivec3& pos) {
static_cast<float>(z + 1)}};
}
AudioEngine& ClientWorld::get_audio() { return m_audio; }
void ClientWorld::request_exit() {
if (m_receive_exit) {
return;