diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index eec52a52ce..e3cc390fbf 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -86,8 +86,8 @@ set(CUBOS_CORE_SOURCE "src/gl/ogl_render_device.cpp" "src/gl/util.cpp" - "src/al/audio_device.cpp" - "src/al/miniaudio_device.cpp" + "src/al/audio_context.cpp" + "src/al/miniaudio_context.cpp" "src/ecs/entity/entity.cpp" "src/ecs/entity/hash.cpp" diff --git a/core/include/cubos/core/al/audio_device.hpp b/core/include/cubos/core/al/audio_context.hpp similarity index 88% rename from core/include/cubos/core/al/audio_device.hpp rename to core/include/cubos/core/al/audio_context.hpp index 07668f5f04..7556d8f032 100644 --- a/core/include/cubos/core/al/audio_device.hpp +++ b/core/include/cubos/core/al/audio_context.hpp @@ -1,5 +1,5 @@ /// @file -/// @brief Class @ref cubos::core::al::AudioDevice and related types. +/// @brief Class @ref cubos::core::al::AudioContext and related types. /// @ingroup core-al #pragma once @@ -36,21 +36,11 @@ namespace cubos::core::al class CUBOS_CORE_API AudioDevice { public: - AudioDevice() = default; virtual ~AudioDevice() = default; /// @brief Forbid copy construction. AudioDevice(const AudioDevice&) = delete; - /// @brief Creates an audio device. - /// @see enumerateDevices() - /// @return Audio device, or nullptr on failure. - static std::shared_ptr create(); - - /// @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 /// @param data Data to be written to the buffer, cubos currently supports .wav, .mp3 and .flac files /// @param datSize Size of the data to be written. @@ -77,6 +67,30 @@ namespace cubos::core::al /// @param velocity Velocity of the listener. /// @param listenerIndex Index of the listener. virtual void setListenerVelocity(const glm::vec3& velocity, unsigned int listenerIndex = 0) = 0; + + protected: + AudioDevice() = default; + }; + + /// @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 devices. + static void enumerateDevices(std::vector& devices); + + /// @brief Creates a new audio device + /// @param specifier The specifier of the audio device, used to identify it + /// @return Handle of the new device + virtual std::shared_ptr createDevice(const std::string& specifier) = 0; }; /// @brief Namespace to store the abstract types implemented by the audio device implementations. 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..1263f60c51 --- /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; + + std::shared_ptr createDevice(const std::string& specifier) override; + + static void enumerateDevices(std::vector& devices); + static std::string getDefaultDevice(); + + private: + ma_context mContext; + }; +} // namespace cubos::core::al diff --git a/core/include/cubos/core/al/miniaudio_device.hpp b/core/include/cubos/core/al/miniaudio_device.hpp deleted file mode 100644 index 47005e2093..0000000000 --- a/core/include/cubos/core/al/miniaudio_device.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#include - -#include - -namespace cubos::core::al -{ - /// Audio device implementation using miniaudio. - class MiniaudioDevice : public AudioDevice - { - public: - MiniaudioDevice(); - ~MiniaudioDevice() override; - - static void enumerateDevices(std::vector& devices); - static std::string getDefaultDevice(); - - Buffer createBuffer(const void* data, size_t dataSize) override; - Source createSource() override; - void setListenerPosition(const glm::vec3& position, ma_uint32 listenerIndex = 0) override; - void setListenerOrientation(const glm::vec3& forward, const glm::vec3& up, - ma_uint32 listenerIndex = 0) override; - void setListenerVelocity(const glm::vec3& velocity, ma_uint32 listenerIndex = 0) override; - - private: - ma_context mContext; - ma_device mDevice; - ma_engine mEngine; - }; -} // namespace cubos::core::al diff --git a/core/src/al/audio_context.cpp b/core/src/al/audio_context.cpp new file mode 100644 index 0000000000..0b49f19caf --- /dev/null +++ b/core/src/al/audio_context.cpp @@ -0,0 +1,13 @@ +#include + +using namespace cubos::core::al; + +std::shared_ptr AudioContext::create() +{ + return std::make_shared(); +} + +void AudioContext::enumerateDevices(std::vector& devices) +{ + MiniaudioContext::enumerateDevices(devices); +} diff --git a/core/src/al/audio_device.cpp b/core/src/al/audio_device.cpp deleted file mode 100644 index ecb5a512cf..0000000000 --- a/core/src/al/audio_device.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include - -using namespace cubos::core::al; - -std::shared_ptr AudioDevice::create() -{ - return std::make_shared(); -} - -void AudioDevice::enumerateDevices(std::vector& devices) -{ - MiniaudioDevice::enumerateDevices(devices); -} diff --git a/core/src/al/miniaudio_device.cpp b/core/src/al/miniaudio_context.cpp similarity index 53% rename from core/src/al/miniaudio_device.cpp rename to core/src/al/miniaudio_context.cpp index 9130e7a475..382c1820cb 100644 --- a/core/src/al/miniaudio_device.cpp +++ b/core/src/al/miniaudio_context.cpp @@ -1,5 +1,5 @@ #define MINIAUDIO_IMPLEMENTATION -#include +#include #include #include @@ -41,13 +41,9 @@ class MiniaudioBuffer : public impl::Buffer class MiniaudioSource : public impl::Source { public: - MiniaudioSource() + MiniaudioSource(ma_engine& engine) + : mEngine(engine) { - if (ma_engine_init(nullptr, &mEngine) != MA_SUCCESS) - { - CUBOS_FAIL("Failed to initialize miniaudio engine."); - abort(); - } } ~MiniaudioSource() override @@ -64,13 +60,13 @@ class MiniaudioSource : public impl::Source if (miniaudioBuffer == nullptr) { CUBOS_FAIL("Buffer is not of type MiniaudioBuffer."); - abort(); + return; } if (ma_sound_init_from_data_source(&mEngine, &miniaudioBuffer->mDecoder, 0, nullptr, &mSound) != MA_SUCCESS) { CUBOS_ERROR("Failed to initialize sound from buffer."); - abort(); + return; } } @@ -130,7 +126,7 @@ class MiniaudioSource : public impl::Source if (ma_sound_start(&mSound) != MA_SUCCESS) { CUBOS_ERROR("Failed to start sound."); - abort(); + return; } } @@ -139,120 +135,171 @@ class MiniaudioSource : public impl::Source ma_engine mEngine; }; -MiniaudioDevice::MiniaudioDevice() +class MiniaudioDevice : public cubos::core::al::AudioDevice { - // Initialize miniaudio context. - if (ma_context_init(nullptr, 0, nullptr, &mContext) != MA_SUCCESS) +public: + MiniaudioDevice(ma_context& context, const std::string& deviceName) + : mContext(context) { - CUBOS_FAIL("Failed to initialize miniaudio context."); - abort(); + 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(); + engineConfig.pPlaybackDeviceID = deviceId; // Use the found device ID + + if (ma_engine_init(&engineConfig, &mEngine) != MA_SUCCESS) + { + CUBOS_FAIL("Failed to initialize audio engine"); + return; + } + } + + ~MiniaudioDevice() override + { + ma_device_uninit(&mDevice); + } + + Buffer createBuffer(const void* data, size_t dataSize) override + { + return std::make_shared(data, dataSize); + } + + Source createSource() override + { + return std::make_shared(mEngine); } - // Initialize miniaudio engine - if (ma_engine_init(nullptr, &mEngine) != MA_SUCCESS) + void setListenerPosition(const glm::vec3& position, ma_uint32 listenerIndex) override { - CUBOS_FAIL("Failed to initialize miniaudio engine."); - abort(); + ma_engine_listener_set_position(&mEngine, listenerIndex, position.x, position.y, position.z); } - // Configure the device. - ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. - deviceConfig.playback.channels = 2; // Set to 0 to use the device's native channel count. - deviceConfig.sampleRate = 48000; // Set to 0 to use the device's native sample rate. + void setListenerOrientation(const glm::vec3& forward, const glm::vec3& up, ma_uint32 listenerIndex) override + { + ma_engine_listener_set_direction(&mEngine, listenerIndex, forward.x, forward.y, forward.z); + ma_engine_listener_set_world_up(&mEngine, listenerIndex, up.x, up.y, up.z); + } - // Initialize the audio device. - if (ma_device_init(&mContext, &deviceConfig, &mDevice) != MA_SUCCESS) + void setListenerVelocity(const glm::vec3& velocity, ma_uint32 listenerIndex) override { - CUBOS_FAIL("Failed to initialize audio device."); - ma_context_uninit(&mContext); - abort(); + ma_engine_listener_set_velocity(&mEngine, listenerIndex, velocity.x, velocity.y, velocity.z); } - ma_device_start(&mDevice); +private: + ma_context mContext; + ma_device mDevice; + ma_engine mEngine; +}; + +MiniaudioContext::MiniaudioContext() +{ + if (ma_context_init(nullptr, 0, nullptr, &mContext) != MA_SUCCESS) + { + CUBOS_FAIL("Failed to initialize audio context."); + return; + } } -MiniaudioDevice::~MiniaudioDevice() +MiniaudioContext::~MiniaudioContext() { - ma_device_uninit(&mDevice); ma_context_uninit(&mContext); } -void MiniaudioDevice::enumerateDevices(std::vector& devices) +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."); - abort(); + CUBOS_ERROR("Failed to initialize audio context for default device lookup"); + return ""; } ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; + if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) { - CUBOS_FAIL("Failed to enumerate devices."); - ma_context_uninit(&context); // Uninitialize context before aborting - abort(); + CUBOS_ERROR("Failed to enumerate audio devices when searching for default"); + return ""; } + ma_context_uninit(&context); + for (ma_uint32 i = 0; i < playbackDeviceCount; i++) { - devices.emplace_back(pPlaybackDeviceInfos[i].name); + if (pPlaybackDeviceInfos[i].isDefault) + { + return pPlaybackDeviceInfos[i].name; + } } - ma_context_uninit(&context); + CUBOS_WARN("No default audio device found"); + return ""; } -std::string MiniaudioDevice::getDefaultDevice() +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_FAIL("Failed to initialize audio context."); - abort(); + CUBOS_ERROR("Failed to initialize audio context for enumeration"); + return; } - std::string defaultDeviceName; - ma_context_enumerate_devices( - &context, - [](ma_context*, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) -> ma_bool32 { - auto* pDefaultDeviceName = static_cast(pUserData); - if (deviceType == ma_device_type_playback && pDeviceInfo->isDefault == MA_TRUE) - { - *pDefaultDeviceName = pDeviceInfo->name; // Set the default device name - return MA_FALSE; - } - return MA_TRUE; - }, - &defaultDeviceName); // Pass defaultDeviceName as pUserData + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount; - ma_context_uninit(&context); - return defaultDeviceName; -} + if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS) + { + CUBOS_ERROR("Failed to enumerate audio devices."); + return; + } -Buffer MiniaudioDevice::createBuffer(const void* data, size_t dataSize) -{ - return std::make_shared(data, dataSize); -} + ma_context_uninit(&context); -Source MiniaudioDevice::createSource() -{ - return std::make_shared(); -} + devices.reserve(playbackDeviceCount); -void MiniaudioDevice::setListenerPosition(const glm::vec3& position, ma_uint32 listenerIndex) -{ - ma_engine_listener_set_position(&mEngine, listenerIndex, position.x, position.y, position.z); -} + for (ma_uint32 i = 0; i < playbackDeviceCount; i++) + { + devices.emplace_back(pPlaybackDeviceInfos[i].name); + } -void MiniaudioDevice::setListenerOrientation(const glm::vec3& forward, const glm::vec3& up, ma_uint32 listenerIndex) -{ - ma_engine_listener_set_direction(&mEngine, listenerIndex, forward.x, forward.y, forward.z); - ma_engine_listener_set_world_up(&mEngine, listenerIndex, up.x, up.y, up.z); + if (devices.empty()) + { + CUBOS_WARN("No audio playback devices found"); + } } -void MiniaudioDevice::setListenerVelocity(const glm::vec3& velocity, ma_uint32 listenerIndex) +std::shared_ptr MiniaudioContext::createDevice(const std::string& specifier) { - ma_engine_listener_set_velocity(&mEngine, listenerIndex, velocity.x, velocity.y, velocity.z); + return std::make_shared(mContext, specifier); } diff --git a/engine/include/cubos/engine/audio/audio.hpp b/engine/include/cubos/engine/audio/audio.hpp index 8b08d2801f..62f7c3bda4 100644 --- a/engine/include/cubos/engine/audio/audio.hpp +++ b/engine/include/cubos/engine/audio/audio.hpp @@ -3,7 +3,7 @@ /// @ingroup audio-plugin #pragma once -#include +#include #include #include @@ -20,7 +20,7 @@ namespace cubos::engine 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 device, core::memory::Stream& stream); + 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 8da3b821c2..fb196361a1 100644 --- a/engine/include/cubos/engine/audio/bridge.hpp +++ b/engine/include/cubos/engine/audio/bridge.hpp @@ -17,12 +17,12 @@ namespace cubos::engine class AudioBridge : public FileBridge { public: - std::shared_ptr mDevice; + std::shared_ptr mContext; /// @brief Constructs a bridge. - AudioBridge(std::shared_ptr device) + AudioBridge(std::shared_ptr context) : FileBridge(core::reflection::reflect