#include "SHpch.h" #include "SHAudioSystem.h" #include "Scene/SHSceneManager.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "ECS_Base/Managers/SHEntityManager.h" #include #include "AudioSystem/SHAudioListenerComponent.h" #include "AudioSystem/SHAudioSourceComponent.h" #include "Math/Transform/SHTransformComponent.h" #pragma warning(push) #pragma warning(disable:26812) //disable warning about preference of enum class over enum as ImGuizmo uses enums #include #include #include #include namespace SHADE { SHAudioSystem::SHAudioSystem() : fmodStudioSystem(nullptr) , extraDriverData(nullptr) , soundList() , bgmChannelGroup(nullptr) , sfxChannelGroup(nullptr) , masterGroup(nullptr) , audioChannels() , result(FMOD_RESULT_FORCEINT) , bgmVolume(1.F) , sfxVolume(1.F) , masterVolume(1.0F) , version(0) , speakerMode(FMOD_SPEAKERMODE_5POINT1) , paused(false) { result = FMOD::Studio::System::create(&fmodStudioSystem); ErrorCheck(); } SHAudioSystem::~SHAudioSystem() { } void SHADE::SHAudioSystem::Init() { SHADE::SHComponentManager::CreateComponentSparseSet(); SHADE::SHComponentManager::CreateComponentSparseSet(); denseListener = &SHComponentManager::GetDense(); fmodStudioSystem->getCoreSystem(&fmodSystem); result = fmodStudioSystem->initialize(AUDIO_SYS_MAX_CHANNELS, AUDIO_SYS_MAX_CHANNELS, FMOD_STUDIO_INIT_NORMAL, extraDriverData); ErrorCheck(); fmodSystem->setSoftwareFormat(0, speakerMode, 0); result = fmodSystem->createChannelGroup("SFX", &sfxChannelGroup); ErrorCheck(); result = fmodSystem->createChannelGroup("BGM", &bgmChannelGroup); ErrorCheck(); result = fmodSystem->getMasterChannelGroup(&masterGroup); ErrorCheck(); result = masterGroup->addGroup(bgmChannelGroup); ErrorCheck(); result = masterGroup->addGroup(sfxChannelGroup); ErrorCheck(); bgmChannelGroup->setVolume(bgmVolume); sfxChannelGroup->setVolume(sfxVolume); masterGroup->setVolume(masterVolume); //SHResourceManager::LoadAllAudio(system, soundList); LoadBank("../../Assets/Audio/Master.bank"); LoadBank("../../Assets/Audio/Master.strings.bank"); //LoadBank("../../Assets/Audio/Music.bank"); LoadBank("../../Assets/Audio/footsteps.bank"); //auto clip = CreateAudioClip("event:/Characters/sfx_footsteps_human"); //clip->Play(); //PlayEventOnce("event:/Characters/sfx_footsteps_raccoon"); //PlayEventOnce("event:/SFX/Dawn/Dawn_Attack"); } void SHADE::SHAudioSystem::Run(float dt) { static_cast(dt); if (GetKeyState(VK_SPACE) & 0x8000) PlayEventOnce("event:/Characters/sfx_footsteps_raccoon"); fmodStudioSystem->update(); if (!denseListener->empty()) { SHAudioListenerComponent& listener = denseListener->at(0); SHTransformComponent* listenerTransform = SHComponentManager::GetComponent_s(listener.GetEID()); if (listenerTransform) { listener.SetPos(listenerTransform->GetLocalPosition()); listener.SetForward({ (listenerTransform->GetLocalScale()[0] > 0.f) ? 1.f : -1.f, 0.f, 0.f }); FMOD_VECTOR pos = { listener.pos[0] ,listener.pos[1] ,0.f }; FMOD_VECTOR forward = { listener.forward[0] ,listener.forward[1] ,listener.forward[2] }; FMOD_VECTOR up = { listener.up[0] ,listener.up[1] ,listener.up[2] }; fmodSystem->set3DListenerAttributes(0, &pos, nullptr, &forward, &up); } } } SHAudioSystem::AudioRoutine::AudioRoutine() : SHSystemRoutine("Audio Routine", false) {} void SHAudioSystem::AudioRoutine::Execute(double dt) noexcept { reinterpret_cast(system)->Run(dt); } void SHADE::SHAudioSystem::Exit() { for (auto& event : eventMap) { result = event.second->releaseAllInstances(); ErrorCheck(); } for (auto& bank : bankMap) { result = bank.second->unload(); ErrorCheck(); } for (auto& sound : soundList) { result = sound.second->release(); ErrorCheck(); } if (bgmChannelGroup) { result = bgmChannelGroup->release(); ErrorCheck(); } if (sfxChannelGroup) { result = sfxChannelGroup->release(); ErrorCheck(); } if (fmodStudioSystem) { result = fmodStudioSystem->release(); ErrorCheck(); } } void SHAudioSystem::ErrorCheck() const { if (result != FMOD_OK) std::cerr << "Audio system error: " << FMOD_ErrorString(result) << std::endl; } void SHAudioSystem::PlayEventOnce(const char* path, bool isSFX, EntityID eid, bool spatial) { if (paused) return; auto it = eventMap.find(path); if (it != eventMap.end()) { FMOD::Studio::EventInstance* event = nullptr; it->second->createInstance(&event); if (event) { event->setVolume(masterVolume * (isSFX ? sfxVolume : bgmVolume)); if (spatial) { if (SHTransformComponent* audioTransform = SHComponentManager::GetComponent_s(eid)) { FMOD_3D_ATTRIBUTES attributes{ {} }; attributes.forward.z = 1.0f; attributes.up.y = 1.0f; SHAudioListenerComponent& listener = denseListener->at(0); SHTransformComponent* listenerTransform = SHComponentManager::GetComponent_s(listener.GetEID()); if (listenerTransform) { attributes.position.z = listenerTransform->GetLocalPosition()[2]; } attributes.position.x = audioTransform->GetLocalPosition()[0]; attributes.position.y = audioTransform->GetLocalPosition()[1]; event->set3DAttributes(&attributes); } } event->start(); event->release(); } } } void SHAudioSystem::PlaySFX(EntityID id, EntityID eid, const bool& loop, const bool& spatial, float min, float max) { SHSound sound = soundList[id]; int index = GetAvailableChannelIndex(); if (index >= 0) { unsigned int mode{}; mode |= (loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); mode |= (spatial ? FMOD_3D : FMOD_2D); sound->setMode(mode); result = fmodSystem->playSound(sound, sfxChannelGroup, false, &audioChannels[index]); if (spatial && SHComponentManager::HasComponent(eid)) { SHTransformComponent* audioTransform = SHComponentManager::GetComponent_s(eid); FMOD_VECTOR fpos{ audioTransform->GetLocalPosition()[0],audioTransform->GetLocalPosition()[1] ,0.f}; audioChannels[index]->set3DAttributes(&fpos, nullptr); audioChannels[index]->setMode(mode); audioChannels[index]->set3DMinMaxDistance(min, max); } ErrorCheck(); } } void SHAudioSystem::PlayBGM(EntityID id, EntityID eid, const bool& loop, const bool& spatial, float min, float max) { SHSound sound = soundList[id]; int index = GetAvailableChannelIndex(); if (index >= 0) { unsigned int mode{}; mode |= (loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); mode |= (spatial ? FMOD_3D : FMOD_2D); sound->setMode(mode); result = fmodSystem->playSound(sound, bgmChannelGroup, false, &audioChannels[index]); if (spatial && SHComponentManager::HasComponent(eid)) { SHTransformComponent* audioTransform = SHComponentManager::GetComponent_s(eid); FMOD_VECTOR fpos{ audioTransform->GetLocalPosition()[0],audioTransform->GetLocalPosition()[1] ,0.f }; audioChannels[index]->set3DAttributes(&fpos, nullptr); audioChannels[index]->setMode(mode); audioChannels[index]->set3DMinMaxDistance(min, max); } ErrorCheck(); } } void SHAudioSystem::SetMute(EntityID id, bool mute) { SHSound sound; for (auto& channel : audioChannels) { channel->getCurrentSound(&sound); if (soundList.find(id)->second == sound) // tbc { channel->setMute(mute); } } } void SHAudioSystem::StopSound(EntityID id) { SHSound sound; for (auto& channel : audioChannels) { channel->getCurrentSound(&sound); if (soundList.find(id)->second == sound) // tbc { channel->stop(); } } } void SHAudioSystem::StopAllSounds() { for (auto& channel : audioChannels) { bool isPlaying{ false }; if (channel->isPlaying(&isPlaying) == FMOD_OK && isPlaying) channel->stop(); } } std::optional SHAudioSystem::GetEventGUID(const char* path) { FMOD_GUID guid; FMOD::Studio::EventDescription* event; result = fmodStudioSystem->getEvent(path, &event); ErrorCheck(); if (result == FMOD_OK) { result = event->getID(&guid); ErrorCheck(); if (result == FMOD_OK) { return guid; } } return std::nullopt; } AudioClip* SHAudioSystem::CreateAudioClip(const char* path) { AudioClipID newID{}; AudioClip* clip = nullptr; auto it = eventMap.find(path); if (it != eventMap.end()) { FMOD::Studio::EventInstance* event = nullptr; it->second->createInstance(&event); if (event) { //event->start(); newID = clipID; clipID++; eventInstances.emplace(newID, AudioClip(newID, event)); clip = &eventInstances[newID]; } } return clip; } //std::vector SHAudioSystem::GetAllEvents() //{ // int count{}; // stringsBank->getEventCount(&count); // std::vector events(count); // auto eventData = events.data(); // int finalCount{}; // stringsBank->getEventList(eventData, count, &finalCount); // std::vector eventNames; // std::transform(events.begin(), events.end(), std::back_inserter(eventNames), [](FMOD::Studio::EventDescription* event) // { // char path[256]; // event->getPath(path, 256, nullptr); // return path; // }); // return eventNames; //} //void SHAudioSystem::PlayEventInstance(FMOD::Studio::EventInstance* instance, bool isSFX, EntityID eid, bool spatial) //{ // instance->setVolume(masterVolume * (isSFX ? sfxVolume : bgmVolume)); // FMOD::ChannelGroup* channelGroup; // if (spatial) // { // if (SHTransformComponent* audioTransform = SHComponentManager::GetComponent_s(eid)) // { // FMOD_3D_ATTRIBUTES attributes{ {} }; // attributes.forward.z = 1.0f; // attributes.up.y = 1.0f; // SHAudioListenerComponent& listener = denseListener->at(0); // SHTransformComponent* listenerTransform = SHComponentManager::GetComponent_s(listener.GetEID()); // if (listenerTransform) // { // attributes.position.z = listenerTransform->GetTranslation()[2]; // } // attributes.position.x = audioTransform->GetTranslation()[0]; // attributes.position.y = audioTransform->GetTranslation()[1]; // instance->set3DAttributes(&attributes); // } // } // instance->start(); //} int SHAudioSystem::GetAvailableChannelIndex() { bool isPlaying = false; for (int i = 0; i < AUDIO_SYS_MAX_CHANNELS; ++i) { audioChannels[i]->isPlaying(&isPlaying); if (!isPlaying) return i; } return -1; } float SHAudioSystem::GetBgmVolume() { return bgmVolume; } float SHAudioSystem::GetSfxVolume() { return sfxVolume; } float SHAudioSystem::GetMasterVolume() { return masterVolume; } void SHAudioSystem::SetBgmVolume(float const bgmvol) { bgmChannelGroup->setVolume(bgmvol); } void SHAudioSystem::SetSfxVolume(float const sfxvol) { sfxChannelGroup->setVolume(sfxvol); } void SHAudioSystem::SetMasterVolume(float const mastervol) { masterGroup->setVolume(mastervol); } void SHAudioSystem::SetPaused(bool pause) { paused = pause; for (auto const& channel : audioChannels) { channel->setPaused(paused); } for (auto const& event : eventMap) { int instanceCount = 0; event.second->getInstanceCount(&instanceCount); std::vector instances(instanceCount); event.second->getInstanceList(instances.data(), instances.size(), &instanceCount); for (auto const& instance : instances) { instance->setPaused(pause); } } } bool SHAudioSystem::GetPaused() const { return paused; } SHVec3 SHAudioSystem::GetListenerPosition() { auto &listener = denseListener->at(0); SHTransformComponent* listenerTransform = SHComponentManager::GetComponent_s(listener.GetEID()); if (listenerTransform) { return listenerTransform->GetLocalPosition(); } return {}; } void SHAudioSystem::LoadBank(const char* path) { FMOD::Studio::Bank* bank = nullptr; result = fmodStudioSystem->loadBankFile(path, FMOD_STUDIO_LOAD_BANK_NORMAL, &bank); ErrorCheck(); if (result != FMOD_OK) return; bankMap.emplace(path, bank); bank->loadSampleData(); int numOfEvents; bank->getEventCount(&numOfEvents); if (numOfEvents > 0) { std::vector events(numOfEvents); bank->getEventList(events.data(), numOfEvents, &numOfEvents); char eventName[512]; for (int i{}; i < numOfEvents; ++i) { FMOD::Studio::EventDescription* event = events[i]; event->getPath(eventName, 512, nullptr); eventMap.emplace(eventName, event); } } } AudioClip::AudioClip(AudioClipID clipID, FMOD::Studio::EventInstance* inst) :instance(inst), id(clipID) { } AudioClip::~AudioClip() { } void AudioClip::Play(bool isSfx) { if (!instance) return; instance->start(); auto audioSystem = SHSystemManager::GetSystem(); instance->setVolume(audioSystem->GetMasterVolume() * (isSfx ? audioSystem->GetSfxVolume() : audioSystem->GetBgmVolume())); } void AudioClip::Play(SHVec3 position, bool isSfx) { if (!instance) return; instance->start(); FMOD_3D_ATTRIBUTES attributes{ {} }; attributes.forward.z = 1.0f; attributes.up.y = 1.0f; auto audioSystem = SHSystemManager::GetSystem(); SHVec3 listenerPos = audioSystem->GetListenerPosition(); attributes.position.x = position[0]; attributes.position.y = position[1]; attributes.position.z = listenerPos[2]; instance->set3DAttributes(&attributes); instance->setVolume(audioSystem->GetMasterVolume() * (isSfx ? audioSystem->GetSfxVolume() : audioSystem->GetBgmVolume())); } void AudioClip::Stop(bool fadeOut) { if (!instance) return; instance->stop(fadeOut ? FMOD_STUDIO_STOP_ALLOWFADEOUT : FMOD_STUDIO_STOP_IMMEDIATE); } void AudioClip::SetPause(bool pause) { if (!instance) return; instance->setPaused(pause); } bool AudioClip::IsPaused() { if (!instance) return true; bool paused{}; instance->getPaused(&paused); return paused; } void AudioClip::SetParameter(const char* paramName, float value) { if (!instance) return; instance->setParameterByName(paramName, value); } void AudioClip::SetParameterLabel(const char* paramName, const char* label) { if (!instance) return; instance->setParameterByNameWithLabel(paramName, label); } float AudioClip::GetParameterValue(const char* paramName) { if (!instance) return {}; float value{}; instance->getParameterByName(paramName, &value); return value; } } #pragma warning(pop)