From 789288e4fb43f2b5d0cf5c2b0977edd58082f4a7 Mon Sep 17 00:00:00 2001 From: Ishan-002 Date: Wed, 22 Dec 2021 16:10:20 +0530 Subject: [PATCH 1/2] Add basic audio mixer in AudioSystem --- .../components/audio/audio_component.cpp | 19 ++ .../components/audio/audio_component.h | 6 +- .../components/audio/music_component.cpp | 140 ++++++------ rootex/framework/systems/audio_system.cpp | 69 ++++++ rootex/framework/systems/audio_system.h | 204 ++++++++++-------- 5 files changed, 280 insertions(+), 158 deletions(-) diff --git a/rootex/framework/components/audio/audio_component.cpp b/rootex/framework/components/audio/audio_component.cpp index e5457fe26..e7a53ff4d 100644 --- a/rootex/framework/components/audio/audio_component.cpp +++ b/rootex/framework/components/audio/audio_component.cpp @@ -125,6 +125,12 @@ void AudioComponent::setLooping(bool enabled) m_AudioSource->setLooping(enabled); } +void AudioComponent::setAudioBus(AudioBus* bus) +{ + m_AudioBus = bus; + bus->addAudioComponent(Ref(this)); +} + void AudioComponent::draw() { RenderSystem::GetSingleton()->submitSphere(getTransformComponent()->getAbsoluteTransform().Translation(), m_MaxDistance); @@ -176,4 +182,17 @@ void AudioComponent::draw() ImGui::DragFloat("Rolloff Factor", &m_RolloffFactor, 1.0f, 0.0f, 100.0f); ImGui::DragFloat("Max Distance", &m_MaxDistance, 1.0f, 0.0f, 100.0f); ImGui::DragFloat("Volume", &m_Volume, 0.01f, 0.0f, 100.0f); + + // Audio mixer UI + if (ImGui::BeginCombo("Bus", AudioSystem::GetSingleton()->getAudioBuses()[0]->getBusName().c_str())) + { + for (auto& cp : AudioSystem::GetSingleton()->getAudioBuses()) + { + if (ImGui::Selectable(cp->getBusName().c_str())) + { + setAudioBus(cp); + } + } + ImGui::EndCombo(); + } } diff --git a/rootex/framework/components/audio/audio_component.h b/rootex/framework/components/audio/audio_component.h index 9ad57ecc3..b47059607 100644 --- a/rootex/framework/components/audio/audio_component.h +++ b/rootex/framework/components/audio/audio_component.h @@ -7,6 +7,7 @@ #include "components/physics/box_collider_component.h" #include "components/physics/sphere_collider_component.h" #include "components/physics/capsule_collider_component.h" +#include "systems/audio_system.h" class AudioComponent : public Component { @@ -22,6 +23,7 @@ class AudioComponent : public Component ALfloat m_MaxDistance; ALfloat m_Volume; AudioSource* m_AudioSource; + AudioBus* m_AudioBus; protected: bool m_IsPlayOnStart; @@ -61,5 +63,7 @@ class AudioComponent : public Component bool setupData() override; JSON::json getJSON() const; - void draw() override; + void draw() override; + + void setAudioBus(AudioBus* bus); }; diff --git a/rootex/framework/components/audio/music_component.cpp b/rootex/framework/components/audio/music_component.cpp index 2ce473d7e..71d645c22 100644 --- a/rootex/framework/components/audio/music_component.cpp +++ b/rootex/framework/components/audio/music_component.cpp @@ -1,70 +1,70 @@ -#include "music_component.h" - -DEFINE_COMPONENT(MusicComponent); - -MusicComponent::MusicComponent(Entity& owner, const JSON::json& data) - : AudioComponent( - owner, - data.value("playOnStart", false), - data.value("volume", 1.0f), - data.value("isLooping", false), - data.value("isAttenuated", false), - (AudioSource::AttenuationModel)data.value("attenuationModel", (int)AudioSource::AttenuationModel::Linear), - (ALfloat)data.value("rollOffFactor", 1.0f), - (ALfloat)data.value("referenceDistance", 1.0f), - (ALfloat)data.value("maxDistance", 100.0f)) - , m_AudioFile(ResourceLoader::CreateAudioResourceFile(data.value("audio", "rootex/assets/ball.wav"))) -{ -} - -MusicComponent::~MusicComponent() -{ - m_StreamingAudioSource.reset(); -} - -bool MusicComponent::setupData() -{ - m_StreamingAudioSource.reset(); - m_StreamingAudioBuffer.reset(new StreamingAudioBuffer(m_AudioFile)); - m_StreamingAudioSource.reset(new StreamingAudioSource(m_StreamingAudioBuffer)); - - setAudioSource(m_StreamingAudioSource.get()); - - return AudioComponent::setupData(); -} - -JSON::json MusicComponent::getJSON() const -{ - JSON::json& j = AudioComponent::getJSON(); - - j["audio"] = m_AudioFile->getPath().string(); - j["playOnStart"] = m_IsPlayOnStart; - - return j; -} - -void MusicComponent::setAudioFile(Ref audioFile) -{ - m_AudioFile = audioFile; - setupData(); -} - -void MusicComponent::draw() -{ - ImGui::Text("%s", m_AudioFile->getPath().generic_string().c_str()); - ImGui::SameLine(); - if (ImGui::Button("Audio File")) - { - EventManager::GetSingleton()->call(EditorEvents::EditorOpenFile, VariantVector { m_AudioFile->getPath().generic_string(), (int)m_AudioFile->getType() }); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_ROOTEX_FOLDER_OPEN "##Select Music")) - { - if (Optional result = OS::SelectFile(SupportedFiles.at(ResourceFile::Type::Audio), "game/assets/")) - { - setAudioFile(ResourceLoader::CreateAudioResourceFile(*result)); - } - } - - AudioComponent::draw(); -} +#include "music_component.h" + +DEFINE_COMPONENT(MusicComponent); + +MusicComponent::MusicComponent(Entity& owner, const JSON::json& data) + : AudioComponent( + owner, + data.value("playOnStart", false), + data.value("volume", 1.0f), + data.value("isLooping", false), + data.value("isAttenuated", false), + (AudioSource::AttenuationModel)data.value("attenuationModel", (int)AudioSource::AttenuationModel::Linear), + (ALfloat)data.value("rollOffFactor", 1.0f), + (ALfloat)data.value("referenceDistance", 1.0f), + (ALfloat)data.value("maxDistance", 100.0f)) + , m_AudioFile(ResourceLoader::CreateAudioResourceFile(data.value("audio", "rootex/assets/ball.wav"))) +{ +} + +MusicComponent::~MusicComponent() +{ + m_StreamingAudioSource.reset(); +} + +bool MusicComponent::setupData() +{ + m_StreamingAudioSource.reset(); + m_StreamingAudioBuffer.reset(new StreamingAudioBuffer(m_AudioFile)); + m_StreamingAudioSource.reset(new StreamingAudioSource(m_StreamingAudioBuffer)); + + setAudioSource(m_StreamingAudioSource.get()); + + return AudioComponent::setupData(); +} + +JSON::json MusicComponent::getJSON() const +{ + JSON::json& j = AudioComponent::getJSON(); + + j["audio"] = m_AudioFile->getPath().string(); + j["playOnStart"] = m_IsPlayOnStart; + + return j; +} + +void MusicComponent::setAudioFile(Ref audioFile) +{ + m_AudioFile = audioFile; + setupData(); +} + +void MusicComponent::draw() +{ + ImGui::Text("%s", m_AudioFile->getPath().generic_string().c_str()); + ImGui::SameLine(); + if (ImGui::Button("Audio File")) + { + EventManager::GetSingleton()->call(EditorEvents::EditorOpenFile, VariantVector { m_AudioFile->getPath().generic_string(), (int)m_AudioFile->getType() }); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_ROOTEX_FOLDER_OPEN "##Select Music")) + { + if (Optional result = OS::SelectFile(SupportedFiles.at(ResourceFile::Type::Audio), "game/assets/")) + { + setAudioFile(ResourceLoader::CreateAudioResourceFile(*result)); + } + } + + AudioComponent::draw(); +} diff --git a/rootex/framework/systems/audio_system.cpp b/rootex/framework/systems/audio_system.cpp index f4918abec..e164b6052 100644 --- a/rootex/framework/systems/audio_system.cpp +++ b/rootex/framework/systems/audio_system.cpp @@ -144,6 +144,57 @@ void AudioSystem::end() smc.getAudioSource()->stop(); } } +void AudioSystem::addNewBus() +{ + // adds a new bus to the vector and the tree +} + +void AudioSystem::removeBus(AudioBus* bus) +{ + // removes an audio bus from the vector and the tree +} + +void AudioBus::addAudioComponent(Ref cp) +{ + m_AudioComponents.push_back(cp); +} + +void AudioSystem::draw() +{ + System::draw(); + for (auto& bus : m_Buses) + { + ImGui::Text(bus->getBusName().c_str()); + + if (ImGui::Button(ICON_ROOTEX_MINUS "##Remove Bus")) + { + removeBus(bus); + } + + // volume of the bus + ImGui::DragFloat("Volume", &bus->getBusVolume(), 0.01f, 0.0f, 100.0f); + + // selecting the parent + if (ImGui::BeginCombo("Parent", m_Buses[0]->getBusName().c_str())) + { + for (auto& cp : m_Buses) + { + if (ImGui::Selectable(cp->getBusName().c_str())) + { + bus->setParent(cp); + } + } + ImGui::EndCombo(); + } + } + + if (ImGui::Button(ICON_ROOTEX_PLUS "##Add Bus")) + { + addNewBus(); + } + + ImGui::Text("AudioMixerSystem"); +} void AudioSystem::setListener(AudioListenerComponent* listenerComponent) { @@ -188,3 +239,21 @@ AudioSystem::AudioSystem() : System("AudioSystem", UpdateOrder::Async, true) { } + +void AudioBus::onVolumeChange(float delta) +{ + for (auto& ac : m_AudioComponents) + { + // the fraction of change is to be decided + } + + for (auto& child : m_Children) + { + child->onVolumeChange(delta); + } +} + +void AudioBus::setParent(AudioBus* parent) +{ + m_Parent = parent; +} diff --git a/rootex/framework/systems/audio_system.h b/rootex/framework/systems/audio_system.h index 224ec311e..05f4295fa 100644 --- a/rootex/framework/systems/audio_system.h +++ b/rootex/framework/systems/audio_system.h @@ -1,87 +1,117 @@ -#pragma once - -#include "common/common.h" - -#include "components/space/transform_component.h" -#include "components/audio/audio_listener_component.h" - -#include "al.h" -#include "alc.h" -#include "alut.h" - -#include "system.h" - -#ifndef ALUT_CHECK -#ifdef _DEBUG -#define ALUT_CHECK(alutFunction) \ - do \ - { \ - alutFunction; \ - AudioSystem::CheckALUTError(#alutFunction, __FILE__, __LINE__); \ - } while (0) -#else -#define ALUT_CHECK(alutFunction) alutFunction -#endif -#endif - -#ifndef AL_CHECK -#ifdef _DEBUG -#define AL_CHECK(alFunction) \ - do \ - { \ - alFunction; \ - AudioSystem::CheckALError(#alFunction, __FILE__, __LINE__); \ - } while (0) -#else -#define AL_CHECK(alFunction) alFunction -#endif -#endif - -class AudioBuffer; -class StaticAudioBuffer; -class StreamingAudioBuffer; - -class AudioSource; -class StreamingAudioSource; - -class ResourceFile; - -/// Audio System responsible for streaming and static audio -class AudioSystem : public System -{ - ALCdevice* m_Device = nullptr; - ALCcontext* m_Context = nullptr; - - AudioListenerComponent* m_Listener = nullptr; - - AudioSystem(); - AudioSystem(AudioSystem&) = delete; - virtual ~AudioSystem() = default; - -public: - static AudioSystem* GetSingleton(); - - /// Returns error string corresponding to AL error codes. - static String GetALErrorString(int errID); - /// Returns error string corresponding to ALC error codes. - static String GetALCErrorString(int errID); - /// Wrapper over alGetError function. - static void CheckALError(const char* msg, const char* fname, int line); - /// Wrapper over alcGetError function. - static void CheckALCError(const char* msg, const char* fname, int line); - /// Wrapper over alutGetError function. - static void CheckALUTError(const char* msg, const char* fname, int line); - - AudioListenerComponent* getListener() const { return m_Listener; } - void setListener(AudioListenerComponent* listenerComponent); - - void restoreListener(); - - bool initialize(const JSON::json& systemData) override; - void setConfig(const SceneSettings& sceneSettings) override; - void shutDown(); - - void update(float deltaMilliseconds) override; - void begin() override; - void end() override; -}; +#pragma once + +#include "common/common.h" + +#include "components/space/transform_component.h" +#include "components/audio/audio_listener_component.h" + +#include "al.h" +#include "alc.h" +#include "alut.h" + +#include "system.h" + +#ifndef ALUT_CHECK +#ifdef _DEBUG +#define ALUT_CHECK(alutFunction) \ + do \ + { \ + alutFunction; \ + AudioSystem::CheckALUTError(#alutFunction, __FILE__, __LINE__); \ + } while (0) +#else +#define ALUT_CHECK(alutFunction) alutFunction +#endif +#endif + +#ifndef AL_CHECK +#ifdef _DEBUG +#define AL_CHECK(alFunction) \ + do \ + { \ + alFunction; \ + AudioSystem::CheckALError(#alFunction, __FILE__, __LINE__); \ + } while (0) +#else +#define AL_CHECK(alFunction) alFunction +#endif +#endif + +class AudioBuffer; +class StaticAudioBuffer; +class StreamingAudioBuffer; + +class AudioSource; +class StreamingAudioSource; + +class ResourceFile; + +/// Audio System responsible for streaming and static audio +class AudioSystem : public System +{ + ALCdevice* m_Device = nullptr; + ALCcontext* m_Context = nullptr; + + AudioListenerComponent* m_Listener = nullptr; + AudioBus* m_RootAudioBus = nullptr; + Vector m_Buses; + + AudioSystem(); + AudioSystem(AudioSystem&) = delete; + virtual ~AudioSystem() = default; + +public: + static AudioSystem* GetSingleton(); + + /// Returns error string corresponding to AL error codes. + static String GetALErrorString(int errID); + /// Returns error string corresponding to ALC error codes. + static String GetALCErrorString(int errID); + /// Wrapper over alGetError function. + static void CheckALError(const char* msg, const char* fname, int line); + /// Wrapper over alcGetError function. + static void CheckALCError(const char* msg, const char* fname, int line); + /// Wrapper over alutGetError function. + static void CheckALUTError(const char* msg, const char* fname, int line); + + AudioListenerComponent* getListener() const { return m_Listener; } + void setListener(AudioListenerComponent* listenerComponent); + + void restoreListener(); + + bool initialize(const JSON::json& systemData) override; + void setConfig(const SceneSettings& sceneSettings) override; + void shutDown(); + + void update(float deltaMilliseconds) override; + void begin() override; + void end() override; + void draw() override; + + Vector getAudioBuses() const { return m_Buses; }; + void addNewBus(); + void removeBus(AudioBus* bus); +}; + +class AudioBus +{ +private: + String m_BusName; + AudioBus* m_Parent = nullptr; + Vector> m_AudioComponents; + Vector m_Children; + bool m_IsMaster; + // TODO: change volume of components when m_Volume changes + float m_Volume; + + AudioBus(); + AudioBus(AudioBus&) = delete; + ~AudioBus() = default; + +public: + void addAudioComponent(Ref cp); + void onVolumeChange(float delta); + void setParent(AudioBus* parent); + String getBusName() { return m_BusName; }; + float& getBusVolume() { return m_Volume; }; +}; From 4e90ad01e2c70567e49bdb016c966889e82224b0 Mon Sep 17 00:00:00 2001 From: Ishan-002 Date: Wed, 26 Jan 2022 03:19:11 +0530 Subject: [PATCH 2/2] Add basic flow for audio mixer --- rootex/core/audio/audio_bus.h | 26 ++++++++++ .../components/audio/audio_component.cpp | 17 ++++++- .../components/audio/audio_component.h | 5 +- rootex/framework/systems/audio_system.cpp | 21 ++++++-- rootex/framework/systems/audio_system.h | 48 ++++++++++--------- 5 files changed, 88 insertions(+), 29 deletions(-) create mode 100644 rootex/core/audio/audio_bus.h diff --git a/rootex/core/audio/audio_bus.h b/rootex/core/audio/audio_bus.h new file mode 100644 index 000000000..3f163b216 --- /dev/null +++ b/rootex/core/audio/audio_bus.h @@ -0,0 +1,26 @@ +#pragma once + +#include "common/common.h" + +class AudioBus +{ +private: + String m_BusName; + AudioBus* m_Parent = nullptr; + Vector> m_AudioComponents; + Vector m_Children; + bool m_IsMaster; // really needed? + // TODO: change volume of components when m_Volume changes + float m_Volume; + + AudioBus(); + AudioBus(AudioBus&) = delete; + ~AudioBus() = default; + +public: + void addAudioComponent(Ref cp); + void onVolumeChange(float delta); + void setParent(AudioBus* parent); + String getBusName() { return m_BusName; }; + float& getBusVolume() { return m_Volume; }; +}; diff --git a/rootex/framework/components/audio/audio_component.cpp b/rootex/framework/components/audio/audio_component.cpp index e7a53ff4d..861d2fcaa 100644 --- a/rootex/framework/components/audio/audio_component.cpp +++ b/rootex/framework/components/audio/audio_component.cpp @@ -28,7 +28,9 @@ AudioComponent::AudioComponent( , m_DependencyOnBoxColliderComponent(this) , m_DependencyOnCapsuleColliderComponent(this) , m_DependencyOnSphereColliderComponent(this) -{ + , m_AudioBus(nullptr) // get the master bus ptr => getMasterBus() +{ + // By default the audio component's bus should be master } RigidBodyComponent* AudioComponent::getCollider() @@ -127,10 +129,21 @@ void AudioComponent::setLooping(bool enabled) void AudioComponent::setAudioBus(AudioBus* bus) { - m_AudioBus = bus; + m_AudioBus = bus; // to be thought upon whether, using BusName here would be better or a pointer bus->addAudioComponent(Ref(this)); } +void AudioComponent::changeVolume(float delta) +{ + if (delta > 1.0f) + { + return; + WARN("Wrong volume change"); + } + + m_Volume = m_Volume * (1.0f - delta); +} + void AudioComponent::draw() { RenderSystem::GetSingleton()->submitSphere(getTransformComponent()->getAbsoluteTransform().Translation(), m_MaxDistance); diff --git a/rootex/framework/components/audio/audio_component.h b/rootex/framework/components/audio/audio_component.h index b47059607..a22a2c5e3 100644 --- a/rootex/framework/components/audio/audio_component.h +++ b/rootex/framework/components/audio/audio_component.h @@ -7,7 +7,9 @@ #include "components/physics/box_collider_component.h" #include "components/physics/sphere_collider_component.h" #include "components/physics/capsule_collider_component.h" -#include "systems/audio_system.h" +#include "systems/audio_system.h" + +class AudioBus; class AudioComponent : public Component { @@ -66,4 +68,5 @@ class AudioComponent : public Component void draw() override; void setAudioBus(AudioBus* bus); + void changeVolume(float delta); }; diff --git a/rootex/framework/systems/audio_system.cpp b/rootex/framework/systems/audio_system.cpp index e164b6052..34e9eaedb 100644 --- a/rootex/framework/systems/audio_system.cpp +++ b/rootex/framework/systems/audio_system.cpp @@ -154,6 +154,16 @@ void AudioSystem::removeBus(AudioBus* bus) // removes an audio bus from the vector and the tree } +AudioBus::AudioBus() + : m_Volume(100.0f) + , m_BusName("Bus " + AudioSystem::GetSingleton()->getAudioBuses().size()) // naming logic to be seen + , m_Parent(nullptr) // if not the first bus, then we need to see how to assign this to the master bus in the starting + , m_IsMaster(false) +{ + m_AudioComponents.clear(); + m_Children.clear(); +} + void AudioBus::addAudioComponent(Ref cp) { m_AudioComponents.push_back(cp); @@ -162,8 +172,9 @@ void AudioBus::addAudioComponent(Ref cp) void AudioSystem::draw() { System::draw(); - for (auto& bus : m_Buses) + for (int i = 0; i < m_Buses.size(); i++) { + AudioBus* bus = m_Buses[i]; ImGui::Text(bus->getBusName().c_str()); if (ImGui::Button(ICON_ROOTEX_MINUS "##Remove Bus")) @@ -172,13 +183,14 @@ void AudioSystem::draw() } // volume of the bus - ImGui::DragFloat("Volume", &bus->getBusVolume(), 0.01f, 0.0f, 100.0f); + ImGui::DragFloat("Volume", &bus->getBusVolume(), 1.0f, 0.0f, 100.0f); // selecting the parent if (ImGui::BeginCombo("Parent", m_Buses[0]->getBusName().c_str())) { - for (auto& cp : m_Buses) + for (int j = 0; j < i; j++) { + AudioBus* cp = m_Buses[j]; if (ImGui::Selectable(cp->getBusName().c_str())) { bus->setParent(cp); @@ -238,12 +250,15 @@ void AudioSystem::shutDown() AudioSystem::AudioSystem() : System("AudioSystem", UpdateOrder::Async, true) { + m_Buses.clear(); + m_Buses.push_back(nullptr); // add the master audio bus here => new AudioBus(is_Master = true) } void AudioBus::onVolumeChange(float delta) { for (auto& ac : m_AudioComponents) { + ac->changeVolume(delta); // the fraction of change is to be decided } diff --git a/rootex/framework/systems/audio_system.h b/rootex/framework/systems/audio_system.h index 05f4295fa..7f6e0535b 100644 --- a/rootex/framework/systems/audio_system.h +++ b/rootex/framework/systems/audio_system.h @@ -4,6 +4,7 @@ #include "components/space/transform_component.h" #include "components/audio/audio_listener_component.h" +#include "components/audio/audio_component.h" #include "al.h" #include "alc.h" @@ -45,6 +46,30 @@ class AudioSource; class StreamingAudioSource; class ResourceFile; +class AudioComponent; + +class AudioBus +{ +private: + String m_BusName; + AudioBus* m_Parent = nullptr; + Vector> m_AudioComponents; + Vector m_Children; + bool m_IsMaster; // really needed? + float m_Volume; + + AudioBus(); + AudioBus(AudioBus&) = delete; + ~AudioBus() = default; + +public: + void addAudioComponent(Ref cp); + void onVolumeChange(float delta); + void setParent(AudioBus* parent); + String getBusName() { return m_BusName; }; + float& getBusVolume() { return m_Volume; }; +}; + /// Audio System responsible for streaming and static audio class AudioSystem : public System @@ -92,26 +117,3 @@ class AudioSystem : public System void addNewBus(); void removeBus(AudioBus* bus); }; - -class AudioBus -{ -private: - String m_BusName; - AudioBus* m_Parent = nullptr; - Vector> m_AudioComponents; - Vector m_Children; - bool m_IsMaster; - // TODO: change volume of components when m_Volume changes - float m_Volume; - - AudioBus(); - AudioBus(AudioBus&) = delete; - ~AudioBus() = default; - -public: - void addAudioComponent(Ref cp); - void onVolumeChange(float delta); - void setParent(AudioBus* parent); - String getBusName() { return m_BusName; }; - float& getBusVolume() { return m_Volume; }; -};