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 3, 2024
1 parent a27f667 commit 9c844c0
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;

Check warning on line 85 in core/include/cubos/core/al/audio_context.hpp

View check run for this annotation

Codecov / codecov/patch

core/include/cubos/core/al/audio_context.hpp#L85

Added line #L85 was not covered by tests
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;

Check warning on line 92 in core/include/cubos/core/al/audio_context.hpp

View check run for this annotation

Codecov / codecov/patch

core/include/cubos/core/al/audio_context.hpp#L92

Added line #L92 was not covered by tests
Expand Down Expand Up @@ -162,6 +153,7 @@ namespace cubos::core::al
Source() = default;

Check warning on line 153 in core/include/cubos/core/al/audio_context.hpp

View check run for this annotation

Codecov / codecov/patch

core/include/cubos/core/al/audio_context.hpp#L153

Added line #L153 was not covered by tests
};

// Abstract audio listener.
class CUBOS_CORE_API Listener
{
public:
Expand All @@ -183,5 +175,26 @@ namespace cubos::core::al
protected:
Listener() = default;

Check warning on line 176 in core/include/cubos/core/al/audio_context.hpp

View check run for this annotation

Codecov / codecov/patch

core/include/cubos/core/al/audio_context.hpp#L176

Added line #L176 was not covered by tests
};

/// @brief Audio device interface used to wrap low-level audio rendering APIs.
class CUBOS_CORE_API AudioDevice
{
public:
virtual ~AudioDevice() = default;

Check warning on line 183 in core/include/cubos/core/al/audio_context.hpp

View check run for this annotation

Codecov / codecov/patch

core/include/cubos/core/al/audio_context.hpp#L183

Added line #L183 was not covered by tests

/// @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;

Check warning on line 197 in core/include/cubos/core/al/audio_context.hpp

View check run for this annotation

Codecov / codecov/patch

core/include/cubos/core/al/audio_context.hpp#L197

Added line #L197 was not covered by tests
};
} // 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>();

Check warning on line 7 in core/src/al/audio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/audio_context.cpp#L7

Added line #L7 was not covered by tests
}

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)

Check warning on line 13 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L13

Added line #L13 was not covered by tests
{
if (ma_decoder_init_memory(data, dataSize, nullptr, &mDecoder) != MA_SUCCESS)
if (ma_decoder_init_memory(data, dataSize, nullptr, &decoder) != MA_SUCCESS)

Check warning on line 15 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L15

Added line #L15 was not covered by tests
{
CUBOS_ERROR("Failed to initialize Decoder from data");

Check warning on line 17 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L17

Added line #L17 was not covered by tests
}

mValid = true;

Check warning on line 20 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L20

Added line #L20 was not covered by tests
}

~MiniaudioBuffer() override

Check warning on line 23 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L23

Added line #L23 was not covered by tests
{
ma_decoder_uninit(&mDecoder);
ma_decoder_uninit(&decoder);

Check warning on line 25 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L25

Added line #L25 was not covered by tests
}

size_t getLength() override
size_t length() override

Check warning on line 28 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L28

Added line #L28 was not covered by tests
{
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);

Check warning on line 31 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L31

Added line #L31 was not covered by tests

if (result != MA_SUCCESS)

Check warning on line 33 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L33

Added line #L33 was not covered by tests
{
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;

Check warning on line 40 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L40

Added line #L40 was not covered by tests
}

bool isValid() const

Check warning on line 43 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L43

Added line #L43 was not covered by tests
{
return mValid;

Check warning on line 45 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L45

Added line #L45 was not covered by tests
}

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)

Check warning on line 76 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L76

Added line #L76 was not covered by tests
{
CUBOS_ERROR("Failed to initialize sound from buffer.");
return;

Check warning on line 79 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L78-L79

Added lines #L78 - L79 were not covered by tests
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;

Check warning on line 233 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L233

Added line #L233 was not covered by tests

mListeners.reserve(listenerCount);
for (ma_uint32 i = 0; i < listenerCount; ++i)

Check warning on line 236 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L235-L236

Added lines #L235 - L236 were not covered by tests
{
Expand Down Expand Up @@ -248,11 +259,17 @@ class MiniaudioDevice : public cubos::core::al::AudioDevice
return mListeners[index];

Check warning on line 259 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L259

Added line #L259 was not covered by tests
}

bool isValid() const

Check warning on line 262 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L262

Added line #L262 was not covered by tests
{
return mValid;

Check warning on line 264 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L264

Added line #L264 was not covered by tests
}

private:
ma_context mContext;
ma_device mDevice;
ma_engine mEngine;
std::vector<std::shared_ptr<MiniaudioListener>> mListeners;
bool mValid = false;
};

MiniaudioContext::MiniaudioContext()

Check warning on line 275 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L275

Added line #L275 was not covered by tests
Expand Down Expand Up @@ -306,25 +323,16 @@ void MiniaudioContext::enumerateDevices(std::vector<std::string>& devices)
{
devices.clear();

Check warning on line 324 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L324

Added line #L324 was not covered by tests

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

Check warning on line 329 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L329

Added line #L329 was not covered by tests
{
CUBOS_ERROR("Failed to enumerate audio devices.");
return;

Check warning on line 332 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L331-L332

Added lines #L331 - L332 were not covered by tests
}

ma_context_uninit(&context);
ma_context_uninit(&mContext);

Check warning on line 335 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L335

Added line #L335 was not covered by tests

devices.reserve(playbackDeviceCount);

Check warning on line 337 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L337

Added line #L337 was not covered by tests

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

Buffer MiniaudioContext::createBuffer(const void* data, size_t dataSize)

Check warning on line 350 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L350

Added line #L350 was not covered by tests
{
return std::make_shared<MiniaudioBuffer>(data, dataSize);
auto buffer = std::make_shared<MiniaudioBuffer>(data, dataSize);
if (!buffer->isValid())

Check warning on line 353 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L352-L353

Added lines #L352 - L353 were not covered by tests
{
CUBOS_ERROR("Failed to create MiniaudioBuffer.");
return nullptr;

Check warning on line 356 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L355-L356

Added lines #L355 - L356 were not covered by tests
}

return buffer;

Check warning on line 359 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L359

Added line #L359 was not covered by tests
}

std::shared_ptr<AudioDevice> MiniaudioContext::createDevice(const std::string& specifier, ma_uint32 listenerCount)
AudioDevice MiniaudioContext::createDevice(ma_uint32 listenerCount, const std::string& specifier)

Check warning on line 362 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L362

Added line #L362 was not covered by tests
{
return std::make_shared<MiniaudioDevice>(mContext, specifier, listenerCount);
auto device = std::make_shared<MiniaudioDevice>(mContext, specifier, listenerCount);
if (!device->isValid())

Check warning on line 365 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L364-L365

Added lines #L364 - L365 were not covered by tests
{
CUBOS_ERROR("Failed to create MiniaudioDevice.");
return nullptr;

Check warning on line 368 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L367-L368

Added lines #L367 - L368 were not covered by tests
}

return device;

Check warning on line 371 in core/src/al/miniaudio_context.cpp

View check run for this annotation

Codecov / codecov/patch

core/src/al/miniaudio_context.cpp#L371

Added line #L371 was not covered by tests
}
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)

Check warning on line 25 in engine/include/cubos/engine/audio/bridge.hpp

View check run for this annotation

Codecov / codecov/patch

engine/include/cubos/engine/audio/bridge.hpp#L23-L25

Added lines #L23 - L25 were not covered by tests
{
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 9c844c0

Please sign in to comment.