AkAudioEvent.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  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. /*=============================================================================
  16. AkEvent.cpp:
  17. =============================================================================*/
  18. #include "AkAudioEvent.h"
  19. #include "AkAudioBank.h"
  20. #include "AkAudioDevice.h"
  21. #include "AkComponent.h"
  22. #include "AkComponentCallbackManager.h"
  23. #include "AkGameObject.h"
  24. #include "AkRoomComponent.h"
  25. #include "WwiseUnrealDefines.h"
  26. #include "Wwise/WwiseExternalSourceManager.h"
  27. #include "Wwise/WwiseResourceLoader.h"
  28. #include "Wwise/API/WwiseSoundEngineAPI.h"
  29. #include "Wwise/Stats/Global.h"
  30. #include "Wwise/Stats/AkAudio.h"
  31. #include <inttypes.h>
  32. #if WITH_EDITORONLY_DATA
  33. #include "Wwise/WwiseProjectDatabase.h"
  34. #include "Wwise/WwiseResourceCooker.h"
  35. #endif
  36. int32 UAkAudioEvent::PostOnActor(const AActor* Actor, const FOnAkPostEventCallback& Delegate, const int32 CallbackMask,
  37. const bool bStopWhenAttachedObjectDestroyed)
  38. {
  39. return PostOnActor(Actor, &Delegate, nullptr, nullptr,
  40. AkCallbackTypeHelpers::GetCallbackMaskFromBlueprintMask(CallbackMask), nullptr, bStopWhenAttachedObjectDestroyed);
  41. }
  42. int32 UAkAudioEvent::PostOnComponent(UAkComponent* Component, const FOnAkPostEventCallback& Delegate,
  43. const int32 CallbackMask, const bool bStopWhenAttachedObjectDestroyed)
  44. {
  45. return PostOnComponent(Component, &Delegate, nullptr, nullptr,
  46. AkCallbackTypeHelpers::GetCallbackMaskFromBlueprintMask(CallbackMask), nullptr, bStopWhenAttachedObjectDestroyed);
  47. }
  48. int32 UAkAudioEvent::PostOnGameObject(UAkGameObject* GameObject, const FOnAkPostEventCallback& Delegate, const int32 CallbackMask)
  49. {
  50. return PostOnGameObject(GameObject, &Delegate, nullptr, nullptr,
  51. AkCallbackTypeHelpers::GetCallbackMaskFromBlueprintMask(CallbackMask), nullptr);
  52. }
  53. int32 UAkAudioEvent::PostOnActorAndWait(const AActor* Actor, const bool bStopWhenAttachedObjectDestroyed,
  54. const FLatentActionInfo LatentActionInfo)
  55. {
  56. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostOnActorAndWait"));
  57. if (UNLIKELY(!IsValid(Actor) || Actor->IsActorBeingDestroyed()))
  58. {
  59. UE_LOG(LogAkAudio, Error, TEXT("Failed to post latent AkAudioEvent '%s' with an actor that's not valid. The actor needs to be valid in order to wait for it."), *GetName());
  60. return AK_INVALID_PLAYING_ID;
  61. }
  62. auto* World = Actor->GetWorld();
  63. if (UNLIKELY(!World))
  64. {
  65. UE_LOG(LogAkAudio, Log, TEXT("Failed to post latent AkAudioEvent '%s' with an actor '%s' world that's not valid."), *GetName(), *Actor->GetName());
  66. return AK_INVALID_PLAYING_ID;
  67. }
  68. FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
  69. FWaitEndOfEventAction* LatentAction = new FWaitEndOfEventAction(LatentActionInfo);
  70. LatentActionManager.AddNewAction(LatentActionInfo.CallbackTarget, LatentActionInfo.UUID, LatentAction);
  71. if (UNLIKELY(!World->AllowAudioPlayback()))
  72. {
  73. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with an actor '%s' world '%s' that doesn't allow audio playback."), *GetName(), *Actor->GetName(), *World->GetName());
  74. LatentAction->EventFinished = true;
  75. return AK_INVALID_PLAYING_ID;
  76. }
  77. const auto PlayingID = PostOnActor(Actor, nullptr, nullptr, nullptr, (AkCallbackType)0, LatentAction, bStopWhenAttachedObjectDestroyed);
  78. if (UNLIKELY(PlayingID == AK_INVALID_PLAYING_ID))
  79. {
  80. LatentAction->EventFinished = true;
  81. }
  82. return PlayingID;
  83. }
  84. int32 UAkAudioEvent::PostOnComponentAndWait(UAkComponent* Component, const bool bStopWhenAttachedObjectDestroyed,
  85. const FLatentActionInfo LatentActionInfo)
  86. {
  87. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostOnComponentAndWait"));
  88. if (UNLIKELY(!IsValid(Component)))
  89. {
  90. UE_LOG(LogAkAudio, Error, TEXT("Failed to post latent AkAudioEvent '%s' with a component that's not valid. The component needs to be valid in order to wait for it."), *GetName());
  91. return AK_INVALID_PLAYING_ID;
  92. }
  93. auto* World = Component->GetWorld();
  94. if (UNLIKELY(!World))
  95. {
  96. UE_LOG(LogAkAudio, Log, TEXT("Failed to post latent AkAudioEvent '%s' with a component '%s' world that's not valid."), *GetName(), *Component->GetName());
  97. return AK_INVALID_PLAYING_ID;
  98. }
  99. FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
  100. FWaitEndOfEventAction* LatentAction = new FWaitEndOfEventAction(LatentActionInfo);
  101. LatentActionManager.AddNewAction(LatentActionInfo.CallbackTarget, LatentActionInfo.UUID, LatentAction);
  102. if (UNLIKELY(!World->AllowAudioPlayback()))
  103. {
  104. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with a component '%s' world '%s' that doesn't allow audio playback."), *GetName(), *Component->GetName(), *World->GetName());
  105. LatentAction->EventFinished = true;
  106. return AK_INVALID_PLAYING_ID;
  107. }
  108. const auto PlayingID = PostOnComponent(Component, nullptr, nullptr, nullptr, (AkCallbackType)0, LatentAction, bStopWhenAttachedObjectDestroyed);
  109. if (UNLIKELY(PlayingID == AK_INVALID_PLAYING_ID))
  110. {
  111. LatentAction->EventFinished = true;
  112. }
  113. return PlayingID;
  114. }
  115. int32 UAkAudioEvent::PostOnGameObjectAndWait(UAkGameObject* GameObject, const FLatentActionInfo LatentActionInfo)
  116. {
  117. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostOnGameObjectAndWait"));
  118. if (UNLIKELY(!IsValid(GameObject)))
  119. {
  120. UE_LOG(LogAkAudio, Error, TEXT("Failed to post latent AkAudioEvent '%s' with a game object that's not valid. The gane object needs to be valid in order to wait for it."), *GetName());
  121. return AK_INVALID_PLAYING_ID;
  122. }
  123. auto* World = GameObject->GetWorld();
  124. if (UNLIKELY(!World))
  125. {
  126. UE_LOG(LogAkAudio, Log, TEXT("Failed to post latent AkAudioEvent '%s' with a game object '%s' world that's not valid."), *GetName(), *GameObject->GetName());
  127. return AK_INVALID_PLAYING_ID;
  128. }
  129. FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
  130. FWaitEndOfEventAction* LatentAction = new FWaitEndOfEventAction(LatentActionInfo);
  131. LatentActionManager.AddNewAction(LatentActionInfo.CallbackTarget, LatentActionInfo.UUID, LatentAction);
  132. if (UNLIKELY(!World->AllowAudioPlayback()))
  133. {
  134. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with a game object '%s' world '%s' that doesn't allow audio playback."), *GetName(), *GameObject->GetName(), *World->GetName());
  135. LatentAction->EventFinished = true;
  136. return AK_INVALID_PLAYING_ID;
  137. }
  138. const auto PlayingID = PostOnGameObject(GameObject, nullptr, nullptr, nullptr, (AkCallbackType)0, LatentAction);
  139. if (UNLIKELY(PlayingID == AK_INVALID_PLAYING_ID))
  140. {
  141. LatentAction->EventFinished = true;
  142. }
  143. return PlayingID;
  144. }
  145. int32 UAkAudioEvent::PostAtLocation(const FVector Location, const FRotator Orientation, const FOnAkPostEventCallback& Callback,
  146. const int32 CallbackMask, const UObject* WorldContextObject)
  147. {
  148. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostAtLocation"));
  149. if (UNLIKELY(!IsValid(WorldContextObject)))
  150. {
  151. UE_LOG(LogAkAudio, Error, TEXT("Failed to post AkAudioEvent '%s' at location with a World Context Object that's not valid."), *GetName());
  152. return AK_INVALID_PLAYING_ID;
  153. }
  154. const auto* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
  155. return PostAtLocation(Location, Orientation, World, &Callback, nullptr, nullptr,
  156. AkCallbackTypeHelpers::GetCallbackMaskFromBlueprintMask(CallbackMask), nullptr);
  157. }
  158. int32 UAkAudioEvent::ExecuteAction(const AkActionOnEventType ActionType, const AActor* Actor, const int32 PlayingID,
  159. const int32 TransitionDuration, const EAkCurveInterpolation FadeCurve)
  160. {
  161. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::ExecuteAction"));
  162. const auto* AudioDevice = FAkAudioDevice::Get();
  163. if (UNLIKELY(!AudioDevice))
  164. {
  165. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to execute an action on AkAudioEvent '%s' without an Audio Device."), *GetName());
  166. return AK_NotInitialized;
  167. }
  168. if (UNLIKELY(!AudioDevice->IsInitialized()))
  169. {
  170. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to execute an action on AkAudioEvent '%s' with the Sound Engine uninitialized."), *GetName());
  171. return AK_NotInitialized;
  172. }
  173. auto* SoundEngine = IWwiseSoundEngineAPI::Get();
  174. if (UNLIKELY(!SoundEngine))
  175. {
  176. UE_LOG(LogAkAudio, Warning, TEXT("Failed to execute an action on AkAudioEvent '%s' without a Sound Engine."), *GetName());
  177. return AK_NotInitialized;
  178. }
  179. if (!Actor)
  180. {
  181. return SoundEngine->ExecuteActionOnEvent(GetShortID(),
  182. static_cast<AK::SoundEngine::AkActionOnEventType>(ActionType),
  183. AK_INVALID_GAME_OBJECT,
  184. TransitionDuration,
  185. static_cast<AkCurveInterpolation>(FadeCurve),
  186. PlayingID
  187. );
  188. }
  189. if (UNLIKELY(Actor->IsActorBeingDestroyed() || !IsValid(Actor)))
  190. {
  191. UE_LOG(LogAkAudio, Error, TEXT("Failed to execute on AkAudioEvent '%s' with an actor that's not valid."), *GetName());
  192. return AK_InvalidParameter;
  193. }
  194. UAkComponent* Component = AudioDevice->GetAkComponent(Actor->GetRootComponent(), FName(), nullptr, EAttachLocation::KeepRelativeOffset);
  195. if (UNLIKELY(!Component))
  196. {
  197. UE_LOG(LogAkAudio, Warning, TEXT("Failed to execute on AkAudioEvent '%s' with an actor that doesn't have an AkComponent on Root."), *GetName());
  198. return AK_InvalidParameter;
  199. }
  200. return SoundEngine->ExecuteActionOnEvent(GetShortID(),
  201. static_cast<AK::SoundEngine::AkActionOnEventType>(ActionType),
  202. Component->GetAkGameObjectID(),
  203. TransitionDuration,
  204. static_cast<AkCurveInterpolation>(FadeCurve),
  205. PlayingID
  206. );
  207. }
  208. AkPlayingID UAkAudioEvent::PostOnActor(const AActor* Actor, const FOnAkPostEventCallback* Delegate,
  209. const AkCallbackFunc Callback, void* Cookie, const AkCallbackType CallbackMask, FWaitEndOfEventAction* LatentAction,
  210. const bool bStopWhenAttachedObjectDestroyed, const EAkAudioContext AudioContext)
  211. {
  212. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostOnActor"));
  213. const auto* AudioDevice = FAkAudioDevice::Get();
  214. if (UNLIKELY(!AudioDevice))
  215. {
  216. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' on actor without an Audio Device."), *GetName());
  217. return AK_INVALID_PLAYING_ID;
  218. }
  219. if (UNLIKELY(!AudioDevice->IsInitialized()))
  220. {
  221. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with the Sound Engine uninitialized."), *GetName());
  222. return AK_INVALID_PLAYING_ID;
  223. }
  224. if (!Actor)
  225. {
  226. return PostAmbient(Delegate, Callback, Cookie, CallbackMask, LatentAction, AudioContext);
  227. }
  228. if (UNLIKELY(Actor->IsActorBeingDestroyed() || !IsValid(Actor)))
  229. {
  230. UE_LOG(LogAkAudio, Error, TEXT("Failed to post AkAudioEvent '%s' with an actor that's not valid."), *GetName());
  231. return AK_INVALID_PLAYING_ID;
  232. }
  233. const auto* World = Actor->GetWorld();
  234. if (UNLIKELY(!World))
  235. {
  236. UE_LOG(LogAkAudio, Log, TEXT("Failed to post AkAudioEvent '%s' with an actor '%s' world that's not valid."), *GetName(), *Actor->GetName());
  237. return AK_INVALID_PLAYING_ID;
  238. }
  239. if (UNLIKELY(!World->AllowAudioPlayback()))
  240. {
  241. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with an actor '%s' world '%s' that doesn't allow audio playback."), *GetName(), *Actor->GetName(), *World->GetName());
  242. return AK_INVALID_PLAYING_ID;
  243. }
  244. UAkComponent* Component = AudioDevice->GetAkComponent(Actor->GetRootComponent(), FName(), nullptr, EAttachLocation::KeepRelativeOffset);
  245. if (UNLIKELY(!Component))
  246. {
  247. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent '%s' with an actor that doesn't have an AkComponent on Root."), *GetName());
  248. return AK_INVALID_PLAYING_ID;
  249. }
  250. return PostOnComponent(Component, Delegate, Callback, Cookie, CallbackMask, LatentAction, bStopWhenAttachedObjectDestroyed, AudioContext);
  251. }
  252. AkPlayingID UAkAudioEvent::PostOnComponent(UAkComponent* Component, const FOnAkPostEventCallback* Delegate,
  253. const AkCallbackFunc Callback, void* Cookie, const AkCallbackType CallbackMask, FWaitEndOfEventAction* LatentAction,
  254. const bool bStopWhenAttachedObjectDestroyed, const EAkAudioContext AudioContext)
  255. {
  256. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostOnComponent"));
  257. if (UNLIKELY(!Component))
  258. {
  259. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with null AkComponent."), *GetName());
  260. return AK_INVALID_PLAYING_ID;
  261. }
  262. if (UNLIKELY(!IsValid(Component)))
  263. {
  264. UE_LOG(LogAkAudio, Error, TEXT("Failed to post AkAudioEvent '%s' with an AkComponent that's not valid."), *GetName());
  265. return AK_INVALID_PLAYING_ID;
  266. }
  267. if (UNLIKELY(Component->StopWhenOwnerDestroyed != bStopWhenAttachedObjectDestroyed))
  268. {
  269. UE_LOG(LogWwiseHints, VeryVerbose, TEXT("Updating AkComponent(%s) StopWhenOwnerDestroyed (%s->%s) because of AkAudioEvent '%s'. You should not modify the StopWhenOwnerDestroyed through a PostEvent unless you know what you are doing."),
  270. *Component->GetName(),
  271. Component->StopWhenOwnerDestroyed ? TEXT("true") : TEXT("false"),
  272. bStopWhenAttachedObjectDestroyed ? TEXT("true") : TEXT("false"),
  273. *GetName());
  274. Component->StopWhenOwnerDestroyed = bStopWhenAttachedObjectDestroyed;
  275. }
  276. return PostOnGameObject(Component, Delegate, Callback, Cookie, CallbackMask, LatentAction, AudioContext);
  277. }
  278. AkPlayingID UAkAudioEvent::PostAtLocation(const FVector& Location, const FRotator& Orientation, const UWorld* World,
  279. const FOnAkPostEventCallback* Delegate, const AkCallbackFunc Callback, void* Cookie, const AkCallbackType CallbackMask,
  280. FWaitEndOfEventAction* LatentAction, const EAkAudioContext AudioContext)
  281. {
  282. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostAtLocation"));
  283. auto* AudioDevice = FAkAudioDevice::Get();
  284. if (UNLIKELY(!AudioDevice))
  285. {
  286. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' at a location without an Audio Device."), *GetName());
  287. return AK_INVALID_PLAYING_ID;
  288. }
  289. if (UNLIKELY(!AudioDevice->IsInitialized()))
  290. {
  291. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' at a location with the Sound Engine uninitialized."), *GetName());
  292. return AK_INVALID_PLAYING_ID;
  293. }
  294. auto* SoundEngine = IWwiseSoundEngineAPI::Get();
  295. if (UNLIKELY(!SoundEngine))
  296. {
  297. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent '%s' at a location without a Sound Engine."), *GetName());
  298. return AK_INVALID_PLAYING_ID;
  299. }
  300. if (UNLIKELY(!World))
  301. {
  302. UE_LOG(LogAkAudio, Log, TEXT("Failed to post AkAudioEvent '%s' at a location without a world world."), *GetName());
  303. return AK_INVALID_PLAYING_ID;
  304. }
  305. if (UNLIKELY(!World->AllowAudioPlayback()))
  306. {
  307. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with a world '%s' that doesn't allow audio playback."), *GetName(), *World->GetName());
  308. return AK_INVALID_PLAYING_ID;
  309. }
  310. const AkGameObjectID ObjectID = (AkGameObjectID)this;
  311. AKRESULT Result = AudioDevice->RegisterGameObject(ObjectID, GetName());
  312. if (UNLIKELY(Result != AK_Success))
  313. {
  314. return AK_INVALID_PLAYING_ID;
  315. }
  316. TArray<AkAuxSendValue> AkReverbVolumes;
  317. AudioDevice->GetAuxSendValuesAtLocation(Location, AkReverbVolumes, World);
  318. Result = SoundEngine->SetGameObjectAuxSendValues(ObjectID, AkReverbVolumes.GetData(), AkReverbVolumes.Num());
  319. UE_CLOG(UNLIKELY(Result != AK_Success), LogAkAudio, Log, TEXT("Could not Set AuxSend Values while PostOnLocation for AkAudioEvent '%s' (ObjId: %" PRIu64 "): (%" PRIu32 ") %s."), *GetName(), ObjectID, Result, WwiseUnrealHelper::GetResultString(Result));
  320. AkRoomID RoomID = 0;
  321. auto& RoomIndex = AudioDevice->GetRoomIndex();
  322. TArray<UAkRoomComponent*> AkRooms = RoomIndex.Query<UAkRoomComponent>(Location, World);
  323. if (LIKELY(AkRooms.Num() > 0))
  324. {
  325. UE_CLOG(AkRooms.Num() > 1, LogAkAudio, Verbose, TEXT("There are %d rooms while PostOnLocation for AkAudioEvent '%s' (ObjId: %" PRIu64 "). Picking the first one."), (int)AkRooms.Num(), *GetName(), ObjectID);
  326. RoomID = AkRooms[0]->GetRoomID();
  327. AudioDevice->SetInSpatialAudioRoom(ObjectID, RoomID);
  328. }
  329. else
  330. {
  331. UE_LOG(LogAkAudio, Verbose, TEXT("No Spatial Audio Room while PostOnLocation AkAudioEvent '%s' (ObjId: %" PRIu64 ")"), *GetName(), ObjectID);
  332. }
  333. AkSoundPosition SoundPosition;
  334. FQuat OrientationQuat(Orientation);
  335. AudioDevice->FVectorsToAKWorldTransform(Location, OrientationQuat.GetForwardVector(), OrientationQuat.GetUpVector(), SoundPosition);
  336. Result = SoundEngine->SetPosition(ObjectID, SoundPosition);
  337. UE_CLOG(UNLIKELY(Result != AK_Success), LogAkAudio, Log, TEXT("Could not Set Position for AkAudioEvent '%s' (ObjId: %" PRIu64 "): (%" PRIu32 ") %s."), *GetName(), ObjectID, Result, WwiseUnrealHelper::GetResultString(Result));
  338. const auto PlayingID = PostOnGameObjectID(ObjectID, Delegate, Callback, Cookie, CallbackMask, LatentAction, AudioContext);
  339. Result = SoundEngine->UnregisterGameObj( ObjectID );
  340. UE_CLOG(UNLIKELY(Result != AK_Success), LogAkAudio, Log, TEXT("Could not Unregister GameObject after PostOnLocation for AkAudioEvent '%s' (ObjId: %" PRIu64 "): (%" PRIu32 ") %s."), *GetName(), ObjectID, Result, WwiseUnrealHelper::GetResultString(Result));
  341. return PlayingID;
  342. }
  343. AkPlayingID UAkAudioEvent::PostAmbient(const FOnAkPostEventCallback* Delegate, AkCallbackFunc Callback, void* Cookie,
  344. const AkCallbackType CallbackMask, FWaitEndOfEventAction* LatentAction, const EAkAudioContext AudioContext)
  345. {
  346. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostAmbient"));
  347. return PostOnGameObjectID(DUMMY_GAMEOBJ, Delegate, Callback, Cookie, CallbackMask, LatentAction, AudioContext);
  348. }
  349. AkPlayingID UAkAudioEvent::PostOnGameObject(UAkGameObject* GameObject, const FOnAkPostEventCallback* Delegate,
  350. const AkCallbackFunc Callback, void* Cookie, const AkCallbackType CallbackMask, FWaitEndOfEventAction* LatentAction,
  351. const EAkAudioContext AudioContext)
  352. {
  353. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostOnGameObject"));
  354. if (UNLIKELY(!GameObject))
  355. {
  356. UE_LOG(LogAkAudio, VeryVerbose, TEXT("Failed to post AkAudioEvent without a GameObject."))
  357. return AK_INVALID_PLAYING_ID;
  358. }
  359. UE_CLOG(LIKELY(GameObject->AkAudioEvent) && UNLIKELY(GameObject->AkAudioEvent != this),
  360. LogWwiseHints, VeryVerbose, TEXT("Posting AkAudioEvent(%s) that is different than the one associated (%s) in the GameObject(%s). You should not have an associated AkAudioEvent if you want to play different Events on the same GameObject."),
  361. *GetName(),
  362. *GameObject->AkAudioEvent->GetName(),
  363. *GameObject->GetName());
  364. GameObject->UpdateObstructionAndOcclusion();
  365. const auto GameObjectID = GameObject->GetAkGameObjectID();
  366. const AkPlayingID PlayingID = PostOnGameObjectID(GameObjectID, Delegate, Callback, Cookie, CallbackMask, LatentAction, AudioContext);
  367. if (PlayingID != AK_INVALID_PLAYING_ID)
  368. {
  369. GameObject->EventPosted();
  370. }
  371. return PlayingID;
  372. }
  373. AkPlayingID UAkAudioEvent::PostOnGameObjectID(const AkGameObjectID GameObjectID, const FOnAkPostEventCallback* Delegate,
  374. const AkCallbackFunc Callback, void* Cookie, const AkCallbackType CallbackMask, FWaitEndOfEventAction* LatentAction,
  375. const EAkAudioContext AudioContext)
  376. {
  377. SCOPED_AKAUDIO_EVENT(TEXT("UAkAudioEvent::PostOnGameObjectID"));
  378. if (Delegate)
  379. {
  380. UE_CLOG(UNLIKELY(Callback), LogAkAudio, Error, TEXT("Both Delegate and Callback used for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 "). Ignoring AkCallback."), *GetName(), GameObjectID);
  381. UE_CLOG(UNLIKELY(LatentAction), LogAkAudio, Error, TEXT("Both Delegate and LatentAction used for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 "). Ignoring LatentAction."), *GetName(), GameObjectID);
  382. UE_CLOG(UNLIKELY(Cookie), LogAkAudio, Error, TEXT("Blueprint Delegate ignores Cookie for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 ")."), *GetName(), GameObjectID);
  383. const auto Result = PostEvent(GameObjectID,
  384. [this, Delegate, CallbackMask](AkGameObjectID GameObjectID)
  385. {
  386. auto* AudioDevice = FAkAudioDevice::Get();
  387. auto* CallbackManager = AudioDevice->GetCallbackManager();
  388. return CallbackManager->CreateCallbackPackage(*Delegate, CallbackMask, GameObjectID, true);
  389. },
  390. AudioContext);
  391. return Result;
  392. }
  393. else if (LatentAction)
  394. {
  395. UE_CLOG(UNLIKELY(Callback), LogAkAudio, Error, TEXT("Both LatentAction and Callback used for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 "). Ignoring AkCallback."), *GetName(), GameObjectID);
  396. UE_CLOG(UNLIKELY(CallbackMask), LogAkAudio, Error, TEXT("LatentAction ignores CallbackMask for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 ")."), *GetName(), GameObjectID);
  397. UE_CLOG(UNLIKELY(Cookie), LogAkAudio, Error, TEXT("LatentAction ignores Cookie for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 ")."), *GetName(), GameObjectID);
  398. const auto Result = PostEvent(GameObjectID,
  399. [this, LatentAction](AkGameObjectID GameObjectID)
  400. {
  401. auto* AudioDevice = FAkAudioDevice::Get();
  402. auto* CallbackManager = AudioDevice->GetCallbackManager();
  403. return CallbackManager->CreateCallbackPackage(LatentAction, GameObjectID, true);
  404. },
  405. AudioContext);
  406. return Result;
  407. }
  408. else
  409. {
  410. UE_CLOG(UNLIKELY(!Callback && CallbackMask), LogAkAudio, Error, TEXT("Callback is not set, but there's a CallbackMask for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 ")."), *GetName(), GameObjectID);
  411. UE_CLOG(UNLIKELY(!Callback && Cookie), LogAkAudio, Error, TEXT("Callback is not set, but there's a Cookie for AkAudioEvent('%s').Post(GameObjectID=%" PRIu64 ")."), *GetName(), GameObjectID);
  412. const auto Result = PostEvent(GameObjectID,
  413. [this, Callback, Cookie, CallbackMask](AkGameObjectID GameObjectID)
  414. {
  415. auto* AudioDevice = FAkAudioDevice::Get();
  416. auto* CallbackManager = AudioDevice->GetCallbackManager();
  417. return CallbackManager->CreateCallbackPackage(Callback, Cookie, CallbackMask, GameObjectID, true);
  418. },
  419. AudioContext);
  420. return Result;
  421. }
  422. }
  423. template <typename FCreateCallbackPackage>
  424. AkPlayingID UAkAudioEvent::PostEvent(const AkGameObjectID GameObjectID, FCreateCallbackPackage&& CreateCallbackPackage,
  425. const EAkAudioContext AudioContext)
  426. {
  427. SCOPED_AKAUDIO_EVENT_2(TEXT("UAkAudioEvent::PostEvent"));
  428. auto* AudioDevice = FAkAudioDevice::Get();
  429. if (UNLIKELY(!AudioDevice))
  430. {
  431. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' without an Audio Device."), *GetName());
  432. return AK_INVALID_PLAYING_ID;
  433. }
  434. if (UNLIKELY(!AudioDevice->IsInitialized()))
  435. {
  436. UE_LOG(LogAkAudio, Verbose, TEXT("Failed to post AkAudioEvent '%s' with the Sound Engine uninitialized."), *GetName());
  437. return AK_INVALID_PLAYING_ID;
  438. }
  439. if (!IsLoaded())
  440. {
  441. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent: Data for '%s' wasn't found. Make sure the GeneratedSoundBanks folder (%s) exists and is properly set in the project settings."), *GetName(), *WwiseUnrealHelper::GetSoundBankDirectory());
  442. return AK_INVALID_PLAYING_ID;
  443. }
  444. if (!IsDataFullyLoaded())
  445. {
  446. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent: Not all localization data for '%s' are loaded. Consider using PostEventAsync()."), *GetName());
  447. return AK_INVALID_PLAYING_ID;
  448. }
  449. auto* CallbackManager = AudioDevice->GetCallbackManager();
  450. if (UNLIKELY(!CallbackManager))
  451. {
  452. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent '%s' without a Callback Manager."), *GetName());
  453. return AK_INVALID_PLAYING_ID;
  454. }
  455. auto* SoundEngine = IWwiseSoundEngineAPI::Get();
  456. if (UNLIKELY(!SoundEngine))
  457. {
  458. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent '%s' without a Sound Engine."), *GetName());
  459. return AK_INVALID_PLAYING_ID;
  460. }
  461. auto* ExternalSourceManager = IWwiseExternalSourceManager::Get();
  462. if (UNLIKELY(!ExternalSourceManager))
  463. {
  464. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent '%s' without the External Source Manager."), *GetName());
  465. return AK_INVALID_PLAYING_ID;
  466. }
  467. auto CallbackPackage = CreateCallbackPackage(GameObjectID);
  468. if (UNLIKELY(!CallbackPackage))
  469. {
  470. UE_LOG(LogAkAudio, Warning, TEXT("Failed to post AkAudioEvent '%s': Could not create CallbackPackage."), *GetName());
  471. return AK_INVALID_PLAYING_ID;
  472. }
  473. TArray<AkExternalSourceInfo> ExternalSources;
  474. const TArray<uint32> ExternalSourceMedia = ExternalSourceManager->PrepareExternalSourceInfos(ExternalSources, GetAllExternalSources());
  475. const auto PlayingID = SoundEngine->PostEvent(
  476. GetShortID()
  477. , GameObjectID
  478. , CallbackPackage->uUserFlags | AK_EndOfEvent
  479. , &FAkComponentCallbackManager::AkComponentCallback
  480. , CallbackPackage
  481. , ExternalSources.Num()
  482. , ExternalSources.GetData()
  483. );
  484. ExternalSourceManager->BindPlayingIdToExternalSources(PlayingID, ExternalSourceMedia);
  485. if (UNLIKELY(PlayingID == AK_INVALID_PLAYING_ID))
  486. {
  487. UE_LOG(LogAkAudio, Log, TEXT("Failed to post AkAudioEvent '%s'."), *GetName());
  488. CallbackManager->RemoveCallbackPackage(CallbackPackage, GameObjectID);
  489. return AK_INVALID_PLAYING_ID;
  490. }
  491. AudioDevice->AddPlayingID(GetShortID(), PlayingID, AudioContext);
  492. UE_LOG(LogAkAudio, VeryVerbose, TEXT("Posted AkAudioEvent '%s' as PlayingId %" PRIu32 "."), *GetName(), PlayingID);
  493. return PlayingID;
  494. }
  495. void UAkAudioEvent::Serialize(FArchive& Ar)
  496. {
  497. Super::Serialize(Ar);
  498. if (HasAnyFlags(RF_ClassDefaultObject))
  499. {
  500. return;
  501. }
  502. #if !UE_SERVER
  503. #if WITH_EDITORONLY_DATA
  504. if (Ar.IsCooking() && Ar.IsSaving() && !Ar.CookingTarget()->IsServerOnly())
  505. {
  506. FWwiseLocalizedEventCookedData CookedDataToArchive;
  507. if (auto* ResourceCooker = FWwiseResourceCooker::GetForArchive(Ar))
  508. {
  509. ResourceCooker->PrepareCookedData(CookedDataToArchive, GetValidatedInfo(EventInfo));
  510. FillMetadata(ResourceCooker->GetProjectDatabase());
  511. }
  512. CookedDataToArchive.Serialize(Ar);
  513. Ar << MaximumDuration;
  514. Ar << MinimumDuration;
  515. Ar << IsInfinite;
  516. Ar << MaxAttenuationRadius;
  517. }
  518. #else
  519. EventCookedData.Serialize(Ar);
  520. Ar << MaximumDuration;
  521. Ar << MinimumDuration;
  522. Ar << IsInfinite;
  523. Ar << MaxAttenuationRadius;
  524. #endif
  525. #endif
  526. }
  527. #if WITH_EDITORONLY_DATA
  528. void UAkAudioEvent::FillInfo()
  529. {
  530. auto* ResourceCooker = FWwiseResourceCooker::GetDefault();
  531. if (UNLIKELY(!ResourceCooker))
  532. {
  533. UE_LOG(LogAkAudio, Error, TEXT("UAkAudioEvent::FillInfo: ResourceCooker not initialized"));
  534. return;
  535. }
  536. auto ProjectDatabase = ResourceCooker->GetProjectDatabase();
  537. if (UNLIKELY(!ProjectDatabase))
  538. {
  539. UE_LOG(LogAkAudio, Error, TEXT("UAkAudioEvent::FillInfo: ProjectDatabase not initialized"));
  540. return;
  541. }
  542. const TSet<FWwiseRefEvent> EventRef = FWwiseDataStructureScopeLock(*ProjectDatabase).GetEvent(GetValidatedInfo(EventInfo));
  543. if (UNLIKELY(EventRef.Num() == 0))
  544. {
  545. UE_LOG(LogAkAudio, Log, TEXT("UAkAudioEvent::FillInfo (%s): Cannot fill Asset Info - Event is not loaded"), *GetName());
  546. return;
  547. }
  548. const FWwiseMetadataEvent* EventMetadata = EventRef.Array()[0].GetEvent();
  549. if (EventMetadata->Name.ToString().IsEmpty() || !EventMetadata->GUID.IsValid() || EventMetadata->Id == AK_INVALID_UNIQUE_ID)
  550. {
  551. UE_LOG(LogAkAudio, Warning, TEXT("UAkAudioEvent::FillInfo: Valid object not found in Project Database"));
  552. return;
  553. }
  554. EventInfo.WwiseGuid = EventMetadata->GUID;
  555. EventInfo.WwiseShortId = EventMetadata->Id;
  556. EventInfo.WwiseName = EventMetadata->Name;
  557. }
  558. void UAkAudioEvent::FillMetadata(FWwiseProjectDatabase* ProjectDatabase)
  559. {
  560. Super::FillMetadata(ProjectDatabase);
  561. const TSet<FWwiseRefEvent> EventRef = FWwiseDataStructureScopeLock(*ProjectDatabase).GetEvent(GetValidatedInfo(EventInfo));
  562. if (UNLIKELY(EventRef.Num() == 0))
  563. {
  564. UE_LOG(LogAkAudio, Log, TEXT("UAkAudioEvent::FillMetadata (%s): Cannot fill Metadata - Event not found in Project Database"), *GetName());
  565. return;
  566. }
  567. const FWwiseMetadataEvent* EventMetadata = EventRef.Array()[0].GetEvent();
  568. if (EventMetadata->Name.ToString().IsEmpty() || !EventMetadata->GUID.IsValid() || EventMetadata->Id == AK_INVALID_UNIQUE_ID)
  569. {
  570. UE_LOG(LogAkAudio, Warning, TEXT("UAkAudioEvent::FillMetadata: Valid object not found in Project Database"));
  571. return;
  572. }
  573. MaximumDuration = EventMetadata->DurationMax;
  574. MinimumDuration = EventMetadata->DurationMin;
  575. IsInfinite = EventMetadata->DurationType == EWwiseMetadataEventDurationType::Infinite;
  576. MaxAttenuationRadius = EventMetadata->MaxAttenuation;
  577. }
  578. #endif
  579. void UAkAudioEvent::LoadEventData()
  580. {
  581. SCOPED_AKAUDIO_EVENT_2(TEXT("LoadEventData"));
  582. auto* ResourceLoader = FWwiseResourceLoader::Get();
  583. if (UNLIKELY(!ResourceLoader))
  584. {
  585. return;
  586. }
  587. UnloadEventData(false);
  588. #if WITH_EDITORONLY_DATA
  589. if (!IWwiseProjectDatabaseModule::ShouldInitializeProjectDatabase())
  590. {
  591. return;
  592. }
  593. auto* ProjectDatabase = FWwiseProjectDatabase::Get();
  594. if (!ProjectDatabase || !ProjectDatabase->IsProjectDatabaseParsed())
  595. {
  596. UE_LOG(LogAkAudio, VeryVerbose, TEXT("UAkAudioEvent::LoadEventData: Not loading '%s' because project database is not parsed."), *GetName())
  597. return;
  598. }
  599. auto* ResourceCooker = FWwiseResourceCooker::GetDefault();
  600. if (UNLIKELY(!ResourceCooker))
  601. {
  602. return;
  603. }
  604. if (UNLIKELY(!ResourceCooker->PrepareCookedData(EventCookedData, GetValidatedInfo(EventInfo))))
  605. {
  606. return;
  607. }
  608. FillMetadata(ResourceCooker->GetProjectDatabase());
  609. #endif
  610. UE_LOG(LogAkAudio, Verbose, TEXT("%s - LoadEventData"), *GetName());
  611. const auto NewlyLoadedEvent = ResourceLoader->LoadEvent(EventCookedData);
  612. auto PreviouslyLoadedEvent = LoadedEvent.exchange(NewlyLoadedEvent);
  613. if (UNLIKELY(PreviouslyLoadedEvent))
  614. {
  615. ResourceLoader->UnloadEvent(MoveTemp(PreviouslyLoadedEvent));
  616. }
  617. }
  618. #if WITH_EDITOR
  619. void UAkAudioEvent::LoadEventDataForContentBrowserPreview()
  620. {
  621. if(!bAutoLoad)
  622. {
  623. OnBeginPIEDelegateHandle = FEditorDelegates::BeginPIE.AddUObject(this, &UAkAudioEvent::OnBeginPIE);
  624. }
  625. LoadEventData();
  626. }
  627. void UAkAudioEvent::OnBeginPIE(const bool bIsSimulating)
  628. {
  629. FEditorDelegates::BeginPIE.Remove(OnBeginPIEDelegateHandle);
  630. OnBeginPIEDelegateHandle.Reset();
  631. UnloadEventData(false);
  632. }
  633. #endif
  634. void UAkAudioEvent::BeginDestroy()
  635. {
  636. Super::BeginDestroy();
  637. #if WITH_EDITOR
  638. if (OnBeginPIEDelegateHandle.IsValid())
  639. {
  640. FEditorDelegates::BeginPIE.Remove(OnBeginPIEDelegateHandle);
  641. OnBeginPIEDelegateHandle.Reset();
  642. }
  643. #endif
  644. }
  645. void UAkAudioEvent::UnloadEventData(bool bAsync)
  646. {
  647. auto PreviouslyLoadedEvent = LoadedEvent.exchange(nullptr);
  648. if (PreviouslyLoadedEvent)
  649. {
  650. auto* ResourceLoader = FWwiseResourceLoader::Get();
  651. if (UNLIKELY(!ResourceLoader))
  652. {
  653. return;
  654. }
  655. UE_LOG(LogAkAudio, Verbose, TEXT("%s - UnloadEventData"), *GetName());
  656. if (bAsync)
  657. {
  658. FWwiseLoadedEventPromise Promise;
  659. Promise.EmplaceValue(MoveTemp(PreviouslyLoadedEvent));
  660. ResourceUnload = ResourceLoader->UnloadEventAsync(Promise.GetFuture());
  661. }
  662. else
  663. {
  664. ResourceLoader->UnloadEvent(MoveTemp(PreviouslyLoadedEvent));
  665. }
  666. }
  667. }
  668. bool UAkAudioEvent::IsDataFullyLoaded() const
  669. {
  670. auto CurrentLoadedEvent = LoadedEvent.load();
  671. if (!CurrentLoadedEvent)
  672. {
  673. return false;
  674. }
  675. return CurrentLoadedEvent->GetValue().LoadedData.IsLoaded();
  676. }
  677. bool UAkAudioEvent::IsLoaded() const
  678. {
  679. return LoadedEvent.load() != nullptr;
  680. }
  681. #if WITH_EDITORONLY_DATA
  682. bool UAkAudioEvent::ObjectIsInSoundBanks()
  683. {
  684. auto* ResourceCooker = FWwiseResourceCooker::GetDefault();
  685. if (UNLIKELY(!ResourceCooker))
  686. {
  687. UE_LOG(LogAkAudio, Error, TEXT("UAkAudioEvent::GetWwiseRef: ResourceCooker not initialized"));
  688. return false;
  689. }
  690. auto ProjectDatabase = ResourceCooker->GetProjectDatabase();
  691. if (UNLIKELY(!ProjectDatabase))
  692. {
  693. UE_LOG(LogAkAudio, Error, TEXT("UAkAudioEvent::GetWwiseRef: ProjectDatabase not initialized"));
  694. return false;
  695. }
  696. const TSet<FWwiseRefEvent> EventRef = FWwiseDataStructureScopeLock(*ProjectDatabase).GetEvent(GetValidatedInfo(EventInfo));
  697. if (UNLIKELY(EventRef.Num() == 0))
  698. {
  699. UE_LOG(LogAkAudio, Log, TEXT("UAkAudioEvent::GetWwiseRef (%s): Event is not loaded"), *GetName());
  700. return false;
  701. }
  702. return EventRef.Array()[0].IsValid();
  703. }
  704. #endif
  705. TArray<FWwiseExternalSourceCookedData> UAkAudioEvent::GetAllExternalSources() const
  706. {
  707. auto CurrentLoadedEvent = LoadedEvent.load();
  708. if (!CurrentLoadedEvent)
  709. {
  710. return {};
  711. }
  712. const auto& EventData = CurrentLoadedEvent->GetValue();
  713. if (!EventData.LoadedData.IsLoaded())
  714. {
  715. return {};
  716. }
  717. const auto& LoadedLanguage = EventData.LanguageRef;
  718. const FWwiseEventCookedData* CookedData = EventCookedData.EventLanguageMap.Find(LoadedLanguage);
  719. if (UNLIKELY(!CookedData))
  720. {
  721. return {};
  722. }
  723. auto Result = CookedData->ExternalSources;
  724. for (const auto& Leaf : CookedData->SwitchContainerLeaves)
  725. {
  726. Result.Append(Leaf.ExternalSources);
  727. }
  728. return Result;
  729. }
  730. #if WITH_EDITORONLY_DATA
  731. void UAkAudioEvent::CookAdditionalFilesOverride(const TCHAR* PackageFilename, const ITargetPlatform* TargetPlatform,
  732. TFunctionRef<void(const TCHAR* Filename, void* Data, int64 Size)> WriteAdditionalFile)
  733. {
  734. if (HasAnyFlags(RF_ClassDefaultObject))
  735. {
  736. return;
  737. }
  738. FWwiseResourceCooker* ResourceCooker = FWwiseResourceCooker::GetForPlatform(TargetPlatform);
  739. if (!ResourceCooker)
  740. {
  741. return;
  742. }
  743. ResourceCooker->SetSandboxRootPath(PackageFilename);
  744. ResourceCooker->CookEvent(GetValidatedInfo(EventInfo), WriteAdditionalFile);
  745. }
  746. #endif