Skip to content

Commit

Permalink
Fixed: Spatial audio
Browse files Browse the repository at this point in the history
  • Loading branch information
WillisMedwell committed Mar 6, 2024
1 parent 7180544 commit 3fac6b5
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 31 deletions.
Binary file added code/Demos/assets/background_sound.wav
Binary file not shown.
39 changes: 31 additions & 8 deletions code/Demos/src/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ struct FontLogic {
renderer.screen_frame_buffer.clear(data.background_colour);
renderer.screen_frame_buffer.resize(renderer.window_width, renderer.window_height);

Renderer::FontBatchRenderer::BatchConfig config {
Renderer::FontBatchRenderer::BatchConfig config {
.resource_manager = data.resource_manager,
.screen_dimensions = glm::vec2 { renderer.window_width, renderer.window_height },
.font_colour = { 0, 0, 0, 1 },
Expand Down Expand Up @@ -404,36 +404,59 @@ struct IsoData {
glm::vec4 background_colour = { 1, 1, 0, 1 };

Core::AudioManager::BufferHandle sound_buffer;
Core::AudioManager::SourceHandle source_handle;

Components::Spinning spinning;
};
struct IsoLogic {
void init(AppRenderer& renderer, Core::AudioManager& audio, IsoData& data) {
data.start_time = std::chrono::high_resolution_clock::now();

Media::Sound sound {};

auto wav_file_data = Utily::FileReader::load_entire_file("assets/woosh.wav");
auto wav_file_data = Utily::FileReader::load_entire_file("assets/background_sound.wav");
wav_file_data.on_error(print_then_quit);
sound.init_from_wav(wav_file_data.value()).on_error(print_then_quit);

auto res = audio.load_sound_into_buffer(sound).on_error(print_then_quit);

data.sound_buffer = res.value();

audio.play_sound(data.sound_buffer).on_error(print_then_quit);
data.spinning.axis_of_rotation = { 0, 1, 0 };
data.spinning.angle = 0;
data.spinning.rotations_per_second = 1;

data.source_handle = audio.play_sound(data.sound_buffer).on_error(print_then_quit).value();
data.start_time = std::chrono::high_resolution_clock::now();
}

void update(float dt, const Core::InputManager& input, Core::AudioManager& audio, AppState& state, IsoData& data) {
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - data.start_time);
if (duration > std::chrono::milliseconds(1)) {
audio.play_sound(data.sound_buffer).on_error(print_then_quit);
data.start_time = std::chrono::high_resolution_clock::now();
}

if (duration > std::chrono::seconds(3) {
state.should_close = true;
}

data.spinning.update(dt);
auto quat = data.spinning.calc_quat();
glm::vec4 point { 5, 0, 0, 1 };
point = quat * point;

audio.set_source_motion(data.source_handle, point, { 0, 0, 0 }).on_error(print_then_quit);

Core::AudioManager::ListenerProperties lp {};
lp.pos = { 0, 0, 0 };
lp.dir = { 0, 0, 1 };
lp.vel = { 0, 0, 0 };
audio.set_listener_properties(lp);
}

void draw(AppRenderer& renderer, IsoData& data) {
renderer.screen_frame_buffer.bind();
renderer.screen_frame_buffer.clear(data.background_colour);
renderer.screen_frame_buffer.resize(renderer.window_width, renderer.window_height);
}
void stop(IsoData& data) {

}
};

Expand Down
22 changes: 16 additions & 6 deletions code/Engine/include/App/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class App
bool _has_stopped = false;
std::chrono::high_resolution_clock::time_point _last_update;

inline static auto _panic = [](auto& error) {
inline static auto panic = [](auto& error) {
std::cerr << error.what() << std::endl;
throw std::runtime_error(std::string(error.what()));
};
Expand All @@ -72,10 +72,13 @@ class App
_context.init(app_name, width, height).on_error(Utily::ErrorHandler::print_then_quit);
_input.init(_context.unsafe_window_handle());

_audio.init().on_error(_panic);
_audio.init().on_error(panic);
_ecs = entt::registry {};

_logic.init(_renderer, _audio, _data);
{
Profiler::Timer timer2("Logic::init()");
_logic.init(_renderer, _audio, _data);
}

_has_init = true;
_has_stopped = false;
Expand All @@ -93,14 +96,21 @@ class App
auto update() -> void {
Profiler::Timer timer("App::update()", { "App" });
double dt = std::chrono::duration<double> { std::chrono::high_resolution_clock::now() - _last_update }.count();
_logic.update(dt, _input, _audio, _state, _data);
{
Profiler::Timer timer2("Logic::update()");
_logic.update(dt, _input, _audio, _state, _data);
}
_last_update = std::chrono::high_resolution_clock::now();
}
auto render() -> void {
Profiler::Timer timer("App::render()", { "App" });
_renderer.window_width = _context.window_width;
_renderer.window_height = _context.window_height;
_logic.draw(_renderer, _data);

{
Profiler::Timer timer2("Logic::draw()");
_logic.draw(_renderer, _data);
}
_context.swap_buffers();
}

Expand All @@ -124,8 +134,8 @@ void auto_run_app(std::string_view app_name = "Auto Running App", uint16_t width

#if defined(CONFIG_TARGET_NATIVE)
{
Profiler::Timer timer("App::main_loop()");
while (app.is_running()) {
Profiler::Timer timer("App::main_loop()");
app.poll_events();
app.update();
app.render();
Expand Down
15 changes: 14 additions & 1 deletion code/Engine/include/Core/AudioManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,25 @@ namespace Core {
struct BufferHandle {
int index = -1;
};
struct SourceHandle {
int index = -1;
};

struct ListenerProperties {
std::optional<glm::vec3> pos = std::nullopt;
std::optional<glm::vec3> vel = std::nullopt;
std::optional<glm::vec3> dir = std::nullopt;
constexpr static glm::vec3 UP_DIR = { 0, 1, 0 };
};

[[nodiscard]] auto init() -> Utily::Result<void, Utily::Error>;
void stop();

[[nodiscard]] auto load_sound_into_buffer(const Media::Sound& sound) -> Utily::Result<BufferHandle, Utily::Error>;
[[nodiscard]] auto play_sound(BufferHandle buffer_handle, glm::vec3 pos = { 0, 0, 0 }) -> Utily::Result<void, Utily::Error>;
[[nodiscard]] auto play_sound(BufferHandle buffer_handle, glm::vec3 pos = { 0, 0, 0 }, glm::vec3 vel = { 0, 0, 0 }) -> Utily::Result<SourceHandle, Utily::Error>;

[[nodiscard]] auto set_source_motion(SourceHandle source_handle, glm::vec3 pos = { 0, 0, 0 }, glm::vec3 vel = { 0, 0, 0 }) -> Utily::Result<void, Utily::Error>;
void set_listener_properties(const ListenerProperties& listener_properties);

private:
constexpr static size_t MAX_BUFFERS = 1024;
Expand Down
62 changes: 58 additions & 4 deletions code/Engine/src/Core/AudioManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,10 @@ namespace Core {
buffer.is_populated = true;

if (sound.openal_format() == Media::Sound::FormatOpenal::stereo16) {
std::cout << "Stereo" << '\n';
buffer.duration = std::chrono::milliseconds { static_cast<int>(static_cast<float>(sound.raw_bytes().size_bytes()) / 4.0f / sound.frequency() * 1000.0f) };
} else if (sound.openal_format() == Media::Sound::FormatOpenal::mono16) {
std::cout << "Mono" << '\n';
buffer.duration = std::chrono::milliseconds { static_cast<int>(static_cast<float>(sound.raw_bytes().size_bytes()) / 2.0f / sound.frequency() * 1000.0f) };
} else {
return Utily::Error("Core::AudioManager::load_sound_into_buffer() failed. Unhandled format");
Expand All @@ -235,7 +237,7 @@ namespace Core {
return Utily::Error("Unable to find empty buffer");
}

auto AudioManager::play_sound(BufferHandle buffer_handle, glm::vec3 pos) -> Utily::Result<void, Utily::Error> {
auto AudioManager::play_sound(BufferHandle buffer_handle, glm::vec3 pos, glm::vec3 vel) -> Utily::Result<Core::AudioManager::SourceHandle, Utily::Error> {
Profiler::Timer timer("Core::AudioManager::play_sound()");

if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) {
Expand Down Expand Up @@ -265,8 +267,8 @@ namespace Core {
return true;
};

for (Source& source : _sources) {

for (int i = 0; i < _sources.size(); ++i) {
Source& source = _sources[i];
if (!is_source_playing(source)) {
const Buffer& buffer = _buffers[buffer_handle.index];

Expand All @@ -276,6 +278,7 @@ namespace Core {
source.attached_buffer = buffer_handle;
}
alSource3f(source.id, AL_POSITION, pos.x, pos.y, pos.z);
alSource3f(source.id, AL_VELOCITY, vel.x, vel.y, vel.z);
{
Profiler::Timer player("alSourcePlay()");
alSourcePlay(source.id);
Expand All @@ -289,9 +292,60 @@ namespace Core {
return Utily::Error("Core::AudioManager::play_sound() failed. Unable to play sound.");
}
}
return {};

return Core::AudioManager::SourceHandle { i };
}
}
return Utily::Error("Core::AudioManager::play_sound() failed. No free sources avaliable");
}

void AudioManager::set_listener_properties(const ListenerProperties& listener_properties) {
Profiler::Timer timer("Core::AudioManager::set_listener_properties()");

constexpr static auto default_val = glm::vec3(std::numeric_limits<float>::min());
static ListenerProperties last = { default_val, default_val, default_val };

if (listener_properties.pos && last.pos.value() != listener_properties.pos.value()) {
alListener3f(AL_POSITION, listener_properties.pos.value().x, listener_properties.pos.value().y, listener_properties.pos.value().z);
last.pos = listener_properties.pos;
}
if (listener_properties.vel && last.vel.value() != listener_properties.vel.value()) {
alListener3f(AL_VELOCITY, listener_properties.vel.value().x, listener_properties.vel.value().y, listener_properties.vel.value().z);
last.vel = listener_properties.vel;
}
if (listener_properties.dir && last.dir.value() != listener_properties.dir.value()) {
glm::vec3 orientation[2] = { listener_properties.dir.value(), listener_properties.UP_DIR };
alListenerfv(AL_ORIENTATION, reinterpret_cast<float*>(orientation));
last.dir = listener_properties.dir;
}
}

auto AudioManager::set_source_motion(SourceHandle source_handle, glm::vec3 pos, glm::vec3 vel) -> Utily::Result<void, Utily::Error> {
Profiler::Timer timer("Core::AudioManager::set_source_motion()");

if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) {
Profiler::Timer timer2("alIsSource() *debug*");
if (source_handle.index >= _sources.size()) {
return Utily::Error("Core::AudioManager::set_source_motion() failed. The source is out of range.");
} else if (!alIsSource(this->_sources[source_handle.index].id)) {
return Utily::Error("Core::AudioManager::set_source_motion() failed. OpenAL has invalidated the source.");
}
}
const uint32_t& source_id = _sources[source_handle.index].id;
{
Profiler::Timer timer3("alSource3f(AL_POSITION)");
alSource3f(source_id, AL_POSITION, pos.x, pos.y, pos.z);
}
{
Profiler::Timer timer3("alSource3f(AL_VELOCITY)");
alSource3f(source_id, AL_VELOCITY, vel.x, vel.y, vel.z);
}

if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) {
if (glGetError() != AL_NO_ERROR) {
return Utily::Error("Core::AudioManager::set_source_motion() failed. Unable to play sound.");
}
}
return {};
}
}
24 changes: 21 additions & 3 deletions code/Engine/src/Media/Sound.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#include "Media/Sound.hpp"

#include "Profiler/Profiler.hpp"
#include <Utily/Utily.hpp>


#define DR_WAV_IMPLEMENTATION
#include <dr_wav.h>

namespace Media {
auto Sound::init_from_wav(const std::vector<uint8_t>& encoded_wav) -> Utily::Result<void, Utily::Error> {
Profiler::Timer timer("Media::Sound::init_from_wav()");

uint32_t num_channels = 0;
uint32_t sample_rate = 0;
Expand All @@ -20,8 +23,23 @@ namespace Media {
return Utily::Error("Media::Sound::init_from_wave() failed. Dr wav failed to parse the file.");
}

_data.resize(dr_wav_data_i16.size());
std::ranges::copy(dr_wav_data_i16, _data.begin());
#if 1 // Convert all sounds to mono format. Stereo is not used for spatial audio.
if (num_channels == 2) {
_data.resize(dr_wav_data_i16.size() / 2);
auto iter = _data.begin();
for (int i = 0; i < dr_wav_data_i16.size(); i += 2) {
*iter = (dr_wav_data_i16[i] / 2) + (dr_wav_data_i16[i + 1] / 2);
++iter;
}
num_channels = 1;

} else
#endif
{
_data.resize(dr_wav_data_i16.size());
std::ranges::copy(dr_wav_data_i16, _data.begin());
}

drwav_free(dr_wav_ptr, nullptr);

_frequency = sample_rate;
Expand All @@ -33,7 +51,7 @@ namespace Media {
} else {
_data.clear();
_frequency = 0;
return Utily::Error("Media::Sound::init_from_wave() failed. Too many channels in .wav data. Can only handle 2 channels.");
return Utily::Error("Media::Sound::init_from_wave() failed. Too many channels in .wav data. Can only handle 2 channels.");
}
return {};
}
Expand Down
19 changes: 10 additions & 9 deletions code/Engine/src/Profiler/Profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ auto Profiler::format_as_trace_event_json() -> std::string {

for (const auto& [process, recordings] : _processes_recordings) {
for (const auto& recording : recordings) {
res += "\t\t{ \"name\": \"";

res += "\t\t{ \"args\":{}, \"name\":\"";
res += recording.name;

if (recording.categories.size()) {
res += "\", \"cat\": \"";
res += "\", \"cat\":\"";
for (std::ptrdiff_t i = 0; i < recording.categories.size() - 1; ++i) {
res += *(recording.categories.begin() + i);
res += ',';
Expand All @@ -52,18 +53,18 @@ auto Profiler::format_as_trace_event_json() -> std::string {
} else {
res += "\", ";
}
res += "\"ph\": \"X\", ";
res += "\"ts\": \"";
res += "\"ph\":\"X\", ";
res += "\"ts\":";
res += std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(recording.start_time - _profiler_start_time).count() / 1000.0);
res += "\", ";
res += "\"dur\": \"";
res += ", ";
res += "\"dur\":";
res += std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(recording.end_time - recording.start_time).count() / 1000.0);
res += "\", ";
res += ", ";
res += "\"pid\": \"";
res += process;
res += "\", \"tid\": \"";
res += "\", \"tid\":";
res += std::to_string(static_cast<uint64_t>(std::hash<std::thread::id> {}(recording.thread_id)));
res += "\" },\n";
res += " },\n";
}
}
if (_processes_recordings.size()) {
Expand Down

0 comments on commit 3fac6b5

Please sign in to comment.