From afa8b3cb0ed3da10cf40c203fb96fbd8dc138d2b Mon Sep 17 00:00:00 2001 From: Diogo Miranda Date: Sun, 6 Oct 2024 15:25:39 +0100 Subject: [PATCH] feat(audio): replace OpenAL audio backend for Miniaudio backend (#1005) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: João Miguel Nogueira <101069446+Dageus@users.noreply.github.com> --- .gitmodules | 6 +- CHANGELOG.md | 1 + core/CMakeLists.txt | 20 +- core/include/cubos/core/al/audio_context.hpp | 199 ++++++++++ core/include/cubos/core/al/audio_device.hpp | 171 -------- .../cubos/core/al/miniaudio_context.hpp | 26 ++ core/lib/miniaudio | 1 + core/lib/openal-soft | 1 - core/src/al/audio_context.cpp | 8 + core/src/al/audio_device.cpp | 13 - core/src/al/miniaudio_context.cpp | 368 ++++++++++++++++++ core/src/al/oal_audio_device.cpp | 262 ------------- core/src/al/oal_audio_device.hpp | 27 -- 13 files changed, 612 insertions(+), 491 deletions(-) create mode 100644 core/include/cubos/core/al/audio_context.hpp delete mode 100644 core/include/cubos/core/al/audio_device.hpp create mode 100644 core/include/cubos/core/al/miniaudio_context.hpp create mode 160000 core/lib/miniaudio delete mode 160000 core/lib/openal-soft create mode 100644 core/src/al/audio_context.cpp delete mode 100644 core/src/al/audio_device.cpp create mode 100644 core/src/al/miniaudio_context.cpp delete mode 100644 core/src/al/oal_audio_device.cpp delete mode 100644 core/src/al/oal_audio_device.hpp diff --git a/.gitmodules b/.gitmodules index 1c42d0a75a..9e184830ac 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "core/lib/glm"] path = core/lib/glm url = https://github.com/g-truc/glm.git -[submodule "core/lib/openal-soft"] - path = core/lib/openal-soft - url = https://github.com/kcat/openal-soft [submodule "core/lib/stduuid"] path = core/lib/stduuid url = https://github.com/mariusbancila/stduuid.git @@ -34,3 +31,6 @@ [submodule "engine/lib/stb_image"] path = engine/lib/stb_image url = https://github.com/GameDevTecnico/cubos-stb.git +[submodule "core/lib/miniaudio"] + path = core/lib/miniaudio + url = https://github.com/mackron/miniaudio diff --git a/CHANGELOG.md b/CHANGELOG.md index 1316cbee6c..3495799951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved Glad and stb-image libs to another repositories, cubos-glad and cubos-stb, respectively (#1323, **@kuukitenshi**). - Moved most tools from Tesseratos to the engine (#1322, **@RiscadoA**). +- Replaced OpenAL audio device with Miniaudio backend (#1005, **@Dageus**, **@diogomsmiranda**) ### Fixed diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9cd7af533b..e3cc390fbf 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -8,7 +8,6 @@ if(WITH_GLFW) option(GLFW_USE_SUBMODULE "Compile GLFW from source?" ON) endif() -option(WITH_OPENAL "With OpenAL?" OFF) option(GLM_USE_SUBMODULE "Compile GLM from source?" ON) set(CUBOS_CORE_ECS_MAX_COMPONENTS "63" CACHE STRING "The maximum number of components registered in an ECS world.") @@ -87,9 +86,8 @@ set(CUBOS_CORE_SOURCE "src/gl/ogl_render_device.cpp" "src/gl/util.cpp" - "src/al/audio_device.cpp" - "src/al/oal_audio_device.cpp" - "src/al/oal_audio_device.hpp" + "src/al/audio_context.cpp" + "src/al/miniaudio_context.cpp" "src/ecs/entity/entity.cpp" "src/ecs/entity/hash.cpp" @@ -193,16 +191,6 @@ if(WITH_GLFW) target_compile_definitions(cubos-core PRIVATE WITH_GLFW) endif() -if(WITH_OPENAL) - set(ALSOFT_UTILS OFF CACHE BOOL "" FORCE) - set(ALSOFT_NO_CONFIG_UTIL OFF CACHE BOOL "" FORCE) - set(ALSOFT_EXAMPLES OFF CACHE BOOL "" FORCE) - add_subdirectory(lib/openal-soft) - target_include_directories(cubos-core PRIVATE lib/openal-soft/include) - target_link_libraries(cubos-core PRIVATE OpenAL) - target_compile_definitions(cubos-core PRIVATE WITH_OPENAL) -endif() - if(GLM_USE_SUBMODULE) add_subdirectory(lib/glm SYSTEM) else() @@ -221,6 +209,10 @@ target_link_libraries(cubos-core PRIVATE cpptrace::cpptrace) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) +add_library(miniaudio INTERFACE) +target_include_directories(miniaudio SYSTEM INTERFACE "lib/miniaudio") +target_link_libraries(cubos-core PUBLIC miniaudio) + target_compile_definitions(cubos-core PUBLIC GLM_FORCE_SILENT_WARNINGS) # Needed for compilation to succeed on MSVC target_link_libraries(cubos-core PUBLIC glm::glm nlohmann_json::nlohmann_json ${CMAKE_DL_LIBS}) target_link_libraries(cubos-core PRIVATE Threads::Threads) diff --git a/core/include/cubos/core/al/audio_context.hpp b/core/include/cubos/core/al/audio_context.hpp new file mode 100644 index 0000000000..31375c033f --- /dev/null +++ b/core/include/cubos/core/al/audio_context.hpp @@ -0,0 +1,199 @@ +/// @file +/// @brief Class @ref cubos::core::al::AudioContext and related types. +/// @ingroup core-al + +#pragma once + +#include +#include +#include + +#include + +#include + +namespace cubos::core::al +{ + namespace impl + { + class Buffer; + class Source; + class Listener; + class AudioDevice; + } // namespace impl + + /// @brief Handle to an audio buffer. + /// @see impl::Buffer - audio buffer interface. + /// @see AudioContext::createBuffer() + /// @ingroup core-al + using Buffer = std::shared_ptr; + + /// @brief Handle to an audio source. + /// @see impl::Source - audio source interface. + /// @see AudioDevice::createSource() + /// @ingroup core-al + using Source = std::shared_ptr; + + /// @brief Handle to an audio listener. + /// @see impl::Listener - audio listener interface. + /// @see AudioDevice::createListener() + /// @ingroup core-al + using Listener = std::shared_ptr; + + /// @brief Handle to an audio device. + /// @see impl::AudioDevice - audio device interface. + /// @see AudioContext::createDevice() + /// @ingroup core-al + using AudioDevice = std::shared_ptr; + + /// @brief Audio context that contains audio devices; + class CUBOS_CORE_API AudioContext + { + public: + AudioContext() = default; + virtual ~AudioContext() = default; + + /// @brief Creates an audio context. + /// @return AudioContext, or nullptr on failure. + static std::shared_ptr create(); + + /// @brief Enumerates the available devices. + /// @param[out] devices Vector to fill with the available device's specifiers. + virtual void enumerateDevices(std::vector& devices) = 0; + + /// @brief Creates a new audio device + /// @param listenerCount Number of audio listeners to be supported by the device. + /// @param specifier Identifier of the audio device. + /// @return Handle of the new device + virtual AudioDevice createDevice(unsigned int listenerCount, const std::string& specifier = "") = 0; + + /// @brief Creates a new audio buffer. + /// @param data Data to be written to the buffer, either .wav, .mp3 or .flac. + /// @param dataSize Size of the data to be written. + /// @return Handle of the new buffer. + virtual Buffer createBuffer(const void* data, size_t dataSize) = 0; + }; + + /// @brief Namespace to store the abstract types implemented by the audio device implementations. + namespace impl + { + /// @brief Abstract audio buffer. + class CUBOS_CORE_API Buffer + { + public: + virtual ~Buffer() = default; + + /// @brief Gets the length in seconds of the audio buffer. + /// @return Length in seconds of the audio buffer. + virtual float length() = 0; + + protected: + Buffer() = default; + }; + + /// @brief Abstract audio source. + class CUBOS_CORE_API Source + { + public: + virtual ~Source() = default; + + /// @brief Sets the buffer to be played by the source. + /// @param buffer Buffer. + virtual void setBuffer(cubos::core::al::Buffer buffer) = 0; + + /// @brief Sets the position of the source, by default, in the world space. + /// @see setRelative() to change this behavior. + /// @param position Position. + virtual void setPosition(const glm::vec3& position) = 0; + + /// @brief Sets the velocity of the source, by default, in the world space. + /// @param velocity Velocity. + virtual void setVelocity(const glm::vec3& velocity) = 0; + + /// @brief Sets the gain of the source. + /// @param gain Gain. + virtual void setGain(float gain) = 0; + + /// @brief Sets the pitch of the source. + /// @param pitch Pitch. + virtual void setPitch(float pitch) = 0; + + /// @brief Sets whether the source plays in a loop. + /// @param looping Looping flag. + virtual void setLooping(bool looping) = 0; + + /// @brief Sets whether the source position and velocity is relative to the listener or + /// not. + /// @param relative Relative flag. + virtual void setRelative(cubos::core::al::Listener listener) = 0; + + /// @brief Sets the maximum distance at which the source is audible. + /// @param maxDistance Maximum distance. + virtual void setMaxDistance(float maxDistance) = 0; + + /// @brief Sets the minimum distance at which the source starts to attenuate. + /// @param minDistance Minimum distance. + virtual void setMinDistance(float minDistance) = 0; + + /// @brief Sets the cone angle, in degrees. While also setting the outerGain. + /// @param innerAngle Outer angle, in degrees. + /// @param outerAngle Inner angle, in degrees. + /// @param coneGain Gain. + virtual void setCone(float innerAngle, float outerAngle, float outerGain) = 0; + + /// @brief Sets the cone direction of the source. + /// @param direction Direction. + virtual void setConeDirection(const glm::vec3& direction) = 0; + + /// @brief Plays the source. + virtual void play() = 0; + + protected: + Source() = default; + }; + + // Abstract audio listener. + class CUBOS_CORE_API Listener + { + public: + virtual ~Listener() = default; + + /// @brief Sets the velocity of the listener. Used to implement the doppler effect. + /// @param velocity Velocity of the listener. + virtual void setVelocity(const glm::vec3& velocity) = 0; + + /// @brief Sets the position of the listener. + /// @param position Position. + virtual void setPosition(const glm::vec3& position) = 0; + + /// @brief Sets the orientation of the listener. + /// @param forward Forward direction of the listener. + /// @param up Up direction of the listener. + virtual void setOrientation(const glm::vec3& forward, const glm::vec3& up) = 0; + + protected: + Listener() = default; + }; + + /// @brief Audio device interface used to wrap low-level audio rendering APIs. + class CUBOS_CORE_API AudioDevice + { + public: + virtual ~AudioDevice() = default; + + /// @brief Forbid copy construction. + AudioDevice(const AudioDevice&) = delete; + + /// @brief Creates a new audio source. + /// @return Handle of the new source. + virtual std::shared_ptr createSource() = 0; + + /// @brief Creates a new audio listener. + /// @return Handle of the new listener. + virtual std::shared_ptr listener(size_t index) = 0; + + protected: + AudioDevice() = default; + }; + } // namespace impl +} // namespace cubos::core::al diff --git a/core/include/cubos/core/al/audio_device.hpp b/core/include/cubos/core/al/audio_device.hpp deleted file mode 100644 index 894498a5c8..0000000000 --- a/core/include/cubos/core/al/audio_device.hpp +++ /dev/null @@ -1,171 +0,0 @@ -/// @file -/// @brief Class @ref cubos::core::al::AudioDevice and related types. -/// @ingroup core-al - -#pragma once - -#include -#include -#include - -#include - -#include - -namespace cubos::core::al -{ - namespace impl - { - class Buffer; - class Source; - } // namespace impl - - /// @brief Handle to an audio buffer. - /// @see impl::Buffer - audio buffer interface. - /// @see AudioDevice::createBuffer() - /// @ingroup core-al - using Buffer = std::shared_ptr; - - /// @brief Handle to an audio source. - /// @see impl::Source - audio source interface. - /// @see AudioDevice::createSource() - /// @ingroup core-al - using Source = std::shared_ptr; - - /// @brief Possible audio formats. - enum class Format - { - Mono8, - Mono16, - Stereo8, - Stereo16, - }; - - /// @brief Audio device interface used to wrap low-level audio rendering APIs. - class CUBOS_CORE_API AudioDevice - { - public: - AudioDevice() = default; - virtual ~AudioDevice() = default; - - /// @brief Forbid copy construction. - AudioDevice(const AudioDevice&) = delete; - - /// @brief Creates an audio device from a given device @p specifier. - /// @see enumerateDevices() - /// @param specifier Device specifier (empty for default). - /// @return Audio device, or nullptr on failure. - static std::shared_ptr create(const std::string& specifier = ""); - - /// @brief Enumerates the available devices. - /// @param[out] devices Vector to fill with the available devices. - static void enumerateDevices(std::vector& devices); - - /// @brief Creates a new audio buffer - /// @return Handle of the new buffer. - virtual Buffer createBuffer() = 0; - - /// @brief Creates a new audio source. - /// @return Handle of the new source. - virtual Source createSource() = 0; - - /// @brief Sets the position of the listener. - /// @param position Position. - virtual void setListenerPosition(const glm::vec3& position) = 0; - - /// @brief Sets the orientation of the listener. - /// @param forward Forward direction of the listener. - /// @param up Up direction of the listener. - virtual void setListenerOrientation(const glm::vec3& forward, const glm::vec3& up) = 0; - - /// @brief Sets the velocity of the listener. Used to implement the doppler effect. - /// @param velocity Velocity of the listener. - virtual void setListenerVelocity(const glm::vec3& velocity) = 0; - }; - - /// @brief Namespace to store the abstract types implemented by the audio device implementations. - namespace impl - { - /// @brief Abstract audio buffer. - class CUBOS_CORE_API Buffer - { - public: - virtual ~Buffer() = default; - - /// @brief Fills the buffer with data. - /// @param format Audio format of the data. - /// @param size Size of the buffer in bytes. - /// @param data Buffer data. - /// @param frequency Audio frequency. - virtual void fill(Format format, std::size_t size, const void* data, std::size_t frequency) = 0; - - protected: - Buffer() = default; - }; - - /// @brief Abstract audio source. - class CUBOS_CORE_API Source - { - public: - virtual ~Source() = default; - - /// @brief Sets the buffer to be played by the source. - /// @param buffer Buffer. - virtual void setBuffer(std::shared_ptr buffer) = 0; - - /// @brief Sets the position of the source, by default, in the world space. - /// @see setRelative() to change this behavior. - /// @param position Position. - virtual void setPosition(const glm::vec3& position) = 0; - - /// @brief Sets the velocity of the source, by default, in the world space. - /// @param velocity Velocity. - virtual void setVelocity(const glm::vec3& velocity) = 0; - - /// @brief Sets the gain of the source. - /// @param gain Gain. - virtual void setGain(float gain) = 0; - - /// @brief Sets the pitch of the source. - /// @param pitch Pitch. - virtual void setPitch(float pitch) = 0; - - /// @brief Sets whether the source plays in a loop. - /// @param looping Looping flag. - virtual void setLooping(bool looping) = 0; - - /// @brief Sets whether the source position and velocity is relative to the listener or - /// not. - /// @param relative Relative flag. - virtual void setRelative(bool relative) = 0; - - /// @brief Sets the maximum distance at which the source is audible. - /// @param maxDistance Maximum distance. - virtual void setDistance(float maxDistance) = 0; - - /// @brief Sets the cone angle of the source, in degrees. By default, 360. - /// @param coneAngle Angle, in degrees. - virtual void setConeAngle(float coneAngle) = 0; - - /// @brief Sets the cone gain of the source. - /// @todo Find out what this is. - /// @param coneGain Gain. - virtual void setConeGain(float coneGain) = 0; - - /// @brief Sets the cone direction of the source. - /// @param direction Direction. - virtual void setConeDirection(const glm::vec3& direction) = 0; - - /// @brief Sets the distance under which the volume for the source would normally drop - /// by half. - /// @param referenceDistance Distance. - virtual void setReferenceDistance(float referenceDistance) = 0; - - /// @brief Plays the source. - virtual void play() = 0; - - protected: - Source() = default; - }; - } // namespace impl -} // namespace cubos::core::al diff --git a/core/include/cubos/core/al/miniaudio_context.hpp b/core/include/cubos/core/al/miniaudio_context.hpp new file mode 100644 index 0000000000..590256dd58 --- /dev/null +++ b/core/include/cubos/core/al/miniaudio_context.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +#include + +namespace cubos::core::al +{ + /// Audio device implementation using miniaudio. + class MiniaudioContext : public AudioContext + { + public: + MiniaudioContext(); + ~MiniaudioContext() override; + + AudioDevice createDevice(unsigned int listenerCount, const std::string& specifier) override; + Buffer createBuffer(const void* data, size_t dataSize) override; + void enumerateDevices(std::vector& devices) override; + std::string getDefaultDevice(); + + private: + ma_context mContext; + }; +} // namespace cubos::core::al diff --git a/core/lib/miniaudio b/core/lib/miniaudio new file mode 160000 index 0000000000..4a5b74bef0 --- /dev/null +++ b/core/lib/miniaudio @@ -0,0 +1 @@ +Subproject commit 4a5b74bef029b3592c54b6048650ee5f972c1a48 diff --git a/core/lib/openal-soft b/core/lib/openal-soft deleted file mode 160000 index d3875f333f..0000000000 --- a/core/lib/openal-soft +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d3875f333fb6abe2f39d82caca329414871ae53b diff --git a/core/src/al/audio_context.cpp b/core/src/al/audio_context.cpp new file mode 100644 index 0000000000..120547f2c9 --- /dev/null +++ b/core/src/al/audio_context.cpp @@ -0,0 +1,8 @@ +#include + +using namespace cubos::core::al; + +std::shared_ptr AudioContext::create() +{ + return std::make_shared(); +} diff --git a/core/src/al/audio_device.cpp b/core/src/al/audio_device.cpp deleted file mode 100644 index df78baa603..0000000000 --- a/core/src/al/audio_device.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "oal_audio_device.hpp" - -using namespace cubos::core::al; - -std::shared_ptr AudioDevice::create(const std::string& specifier) -{ - return std::make_shared(specifier); -} - -void AudioDevice::enumerateDevices(std::vector& devices) -{ - OALAudioDevice::enumerateDevices(devices); -} diff --git a/core/src/al/miniaudio_context.cpp b/core/src/al/miniaudio_context.cpp new file mode 100644 index 0000000000..ed5c665590 --- /dev/null +++ b/core/src/al/miniaudio_context.cpp @@ -0,0 +1,368 @@ +#define MINIAUDIO_IMPLEMENTATION +#include +#include +#include + +using namespace cubos::core::al; + +class MiniaudioBuffer : public impl::Buffer +{ +public: + ma_decoder decoder; + + MiniaudioBuffer(const void* data, size_t dataSize) + { + if (ma_decoder_init_memory(data, dataSize, nullptr, &decoder) != MA_SUCCESS) + { + CUBOS_ERROR("Failed to initialize Decoder from data"); + } + else + { + mValid = true; + } + } + + ~MiniaudioBuffer() override + { + ma_decoder_uninit(&decoder); + } + + float length() override + { + ma_uint64 lengthInPCMFrames; + ma_result result = ma_decoder_get_length_in_pcm_frames(&decoder, &lengthInPCMFrames); + + if (result != MA_SUCCESS) + { + CUBOS_ERROR("Failed to get the length of audio in PCM frames"); + return 0; + } + + // Calculate the length in seconds: Length in PCM frames divided by the sample rate. + return static_cast(lengthInPCMFrames) / static_cast(decoder.outputSampleRate); + } + + bool isValid() const + { + return mValid; + } + +private: + bool mValid = false; +}; + +class MiniaudioListener : public impl::Listener +{ +public: + MiniaudioListener(ma_engine& engine, unsigned int index) + : mEngine(engine) + , mIndex(index) + { + } + + ~MiniaudioListener() override = default; + + void setPosition(const glm::vec3& position) override + { + ma_engine_listener_set_position(&mEngine, mIndex, position.x, position.y, position.z); + } + + void setOrientation(const glm::vec3& forward, const glm::vec3& up) override + { + ma_engine_listener_set_direction(&mEngine, mIndex, forward.x, forward.y, forward.z); + ma_engine_listener_set_world_up(&mEngine, mIndex, up.x, up.y, up.z); + } + + void setVelocity(const glm::vec3& velocity) override + { + ma_engine_listener_set_velocity(&mEngine, mIndex, velocity.x, velocity.y, velocity.z); + } + + unsigned int index() const + { + return mIndex; + } + +private: + ma_engine& mEngine; + unsigned int mIndex; +}; + +class MiniaudioSource : public impl::Source +{ +public: + MiniaudioSource(ma_engine& engine) + : mEngine(engine) + { + } + + ~MiniaudioSource() override + { + ma_sound_uninit(&mSound); + } + + void setBuffer(Buffer buffer) override + { + // Try to dynamically cast the Buffer to a MiniaudioBuffer. + auto miniaudioBuffer = std::static_pointer_cast(buffer); + + if (ma_sound_init_from_data_source(&mEngine, &miniaudioBuffer->decoder, 0, nullptr, &mSound) != MA_SUCCESS) + { + CUBOS_ERROR("Failed to initialize sound from buffer"); + return; + } + } + + void setPosition(const glm::vec3& position) override + { + ma_sound_set_position(&mSound, position.x, position.y, position.z); + } + + void setVelocity(const glm::vec3& velocity) override + { + ma_sound_set_velocity(&mSound, velocity.x, velocity.y, velocity.z); + } + + void setGain(float gain) override + { + ma_sound_set_volume(&mSound, gain); + } + + void setPitch(float pitch) override + { + ma_sound_set_pitch(&mSound, pitch); + } + + void setLooping(bool looping) override + { + ma_sound_set_looping(&mSound, static_cast(looping)); + } + + void setRelative(Listener listener) override + { + CUBOS_ASSERT(listener != nullptr); + + // Try to dynamically cast the Listener to a MiniaudioListener. + auto miniaudioListener = std::static_pointer_cast(listener); + + ma_sound_set_pinned_listener_index(&mSound, miniaudioListener->index()); + + ma_sound_set_positioning(&mSound, ma_positioning_relative); + } + + void setMaxDistance(float maxDistance) override + { + ma_sound_set_max_distance(&mSound, maxDistance); + } + + void setMinDistance(float minDistance) override + { + ma_sound_set_min_distance(&mSound, minDistance); + } + + void setCone(float innerAngle, float outerAngle, float outerGain = 1.0F) override + { + ma_sound_set_cone(&mSound, innerAngle, outerAngle, outerGain); + } + + void setConeDirection(const glm::vec3& direction) override + { + ma_sound_set_direction(&mSound, direction.x, direction.y, direction.z); + } + + void play() override + { + if (ma_sound_start(&mSound) != MA_SUCCESS) + { + CUBOS_ERROR("Failed to start sound"); + return; + } + } + +private: + ma_sound mSound; + ma_engine& mEngine; +}; + +class MiniaudioDevice : public impl::AudioDevice +{ +public: + MiniaudioDevice(ma_context& context, const std::string& deviceName, ma_uint32 listenerCount) + : mContext(context) + { + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount; + ma_result result = + ma_context_get_devices(&mContext, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr); + + if (result != MA_SUCCESS) + { + CUBOS_FAIL("Failed to enumerate audio devices"); + return; + } + + ma_device_id* deviceId = nullptr; + for (ma_uint32 i = 0; i < playbackDeviceCount; i++) + { + if (deviceName == pPlaybackDeviceInfos[i].name) + { + deviceId = &pPlaybackDeviceInfos[i].id; + break; + } + } + + if (deviceId == nullptr) + { + CUBOS_FAIL("Audio device '{}' not found", deviceName); + return; + } + + ma_engine_config engineConfig = ma_engine_config_init(); + + if (listenerCount > MA_ENGINE_MAX_LISTENERS) + { + CUBOS_FAIL("Maximum number of listeners is 4"); + return; + } + + engineConfig.listenerCount = listenerCount; + engineConfig.pPlaybackDeviceID = deviceId; // Use the found device ID + + if (ma_engine_init(&engineConfig, &mEngine) != MA_SUCCESS) + { + CUBOS_FAIL("Failed to initialize audio engine"); + return; + } + + mValid = true; + + mListeners.reserve(listenerCount); + for (ma_uint32 i = 0; i < listenerCount; ++i) + { + mListeners.emplace_back(std::make_shared(mEngine, i)); + } + } + + ~MiniaudioDevice() override + { + ma_device_uninit(&mDevice); + } + + Source createSource() override + { + return std::make_shared(mEngine); + } + + Listener listener(size_t index) override + { + if (index >= mListeners.size()) + { + CUBOS_ERROR("Listener index out of range"); + return nullptr; + } + return mListeners[index]; + } + + bool isValid() const + { + return mValid; + } + +private: + ma_context mContext; + ma_device mDevice; + ma_engine mEngine; + std::vector> mListeners; + bool mValid = false; +}; + +MiniaudioContext::MiniaudioContext() +{ + if (ma_context_init(nullptr, 0, nullptr, &mContext) != MA_SUCCESS) + { + CUBOS_FAIL("Failed to initialize audio context."); + return; + } +} + +MiniaudioContext::~MiniaudioContext() +{ + ma_context_uninit(&mContext); +} + +std::string MiniaudioContext::getDefaultDevice() +{ + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount; + + if (ma_context_get_devices(&mContext, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) + { + CUBOS_ERROR("Failed to enumerate audio devices when searching for default"); + return ""; + } + + ma_context_uninit(&mContext); + + for (ma_uint32 i = 0; i < playbackDeviceCount; i++) + { + if (pPlaybackDeviceInfos[i].isDefault != 0u) + { + return pPlaybackDeviceInfos[i].name; + } + } + + CUBOS_WARN("No default audio device found"); + return ""; +} + +void MiniaudioContext::enumerateDevices(std::vector& devices) +{ + devices.clear(); + + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount; + + if (ma_context_get_devices(&mContext, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) + { + CUBOS_ERROR("Failed to enumerate audio devices"); + return; + } + + ma_context_uninit(&mContext); + + devices.reserve(playbackDeviceCount); + + for (ma_uint32 i = 0; i < playbackDeviceCount; i++) + { + devices.emplace_back(pPlaybackDeviceInfos[i].name); + } + + if (devices.empty()) + { + CUBOS_WARN("No audio playback devices found"); + } +} + +Buffer MiniaudioContext::createBuffer(const void* data, size_t dataSize) +{ + auto buffer = std::make_shared(data, dataSize); + if (!buffer->isValid()) + { + CUBOS_ERROR("Failed to create MiniaudioBuffer"); + return nullptr; + } + + return buffer; +} + +AudioDevice MiniaudioContext::createDevice(ma_uint32 listenerCount, const std::string& specifier) +{ + auto device = std::make_shared(mContext, specifier, listenerCount); + if (!device->isValid()) + { + CUBOS_ERROR("Failed to create MiniaudioDevice"); + return nullptr; + } + + return device; +} diff --git a/core/src/al/oal_audio_device.cpp b/core/src/al/oal_audio_device.cpp deleted file mode 100644 index e3890f3cba..0000000000 --- a/core/src/al/oal_audio_device.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "oal_audio_device.hpp" - -#include - -#ifdef WITH_OPENAL -#include -#include -#endif // WITH_OPENAL - -#include - -#define UNSUPPORTED() \ - do \ - { \ - CUBOS_CRITICAL("Unsupported when building without OpenAL"); \ - abort(); \ - } while (0) - -using namespace cubos::core::al; - -#ifdef WITH_OPENAL -class OALBuffer : public impl::Buffer -{ -public: - OALBuffer(ALuint id) - : id(id) - { - } - - ~OALBuffer() override - { - alDeleteBuffers(1, &this->id); - } - - void fill(Format format, std::size_t size, const void* data, std::size_t frequency) override - { - ALenum alFormat = 0; - - switch (format) - { - case Format::Mono8: - alFormat = AL_FORMAT_MONO8; - break; - - case Format::Mono16: - alFormat = AL_FORMAT_MONO16; - break; - - case Format::Stereo8: - alFormat = AL_FORMAT_STEREO8; - break; - - case Format::Stereo16: - alFormat = AL_FORMAT_STEREO16; - break; - } - - alBufferData(this->id, alFormat, data, static_cast(size), static_cast(frequency)); - } - - ALuint id; -}; - -class OALSource : public impl::Source -{ -public: - OALSource(ALuint id) - : id(id) - { - } - - ~OALSource() override - { - alDeleteSources(1, &this->id); - } - - void setBuffer(Buffer buffer) override - { - auto oalBuffer = std::dynamic_pointer_cast(buffer); - alSourcei(this->id, AL_BUFFER, static_cast(oalBuffer->id)); - } - - void setPosition(const glm::vec3& position) override - { - alSource3f(this->id, AL_POSITION, position.x, position.y, position.z); - } - - void setVelocity(const glm::vec3& velocity) override - { - alSource3f(this->id, AL_VELOCITY, velocity.x, velocity.y, velocity.z); - } - - void setGain(float gain) override - { - alSourcef(this->id, AL_GAIN, gain); - } - - void setPitch(float pitch) override - { - alSourcef(this->id, AL_PITCH, pitch); - } - - void setLooping(bool looping) override - { - alSourcei(this->id, AL_LOOPING, static_cast(looping)); - } - - void setRelative(bool relative) override - { - alSourcei(this->id, AL_SOURCE_RELATIVE, static_cast(relative)); - } - - void setDistance(float maxDistance) override - { - alSourcef(this->id, AL_MAX_DISTANCE, maxDistance); - } - - void setConeAngle(float coneAngle) override - { - alSourcef(this->id, AL_CONE_OUTER_ANGLE, coneAngle); - } - - void setConeGain(float coneGain) override - { - alSourcef(this->id, AL_CONE_INNER_ANGLE, coneGain); - } - - void setConeDirection(const glm::vec3& direction) override - { - alSource3f(this->id, AL_DIRECTION, direction.x, direction.y, direction.z); - } - - void setReferenceDistance(float referenceDistance) override - { - alSourcef(this->id, AL_REFERENCE_DISTANCE, referenceDistance); - } - - void play() override - { - alSourcePlay(this->id); - } - - ALuint id; -}; -#endif // WITH_OPENAL - -OALAudioDevice::OALAudioDevice(const std::string& specifier) -{ -#ifdef WITH_OPENAL - auto* device = alcOpenDevice(specifier.c_str()); - auto* context = alcCreateContext(device, nullptr); - alcMakeContextCurrent(context); -#else - (void)specifier; - UNSUPPORTED(); -#endif // WITH_OPENAL -} - -OALAudioDevice::~OALAudioDevice() // NOLINT(modernize-use-equals-default) -{ -#ifdef WITH_OPENAL - auto* context = alcGetCurrentContext(); - auto* device = alcGetContextsDevice(context); - alcMakeContextCurrent(nullptr); - alcDestroyContext(context); - alcCloseDevice(device); -#endif // WITH_OPENAL -} - -void OALAudioDevice::enumerateDevices(std::vector& devices) -{ -#ifdef WITH_OPENAL - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") == AL_FALSE) - { - CUBOS_CRITICAL("Missing extension ALC_ENUMERATION_EXT"); - abort(); - } - - const char* pointer = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); - - while (*pointer != '\0') - { - std::string s = pointer; - devices.push_back(s); - pointer += s.size() + 1; - } -#else - (void)devices; - UNSUPPORTED(); -#endif // WITH_OPENAL -} - -std::string OALAudioDevice::getDefaultDevice() -{ -#ifdef WITH_OPENAL - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") == AL_FALSE) - { - CUBOS_CRITICAL("Missing extension ALC_ENUMERATION_EXT"); - abort(); - } - - return alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); -#else - UNSUPPORTED(); -#endif // WITH_OPENAL -} - -Buffer OALAudioDevice::createBuffer() -{ -#ifdef WITH_OPENAL - ALuint id; - alGenBuffers(1, &id); - - return std::make_shared(id); -#else - UNSUPPORTED(); -#endif // WITH_OPENAL -} - -Source OALAudioDevice::createSource() -{ -#ifdef WITH_OPENAL - ALuint sources; - alGenSources(1, &sources); - - return std::make_shared(sources); -#else - UNSUPPORTED(); -#endif // WITH_OPENAL -} - -void OALAudioDevice::setListenerPosition(const glm::vec3& position) -{ -#ifdef WITH_OPENAL - alListener3f(AL_POSITION, position.x, position.y, position.z); -#else - (void)position; - UNSUPPORTED(); -#endif // WITH_OPENAL -} - -void OALAudioDevice::setListenerOrientation(const glm::vec3& forward, const glm::vec3& up) -{ -#ifdef WITH_OPENAL - float orientation[6] = {forward.x, forward.y, forward.z, up.x, up.y, up.z}; - alListenerfv(AL_ORIENTATION, orientation); -#else - (void)forward; - (void)up; - UNSUPPORTED(); -#endif // WITH_OPENAL -} - -void OALAudioDevice::setListenerVelocity(const glm::vec3& velocity) -{ -#ifdef WITH_OPENAL - alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z); -#else - (void)velocity; - UNSUPPORTED(); -#endif // WITH_OPENAL -} diff --git a/core/src/al/oal_audio_device.hpp b/core/src/al/oal_audio_device.hpp deleted file mode 100644 index edba976809..0000000000 --- a/core/src/al/oal_audio_device.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -namespace cubos::core::al -{ - /// Audio device implementation using OpenAL. - /// @see AudioDevice. - class OALAudioDevice : public AudioDevice - { - public: - /// @param specifier Specifies the device to use (empty for default). - OALAudioDevice(const std::string& specifier = ""); - ~OALAudioDevice() override; - - /// Enumerates the available devices. - /// @param devices The vector to fill with the available devices. - static void enumerateDevices(std::vector& devices); - static std::string getDefaultDevice(); - - Buffer createBuffer() override; - Source createSource() override; - void setListenerPosition(const glm::vec3& position) override; - void setListenerOrientation(const glm::vec3& forward, const glm::vec3& up) override; - void setListenerVelocity(const glm::vec3& velocity) override; - }; -} // namespace cubos::core::al