123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- /*******************************************************************************
- The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
- Technology released in source code form as part of the game integration package.
- The content of this file may not be used without valid licenses to the
- AUDIOKINETIC Wwise Technology.
- Note that the use of the game engine is subject to the Unreal(R) Engine End User
- License Agreement at https://www.unrealengine.com/en-US/eula/unreal
-
- License Usage
-
- Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
- this file in accordance with the end user license agreement provided with the
- software or, alternatively, in accordance with the terms contained
- in a written agreement between you and Audiokinetic Inc.
- Copyright (c) 2023 Audiokinetic Inc.
- *******************************************************************************/
- #include "AkComponentCallbackManager.h"
- #include "AkAudioDevice.h"
- #include "AkInclude.h"
- #include "Misc/ScopeLock.h"
- #include "Misc/ScopeExit.h"
- #include "Async/Async.h"
- #include "AkCallbackInfoPool.h"
- #include "AkComponent.h"
- #include "Wwise/WwiseExternalSourceManager.h"
- #include "Wwise/WwiseRetriggerableAsyncTask.h"
- #include "UObject/UObjectThreadContext.h"
- struct FAkComponentCallbackManager_Constants
- {
- /// Optimization policy
- enum class Optimize
- {
- MemoryUsage,
- Speed,
- Value = MemoryUsage, ///< Set to either MemoryUsage or Speed
- };
- /// The default number of expected simultaneously playing sounds on a specific GameObject
- enum { ReserveSize = 8, };
- };
- FAkComponentCallbackManager* FAkComponentCallbackManager::Instance = nullptr;
- FAkComponentCallbackManager* FAkComponentCallbackManager::GetInstance()
- {
- return Instance;
- }
- void FAkFunctionPtrEventCallbackPackage::HandleAction(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
- {
- if (pfnUserCallback)
- {
- in_pCallbackInfo->pCookie = pUserCookie;
- pfnUserCallback(in_eType, in_pCallbackInfo);
- in_pCallbackInfo->pCookie = (void*)this;
- }
- }
- void FAkFunctionPtrEventCallbackPackage::CancelCallback()
- {
- pfnUserCallback = nullptr;
- uUserFlags = 0;
- }
- void FAkBlueprintDelegateEventCallbackPackage::HandleAction(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
- {
- if (BlueprintCallback.IsBound())
- {
- AkCallbackInfo* cbInfoCopy = AkCallbackTypeHelpers::CopyWwiseCallbackInfo(in_eType, in_pCallbackInfo);
- EAkCallbackType BlueprintCallbackType = AkCallbackTypeHelpers::GetBlueprintCallbackTypeFromAkCallbackType(in_eType);
- auto CachedBlueprintCallback = BlueprintCallback;
- auto* Task = new FWwiseRetriggerableAsyncTask(ENamedThreads::GameThread, [cbInfoCopy, BlueprintCallbackType, CachedBlueprintCallback]
- {
- if (FUObjectThreadContext::Get().IsRoutingPostLoad)
- {
- UE_LOG(LogAkAudio, Verbose, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Delaying Blueprint execution because IsRoutingPostLoad."));
- return EWwiseDeferredAsyncResult::KeepRunning;
- }
- if (!cbInfoCopy)
- {
- UE_LOG(LogAkAudio, Log, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Could not get CallbackInfo structure, callback will be ignored."));
- return EWwiseDeferredAsyncResult::Done;
- }
- ON_SCOPE_EXIT {
- FMemory::Free(cbInfoCopy);
- };
- if (!CachedBlueprintCallback.IsBound())
- {
- UE_LOG(LogAkAudio, Log, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Blueprint delegate is not bound, it will be ignored."));
- return EWwiseDeferredAsyncResult::Done;
- }
- UAkComponent* akComponent = (UAkComponent*)cbInfoCopy->gameObjID;
- if (cbInfoCopy->gameObjID != DUMMY_GAMEOBJ && !IsValid(akComponent))
- {
- UE_LOG(LogAkAudio, Log, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Could not get valid AkComponent, callback will be ignored."));
- return EWwiseDeferredAsyncResult::Done;
- }
- UAkCallbackInfo* BlueprintAkCallbackInfo = AkCallbackTypeHelpers::GetBlueprintableCallbackInfo(BlueprintCallbackType, cbInfoCopy);
- CachedBlueprintCallback.ExecuteIfBound(BlueprintCallbackType, BlueprintAkCallbackInfo);
- if (auto AudioDevice = FAkAudioDevice::Get())
- {
- if (auto CallbackInfoPool = AudioDevice->GetAkCallbackInfoPool())
- {
- CallbackInfoPool->Release(BlueprintAkCallbackInfo);
- }
- }
- return EWwiseDeferredAsyncResult::Done;
- });
- Task->ScheduleTask();
- }
- }
- void FAkBlueprintDelegateEventCallbackPackage::CancelCallback()
- {
- BlueprintCallback.Clear();
- uUserFlags = 0;
- }
- void FAkLatentActionEventCallbackPackage::HandleAction(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
- {
- // Don't access EndOfEventLatentAction if it's been deleted already
- if (!LatentActionValidityToken->bValid)
- {
- return;
- }
- if (EndOfEventLatentAction)
- {
- EndOfEventLatentAction->EventFinished = true;
- }
- }
- void FAkComponentCallbackManager::AkComponentCallback(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
- {
- auto pPackage = (IAkUserEventCallbackPackage*)in_pCallbackInfo->pCookie;
- if (Instance && pPackage)
- {
- const auto& gameObjID = in_pCallbackInfo->gameObjID;
- bool deletePackage = false;
- {
- FScopeLock Lock(&Instance->CriticalSection);
- auto pPackageSet = Instance->GameObjectToPackagesMap.Find(gameObjID);
- if (pPackageSet && in_eType == AK_EndOfEvent)
- {
- Instance->RemovePackageFromSet(pPackageSet, pPackage, gameObjID);
- }
- }
- if (in_eType == AK_EndOfEvent)
- {
- deletePackage = true;
- if (auto* Device = FAkAudioDevice::Get())
- {
- Device->RemovePlayingID(((AkEventCallbackInfo*)in_pCallbackInfo)->eventID, ((AkEventCallbackInfo*)in_pCallbackInfo)->playingID);
- }
- if(pPackage->HasExternalSources)
- {
- if (auto* ExternalSourceMananger = IWwiseExternalSourceManager::Get())
- {
- ExternalSourceMananger->OnEndOfEvent(((AkEventCallbackInfo*)in_pCallbackInfo)->playingID);
- }
- }
- }
- if ((pPackage->uUserFlags & in_eType) != 0)
- {
- pPackage->HandleAction(in_eType, in_pCallbackInfo);
- }
-
- if (deletePackage)
- {
- delete pPackage;
- }
- }
- }
- FAkComponentCallbackManager::FAkComponentCallbackManager()
- {
- if (Instance != nullptr)
- {
- UE_LOG(LogAkAudio, Error, TEXT("FAkComponentCallbackManager has already been instantiated."));
- }
- Instance = this;
- }
- FAkComponentCallbackManager::~FAkComponentCallbackManager()
- {
- for (auto& Item : GameObjectToPackagesMap)
- {
- for (auto pPackage : Item.Value)
- {
- delete pPackage;
- }
- }
- Instance = nullptr;
- }
- IAkUserEventCallbackPackage* FAkComponentCallbackManager::CreateCallbackPackage(AkCallbackFunc in_cbFunc, void* in_Cookie, uint32 in_Flags, AkGameObjectID in_gameObjID, bool HasExternalSources)
- {
- uint32 KeyHash = GetKeyHash(in_Cookie);
- auto pPackage = new FAkFunctionPtrEventCallbackPackage(in_cbFunc, in_Cookie, in_Flags, KeyHash, HasExternalSources);
- if (pPackage)
- {
- FScopeLock Lock(&CriticalSection);
- GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Add(pPackage);
- UserCookieHashToPackageMap.Add(KeyHash, pPackage);
- }
- return pPackage;
- }
- IAkUserEventCallbackPackage* FAkComponentCallbackManager::CreateCallbackPackage(FOnAkPostEventCallback BlueprintCallback, uint32 in_Flags, AkGameObjectID in_gameObjID, bool HasExternalSources)
- {
- uint32 KeyHash = GetKeyHash(BlueprintCallback);
- auto pPackage = new FAkBlueprintDelegateEventCallbackPackage(BlueprintCallback, in_Flags, KeyHash, HasExternalSources);
- if (pPackage)
- {
- FScopeLock Lock(&CriticalSection);
- GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Add(pPackage);
- UserCookieHashToPackageMap.Add(KeyHash, pPackage);
- }
- return pPackage;
- }
- IAkUserEventCallbackPackage* FAkComponentCallbackManager::CreateCallbackPackage(FWaitEndOfEventAction* LatentAction, AkGameObjectID in_gameObjID, bool HasExternalSources)
- {
- auto pPackage = new FAkLatentActionEventCallbackPackage(LatentAction, 0, HasExternalSources);
- if (pPackage)
- {
- FScopeLock Lock(&CriticalSection);
- GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Add(pPackage);
- }
- return pPackage;
- }
- void FAkComponentCallbackManager::RemoveCallbackPackage(IAkUserEventCallbackPackage* in_Package, AkGameObjectID in_gameObjID)
- {
- {
- FScopeLock Lock(&CriticalSection);
- auto pPackageSet = GameObjectToPackagesMap.Find(in_gameObjID);
- if (pPackageSet)
- {
- RemovePackageFromSet(pPackageSet, in_Package, in_gameObjID);
- }
- }
- delete in_Package;
- }
- void FAkComponentCallbackManager::CancelEventCallback(void* in_Cookie)
- {
- CancelKeyHash(GetKeyHash(in_Cookie));
- }
- void FAkComponentCallbackManager::CancelEventCallback(const FOnAkPostEventCallback& in_Delegate)
- {
- CancelKeyHash(GetKeyHash(in_Delegate));
- }
- void FAkComponentCallbackManager::CancelKeyHash(uint32 HashToCancel)
- {
- FScopeLock AutoLock(&CriticalSection);
- TArray<IAkUserEventCallbackPackage*> PackagesToCancel;
- UserCookieHashToPackageMap.MultiFind(HashToCancel, PackagesToCancel);
- for (auto iter = PackagesToCancel.CreateConstIterator(); iter; ++iter)
- {
- if (*iter)
- {
- (*iter)->CancelCallback();
- }
- }
- }
- void FAkComponentCallbackManager::RegisterGameObject(AkGameObjectID in_gameObjID)
- {
- if (FAkComponentCallbackManager_Constants::Optimize::Value == FAkComponentCallbackManager_Constants::Optimize::Speed)
- {
- FScopeLock Lock(&CriticalSection);
- GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Reserve(FAkComponentCallbackManager_Constants::ReserveSize);
- }
- }
- void FAkComponentCallbackManager::UnregisterGameObject(AkGameObjectID in_gameObjID)
- {
- // Do not cancel callbacks with the SoundEngine, as we need them for the
- // playingID bookkeeping. Deleting the packages will ensure we do not callback
- // into objects that may have been destroyed.
- FScopeLock Lock(&CriticalSection);
- auto pPackageSet = GameObjectToPackagesMap.Find(in_gameObjID);
- if (pPackageSet)
- {
- for (auto pPackage : *pPackageSet)
- {
- pPackage->CancelCallback();
- UserCookieHashToPackageMap.Remove(pPackage->KeyHash, pPackage);
- }
- GameObjectToPackagesMap.Remove(in_gameObjID);
- }
- }
- bool FAkComponentCallbackManager::HasActiveEvents(AkGameObjectID in_gameObjID)
- {
- FScopeLock Lock(&CriticalSection);
- auto pPackageSet = GameObjectToPackagesMap.Find(in_gameObjID);
- return pPackageSet && pPackageSet->Num() > 0;
- }
- void FAkComponentCallbackManager::RemovePackageFromSet(FAkComponentCallbackManager::PackageSet* in_pPackageSet, IAkUserEventCallbackPackage* in_pPackage, AkGameObjectID in_gameObjID)
- {
- // No need for a lock here because those calling this function are already locking
- in_pPackageSet->Remove(in_pPackage);
- UserCookieHashToPackageMap.Remove(in_pPackage->KeyHash, in_pPackage);
- if (FAkComponentCallbackManager_Constants::Optimize::Value == FAkComponentCallbackManager_Constants::Optimize::MemoryUsage)
- {
- if (in_pPackageSet->Num() == 0)
- {
- GameObjectToPackagesMap.Remove(in_gameObjID);
- }
- }
- }
- uint32 FAkComponentCallbackManager::GetKeyHash(void* Key)
- {
- return GetTypeHash(Key);
- }
- uint32 FAkComponentCallbackManager::GetKeyHash(const FOnAkPostEventCallback& Key)
- {
- return HashCombine(GetTypeHash(Key.GetUObject()), GetTypeHash(Key.GetFunctionName()));
- }
|