WaapiDataSource.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221
  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 "WaapiDataSource.h"
  16. #include "AkSettings.h"
  17. #include "AkSettingsPerUser.h"
  18. #include "AkWaapiUtils.h"
  19. #include "IAudiokineticTools.h"
  20. #include "PackageTools.h"
  21. #include "WwiseItemType.h"
  22. #include "Async/Async.h"
  23. #include "Dom/JsonObject.h"
  24. #include "Misc/Paths.h"
  25. #include "WaapiPicker/WwiseTreeItem.h"
  26. FWaapiDataSource::~FWaapiDataSource()
  27. {
  28. TearDown();
  29. }
  30. bool FWaapiDataSource::Init()
  31. {
  32. auto WaapiClient = FAkWaapiClient::Get();
  33. if (!WaapiClient)
  34. {
  35. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to get the Waapi client."));
  36. return false;
  37. }
  38. ProjectLoadedHandle = WaapiClient->OnProjectLoaded.AddRaw(
  39. this, &FWaapiDataSource::OnProjectLoadedCallback);
  40. ConnectionLostHandle = WaapiClient->OnConnectionLost.AddRaw(
  41. this, &FWaapiDataSource::OnConnectionLostCallback);
  42. ClientBeginDestroyHandle = WaapiClient->OnClientBeginDestroy.AddRaw(
  43. this, &FWaapiDataSource::OnWaapiClientBeginDestroyCallback);
  44. SubscribeWaapiCallbacks();
  45. return true;
  46. }
  47. bool FWaapiDataSource::TearDown()
  48. {
  49. auto WaapiClient = FAkWaapiClient::Get();
  50. if (!WaapiClient)
  51. {
  52. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to get the Waapi client."));
  53. return false;
  54. }
  55. if (ProjectLoadedHandle.IsValid())
  56. {
  57. WaapiClient->OnProjectLoaded.Remove(ProjectLoadedHandle);
  58. ProjectLoadedHandle.Reset();
  59. }
  60. if (ConnectionLostHandle.IsValid())
  61. {
  62. WaapiClient->OnConnectionLost.Remove(ConnectionLostHandle);
  63. ConnectionLostHandle.Reset();
  64. }
  65. RemoveClientCallbacks();
  66. WaapiClient->OnClientBeginDestroy.Remove(ClientBeginDestroyHandle);
  67. return true;
  68. }
  69. void FWaapiDataSource::ConstructTree(bool bShouldRefresh)
  70. {
  71. if (IsProjectLoaded() != EWwiseConnectionStatus::Connected)
  72. {
  73. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to construct Waapi Tree. The Wwise project is not connected."));
  74. return;
  75. }
  76. {
  77. FScopeLock AutoLock(&WaapiRootItemsLock);
  78. RootItems.Empty();
  79. NodesByPath.Empty();
  80. }
  81. for (int i = EWwiseItemType::Event; i <= EWwiseItemType::LastWwiseBrowserType; ++i)
  82. {
  83. FWwiseTreeItemPtr TreeRoot = ConstructTreeRoot(static_cast<EWwiseItemType::Type>(i));
  84. {
  85. FScopeLock AutoLock(&WaapiRootItemsLock);
  86. RootItems.Add(TreeRoot);
  87. NodesByPath.Add(TreeRoot->FolderPath, TreeRoot);
  88. LoadChildren(TreeRoot);
  89. }
  90. }
  91. if(bShouldRefresh)
  92. {
  93. WaapiDataSourceRefreshed.ExecuteIfBound();
  94. }
  95. }
  96. FWwiseTreeItemPtr FWaapiDataSource::ConstructTreeRoot(EWwiseItemType::Type Type)
  97. {
  98. FGuid InItemGuid = FGuid();
  99. uint32 ShortId = 0;
  100. TSharedPtr<FJsonObject> Result;
  101. uint32_t ItemChildrenCount = 0;
  102. FString Path = WwiseWaapiHelper::BACK_SLASH + EWwiseItemType::FolderNames[static_cast<int>(Type)];
  103. if (IsProjectLoaded() != EWwiseConnectionStatus::Connected)
  104. {
  105. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to construct Waapi Root Items. The Wwise project is not connected."));
  106. return nullptr;
  107. }
  108. // Get the root item of the given hierarchy using its path
  109. if (LoadWaapiInfo(WwiseWaapiHelper::PATH, Path, Result, {}))
  110. {
  111. const TSharedPtr<FJsonObject> ItemInfoObj = Result->GetArrayField(WwiseWaapiHelper::RETURN)[0]->AsObject();
  112. const FString ItemIdString = ItemInfoObj->GetStringField(WwiseWaapiHelper::ID);
  113. Path = ItemInfoObj->GetStringField(WwiseWaapiHelper::PATH);
  114. ItemChildrenCount = ItemInfoObj->GetNumberField(WwiseWaapiHelper::CHILDREN_COUNT);
  115. FGuid::ParseExact(ItemIdString, EGuidFormats::DigitsWithHyphensInBraces, InItemGuid);
  116. }
  117. else
  118. {
  119. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to get information for Waapi items at : %s"), *Path);
  120. if (Result && Result->GetStringField(TEXT("uri")) == TEXT("ak.wwise.locked"))
  121. {
  122. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to get information for Waapi items. The item is locked."));
  123. }
  124. }
  125. // Create the new tree item
  126. FWwiseTreeItemPtr NewRootParent = MakeShared<FWwiseTreeItem>(EWwiseItemType::BrowserDisplayNames[static_cast<int>(Type)], Path, nullptr, EWwiseItemType::Folder, InItemGuid);
  127. NewRootParent->ShortId = ShortId;
  128. NewRootParent->bWaapiRefExists = true;
  129. NewRootParent->ChildCountInWwise = ItemChildrenCount;
  130. return NewRootParent;
  131. }
  132. FWwiseTreeItemPtr FWaapiDataSource::ConstructWwiseTreeItem(const TSharedPtr<FJsonObject>& InItemInfoObj)
  133. {
  134. static const FString ValidPaths[] = {
  135. EWwiseItemType::FolderNames[EWwiseItemType::Event],
  136. EWwiseItemType::FolderNames[EWwiseItemType::AuxBus],
  137. EWwiseItemType::FolderNames[EWwiseItemType::ActorMixer],
  138. EWwiseItemType::FolderNames[EWwiseItemType::GameParameter],
  139. EWwiseItemType::FolderNames[EWwiseItemType::State],
  140. EWwiseItemType::FolderNames[EWwiseItemType::Switch],
  141. EWwiseItemType::FolderNames[EWwiseItemType::Trigger],
  142. EWwiseItemType::FolderNames[EWwiseItemType::AcousticTexture],
  143. EWwiseItemType::FolderNames[EWwiseItemType::EffectShareSet]
  144. };
  145. static auto IsValidPath = [](const FString& Input, const auto& Source) -> bool {
  146. for (const auto& Item : Source)
  147. {
  148. if (Input.StartsWith(WwiseWaapiHelper::BACK_SLASH + Item))
  149. {
  150. return true;
  151. }
  152. }
  153. return false;
  154. };
  155. const FString ItemTypeString = InItemInfoObj->GetStringField(WwiseWaapiHelper::TYPE);
  156. auto ItemType = EWwiseItemType::FromString(ItemTypeString);
  157. if (ItemType == EWwiseItemType::None)
  158. {
  159. return {};
  160. }
  161. const FString ItemPath = InItemInfoObj->GetStringField(WwiseWaapiHelper::PATH);
  162. if (IsValidPath(ItemPath, ValidPaths))
  163. {
  164. const FString ItemIdString = InItemInfoObj->GetStringField(WwiseWaapiHelper::ID);
  165. FGuid InItemId = FGuid::NewGuid();
  166. FGuid::ParseExact(ItemIdString, EGuidFormats::DigitsWithHyphensInBraces, InItemId);
  167. const FString ItemName = InItemInfoObj->GetStringField(WwiseWaapiHelper::NAME);
  168. if (ItemName.IsEmpty())
  169. {
  170. return {};
  171. }
  172. const uint32_t ItemChildrenCount = InItemInfoObj->GetNumberField(WwiseWaapiHelper::CHILDREN_COUNT);
  173. if (ItemType == EWwiseItemType::StandaloneWorkUnit)
  174. {
  175. FString WorkUnitType;
  176. if (InItemInfoObj->TryGetStringField(WwiseWaapiHelper::WORKUNIT_TYPE, WorkUnitType) && WorkUnitType == "FOLDER")
  177. {
  178. ItemType = EWwiseItemType::PhysicalFolder;
  179. }
  180. }
  181. FWwiseTreeItemPtr TreeItem = MakeShared<FWwiseTreeItem>(ItemName, ItemPath, nullptr, ItemType, InItemId);
  182. TreeItem->WaapiName = ItemName;
  183. TreeItem->bWaapiRefExists = true;
  184. if (TreeItem->IsFolder())
  185. {
  186. NodesByPath.Add(TreeItem->FolderPath, TreeItem);
  187. }
  188. if ((ItemType != EWwiseItemType::Event) && (ItemType != EWwiseItemType::Sound))
  189. {
  190. TreeItem->ChildCountInWwise = ItemChildrenCount;
  191. }
  192. return TreeItem;
  193. }
  194. return {};
  195. }
  196. FWwiseTreeItemPtr FWaapiDataSource::ConstructWwiseTreeItem(const TSharedPtr<FJsonValue>& InJsonItem)
  197. {
  198. return ConstructWwiseTreeItem(InJsonItem->AsObject());
  199. }
  200. FWwiseTreeItemPtr FWaapiDataSource::FindItemFromPath(const FWwiseTreeItemPtr& InParentItem,
  201. const FString& InCurrentItemPath)
  202. {
  203. return InParentItem->FindItemRecursive(InCurrentItemPath);
  204. }
  205. FWwiseTreeItemPtr FWaapiDataSource::FindItemFromPath(const FString& InCurrentItemPath)
  206. {
  207. auto FoundItem = NodesByPath.Find(InCurrentItemPath);
  208. if (FoundItem)
  209. {
  210. return *FoundItem;
  211. }
  212. return nullptr;
  213. }
  214. FWwiseTreeItemPtr FWaapiDataSource::FindOrConstructTreeItemFromJsonObject(
  215. const TSharedPtr<FJsonObject>& ObjectJson)
  216. {
  217. FString objectPath;
  218. if (!ObjectJson->TryGetStringField(WwiseWaapiHelper::PATH, objectPath))
  219. {
  220. return {};
  221. }
  222. FString stringId;
  223. if (!ObjectJson->TryGetStringField(WwiseWaapiHelper::ID, stringId))
  224. {
  225. return {};
  226. }
  227. FGuid id;
  228. FGuid::ParseExact(stringId, EGuidFormats::DigitsWithHyphensInBraces, id);
  229. TArray<FString> pathParts;
  230. objectPath.ParseIntoArray(pathParts, *WwiseWaapiHelper::BACK_SLASH);
  231. if (pathParts.Num() == 0)
  232. {
  233. return {};
  234. }
  235. TSharedPtr<FWwiseTreeItem> treeItem;
  236. TArray<TSharedPtr<FWwiseTreeItem>>* children = &RootItems;
  237. TArray<TSharedPtr<FWwiseTreeItem>> itemsToExpand;
  238. FString folderPath;
  239. for (auto& part : pathParts)
  240. {
  241. folderPath += FString::Printf(TEXT("%s%s"), *WwiseWaapiHelper::BACK_SLASH, *part);
  242. bool found = false;
  243. for (auto& item : *children)
  244. {
  245. if (item->ItemId == id)
  246. {
  247. treeItem = item;
  248. break;
  249. }
  250. if (item->FolderPath == folderPath)
  251. {
  252. if (!item->IsExpanded)
  253. {
  254. const FString itemIdStringField = item->ItemId.ToString(EGuidFormats::DigitsWithHyphensInBraces);
  255. TSharedPtr<FJsonObject> Result;
  256. bool bIdFound = false;
  257. if(item->ItemId.IsValid())
  258. {
  259. // Request data from Wwise UI using WAAPI and use them to create a Wwise tree item, getting the informations from a specific "ID".
  260. bIdFound = CallWaapiGetInfoFrom(WwiseWaapiHelper::ID, itemIdStringField, Result, { { WwiseWaapiHelper::SELECT, { WwiseWaapiHelper::CHILDREN }, {} } });
  261. }
  262. if (!bIdFound)
  263. {
  264. // Folders do not have a specific "ID". Search with the "PATH" instead.
  265. if (!CallWaapiGetInfoFrom(WwiseWaapiHelper::PATH, item->FolderPath, Result, { { WwiseWaapiHelper::SELECT, { WwiseWaapiHelper::CHILDREN }, {} } }))
  266. {
  267. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to get information from id : %s"), *itemIdStringField);
  268. return {};
  269. }
  270. }
  271. itemsToExpand.Add(item);
  272. }
  273. treeItem = item;
  274. children = treeItem->GetChildrenMutable();
  275. found = true;
  276. }
  277. }
  278. if (treeItem && treeItem->ItemId == id)
  279. {
  280. break;
  281. }
  282. if (!found)
  283. {
  284. return {};
  285. }
  286. }
  287. if (treeItem.IsValid() && treeItem->ItemId != id)
  288. {
  289. return {};
  290. }
  291. WwiseExpansionChange.ExecuteIfBound(itemsToExpand);
  292. return treeItem;
  293. }
  294. void FWaapiDataSource::FindAndCreateItems(FWwiseTreeItemPtr CurrentItem)
  295. {
  296. FString LastPathVisited = CurrentItem->FolderPath;
  297. LastPathVisited.RemoveFromEnd(WwiseWaapiHelper::BACK_SLASH + CurrentItem->DisplayName);
  298. FWwiseTreeItemPtr RootItem = GetRootItem(CurrentItem->FolderPath);
  299. if (!RootItem || CurrentItem->FolderPath == RootItem->FolderPath)
  300. {
  301. return;
  302. }
  303. if (LastPathVisited == RootItem->FolderPath)
  304. {
  305. RootItem->AddChild(CurrentItem);
  306. return;
  307. }
  308. FWwiseTreeItemPtr ParentItem = FindItemFromPath(RootItem, LastPathVisited);
  309. if (ParentItem.IsValid())
  310. {
  311. ParentItem->AddChild(CurrentItem);
  312. }
  313. else
  314. {
  315. TSharedPtr<FJsonObject> Result;
  316. // Request data from Wwise UI using WAAPI and use them to create a Wwise tree item, getting the informations from a specific "PATH".
  317. if (LoadWaapiInfo(WwiseWaapiHelper::PATH, LastPathVisited, Result, {}))
  318. {
  319. // Recover the information from the Json object Result and use it to construct the tree item.
  320. FWwiseTreeItemPtr NewRootItem = ConstructWwiseTreeItem(Result->GetArrayField(WwiseWaapiHelper::RETURN)[0]);
  321. CurrentItem->Parent = NewRootItem;
  322. NewRootItem->AddChild(CurrentItem);
  323. FindAndCreateItems(NewRootItem);
  324. }
  325. else
  326. {
  327. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to get information from Waapi path : %s"), *LastPathVisited);
  328. }
  329. }
  330. }
  331. FString FWaapiDataSource::GetItemWorkUnitPath(FWwiseTreeItemPtr InItem)
  332. {
  333. auto WaapiClient = FAkWaapiClient::Get();
  334. if (!WaapiClient)
  335. {
  336. UE_LOG(LogAudiokineticTools, Log, TEXT("Unable to connect to localhost"));
  337. return {};
  338. }
  339. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  340. {
  341. TSharedPtr<FJsonObject> From = MakeShared<FJsonObject>();
  342. From->SetArrayField(WwiseWaapiHelper::PATH, TArray<TSharedPtr<FJsonValue>> { MakeShared<FJsonValueString>(InItem->FolderPath) });
  343. Args->SetObjectField(WwiseWaapiHelper::FROM, From);
  344. }
  345. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  346. Options->SetArrayField(WwiseWaapiHelper::RETURN, TArray<TSharedPtr<FJsonValue>> { MakeShared<FJsonValueString>(WwiseWaapiHelper::FILEPATH) });
  347. #if AK_SUPPORT_WAAPI
  348. TSharedPtr<FJsonObject> outJsonResult;
  349. if (!WaapiClient->Call(ak::wwise::core::object::get, Args, Options, outJsonResult))
  350. {
  351. UE_LOG(LogAudiokineticTools, Log, TEXT("Call Failed to get %s's Work Unit Path"), *InItem->DisplayName);
  352. return {};
  353. }
  354. FString Path = outJsonResult->GetArrayField(WwiseWaapiHelper::RETURN)[0]->AsObject()->GetStringField(
  355. WwiseWaapiHelper::FILEPATH);
  356. return Path;
  357. #else
  358. return {};
  359. #endif
  360. }
  361. FWwiseTreeItemPtr FWaapiDataSource::GetRootItem(EWwiseItemType::Type RootType)
  362. {
  363. if (IsProjectLoaded() != EWwiseConnectionStatus::Connected)
  364. {
  365. return nullptr;
  366. }
  367. check(RootType <= EWwiseItemType::LastWwiseBrowserType)
  368. if (RootType > RootItems.Num() - 1)
  369. {
  370. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to get Waapi Root Items. Index out of range."));
  371. return nullptr;
  372. }
  373. return RootItems[RootType];
  374. }
  375. FWwiseTreeItemPtr FWaapiDataSource::GetRootItem(const FString& InFullPath)
  376. {
  377. for (int i = EWwiseItemType::Event; i <= EWwiseItemType::LastWwiseBrowserType; ++i)
  378. {
  379. if (InFullPath.StartsWith(RootItems[i]->FolderPath))
  380. {
  381. return RootItems[i];
  382. }
  383. }
  384. return {};
  385. }
  386. FWwiseTreeItemPtr FWaapiDataSource::LoadFilteredRootItem(EWwiseItemType::Type RootType, TSharedPtr<StringFilter> CurrentFilter)
  387. {
  388. check(CurrentFilter.IsValid())
  389. if (RootType > RootItems.Num() - 1)
  390. {
  391. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to get Waapi Root Items. Index out of range."));
  392. return nullptr;
  393. }
  394. FString CurrentFilterText = CurrentFilter->GetRawFilterText().ToString();
  395. FString Path = WwiseWaapiHelper::BACK_SLASH + EWwiseItemType::FolderNames[static_cast<int>(RootType)];
  396. FScopeLock Autolock(&WaapiRootItemsLock);
  397. for (int i = EWwiseItemType::Event; i <= EWwiseItemType::LastWwiseBrowserType; ++i)
  398. {
  399. RootItems[i]->EmptyChildren();
  400. }
  401. TSharedPtr<FJsonObject> Result;
  402. if (LoadWaapiInfo(WwiseWaapiHelper::PATH, Path, Result,
  403. {
  404. { WwiseWaapiHelper::SELECT , { WwiseWaapiHelper::DESCENDANTS }, {} },
  405. { WwiseWaapiHelper::WHERE, { WwiseWaapiHelper::NAMECONTAINS, CurrentFilterText}, {} }
  406. }))
  407. {
  408. // Recover the information from the Json object Result and use it to construct the tree item.
  409. TArray<TSharedPtr<FJsonValue>> SearchResultArray = Result->GetArrayField(WwiseWaapiHelper::RETURN);
  410. if (SearchResultArray.Num())
  411. {
  412. // The map contains each path and the correspondent object of the search result.
  413. TMap < FString, FWwiseTreeItemPtr> SearchedResultTreeItem;
  414. for (int i = 0; i < SearchResultArray.Num(); i++)
  415. {
  416. // Fill the map with the path-object elements.
  417. FWwiseTreeItemPtr NewRootChild = ConstructWwiseTreeItem(SearchResultArray[i]);
  418. if (NewRootChild.IsValid())
  419. {
  420. FindAndCreateItems(NewRootChild);
  421. }
  422. }
  423. }
  424. }
  425. else
  426. {
  427. UE_LOG(LogAudiokineticTools, Log, TEXT("Failed to get information from Waapi item search : %s"), *CurrentFilterText);
  428. }
  429. return GetRootItem(RootType);
  430. }
  431. bool FWaapiDataSource::LoadWaapiInfo(const FString& InFromField, const FString& InFromString,
  432. TSharedPtr<FJsonObject>& OutJsonResult, const TArray<WaapiTransformStringField>& TransformFields)
  433. {
  434. auto WaapiClient = FAkWaapiClient::Get();
  435. if (!WaapiClient)
  436. {
  437. UE_LOG(LogAudiokineticTools, Log, TEXT("Unable to connect to localhost"));
  438. return false;
  439. }
  440. #if AK_SUPPORT_WAAPI
  441. // Construct the Json arguments from a specific id/path
  442. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  443. {
  444. TSharedPtr<FJsonObject> From = MakeShared<FJsonObject>();
  445. From->SetArrayField(InFromField, TArray<TSharedPtr<FJsonValue>> {MakeShared<FJsonValueString>(InFromString)});
  446. Args->SetObjectField(WwiseWaapiHelper::FROM, From);
  447. }
  448. if (TransformFields.Num())
  449. {
  450. TArray<TSharedPtr<FJsonValue>> Transform;
  451. for (const auto& TransformValue : TransformFields)
  452. {
  453. TSharedPtr<FJsonObject> InsideTransform = MakeShared<FJsonObject>();
  454. TArray<TSharedPtr<FJsonValue>> JsonArray;
  455. for (auto TransformStringValueArg : TransformValue.valueStringArgs)
  456. {
  457. JsonArray.Add(MakeShared<FJsonValueString>(TransformStringValueArg));
  458. }
  459. for (auto TransformNumberValueArg : TransformValue.valueNumberArgs)
  460. {
  461. JsonArray.Add(MakeShared<FJsonValueNumber>(TransformNumberValueArg));
  462. }
  463. InsideTransform->SetArrayField(TransformValue.keyArg, JsonArray);
  464. Transform.Add(MakeShared<FJsonValueObject>(InsideTransform));
  465. }
  466. Args->SetArrayField(WwiseWaapiHelper::TRANSFORM, Transform);
  467. }
  468. // Construct the Options Json object : Getting specific infos to construct the WwiseTreeItem "id - name - type - childrenCount - path - parent"
  469. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  470. Options->SetArrayField(WwiseWaapiHelper::RETURN, TArray<TSharedPtr<FJsonValue>>
  471. {
  472. MakeShared<FJsonValueString>(WwiseWaapiHelper::ID),
  473. MakeShared<FJsonValueString>(WwiseWaapiHelper::NAME),
  474. MakeShared<FJsonValueString>(WwiseWaapiHelper::TYPE),
  475. MakeShared<FJsonValueString>(WwiseWaapiHelper::CHILDREN_COUNT),
  476. MakeShared<FJsonValueString>(WwiseWaapiHelper::PATH),
  477. MakeShared<FJsonValueString>(WwiseWaapiHelper::WORKUNIT_TYPE),
  478. });
  479. return WaapiClient->Call(ak::wwise::core::object::get, Args, Options, OutJsonResult);
  480. #else
  481. return false;
  482. #endif
  483. }
  484. bool FWaapiDataSource::IsTreeDirty()
  485. {
  486. return false;
  487. }
  488. void FWaapiDataSource::Tick(const double InCurrentTime, const float InDeltaTime)
  489. {
  490. }
  491. int32 FWaapiDataSource::LoadChildren(const FGuid& InParentId, const FString& InParentPath, TArray<FWwiseTreeItemPtr>& OutChildren)
  492. {
  493. UE_LOG(LogAudiokineticTools, VeryVerbose, TEXT("Loading Children for Waapi item %s, id: %s"), *InParentPath, *InParentId.ToString())
  494. FString InFromField;
  495. FString InStringField;
  496. OutChildren = {};
  497. if (InParentId.IsValid())
  498. {
  499. InFromField = WwiseWaapiHelper::ID;
  500. InStringField = InParentId.ToString(EGuidFormats::DigitsWithHyphensInBraces);
  501. }
  502. else
  503. {
  504. InFromField = WwiseWaapiHelper::PATH;
  505. InStringField = InParentPath;
  506. }
  507. TSharedPtr<FJsonObject> Result;
  508. // Request data from Wwise UI using WAAPI and use them to create a Wwise tree item, getting the information from a specific "ID".
  509. if (!LoadWaapiInfo(WwiseWaapiHelper::PATH, InParentPath, Result,
  510. {
  511. { WwiseWaapiHelper::SELECT , { WwiseWaapiHelper::DESCENDANTS }, {} },
  512. { WwiseWaapiHelper::WHERE, { WwiseWaapiHelper::NAMECONTAINS, ""}, {}}
  513. }))
  514. {
  515. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to load Waapi information for %s."), *InStringField);
  516. return 0;
  517. }
  518. TArray<TSharedPtr<FJsonValue>> StructJsonArray = Result->GetArrayField(WwiseWaapiHelper::RETURN);
  519. if (StructJsonArray.Num() == 0)
  520. {
  521. return 0;
  522. }
  523. FWwiseTreeItemPtr ExistingParent = nullptr;
  524. if (auto ParentPtr = NodesByPath.Find(InParentPath))
  525. {
  526. ExistingParent = *ParentPtr;
  527. }
  528. if(!ExistingParent)
  529. {
  530. UE_LOG(LogAudiokineticTools, Error, TEXT("No Parent found at %s."), *InParentPath)
  531. return 0;
  532. }
  533. ExistingParent->EmptyChildren();
  534. for (int i = 0; i < StructJsonArray.Num(); i++)
  535. {
  536. FWwiseTreeItemPtr NewChild = ConstructWwiseTreeItem(StructJsonArray[i]);
  537. if (NewChild)
  538. {
  539. FindAndCreateItems(NewChild);
  540. }
  541. }
  542. return OutChildren.Num();
  543. }
  544. int32 FWaapiDataSource::LoadChildren(FWwiseTreeItemPtr ParentTreeItem)
  545. {
  546. if (!ParentTreeItem)
  547. {
  548. return 0;
  549. }
  550. TArray<FWwiseTreeItemPtr> OutChildren;
  551. LoadChildren(ParentTreeItem->ItemId, ParentTreeItem->FolderPath, OutChildren);
  552. return ParentTreeItem->GetChildren().Num();
  553. }
  554. int32 FWaapiDataSource::GetChildItemCount(const FWwiseTreeItemPtr& InParentItem)
  555. {
  556. return InParentItem->ChildCountInWwise;
  557. }
  558. FString FWaapiDataSource::LoadProjectName()
  559. {
  560. auto WaapiClient = FAkWaapiClient::Get();
  561. if (!WaapiClient)
  562. {
  563. return {};
  564. }
  565. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  566. {
  567. TSharedPtr<FJsonObject> OfType = MakeShared<FJsonObject>();
  568. OfType->SetArrayField(WwiseWaapiHelper::OF_TYPE, TArray<TSharedPtr<FJsonValue>> { MakeShared<FJsonValueString>(WwiseWaapiHelper::PROJECT) });
  569. Args->SetObjectField(WwiseWaapiHelper::FROM, OfType);
  570. }
  571. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  572. {
  573. Options->SetArrayField(WwiseWaapiHelper::RETURN, TArray<TSharedPtr<FJsonValue>>
  574. {
  575. MakeShared<FJsonValueString>(WwiseWaapiHelper::NAME),
  576. MakeShared<FJsonValueString>(WwiseWaapiHelper::FILEPATH),
  577. });
  578. }
  579. #if AK_SUPPORT_WAAPI
  580. TSharedPtr<FJsonObject> outJsonResult;
  581. if (WaapiClient->Call(ak::wwise::core::object::get, Args, Options, outJsonResult))
  582. {
  583. // Recover the information from the Json object Result and use it to get the item id.
  584. TArray<TSharedPtr<FJsonValue>> StructJsonArray = outJsonResult->GetArrayField(WwiseWaapiHelper::RETURN);
  585. if (StructJsonArray.Num())
  586. {
  587. FString Path = StructJsonArray[0]->AsObject()->GetStringField(WwiseWaapiHelper::FILEPATH);
  588. return FPaths::GetCleanFilename(Path);
  589. }
  590. else
  591. {
  592. UE_LOG(LogAudiokineticTools, Log, TEXT("Unable to get the project name"));
  593. return {};
  594. }
  595. }
  596. #endif
  597. return {};
  598. }
  599. void FWaapiDataSource::HandleFindWwiseItemInProjectExplorerCommandExecute(
  600. const TArray<FWwiseTreeItemPtr>& SelectedItems) const
  601. {
  602. auto waapiClient = FAkWaapiClient::Get();
  603. if (!waapiClient)
  604. {
  605. UE_LOG(LogAudiokineticTools, Log, TEXT("Unable to connect to localhost"));
  606. return;
  607. }
  608. if (SelectedItems.Num() == 0)
  609. return;
  610. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  611. Args->SetStringField(WwiseWaapiHelper::COMMAND, WwiseWaapiHelper::FIND_IN_PROJECT_EXPLORER);
  612. TArray<TSharedPtr<FJsonValue>> SelectedObjects;
  613. for (auto SelectedItem : SelectedItems)
  614. {
  615. if(SelectedItem->ItemId.IsValid())
  616. {
  617. SelectedObjects.Add(MakeShared<FJsonValueString>(SelectedItem->ItemId.ToString(EGuidFormats::DigitsWithHyphensInBraces)));
  618. }
  619. }
  620. Args->SetArrayField(WwiseWaapiHelper::OBJECTS, SelectedObjects);
  621. #if AK_SUPPORT_WAAPI
  622. TSharedPtr<FJsonObject> Result;
  623. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  624. if (!waapiClient->Call(ak::wwise::ui::commands::execute, Args, Options, Result))
  625. {
  626. UE_LOG(LogAudiokineticTools, Log, TEXT("Call Failed"));
  627. }
  628. #endif
  629. }
  630. void FWaapiDataSource::OnProjectLoadedCallback()
  631. {
  632. SubscribeWaapiCallbacks();
  633. ConstructTree(true);
  634. }
  635. void FWaapiDataSource::OnConnectionLostCallback()
  636. {
  637. {
  638. FScopeLock AutoLock(&WaapiRootItemsLock);
  639. RootItems.Empty();
  640. NodesByPath.Empty();
  641. }
  642. WaapiDataSourceRefreshed.ExecuteIfBound();
  643. UnsubscribeWaapiCallbacks();
  644. }
  645. void FWaapiDataSource::OnWaapiClientBeginDestroyCallback()
  646. {
  647. auto waapiClient = FAkWaapiClient::Get();
  648. if (waapiClient == nullptr)
  649. return;
  650. if (ProjectLoadedHandle.IsValid())
  651. {
  652. waapiClient->OnProjectLoaded.Remove(ProjectLoadedHandle);
  653. ProjectLoadedHandle.Reset();
  654. }
  655. if (ConnectionLostHandle.IsValid())
  656. {
  657. waapiClient->OnConnectionLost.Remove(ConnectionLostHandle);
  658. ConnectionLostHandle.Reset();
  659. }
  660. UnsubscribeWaapiCallbacks();
  661. }
  662. void FWaapiDataSource::OnWaapiRenamed(uint64_t Id, TSharedPtr<FJsonObject> Response)
  663. {
  664. FString OldName;
  665. if (Response->TryGetStringField(WwiseWaapiHelper::OLD_NAME, OldName) && OldName.IsEmpty())
  666. {
  667. return;
  668. }
  669. FString NewName;
  670. if (Response->TryGetStringField(WwiseWaapiHelper::NEW_NAME, NewName) && NewName.IsEmpty())
  671. {
  672. return;
  673. }
  674. const TSharedPtr<FJsonObject>* ObjectJsonPtr = nullptr;
  675. if (!Response->TryGetObjectField(WwiseWaapiHelper::OBJECT, ObjectJsonPtr))
  676. {
  677. return;
  678. }
  679. FString ObjectPath;
  680. if (!ObjectJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::PATH, ObjectPath))
  681. {
  682. return;
  683. }
  684. FString OldPath(ObjectPath);
  685. OldPath.RemoveFromEnd(WwiseWaapiHelper::BACK_SLASH + NewName);
  686. auto TreeItem = FindItemFromPath(OldPath);
  687. FString ItemIdString;
  688. if(ObjectJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::ID, ItemIdString))
  689. {
  690. FGuid ItemId = FGuid::NewGuid();
  691. FGuid::ParseExact(ItemIdString, EGuidFormats::DigitsWithHyphensInBraces, ItemId);
  692. if(ItemsToCreate.Contains(ItemId))
  693. {
  694. ItemsToCreate.Remove(ItemId);
  695. if (TreeItem)
  696. {
  697. auto ChildItem = ConstructWwiseTreeItem(*ObjectJsonPtr);
  698. if (ChildItem)
  699. {
  700. TreeItem->AddChild(ChildItem);
  701. if(ChildItem->IsFolder())
  702. {
  703. NodesByPath.Add(ChildItem->FolderPath, ChildItem);
  704. }
  705. }
  706. WaapiDataSourceRefreshed.ExecuteIfBound();
  707. return;
  708. }
  709. }
  710. }
  711. OldPath += WwiseWaapiHelper::BACK_SLASH + OldName;
  712. NodesByPath.Remove(OldPath);
  713. if(TreeItem)
  714. {
  715. for(auto& Child : TreeItem->GetChildren())
  716. {
  717. if(Child->DisplayName == OldName)
  718. {
  719. Child->DisplayName = UPackageTools::SanitizePackageName(NewName);
  720. Child->WaapiName = NewName;
  721. break;
  722. }
  723. }
  724. }
  725. WaapiDataSourceRefreshed.ExecuteIfBound();
  726. }
  727. void FWaapiDataSource::OnWaapiChildAdded(uint64_t Id, TSharedPtr<FJsonObject> Response)
  728. {
  729. const TSharedPtr<FJsonObject>* ChildJsonPtr = nullptr;
  730. if (!Response->TryGetObjectField(WwiseWaapiHelper::CHILD, ChildJsonPtr))
  731. {
  732. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to get Child Object field."));
  733. return;
  734. }
  735. FString ChildName;
  736. if (!ChildJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::NAME, ChildName))
  737. {
  738. return;
  739. }
  740. FString ChildPath;
  741. if (!ChildJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::PATH, ChildPath))
  742. {
  743. return;
  744. }
  745. const TSharedPtr<FJsonObject>* ParentJsonPtr = nullptr;
  746. if (!Response->TryGetObjectField(WwiseWaapiHelper::PARENT, ParentJsonPtr))
  747. {
  748. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to get Parent Object field."));
  749. return;
  750. }
  751. FString TreeItemParentPath;
  752. if (!ParentJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::PATH, TreeItemParentPath))
  753. {
  754. return;
  755. }
  756. FString TreeItemParentName;
  757. if (!ParentJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::NAME, TreeItemParentName))
  758. {
  759. return;
  760. }
  761. //The Item was copy pasted. Add it to the list to be created with the rename callback
  762. if(TreeItemParentPath == TreeItemParentName)
  763. {
  764. FString ItemIdString = ChildJsonPtr->Get()->GetStringField(WwiseWaapiHelper::ID);
  765. FGuid ItemId = FGuid::NewGuid();
  766. FGuid::ParseExact(ItemIdString, EGuidFormats::DigitsWithHyphensInBraces, ItemId);
  767. ItemsToCreate.Add(ItemId);
  768. return;
  769. }
  770. bool bChildAdded = false;
  771. if (auto ParentItem = NodesByPath.Find(TreeItemParentPath))
  772. {
  773. if (!ParentItem->Get()->FindItemRecursive(ChildPath))
  774. {
  775. auto ChildItem = ConstructWwiseTreeItem(*ChildJsonPtr);
  776. if (ChildItem)
  777. {
  778. ParentItem->Get()->AddChild(ChildItem);
  779. if(ChildItem->IsFolder())
  780. {
  781. NodesByPath.Add(ChildItem->FolderPath, ChildItem);
  782. }
  783. bChildAdded = true;
  784. }
  785. }
  786. }
  787. if(bChildAdded)
  788. {
  789. WaapiDataSourceRefreshed.ExecuteIfBound();
  790. }
  791. }
  792. void FWaapiDataSource::OnWaapiChildRemoved(uint64_t Id, TSharedPtr<FJsonObject> Response)
  793. {
  794. const TSharedPtr<FJsonObject>* ChildJsonPtr = nullptr;
  795. if (!Response->TryGetObjectField(WwiseWaapiHelper::CHILD, ChildJsonPtr))
  796. {
  797. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to get Child Object field."));
  798. return;
  799. }
  800. const TSharedPtr<FJsonObject>* ParentJsonPtr = nullptr;
  801. if (!Response->TryGetObjectField(WwiseWaapiHelper::PARENT, ParentJsonPtr))
  802. {
  803. UE_LOG(LogAudiokineticTools, Error, TEXT("Failed to get Parent Object field."));
  804. return;
  805. }
  806. FString ItemIdString;
  807. FString ItemName;
  808. if (!ChildJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::ID, ItemIdString))
  809. {
  810. return;
  811. }
  812. if (!ChildJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::NAME, ItemName))
  813. {
  814. return;
  815. }
  816. FString TreeItemParentPath;
  817. if (!ParentJsonPtr->Get()->TryGetStringField(WwiseWaapiHelper::PATH, TreeItemParentPath))
  818. {
  819. return;
  820. }
  821. // This seems to be the path to the removed item, and not its parent?
  822. if (auto ParentTreeItem = NodesByPath.Find(TreeItemParentPath))
  823. {
  824. FGuid ItemId = FGuid(ItemIdString);
  825. NodesByPath.Remove(TreeItemParentPath / ItemName);
  826. (*ParentTreeItem)->RemoveChild(ItemId);
  827. }
  828. WaapiDataSourceRefreshed.ExecuteIfBound();
  829. }
  830. void FWaapiDataSource::OnWwiseSelectionChanged(uint64_t Id, TSharedPtr<FJsonObject> Response)
  831. {
  832. AsyncTask(ENamedThreads::GameThread, [this, Response]()
  833. {
  834. const UAkSettingsPerUser* AkSettingsPerUser = GetDefault<UAkSettingsPerUser>();
  835. if (AkSettingsPerUser && AkSettingsPerUser->AutoSyncSelection)
  836. {
  837. const TArray<TSharedPtr<FJsonValue>>* objectsJsonArray = nullptr;
  838. if (Response->TryGetArrayField(WwiseWaapiHelper::OBJECTS, objectsJsonArray))
  839. {
  840. TArray<TSharedPtr<FWwiseTreeItem>> TreeItems;
  841. for (auto JsonObject : *objectsJsonArray)
  842. {
  843. auto TreeItem = FindOrConstructTreeItemFromJsonObject(JsonObject->AsObject());
  844. if (TreeItem)
  845. {
  846. TreeItems.Add(TreeItem);
  847. }
  848. }
  849. WwiseSelectionChange.ExecuteIfBound(TreeItems);
  850. }
  851. }
  852. });
  853. }
  854. void FWaapiDataSource::SubscribeWaapiCallbacks()
  855. {
  856. struct SubscriptionData
  857. {
  858. const char* Uri;
  859. WampEventCallback Callback;
  860. uint64* SubscriptionId;
  861. };
  862. #if AK_SUPPORT_WAAPI
  863. const SubscriptionData Subscriptions[] = {
  864. {ak::wwise::core::object::nameChanged, WampEventCallback::CreateRaw(this, &FWaapiDataSource::OnWaapiRenamed), &WaapiSubscriptionIds.Renamed},
  865. {ak::wwise::core::object::childAdded, WampEventCallback::CreateRaw(this, &FWaapiDataSource::OnWaapiChildAdded), &WaapiSubscriptionIds.ChildAdded},
  866. {ak::wwise::core::object::childRemoved, WampEventCallback::CreateRaw(this, &FWaapiDataSource::OnWaapiChildRemoved), &WaapiSubscriptionIds.ChildRemoved},
  867. {ak::wwise::ui::selectionChanged, WampEventCallback::CreateRaw(this, &FWaapiDataSource::OnWwiseSelectionChanged), &WaapiSubscriptionIds.SelectionChanged},
  868. };
  869. #endif
  870. auto waapiClient = FAkWaapiClient::Get();
  871. if (!waapiClient)
  872. {
  873. return;
  874. }
  875. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  876. Options->SetArrayField(WwiseWaapiHelper::RETURN, TArray<TSharedPtr<FJsonValue>>
  877. {
  878. MakeShared<FJsonValueString>(WwiseWaapiHelper::ID),
  879. MakeShared<FJsonValueString>(WwiseWaapiHelper::NAME),
  880. MakeShared<FJsonValueString>(WwiseWaapiHelper::TYPE),
  881. MakeShared<FJsonValueString>(WwiseWaapiHelper::CHILDREN_COUNT),
  882. MakeShared<FJsonValueString>(WwiseWaapiHelper::PATH),
  883. MakeShared<FJsonValueString>(WwiseWaapiHelper::PARENT),
  884. MakeShared<FJsonValueString>(WwiseWaapiHelper::WORKUNIT_TYPE),
  885. });
  886. TSharedPtr<FJsonObject> Result;
  887. #if AK_SUPPORT_WAAPI
  888. for (auto& Subscription : Subscriptions)
  889. {
  890. if (*Subscription.SubscriptionId == 0)
  891. {
  892. waapiClient->Subscribe(Subscription.Uri,
  893. Options,
  894. Subscription.Callback,
  895. *Subscription.SubscriptionId,
  896. Result
  897. );
  898. }
  899. }
  900. #endif
  901. }
  902. EWwiseConnectionStatus FWaapiDataSource::IsProjectLoaded()
  903. {
  904. if(UAkSettingsPerUser* AkUserSettings = GetMutableDefault<UAkSettingsPerUser>())
  905. {
  906. if(!AkUserSettings->bAutoConnectToWAAPI)
  907. {
  908. return EWwiseConnectionStatus::SettingDisabled;
  909. }
  910. }
  911. if(FAkWaapiClient::IsProjectLoaded())
  912. {
  913. return EWwiseConnectionStatus::Connected;
  914. }
  915. if(FAkWaapiClient::Get()->bIsWrongProjectLoaded)
  916. {
  917. return EWwiseConnectionStatus::WrongProjectOpened;
  918. }
  919. return EWwiseConnectionStatus::WwiseNotOpen;
  920. }
  921. void FWaapiDataSource::SelectInProjectExplorer(TArray<FWwiseTreeItemPtr>& InTreeItems)
  922. {
  923. auto WaapiClient = FAkWaapiClient::Get();
  924. if (!WaapiClient)
  925. {
  926. UE_LOG(LogAudiokineticTools, Log, TEXT("Unable to connect to localhost"));
  927. return;
  928. }
  929. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  930. Args->SetStringField(WwiseWaapiHelper::COMMAND, WwiseWaapiHelper::FIND_IN_PROJECT_EXPLORER);
  931. TArray<TSharedPtr<FJsonValue>> SelectedObjects;
  932. for (const auto& TreeItem : InTreeItems)
  933. {
  934. SelectedObjects.Add(MakeShared<FJsonValueString>(TreeItem->ItemId.ToString(EGuidFormats::DigitsWithHyphensInBraces)));
  935. }
  936. Args->SetArrayField(WwiseWaapiHelper::OBJECTS, SelectedObjects);
  937. #if AK_SUPPORT_WAAPI
  938. TSharedPtr<FJsonObject> Result;
  939. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  940. if (!WaapiClient->Call(ak::wwise::ui::commands::execute, Args, Options, Result))
  941. {
  942. UE_LOG(LogAudiokineticTools, Log, TEXT("Call Failed to Select Items in Project Explorer."));
  943. }
  944. #endif
  945. }
  946. bool FWaapiDataSource::CallWaapiGetInfoFrom(const FString& inFromField, const FString& inFromString, TSharedPtr<FJsonObject>& outJsonResult, const TArray<TransformStringField>& TransformFields)
  947. {
  948. auto waapiClient = FAkWaapiClient::Get();
  949. if (!waapiClient)
  950. {
  951. return false;
  952. }
  953. #if AK_SUPPORT_WAAPI
  954. // Construct the arguments Json object : Getting infos "from - a specific id/path"
  955. TSharedRef<FJsonObject> Args = MakeShared<FJsonObject>();
  956. {
  957. TSharedPtr<FJsonObject> from = MakeShared<FJsonObject>();
  958. from->SetArrayField(inFromField, TArray<TSharedPtr<FJsonValue>> { MakeShared<FJsonValueString>(inFromString) });
  959. Args->SetObjectField(WwiseWaapiHelper::FROM, from);
  960. // In case we would recover the children of the object that have the id : ID or the path : PATH, then we set isGetChildren to true.
  961. if (TransformFields.Num())
  962. {
  963. TArray<TSharedPtr<FJsonValue>> transform;
  964. for (auto TransformValue : TransformFields)
  965. {
  966. TSharedPtr<FJsonObject> insideTransform = MakeShared<FJsonObject>();
  967. TArray<TSharedPtr<FJsonValue>> JsonArray;
  968. for (auto TransformStringValueArg : TransformValue.valueStringArgs)
  969. {
  970. JsonArray.Add(MakeShared<FJsonValueString>(TransformStringValueArg));
  971. }
  972. for (auto TransformNumberValueArg : TransformValue.valueNumberArgs)
  973. {
  974. JsonArray.Add(MakeShared<FJsonValueNumber>(TransformNumberValueArg));
  975. }
  976. insideTransform->SetArrayField(TransformValue.keyArg, JsonArray);
  977. transform.Add(MakeShared<FJsonValueObject>(insideTransform));
  978. }
  979. Args->SetArrayField(WwiseWaapiHelper::TRANSFORM, transform);
  980. }
  981. }
  982. // Construct the Options Json object : Getting specific infos to construct the wwise tree item "id - name - type - childrenCount - path - parent"
  983. TSharedRef<FJsonObject> Options = MakeShared<FJsonObject>();
  984. Options->SetArrayField(WwiseWaapiHelper::RETURN, TArray<TSharedPtr<FJsonValue>>
  985. {
  986. MakeShared<FJsonValueString>(WwiseWaapiHelper::ID),
  987. MakeShared<FJsonValueString>(WwiseWaapiHelper::NAME),
  988. MakeShared<FJsonValueString>(WwiseWaapiHelper::TYPE),
  989. MakeShared<FJsonValueString>(WwiseWaapiHelper::CHILDREN_COUNT),
  990. MakeShared<FJsonValueString>(WwiseWaapiHelper::PATH),
  991. MakeShared<FJsonValueString>(WwiseWaapiHelper::WORKUNIT_TYPE),
  992. });
  993. // Request data from Wwise using WAAPI
  994. return waapiClient->Call(ak::wwise::core::object::get, Args, Options, outJsonResult);
  995. #endif
  996. return false;
  997. }
  998. void FWaapiDataSource::UnsubscribeWaapiCallbacks()
  999. {
  1000. auto WaapiClient = FAkWaapiClient::Get();
  1001. if (!WaapiClient)
  1002. {
  1003. return;
  1004. }
  1005. auto DoUnsubscribe = [WaapiClient](uint64& subscriptionId) {
  1006. if (subscriptionId > 0)
  1007. {
  1008. TSharedPtr<FJsonObject> Result;
  1009. WaapiClient->Unsubscribe(subscriptionId, Result);
  1010. subscriptionId = 0;
  1011. }
  1012. };
  1013. DoUnsubscribe(WaapiSubscriptionIds.Renamed);
  1014. DoUnsubscribe(WaapiSubscriptionIds.ChildAdded);
  1015. DoUnsubscribe(WaapiSubscriptionIds.ChildRemoved);
  1016. DoUnsubscribe(WaapiSubscriptionIds.SelectionChanged);
  1017. }
  1018. void FWaapiDataSource::RemoveClientCallbacks()
  1019. {
  1020. auto WaapiClient = FAkWaapiClient::Get();
  1021. if (WaapiClient == nullptr)
  1022. {
  1023. return;
  1024. }
  1025. if (ProjectLoadedHandle.IsValid())
  1026. {
  1027. WaapiClient->OnProjectLoaded.Remove(ProjectLoadedHandle);
  1028. ProjectLoadedHandle.Reset();
  1029. }
  1030. if (ConnectionLostHandle.IsValid())
  1031. {
  1032. WaapiClient->OnConnectionLost.Remove(ConnectionLostHandle);
  1033. ConnectionLostHandle.Reset();
  1034. }
  1035. UnsubscribeWaapiCallbacks();
  1036. }