WwiseEventTracking.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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 "WwiseEventTracking.h"
  16. #include "AkAudioEvent.h"
  17. #include "Wwise/WwiseExternalSourceManager.h"
  18. void FWwiseEventTracker::PostEventCallbackHandler(AkCallbackType in_eType, AkCallbackInfo * in_pCallbackInfo)
  19. {
  20. if (in_pCallbackInfo == nullptr)
  21. return;
  22. auto Tracker = (FWwiseEventTracker*)in_pCallbackInfo->pCookie;
  23. if (Tracker == nullptr)
  24. return;
  25. /* Event end */
  26. if (in_eType == AkCallbackType::AK_EndOfEvent)
  27. {
  28. const auto CBInfo = (AkEventCallbackInfo*)in_pCallbackInfo;
  29. const auto IDToStop = CBInfo->playingID;
  30. Tracker->RemovePlayingID(IDToStop);
  31. Tracker->RemoveScheduledStop(IDToStop);
  32. }/* Received close to the beginning of the event */
  33. else if (in_eType == AkCallbackType::AK_Duration)
  34. {
  35. const auto CBInfo = (AkDurationCallbackInfo*)in_pCallbackInfo;
  36. Tracker->CurrentDurationEstimation = (CBInfo->fEstimatedDuration * Tracker->CurrentDurationProportionRemaining) / 1000.0f;
  37. }
  38. }
  39. void FWwiseEventTracker::RemoveScheduledStop(AkPlayingID InID)
  40. {
  41. FScopeLock autoLock(&ScheduledStopsLock);
  42. for (auto PlayingID : ScheduledStops)
  43. {
  44. if (PlayingID == InID)
  45. {
  46. ScheduledStops.Remove(PlayingID);
  47. break;
  48. }
  49. }
  50. }
  51. void FWwiseEventTracker::RemovePlayingID(AkPlayingID InID)
  52. {
  53. FScopeLock autoLock(&PlayingIDsLock);
  54. for (auto PlayingID : PlayingIDs)
  55. {
  56. if (PlayingID == InID)
  57. {
  58. PlayingIDs.Remove(PlayingID);
  59. break;
  60. }
  61. }
  62. }
  63. void FWwiseEventTracker::TryAddPlayingID(const AkPlayingID & PlayingID)
  64. {
  65. if (PlayingID != AK_INVALID_PLAYING_ID)
  66. {
  67. FScopeLock autoLock(&PlayingIDsLock);
  68. PlayingIDs.Add(PlayingID);
  69. }
  70. }
  71. void FWwiseEventTracker::EmptyPlayingIDs()
  72. {
  73. FScopeLock autoLock(&PlayingIDsLock);
  74. PlayingIDs.Empty();
  75. }
  76. void FWwiseEventTracker::EmptyScheduledStops()
  77. {
  78. FScopeLock autoLock(&ScheduledStopsLock);
  79. ScheduledStops.Empty();
  80. }
  81. bool FWwiseEventTracker::PlayingIDHasScheduledStop(AkPlayingID InID)
  82. {
  83. FScopeLock autoLock(&ScheduledStopsLock);
  84. for (auto PlayingID : ScheduledStops)
  85. {
  86. if (PlayingID == InID)
  87. {
  88. return true;
  89. }
  90. }
  91. return false;
  92. }
  93. void FWwiseEventTracker::AddScheduledStop(AkPlayingID InID)
  94. {
  95. FScopeLock autoLock(&ScheduledStopsLock);
  96. ScheduledStops.Add(InID);
  97. }
  98. namespace WwiseEventTriggering
  99. {
  100. TArray<AkPlayingID, TInlineAllocator<16>> GetPlayingIds(FWwiseEventTracker & EventTracker)
  101. {
  102. FScopeLock autoLock(&EventTracker.PlayingIDsLock);
  103. return TArray<AkPlayingID, TInlineAllocator<16>> { EventTracker.PlayingIDs };
  104. }
  105. void LogDirtyPlaybackWarning()
  106. {
  107. UE_LOG(LogAkAudio, Warning, TEXT("Playback occurred from sequencer section with new changes. You may need to save your diry work units and re-generate your soundbanks."));
  108. }
  109. void StopAllPlayingIDs(FAkAudioDevice * AudioDevice, FWwiseEventTracker & EventTracker)
  110. {
  111. ensure(AudioDevice != nullptr);
  112. if (AudioDevice)
  113. {
  114. for (auto PlayingID : GetPlayingIds(EventTracker))
  115. {
  116. AudioDevice->StopPlayingID(PlayingID);
  117. }
  118. }
  119. }
  120. AkPlayingID PostEventOnDummyObject(FAkAudioDevice * AudioDevice, FWwiseEventTracker & EventTracker, float CurrentTime)
  121. {
  122. ensure(AudioDevice != nullptr);
  123. if (EventTracker.EventName.IsEmpty())
  124. {
  125. UE_LOG(LogAkAudio, Warning, TEXT("Attempted to post an AkEvent from an empty Sequencer section."));
  126. return AK_INVALID_PLAYING_ID;
  127. }
  128. if (AudioDevice)
  129. {
  130. AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
  131. if (EventTracker.Event)
  132. {
  133. PlayingID = EventTracker.Event->PostAmbient(nullptr, &FWwiseEventTracker::PostEventCallbackHandler, &EventTracker,
  134. (AkCallbackType)(AK_EndOfEvent | AK_Duration), nullptr);
  135. }
  136. if (LIKELY(PlayingID != AK_INVALID_PLAYING_ID))
  137. {
  138. EventTracker.TryAddPlayingID(PlayingID);
  139. if (EventTracker.IsDirty)
  140. LogDirtyPlaybackWarning();
  141. return PlayingID;
  142. }
  143. }
  144. return AK_INVALID_PLAYING_ID;
  145. }
  146. AkPlayingID PostEvent(UObject * Object, FAkAudioDevice * AudioDevice, FWwiseEventTracker & EventTracker, float CurrentTime)
  147. {
  148. ensure(AudioDevice != nullptr);
  149. if (EventTracker.EventName.IsEmpty())
  150. {
  151. UE_LOG(LogAkAudio, Warning, TEXT("Attempted to post an AkEvent from an empty Sequencer section."));
  152. return AK_INVALID_PLAYING_ID;
  153. }
  154. if (Object && AudioDevice)
  155. {
  156. auto AkComponent = Cast<UAkComponent>(Object);
  157. if (!IsValid(AkComponent))
  158. {
  159. auto Actor = CastChecked<AActor>(Object);
  160. if (IsValid(Actor))
  161. {
  162. AkComponent = AudioDevice->GetAkComponent(Actor->GetRootComponent(), FName(), NULL, EAttachLocation::KeepRelativeOffset);
  163. }
  164. }
  165. if (IsValid(AkComponent))
  166. {
  167. AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
  168. if (EventTracker.Event)
  169. {
  170. PlayingID = EventTracker.Event->PostOnComponent(AkComponent, nullptr, &FWwiseEventTracker::PostEventCallbackHandler, &EventTracker, (AkCallbackType)(AK_EndOfEvent | AK_Duration), nullptr, AkComponent->StopWhenOwnerDestroyed);
  171. }
  172. EventTracker.TryAddPlayingID(PlayingID);
  173. if (EventTracker.IsDirty)
  174. LogDirtyPlaybackWarning();
  175. return PlayingID;
  176. }
  177. }
  178. return AK_INVALID_PLAYING_ID;
  179. }
  180. void StopEvent(FAkAudioDevice * AudioDevice, AkPlayingID InPlayingID, FWwiseEventTracker * EventTracker)
  181. {
  182. ensure(AudioDevice != nullptr);
  183. if (AudioDevice)
  184. AudioDevice->StopPlayingID(InPlayingID);
  185. }
  186. void TriggerStopEvent(FAkAudioDevice * AudioDevice, FWwiseEventTracker & EventTracker, AkPlayingID PlayingID)
  187. {
  188. AudioDevice->StopPlayingID(PlayingID, (float)EventTracker.ScrubTailLengthMs, AkCurveInterpolation::AkCurveInterpolation_Log1);
  189. EventTracker.AddScheduledStop(PlayingID);
  190. }
  191. void ScheduleStopEventsForCurrentlyPlayingIDs(FAkAudioDevice * AudioDevice, FWwiseEventTracker & EventTracker)
  192. {
  193. ensure(AudioDevice != nullptr);
  194. if (AudioDevice)
  195. {
  196. for (auto PlayingID : GetPlayingIds(EventTracker))
  197. {
  198. if (!EventTracker.PlayingIDHasScheduledStop(PlayingID))
  199. {
  200. TriggerStopEvent(AudioDevice, EventTracker, PlayingID);
  201. }
  202. }
  203. }
  204. }
  205. void TriggerScrubSnippetOnDummyObject(FAkAudioDevice * AudioDevice, FWwiseEventTracker & EventTracker)
  206. {
  207. ensure(AudioDevice != nullptr);
  208. if (EventTracker.EventName.IsEmpty())
  209. {
  210. return;
  211. }
  212. if (AudioDevice)
  213. {
  214. AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
  215. if (EventTracker.Event)
  216. {
  217. PlayingID = EventTracker.Event->PostAmbient(nullptr, &FWwiseEventTracker::PostEventCallbackHandler, &EventTracker,
  218. (AkCallbackType)(AK_EndOfEvent | AK_Duration), nullptr);
  219. }
  220. if (LIKELY(PlayingID != AK_INVALID_PLAYING_ID))
  221. {
  222. EventTracker.TryAddPlayingID(PlayingID);
  223. if (EventTracker.IsDirty)
  224. LogDirtyPlaybackWarning();
  225. TriggerStopEvent(AudioDevice, EventTracker, PlayingID);
  226. }
  227. }
  228. }
  229. void TriggerScrubSnippet(UObject * Object, FAkAudioDevice * AudioDevice, FWwiseEventTracker & EventTracker)
  230. {
  231. ensure(AudioDevice != nullptr);
  232. if (EventTracker.EventName.IsEmpty())
  233. {
  234. return;
  235. }
  236. if (Object && AudioDevice)
  237. {
  238. auto AkComponent = Cast<UAkComponent>(Object);
  239. if (!IsValid(AkComponent))
  240. {
  241. auto Actor = CastChecked<AActor>(Object);
  242. if (IsValid(Actor))
  243. {
  244. AkComponent = AudioDevice->GetAkComponent(Actor->GetRootComponent(), FName(), NULL, EAttachLocation::KeepRelativeOffset);
  245. }
  246. }
  247. if (IsValid(AkComponent))
  248. {
  249. AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
  250. if (EventTracker.Event)
  251. {
  252. PlayingID = EventTracker.Event->PostOnComponent(AkComponent, nullptr, &FWwiseEventTracker::PostEventCallbackHandler, &EventTracker,
  253. (AkCallbackType)(AK_EndOfEvent | AK_Duration), nullptr, AkComponent->StopWhenOwnerDestroyed);
  254. }
  255. if (LIKELY(PlayingID != AK_INVALID_PLAYING_ID))
  256. {
  257. EventTracker.TryAddPlayingID(PlayingID);
  258. if (EventTracker.IsDirty)
  259. LogDirtyPlaybackWarning();
  260. TriggerStopEvent(AudioDevice, EventTracker, PlayingID);
  261. }
  262. }
  263. }
  264. }
  265. void SeekOnEvent(UObject * Object, FAkAudioDevice * AudioDevice, AkReal32 in_fPercent, FWwiseEventTracker & EventTracker, AkPlayingID InPlayingID)
  266. {
  267. ensure(AudioDevice != nullptr);
  268. if (EventTracker.EventName.IsEmpty())
  269. {
  270. return;
  271. }
  272. if (Object && AudioDevice)
  273. {
  274. auto AkComponent = Cast<UAkComponent>(Object);
  275. if (!IsValid(AkComponent))
  276. {
  277. auto Actor = CastChecked<AActor>(Object);
  278. if (IsValid(Actor))
  279. {
  280. AkComponent = AudioDevice->GetAkComponent(Actor->GetRootComponent(), FName(), NULL, EAttachLocation::KeepRelativeOffset);
  281. }
  282. }
  283. if (IsValid(AkComponent))
  284. {
  285. const AkUInt32 ShortID = AudioDevice->GetShortID(EventTracker.Event, EventTracker.EventName);
  286. AudioDevice->SeekOnEvent(ShortID, AkComponent, in_fPercent, false, InPlayingID);
  287. }
  288. }
  289. }
  290. void SeekOnEvent(UObject * Object, FAkAudioDevice * AudioDevice, AkReal32 in_fPercent, FWwiseEventTracker & EventTracker)
  291. {
  292. for (auto PlayingID : GetPlayingIds(EventTracker))
  293. {
  294. WwiseEventTriggering::SeekOnEvent(Object, AudioDevice, in_fPercent, EventTracker, PlayingID);
  295. }
  296. }
  297. void SeekOnEventWithDummyObject(FAkAudioDevice * AudioDevice, AkReal32 ProportionalTime, FWwiseEventTracker & EventTracker, AkPlayingID InPlayingID)
  298. {
  299. ensure(AudioDevice != nullptr);
  300. if (EventTracker.EventName.IsEmpty())
  301. {
  302. return;
  303. }
  304. if (AudioDevice)
  305. {
  306. if (ProportionalTime < 1.0f && ProportionalTime >= 0.0f)
  307. {
  308. AActor* DummyActor = nullptr;
  309. const AkUInt32 ShortID = AudioDevice->GetShortID(EventTracker.Event, EventTracker.EventName);
  310. AudioDevice->SeekOnEvent(ShortID, DummyActor, ProportionalTime, false, InPlayingID);
  311. // Update the duration proportion remaining property of the event tracker, rather than updating the current duration directly here.
  312. // This way, we ensure that the current duration is updated first by any PostEvent callback,
  313. // before it is then multiplied by the remaining proportion.
  314. EventTracker.CurrentDurationProportionRemaining = 1.0f - ProportionalTime;
  315. }
  316. }
  317. }
  318. void SeekOnEventWithDummyObject(FAkAudioDevice * AudioDevice, AkReal32 ProportionalTime, FWwiseEventTracker & EventTracker)
  319. {
  320. for (auto PlayingID : GetPlayingIds(EventTracker))
  321. {
  322. WwiseEventTriggering::SeekOnEventWithDummyObject(AudioDevice, ProportionalTime, EventTracker, PlayingID);
  323. }
  324. }
  325. }