AkComponentCallbackManager.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. #include "AkComponentCallbackManager.h"
  16. #include "AkAudioDevice.h"
  17. #include "AkInclude.h"
  18. #include "Misc/ScopeLock.h"
  19. #include "Misc/ScopeExit.h"
  20. #include "Async/Async.h"
  21. #include "AkCallbackInfoPool.h"
  22. #include "AkComponent.h"
  23. #include "Wwise/WwiseExternalSourceManager.h"
  24. #include "Wwise/WwiseRetriggerableAsyncTask.h"
  25. #include "UObject/UObjectThreadContext.h"
  26. struct FAkComponentCallbackManager_Constants
  27. {
  28. /// Optimization policy
  29. enum class Optimize
  30. {
  31. MemoryUsage,
  32. Speed,
  33. Value = MemoryUsage, ///< Set to either MemoryUsage or Speed
  34. };
  35. /// The default number of expected simultaneously playing sounds on a specific GameObject
  36. enum { ReserveSize = 8, };
  37. };
  38. FAkComponentCallbackManager* FAkComponentCallbackManager::Instance = nullptr;
  39. FAkComponentCallbackManager* FAkComponentCallbackManager::GetInstance()
  40. {
  41. return Instance;
  42. }
  43. void FAkFunctionPtrEventCallbackPackage::HandleAction(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
  44. {
  45. if (pfnUserCallback)
  46. {
  47. in_pCallbackInfo->pCookie = pUserCookie;
  48. pfnUserCallback(in_eType, in_pCallbackInfo);
  49. in_pCallbackInfo->pCookie = (void*)this;
  50. }
  51. }
  52. void FAkFunctionPtrEventCallbackPackage::CancelCallback()
  53. {
  54. pfnUserCallback = nullptr;
  55. uUserFlags = 0;
  56. }
  57. void FAkBlueprintDelegateEventCallbackPackage::HandleAction(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
  58. {
  59. if (BlueprintCallback.IsBound())
  60. {
  61. AkCallbackInfo* cbInfoCopy = AkCallbackTypeHelpers::CopyWwiseCallbackInfo(in_eType, in_pCallbackInfo);
  62. EAkCallbackType BlueprintCallbackType = AkCallbackTypeHelpers::GetBlueprintCallbackTypeFromAkCallbackType(in_eType);
  63. auto CachedBlueprintCallback = BlueprintCallback;
  64. auto* Task = new FWwiseRetriggerableAsyncTask(ENamedThreads::GameThread, [cbInfoCopy, BlueprintCallbackType, CachedBlueprintCallback]
  65. {
  66. if (FUObjectThreadContext::Get().IsRoutingPostLoad)
  67. {
  68. UE_LOG(LogAkAudio, Verbose, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Delaying Blueprint execution because IsRoutingPostLoad."));
  69. return EWwiseDeferredAsyncResult::KeepRunning;
  70. }
  71. if (!cbInfoCopy)
  72. {
  73. UE_LOG(LogAkAudio, Log, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Could not get CallbackInfo structure, callback will be ignored."));
  74. return EWwiseDeferredAsyncResult::Done;
  75. }
  76. ON_SCOPE_EXIT {
  77. FMemory::Free(cbInfoCopy);
  78. };
  79. if (!CachedBlueprintCallback.IsBound())
  80. {
  81. UE_LOG(LogAkAudio, Log, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Blueprint delegate is not bound, it will be ignored."));
  82. return EWwiseDeferredAsyncResult::Done;
  83. }
  84. UAkComponent* akComponent = (UAkComponent*)cbInfoCopy->gameObjID;
  85. if (cbInfoCopy->gameObjID != DUMMY_GAMEOBJ && !IsValid(akComponent))
  86. {
  87. UE_LOG(LogAkAudio, Log, TEXT("FAkBlueprintDelegateEventCallbackPackage::HandleAction: Could not get valid AkComponent, callback will be ignored."));
  88. return EWwiseDeferredAsyncResult::Done;
  89. }
  90. UAkCallbackInfo* BlueprintAkCallbackInfo = AkCallbackTypeHelpers::GetBlueprintableCallbackInfo(BlueprintCallbackType, cbInfoCopy);
  91. CachedBlueprintCallback.ExecuteIfBound(BlueprintCallbackType, BlueprintAkCallbackInfo);
  92. if (auto AudioDevice = FAkAudioDevice::Get())
  93. {
  94. if (auto CallbackInfoPool = AudioDevice->GetAkCallbackInfoPool())
  95. {
  96. CallbackInfoPool->Release(BlueprintAkCallbackInfo);
  97. }
  98. }
  99. return EWwiseDeferredAsyncResult::Done;
  100. });
  101. Task->ScheduleTask();
  102. }
  103. }
  104. void FAkBlueprintDelegateEventCallbackPackage::CancelCallback()
  105. {
  106. BlueprintCallback.Clear();
  107. uUserFlags = 0;
  108. }
  109. void FAkLatentActionEventCallbackPackage::HandleAction(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
  110. {
  111. // Don't access EndOfEventLatentAction if it's been deleted already
  112. if (!LatentActionValidityToken->bValid)
  113. {
  114. return;
  115. }
  116. if (EndOfEventLatentAction)
  117. {
  118. EndOfEventLatentAction->EventFinished = true;
  119. }
  120. }
  121. void FAkComponentCallbackManager::AkComponentCallback(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
  122. {
  123. auto pPackage = (IAkUserEventCallbackPackage*)in_pCallbackInfo->pCookie;
  124. if (Instance && pPackage)
  125. {
  126. const auto& gameObjID = in_pCallbackInfo->gameObjID;
  127. bool deletePackage = false;
  128. {
  129. FScopeLock Lock(&Instance->CriticalSection);
  130. auto pPackageSet = Instance->GameObjectToPackagesMap.Find(gameObjID);
  131. if (pPackageSet && in_eType == AK_EndOfEvent)
  132. {
  133. Instance->RemovePackageFromSet(pPackageSet, pPackage, gameObjID);
  134. }
  135. }
  136. if (in_eType == AK_EndOfEvent)
  137. {
  138. deletePackage = true;
  139. if (auto* Device = FAkAudioDevice::Get())
  140. {
  141. Device->RemovePlayingID(((AkEventCallbackInfo*)in_pCallbackInfo)->eventID, ((AkEventCallbackInfo*)in_pCallbackInfo)->playingID);
  142. }
  143. if(pPackage->HasExternalSources)
  144. {
  145. if (auto* ExternalSourceMananger = IWwiseExternalSourceManager::Get())
  146. {
  147. ExternalSourceMananger->OnEndOfEvent(((AkEventCallbackInfo*)in_pCallbackInfo)->playingID);
  148. }
  149. }
  150. }
  151. if ((pPackage->uUserFlags & in_eType) != 0)
  152. {
  153. pPackage->HandleAction(in_eType, in_pCallbackInfo);
  154. }
  155. if (deletePackage)
  156. {
  157. delete pPackage;
  158. }
  159. }
  160. }
  161. FAkComponentCallbackManager::FAkComponentCallbackManager()
  162. {
  163. if (Instance != nullptr)
  164. {
  165. UE_LOG(LogAkAudio, Error, TEXT("FAkComponentCallbackManager has already been instantiated."));
  166. }
  167. Instance = this;
  168. }
  169. FAkComponentCallbackManager::~FAkComponentCallbackManager()
  170. {
  171. for (auto& Item : GameObjectToPackagesMap)
  172. {
  173. for (auto pPackage : Item.Value)
  174. {
  175. delete pPackage;
  176. }
  177. }
  178. Instance = nullptr;
  179. }
  180. IAkUserEventCallbackPackage* FAkComponentCallbackManager::CreateCallbackPackage(AkCallbackFunc in_cbFunc, void* in_Cookie, uint32 in_Flags, AkGameObjectID in_gameObjID, bool HasExternalSources)
  181. {
  182. uint32 KeyHash = GetKeyHash(in_Cookie);
  183. auto pPackage = new FAkFunctionPtrEventCallbackPackage(in_cbFunc, in_Cookie, in_Flags, KeyHash, HasExternalSources);
  184. if (pPackage)
  185. {
  186. FScopeLock Lock(&CriticalSection);
  187. GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Add(pPackage);
  188. UserCookieHashToPackageMap.Add(KeyHash, pPackage);
  189. }
  190. return pPackage;
  191. }
  192. IAkUserEventCallbackPackage* FAkComponentCallbackManager::CreateCallbackPackage(FOnAkPostEventCallback BlueprintCallback, uint32 in_Flags, AkGameObjectID in_gameObjID, bool HasExternalSources)
  193. {
  194. uint32 KeyHash = GetKeyHash(BlueprintCallback);
  195. auto pPackage = new FAkBlueprintDelegateEventCallbackPackage(BlueprintCallback, in_Flags, KeyHash, HasExternalSources);
  196. if (pPackage)
  197. {
  198. FScopeLock Lock(&CriticalSection);
  199. GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Add(pPackage);
  200. UserCookieHashToPackageMap.Add(KeyHash, pPackage);
  201. }
  202. return pPackage;
  203. }
  204. IAkUserEventCallbackPackage* FAkComponentCallbackManager::CreateCallbackPackage(FWaitEndOfEventAction* LatentAction, AkGameObjectID in_gameObjID, bool HasExternalSources)
  205. {
  206. auto pPackage = new FAkLatentActionEventCallbackPackage(LatentAction, 0, HasExternalSources);
  207. if (pPackage)
  208. {
  209. FScopeLock Lock(&CriticalSection);
  210. GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Add(pPackage);
  211. }
  212. return pPackage;
  213. }
  214. void FAkComponentCallbackManager::RemoveCallbackPackage(IAkUserEventCallbackPackage* in_Package, AkGameObjectID in_gameObjID)
  215. {
  216. {
  217. FScopeLock Lock(&CriticalSection);
  218. auto pPackageSet = GameObjectToPackagesMap.Find(in_gameObjID);
  219. if (pPackageSet)
  220. {
  221. RemovePackageFromSet(pPackageSet, in_Package, in_gameObjID);
  222. }
  223. }
  224. delete in_Package;
  225. }
  226. void FAkComponentCallbackManager::CancelEventCallback(void* in_Cookie)
  227. {
  228. CancelKeyHash(GetKeyHash(in_Cookie));
  229. }
  230. void FAkComponentCallbackManager::CancelEventCallback(const FOnAkPostEventCallback& in_Delegate)
  231. {
  232. CancelKeyHash(GetKeyHash(in_Delegate));
  233. }
  234. void FAkComponentCallbackManager::CancelKeyHash(uint32 HashToCancel)
  235. {
  236. FScopeLock AutoLock(&CriticalSection);
  237. TArray<IAkUserEventCallbackPackage*> PackagesToCancel;
  238. UserCookieHashToPackageMap.MultiFind(HashToCancel, PackagesToCancel);
  239. for (auto iter = PackagesToCancel.CreateConstIterator(); iter; ++iter)
  240. {
  241. if (*iter)
  242. {
  243. (*iter)->CancelCallback();
  244. }
  245. }
  246. }
  247. void FAkComponentCallbackManager::RegisterGameObject(AkGameObjectID in_gameObjID)
  248. {
  249. if (FAkComponentCallbackManager_Constants::Optimize::Value == FAkComponentCallbackManager_Constants::Optimize::Speed)
  250. {
  251. FScopeLock Lock(&CriticalSection);
  252. GameObjectToPackagesMap.FindOrAdd(in_gameObjID).Reserve(FAkComponentCallbackManager_Constants::ReserveSize);
  253. }
  254. }
  255. void FAkComponentCallbackManager::UnregisterGameObject(AkGameObjectID in_gameObjID)
  256. {
  257. // Do not cancel callbacks with the SoundEngine, as we need them for the
  258. // playingID bookkeeping. Deleting the packages will ensure we do not callback
  259. // into objects that may have been destroyed.
  260. FScopeLock Lock(&CriticalSection);
  261. auto pPackageSet = GameObjectToPackagesMap.Find(in_gameObjID);
  262. if (pPackageSet)
  263. {
  264. for (auto pPackage : *pPackageSet)
  265. {
  266. pPackage->CancelCallback();
  267. UserCookieHashToPackageMap.Remove(pPackage->KeyHash, pPackage);
  268. }
  269. GameObjectToPackagesMap.Remove(in_gameObjID);
  270. }
  271. }
  272. bool FAkComponentCallbackManager::HasActiveEvents(AkGameObjectID in_gameObjID)
  273. {
  274. FScopeLock Lock(&CriticalSection);
  275. auto pPackageSet = GameObjectToPackagesMap.Find(in_gameObjID);
  276. return pPackageSet && pPackageSet->Num() > 0;
  277. }
  278. void FAkComponentCallbackManager::RemovePackageFromSet(FAkComponentCallbackManager::PackageSet* in_pPackageSet, IAkUserEventCallbackPackage* in_pPackage, AkGameObjectID in_gameObjID)
  279. {
  280. // No need for a lock here because those calling this function are already locking
  281. in_pPackageSet->Remove(in_pPackage);
  282. UserCookieHashToPackageMap.Remove(in_pPackage->KeyHash, in_pPackage);
  283. if (FAkComponentCallbackManager_Constants::Optimize::Value == FAkComponentCallbackManager_Constants::Optimize::MemoryUsage)
  284. {
  285. if (in_pPackageSet->Num() == 0)
  286. {
  287. GameObjectToPackagesMap.Remove(in_gameObjID);
  288. }
  289. }
  290. }
  291. uint32 FAkComponentCallbackManager::GetKeyHash(void* Key)
  292. {
  293. return GetTypeHash(Key);
  294. }
  295. uint32 FAkComponentCallbackManager::GetKeyHash(const FOnAkPostEventCallback& Key)
  296. {
  297. return HashCombine(GetTypeHash(Key.GetUObject()), GetTypeHash(Key.GetFunctionName()));
  298. }