Skip to content

Commit

Permalink
feat(audio): replace OpenAL audio device for Miniaudio backend (#1005)
Browse files Browse the repository at this point in the history
  • Loading branch information
diogomsmiranda committed Sep 24, 2024
1 parent 48bc4ab commit e72f419
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 41 deletions.
4 changes: 2 additions & 2 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ set(CUBOS_CORE_SOURCE
"src/gl/util.cpp"

"src/al/audio_device.cpp"
"src/al/oal_audio_device.cpp"
"src/al/oal_audio_device.hpp"
"src/al/miniaudio_device.cpp"
"src/al/miniaudio_device.hpp"

"src/ecs/entity/entity.cpp"
"src/ecs/entity/hash.cpp"
Expand Down
55 changes: 20 additions & 35 deletions core/include/cubos/core/al/audio_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <vector>

#include <glm/glm.hpp>
#include <miniaudio.h>

#include <cubos/core/api.hpp>

Expand All @@ -32,15 +33,6 @@ namespace cubos::core::al
/// @ingroup core-al
using Source = std::shared_ptr<impl::Source>;

/// @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
{
Expand All @@ -51,36 +43,40 @@ namespace cubos::core::al
/// @brief Forbid copy construction.
AudioDevice(const AudioDevice&) = delete;

/// @brief Creates an audio device from a given device @p specifier.
/// @brief Creates an audio device.
/// @see enumerateDevices()
/// @param specifier Device specifier (empty for default).
/// @return Audio device, or nullptr on failure.
static std::shared_ptr<AudioDevice> create(const std::string& specifier = "");
static std::shared_ptr<AudioDevice> create();

/// @brief Enumerates the available devices.
/// @param[out] devices Vector to fill with the available devices.
static void enumerateDevices(std::vector<std::string>& devices);

/// @brief Creates a new audio buffer
/// @param filePath File path to create buffer from.
/// @return Handle of the new buffer.
virtual Buffer createBuffer() = 0;
virtual Buffer createBuffer(const std::string& filePath) = 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;
/// @param listenerIndex Index of the listener
virtual void setListenerPosition(const glm::vec3& position, unsigned int listenerIndex = 0) = 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;
/// @param listenerIndex Index of the listener
virtual void setListenerOrientation(const glm::vec3& forward, const glm::vec3& up,
unsigned int listenerIndex = 0) = 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;
/// @param listenerIndex Index of the listener
virtual void setListenerVelocity(const glm::vec3& velocity, unsigned int listenerIndex = 0) = 0;
};

/// @brief Namespace to store the abstract types implemented by the audio device implementations.
Expand All @@ -92,13 +88,6 @@ namespace cubos::core::al
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;
};
Expand Down Expand Up @@ -141,26 +130,22 @@ namespace cubos::core::al

/// @brief Sets the maximum distance at which the source is audible.
/// @param maxDistance Maximum distance.
virtual void setDistance(float maxDistance) = 0;
virtual void setMaxDistance(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 minimum distance at which the source starts to attenuate.
/// @param minDistance Minimum distance.
virtual void setMinDistance(float minDistance) = 0;

/// @brief Sets the cone gain of the source.
/// @todo Find out what this is.
/// @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 setConeGain(float coneGain) = 0;
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 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;

Expand Down
8 changes: 4 additions & 4 deletions core/src/al/audio_device.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#include "oal_audio_device.hpp"
#include "miniaudio_device.hpp"

using namespace cubos::core::al;

std::shared_ptr<AudioDevice> AudioDevice::create(const std::string& specifier)
std::shared_ptr<AudioDevice> AudioDevice::create()
{
return std::make_shared<OALAudioDevice>(specifier);
return std::make_shared<MiniaudioDevice>();
}

void AudioDevice::enumerateDevices(std::vector<std::string>& devices)
{
OALAudioDevice::enumerateDevices(devices);
MiniaudioDevice::enumerateDevices(devices);
}
245 changes: 245 additions & 0 deletions core/src/al/miniaudio_device.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio_device.hpp"

#include <cubos/core/log.hpp>
#include <cubos/core/reflection/external/string.hpp>

using namespace cubos::core::al;

class MiniaudioBuffer : public impl::Buffer
{
public:
std::string path;

MiniaudioBuffer(const std::string& filePath)
{
if (ma_decoder_init_file(filePath.c_str(), nullptr, &mDecoder) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to load audio file: {}", filePath);
abort();
}

path = filePath;
}

~MiniaudioBuffer()
{
ma_decoder_uninit(&mDecoder);
}

private:
ma_decoder mDecoder;
};

class MiniaudioSource : public impl::Source
{
public:
MiniaudioSource()
{
if (ma_engine_init(nullptr, &mEngine) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to initialize miniaudio engine.");
abort();
}
}

~MiniaudioSource() override
{
ma_sound_uninit(&mSound);
ma_engine_uninit(&mEngine);
}

void setBuffer(cubos::core::al::Buffer buffer) override
{
auto miniaudioBuffer = std::dynamic_pointer_cast<MiniaudioBuffer>(buffer);
if (ma_sound_init_from_file(&mEngine, miniaudioBuffer->path.c_str(), MA_SOUND_FLAG_STREAM, nullptr, nullptr,
&mSound) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed while initating sound from buffer file.");
abort();
}
}

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, looping);
}

void setRelative(bool relative) override
{
relative ? ma_sound_set_positioning(&mSound, ma_positioning_relative)
: ma_sound_set_positioning(&mSound, ma_positioning_absolute);
}

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_CRITICAL("Failed to start sound.");
abort();
}
}

