Skip to content

Commit

Permalink
feat(audio): add audioPlugin
Browse files Browse the repository at this point in the history
Co-Authored-By: João Miguel Nogueira <[email protected]>
  • Loading branch information
diogomsmiranda and Dageus committed Nov 29, 2024
1 parent b7c2823 commit c83baa2
Show file tree
Hide file tree
Showing 23 changed files with 651 additions and 30 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Point light shadows (#1188, **@tomas7770**).
- Added method to save Settings to file (#1349, **@SrGesus**).
- Added resource to easily configure magic numbers in solver (#1281, **@GCeSilva**).
- Audio Plugin (#1004, **@Dageus**, **@diogomsmiranda**).

### Changed

Expand Down Expand Up @@ -52,7 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Deadzone for input axis (#844, **@kuukitenshi**).
- Generic Camera component to hold projection matrix (#1331, **@mkuritsu**).
- Initial application debugging through Tesseratos (#1303, **@RiscadoA**).
- Print stacktrace with *cpptrace* on calls to CUBOS_FAIL (#1172, **@RiscadoA**).
- Print stacktrace with _cpptrace_ on calls to CUBOS_FAIL (#1172, **@RiscadoA**).
- Orthographic Camera component (#1182, **@mkuritsu**).
- Importer plugin (#1299, **@Scarface1809**).
- Handle body rotation on penetration solving (#1272, **@fallenatlas**).
Expand Down
21 changes: 18 additions & 3 deletions core/include/cubos/core/al/audio_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,16 @@ namespace cubos::core::al
{
public:
AudioContext() = default;
virtual ~AudioContext() = default;
virtual ~AudioContext();

/// @brief Creates an audio context.
/// @return AudioContext, or nullptr on failure.
static std::shared_ptr<AudioContext> create();

/// @brief Gets the maximum number of listeners supported by the audio context.
/// @return Maximum number of listeners.
virtual int getMaxListenerCount() const = 0;

/// @brief Enumerates the available devices.
/// @param[out] devices Vector to fill with the available device's specifiers.
virtual void enumerateDevices(std::vector<std::string>& devices) = 0;
Expand All @@ -72,6 +76,10 @@ namespace cubos::core::al
/// @param dataSize Size of the data to be written.
/// @return Handle of the new buffer.
virtual Buffer createBuffer(const void* data, size_t dataSize) = 0;

/// @brief Gets the default audio device.
/// @return Name of the default audio device.
virtual std::string getDefaultDevice() = 0;
};

/// @brief Namespace to store the abstract types implemented by the audio device implementations.
Expand Down Expand Up @@ -148,6 +156,12 @@ namespace cubos::core::al
/// @brief Plays the source.
virtual void play() = 0;

/// @brief Stops the source, restarting buffer to position 0.
virtual void stop() = 0;

/// @brief Pauses the source, allowing to be played from the moment it was paused.
virtual void pause() = 0;

protected:
Source() = default;
};
Expand Down Expand Up @@ -188,8 +202,9 @@ namespace cubos::core::al
/// @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.
/// @brief Gets the listener with the specific index.
/// @param index Index of the listener.
/// @return Handle of the listener.
virtual std::shared_ptr<impl::Listener> listener(size_t index) = 0;

protected:
Expand Down
3 changes: 2 additions & 1 deletion core/include/cubos/core/al/miniaudio_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace cubos::core::al
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;
std::string getDefaultDevice();
int getMaxListenerCount() const override;
std::string getDefaultDevice() override;

private:
#ifdef CUBOS_CORE_MINIAUDIO
Expand Down
2 changes: 2 additions & 0 deletions core/src/al/audio_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ std::shared_ptr<AudioContext> AudioContext::create()
return nullptr;
#endif
}

cubos::core::al::AudioContext::~AudioContext() = default;
88 changes: 63 additions & 25 deletions core/src/al/miniaudio_context.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#define MINIAUDIO_IMPLEMENTATION
#include <cstring>

#include <cubos/core/al/miniaudio_context.hpp>
#include <cubos/core/reflection/external/string.hpp>
#include <cubos/core/tel/logging.hpp>
Expand All @@ -11,9 +13,12 @@ class MiniaudioBuffer : public impl::Buffer
public:
ma_decoder decoder;

MiniaudioBuffer(const void* data, size_t dataSize)
MiniaudioBuffer(const void* srcData, size_t dataSize)
{
if (ma_decoder_init_memory(data, dataSize, nullptr, &decoder) != MA_SUCCESS)
mData = operator new(dataSize);
std::memcpy(mData, srcData, dataSize);

if (ma_decoder_init_memory(mData, dataSize, nullptr, &decoder) != MA_SUCCESS)
{
CUBOS_ERROR("Failed to initialize Decoder from data");
}
Expand All @@ -26,6 +31,7 @@ class MiniaudioBuffer : public impl::Buffer
~MiniaudioBuffer() override
{
ma_decoder_uninit(&decoder);
operator delete(mData);
}

float length() override
Expand All @@ -50,6 +56,7 @@ class MiniaudioBuffer : public impl::Buffer

private:
bool mValid = false;
void* mData;
};

class MiniaudioListener : public impl::Listener
Expand Down Expand Up @@ -93,7 +100,8 @@ class MiniaudioSource : public impl::Source
{
public:
MiniaudioSource(ma_engine& engine)
: mEngine(engine)
: mSound({})
, mEngine(engine)
{
}

Expand All @@ -104,6 +112,8 @@ class MiniaudioSource : public impl::Source

void setBuffer(Buffer buffer) override
{
ma_sound_uninit(&mSound);

// Try to dynamically cast the Buffer to a MiniaudioBuffer.
auto miniaudioBuffer = std::static_pointer_cast<MiniaudioBuffer>(buffer);

Expand Down Expand Up @@ -180,6 +190,25 @@ class MiniaudioSource : public impl::Source
}
}

void stop() override
{
if (ma_sound_stop(&mSound) != MA_SUCCESS)
{
CUBOS_ERROR("Failed to stop sound");
return;
}
ma_sound_seek_to_pcm_frame(&mSound, 0);
}

void pause() override
{
if (ma_sound_stop(&mSound) != MA_SUCCESS)
{
CUBOS_ERROR("Failed to pause sound");
return;
}
}

private:
ma_sound mSound;
ma_engine& mEngine;
Expand All @@ -191,6 +220,12 @@ class MiniaudioDevice : public impl::AudioDevice
MiniaudioDevice(ma_context& context, const std::string& deviceName, ma_uint32 listenerCount)
: mContext(context)
{
if (listenerCount > MA_ENGINE_MAX_LISTENERS)
{
CUBOS_FAIL("Maximum number of listeners is 4");
return;
}

ma_device_info* pPlaybackDeviceInfos;
ma_uint32 playbackDeviceCount;
ma_result result =
Expand All @@ -203,31 +238,29 @@ class MiniaudioDevice : public impl::AudioDevice
}

ma_device_id* deviceId = nullptr;
for (ma_uint32 i = 0; i < playbackDeviceCount; i++)

if (!deviceName.empty()) // in case a device name is provided
{
if (deviceName == pPlaybackDeviceInfos[i].name)
for (ma_uint32 i = 0; i < playbackDeviceCount; i++)
{
deviceId = &pPlaybackDeviceInfos[i].id;
break;
if (deviceName == pPlaybackDeviceInfos[i].name)
{
deviceId = &pPlaybackDeviceInfos[i].id;
break;
}
}
}

if (deviceId == nullptr)
{
CUBOS_FAIL("Audio device '{}' not found", deviceName);
return;
if (deviceId == nullptr)
{
CUBOS_FAIL("Audio device '{}' not found", deviceName);
return;
}
}

ma_engine_config engineConfig = ma_engine_config_init();

if (listenerCount > MA_ENGINE_MAX_LISTENERS)
{
CUBOS_FAIL("Maximum number of listeners is 4");
return;
}

engineConfig.listenerCount = listenerCount;
engineConfig.pPlaybackDeviceID = deviceId; // Use the found device ID
engineConfig.pPlaybackDeviceID = deviceId;

if (ma_engine_init(&engineConfig, &mEngine) != MA_SUCCESS)
{
Expand All @@ -246,6 +279,7 @@ class MiniaudioDevice : public impl::AudioDevice

~MiniaudioDevice() override
{
ma_engine_uninit(&mEngine);
ma_device_uninit(&mDevice);
}

Expand Down Expand Up @@ -296,6 +330,15 @@ MiniaudioContext::~MiniaudioContext()
#endif
}

int MiniaudioContext::getMaxListenerCount() const
{
#ifdef CUBOS_CORE_MINIAUDIO
return MA_ENGINE_MAX_LISTENERS;
#else
return 0;
#endif
}

std::string MiniaudioContext::getDefaultDevice()
{
#ifdef CUBOS_CORE_MINIAUDIO
Expand All @@ -308,11 +351,9 @@ std::string MiniaudioContext::getDefaultDevice()
return "";
}

ma_context_uninit(&mContext);

for (ma_uint32 i = 0; i < playbackDeviceCount; i++)
{
if (pPlaybackDeviceInfos[i].isDefault != 0u)
if (pPlaybackDeviceInfos[i].isDefault != 0U)
{
return pPlaybackDeviceInfos[i].name;
}
Expand All @@ -339,8 +380,6 @@ void MiniaudioContext::enumerateDevices(std::vector<std::string>& devices)
return;
}

ma_context_uninit(&mContext);

devices.reserve(playbackDeviceCount);

for (ma_uint32 i = 0; i < playbackDeviceCount; i++)
Expand Down Expand Up @@ -388,7 +427,6 @@ AudioDevice MiniaudioContext::createDevice(unsigned int listenerCount, const std
return device;
#else
(void)listenerCount;
(void)specifier;
return nullptr;
#endif
}
9 changes: 9 additions & 0 deletions engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ 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/source_impl.cpp"
"src/audio/listener.cpp"
"src/audio/listener_impl.cpp"
"src/audio/pause.cpp"
"src/audio/play.cpp"
"src/audio/stop.cpp"

"src/audio/audio.cpp"
"src/audio/bridge.cpp"

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 <map>

#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;

/// @brief Whether the listener is active or not.
bool active{false};
};
} // namespace cubos::engine
22 changes: 22 additions & 0 deletions engine/include/cubos/engine/audio/pause.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// @file
/// @brief Component @ref cubos::engine::AudioPause.
/// @ingroup audio-plugin

#pragma once

#include <glm/glm.hpp>

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

#include <cubos/engine/api.hpp>
#include <cubos/engine/prelude.hpp>

namespace cubos::engine
{
/// @brief Component which triggers the pause of an audio source.
/// @ingroup audio-plugin
struct CUBOS_ENGINE_API AudioPause
{
CUBOS_REFLECT;
};
} // namespace cubos::engine
22 changes: 22 additions & 0 deletions engine/include/cubos/engine/audio/play.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// @file
/// @brief Component @ref cubos::engine::AudioPlay.
/// @ingroup audio-plugin

#pragma once

#include <glm/glm.hpp>

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

#include <cubos/engine/api.hpp>
#include <cubos/engine/prelude.hpp>

namespace cubos::engine
{
/// @brief Component which triggers the play of an audio source.
/// @ingroup audio-plugin
struct CUBOS_ENGINE_API AudioPlay
{
CUBOS_REFLECT;
};
} // namespace cubos::engine
Loading

0 comments on commit c83baa2

Please sign in to comment.