diff --git a/core/include/cubos/core/al/audio_context.hpp b/core/include/cubos/core/al/audio_context.hpp index 20894bfc56..98b7b14ab4 100644 --- a/core/include/cubos/core/al/audio_context.hpp +++ b/core/include/cubos/core/al/audio_context.hpp @@ -19,11 +19,12 @@ namespace cubos::core::al class Buffer; class Source; class Listener; + class AudioDevice; } // namespace impl /// @brief Handle to an audio buffer. /// @see impl::Buffer - audio buffer interface. - /// @see AudioDevice::createBuffer() + /// @see AudioContext::createBuffer() /// @ingroup core-al using Buffer = std::shared_ptr; @@ -39,26 +40,11 @@ namespace cubos::core::al /// @ingroup core-al using Listener = std::shared_ptr; - /// @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 Source createSource() = 0; - - /// @brief Creates a new audio listener. - /// @return Handle of the new listener. - virtual Listener listener(size_t index) = 0; - - protected: - AudioDevice() = default; - }; + /// @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 @@ -73,15 +59,17 @@ namespace cubos::core::al /// @brief Enumerates the available devices. /// @param[out] devices Vector to fill with the available devices. - static void enumerateDevices(std::vector& devices); + virtual void enumerateDevices(std::vector& devices) = 0; /// @brief Creates a new 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 std::shared_ptr createDevice(const std::string& specifier, unsigned int listenerCount) = 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, cubos currently supports .wav, .mp3 and .flac files. + /// @param data Data to be written to the buffer, either .wav, .mp3 or .flac. /// @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; @@ -95,7 +83,10 @@ namespace cubos::core::al { public: virtual ~Buffer() = default; - virtual size_t getLength() = 0; + + /// @brief Gets the length in seconds of the audio buffer. + /// @return Length in seconds of the audio buffer. + virtual size_t length() = 0; protected: Buffer() = default; @@ -162,6 +153,7 @@ namespace cubos::core::al Source() = default; }; + // Abstract audio listener. class CUBOS_CORE_API Listener { public: @@ -183,5 +175,26 @@ namespace cubos::core::al 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/miniaudio_context.hpp b/core/include/cubos/core/al/miniaudio_context.hpp index 630f74ff97..c971bac269 100644 --- a/core/include/cubos/core/al/miniaudio_context.hpp +++ b/core/include/cubos/core/al/miniaudio_context.hpp @@ -15,10 +15,10 @@ namespace cubos::core::al MiniaudioContext(); ~MiniaudioContext() override; - std::shared_ptr createDevice(const std::string& specifier, unsigned int listenerCount) 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; - static void enumerateDevices(std::vector& devices); static std::string getDefaultDevice(); private: diff --git a/core/src/al/audio_context.cpp b/core/src/al/audio_context.cpp index 0b49f19caf..120547f2c9 100644 --- a/core/src/al/audio_context.cpp +++ b/core/src/al/audio_context.cpp @@ -6,8 +6,3 @@ std::shared_ptr AudioContext::create() { return std::make_shared(); } - -void AudioContext::enumerateDevices(std::vector& devices) -{ - MiniaudioContext::enumerateDevices(devices); -} diff --git a/core/src/al/miniaudio_context.cpp b/core/src/al/miniaudio_context.cpp index 1b1abe3167..3b4d662b95 100644 --- a/core/src/al/miniaudio_context.cpp +++ b/core/src/al/miniaudio_context.cpp @@ -3,31 +3,32 @@ #include #include -// TODO: settings.getInteger("audio ") - using namespace cubos::core::al; class MiniaudioBuffer : public impl::Buffer { public: - ma_decoder mDecoder; + ma_decoder decoder; + MiniaudioBuffer(const void* data, size_t dataSize) { - if (ma_decoder_init_memory(data, dataSize, nullptr, &mDecoder) != MA_SUCCESS) + if (ma_decoder_init_memory(data, dataSize, nullptr, &decoder) != MA_SUCCESS) { CUBOS_ERROR("Failed to initialize Decoder from data"); } + + mValid = true; } ~MiniaudioBuffer() override { - ma_decoder_uninit(&mDecoder); + ma_decoder_uninit(&decoder); } - size_t getLength() override + size_t length() override { ma_uint64 lengthInPCMFrames; - ma_result result = ma_decoder_get_length_in_pcm_frames(&mDecoder, &lengthInPCMFrames); + ma_result result = ma_decoder_get_length_in_pcm_frames(&decoder, &lengthInPCMFrames); if (result != MA_SUCCESS) { @@ -36,8 +37,16 @@ class MiniaudioBuffer : public impl::Buffer } // Calculate the length in seconds: Length in PCM frames divided by the sample rate. - return static_cast(lengthInPCMFrames) / mDecoder.outputSampleRate; + return static_cast(lengthInPCMFrames) / decoder.outputSampleRate; + } + + bool isValid() const + { + return mValid; } + +private: + bool mValid = false; }; class MiniaudioSource : public impl::Source @@ -64,7 +73,7 @@ class MiniaudioSource : public impl::Source return; } - if (ma_sound_init_from_data_source(&mEngine, &miniaudioBuffer->mDecoder, 0, nullptr, &mSound) != MA_SUCCESS) + if (ma_sound_init_from_data_source(&mEngine, &miniaudioBuffer->decoder, 0, nullptr, &mSound) != MA_SUCCESS) { CUBOS_ERROR("Failed to initialize sound from buffer."); return; @@ -133,7 +142,7 @@ class MiniaudioSource : public impl::Source private: ma_sound mSound; - ma_engine mEngine; + ma_engine& mEngine; }; class MiniaudioListener : public impl::Listener @@ -170,7 +179,7 @@ class MiniaudioListener : public impl::Listener unsigned int mIndex; }; -class MiniaudioDevice : public cubos::core::al::AudioDevice +class MiniaudioDevice : public impl::AudioDevice { public: MiniaudioDevice(ma_context& context, const std::string& deviceName, ma_uint32 listenerCount) @@ -221,6 +230,8 @@ class MiniaudioDevice : public cubos::core::al::AudioDevice return; } + mValid = true; + mListeners.reserve(listenerCount); for (ma_uint32 i = 0; i < listenerCount; ++i) { @@ -248,11 +259,17 @@ class MiniaudioDevice : public cubos::core::al::AudioDevice 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() @@ -306,25 +323,16 @@ void MiniaudioContext::enumerateDevices(std::vector& devices) { devices.clear(); - // 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 enumeration"); - return; - } - ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; - if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) + if (ma_context_get_devices(&mContext, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) { CUBOS_ERROR("Failed to enumerate audio devices."); return; } - ma_context_uninit(&context); + ma_context_uninit(&mContext); devices.reserve(playbackDeviceCount); @@ -341,10 +349,24 @@ void MiniaudioContext::enumerateDevices(std::vector& devices) Buffer MiniaudioContext::createBuffer(const void* data, size_t dataSize) { - return std::make_shared(data, dataSize); + auto buffer = std::make_shared(data, dataSize); + if (!buffer->isValid()) + { + CUBOS_ERROR("Failed to create MiniaudioBuffer."); + return nullptr; + } + + return buffer; } -std::shared_ptr MiniaudioContext::createDevice(const std::string& specifier, ma_uint32 listenerCount) +AudioDevice MiniaudioContext::createDevice(ma_uint32 listenerCount, const std::string& specifier) { - return std::make_shared(mContext, specifier, listenerCount); + auto device = std::make_shared(mContext, specifier, listenerCount); + if (!device->isValid()) + { + CUBOS_ERROR("Failed to create MiniaudioDevice."); + return nullptr; + } + + return device; } diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index b5dd9dd3a0..a078a3eb23 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -36,6 +36,9 @@ 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" diff --git a/engine/include/cubos/engine/audio/audio.hpp b/engine/include/cubos/engine/audio/audio.hpp index 62f7c3bda4..d427f3d190 100644 --- a/engine/include/cubos/engine/audio/audio.hpp +++ b/engine/include/cubos/engine/audio/audio.hpp @@ -17,10 +17,11 @@ namespace cubos::engine struct CUBOS_ENGINE_API Audio { CUBOS_REFLECT; - std::shared_ptr mData; // Raw data of the audio - size_t mLength; // Audio length in seconds TODO: add getter in audio - explicit Audio(std::shared_ptr, core::memory::Stream& stream); + /// @brief Audio buffer. + cubos::core::al::Buffer buffer; + + explicit Audio(std::shared_ptr, core::memory::Stream& stream); Audio(Audio&& other) noexcept; ~Audio(); }; diff --git a/engine/include/cubos/engine/audio/bridge.hpp b/engine/include/cubos/engine/audio/bridge.hpp index fb196361a1..6fd5209668 100644 --- a/engine/include/cubos/engine/audio/bridge.hpp +++ b/engine/include/cubos/engine/audio/bridge.hpp @@ -17,10 +17,10 @@ namespace cubos::engine class AudioBridge : public FileBridge { public: - std::shared_ptr mContext; + std::shared_ptr mContext; /// @brief Constructs a bridge. - AudioBridge(std::shared_ptr context) + AudioBridge(std::shared_ptr context) : FileBridge(core::reflection::reflect