AkWaapiClient.cpp 44 KB

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