diff --git a/core/include/cubos/core/al/audio_context.hpp b/core/include/cubos/core/al/audio_context.hpp index 31375c033f..98b7b14ab4 100644 --- a/core/include/cubos/core/al/audio_context.hpp +++ b/core/include/cubos/core/al/audio_context.hpp @@ -58,18 +58,19 @@ namespace cubos::core::al static std::shared_ptr create(); /// @brief Enumerates the available devices. - /// @param[out] devices Vector to fill with the available device's specifiers. + /// @param[out] devices Vector to fill with the available devices. 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. + /// @param specifier The specifier of the audio device, used to identify it + /// @param listenerCount The number of audio listener to be created by the device's engine. /// @return Handle of the new device - virtual AudioDevice createDevice(unsigned int listenerCount, const std::string& specifier = "") = 0; + virtual AudioDevice createDevice(unsigned int listenerCount, + const std::string& specifier = "MiniaudioDevice") = 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. + /// @param datSize Size of the data to be written. /// @return Handle of the new buffer. virtual Buffer createBuffer(const void* data, size_t dataSize) = 0; }; @@ -85,7 +86,7 @@ namespace cubos::core::al /// @brief Gets the length in seconds of the audio buffer. /// @return Length in seconds of the audio buffer. - virtual float length() = 0; + virtual size_t length() = 0; protected: Buffer() = default; @@ -125,7 +126,7 @@ namespace cubos::core::al /// @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; + virtual void setRelative(bool relative) = 0; /// @brief Sets the maximum distance at which the source is audible. /// @param maxDistance Maximum distance. diff --git a/core/include/cubos/core/al/miniaudio_context.hpp b/core/include/cubos/core/al/miniaudio_context.hpp index 590256dd58..c971bac269 100644 --- a/core/include/cubos/core/al/miniaudio_context.hpp +++ b/core/include/cubos/core/al/miniaudio_context.hpp @@ -18,7 +18,8 @@ namespace cubos::core::al 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(); + + static std::string getDefaultDevice(); private: ma_context mContext; diff --git a/core/src/al/miniaudio_context.cpp b/core/src/al/miniaudio_context.cpp index ca3ad4b52d..3b4d662b95 100644 --- a/core/src/al/miniaudio_context.cpp +++ b/core/src/al/miniaudio_context.cpp @@ -1,7 +1,7 @@ #define MINIAUDIO_IMPLEMENTATION #include +#include #include -#include using namespace cubos::core::al; @@ -16,10 +16,8 @@ class MiniaudioBuffer : public impl::Buffer { CUBOS_ERROR("Failed to initialize Decoder from data"); } - else - { - mValid = true; - } + + mValid = true; } ~MiniaudioBuffer() override @@ -27,19 +25,19 @@ class MiniaudioBuffer : public impl::Buffer ma_decoder_uninit(&decoder); } - float length() override + size_t 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"); + 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); + return static_cast(lengthInPCMFrames) / decoder.outputSampleRate; } bool isValid() const @@ -51,43 +49,6 @@ class MiniaudioBuffer : public impl::Buffer 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: @@ -104,11 +65,17 @@ class MiniaudioSource : public impl::Source void setBuffer(Buffer buffer) override { // Try to dynamically cast the Buffer to a MiniaudioBuffer. - auto miniaudioBuffer = std::static_pointer_cast(buffer); + auto miniaudioBuffer = std::dynamic_pointer_cast(buffer); + + if (miniaudioBuffer == nullptr) + { + CUBOS_FAIL("Buffer is not of type MiniaudioBuffer."); + return; + } if (ma_sound_init_from_data_source(&mEngine, &miniaudioBuffer->decoder, 0, nullptr, &mSound) != MA_SUCCESS) { - CUBOS_ERROR("Failed to initialize sound from buffer"); + CUBOS_ERROR("Failed to initialize sound from buffer."); return; } } @@ -138,16 +105,10 @@ class MiniaudioSource : public impl::Source ma_sound_set_looping(&mSound, static_cast(looping)); } - void setRelative(Listener listener) override + void setRelative(bool relative) 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); + relative ? ma_sound_set_positioning(&mSound, ma_positioning_relative) + : ma_sound_set_positioning(&mSound, ma_positioning_absolute); } void setMaxDistance(float maxDistance) override @@ -174,7 +135,7 @@ class MiniaudioSource : public impl::Source { if (ma_sound_start(&mSound) != MA_SUCCESS) { - CUBOS_ERROR("Failed to start sound"); + CUBOS_ERROR("Failed to start sound."); return; } } @@ -184,11 +145,46 @@ class MiniaudioSource : public impl::Source ma_engine& mEngine; }; +class MiniaudioListener : public impl::Listener +{ +public: + MiniaudioListener(ma_engine& engine, unsigned int index) + : mEngine(engine) + , mIndex(index) + { + } + + ~MiniaudioListener() override + { + } + + 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); + } + +private: + ma_engine& mEngine; + unsigned int mIndex; +}; + class MiniaudioDevice : public impl::AudioDevice { public: MiniaudioDevice(ma_context& context, const std::string& deviceName, ma_uint32 listenerCount) : mContext(context) + , mListeners() { ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; @@ -292,20 +288,28 @@ MiniaudioContext::~MiniaudioContext() std::string MiniaudioContext::getDefaultDevice() { + // Create a temporary context + ma_context context; + if (ma_context_init(nullptr, 0, nullptr, &context) != MA_SUCCESS) + { + CUBOS_ERROR("Failed to initialize audio context for default device lookup"); + return ""; + } + ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; - if (ma_context_get_devices(&mContext, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) + if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) { CUBOS_ERROR("Failed to enumerate audio devices when searching for default"); return ""; } - ma_context_uninit(&mContext); + ma_context_uninit(&context); for (ma_uint32 i = 0; i < playbackDeviceCount; i++) { - if (pPlaybackDeviceInfos[i].isDefault != 0u) + if (pPlaybackDeviceInfos[i].isDefault) { return pPlaybackDeviceInfos[i].name; } @@ -324,7 +328,7 @@ void MiniaudioContext::enumerateDevices(std::vector& devices) if (ma_context_get_devices(&mContext, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) { - CUBOS_ERROR("Failed to enumerate audio devices"); + CUBOS_ERROR("Failed to enumerate audio devices."); return; } @@ -348,7 +352,7 @@ 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"); + CUBOS_ERROR("Failed to create MiniaudioBuffer."); return nullptr; } @@ -360,7 +364,7 @@ AudioDevice MiniaudioContext::createDevice(ma_uint32 listenerCount, const std::s auto device = std::make_shared(mContext, specifier, listenerCount); if (!device->isValid()) { - CUBOS_ERROR("Failed to create MiniaudioDevice"); + CUBOS_ERROR("Failed to create MiniaudioDevice."); return nullptr; } diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index e29913c7ee..5cbe83acfe 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -36,6 +36,12 @@ set(CUBOS_ENGINE_SOURCE "src/utils/free_camera/plugin.cpp" "src/utils/free_camera/controller.cpp" + "src/audio/plugin.cpp" + "src/audio/source.cpp" + "src/audio/listener.cpp" + "src/audio/audio.cpp" + "src/audio/bridge.cpp" + "src/assets/plugin.cpp" "src/assets/assets.cpp" "src/assets/bridge.cpp" diff --git a/engine/include/cubos/engine/audio/audio.hpp b/engine/include/cubos/engine/audio/audio.hpp new file mode 100644 index 0000000000..d427f3d190 --- /dev/null +++ b/engine/include/cubos/engine/audio/audio.hpp @@ -0,0 +1,28 @@ +/// @file +/// @brief Class @ref cubos::engine::Audio. +/// @ingroup audio-plugin +#pragma once + +#include +#include +#include + +#include + +namespace cubos::engine +{ + /// @brief Asset containing raw Audio data. + /// + /// @ingroup audio-plugin + struct CUBOS_ENGINE_API Audio + { + CUBOS_REFLECT; + + /// @brief Audio buffer. + cubos::core::al::Buffer buffer; + + explicit Audio(std::shared_ptr, core::memory::Stream& stream); + Audio(Audio&& other) noexcept; + ~Audio(); + }; +} // namespace cubos::engine diff --git a/engine/include/cubos/engine/audio/bridge.hpp b/engine/include/cubos/engine/audio/bridge.hpp new file mode 100644 index 0000000000..6fd5209668 --- /dev/null +++ b/engine/include/cubos/engine/audio/bridge.hpp @@ -0,0 +1,33 @@ +/// @file +/// @brief Class @ref cubos::engine::AudioBridge. +/// @ingroup audio-plugin + +#pragma once + +#include +#include + +namespace cubos::engine +{ + /// @brief Bridge which loads and saves @ref Sound assets. + /// + /// Uses the format specified in @ref Audio::loadFrom and @ref Audio::writeTo + /// + /// @ingroup audio-plugin + class AudioBridge : public FileBridge + { + public: + std::shared_ptr mContext; + + /// @brief Constructs a bridge. + AudioBridge(std::shared_ptr context) + : FileBridge(core::reflection::reflect