Skip to content

Commit

Permalink
feat(audio): add AudioPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
diogomsmiranda committed Oct 2, 2024
1 parent 52949f6 commit d30fa15
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 71 deletions.
63 changes: 38 additions & 25 deletions core/include/cubos/core/al/audio_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<impl::Buffer>;

Expand All @@ -39,26 +40,11 @@ namespace cubos::core::al
/// @ingroup core-al
using Listener = std::shared_ptr<impl::Listener>;

/// @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<impl::AudioDevice>;

/// @brief Audio context that contains audio devices;
class CUBOS_CORE_API AudioContext
Expand All @@ -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<std::string>& devices);
virtual void enumerateDevices(std::vector<std::string>& 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<AudioDevice> 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;
Expand All @@ -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;
Expand Down Expand Up @@ -162,6 +153,7 @@ namespace cubos::core::al
Source() = default;
};

// Abstract audio listener.
class CUBOS_CORE_API Listener
{
public:
Expand All @@ -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<impl::Source> createSource() = 0;

/// @brief Creates a new audio listener.
/// @return Handle of the new listener.
virtual std::shared_ptr<impl::Listener> listener(size_t index) = 0;

protected:
AudioDevice() = default;
};
} // namespace impl
} // namespace cubos::core::al
4 changes: 2 additions & 2 deletions core/include/cubos/core/al/miniaudio_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ namespace cubos::core::al
MiniaudioContext();
~MiniaudioContext() override;

std::shared_ptr<AudioDevice> 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<std::string>& devices) override;

static void enumerateDevices(std::vector<std::string>& devices);
static std::string getDefaultDevice();

private:
Expand Down
5 changes: 0 additions & 5 deletions core/src/al/audio_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,3 @@ std::shared_ptr<AudioContext> AudioContext::create()
{
return std::make_shared<MiniaudioContext>();
}

void AudioContext::enumerateDevices(std::vector<std::string>& devices)
{
MiniaudioContext::enumerateDevices(devices);
}
72 changes: 47 additions & 25 deletions core/src/al/miniaudio_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,32 @@
#include <cubos/core/log.hpp>
#include <cubos/core/reflection/external/string.hpp>

// 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)
{
Expand All @@ -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<size_t>(lengthInPCMFrames) / mDecoder.outputSampleRate;
return static_cast<size_t>(lengthInPCMFrames) / decoder.outputSampleRate;
}

bool isValid() const
{
return mValid;
}

private:
bool mValid = false;
};

class MiniaudioSource : public impl::Source
Expand All @@ -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;
Expand Down Expand Up @@ -133,7 +142,7 @@ class MiniaudioSource : public impl::Source

private:
ma_sound mSound;
ma_engine mEngine;
ma_engine& mEngine;
};

class MiniaudioListener : public impl::Listener
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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<std::shared_ptr<MiniaudioListener>> mListeners;
bool mValid = false;
};

MiniaudioContext::MiniaudioContext()
Expand Down Expand Up @@ -306,25 +323,16 @@ void MiniaudioContext::enumerateDevices(std::vector<std::string>& 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);

Expand All @@ -341,10 +349,24 @@ void MiniaudioContext::enumerateDevices(std::vector<std::string>& devices)

Buffer MiniaudioContext::createBuffer(const void* data, size_t dataSize)
{
return std::make_shared<MiniaudioBuffer>(data, dataSize);
auto buffer = std::make_shared<MiniaudioBuffer>(data, dataSize);
if (!buffer->isValid())
{
CUBOS_ERROR("Failed to create MiniaudioBuffer.");
return nullptr;
}

return buffer;
}

std::shared_ptr<AudioDevice> MiniaudioContext::createDevice(const std::string& specifier, ma_uint32 listenerCount)
AudioDevice MiniaudioContext::createDevice(ma_uint32 listenerCount, const std::string& specifier)
{
return std::make_shared<MiniaudioDevice>(mContext, specifier, listenerCount);
auto device = std::make_shared<MiniaudioDevice>(mContext, specifier, listenerCount);
if (!device->isValid())
{
CUBOS_ERROR("Failed to create MiniaudioDevice.");
return nullptr;
}

return device;
}
3 changes: 3 additions & 0 deletions engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
7 changes: 4 additions & 3 deletions engine/include/cubos/engine/audio/audio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ namespace cubos::engine
struct CUBOS_ENGINE_API Audio
{
CUBOS_REFLECT;
std::shared_ptr<cubos::core::al::impl::Buffer> mData; // Raw data of the audio
size_t mLength; // Audio length in seconds TODO: add getter in audio

explicit Audio(std::shared_ptr<cubos::core::al::MiniaudioContext>, core::memory::Stream& stream);
/// @brief Audio buffer.
cubos::core::al::Buffer buffer;

explicit Audio(std::shared_ptr<cubos::core::al::AudioContext>, core::memory::Stream& stream);
Audio(Audio&& other) noexcept;
~Audio();
};
Expand Down
4 changes: 2 additions & 2 deletions engine/include/cubos/engine/audio/bridge.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ namespace cubos::engine
class AudioBridge : public FileBridge
{
public:
std::shared_ptr<cubos::core::al::MiniaudioContext> mContext;
std::shared_ptr<cubos::core::al::AudioContext> mContext;

/// @brief Constructs a bridge.
AudioBridge(std::shared_ptr<cubos::core::al::MiniaudioContext> context)
AudioBridge(std::shared_ptr<cubos::core::al::AudioContext> context)
: FileBridge(core::reflection::reflect<Audio>())
, mContext(context)
{
Expand Down
25 changes: 25 additions & 0 deletions engine/include/cubos/engine/audio/listener.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// @file
/// @brief Component @ref cubos::engine::AudioListener.
/// @ingroup audio-plugin

#pragma once

#include <cubos/core/al/audio_context.hpp>
#include <cubos/core/reflection/reflect.hpp>

#include <cubos/engine/api.hpp>

namespace cubos::engine
{
/// @brief Component which adds an AudioListener to the entitiy
/// @ingroup audio-plugin
struct CUBOS_ENGINE_API AudioListener
{
CUBOS_REFLECT;

bool active = false; //< Is the listener active?

private:
cubos::core::al::Listener listener;
};
} // namespace cubos::engine
Loading

0 comments on commit d30fa15

Please sign in to comment.