private:
ma_sound mSound;
ma_engine mEngine;
};

MiniaudioDevice::MiniaudioDevice()
{
// Initialize miniaudio context.
if (ma_context_init(nullptr, 0, nullptr, &mContext) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to initialize miniaudio context.");
abort();
}

// Initialize miniaudio engine
if (ma_engine_init(nullptr, &mEngine) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to initialize miniaudio engine.");
abort();
}

// 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.

// Initialize the audio device.
if (ma_device_init(&mContext, &deviceConfig, &mDevice) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to initialize audio device.");
ma_context_uninit(&mContext);
abort();
}

ma_device_start(&mDevice);
}

MiniaudioDevice::~MiniaudioDevice()
{

ma_device_uninit(&mDevice);
ma_context_uninit(&mContext);
}

void MiniaudioDevice::enumerateDevices(std::vector<std::string>& devices)
{
ma_context context;
if (ma_context_init(nullptr, 0, nullptr, &context) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to initialize audio context.");
abort();
}

ma_device_info* pPlaybackDeviceInfos;
ma_uint32 playbackDeviceCount;
if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, nullptr, nullptr) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to enumerate devices.");
ma_context_uninit(&context); // Uninitialize context before aborting
abort();
}

for (ma_uint32 i = 0; i < playbackDeviceCount; i++)
{
devices.push_back(pPlaybackDeviceInfos[i].name);
}

ma_context_uninit(&context);
}

std::string MiniaudioDevice::getDefaultDevice()
{
ma_context context;
if (ma_context_init(nullptr, 0, nullptr, &context) != MA_SUCCESS)
{
CUBOS_CRITICAL("Failed to initialize audio context.");
abort();
}

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<std::string*>(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_context_uninit(&context);
return defaultDeviceName;
}

Buffer MiniaudioDevice::createBuffer(const std::string& filePath)
{
return std::make_shared<MiniaudioBuffer>(filePath);
}

Source MiniaudioDevice::createSource()
{
return std::make_shared<MiniaudioSource>();
}

void MiniaudioDevice::setListenerPosition(const glm::vec3& position, ma_uint32 listenerIndex)
{
ma_engine_listener_set_position(&mEngine, listenerIndex, position.x, position.y, position.z);
}

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);
}

void MiniaudioDevice::setListenerVelocity(const glm::vec3& velocity, ma_uint32 listenerIndex)
{
ma_engine_listener_set_velocity(&mEngine, listenerIndex, velocity.x, velocity.y, velocity.z);
}
Loading

0 comments on commit e72f419

Please sign in to comment.