From 71805442d51db5e15dd547d2e310e1cd1f93e28d Mon Sep 17 00:00:00 2001 From: WillisMedwell Date: Wed, 6 Mar 2024 10:43:55 +1100 Subject: [PATCH] Simplified Audio: removed abstraction of buffers & sources --- code/Demos/src/Main.cpp | 48 +-- code/Engine/include/App/App.hpp | 39 ++- code/Engine/include/Audio/Audio.hpp | 8 - code/Engine/include/Audio/Buffer.hpp | 31 -- code/Engine/include/Audio/Context.hpp | 26 -- code/Engine/include/Audio/Device.hpp | 25 -- code/Engine/include/Audio/Source.hpp | 32 -- code/Engine/include/Config.hpp | 2 + code/Engine/include/Core/AudioManager.hpp | 65 ++++ code/Engine/include/Core/Core.hpp | 4 +- code/Engine/include/Profiler/Profiler.hpp | 2 +- .../include/Renderer/ResourceManager.hpp | 2 +- code/Engine/src/Audio/Buffer.cpp | 68 ---- code/Engine/src/Audio/Context.cpp | 56 ---- code/Engine/src/Audio/Device.cpp | 37 --- code/Engine/src/Audio/Source.cpp | 92 ------ code/Engine/src/Core/AudioManager.cpp | 297 ++++++++++++++++++ code/Engine/src/Core/OpenglContext.cpp | 17 +- code/build-native.bat | 5 +- 19 files changed, 419 insertions(+), 437 deletions(-) delete mode 100644 code/Engine/include/Audio/Audio.hpp delete mode 100644 code/Engine/include/Audio/Buffer.hpp delete mode 100644 code/Engine/include/Audio/Context.hpp delete mode 100644 code/Engine/include/Audio/Device.hpp delete mode 100644 code/Engine/include/Audio/Source.hpp create mode 100644 code/Engine/include/Core/AudioManager.hpp delete mode 100644 code/Engine/src/Audio/Buffer.cpp delete mode 100644 code/Engine/src/Audio/Context.cpp delete mode 100644 code/Engine/src/Audio/Device.cpp delete mode 100644 code/Engine/src/Audio/Source.cpp create mode 100644 code/Engine/src/Core/AudioManager.cpp diff --git a/code/Demos/src/Main.cpp b/code/Demos/src/Main.cpp index 5da14f2..6b27b2e 100644 --- a/code/Demos/src/Main.cpp +++ b/code/Demos/src/Main.cpp @@ -12,6 +12,7 @@ using namespace std::literals; static inline auto print_then_quit = [](const auto& error) { std::cout << error.what(); std::this_thread::sleep_for(std::chrono::seconds(1)); + throw std::runtime_error(std::string(error.what())); exit(EXIT_FAILURE); }; @@ -367,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 }, @@ -402,52 +403,37 @@ struct IsoData { std::chrono::steady_clock::time_point start_time; glm::vec4 background_colour = { 1, 1, 0, 1 }; - Audio::Device audio_device; - Audio::Context audio_context; - Audio::Buffer audio_buffer; - Audio::Source audio_source; - - // Cameras::Isometric camera; + Core::AudioManager::BufferHandle sound_buffer; }; struct IsoLogic { - void init(AppRenderer& renderer, entt::registry& ecs, IsoData& data) { + 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"); - wav_file_data.on_error(Utily::ErrorHandler::print_then_quit); - sound.init_from_wav(wav_file_data.value()); - - - // data.camera.position = { 0, 1, -1}; - // data.camera.set_direction_via_angles(-45, 0); - - data.audio_device.init().on_error(Utily::ErrorHandler::print_then_quit); - data.audio_context.init(data.audio_device).on_error(Utily::ErrorHandler::print_then_quit); - data.audio_buffer.init().on_error(Utily::ErrorHandler::print_then_quit); - data.audio_buffer.load_sound(sound).on_error(Utily::ErrorHandler::print_then_quit); - - data.audio_source.init().on_error(Utily::ErrorHandler::print_then_quit); - data.audio_source.bind(data.audio_buffer).on_error(Utily::ErrorHandler::print_then_quit); - data.audio_source.play().on_error(Utily::ErrorHandler::print_then_quit); - + 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); } - void update(float dt, const Core::InputManager& input, AppState& state, entt::registry& ecs, IsoData& data) { + void update(float dt, const Core::InputManager& input, Core::AudioManager& audio, AppState& state, IsoData& data) { auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - data.start_time); - if (duration > std::chrono::milliseconds(250)) { - data.audio_source.play().on_error(Utily::ErrorHandler::print_then_quit); + 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(); } } - void draw(AppRenderer& renderer, entt::registry& ecs, IsoData& data) { + 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 play() { - } - void stop() { + void stop(IsoData& data) { } }; diff --git a/code/Engine/include/App/App.hpp b/code/Engine/include/App/App.hpp index 8bbfa20..89f9a62 100644 --- a/code/Engine/include/App/App.hpp +++ b/code/Engine/include/App/App.hpp @@ -13,11 +13,8 @@ #include "Core/Core.hpp" #include "Profiler/Profiler.hpp" - #include "Core/Input.hpp" -#include "Audio/Audio.hpp" - #include "App/AppRenderer.hpp" #include @@ -28,18 +25,18 @@ struct AppState { }; template -concept HasValidAppLogic = requires(T t, double dt, AppState& state, AppData& data, AppRenderer& renderer, const Core::InputManager& input, entt::registry& ecs) { +concept HasValidAppLogic = requires(T t, double dt, AppState& state, AppData& data, AppRenderer& renderer, Core::AudioManager& audio, const Core::InputManager& input) { { - t.init(renderer, ecs, data) + t.init(renderer, audio, data) } -> std::same_as; { - t.update(dt, input, state, ecs, data) + t.update(dt, input, audio, state, data) } -> std::same_as; { - t.draw(renderer, ecs, data) + t.draw(renderer, data) } -> std::same_as; { - t.stop() + t.stop(data) } -> std::same_as; }; @@ -55,26 +52,30 @@ class App AppData _data; AppLogic _logic; - Audio::Device _audio_device; - Audio::Context _audio_context; + Core::AudioManager _audio; Core::OpenglContext _context; bool _has_init = false; bool _has_stopped = false; std::chrono::high_resolution_clock::time_point _last_update; + inline static auto _panic = [](auto& error) { + std::cerr << error.what() << std::endl; + throw std::runtime_error(std::string(error.what())); + }; + public: auto init(std::string_view app_name, uint_fast16_t width, uint_fast16_t height) -> void { Profiler::instance().switch_to_process(Utily::Reflection::get_type_name()); Profiler::Timer timer("App::init()", { "App" }); _context.init(app_name, width, height).on_error(Utily::ErrorHandler::print_then_quit); - _ecs = entt::registry {}; - _logic.init(_renderer, _ecs, _data); _input.init(_context.unsafe_window_handle()); - /*_audio_device.init().on_error(Utily::ErrorHandler::print_then_quit); - _audio_context.init(_audio_device).on_error(Utily::ErrorHandler::print_then_quit);*/ + _audio.init().on_error(_panic); + _ecs = entt::registry {}; + + _logic.init(_renderer, _audio, _data); _has_init = true; _has_stopped = false; @@ -82,26 +83,24 @@ class App auto stop() -> void { if (!_has_stopped) { Profiler::Timer timer("App::stop()", { "App" }); + _logic.stop(_data); _renderer.stop(); - _logic.stop(); + _audio.stop(); _context.stop(); - - /*_audio_context.stop(); - _audio_device.stop();*/ } _has_stopped = true; } auto update() -> void { Profiler::Timer timer("App::update()", { "App" }); double dt = std::chrono::duration { std::chrono::high_resolution_clock::now() - _last_update }.count(); - _logic.update(dt, _input, _state, _ecs, _data); + _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, _ecs, _data); + _logic.draw(_renderer, _data); _context.swap_buffers(); } diff --git a/code/Engine/include/Audio/Audio.hpp b/code/Engine/include/Audio/Audio.hpp deleted file mode 100644 index da07b99..0000000 --- a/code/Engine/include/Audio/Audio.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "Audio/Device.hpp" -#include "Audio/Context.hpp" -#include "Audio/Source.hpp" -#include "Audio/Buffer.hpp" - - diff --git a/code/Engine/include/Audio/Buffer.hpp b/code/Engine/include/Audio/Buffer.hpp deleted file mode 100644 index dea68bc..0000000 --- a/code/Engine/include/Audio/Buffer.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Audio/Context.hpp" -#include "Audio/Device.hpp" - -#include "Media/Sound.hpp" - -#include -#include - -namespace Audio { - class Source; - - class Buffer - { - private: - constexpr static auto INVALID_ID = std::numeric_limits::max(); - std::optional _id = std::nullopt; - - public: - [[nodiscard]] auto init() noexcept -> Utily::Result; - void stop() noexcept; - - [[nodiscard]] auto load_sound(const Media::Sound& sound) -> Utily::Result; - - ~Buffer(); - - // give access to the id of the - friend class Source; - }; -} diff --git a/code/Engine/include/Audio/Context.hpp b/code/Engine/include/Audio/Context.hpp deleted file mode 100644 index be58d63..0000000 --- a/code/Engine/include/Audio/Context.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include - -#include "Audio/Device.hpp" - -namespace Audio { - class Context - { - private: - std::optional _context = std::nullopt; - void debug_validate(); - - public: - auto init(Audio::Device& device) -> Utily::Result; - void stop(); - - [[nodiscard]] inline auto unsafe_context_handle() -> void* { - debug_validate(); - return _context.value_or(nullptr); - } - - ~Context(); - }; -} \ No newline at end of file diff --git a/code/Engine/include/Audio/Device.hpp b/code/Engine/include/Audio/Device.hpp deleted file mode 100644 index 91f9e77..0000000 --- a/code/Engine/include/Audio/Device.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -#include - -namespace Audio { - class Device - { - private: - std::optional _device = std::nullopt; - void debug_validate(); - - public: - auto init() -> Utily::Result; - void stop(); - - [[nodiscard]] inline auto unsafe_device_handle() -> void* { - debug_validate(); - return _device.value_or(nullptr); - } - - ~Device(); - }; -} \ No newline at end of file diff --git a/code/Engine/include/Audio/Source.hpp b/code/Engine/include/Audio/Source.hpp deleted file mode 100644 index deb33bb..0000000 --- a/code/Engine/include/Audio/Source.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Audio/Buffer.hpp" -#include "Audio/Context.hpp" -#include "Audio/Device.hpp" - - -#include -#include - -namespace Audio { - class Source - { - private: - constexpr static uint32_t INVALID_ID = std::numeric_limits::max(); - - std::optional _id = std::nullopt; - - bool _has_bound_buffer = false; - - public: - auto init() -> Utily::Result; - auto bind(Audio::Buffer& buffer) -> Utily::Result; - void unbind(); - - auto play() -> Utily::Result; - - void stop(); - - ~Source(); - }; -} diff --git a/code/Engine/include/Config.hpp b/code/Engine/include/Config.hpp index edf0cdb..931e621 100644 --- a/code/Engine/include/Config.hpp +++ b/code/Engine/include/Config.hpp @@ -34,6 +34,8 @@ namespace Config { constexpr static bool ENABLE_VSYNC = false; } + + #include #include diff --git a/code/Engine/include/Core/AudioManager.hpp b/code/Engine/include/Core/AudioManager.hpp new file mode 100644 index 0000000..fffe159 --- /dev/null +++ b/code/Engine/include/Core/AudioManager.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "Config.hpp" +#include "Media/Sound.hpp" + +namespace Core { + + class AudioManager + { + public: + struct BufferHandle { + int index = -1; + }; + + [[nodiscard]] auto init() -> Utily::Result; + void stop(); + + [[nodiscard]] auto load_sound_into_buffer(const Media::Sound& sound) -> Utily::Result; + [[nodiscard]] auto play_sound(BufferHandle buffer_handle, glm::vec3 pos = { 0, 0, 0 }) -> Utily::Result; + + private: + constexpr static size_t MAX_BUFFERS = 1024; + constexpr static size_t MAX_SOURCES = 256; + + bool _has_init = false; + bool _has_stopped = false; + + std::optional _device = std::nullopt; + std::optional _context = std::nullopt; + + struct Buffer { + uint32_t id = std::numeric_limits::max(); + bool is_populated = false; + std::optional duration = std::nullopt; + }; + struct Source { + uint32_t id = std::numeric_limits::max(); + std::optional attached_buffer = std::nullopt; + std::optional expected_finish = std::nullopt; + }; + + std::array _raw_buffers {}; + std::array _raw_sources {}; + + std::span _buffers {}; + std::span _sources {}; + + [[nodiscard]] auto init_device() -> Utily::Result; + [[nodiscard]] auto init_context() -> Utily::Result; + [[nodiscard]] auto init_buffers() -> Utily::Result; + [[nodiscard]] auto init_sources() -> Utily::Result; + + void stop_device(); + void stop_context(); + void stop_buffers(); + void stop_sources(); + }; +} \ No newline at end of file diff --git a/code/Engine/include/Core/Core.hpp b/code/Engine/include/Core/Core.hpp index 3dc8117..5024b1c 100644 --- a/code/Engine/include/Core/Core.hpp +++ b/code/Engine/include/Core/Core.hpp @@ -11,6 +11,7 @@ namespace Core { class VertexBuffer; class FrameBuffer; class ScreenFrameBuffer; + class AudioManager; } #include "Fence.hpp" @@ -22,4 +23,5 @@ namespace Core { #include "VertexArray.hpp" #include "VertexBuffer.hpp" #include "VertexBufferLayout.hpp" -#include "FrameBuffer.hpp" \ No newline at end of file +#include "FrameBuffer.hpp" +#include "AudioManager.hpp" \ No newline at end of file diff --git a/code/Engine/include/Profiler/Profiler.hpp b/code/Engine/include/Profiler/Profiler.hpp index e48beab..3734c33 100644 --- a/code/Engine/include/Profiler/Profiler.hpp +++ b/code/Engine/include/Profiler/Profiler.hpp @@ -35,7 +35,7 @@ class Profiler Timer(const Timer&) = delete; Timer(Timer&&) = delete; - Timer(std::string_view function_name = std::source_location::current().function_name(), std::vector cats = {}); + Timer(std::string_view function_name, std::vector cats = {}); ~Timer(); }; diff --git a/code/Engine/include/Renderer/ResourceManager.hpp b/code/Engine/include/Renderer/ResourceManager.hpp index 49b67ec..d44369c 100644 --- a/code/Engine/include/Renderer/ResourceManager.hpp +++ b/code/Engine/include/Renderer/ResourceManager.hpp @@ -21,7 +21,7 @@ namespace Renderer { throw std::runtime_error(std::string{error.what()}); } void operator()(auto& error) { - throw std::runtime_error("Render panic"); + throw std::runtime_error(std::string(error)); } }; diff --git a/code/Engine/src/Audio/Buffer.cpp b/code/Engine/src/Audio/Buffer.cpp deleted file mode 100644 index a657ce3..0000000 --- a/code/Engine/src/Audio/Buffer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "Audio/Buffer.hpp" - -#include "Config.hpp" - -namespace Audio { - auto Buffer::init() noexcept -> Utily::Result { - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (_id) { - return Utily::Error("Audio::Buffer::init() failed. Already initialised, do not re-init without calling stop() first."); - } - } - stop(); - _id = INVALID_ID; - alGenBuffers(1, &_id.value()); - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (auto error = alGetError(); error == AL_INVALID_VALUE) { - _id = std::nullopt; - return Utily::Error("Audio::Buffer::init() failed. alGenBuffers() failed: Buffer array isn't large enough."); - } else if (error == AL_OUT_OF_MEMORY) { - _id = std::nullopt; - return Utily::Error("Audio::Buffer::init() failed. alGenBuffers() failed: Ran out of memory."); - } else if (_id.value() == INVALID_ID) { - _id = std::nullopt; - return Utily::Error("Audio::Buffer::init() failed. alGenBuffers() failed: no error message from openal."); - } - } - return {}; - } - - void Buffer::stop() noexcept { - if (_id) { - alDeleteBuffers(1, &_id.value()); - } - _id = std::nullopt; - } - - auto Buffer::load_sound(const Media::Sound& sound) -> Utily::Result { - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (_id.value_or(INVALID_ID) == INVALID_ID) { - return Utily::Error("Audio::Buffer::load_sound() failed. The Audio::Buffer is not valid."); - } - } - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (sound.raw_bytes().size_bytes() == 0) { - return Utily::Error("Audio::Buffer::load_sound() failed. The Sound passed in has no data."); - } - } - alBufferData(*_id, (ALenum)sound.openal_format(), sound.raw_bytes().data(), sound.raw_bytes().size_bytes(), sound.frequency()); - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (auto error = glGetError(); error == AL_OUT_OF_MEMORY) { - return Utily::Error("Audio::Buffer::load_sound() failed. alBufferData() failed: Ran out of memory."); - } else if (error == AL_INVALID_ENUM) { - return Utily::Error("Audio::Buffer::load_sound() failed. alBufferData() failed: The sound format does not exist."); - } else if (error == AL_INVALID_VALUE) { - if (sound.raw_bytes().data() == nullptr) { - return Utily::Error("Audio::Buffer::load_sound() failed. alBufferData() failed: The Sound.data() is nullptr."); - } else { - return Utily::Error("Audio::Buffer::load_sound() failed. alBufferData() failed: The Sound::size_of_bytes() is invaid for the format."); - } - } - } - return {}; - } - - Buffer::~Buffer() { - stop(); - } -} \ No newline at end of file diff --git a/code/Engine/src/Audio/Context.cpp b/code/Engine/src/Audio/Context.cpp deleted file mode 100644 index 5d0af37..0000000 --- a/code/Engine/src/Audio/Context.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "Audio/Context.hpp" - -#include "Config.hpp" - -namespace Audio { - - void Context::debug_validate() { - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (!_context.value_or(nullptr)) { - throw std::runtime_error("Audio::Context not initialised"); - } - } - } - - auto Context::init(Audio::Device& device) -> Utily::Result { - if (_context) { - stop(); - } - _context = reinterpret_cast(alcCreateContext(reinterpret_cast(device.unsafe_device_handle()), nullptr)); - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (!_context.value_or(nullptr)) { - _context = std::nullopt; - return Utily::Error { "Audio::Context::init() failed. Openal::alcCreateContext() failed: no error message from openal." }; - } - } - alcMakeContextCurrent(reinterpret_cast(*_context)); - - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - /* - Need to set current context before glGetError() can be called, - hence why we do createContext() fail checks after MakeContextCurrent(). - */ - if (auto error = alGetError(); error == ALC_INVALID_VALUE) { - _context = std::nullopt; - return Utily::Error { "Audio::Context::init() failed. Openal::alcCreateContext() failed: cannot make additional context for this device." }; - } else if (error == ALC_INVALID_DEVICE) { - _context = std::nullopt; - return Utily::Error { "Audio::Context::init() failed. Openal::alcCreateContext() failed: invalid device." }; - } else if (error == ALC_INVALID_CONTEXT) { - return Utily::Error { "Audio::Context::init() failed. Openal::alcMakeContextCurrent() failed: invalid context." }; - } - } - return {}; - } - - void Context::stop() { - if (_context) { - alcMakeContextCurrent(nullptr); - alcDestroyContext(reinterpret_cast(*_context)); - } - _context = std::nullopt; - } - Context::~Context() { - stop(); - } -} \ No newline at end of file diff --git a/code/Engine/src/Audio/Device.cpp b/code/Engine/src/Audio/Device.cpp deleted file mode 100644 index fa1cdab..0000000 --- a/code/Engine/src/Audio/Device.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "Audio/Device.hpp" - -#include "Config.hpp" - -namespace Audio { - - void Device::debug_validate() { - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (!_device.value_or(nullptr)) { - throw std::runtime_error("Audio::Device not initialised"); - } - } - } - - auto Device::init() -> Utily::Result { - if (_device) { - stop(); - } - _device = reinterpret_cast(alcOpenDevice(nullptr)); - if (!_device.value_or(nullptr)) { - _device = std::nullopt; - return Utily::Error { "Device::init() failed. Openal::alcOpenDevice() failed to open a sound device." }; - } - return {}; - } - - void Device::stop() { - if (_device) { - alcCloseDevice(reinterpret_cast(*_device)); - } - _device = std::nullopt; - } - - Device::~Device() { - stop(); - } -} \ No newline at end of file diff --git a/code/Engine/src/Audio/Source.cpp b/code/Engine/src/Audio/Source.cpp deleted file mode 100644 index 3986d96..0000000 --- a/code/Engine/src/Audio/Source.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "Audio/Source.hpp" - -#include "Config.hpp" - -namespace Audio { - - auto Source::init() -> Utily::Result { - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (_id.value_or(INVALID_ID) != INVALID_ID) { - return Utily::Error("Audio::Source::init() failed. Trying to reinit."); - } - } - stop(); - - _id = INVALID_ID; - alGenSources(1, &_id.value()); - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (auto error = alGetError(); error == AL_OUT_OF_MEMORY) { - return Utily::Error("Audio::Source::init() failed. alGenSources() failed: Openal ran out of memory."); - } else if (error == AL_INVALID_VALUE) { - return Utily::Error("Audio::Source::init() failed. alGenSources() failed: Not enough memory resources or bad id ptr."); - } else if (error == AL_INVALID_OPERATION) { - return Utily::Error("Audio::Source::init() failed. alGenSources() failed: There is no current context."); - } else if (_id.value_or(INVALID_ID) == INVALID_ID) { - return Utily::Error("Audio::Source::init() failed. Openal failed to set the id."); - } - } - return {}; - } - auto Source::bind(Audio::Buffer& buffer) -> Utily::Result { - - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (_id.value_or(INVALID_ID) == INVALID_ID) { - return Utily::Error("Audio::Source::bind() failed. The source has not be initialised."); - } else if (buffer._id.value_or(Audio::Buffer::INVALID_ID) == Audio::Buffer::INVALID_ID) { - return Utily::Error("Audio::Source::bind() failed. The buffer passed in has not be initialised."); - } - } - alSourcei(_id.value(), AL_BUFFER, buffer._id.value()); - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (auto error = glGetError(); error == AL_INVALID_VALUE) { - return Utily::Error("Audio::Source::bind() failed. Openal value is out of range."); - } else if (error == AL_INVALID_ENUM) { - return Utily::Error("Audio::Source::bind() failed. Openal says bad enum."); - } else if (error == AL_INVALID_NAME) { - return Utily::Error("Audio::Source::bind() failed. Openal does not recognise the source id."); - } else if (error == AL_INVALID_OPERATION) { - return Utily::Error("Audio::Source::bind() failed. Openal has no context."); - } - } - _has_bound_buffer = true; - return {}; - } - - void Source::unbind() { - if (_id) { - alSourcei(_id.value(), AL_BUFFER, 0); - } - _has_bound_buffer = false; - } - auto Source::play() -> Utily::Result { - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (_id.value_or(INVALID_ID) == INVALID_ID) { - return Utily::Error("Audio::Source::play() failed. The source has not been initialised."); - } else if (!_has_bound_buffer) { - return Utily::Error("Audio::Source::play() failed. There is no buffer currently bound to this source."); - } - } - alSourcePlay(_id.value()); - - if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { - if (auto error = alGetError(); error == AL_INVALID_NAME) { - return Utily::Error("Audio::Source::play() failed. The id has been invalidated -> e.g. someone has called alDeleteSource() for this id."); - } else if (error == AL_INVALID_OPERATION) { - return Utily::Error("Audio::Source::play() failed. There is no current context."); - } - } - return {}; - } - void Source::stop() { - if (_id) { - alSourceStop(_id.value()); - alDeleteSources(1, &_id.value()); - } - _id = std::nullopt; - _has_bound_buffer = false; - } - Source::~Source() { - stop(); - } - -} diff --git a/code/Engine/src/Core/AudioManager.cpp b/code/Engine/src/Core/AudioManager.cpp new file mode 100644 index 0000000..f027dbe --- /dev/null +++ b/code/Engine/src/Core/AudioManager.cpp @@ -0,0 +1,297 @@ +#include "Core/AudioManager.hpp" + +#include "Profiler/Profiler.hpp" + +namespace Core { + + auto AudioManager::init() -> Utily::Result { + + Profiler::Timer timer("Core::AudioManager::init()"); + + if (_has_init) { + return Utily::Error("Trying to reinitialise AudioManager."); + } + if (auto result = init_device(); result.has_error()) { + return result.error(); + } + if (auto result = init_context(); result.has_error()) { + return result.error(); + } + if (auto result = init_buffers(); result.has_error()) { + return result.error(); + } + if (auto result = init_sources(); result.has_error()) { + return result.error(); + } + _has_init = true; + return {}; + } + + auto AudioManager::init_device() -> Utily::Result { + Profiler::Timer timer("Core::AudioManager::init_device()"); + + if (_device) { + return Utily::Error("The sound device has already been initialised."); + } + _device = reinterpret_cast(alcOpenDevice(nullptr)); + if (*_device == nullptr) { + _device = std::nullopt; + return Utily::Error("Openal::alcOpenDevice() failed to open a sound device."); + } + return {}; + } + auto AudioManager::init_context() -> Utily::Result { + + Profiler::Timer timer("Core::AudioManager::init_context()"); + + if (_context) { + return Utily::Error("The openal context has already been initialised."); + } else if (!_device) { + return Utily::Error("Could not init sound context. The sound device has not been initialised first."); + } + + _context = reinterpret_cast(alcCreateContext(reinterpret_cast(*_device), nullptr)); + if (*_context == nullptr) { + _context = std::nullopt; + return Utily::Error { "Could not init sound context. Openal::alcCreateContext() failed." }; + } + alcMakeContextCurrent(reinterpret_cast(*_context)); + + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (auto error = alGetError(); error == ALC_INVALID_VALUE) { + _context = std::nullopt; + return Utily::Error { "Audio::Context::init() Openal::alcCreateContext() failed: cannot make additional context for this device." }; + } else if (error == ALC_INVALID_DEVICE) { + _context = std::nullopt; + return Utily::Error { "Audio::Context::init() failed. Openal::alcCreateContext() failed: invalid device." }; + } else if (error == ALC_INVALID_CONTEXT) { + return Utily::Error { "Audio::Context::init() failed. Openal::alcMakeContextCurrent() failed: invalid context." }; + } else if (error != ALC_NO_ERROR) { + return Utily::Error { "Audio::Context::init() failed. Openal::alcMakeContextCurrent() failed: no given reason." }; + } + } + return {}; + } + auto AudioManager::init_buffers() -> Utily::Result { + Profiler::Timer timer("Core::AudioManager::init_buffers()"); + + if (_buffers.size()) { + return Utily::Error("The sound buffers have already been initialised."); + } else if (!_device || !_context) { + return Utily::Error("The device or context has not been initialised."); + } + + std::array buffer_ids { 0 }; + + for (size_t num_buffers = buffer_ids.size(); num_buffers > 0; --num_buffers) { + alGenBuffers(num_buffers, buffer_ids.data()); + if (glGetError() == AL_NO_ERROR && buffer_ids.front() != 0) { + const size_t num_buffers = std::distance(buffer_ids.begin(), std::ranges::find(buffer_ids, 0)); + _buffers = std::span { _raw_buffers.begin(), _raw_buffers.begin() + num_buffers }; + + std::transform( + buffer_ids.begin(), + buffer_ids.end(), + _buffers.begin(), + [](uint32_t id) { return Buffer(id); }); + break; + } + } + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (_buffers.size() == 0) { + return Utily::Error("Unable to create the audio buffers."); + } + bool all_buffers_are_valid = std::ranges::all_of(_buffers, [](Buffer& b) { return alIsBuffer(b.id); }); + if (!all_buffers_are_valid) { + return Utily::Error("The buffers have been invalidated after they were created."); + } + } + return {}; + } + auto AudioManager::init_sources() -> Utily::Result { + Profiler::Timer timer("Core::AudioManager::init_sources()"); + + if (_sources.size()) { + return Utily::Error("The sound sources have already been initialised."); + } else if (!_device || !_context) { + return Utily::Error("The device or context has not been initialised."); + } + + std::array source_ids { 0 }; + + for (size_t num_sources = source_ids.size(); num_sources > 0; --num_sources) { + alGenSources(num_sources, source_ids.data()); + if (glGetError() == AL_NO_ERROR && source_ids.front() != 0) { + const size_t num_sources = std::distance(source_ids.begin(), std::ranges::find(source_ids, 0)); + _sources = std::span { _raw_sources.begin(), _raw_sources.begin() + num_sources }; + + std::transform( + source_ids.begin(), + source_ids.end(), + _sources.begin(), + [](uint32_t id) { return Source(id); }); + + break; + } + } + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (_sources.size() == 0) { + return Utily::Error("Unable to create the audio sources."); + } + bool all_sources_are_valid = std::ranges::all_of(_sources, [](const Source& s) { return alIsSource(s.id); }); + if (!all_sources_are_valid) { + return Utily::Error("The sources have been invalidated after they were created."); + } + } + return {}; + } + + void AudioManager::stop() { + + stop_sources(); + stop_buffers(); + stop_context(); + stop_device(); + _has_stopped = true; + } + + void AudioManager::stop_device() { + if (_device) { + alcCloseDevice(reinterpret_cast(*_device)); + } + _device = std::nullopt; + } + void AudioManager::stop_context() { + if (_context) { + alcMakeContextCurrent(nullptr); + alcDestroyContext(reinterpret_cast(*_context)); + } + _context = std::nullopt; + } + void AudioManager::stop_buffers() { + if (_buffers.size()) { + std::array buffer_ids; + std::transform(_buffers.begin(), _buffers.end(), buffer_ids.begin(), [](Buffer& b) { return b.id; }); + + alDeleteBuffers(_buffers.size(), buffer_ids.data()); + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (glGetError() != AL_NO_ERROR) { + throw std::runtime_error("failed to destroy sources and buffers"); + } + } + } + _buffers = {}; + } + void AudioManager::stop_sources() { + if (_sources.size()) { + std::array source_ids; + std::transform(_sources.begin(), _sources.end(), source_ids.begin(), [](Source& s) { return s.id; }); + + alDeleteSources(_sources.size(), source_ids.data()); + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (glGetError() != AL_NO_ERROR) { + throw std::runtime_error("failed to destroy sources and buffers"); + } + } + } + _sources = {}; + } + + auto AudioManager::load_sound_into_buffer(const Media::Sound& sound) -> Utily::Result { + Profiler::Timer timer("Core::AudioManager::load_sound_into_buffer()"); + + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (!_has_init) { + return Utily::Error("Has not been initialsied"); + } else if (!_device || !_context || !_sources.size() || !_buffers.size()) { + return Utily::Error("Has not successfully initialsied"); + } + } + + for (int i = 0; i < _buffers.size(); ++i) { + Buffer& buffer = _buffers[i]; + + if (!buffer.is_populated) { + alBufferData(buffer.id, (ALenum)sound.openal_format(), sound.raw_bytes().data(), sound.raw_bytes().size_bytes(), sound.frequency()); + + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (glGetError() != AL_NO_ERROR) { + return Utily::Error("Unable to load sound into buffer"); + } + } + buffer.is_populated = true; + + if (sound.openal_format() == Media::Sound::FormatOpenal::stereo16) { + buffer.duration = std::chrono::milliseconds { static_cast(static_cast(sound.raw_bytes().size_bytes()) / 4.0f / sound.frequency() * 1000.0f) }; + } else if (sound.openal_format() == Media::Sound::FormatOpenal::mono16) { + buffer.duration = std::chrono::milliseconds { static_cast(static_cast(sound.raw_bytes().size_bytes()) / 2.0f / sound.frequency() * 1000.0f) }; + } else { + return Utily::Error("Core::AudioManager::load_sound_into_buffer() failed. Unhandled format"); + } + + return BufferHandle { i }; + } + } + return Utily::Error("Unable to find empty buffer"); + } + + auto AudioManager::play_sound(BufferHandle buffer_handle, glm::vec3 pos) -> Utily::Result { + Profiler::Timer timer("Core::AudioManager::play_sound()"); + + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (!_has_init) { + return Utily::Error("Core::AudioManager::play_sound() failed. The manager has not been initialsied"); + } else if (!_device || !_context || !_sources.size() || !_buffers.size()) { + return Utily::Error("Core::AudioManager::play_sound() failed. The manager tried, but has not successfully initialsied"); + } + } + + const auto now = std::chrono::high_resolution_clock::now(); + + auto is_source_playing = [&now](Source& source) { + if (!source.expected_finish) { + return false; + } else if (now <= source.expected_finish.value()) { + return true; + } + + ALint state = 0; + alGetSourcei(source.id, AL_SOURCE_STATE, &state); + + if (state != AL_PLAYING) { + source.expected_finish = std::nullopt; + return false; + } + return true; + }; + + for (Source& source : _sources) { + + if (!is_source_playing(source)) { + const Buffer& buffer = _buffers[buffer_handle.index]; + + // attach buffer to source. + if (!source.attached_buffer || source.attached_buffer.value().index != buffer_handle.index) { + alSourcei(source.id, AL_BUFFER, buffer.id); + source.attached_buffer = buffer_handle; + } + alSource3f(source.id, AL_POSITION, pos.x, pos.y, pos.z); + { + Profiler::Timer player("alSourcePlay()"); + alSourcePlay(source.id); + } + + source.expected_finish = std::chrono::steady_clock::now() + + buffer.duration.value_or(std::chrono::milliseconds(0)); + + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { + if (glGetError() != AL_NO_ERROR) { + return Utily::Error("Core::AudioManager::play_sound() failed. Unable to play sound."); + } + } + return {}; + } + } + return Utily::Error("Core::AudioManager::play_sound() failed. No free sources avaliable"); + } +} \ No newline at end of file diff --git a/code/Engine/src/Core/OpenglContext.cpp b/code/Engine/src/Core/OpenglContext.cpp index 6b09fce..3424293 100644 --- a/code/Engine/src/Core/OpenglContext.cpp +++ b/code/Engine/src/Core/OpenglContext.cpp @@ -246,12 +246,17 @@ namespace Core { void OpenglContext::poll_events() { Profiler::Timer timer("OpenglContext::poll_events()", { "OpenglContext" }); - validate_window(); - int width, height; - glfwGetWindowSize(*_window, &width, &height); - glfwPollEvents(); - window_width = static_cast(width); - window_height = static_cast(height); + { + Profiler::Timer timer2("glfwPollEvents()", { "OpenglContext" }); + glfwPollEvents(); + } + { + Profiler::Timer timer3("glfwGetWindowSize()", { "OpenglContext" }); + int width, height; + glfwGetWindowSize(*_window, &width, &height); + window_width = static_cast(width); + window_height = static_cast(height); + } } } \ No newline at end of file diff --git a/code/build-native.bat b/code/build-native.bat index 4d0c982..12a31c1 100644 --- a/code/build-native.bat +++ b/code/build-native.bat @@ -3,6 +3,7 @@ set VCPKG_PATH=C:/apps/vcpkg/vcpkg/ set BUILD_TYPE=Debug + if not exist "build-native\" ( mkdir build-native ) @@ -10,9 +11,9 @@ if not exist "build-native\" ( cd build-native @REM call cmake .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=%VCPKG_PATH%/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -call cmake --build . --config %BUILD_TYPE% +call cmake --build . --config %BUILD_TYPE% cd Test -call Test +@REM call Test @REM cd ../Demos @REM call Demos \ No newline at end of file