AkWaapiClient.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. /*=============================================================================
  16. AkWaapiClient.cpp: Audiokinetic WAAPI interface object.
  17. Unreal is RHS with Y and Z swapped (or technically LHS with flipped axis)
  18. =============================================================================*/
  19. /*------------------------------------------------------------------------------------
  20. Audio includes.
  21. ------------------------------------------------------------------------------------*/
  22. #include "AkWaapiClient.h"
  23. #include "AkAudioDevice.h"
  24. #include "AkSettings.h"
  25. #include "AkSettingsPerUser.h"
  26. #include "AkUnrealHelper.h"
  27. #include "Serialization/JsonSerializer.h"
  28. #include "Async/Async.h"
  29. #include "Misc/ScopeLock.h"
  30. #include "Misc/CoreDelegates.h"
  31. #include "Wwise/API/WAAPI.h"
  32. #if AK_SUPPORT_WAAPI
  33. #if PLATFORM_WINDOWS
  34. // Problem with the Windows API as a whole: it uses noexcept straight up, whether exceptions are used or not, generating a warning that Unreal then converts to an error.
  35. #if _MSC_VER >= 1910
  36. #pragma warning (disable: 4577)
  37. #endif // _MSC_VER >= 1910
  38. #endif // #if PLATFORM_WINDOWS
  39. typedef AK::WwiseAuthoringAPI::JsonProvider JsonProvider;
  40. #endif
  41. /*------------------------------------------------------------------------------------
  42. Statics and Globals
  43. ------------------------------------------------------------------------------------*/
  44. FAkWaapiClient* g_AkWaapiClient = nullptr;
  45. /*------------------------------------------------------------------------------------
  46. FAkWaapiClientConnectionHandler
  47. ------------------------------------------------------------------------------------*/
  48. FAkWaapiClientConnectionHandler::FAkWaapiClientConnectionHandler(FAkWaapiClient& in_Client) : m_Client(in_Client)
  49. {
  50. WaitEvent = FPlatformProcess::GetSynchEventFromPool(true);
  51. }
  52. FAkWaapiClientConnectionHandler::~FAkWaapiClientConnectionHandler()
  53. {
  54. FPlatformProcess::ReturnSynchEventToPool(WaitEvent);
  55. WaitEvent = nullptr;
  56. }
  57. void FAkWaapiClientConnectionHandler::RegisterAutoConnectChangedCallback()
  58. {
  59. #if WITH_EDITOR
  60. FScopeLock Lock(&AkSettingsSection);
  61. if (auto AkSettingsPerUser = GetDefault<UAkSettingsPerUser>())
  62. {
  63. AutoConnectChangedHandle = AkSettingsPerUser->OnAutoConnectToWaapiChanged.AddLambda([this, AkSettingsPerUser]()
  64. {
  65. ResetReconnectionDelay();
  66. if (AkSettingsPerUser->bAutoConnectToWAAPI)
  67. WaitEvent->Trigger();
  68. else
  69. {
  70. m_Client.BroadcastConnectionLost();
  71. }
  72. });
  73. }
  74. #endif
  75. }
  76. void FAkWaapiClientConnectionHandler::Wake()
  77. {
  78. WaitEvent->Trigger();
  79. }
  80. /* FRunnable interface */
  81. bool FAkWaapiClientConnectionHandler::Init()
  82. {
  83. return true;
  84. }
  85. uint32 FAkWaapiClientConnectionHandler::Run()
  86. {
  87. #if AK_SUPPORT_WAAPI
  88. AKASSERT(!IsInGameThread());
  89. while (!ThreadShouldExit)
  90. {
  91. if (!m_Client.IsProjectLoaded())
  92. {
  93. /** Check if we should attempt to reconnect according to the Wwise Plugin Settings. */
  94. bool bReconnect = !m_Client.IsDisconnecting() && !m_Client.AppIsExiting();
  95. {
  96. FScopeLock Lock(&AkSettingsSection);
  97. if (auto AkSettingsPerUser = GetDefault<UAkSettingsPerUser>())
  98. {
  99. bReconnect = AkSettingsPerUser->bAutoConnectToWAAPI;
  100. }
  101. }
  102. /** If we previously had a connection (and we're not exiting), broadcast connection lost.*/
  103. if (hadConnection && !m_Client.AppIsExiting())
  104. {
  105. if (bReconnect)
  106. UE_LOG(LogAkAudio, Warning, TEXT("Lost connection to WAAPI client. Attempting reconnection ..."));
  107. hadConnection = false;
  108. AsyncTask(ENamedThreads::GameThread, [this]()
  109. {
  110. m_Client.BroadcastConnectionLost();
  111. });
  112. }
  113. /** If we should reconnect, attempt a reconnection and, if successful, call the client's connection established function on the game thread.
  114. * Otherwise, print a failed connection log.
  115. */
  116. if (bReconnect)
  117. {
  118. if (AttemptReconnect())
  119. {
  120. hadConnection = true;
  121. m_Client.SetConnectionClosing(false);
  122. ResetReconnectionDelay();
  123. AsyncTask(ENamedThreads::GameThread, [this]()
  124. {
  125. m_Client.ConnectionEstablished();
  126. });
  127. }
  128. else
  129. {
  130. if (LogOutputCount.GetValue() < 7)
  131. {
  132. UE_LOG(LogAkAudio, Warning, TEXT("Failed to connect to WAAPI client on local host. Trying again in %i seconds."), ReconnectDelay.GetValue());
  133. LogOutputCount.Increment();
  134. }
  135. }
  136. /** Delay the next reconnection attempt according to the ReconnectDelay value. */
  137. const int iCurrentDelay = ReconnectDelay.GetValue();
  138. if (iCurrentDelay < m_iMaxReconnectDelay)
  139. ReconnectDelay.Set(iCurrentDelay * 2);
  140. WaitEvent->Wait(iCurrentDelay * 1000);
  141. WaitEvent->Reset();
  142. }
  143. else
  144. {
  145. /** We shouldn't attempt reconnection, so wait until the auto-reconnect option is changed. */
  146. WaitEvent->Wait();
  147. WaitEvent->Reset();
  148. }
  149. }
  150. else /** We're already connected. Check connection status. */
  151. {
  152. TSharedRef<FJsonObject> args = MakeShareable(new FJsonObject());
  153. TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
  154. TSharedPtr<FJsonObject> result = MakeShareable(new FJsonObject());
  155. m_Client.Call(ak::wwise::core::getInfo, args, options, result, 500, true);
  156. WaitEvent->Wait(ConnectionMonitorDelay.GetValue() * 1000);
  157. WaitEvent->Reset();
  158. }
  159. }
  160. #endif
  161. return 0;
  162. }
  163. void FAkWaapiClientConnectionHandler::ResetReconnectionDelay()
  164. {
  165. bool bReconnect = true;
  166. {
  167. FScopeLock Lock(&AkSettingsSection);
  168. if (auto AkSettingsPerUser = GetDefault<UAkSettingsPerUser>())
  169. {
  170. bReconnect = AkSettingsPerUser->bAutoConnectToWAAPI;
  171. }
  172. }
  173. if (bReconnect && !m_Client.AppIsExiting() && !m_Client.IsDisconnecting())
  174. {
  175. ReconnectDelay.Set(2);
  176. LogOutputCount.Set(0);
  177. }
  178. }
  179. void FAkWaapiClientConnectionHandler::Stop()
  180. {
  181. ThreadShouldExit = true;
  182. }
  183. void FAkWaapiClientConnectionHandler::Exit()
  184. {
  185. ThreadShouldExit = true;
  186. }
  187. bool FAkWaapiClientConnectionHandler::AttemptReconnect()
  188. {
  189. #if AK_SUPPORT_WAAPI
  190. if (m_Client.AttemptConnection())
  191. {
  192. UE_LOG(LogAkAudio, Log, TEXT("Successfully connected to Wwise Authoring on localhost."));
  193. return true;
  194. }
  195. #endif
  196. return false;
  197. }
  198. /*------------------------------------------------------------------------------------
  199. Helpers
  200. ------------------------------------------------------------------------------------*/
  201. struct FAkWaapiClientImpl
  202. {
  203. void Init(FAkWaapiClient& in_Client)
  204. {
  205. #if AK_SUPPORT_WAAPI
  206. auto* WAAPI = IWAAPI::Get();
  207. if (UNLIKELY(!WAAPI))
  208. {
  209. return;
  210. }
  211. m_Client = WAAPI->NewClient();
  212. if (UNLIKELY(!m_Client))
  213. {
  214. return;
  215. }
  216. m_pConnectionHandler = MakeShareable(new FAkWaapiClientConnectionHandler(in_Client));
  217. FString ThreadName(FString::Printf(TEXT("WAAPIClientConnectionThread%i"), ThreadCounter.Increment()));
  218. m_pReconnectionThread = MakeShareable(FRunnableThread::Create(m_pConnectionHandler.Get(),
  219. *ThreadName, 0,
  220. EThreadPriority::TPri_BelowNormal));
  221. m_pConnectionHandler->RegisterAutoConnectChangedCallback();
  222. #endif
  223. }
  224. ~FAkWaapiClientImpl()
  225. {
  226. #if AK_SUPPORT_WAAPI
  227. if (m_pConnectionHandler.IsValid())
  228. {
  229. m_pConnectionHandler->Exit();
  230. m_pConnectionHandler->Wake();
  231. }
  232. if (m_pReconnectionThread.IsValid())
  233. {
  234. if (!m_pReconnectionThread->Kill(true))
  235. {
  236. UE_LOG(LogAkAudio, Error, TEXT("WAAPI Connection Thread Failed to Exit!"));
  237. }
  238. }
  239. delete m_Client; m_Client = nullptr;
  240. #endif
  241. }
  242. #if AK_SUPPORT_WAAPI
  243. /** Map containing id keys and WampEventCallback values. */
  244. TMap<uint64_t, WampEventCallback> m_wampEventCallbackMap;
  245. IWAAPI::Client* m_Client = nullptr;
  246. /** A non-0 value indicates that UE is exiting. */
  247. FThreadSafeCounter AppExitingCounter = 0;
  248. FThreadSafeCounter ThreadCounter;
  249. /** Thread on which the WAAPI connection is monitored. */
  250. TSharedPtr<FRunnableThread> m_pReconnectionThread;
  251. /** The connection to WAAPI is monitored by this connection handler.
  252. * It tries to reconnect when connection is lost, and continuously polls WAAPI for the connection status when WAAPI is connected.
  253. * This behaviour can be disabled in AkSettings using the AutoConnectToWaapi boolean option.
  254. */
  255. TSharedPtr<FAkWaapiClientConnectionHandler> m_pConnectionHandler;
  256. FCriticalSection ClientSection;
  257. /** Flag indicating whether the correct project has been loaded (it's "correct" if it matches the Project Path in AkSettings.) */
  258. FThreadSafeBool bProjectLoaded = false;
  259. /** Flag indicating if the connection is being killed and shouldn't be restarted. */
  260. FThreadSafeBool bIsConnectionClosing = false;
  261. #endif
  262. };
  263. bool FAkWaapiClient::JsonObjectToString(const TSharedRef<FJsonObject>& in_jsonObject, FString& ou_jsonObjectString)
  264. {
  265. TSharedRef<TJsonWriter<>> JsonWriterArgs = TJsonWriterFactory<>::Create(&ou_jsonObjectString);
  266. auto result = FJsonSerializer::Serialize(in_jsonObject, JsonWriterArgs);
  267. if (!result)
  268. {
  269. UE_LOG(LogAkAudio, Log, TEXT("Unable to get a string representation of the Json Object."));
  270. }
  271. JsonWriterArgs->Close();
  272. return result;
  273. }
  274. #if AK_SUPPORT_WAAPI
  275. void WampEventCallbacks(const uint64_t& in_subscriptionId, const JsonProvider& in_rJson)
  276. {
  277. if (g_AkWaapiClient == nullptr)
  278. return;
  279. auto wampEventCallbacks = g_AkWaapiClient->GetWampEventCallback(in_subscriptionId);
  280. if (!wampEventCallbacks)
  281. return;
  282. auto* WAAPI = IWAAPI::Get();
  283. if (UNLIKELY(!WAAPI))
  284. {
  285. return;
  286. }
  287. TSharedPtr<FJsonObject> ueJsonObject;
  288. TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(UTF8_TO_TCHAR(WAAPI->GetJsonString(in_rJson).c_str()));
  289. if (!FJsonSerializer::Deserialize(Reader, ueJsonObject) || !ueJsonObject.IsValid())
  290. {
  291. UE_LOG(LogAkAudio, Log, TEXT("Unable to deserialize a JSON object from the string : %s"), UTF8_TO_TCHAR(WAAPI->GetJsonString(in_rJson).c_str()));
  292. return;
  293. }
  294. wampEventCallbacks->Execute(in_subscriptionId, ueJsonObject);
  295. }
  296. #endif
  297. /*------------------------------------------------------------------------------------
  298. Implementation.
  299. ------------------------------------------------------------------------------------*/
  300. FAkWaapiClient::~FAkWaapiClient()
  301. {
  302. delete m_Impl;
  303. }
  304. void FAkWaapiClient::Initialize()
  305. {
  306. #if AK_SUPPORT_WAAPI
  307. if (!g_AkWaapiClient)
  308. {
  309. g_AkWaapiClient = new FAkWaapiClient();
  310. if(g_AkWaapiClient)
  311. {
  312. g_AkWaapiClient->m_Impl->Init(*g_AkWaapiClient);
  313. }
  314. FCoreDelegates::OnPreExit.AddLambda([]
  315. {
  316. if (g_AkWaapiClient != nullptr)
  317. {
  318. g_AkWaapiClient->m_Impl->AppExitingCounter.Increment();
  319. TArray<uint64_t> aSubscriptionIDs;
  320. g_AkWaapiClient->m_Impl->m_wampEventCallbackMap.GetKeys(aSubscriptionIDs);
  321. TSharedPtr<FJsonObject> jsonResult = MakeShareable(new FJsonObject());
  322. for (auto iSubscriptionID : aSubscriptionIDs)
  323. {
  324. g_AkWaapiClient->Unsubscribe(iSubscriptionID, jsonResult);
  325. }
  326. }
  327. DeleteInstance();
  328. });
  329. }
  330. #endif
  331. }
  332. /** Returns the singleton instance of FAkWaapiClient. Be sure to call FAkWaapiClient::Initialize() first (i.e. during Module startup). */
  333. FAkWaapiClient* FAkWaapiClient::Get()
  334. {
  335. return g_AkWaapiClient;
  336. }
  337. bool FAkWaapiClient::AppIsExiting()
  338. {
  339. #if AK_SUPPORT_WAAPI
  340. return m_Impl->AppExitingCounter.GetValue() != 0;
  341. #else
  342. return false;
  343. #endif
  344. }
  345. void FAkWaapiClient::SetConnectionClosing(bool isClosing)
  346. {
  347. #if AK_SUPPORT_WAAPI
  348. m_Impl->bIsConnectionClosing = isClosing;
  349. #endif
  350. }
  351. void FAkWaapiClient::DeleteInstance()
  352. {
  353. #if AK_SUPPORT_WAAPI
  354. if (g_AkWaapiClient == nullptr)
  355. return;
  356. g_AkWaapiClient->OnClientBeginDestroy.Broadcast();
  357. g_AkWaapiClient->m_Impl->bIsConnectionClosing = true;
  358. if (g_AkWaapiClient->m_Impl->m_Client)
  359. {
  360. g_AkWaapiClient->m_Impl->m_Client->Disconnect();
  361. }
  362. delete g_AkWaapiClient;
  363. g_AkWaapiClient = nullptr;
  364. #endif
  365. }
  366. bool FAkWaapiClient::IsDisconnecting()
  367. {
  368. #if AK_SUPPORT_WAAPI
  369. return m_Impl->bIsConnectionClosing;
  370. #else
  371. return false;
  372. #endif
  373. }
  374. WampEventCallback* FAkWaapiClient::GetWampEventCallback(const uint64_t& in_subscriptionId)
  375. {
  376. #if AK_SUPPORT_WAAPI
  377. return m_Impl->m_wampEventCallbackMap.Find(in_subscriptionId);
  378. #else
  379. return nullptr;
  380. #endif
  381. }
  382. bool FAkWaapiClient::IsProjectLoaded()
  383. {
  384. #if AK_SUPPORT_WAAPI
  385. if (g_AkWaapiClient == nullptr)
  386. return false;
  387. if (!g_AkWaapiClient->IsConnected())
  388. {
  389. return false;
  390. }
  391. return g_AkWaapiClient->m_Impl->bProjectLoaded;
  392. #else
  393. return false;
  394. #endif
  395. }
  396. /** This is called when the reconnection handler successfully connects to WAAPI.
  397. * We check if the correct project is loaded on a background thread. If it is, we broadcast OnProjectLoaded.
  398. * We also subscribe to ak::wwise::core::project::loaded in order to check the project whenever one is loaded.
  399. * If an incorrect project is loaded we broadcast OnConnectionLost.
  400. */
  401. void FAkWaapiClient::ConnectionEstablished()
  402. {
  403. #if AK_SUPPORT_WAAPI
  404. ensure(IsInGameThread());
  405. if (g_AkWaapiClient == nullptr)
  406. return;
  407. // Broadcast OnProjectLoaded. If we are here, it means connection was successful, and that the correct project is
  408. // loaded and available
  409. OnProjectLoaded.Broadcast();
  410. //We also subscribe to ak::wwise::core::project::loaded in order to check the project whenever one is loaded.
  411. auto projectLoadedCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> in_UEJsonObject)
  412. {
  413. AsyncTask(ENamedThreads::GameThread, [this, id, in_UEJsonObject]()
  414. {
  415. m_Impl->bProjectLoaded = CheckProjectLoaded();
  416. if (m_Impl->bProjectLoaded)
  417. {
  418. OnProjectLoaded.Broadcast();
  419. }
  420. else if (!AppIsExiting())//If an incorrect project is loaded we broadcast OnConnectionLost
  421. {
  422. BroadcastConnectionLost();
  423. }
  424. });
  425. });
  426. TSharedPtr<FJsonObject> projectLoadedSubscriptionResult = MakeShareable(new FJsonObject());
  427. TSharedRef<FJsonObject> projectLoadedOptions = MakeShareable(new FJsonObject());
  428. uint64_t projectLoadedSubscriptionID;
  429. g_AkWaapiClient->Subscribe(ak::wwise::core::project::loaded, projectLoadedOptions, projectLoadedCallback, projectLoadedSubscriptionID, projectLoadedSubscriptionResult);
  430. //And we need to subscribe to ak::wwise::core::project::postClosed such that we are able to re-connect to WAAPI (for example if Wwise is closed then opened again).
  431. auto projectClosedCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> in_UEJsonObject)
  432. {
  433. AsyncTask(ENamedThreads::GameThread, [this]()
  434. {
  435. BroadcastConnectionLost();
  436. });
  437. });
  438. TSharedPtr<FJsonObject> projectClosedSubscriptionResult = MakeShareable(new FJsonObject());
  439. TSharedRef<FJsonObject> projectClosedOptions = MakeShareable(new FJsonObject());
  440. uint64_t projectClosedSubscriptionID;
  441. g_AkWaapiClient->Subscribe(ak::wwise::core::project::postClosed, projectClosedOptions, projectClosedCallback, projectClosedSubscriptionID, projectClosedSubscriptionResult);
  442. #endif
  443. }
  444. /** Returns the path of the Wwise project as defined in AkSettings (WWise Plugin Settings). */
  445. bool FAkWaapiClient::GetProjectPath(TSharedPtr<FJsonObject>& inOutJsonResult, FString& ProjectPath)
  446. {
  447. #if AK_SUPPORT_WAAPI
  448. TArray<TSharedPtr<FJsonValue>> inFromItems;
  449. inFromItems.Add(MakeShareable(new FJsonValueString("Project")));
  450. const bool bSuccess = WAAPIGet(WAAPIGetFromOption::OF_TYPE, inFromItems, (AkInt64)WAAPIGetReturnOptionFlag::FILEPATH,
  451. inOutJsonResult, WAAPIGetTransformOption::NONE, TArray<TSharedPtr<FJsonValue>>(), true);
  452. if (bSuccess)
  453. {
  454. if (inOutJsonResult->HasField(FAkWaapiClient::WAAPIStrings::RETURN))
  455. {
  456. TArray<TSharedPtr<FJsonValue>> returnJson = inOutJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
  457. if (returnJson.Num() > 0)
  458. ProjectPath = returnJson[0]->AsObject()->GetStringField(FAkWaapiClient::WAAPIStrings::FILEPATH);
  459. }
  460. }
  461. return bSuccess;
  462. #else
  463. return true;
  464. #endif
  465. }
  466. // WAAPI can become temporarily unavailable from time to time, this should be taken into account in the design.
  467. // Please note that you shouldn't use this kind of design unless you know your call is accurate. If you do this
  468. // and the error is caused by an argument problem, you might end up with an infinite retry loop.
  469. FString GetAndWaitForCurrentProject(float RetrySleepTimeSeconds)
  470. {
  471. FString ProjectPath;
  472. static int RetryCount = 5;
  473. while (g_AkWaapiClient->IsConnected())
  474. {
  475. TSharedPtr<FJsonObject> JsonResult = MakeShareable(new FJsonObject());
  476. if (g_AkWaapiClient->GetProjectPath(JsonResult, ProjectPath))
  477. break;
  478. if (RetrySleepTimeSeconds > 0.0f)
  479. {
  480. if (--RetryCount == 0)
  481. {
  482. RetryCount = 5;
  483. return {};
  484. }
  485. // Avoid flooding with requests while WAAPI is unavailable.
  486. FPlatformProcess::Sleep(RetrySleepTimeSeconds);
  487. }
  488. }
  489. #if PLATFORM_MAC
  490. if(ProjectPath.StartsWith(TEXT("Y:")))
  491. {
  492. ProjectPath.ReplaceInline(TEXT("Y:"), *FPlatformMisc::GetEnvironmentVariable(TEXT("HOME")));
  493. }
  494. if(ProjectPath.StartsWith(TEXT("Z:")))
  495. {
  496. ProjectPath.ReplaceInline(TEXT("Z:"), *FPlatformMisc::GetEnvironmentVariable(TEXT("ROOT")));
  497. }
  498. ProjectPath.ReplaceInline(TEXT("\\"), TEXT("/"));
  499. #endif
  500. return ProjectPath;
  501. }
  502. /** Checks if the currently loaded Wwise project matches the project path set in AkSettings (Wwise plugin settings).
  503. * NOTE: This function will block while Wwise has a modal window open. It should not be called on the Game thread.
  504. */
  505. bool FAkWaapiClient::CheckProjectLoaded()
  506. {
  507. #if AK_SUPPORT_WAAPI
  508. if (g_AkWaapiClient == nullptr)
  509. return false;
  510. if (!g_AkWaapiClient->IsConnected())
  511. {
  512. g_AkWaapiClient->m_Impl->m_pConnectionHandler->ResetReconnectionDelay();
  513. return false;
  514. }
  515. if (const UAkSettings* AkSettings = GetDefault<UAkSettings>())
  516. {
  517. auto ProjectPathString = AkUnrealHelper::GetWwiseProjectPath();
  518. FString sCurrentlyLoadedProjectPath = GetAndWaitForCurrentProject(1.0f);
  519. if (FPaths::IsSamePath(sCurrentlyLoadedProjectPath, ProjectPathString))
  520. {
  521. return true;
  522. }
  523. }
  524. #endif
  525. return false;
  526. }
  527. void FAkWaapiClient::BroadcastConnectionLost()
  528. {
  529. #if AK_SUPPORT_WAAPI
  530. m_Impl->bProjectLoaded = false;
  531. OnConnectionLost.Broadcast();
  532. #endif
  533. }
  534. bool FAkWaapiClient::IsConnected()
  535. {
  536. #if AK_SUPPORT_WAAPI
  537. if (UNLIKELY(!g_AkWaapiClient->m_Impl->m_Client))
  538. {
  539. return false;
  540. }
  541. return m_Impl->m_Client->IsConnected();
  542. #else
  543. return false;
  544. #endif
  545. }
  546. bool FAkWaapiClient::AttemptConnection()
  547. {
  548. bIsWrongProjectLoaded = false;
  549. bool bConnected = false;
  550. #if AK_SUPPORT_WAAPI
  551. if (UNLIKELY(!g_AkWaapiClient->m_Impl->m_Client))
  552. {
  553. bConnected = false;
  554. }
  555. else if (const UAkSettingsPerUser* AkSettingsPerUser = GetDefault<UAkSettingsPerUser>())
  556. {
  557. bConnected = m_Impl->m_Client->Connect(TCHAR_TO_UTF8(*AkSettingsPerUser->WaapiIPAddress), AkSettingsPerUser->WaapiPort);
  558. }
  559. else
  560. {
  561. bConnected = m_Impl->m_Client->Connect(WAAPI_LOCAL_HOST_IP_STRING, WAAPI_PORT);
  562. }
  563. if (bConnected)
  564. {
  565. bool bProjectLoaded = CheckProjectLoaded();
  566. if (!bProjectLoaded)
  567. {
  568. // We successfully connected, but the wrong project is open (or getting the project timed out). Disconnect.
  569. // We will attemps reconnection later.
  570. bIsWrongProjectLoaded = true;
  571. m_Impl->m_Client->Disconnect();
  572. bConnected = false;
  573. }
  574. m_Impl->bProjectLoaded = bProjectLoaded;
  575. }
  576. #endif
  577. return bConnected;
  578. }
  579. bool FAkWaapiClient::Subscribe(const char* in_uri, const FString& in_options, WampEventCallback in_callback,
  580. uint64& out_subscriptionId, FString& out_result, int in_iTimeoutMs /*= 500*/)
  581. {
  582. bool eResult = false;
  583. #if AK_SUPPORT_WAAPI
  584. std::string out_resultString("");
  585. if (IsConnected())
  586. {
  587. if (!m_Impl->bIsConnectionClosing)
  588. {
  589. // Call for the AK WAAPI method using string params.
  590. if (LIKELY(g_AkWaapiClient->m_Impl->m_Client))
  591. {
  592. FScopeLock Lock(&m_Impl->ClientSection);
  593. eResult = m_Impl->m_Client->Subscribe(in_uri, TCHAR_TO_UTF8(*in_options), &WampEventCallbacks, out_subscriptionId, out_resultString, in_iTimeoutMs);
  594. }
  595. if (eResult)
  596. {
  597. m_Impl->m_wampEventCallbackMap.Add(out_subscriptionId, in_callback);
  598. }
  599. else
  600. {
  601. UE_LOG(LogAkAudio, Log, TEXT("Subscription failed: %s"), *FString(UTF8_TO_TCHAR(out_resultString.c_str())));
  602. }
  603. out_result = FString(UTF8_TO_TCHAR(out_resultString.c_str()));
  604. }
  605. }
  606. else
  607. {
  608. if (m_Impl && m_Impl->m_pConnectionHandler)
  609. {
  610. m_Impl->m_pConnectionHandler->ResetReconnectionDelay();
  611. }
  612. }
  613. #endif
  614. return eResult;
  615. }
  616. bool FAkWaapiClient::Subscribe(const char* in_uri, const TSharedRef<FJsonObject>& in_options, WampEventCallback in_callback,
  617. uint64& out_subscriptionId, TSharedPtr<FJsonObject>& out_result, int in_iTimeoutMs /*= 500*/)
  618. {
  619. bool eResult = false;
  620. #if AK_SUPPORT_WAAPI
  621. FString in_optionsString = TEXT("");
  622. // Retrieve the options data string from the Json object.
  623. JsonObjectToString(in_options, in_optionsString);
  624. FString out_resultString(TEXT(""));
  625. // Call for the AK WAAPI method using string params.
  626. eResult = Subscribe(in_uri, in_optionsString, in_callback, out_subscriptionId, out_resultString, in_iTimeoutMs);
  627. if (!eResult)
  628. {
  629. // Deserialize a JSON object from the string.
  630. TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(out_resultString);
  631. if ((!FJsonSerializer::Deserialize(Reader, out_result) || !out_result.IsValid()) && IsConnected())
  632. {
  633. UE_LOG(LogAkAudio, Log, TEXT("Subscribe: Output result -> unable to deserialize the Json object from the string : %s"), *out_resultString);
  634. }
  635. }
  636. #endif
  637. return eResult;
  638. }
  639. bool FAkWaapiClient::Unsubscribe(const uint64_t& in_subscriptionId, FString& out_result, int in_iTimeoutMs /*= 500*/, bool in_bSilenceLog /*= false*/)
  640. {
  641. bool eResult = false;
  642. #if AK_SUPPORT_WAAPI
  643. if (IsConnected())
  644. {
  645. if (!m_Impl->bIsConnectionClosing)
  646. {
  647. std::string out_resultString("");
  648. // Call the AK WAAPI method.
  649. if (LIKELY(g_AkWaapiClient->m_Impl->m_Client))
  650. {
  651. FScopeLock Lock(&m_Impl->ClientSection);
  652. eResult = m_Impl->m_Client->Unsubscribe(in_subscriptionId, out_resultString, in_iTimeoutMs);
  653. }
  654. if (eResult)
  655. {
  656. if (m_Impl->m_wampEventCallbackMap.Contains(in_subscriptionId))
  657. m_Impl->m_wampEventCallbackMap.Remove(in_subscriptionId);
  658. }
  659. else if (!in_bSilenceLog)
  660. {
  661. UE_LOG(LogAkAudio, Log, TEXT("Unsubscription failed: %s"), *FString(UTF8_TO_TCHAR(out_resultString.c_str())));
  662. }
  663. out_result = FString(UTF8_TO_TCHAR(out_resultString.c_str()));
  664. }
  665. }
  666. else
  667. {
  668. if (m_Impl && m_Impl->m_pConnectionHandler)
  669. {
  670. m_Impl->m_pConnectionHandler->ResetReconnectionDelay();
  671. }
  672. }
  673. #endif
  674. return eResult;
  675. }
  676. bool FAkWaapiClient::Unsubscribe(const uint64_t& in_subscriptionId, TSharedPtr<FJsonObject>& out_result, int in_iTimeoutMs /*= 500*/, bool in_bSilenceLog /*= false*/)
  677. {
  678. bool eResult = false;
  679. #if AK_SUPPORT_WAAPI
  680. if (IsConnected())
  681. {
  682. FString out_resultString(TEXT(""));
  683. // Call the AK WAAPI method.
  684. eResult = Unsubscribe(in_subscriptionId, out_resultString, in_iTimeoutMs, in_bSilenceLog);
  685. if (!eResult)
  686. {
  687. // Deserialize a JSON object from the string
  688. TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(out_resultString);
  689. if ((!FJsonSerializer::Deserialize(Reader, out_result) || !out_result.IsValid()) && IsConnected())
  690. {
  691. UE_LOG(LogAkAudio, Log, TEXT("Unsubscribe: Output result -> unable to deserialize the Json object from the string : %s"), *out_resultString);
  692. }
  693. }
  694. }
  695. else
  696. {
  697. if (m_Impl && m_Impl->m_pConnectionHandler)
  698. {
  699. m_Impl->m_pConnectionHandler->ResetReconnectionDelay();
  700. }
  701. }
  702. #endif
  703. return eResult;
  704. }
  705. bool FAkWaapiClient::RemoveWampEventCallback(const uint64_t in_subscriptionId)
  706. {
  707. #if AK_SUPPORT_WAAPI
  708. if (m_Impl->m_wampEventCallbackMap.Contains(in_subscriptionId))
  709. {
  710. m_Impl->m_wampEventCallbackMap.Remove(in_subscriptionId);
  711. return true;
  712. }
  713. #endif
  714. return false;
  715. }
  716. bool FAkWaapiClient::Call(const char* in_uri, const FString& in_args, const FString& in_options, FString& out_result, int in_iTimeoutMs /*= 500*/, bool silenceLog /* = false*/)
  717. {
  718. bool eResult = false;
  719. #if AK_SUPPORT_WAAPI
  720. if (IsConnected())
  721. {
  722. if (!m_Impl->bIsConnectionClosing)
  723. {
  724. std::string out_resultString("");
  725. // Call the AK WAAPI method.
  726. if (LIKELY(g_AkWaapiClient->m_Impl->m_Client))
  727. {
  728. FScopeLock Lock(&m_Impl->ClientSection);
  729. eResult = m_Impl->m_Client->Call(in_uri, TCHAR_TO_UTF8(*in_args), TCHAR_TO_UTF8(*in_options), out_resultString, in_iTimeoutMs);
  730. }
  731. if (!eResult && !silenceLog)
  732. {
  733. UE_LOG(LogAkAudio, Log, TEXT("Call failed: %s"), *FString(UTF8_TO_TCHAR(out_resultString.c_str())));
  734. }
  735. out_result = FString(UTF8_TO_TCHAR(out_resultString.c_str()));
  736. }
  737. }
  738. else
  739. {
  740. if (m_Impl && m_Impl->m_pConnectionHandler)
  741. {
  742. m_Impl->m_pConnectionHandler->ResetReconnectionDelay();
  743. }
  744. }
  745. #endif
  746. return eResult;
  747. }
  748. bool FAkWaapiClient::Call(const char* in_uri, const TSharedRef<FJsonObject>& in_args, const TSharedRef<FJsonObject>& in_options,
  749. TSharedPtr<FJsonObject>& out_result, int in_iTimeoutMs /*= 500*/, bool silenceLog /*= false*/)
  750. {
  751. bool eResult = false;
  752. #if AK_SUPPORT_WAAPI
  753. FString in_argsString = TEXT("");
  754. FString in_optionsString = TEXT("");
  755. // Make sure the arguments are valid Json data.
  756. JsonObjectToString(in_args, in_argsString);
  757. // Make sure the options are valid Json data.
  758. JsonObjectToString(in_options, in_optionsString);
  759. FString out_resultString(TEXT(""));
  760. // Call the AK WAAPI method.
  761. eResult = Call(in_uri, in_argsString, in_optionsString, out_resultString, in_iTimeoutMs, silenceLog);
  762. // Deserialize a JSON object from the string.
  763. TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(out_resultString);
  764. if (!FJsonSerializer::Deserialize(Reader, out_result) || !out_result.IsValid())
  765. {
  766. if (!silenceLog && IsConnected())
  767. {
  768. UE_LOG(LogAkAudio, Log, TEXT("Output result -> unable to deserialize a JSON object from the string : %s"), *out_resultString);
  769. }
  770. }
  771. #endif
  772. return eResult;
  773. }
  774. bool FAkWaapiClient::Call(const char* inUri, const TArray<KeyValueArgs>& Values,
  775. TSharedPtr<FJsonObject>& outJsonResult)
  776. {
  777. TSharedRef<FJsonObject> Args = MakeShareable(new FJsonObject());
  778. for (const auto& Value : Values)
  779. {
  780. Args->SetStringField(Value.KeyArg, Value.ValueArg);
  781. }
  782. // Construct the options Json object;
  783. TSharedRef<FJsonObject> Options = MakeShareable(new FJsonObject());
  784. // Request infos/changes in Waapi Picker using WAAPI
  785. if (Call(inUri, Args, Options, outJsonResult))
  786. {
  787. return true;
  788. }
  789. UE_LOG(LogAkAudio, Log, TEXT("Call %hs failed."), inUri);
  790. return false;
  791. }
  792. FAkWaapiClient::FAkWaapiClient()
  793. : m_Impl(new FAkWaapiClientImpl)
  794. {
  795. }
  796. /** Sets in_outParentGUID to the object ID of a parent of object in_objectGUID of type in_strType. */
  797. void FAkWaapiClient::GetParentOfType(FGuid in_objectGUID, FGuid& in_outParentGUID, FString in_strType)
  798. {
  799. #if AK_SUPPORT_WAAPI
  800. if (g_AkWaapiClient == nullptr)
  801. return;
  802. /* Construct the relevant WAAPI json fields. */
  803. AkInt64 returnFlags = (AkInt64)WAAPIGetReturnOptionFlag::ID |
  804. (AkInt64)WAAPIGetReturnOptionFlag::TYPE;
  805. TArray<TSharedPtr<FJsonValue>> fromID;
  806. fromID.Add(MakeShareable(new FJsonValueString(in_objectGUID.ToString(EGuidFormats::DigitsWithHyphensInBraces))));
  807. TSharedPtr<FJsonObject> outJsonResult;
  808. if (!WAAPIGet(WAAPIGetFromOption::ID, fromID, returnFlags, outJsonResult))
  809. return;
  810. if (!outJsonResult->HasField(WAAPIStrings::RETURN))
  811. return;
  812. TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(WAAPIStrings::RETURN);
  813. if (returnJson.Num() <= 0)
  814. return;
  815. FString objectType;
  816. {
  817. TSharedPtr<FJsonObject> typeObject = returnJson[0]->AsObject();
  818. if (typeObject->HasField(WAAPIStrings::TYPE))
  819. objectType = typeObject->GetStringField(WAAPIStrings::TYPE);
  820. }
  821. in_outParentGUID = in_objectGUID;
  822. if (objectType.Equals(in_strType, ESearchCase::IgnoreCase))
  823. return;
  824. TSharedPtr<FJsonObject> select = MakeShareable(new FJsonObject());
  825. TArray<TSharedPtr<FJsonValue>> selectJsonArray;
  826. selectJsonArray.Add(MakeShareable(new FJsonValueString(WAAPIStrings::PARENT)));
  827. select->SetArrayField(WAAPIStrings::SELECT, selectJsonArray);
  828. TArray<TSharedPtr<FJsonValue>> transform;
  829. transform.Add(MakeShareable(new FJsonValueObject(select)));
  830. while (!objectType.Equals(in_strType, ESearchCase::IgnoreCase))
  831. {
  832. fromID.Empty();
  833. fromID.Add(MakeShareable(new FJsonValueString(in_outParentGUID.ToString(EGuidFormats::DigitsWithHyphensInBraces))));
  834. outJsonResult = MakeShareable(new FJsonObject());
  835. if (!WAAPIGet(WAAPIGetFromOption::ID, fromID, returnFlags, outJsonResult, WAAPIGetTransformOption::SELECT, selectJsonArray))
  836. break;
  837. if (!outJsonResult->HasField(WAAPIStrings::RETURN))
  838. continue;
  839. TArray<TSharedPtr<FJsonValue>> aReturnJson = outJsonResult->GetArrayField(WAAPIStrings::RETURN);
  840. TSharedPtr<FJsonObject> returnObject = aReturnJson[0]->AsObject();
  841. if (returnObject->HasField(WAAPIStrings::TYPE))
  842. objectType = returnObject->GetStringField(WAAPIStrings::TYPE);
  843. if (returnObject->HasField(WAAPIStrings::ID))
  844. FGuid::Parse(returnObject->GetStringField(WAAPIStrings::ID), in_outParentGUID);
  845. }
  846. #endif
  847. }
  848. bool FAkWaapiClient::IsProjectDirty()
  849. {
  850. #if AK_SUPPORT_WAAPI
  851. /* Ensure that Initialize() has been called! */
  852. AKASSERT(g_AkWaapiClient != nullptr);
  853. TArray<TSharedPtr<FJsonValue>> inFromItems;
  854. AkInt64 iReturnOptions = (uint64_t)WAAPIGetReturnOptionFlag::WORKUNIT_IS_DIRTY;
  855. TSharedPtr<FJsonObject> pJsonResult = MakeShareable(new FJsonObject());
  856. inFromItems.Add(MakeShareable(new FJsonValueString("WorkUnit")));
  857. WAAPIGet(WAAPIGetFromOption::OF_TYPE, inFromItems, iReturnOptions, pJsonResult);
  858. if (pJsonResult->HasField(WAAPIStrings::RETURN))
  859. {
  860. TArray<TSharedPtr<FJsonValue>> returnJson = pJsonResult->GetArrayField(WAAPIStrings::RETURN);
  861. FString workunitDirty = GetReturnOptionString(WAAPIGetReturnOptionFlag::WORKUNIT_IS_DIRTY);
  862. for (auto json : returnJson)
  863. {
  864. if (json->AsObject()->HasField(workunitDirty) && json->AsObject()->GetBoolField(workunitDirty))
  865. return true;
  866. }
  867. }
  868. #endif
  869. return false;
  870. }
  871. /**
  872. * WAAPI Structures
  873. */
  874. FString FAkWaapiClient::GetFromOptionString(WAAPIGetFromOption from)
  875. {
  876. switch (from)
  877. {
  878. case WAAPIGetFromOption::ID: return "id";
  879. case WAAPIGetFromOption::SEARCH: return "search";
  880. case WAAPIGetFromOption::PATH: return "path";
  881. case WAAPIGetFromOption::OF_TYPE: return "ofType";
  882. case WAAPIGetFromOption::QUERY: return "query";
  883. default: AKASSERT(false && "From option unhandled"); return "";
  884. }
  885. }
  886. FString FAkWaapiClient::GetTransformOptionString(WAAPIGetTransformOption transform)
  887. {
  888. switch (transform)
  889. {
  890. case WAAPIGetTransformOption::SELECT: return "select";
  891. case WAAPIGetTransformOption::RANGE: return "range";
  892. case WAAPIGetTransformOption::WHERE: return "where";
  893. case WAAPIGetTransformOption::NONE: return "";
  894. default: AKASSERT(false && "Transform option unhandled"); return "";
  895. }
  896. }
  897. FAkWaapiClient::WAAPIGetReturnOptionFlag FAkWaapiClient::GetReturnOptionFlagValue(int in_iFlagIndex)
  898. {
  899. return (WAAPIGetReturnOptionFlag)(AkInt64)pow(2, in_iFlagIndex);
  900. }
  901. FString FAkWaapiClient::GetReturnOptionString(WAAPIGetReturnOptionFlag returnOption)
  902. {
  903. switch (returnOption)
  904. {
  905. case WAAPIGetReturnOptionFlag::ID: return "id";
  906. case WAAPIGetReturnOptionFlag::NAME: return "name";
  907. case WAAPIGetReturnOptionFlag::NOTES: return "notes";
  908. case WAAPIGetReturnOptionFlag::TYPE: return "type";
  909. case WAAPIGetReturnOptionFlag::PATH: return "path";
  910. case WAAPIGetReturnOptionFlag::PARENT: return "parent";
  911. case WAAPIGetReturnOptionFlag::OWNER: return "owner";
  912. case WAAPIGetReturnOptionFlag::IS_PLAYABLE: return "isPlayable";
  913. case WAAPIGetReturnOptionFlag::SHORT_ID: return "shortId";
  914. case WAAPIGetReturnOptionFlag::CATEGORY: return "category";
  915. case WAAPIGetReturnOptionFlag::FILEPATH: return "filePath";
  916. case WAAPIGetReturnOptionFlag::WORKUNIT: return "workunit";
  917. case WAAPIGetReturnOptionFlag::CHILDREN_COUNT: return "childrenCount";
  918. case WAAPIGetReturnOptionFlag::MUSIC_TRANSITION_ROOT: return "music:transitionRoot";
  919. case WAAPIGetReturnOptionFlag::MUSIC_PLAYLIST_ROOT: return "music:playlistRoot";
  920. case WAAPIGetReturnOptionFlag::SOUND_ORIGINAL_WAV_FILE_PATH: return "sound:originalWavFilePath";
  921. case WAAPIGetReturnOptionFlag::SOUND_CONVERTED_WEM_FILE_PATH: return "sound:convertedWemFilePath";
  922. case WAAPIGetReturnOptionFlag::SOUNDBANK_BANK_FILE_PATH: return "soundbank:bnkFilePath";
  923. case WAAPIGetReturnOptionFlag::AUDIO_SOURCE_PLAYBACK_DURATION: return "audioSource:playbackDuration";
  924. case WAAPIGetReturnOptionFlag::AUDIO_SOURCE_MAX_DURATION_SOURCE: return "audioSource:maxDurationSource";
  925. case WAAPIGetReturnOptionFlag::AUDIO_SOURCE_TRIM_VALUES: return "audioSource:trimValues";
  926. case WAAPIGetReturnOptionFlag::WORKUNIT_IS_DEFAULT: return "workunit:isDefault";
  927. case WAAPIGetReturnOptionFlag::WORKUNIT_TYPE: return "workunit:type";
  928. case WAAPIGetReturnOptionFlag::WORKUNIT_IS_DIRTY: return "workunit:isDirty";
  929. default: AKASSERT(false && "Return option unhandled"); return "";
  930. }
  931. }
  932. /**
  933. * JSon Helpers
  934. */
  935. TSharedRef<FJsonObject> FAkWaapiClient::CreateWAAPIGetArgumentJson(WAAPIGetFromOption in_FromOption, TArray<TSharedPtr<FJsonValue>> in_FromItems,
  936. WAAPIGetTransformOption in_TransformOption /*= WAAPIGetTransformOption::NONE*/,
  937. TArray<TSharedPtr<FJsonValue>> in_TransformItems /*= TArray<TSharedPtr<FJsonValue>>()*/)
  938. {
  939. TSharedRef<FJsonObject> args = MakeShareable(new FJsonObject());
  940. TSharedPtr<FJsonObject> from = MakeShareable(new FJsonObject());
  941. from->SetArrayField(GetFromOptionString(in_FromOption), in_FromItems);
  942. args->SetObjectField(FAkWaapiClient::WAAPIStrings::FROM, from);
  943. if (in_TransformOption != WAAPIGetTransformOption::NONE && in_TransformItems.Num() > 0)
  944. {
  945. TArray<TSharedPtr<FJsonValue>> transformArgArray;
  946. TSharedPtr<FJsonObject> transformObjectArg = MakeShareable(new FJsonObject());
  947. transformObjectArg->SetArrayField(GetTransformOptionString(in_TransformOption), in_TransformItems);
  948. transformArgArray.Add(MakeShareable(new FJsonValueObject(transformObjectArg)));
  949. args->SetArrayField(FAkWaapiClient::WAAPIStrings::TRANSFORM, transformArgArray);
  950. }
  951. return args;
  952. }
  953. TSharedRef<FJsonObject> FAkWaapiClient::CreateWAAPIGetReturnOptionsJson(AkInt64 ReturnOptions)
  954. {
  955. TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
  956. TArray<TSharedPtr<FJsonValue>> StructJsonArray;
  957. for (int bitIndex = 0; bitIndex < (int)WAAPIGetReturnOptionFlag::NUM_FLAGS; ++bitIndex)
  958. {
  959. WAAPIGetReturnOptionFlag returnOption = GetReturnOptionFlagValue(bitIndex);
  960. if ((ReturnOptions & (AkInt64)returnOption) != 0)
  961. {
  962. StructJsonArray.Add(MakeShareable(new FJsonValueString(GetReturnOptionString(returnOption))));
  963. }
  964. }
  965. options->SetArrayField(FAkWaapiClient::WAAPIStrings::RETURN, StructJsonArray);
  966. return options;
  967. }
  968. /**
  969. * WAAPI Helpers
  970. */
  971. bool FAkWaapiClient::WAAPIGet(WAAPIGetFromOption inFromField,
  972. TArray<TSharedPtr<FJsonValue>> inFromItems,
  973. AkInt64 inReturnOptionsFlags,
  974. TSharedPtr<FJsonObject>& outJsonResult,
  975. WAAPIGetTransformOption inTransformField /*= WAAPIGetTransformOption::NONE*/,
  976. TArray<TSharedPtr<FJsonValue>> inTransformItems /*= TArray<TSharedPtr<FJsonValue>>()*/,
  977. bool in_bSilenceLog /*= false*/)
  978. {
  979. #if AK_SUPPORT_WAAPI
  980. TSharedRef<FJsonObject> getArgsJson = CreateWAAPIGetArgumentJson(inFromField, inFromItems, inTransformField, inTransformItems);
  981. TSharedRef<FJsonObject> returnOptionsJson = CreateWAAPIGetReturnOptionsJson(inReturnOptionsFlags);
  982. if (g_AkWaapiClient != nullptr && g_AkWaapiClient->IsConnected())
  983. {
  984. if (g_AkWaapiClient->Call(ak::wwise::core::object::get, getArgsJson, returnOptionsJson, outJsonResult, 500, in_bSilenceLog))
  985. return true;
  986. else if (!in_bSilenceLog)
  987. UE_LOG(LogAkAudio, Log, TEXT("Call to ak.wwise.core.object.get Failed"));
  988. }
  989. #endif
  990. return false;
  991. }
  992. bool FAkWaapiClient::GetGUIDForObjectOfTypeWithName(FGuid& io_GUID, const FString& in_sTypeName, const FString& in_sName)
  993. {
  994. #if AK_SUPPORT_WAAPI
  995. TArray<TSharedPtr<FJsonValue>> nameArray;
  996. nameArray.Add(MakeShareable(new FJsonValueString(in_sName)));
  997. TSharedPtr<FJsonObject> outJsonResult = MakeShareable(new FJsonObject());
  998. AkInt64 returnOptionFlags = (AkInt64)WAAPIGetReturnOptionFlag::ID | (AkInt64)WAAPIGetReturnOptionFlag::NAME | (AkInt64)WAAPIGetReturnOptionFlag::TYPE;
  999. if (WAAPIGet(WAAPIGetFromOption::SEARCH, nameArray, returnOptionFlags, outJsonResult))
  1000. {
  1001. if (outJsonResult->HasField(WAAPIStrings::RETURN))
  1002. {
  1003. TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(WAAPIStrings::RETURN);
  1004. for (auto json : returnJson)
  1005. {
  1006. auto jsonObj = json->AsObject();
  1007. auto name = jsonObj->GetStringField(WAAPIStrings::NAME);
  1008. auto typeName = jsonObj->GetStringField(WAAPIStrings::TYPE);
  1009. if (name == in_sName && typeName.Equals(in_sTypeName, ESearchCase::IgnoreCase))
  1010. {
  1011. auto iD = jsonObj->GetStringField(WAAPIStrings::ID);
  1012. FGuid::Parse(iD, io_GUID);
  1013. return true;
  1014. }
  1015. }
  1016. }
  1017. }
  1018. #endif
  1019. return false;
  1020. }
  1021. void FAkWaapiClient::SaveProject()
  1022. {
  1023. #if AK_SUPPORT_WAAPI
  1024. TSharedRef<FJsonObject> argsJson = MakeShareable(new FJsonObject());
  1025. TSharedRef<FJsonObject> optionsJson = MakeShareable(new FJsonObject());
  1026. TSharedPtr<FJsonObject> outputJson = MakeShareable(new FJsonObject());
  1027. if (g_AkWaapiClient->IsConnected())
  1028. {
  1029. g_AkWaapiClient->Call(ak::wwise::core::project::save, argsJson, optionsJson, outputJson);
  1030. }
  1031. #endif
  1032. }
  1033. const FString FAkWaapiClient::WAAPIStrings::BACK_SLASH = TEXT("\\");
  1034. const FString FAkWaapiClient::WAAPIStrings::ID = TEXT("id");
  1035. const FString FAkWaapiClient::WAAPIStrings::RETURN = TEXT("return");
  1036. const FString FAkWaapiClient::WAAPIStrings::PATH = TEXT("path");
  1037. const FString FAkWaapiClient::WAAPIStrings::FILEPATH = TEXT("filePath");
  1038. const FString FAkWaapiClient::WAAPIStrings::FROM = TEXT("from");
  1039. const FString FAkWaapiClient::WAAPIStrings::NAME = TEXT("name");
  1040. const FString FAkWaapiClient::WAAPIStrings::TYPE = TEXT("type");
  1041. const FString FAkWaapiClient::WAAPIStrings::CHILDREN = TEXT("children");
  1042. const FString FAkWaapiClient::WAAPIStrings::CHILDREN_COUNT = TEXT("childrenCount");
  1043. const FString FAkWaapiClient::WAAPIStrings::ANCESTORS = TEXT("ancestors");
  1044. const FString FAkWaapiClient::WAAPIStrings::DESCENDANTS = TEXT("descendants");
  1045. const FString FAkWaapiClient::WAAPIStrings::WOKUNIT_TYPE = TEXT("workunit:type");
  1046. const FString FAkWaapiClient::WAAPIStrings::FOLDER = TEXT("Folder");
  1047. const FString FAkWaapiClient::WAAPIStrings::PHYSICAL_FOLDER = TEXT("PhysicalFolder");
  1048. const FString FAkWaapiClient::WAAPIStrings::SEARCH = TEXT("search");
  1049. const FString FAkWaapiClient::WAAPIStrings::PARENT = TEXT("parent");
  1050. const FString FAkWaapiClient::WAAPIStrings::SELECT = TEXT("select");
  1051. const FString FAkWaapiClient::WAAPIStrings::TRANSFORM = TEXT("transform");
  1052. const FString FAkWaapiClient::WAAPIStrings::OBJECT = TEXT("object");
  1053. const FString FAkWaapiClient::WAAPIStrings::OBJECTS = TEXT("objects");
  1054. const FString FAkWaapiClient::WAAPIStrings::VALUE = TEXT("value");
  1055. const FString FAkWaapiClient::WAAPIStrings::COMMAND = TEXT("command");
  1056. const FString FAkWaapiClient::WAAPIStrings::TRANSPORT = TEXT("transport");
  1057. const FString FAkWaapiClient::WAAPIStrings::ACTION = TEXT("action");
  1058. const FString FAkWaapiClient::WAAPIStrings::PLAY = TEXT("play");
  1059. const FString FAkWaapiClient::WAAPIStrings::STOP = TEXT("stop");
  1060. const FString FAkWaapiClient::WAAPIStrings::STOPPED = TEXT("stopped");
  1061. const FString FAkWaapiClient::WAAPIStrings::DISPLAY_NAME = TEXT("displayName");
  1062. const FString FAkWaapiClient::WAAPIStrings::DELETE_ITEMS = TEXT("Delete Items");
  1063. const FString FAkWaapiClient::WAAPIStrings::DRAG_DROP_ITEMS = TEXT("Drag Drop Items");
  1064. const FString FAkWaapiClient::WAAPIStrings::UNDO = TEXT("Undo");
  1065. const FString FAkWaapiClient::WAAPIStrings::REDO = TEXT("Redo");
  1066. const FString FAkWaapiClient::WAAPIStrings::STATE = TEXT("state");
  1067. const FString FAkWaapiClient::WAAPIStrings::OF_TYPE = TEXT("ofType");
  1068. const FString FAkWaapiClient::WAAPIStrings::PROJECT = TEXT("Project");
  1069. const FString FAkWaapiClient::WAAPIStrings::PROPERTY = TEXT("property");
  1070. const FString FAkWaapiClient::WAAPIStrings::VOLUME = TEXT("Volume");
  1071. const FString FAkWaapiClient::WAAPIStrings::FIND_IN_PROJECT_EXPLORER = TEXT("FindInProjectExplorerSelectionChannel1");
  1072. const FString FAkWaapiClient::WAAPIStrings::TRIMMED_DURATION = TEXT("trimmedDuration");
  1073. const FString FAkWaapiClient::WwiseTypeStrings::SOUND = TEXT("Sound");
  1074. const FString FAkWaapiClient::WwiseTypeStrings::WORKUNIT = TEXT("WorkUnit");
  1075. const FString FAkWaapiClient::AudioPeaksStrings::Args::OBJECT = TEXT("object");
  1076. const FString FAkWaapiClient::AudioPeaksStrings::Args::NUM_PEAKS = TEXT("numPeaks");
  1077. const FString FAkWaapiClient::AudioPeaksStrings::Args::TIME_FROM = TEXT("timeFrom");
  1078. const FString FAkWaapiClient::AudioPeaksStrings::Args::TIME_TO = TEXT("timeTo");
  1079. const FString FAkWaapiClient::AudioPeaksStrings::Args::CROSS_CHANNEL_PEAKS = TEXT("getCrossChannelPeaks");
  1080. const FString FAkWaapiClient::AudioPeaksStrings::Results::PEAKS_BINARY = TEXT("peaksBinaryStrings");
  1081. const FString FAkWaapiClient::AudioPeaksStrings::Results::MAX_ABS_VALUE = TEXT("maxAbsValue");
  1082. const FString FAkWaapiClient::AudioPeaksStrings::Results::PEAKS_ARRAY_LENGTH = TEXT("peaksArrayLength");
  1083. const FString FAkWaapiClient::AudioPeaksStrings::Results::PEAKS_DATA_SIZE = TEXT("peaksDataSize");
  1084. const FString FAkWaapiClient::PropertyChangedStrings::RequiredOptions::OBJECT = TEXT("object");
  1085. const FString FAkWaapiClient::PropertyChangedStrings::RequiredOptions::PROPERTY = TEXT("property");
  1086. const FString FAkWaapiClient::PropertyChangedStrings::OptionalOptions::RETURN = TEXT("return");
  1087. const FString FAkWaapiClient::PropertyChangedStrings::OptionalOptions::PLATFORM = TEXT("platform");
  1088. const FString FAkWaapiClient::AudioSourceProperties::TRIM_END = TEXT("TrimEnd");
  1089. const FString FAkWaapiClient::AudioSourceProperties::TRIM_BEGIN = TEXT("TrimBegin");
  1090. const FString FAkWaapiClient::PlaybackDurationStrings::MIN = TEXT("playbackDurationMin");
  1091. const FString FAkWaapiClient::PlaybackDurationStrings::MAX = TEXT("playbackDurationMax");
  1092. const FString FAkWaapiClient::PlaybackDurationStrings::TYPE = TEXT("playbackDurationType");
  1093. const FString FAkWaapiClient::TrimValuesStrings::TRIM_BEGIN = TEXT("trimBegin");
  1094. const FString FAkWaapiClient::TrimValuesStrings::TRIM_END = TEXT("trimEnd");
  1095. // end