MovieSceneAkAudioEventSection.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  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 "MovieSceneAkAudioEventSection.h"
  16. #include "AkAudioDevice.h"
  17. #include "AkAudioEvent.h"
  18. #include "AkWaapiClient.h"
  19. #include "MovieSceneAkAudioEventTrack.h"
  20. #include "Misc/Base64.h"
  21. #include "MovieScene.h"
  22. #include "MovieSceneCommonHelpers.h"
  23. #include "MovieSceneSection.h"
  24. #include "MovieSceneAkAudioEventTemplate.h"
  25. #include "Async/Async.h"
  26. /** Default values and helper used by UMovieSceneAkAudioEventSection when querying event information. */
  27. namespace AkAudioEventSectionHelper
  28. {
  29. // durations are in seconds
  30. const float DefaultDurationForEventSpecifiedByName = 0.5f;
  31. const float MinimumDuration = 0.05f;
  32. const float MaximumDuration = 720000.f;
  33. int32 GetMaxDurationForFrameRate(FFrameRate FrameRate)
  34. {
  35. float Duration = (FrameRate.Denominator * MaximumDuration) / FrameRate.Numerator;
  36. return static_cast<int32>(FMath::FloorToFloat(Duration));
  37. }
  38. FFloatRange GetDuration(UAkAudioEvent* Event, FFrameRate FrameRate)
  39. {
  40. if (Event == nullptr)
  41. {
  42. return FFloatRange(DefaultDurationForEventSpecifiedByName);
  43. }
  44. if (Event->IsInfinite)
  45. {
  46. return FFloatRange(FMath::Min((float)GetMaxDurationForFrameRate(FrameRate), MaximumDuration));
  47. }
  48. return FFloatRange(Event->MinimumDuration, TRangeBound<float>::Inclusive(FMath::Clamp(Event->MaximumDuration, MinimumDuration, MaximumDuration)));
  49. }
  50. }
  51. //===================================================================================================
  52. // Initialization / Destruction
  53. //===================================================================================================
  54. void UMovieSceneAkAudioEventSection::Initialize()
  55. {
  56. #if WITH_EDITOR
  57. if ((Event != nullptr || EventName != "") && InitState != AkEventSectionState::EUnrecognized)
  58. {
  59. UpdateAudioSourceInfo();
  60. SubscribeToEventChildAddedRemoved();
  61. FAkWaapiClient* pWaapiClient = FAkWaapiClient::Get();
  62. if (pWaapiClient != nullptr)
  63. {
  64. if (InitState == AkEventSectionState::EUninitialized && iChildAddedInitializeSubscriptionID == 0 && pWaapiClient->IsConnected())
  65. {
  66. UE_LOG(LogAkAudio, Warning,
  67. TEXT("Failed to initialize Section for Event: %s"),
  68. Event == nullptr ? *EventName : *(Event->GetName()));
  69. //===========================================================
  70. // Callback
  71. //===========================================================
  72. auto updateAudioSourceInfoCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> in_UEJsonObject)
  73. {
  74. iCallbackCounter.Increment();
  75. AsyncTask(ENamedThreads::GameThread, [this, in_UEJsonObject]()
  76. {
  77. FScopeLock Lock(&WAAPISection);
  78. Initialize();
  79. if (InitState == AkEventSectionState::EInitialized)
  80. {
  81. RequiresUpdate = true;
  82. }
  83. iCallbackCounter.Decrement();
  84. });
  85. });
  86. #if AK_SUPPORT_WAAPI
  87. const auto addedUri = ak::wwise::core::object::childAdded;
  88. TSharedRef<FJsonObject> emptyOptions = MakeShareable(new FJsonObject());
  89. TSharedPtr<FJsonObject> subscriptionResult = MakeShareable(new FJsonObject());
  90. //===========================================================
  91. // Child Added
  92. //===========================================================
  93. pWaapiClient->Subscribe(addedUri, emptyOptions, updateAudioSourceInfoCallback, iChildAddedInitializeSubscriptionID, subscriptionResult);
  94. #endif
  95. }
  96. }
  97. }
  98. #endif //WITH_EDITOR
  99. }
  100. void UMovieSceneAkAudioEventSection::BeginDestroy()
  101. {
  102. #if WITH_EDITOR
  103. /* Wait for WAAPI callbacks to complete */
  104. while (iCallbackCounter.GetValue() > 0) {}
  105. UnsubscribeAllWAAPICallbacks();
  106. #endif
  107. if (auto* TrackerPtr = EventTracker.Get())
  108. {
  109. if (TrackerPtr->IsPlaying())
  110. {
  111. FAkAudioDevice::Get()->CancelEventCallbackCookie(EventTracker.Get());
  112. }
  113. }
  114. Super::BeginDestroy();
  115. }
  116. #if WITH_EDITOR
  117. void UMovieSceneAkAudioEventSection::PostEditChangeProperty(struct FPropertyChangedEvent& e)
  118. {
  119. FName PropertyName = (e.Property != nullptr) ? e.Property->GetFName() : NAME_None;
  120. if (PropertyName == GET_MEMBER_NAME_CHECKED(UMovieSceneAkAudioEventSection, Event)
  121. || PropertyName == GET_MEMBER_NAME_CHECKED(UMovieSceneAkAudioEventSection, EventName))
  122. {
  123. UpdateAkEventInfo();
  124. }
  125. Super::PostEditChangeProperty(e);
  126. }
  127. //===================================================================================================
  128. // WAAPI Subscriptions
  129. //===================================================================================================
  130. /** Registers a call to update the audio source info when a child is added or removed from in_sParentID */
  131. void UMovieSceneAkAudioEventSection::SubscribeToChildAddedRemoved(FString in_sParentID, uint64& in_iAddedSubID, uint64& in_iRemovedSubID)
  132. {
  133. FAkWaapiClient* pWaapiClient = FAkWaapiClient::Get();
  134. if (pWaapiClient != nullptr)
  135. {
  136. UnsubscribeWAAPICallback(in_iAddedSubID);
  137. UnsubscribeWAAPICallback(in_iRemovedSubID);
  138. //===========================================================
  139. // Callback
  140. //===========================================================
  141. auto updateAudioSourceInfoCallback = WampEventCallback::CreateLambda([this, in_sParentID](uint64_t id, TSharedPtr<FJsonObject> in_UEJsonObject)
  142. {
  143. iCallbackCounter.Increment();
  144. AsyncTask(ENamedThreads::GameThread, [this, in_sParentID, in_UEJsonObject]()
  145. {
  146. auto parentIDString = in_UEJsonObject->GetObjectField(FAkWaapiClient::WAAPIStrings::PARENT)->GetStringField(FAkWaapiClient::WAAPIStrings::ID);
  147. if (parentIDString.Equals(in_sParentID, ESearchCase::IgnoreCase))
  148. {
  149. FScopeLock Lock(&WAAPISection);
  150. UpdateAudioSourceInfo();
  151. RequiresUpdate = true;
  152. }
  153. iCallbackCounter.Decrement();
  154. });
  155. });
  156. #if AK_SUPPORT_WAAPI
  157. const auto addedUri = ak::wwise::core::object::childAdded;
  158. const auto removedUri = ak::wwise::core::object::childRemoved;
  159. TSharedRef<FJsonObject> emptyOptions = MakeShareable(new FJsonObject());
  160. TSharedPtr<FJsonObject> subscriptionResult = MakeShareable(new FJsonObject());
  161. //===========================================================
  162. // Child Added
  163. //===========================================================
  164. pWaapiClient->Subscribe(addedUri, emptyOptions, updateAudioSourceInfoCallback, in_iAddedSubID, subscriptionResult);
  165. //===========================================================
  166. // Child Removed
  167. //===========================================================
  168. subscriptionResult = MakeShareable(new FJsonObject());
  169. pWaapiClient->Subscribe(removedUri, emptyOptions, updateAudioSourceInfoCallback, in_iRemovedSubID, subscriptionResult);
  170. #endif // AK_SUPPORT_WAAPI
  171. }
  172. }
  173. void UMovieSceneAkAudioEventSection::SubscribeToTrimChanges()
  174. {
  175. UnsubscribeWAAPICallback(iTrimBeginSubscriptionID);
  176. UnsubscribeWAAPICallback(iTrimEndSubscriptionID);
  177. //===========================================================
  178. // Subscribe to TrimBegin and TrimEnd changed events for
  179. // new event details.
  180. //===========================================================
  181. auto pWaapiClient = FAkWaapiClient::Get();
  182. if (pWaapiClient != nullptr && pWaapiClient->IsConnected() && AudioSourceInfoIsValid())
  183. {
  184. auto updateTrimCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> in_UEJsonObject)
  185. {
  186. iCallbackCounter.Increment();
  187. AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this, id, in_UEJsonObject]()
  188. {
  189. FScopeLock Lock(&WAAPISection);
  190. UpdateTrimData();
  191. CheckForWorkunitChanges(true);
  192. RequiresUpdate = true;
  193. iCallbackCounter.Decrement();
  194. });
  195. });
  196. //===========================================================
  197. // TrimEnd
  198. //===========================================================
  199. TSharedPtr<FJsonObject> trimEndSubscriptionResult = MakeShareable(new FJsonObject());
  200. TSharedRef<FJsonObject> trimEndChangedOptions = MakeShareable(new FJsonObject());
  201. {
  202. trimEndChangedOptions->SetStringField(FAkWaapiClient::PropertyChangedStrings::RequiredOptions::OBJECT, MaxDurationSourceID);
  203. trimEndChangedOptions->SetStringField(FAkWaapiClient::PropertyChangedStrings::RequiredOptions::PROPERTY, FAkWaapiClient::AudioSourceProperties::TRIM_END);
  204. }
  205. #if AK_SUPPORT_WAAPI
  206. pWaapiClient->Subscribe(ak::wwise::core::object::propertyChanged, trimEndChangedOptions, updateTrimCallback, iTrimEndSubscriptionID, trimEndSubscriptionResult);
  207. #endif
  208. //===========================================================
  209. // TrimBegin
  210. //===========================================================
  211. TSharedPtr<FJsonObject> trimBeginSubscriptionResult = MakeShareable(new FJsonObject());
  212. TSharedRef<FJsonObject> trimBeginChangedOptions = MakeShareable(new FJsonObject());
  213. {
  214. trimBeginChangedOptions->SetStringField(FAkWaapiClient::PropertyChangedStrings::RequiredOptions::OBJECT, MaxDurationSourceID);
  215. trimBeginChangedOptions->SetStringField(FAkWaapiClient::PropertyChangedStrings::RequiredOptions::PROPERTY, FAkWaapiClient::AudioSourceProperties::TRIM_BEGIN);
  216. }
  217. #if AK_SUPPORT_WAAPI
  218. pWaapiClient->Subscribe(ak::wwise::core::object::propertyChanged, trimBeginChangedOptions, updateTrimCallback, iTrimBeginSubscriptionID, trimBeginSubscriptionResult);
  219. #endif
  220. }
  221. }
  222. void UMovieSceneAkAudioEventSection::SubscribeToEventChildAddedRemoved()
  223. {
  224. UnsubscribeWAAPICallback(iChildAddedSubscriptionID);
  225. UnsubscribeWAAPICallback(iChildRemovedSubscriptionID);
  226. auto sParentID = GetEventWwiseGUID().ToString(EGuidFormats::DigitsWithHyphensInBraces);
  227. SubscribeToChildAddedRemoved(sParentID, iChildAddedSubscriptionID, iChildRemovedSubscriptionID);
  228. SubscribeToTrimChanges();
  229. }
  230. /** Subscribes to child added and child removed for each of the action targets in the Wwise event that this section triggers. */
  231. void UMovieSceneAkAudioEventSection::SubscribeToEventChildren()
  232. {
  233. auto pWaapiClient = FAkWaapiClient::Get();
  234. if (pWaapiClient != nullptr)
  235. {
  236. for (int i = 0; i < EventActionSubscriptionIDs.Num(); ++i)
  237. {
  238. UnsubscribeWAAPICallback(EventActionSubscriptionIDs[i]);
  239. }
  240. EventActionSubscriptionIDs.Empty();
  241. /* Construct the relevant WAAPI json fields. */
  242. TArray<TSharedPtr<FJsonValue>> fromID;
  243. fromID.Add(MakeShareable(new FJsonValueString(GetEventWwiseGUID().ToString(EGuidFormats::DigitsWithHyphensInBraces))));
  244. AkInt64 returnFlags = (AkInt64)FAkWaapiClient::WAAPIGetReturnOptionFlag::ID | (AkInt64)FAkWaapiClient::WAAPIGetReturnOptionFlag::TYPE;
  245. TSharedPtr<FJsonObject> select = MakeShareable(new FJsonObject());
  246. TArray<TSharedPtr<FJsonValue>> selectJsonArray;
  247. selectJsonArray.Add(MakeShareable(new FJsonValueString(FAkWaapiClient::WAAPIStrings::DESCENDANTS)));
  248. select->SetArrayField(FAkWaapiClient::WAAPIStrings::SELECT, selectJsonArray);
  249. TArray<TSharedPtr<FJsonValue>> transform;
  250. transform.Add(MakeShareable(new FJsonValueObject(select)));
  251. TSharedPtr<FJsonObject> outJsonResult;
  252. if (FAkWaapiClient::WAAPIGet(FAkWaapiClient::WAAPIGetFromOption::ID, fromID, returnFlags, outJsonResult, FAkWaapiClient::WAAPIGetTransformOption::SELECT, selectJsonArray))
  253. {
  254. if (outJsonResult->HasField(FAkWaapiClient::WAAPIStrings::RETURN))
  255. {
  256. TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
  257. for (int descendant = 0; descendant < returnJson.Num(); ++descendant)
  258. {
  259. auto pJsonObj = returnJson[descendant]->AsObject();
  260. auto descendantID = pJsonObj->GetStringField("id");
  261. auto descendantType = pJsonObj->GetStringField("type");
  262. if (descendantType.Equals("Action", ESearchCase::IgnoreCase))
  263. {
  264. /** When we find a child of type action, get the target property */
  265. fromID.Empty();
  266. fromID.Add(MakeShareable(new FJsonValueString(descendantID)));
  267. outJsonResult = MakeShareable(new FJsonObject());
  268. TSharedRef<FJsonObject> args = MakeShareable(new FJsonObject());
  269. TSharedPtr<FJsonObject> from = MakeShareable(new FJsonObject());
  270. from->SetArrayField(FAkWaapiClient::GetFromOptionString(FAkWaapiClient::WAAPIGetFromOption::ID), fromID);
  271. args->SetObjectField(FAkWaapiClient::WAAPIStrings::FROM, from);
  272. TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
  273. TArray<TSharedPtr<FJsonValue>> StructJsonArray;
  274. StructJsonArray.Add(MakeShareable(new FJsonValueString("@Target")));
  275. options->SetArrayField(FAkWaapiClient::WAAPIStrings::RETURN, StructJsonArray);
  276. #if AK_SUPPORT_WAAPI
  277. if (pWaapiClient->Call(ak::wwise::core::object::get, args, options, outJsonResult, 500))
  278. {
  279. if (outJsonResult->HasField(FAkWaapiClient::WAAPIStrings::RETURN))
  280. {
  281. TArray<TSharedPtr<FJsonValue>> descendantReturnJson = outJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
  282. auto pDescendantJsonObj = descendantReturnJson[0]->AsObject();
  283. if (pDescendantJsonObj->HasField("@Target"))
  284. {
  285. /** Subscribe to child added and child removed for the target object. */
  286. auto targetObj = pDescendantJsonObj->GetObjectField("@Target");
  287. auto targetID = targetObj->GetStringField("id");
  288. uint64 addedSubID = 0;
  289. uint64 removedSubID = 0;
  290. SubscribeToChildAddedRemoved(targetID, addedSubID, removedSubID);
  291. if (addedSubID != 0)
  292. EventActionSubscriptionIDs.Add(addedSubID);
  293. if (removedSubID != 0)
  294. EventActionSubscriptionIDs.Add(removedSubID);
  295. }
  296. }
  297. }
  298. #endif
  299. }
  300. }
  301. }
  302. }
  303. }
  304. }
  305. void UMovieSceneAkAudioEventSection::UnsubscribeWAAPICallback(uint64& in_iSubID)
  306. {
  307. if (in_iSubID != 0)
  308. {
  309. FScopeLock Lock(&WAAPISection);
  310. TSharedPtr<FJsonObject> unsubscribeResult = MakeShareable(new FJsonObject());
  311. FAkWaapiClient* pWaapiClient = FAkWaapiClient::Get();
  312. if (pWaapiClient != nullptr)
  313. pWaapiClient->Unsubscribe(in_iSubID, unsubscribeResult, 500, true);
  314. in_iSubID = 0;
  315. }
  316. }
  317. void UMovieSceneAkAudioEventSection::UnsubscribeAllWAAPICallbacks()
  318. {
  319. UnsubscribeWAAPICallback(iChildAddedInitializeSubscriptionID);
  320. UnsubscribeWAAPICallback(iTrimBeginSubscriptionID);
  321. UnsubscribeWAAPICallback(iTrimEndSubscriptionID);
  322. UnsubscribeWAAPICallback(iChildAddedSubscriptionID);
  323. UnsubscribeWAAPICallback(iChildRemovedSubscriptionID);
  324. for (int i = 0; i < EventActionSubscriptionIDs.Num(); ++i)
  325. {
  326. UnsubscribeWAAPICallback(EventActionSubscriptionIDs[i]);
  327. }
  328. EventActionSubscriptionIDs.Empty();
  329. }
  330. void UMovieSceneAkAudioEventSection::WAAPIGetPeaks(const char* in_uri,
  331. TSharedRef<FJsonObject> in_getPeaksArgs,
  332. TSharedRef<FJsonObject> in_getPeaksOptions,
  333. TSharedPtr<FJsonObject> in_getPeaksResults)
  334. {
  335. auto WaapiClient = FAkWaapiClient::Get();
  336. if (WaapiClient != nullptr)
  337. {
  338. if (WaapiClient->Call(in_uri, in_getPeaksArgs, in_getPeaksOptions, in_getPeaksResults, 2000))
  339. {
  340. int numPeaksReturned = (int)in_getPeaksResults->GetNumberField(FAkWaapiClient::AudioPeaksStrings::Results::PEAKS_ARRAY_LENGTH);
  341. if (numPeaksReturned > 0)
  342. {
  343. /* Decode the peaks data binary string from WAAPI to the AudioSourcePeaks array. */
  344. auto peaksBinaryString = in_getPeaksResults->GetArrayField(FAkWaapiClient::AudioPeaksStrings::Results::PEAKS_BINARY)[0]->AsString();
  345. TArray<uint8> peaksData;
  346. FBase64::Decode(peaksBinaryString, peaksData);
  347. int16* peaks = reinterpret_cast<int16*>(peaksData.GetData());
  348. double maxAbsPeakValue = (double)in_getPeaksResults->GetNumberField(FAkWaapiClient::AudioPeaksStrings::Results::MAX_ABS_VALUE);
  349. AudioSourcePeaks.Empty();
  350. for (int p = 0; p < numPeaksReturned * 2; ++p)
  351. {
  352. int16 peakValue = peaks[p];
  353. AudioSourcePeaks.Add(peakValue / maxAbsPeakValue);
  354. }
  355. }
  356. }
  357. else
  358. {
  359. UE_LOG(LogAkAudio, Warning, TEXT("Failed to get audio source peak data from WAAPI"));
  360. }
  361. }
  362. }
  363. //===================================================================================================
  364. //===================================================================================================
  365. #endif //WITH_EDITOR
  366. //===================================================================================================
  367. // Getters
  368. //===================================================================================================
  369. bool UMovieSceneAkAudioEventSection::EventShouldStopAtSectionEnd() const { return StopAtSectionEnd; }
  370. #if WITH_EDITOR
  371. FGuid UMovieSceneAkAudioEventSection::GetEventWwiseGUID() const
  372. {
  373. if (Event)
  374. {
  375. return Event->GetWwiseGuid();
  376. }
  377. else
  378. {
  379. UE_LOG(LogAkAudio, Log, TEXT("UMovieSceneAkAudioEventSection: Using deprecated event name to find GUID. Please set the \"Event\" property to a valid AkAudioEvent asset."));
  380. FGuid EventGUID;
  381. FAkWaapiClient::GetGUIDForObjectOfTypeWithName(EventGUID, "Event", EventName);
  382. return EventGUID;
  383. }
  384. }
  385. FString UMovieSceneAkAudioEventSection::GetEventWwiseName() const { return Event->GetWwiseName().ToString(); }
  386. const TArray<double>& UMovieSceneAkAudioEventSection::GetAudioSourcePeaks() const { return AudioSourcePeaks; }
  387. /** Returns the number of min max magnitude pairs in the current peaks array. */
  388. const int UMovieSceneAkAudioEventSection::GetNumMinMaxPairs() const { return AudioSourcePeaks.Num() / 2; }
  389. const float UMovieSceneAkAudioEventSection::GetMaxSourceDuration() const { return MaxSourceDuration; }
  390. #endif
  391. /** Returns the minimum and maximum durations for the specified Event or EventName. This uses the generated XML data, not WAAPI. */
  392. int32 UMovieSceneAkAudioEventSection::GetMaxEventDuration() const
  393. {
  394. FFrameRate FrameRate = GetTypedOuter<UMovieScene>()->GetTickResolution();
  395. auto MaxDuration = AkAudioEventSectionHelper::GetDuration(Event, FrameRate).GetUpperBoundValue();
  396. return FrameRate.AsFrameNumber(MaxDuration).Value;
  397. }
  398. float UMovieSceneAkAudioEventSection::GetStartTime() const
  399. {
  400. FFrameRate FrameRate = GetTypedOuter<UMovieScene>()->GetTickResolution();
  401. return (float)FrameRate.AsSeconds(GetRange().GetLowerBoundValue());
  402. }
  403. float UMovieSceneAkAudioEventSection::GetEndTime() const
  404. {
  405. FFrameRate FrameRate = GetTypedOuter<UMovieScene>()->GetTickResolution();
  406. return (float)FrameRate.AsSeconds(GetRange().GetUpperBoundValue());
  407. }
  408. FFloatRange UMovieSceneAkAudioEventSection::GetEventDuration() const
  409. {
  410. FFrameRate FrameRate = GetTypedOuter<UMovieScene>()->GetTickResolution();
  411. return AkAudioEventSectionHelper::GetDuration(Event, FrameRate);
  412. }
  413. #if !UE_4_26_OR_LATER
  414. FMovieSceneEvalTemplatePtr UMovieSceneAkAudioEventSection::GenerateTemplate() const
  415. {
  416. return FMovieSceneAkAudioEventTemplate(this);
  417. }
  418. #endif
  419. #if WITH_EDITOR
  420. /** Associate a new AK audio event with this section. Also updates section time and audio source info. */
  421. bool UMovieSceneAkAudioEventSection::SetEvent(UAkAudioEvent* AudioEvent, const FString& Name)
  422. {
  423. bool dataLoaded = true;
  424. // Update the event details.
  425. if (AudioEvent != nullptr)
  426. {
  427. Event = AudioEvent;
  428. EventName = Name;
  429. }
  430. else
  431. {
  432. EventName = Name;
  433. }
  434. return UpdateAkEventInfo();
  435. }
  436. bool UMovieSceneAkAudioEventSection::UpdateAkEventInfo()
  437. {
  438. UpdateAudioSourceInfo();
  439. if (Event && Event->MaximumDuration != FLT_MAX && Event->MinimumDuration != FLT_MAX)
  440. {
  441. MatchSectionLengthToEventLength();
  442. SubscribeToEventChildAddedRemoved();
  443. return true;
  444. }
  445. else if (Event)
  446. {
  447. UE_LOG(LogAkAudio, Error, TEXT("%s Event doesn't have a defined duration."), *Event->GetFName().ToString());
  448. }
  449. return false;
  450. }
  451. #endif
  452. #if WITH_EDITOR
  453. void UMovieSceneAkAudioEventSection::MatchSectionLengthToEventLength()
  454. {
  455. SetRange(TRange<FFrameNumber>(GetRange().GetLowerBoundValue(), GetRange().GetLowerBoundValue() + GetMaxEventDuration()));
  456. }
  457. /** Get the audio peaks data (min max magnitude pairs) for the current MaxDurationSourceID, using WAAPI.
  458. * @param in_iNumPeaks - The number of peaks required.
  459. */
  460. void UMovieSceneAkAudioEventSection::UpdateAudioSourcePeaks(int in_iNumPeaks)
  461. {
  462. checkf(in_iNumPeaks > 0, TEXT("UMovieSceneAkAudioEventSection::UpdateAudioSourcePeaks: iNumPeaks (%d) <= 0"), in_iNumPeaks);
  463. /* Construct the relevant WAAPI json fields */
  464. TSharedRef<FJsonObject> getPeaksArgs = MakeShareable(new FJsonObject());
  465. TSharedRef<FJsonObject> getPeaksOptions = MakeShareable(new FJsonObject());
  466. TSharedPtr<FJsonObject> getPeaksResult = MakeShareable(new FJsonObject());
  467. getPeaksArgs->SetStringField(FAkWaapiClient::AudioPeaksStrings::Args::OBJECT, MaxDurationSourceID);
  468. getPeaksArgs->SetNumberField(FAkWaapiClient::AudioPeaksStrings::Args::NUM_PEAKS, in_iNumPeaks);
  469. getPeaksArgs->SetBoolField(FAkWaapiClient::AudioPeaksStrings::Args::CROSS_CHANNEL_PEAKS, true);
  470. #if AK_SUPPORT_WAAPI
  471. WAAPIGetPeaks(ak::wwise::core::audioSourcePeaks::getMinMaxPeaksInTrimmedRegion, getPeaksArgs, getPeaksOptions, getPeaksResult);
  472. #endif
  473. }
  474. /** Get the audio peaks data (min max magnitude pairs) for the current MaxDurationSourceID, using WAAPI.
  475. * @param in_iNumPeaks - The number of peaks required.
  476. * @param in_dtimeFrom - The start time of the time period for which peaks are required
  477. * @param in_dTimeTo - The end time of the time period for which peaks are required.
  478. */
  479. void UMovieSceneAkAudioEventSection::UpdateAudioSourcePeaks(int in_iNumPeaks, double in_dTimeFrom, double in_dTimeTo)
  480. {
  481. checkf(in_iNumPeaks > 0, TEXT("UMovieSceneAkAudioEventSection::UpdateAudioSourcePeaks: iNumPeaks (%d) <= 0"), in_iNumPeaks);
  482. /* Construct the relevant WAAPI json fields */
  483. TSharedRef<FJsonObject> getPeaksArgs = MakeShareable(new FJsonObject());
  484. TSharedRef<FJsonObject> getPeaksOptions = MakeShareable(new FJsonObject());
  485. TSharedPtr<FJsonObject> getPeaksResult = MakeShareable(new FJsonObject());
  486. getPeaksArgs->SetStringField(FAkWaapiClient::AudioPeaksStrings::Args::OBJECT, MaxDurationSourceID);
  487. getPeaksArgs->SetNumberField(FAkWaapiClient::AudioPeaksStrings::Args::NUM_PEAKS, in_iNumPeaks);
  488. getPeaksArgs->SetNumberField(FAkWaapiClient::AudioPeaksStrings::Args::TIME_FROM, in_dTimeFrom);
  489. getPeaksArgs->SetNumberField(FAkWaapiClient::AudioPeaksStrings::Args::TIME_TO, in_dTimeTo);
  490. getPeaksArgs->SetBoolField(FAkWaapiClient::AudioPeaksStrings::Args::CROSS_CHANNEL_PEAKS, true);
  491. #if AK_SUPPORT_WAAPI
  492. WAAPIGetPeaks(ak::wwise::core::audioSourcePeaks::getMinMaxPeaksInRegion, getPeaksArgs, getPeaksOptions, getPeaksResult);
  493. #endif
  494. }
  495. /** Update the trim data for the longest audio source used by the Wwise event that this section triggers. */
  496. void UMovieSceneAkAudioEventSection::UpdateTrimData()
  497. {
  498. checkf(MaxDurationSourceID != "", TEXT("UMovieSceneAkAudioEventSection::UpdateTrimData: MaxDurationSourceID is empty."));
  499. AkInt64 returnFlag = (AkInt64)FAkWaapiClient::WAAPIGetReturnOptionFlag::AUDIO_SOURCE_TRIM_VALUES;
  500. TArray<TSharedPtr<FJsonValue>> fromAudioSourceID;
  501. fromAudioSourceID.Add(MakeShareable(new FJsonValueString(MaxDurationSourceID)));
  502. TSharedPtr<FJsonObject> trimJsonResult;
  503. if (FAkWaapiClient::WAAPIGet(FAkWaapiClient::WAAPIGetFromOption::ID, fromAudioSourceID, returnFlag, trimJsonResult))
  504. {
  505. TArray<TSharedPtr<FJsonValue>> trimReturnJson = trimJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
  506. auto trimJsonObj = trimReturnJson[0]->AsObject();
  507. auto trimValuesString = FAkWaapiClient::GetReturnOptionString(FAkWaapiClient::WAAPIGetReturnOptionFlag::AUDIO_SOURCE_TRIM_VALUES);
  508. auto trimValuesJson = trimJsonObj->GetObjectField(trimValuesString);
  509. /* We only keep track of the TrimBegin value, as the waveform length is calculated using the duration value. */
  510. TrimBegin = trimValuesJson->GetNumberField(FAkWaapiClient::TrimValuesStrings::TRIM_BEGIN);
  511. auto TrimEnd = trimValuesJson->GetNumberField(FAkWaapiClient::TrimValuesStrings::TRIM_END);
  512. MaxSourceDuration = TrimEnd - TrimBegin;
  513. }
  514. }
  515. /** Use WAAPI to update the MaxDurationSourceID and MaxSourceDuration. */
  516. void UMovieSceneAkAudioEventSection::UpdateAudioSourceInfo()
  517. {
  518. UE_LOG(LogAkAudio, Verbose,
  519. TEXT("UMovieSceneAkAudioEventSection::UpdateAudioSourceInfo: Updating section %s source info (Event %s)"),
  520. Event == nullptr ? *EventName : *(Event->GetName()),
  521. *GetName());
  522. // Invalidate all audio source info data.
  523. InvalidateAudioSourceInfo();
  524. TrimBegin = 0.0f;
  525. EventTracker->IsDirty = false;
  526. // Update the event state in case it had previously been set to EUnrecognised
  527. InitState = AkEventSectionState::EUninitialized;
  528. TArray<TSharedPtr<FJsonValue>> fromID;
  529. fromID.Add(MakeShareable(new FJsonValueString(GetEventWwiseGUID().ToString(EGuidFormats::DigitsWithHyphensInBraces))));
  530. AkInt64 returnFlags = (AkInt64)FAkWaapiClient::WAAPIGetReturnOptionFlag::AUDIO_SOURCE_MAX_DURATION_SOURCE |
  531. (AkInt64)FAkWaapiClient::WAAPIGetReturnOptionFlag::AUDIO_SOURCE_PLAYBACK_DURATION;
  532. TSharedPtr<FJsonObject> outJsonResult;
  533. if (FAkWaapiClient::WAAPIGet(FAkWaapiClient::WAAPIGetFromOption::ID, fromID, returnFlags, outJsonResult))
  534. {
  535. /* Update MaxDurationSourceID and MaxSourceDuration from WAAPI return json. */
  536. TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
  537. auto jsonObj = returnJson[0]->AsObject();
  538. auto maxDurationSourceString = FAkWaapiClient::GetReturnOptionString(FAkWaapiClient::WAAPIGetReturnOptionFlag::AUDIO_SOURCE_MAX_DURATION_SOURCE);
  539. auto maxDurationSourceJson = jsonObj->GetObjectField(maxDurationSourceString);
  540. auto newMaxDurationSourceID = maxDurationSourceJson->GetStringField(FAkWaapiClient::WAAPIStrings::ID);
  541. MaxDurationSourceID = newMaxDurationSourceID;
  542. MaxSourceDuration = maxDurationSourceJson->GetNumberField(FAkWaapiClient::WAAPIStrings::TRIMMED_DURATION);
  543. UpdateTrimData();
  544. SubscribeToTrimChanges();
  545. SubscribeToEventChildren();
  546. InitState = AkEventSectionState::EInitialized;
  547. UnsubscribeWAAPICallback(iChildAddedInitializeSubscriptionID);
  548. UE_LOG(LogAkAudio, Verbose,
  549. TEXT("UMovieSceneAkAudioEventSection::UpdateAudioSourceInfo: Updated section %s source info (Event %s)"),
  550. Event == nullptr ? *EventName : *(Event->GetName()),
  551. *GetName());
  552. }
  553. else
  554. {
  555. InitState = AkEventSectionState::EUnrecognized;
  556. UE_LOG(LogAkAudio, Verbose,
  557. TEXT("UMovieSceneAkAudioEventSection::UpdateAudioSourceInfo: Failed to update section %s source info (Event %s)"),
  558. Event == nullptr ? *EventName : *(Event->GetName()),
  559. *GetName());
  560. }
  561. CheckForWorkunitChanges(true);
  562. RequiresUpdate = true;
  563. }
  564. void UMovieSceneAkAudioEventSection::InvalidateAudioSourceInfo()
  565. {
  566. AudioSourcePeaks.Empty();
  567. MaxSourceDuration = -1.0f;
  568. MaxDurationSourceID = "";
  569. }
  570. bool UMovieSceneAkAudioEventSection::AudioSourceInfoIsValid() const
  571. {
  572. return MaxDurationSourceID != "" && MaxSourceDuration != -1.0f;
  573. }
  574. void UMovieSceneAkAudioEventSection::CheckForWorkunitChanges(bool in_bNotifyTrack)
  575. {
  576. /* Check if either of the workunits that contain the event or the longest audio source are dirty.
  577. */
  578. bool bIsDirty = false;
  579. bIsDirty |= CheckWorkunitChangesForID(GetEventWwiseGUID());
  580. if (!MaxDurationSourceID.IsEmpty())
  581. {
  582. FGuid maxDurationID;
  583. FGuid::Parse(MaxDurationSourceID, maxDurationID);
  584. bIsDirty |= CheckWorkunitChangesForID(maxDurationID);
  585. }
  586. EventTracker->IsDirty = bIsDirty;
  587. if (bIsDirty && in_bNotifyTrack)
  588. {
  589. if (UMovieSceneAkAudioEventTrack* OwnerTrack = Cast<UMovieSceneAkAudioEventTrack>(GetOuter()))
  590. {
  591. OwnerTrack->WorkUnitChangesDetectedFromSection(this);
  592. }
  593. }
  594. }
  595. bool UMovieSceneAkAudioEventSection::CheckWorkunitChangesForID(FGuid in_objectGUID)
  596. {
  597. FGuid workUnitGUID;
  598. FAkWaapiClient::GetParentOfType(in_objectGUID, workUnitGUID, FAkWaapiClient::WwiseTypeStrings::WORKUNIT);
  599. TArray<TSharedPtr<FJsonValue>> fromID;
  600. fromID.Add(MakeShareable(new FJsonValueString(workUnitGUID.ToString(EGuidFormats::DigitsWithHyphensInBraces))));
  601. AkInt64 returnFlags = (AkInt64)FAkWaapiClient::WAAPIGetReturnOptionFlag::TYPE |
  602. (AkInt64)FAkWaapiClient::WAAPIGetReturnOptionFlag::WORKUNIT_IS_DIRTY;
  603. TSharedPtr<FJsonObject> outJsonResult;
  604. if (FAkWaapiClient::WAAPIGet(FAkWaapiClient::WAAPIGetFromOption::ID, fromID, returnFlags, outJsonResult))
  605. {
  606. TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
  607. auto jsonObj = returnJson[0]->AsObject();
  608. auto typeString = FAkWaapiClient::GetReturnOptionString(FAkWaapiClient::WAAPIGetReturnOptionFlag::TYPE);
  609. auto objType = jsonObj->GetStringField(typeString);
  610. checkf(objType.Equals("workunit", ESearchCase::IgnoreCase), TEXT("UMovieSceneAkAudioEventSection::CheckWorkunitChangesForID: objType (%s) != workunit"), *objType);
  611. auto workunitDirtyString = FAkWaapiClient::GetReturnOptionString(FAkWaapiClient::WAAPIGetReturnOptionFlag::WORKUNIT_IS_DIRTY);
  612. auto workUnitIsDirty = jsonObj->GetBoolField(workunitDirtyString);
  613. return workUnitIsDirty;
  614. }
  615. return false;
  616. }
  617. #endif // WITH_EDITOR