AudioSystem.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AudioSystem.h>
  9. #include <AudioProxy.h>
  10. #include <SoundCVars.h>
  11. #include <AudioSystem_Traits_Platform.h>
  12. #include <AzCore/PlatformDef.h>
  13. #include <AzCore/Console/ILogger.h>
  14. #include <AzCore/Debug/Profiler.h>
  15. #include <AzCore/std/bind/bind.h>
  16. #include <AzCore/StringFunc/StringFunc.h>
  17. namespace Audio
  18. {
  19. static constexpr const char AudioControlsBasePath[]{ "libs/gameaudio/" };
  20. // Save off the threadId of the "Main Thread" that was used to connect EBuses.
  21. AZStd::thread_id g_mainThreadId;
  22. AZStd::thread_id g_audioThreadId;
  23. ///////////////////////////////////////////////////////////////////////////////////////////////////
  24. // CAudioThread
  25. ///////////////////////////////////////////////////////////////////////////////////////////////////
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. CAudioThread::~CAudioThread()
  28. {
  29. Deactivate();
  30. }
  31. ///////////////////////////////////////////////////////////////////////////////////////////////////
  32. void CAudioThread::Run()
  33. {
  34. AZ_Assert(m_audioSystem, "Audio Thread has no Audio System to run!\n");
  35. g_audioThreadId = AZStd::this_thread::get_id();
  36. m_running = true;
  37. while (m_running)
  38. {
  39. m_audioSystem->InternalUpdate();
  40. }
  41. }
  42. ///////////////////////////////////////////////////////////////////////////////////////////////////
  43. void CAudioThread::Activate(CAudioSystem* const audioSystem)
  44. {
  45. m_audioSystem = audioSystem;
  46. AZStd::thread_desc threadDesc;
  47. threadDesc.m_name = "Audio Thread";
  48. threadDesc.m_cpuId = AZ_TRAIT_AUDIOSYSTEM_AUDIO_THREAD_AFFINITY;
  49. auto threadFunc = AZStd::bind(&CAudioThread::Run, this);
  50. m_thread = AZStd::thread(threadDesc, threadFunc);
  51. }
  52. ///////////////////////////////////////////////////////////////////////////////////////////////////
  53. void CAudioThread::Deactivate()
  54. {
  55. if (m_running)
  56. {
  57. m_running = false;
  58. m_thread.join();
  59. }
  60. }
  61. ///////////////////////////////////////////////////////////////////////////////////////////////////
  62. // CAudioSystem
  63. ///////////////////////////////////////////////////////////////////////////////////////////////////
  64. ///////////////////////////////////////////////////////////////////////////////////////////////////
  65. CAudioSystem::CAudioSystem()
  66. : m_bSystemInitialized(false)
  67. {
  68. g_mainThreadId = AZStd::this_thread::get_id();
  69. m_apAudioProxies.reserve(Audio::CVars::s_AudioObjectPoolSize);
  70. m_apAudioProxiesToBeFreed.reserve(16);
  71. m_controlsPath.assign(Audio::AudioControlsBasePath);
  72. #if !defined(AUDIO_RELEASE)
  73. AzFramework::DebugDisplayEventBus::Handler::BusConnect();
  74. #endif // !AUDIO_RELEASE
  75. }
  76. ///////////////////////////////////////////////////////////////////////////////////////////////////
  77. CAudioSystem::~CAudioSystem()
  78. {
  79. #if !defined(AUDIO_RELEASE)
  80. AzFramework::DebugDisplayEventBus::Handler::BusDisconnect();
  81. #endif // !AUDIO_RELEASE
  82. }
  83. ///////////////////////////////////////////////////////////////////////////////////////////////////
  84. void CAudioSystem::PushRequest(AudioRequestVariant&& request)
  85. {
  86. AZStd::scoped_lock lock(m_pendingRequestsMutex);
  87. m_pendingRequestsQueue.push_back(AZStd::move(request));
  88. }
  89. ///////////////////////////////////////////////////////////////////////////////////////////////////
  90. void CAudioSystem::PushRequests(AudioRequestsQueue& requests)
  91. {
  92. AZStd::scoped_lock lock(m_pendingRequestsMutex);
  93. for (auto& request : requests)
  94. {
  95. m_pendingRequestsQueue.push_back(AZStd::move(request));
  96. }
  97. }
  98. ///////////////////////////////////////////////////////////////////////////////////////////////////
  99. void CAudioSystem::PushRequestBlocking(AudioRequestVariant&& request)
  100. {
  101. // Add this request to be processed immediately.
  102. // Release the m_processingEvent so that when the request is finished the audio thread doesn't
  103. // block through it's normal time slice and can immediately re-enter the run loop to process more.
  104. // Acquire the m_mainEvent semaphore to block the main thread.
  105. // This helps when there's a longer queue of blocking requests so that the back-and-forth between
  106. // threads is minimized.
  107. {
  108. AZStd::scoped_lock lock(m_blockingRequestsMutex);
  109. m_blockingRequestsQueue.push_back(AZStd::move(request));
  110. }
  111. m_processingEvent.release();
  112. m_mainEvent.acquire();
  113. }
  114. ///////////////////////////////////////////////////////////////////////////////////////////////////
  115. void CAudioSystem::PushCallback(AudioRequestVariant&& callback)
  116. {
  117. AZStd::scoped_lock lock(m_pendingCallbacksMutex);
  118. m_pendingCallbacksQueue.push_back(AZStd::move(callback));
  119. }
  120. ///////////////////////////////////////////////////////////////////////////////////////////////////
  121. void CAudioSystem::ExternalUpdate()
  122. {
  123. // Main Thread!
  124. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ExternalUpdate - called from non-Main thread!");
  125. {
  126. AudioRequestsQueue callbacksToProcess{};
  127. {
  128. AZStd::scoped_lock lock(m_pendingCallbacksMutex);
  129. callbacksToProcess = AZStd::move(m_pendingCallbacksQueue);
  130. }
  131. while (!callbacksToProcess.empty())
  132. {
  133. AudioRequestVariant& callbackVariant(callbacksToProcess.front());
  134. AZStd::visit(
  135. [](auto&& request)
  136. {
  137. if (request.m_callback)
  138. {
  139. request.m_callback(request);
  140. }
  141. },
  142. callbackVariant);
  143. callbacksToProcess.pop_front();
  144. }
  145. }
  146. // Other notifications to be sent out...
  147. AudioTriggerNotificationBus::ExecuteQueuedEvents();
  148. // Free any Audio Proxies that are queued up for deletion...
  149. for (auto audioProxy : m_apAudioProxiesToBeFreed)
  150. {
  151. azdestroy(audioProxy, Audio::AudioSystemAllocator);
  152. }
  153. m_apAudioProxiesToBeFreed.clear();
  154. }
  155. ///////////////////////////////////////////////////////////////////////////////////////////////////
  156. void CAudioSystem::InternalUpdate()
  157. {
  158. // Audio Thread!
  159. AZ_Assert(g_audioThreadId == AZStd::this_thread::get_id(), "AudioSystem::InternalUpdate - called from non-Audio thread!");
  160. AZ_PROFILE_FUNCTION(Audio);
  161. auto startUpdateTime = AZStd::chrono::steady_clock::now(); // stamp the start time
  162. // Process a single blocking request, if any, and release the semaphore the main thread is trying to acquire.
  163. // This ensures that main thread will become unblocked quickly.
  164. // If blocking requests were processed, can skip processing of normal requests and skip having
  165. // the audio thread block through the rest of its update period.
  166. bool handleBlockingRequest = false;
  167. AudioRequestVariant blockingRequest;
  168. {
  169. AZStd::scoped_lock lock(m_blockingRequestsMutex);
  170. handleBlockingRequest = !m_blockingRequestsQueue.empty();
  171. if (handleBlockingRequest)
  172. {
  173. blockingRequest = AZStd::move(m_blockingRequestsQueue.front());
  174. m_blockingRequestsQueue.pop_front();
  175. }
  176. }
  177. if (handleBlockingRequest)
  178. {
  179. m_oATL.ProcessRequest(AZStd::move(blockingRequest));
  180. m_mainEvent.release();
  181. }
  182. if (!handleBlockingRequest)
  183. {
  184. // Normal request processing: lock and swap the pending requests queue
  185. // so that the queue can be opened for new requests while the current set
  186. // of requests can be processed.
  187. AudioRequestsQueue requestsToProcess{};
  188. {
  189. AZStd::scoped_lock lock(m_pendingRequestsMutex);
  190. requestsToProcess = AZStd::move(m_pendingRequestsQueue);
  191. }
  192. while (!requestsToProcess.empty())
  193. {
  194. // Normal request...
  195. AudioRequestVariant& request(requestsToProcess.front());
  196. m_oATL.ProcessRequest(AZStd::move(request));
  197. requestsToProcess.pop_front();
  198. }
  199. }
  200. m_oATL.Update();
  201. if (!handleBlockingRequest)
  202. {
  203. auto endUpdateTime = AZStd::chrono::steady_clock::now(); // stamp the end time
  204. auto elapsedUpdateTime = AZStd::chrono::duration_cast<duration_ms>(endUpdateTime - startUpdateTime);
  205. if (elapsedUpdateTime < m_targetUpdatePeriod)
  206. {
  207. AZ_PROFILE_SCOPE(Audio, "Wait Remaining Time in Update Period");
  208. m_processingEvent.try_acquire_for(m_targetUpdatePeriod - elapsedUpdateTime);
  209. }
  210. }
  211. }
  212. ///////////////////////////////////////////////////////////////////////////////////////////////////
  213. bool CAudioSystem::Initialize()
  214. {
  215. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Initialize - called from a non-Main thread!");
  216. if (!m_bSystemInitialized)
  217. {
  218. m_audioSystemThread.Deactivate();
  219. m_oATL.Initialize();
  220. m_audioSystemThread.Activate(this);
  221. for (AZ::u64 i = 0; i < Audio::CVars::s_AudioObjectPoolSize; ++i)
  222. {
  223. auto audioProxy = azcreate(CAudioProxy, (), Audio::AudioSystemAllocator);
  224. m_apAudioProxies.push_back(audioProxy);
  225. }
  226. m_bSystemInitialized = true;
  227. }
  228. return m_bSystemInitialized;
  229. }
  230. ///////////////////////////////////////////////////////////////////////////////////////////////////
  231. void CAudioSystem::Release()
  232. {
  233. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Release - called from a non-Main thread!");
  234. // Mark the system as uninitialized before we destroy the audio proxies so that we can avoid
  235. // recycling them on system shutdown.
  236. m_bSystemInitialized = false;
  237. for (auto audioProxy : m_apAudioProxies)
  238. {
  239. azdestroy(audioProxy, Audio::AudioSystemAllocator);
  240. }
  241. for (auto audioProxy : m_apAudioProxiesToBeFreed)
  242. {
  243. azdestroy(audioProxy, Audio::AudioSystemAllocator);
  244. }
  245. m_apAudioProxies.clear();
  246. m_apAudioProxiesToBeFreed.clear();
  247. // Release the audio implementation...
  248. Audio::SystemRequest::Shutdown shutdownRequest;
  249. AZ::Interface<IAudioSystem>::Get()->PushRequestBlocking(AZStd::move(shutdownRequest));
  250. m_audioSystemThread.Deactivate();
  251. m_oATL.ShutDown();
  252. }
  253. ///////////////////////////////////////////////////////////////////////////////////////////////////
  254. TAudioControlID CAudioSystem::GetAudioTriggerID(const char* const sAudioTriggerName) const
  255. {
  256. return m_oATL.GetAudioTriggerID(sAudioTriggerName);
  257. }
  258. ///////////////////////////////////////////////////////////////////////////////////////////////////
  259. TAudioControlID CAudioSystem::GetAudioRtpcID(const char* const sAudioRtpcName) const
  260. {
  261. return m_oATL.GetAudioRtpcID(sAudioRtpcName);
  262. }
  263. ///////////////////////////////////////////////////////////////////////////////////////////////////
  264. TAudioControlID CAudioSystem::GetAudioSwitchID(const char* const sAudioStateName) const
  265. {
  266. return m_oATL.GetAudioSwitchID(sAudioStateName);
  267. }
  268. ///////////////////////////////////////////////////////////////////////////////////////////////////
  269. TAudioSwitchStateID CAudioSystem::GetAudioSwitchStateID(const TAudioControlID nSwitchID, const char* const sAudioSwitchStateName) const
  270. {
  271. return m_oATL.GetAudioSwitchStateID(nSwitchID, sAudioSwitchStateName);
  272. }
  273. ///////////////////////////////////////////////////////////////////////////////////////////////////
  274. TAudioPreloadRequestID CAudioSystem::GetAudioPreloadRequestID(const char* const sAudioPreloadRequestName) const
  275. {
  276. return m_oATL.GetAudioPreloadRequestID(sAudioPreloadRequestName);
  277. }
  278. ///////////////////////////////////////////////////////////////////////////////////////////////////
  279. TAudioEnvironmentID CAudioSystem::GetAudioEnvironmentID(const char* const sAudioEnvironmentName) const
  280. {
  281. return m_oATL.GetAudioEnvironmentID(sAudioEnvironmentName);
  282. }
  283. ///////////////////////////////////////////////////////////////////////////////////////////////////
  284. bool CAudioSystem::ReserveAudioListenerID(TAudioObjectID& rAudioObjectID)
  285. {
  286. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReserveAudioListenerID - called from a non-Main thread!");
  287. return m_oATL.ReserveAudioListenerID(rAudioObjectID);
  288. }
  289. ///////////////////////////////////////////////////////////////////////////////////////////////////
  290. bool CAudioSystem::ReleaseAudioListenerID(TAudioObjectID const nAudioObjectID)
  291. {
  292. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReleaseAudioListenerID - called from a non-Main thread!");
  293. return m_oATL.ReleaseAudioListenerID(nAudioObjectID);
  294. }
  295. ///////////////////////////////////////////////////////////////////////////////////////////////////
  296. bool CAudioSystem::SetAudioListenerOverrideID(const TAudioObjectID nAudioObjectID)
  297. {
  298. return m_oATL.SetAudioListenerOverrideID(nAudioObjectID);
  299. }
  300. ///////////////////////////////////////////////////////////////////////////////////////////////////
  301. const char* CAudioSystem::GetControlsPath() const
  302. {
  303. return m_controlsPath.c_str();
  304. }
  305. ///////////////////////////////////////////////////////////////////////////////////////////////////
  306. void CAudioSystem::UpdateControlsPath()
  307. {
  308. AZStd::string controlsPath{ Audio::AudioControlsBasePath };
  309. const AZStd::string& subPath = m_oATL.GetControlsImplSubPath();
  310. if (!subPath.empty())
  311. {
  312. controlsPath.append(subPath);
  313. }
  314. if (AZ::StringFunc::RelativePath::Normalize(controlsPath))
  315. {
  316. m_controlsPath = controlsPath;
  317. }
  318. else
  319. {
  320. AZLOG_ERROR("AudioSystem::UpdateControlsPath - failed to normalize the controls path '%s'!", controlsPath.c_str());
  321. }
  322. }
  323. ///////////////////////////////////////////////////////////////////////////////////////////////////
  324. void CAudioSystem::RefreshAudioSystem([[maybe_unused]] const char* const levelName)
  325. {
  326. #if !defined(AUDIO_RELEASE)
  327. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RefreshAudioSystem - called from a non-Main thread!");
  328. // Get the controls path and a level-specific preload Id first.
  329. // This will be passed with the request so that it doesn't have to lookup this data
  330. // and punch the AudioSystemRequestBus from the Audio Thread.
  331. const char* audioControlsPath = GetControlsPath();
  332. Audio::TAudioPreloadRequestID levelPreloadId = INVALID_AUDIO_PRELOAD_REQUEST_ID;
  333. if (levelName && levelName[0] != '\0')
  334. {
  335. levelPreloadId = GetAudioPreloadRequestID(levelName);
  336. }
  337. Audio::SystemRequest::ReloadAll reloadRequest;
  338. reloadRequest.m_controlsPath = audioControlsPath;
  339. reloadRequest.m_levelName = levelName;
  340. reloadRequest.m_levelPreloadId = levelPreloadId;
  341. AZ::Interface<IAudioSystem>::Get()->PushRequestBlocking(AZStd::move(reloadRequest));
  342. #endif // !AUDIO_RELEASE
  343. }
  344. ///////////////////////////////////////////////////////////////////////////////////////////////////
  345. IAudioProxy* CAudioSystem::GetAudioProxy()
  346. {
  347. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetAudioProxy - called from a non-Main thread!");
  348. CAudioProxy* audioProxy = nullptr;
  349. if (!m_apAudioProxies.empty())
  350. {
  351. audioProxy = m_apAudioProxies.back();
  352. m_apAudioProxies.pop_back();
  353. }
  354. else
  355. {
  356. audioProxy = azcreate(CAudioProxy, (), Audio::AudioSystemAllocator);
  357. AZ_Assert(audioProxy != nullptr, "AudioSystem::GetAudioProxy - failed to create new AudioProxy instance!");
  358. }
  359. return static_cast<IAudioProxy*>(audioProxy);
  360. }
  361. ///////////////////////////////////////////////////////////////////////////////////////////////////
  362. void CAudioSystem::RecycleAudioProxy(IAudioProxy* const audioProxyI)
  363. {
  364. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RecycleAudioProxy - called from a non-Main thread!");
  365. auto const audioProxy = static_cast<CAudioProxy*>(audioProxyI);
  366. // If the system is shutting down, don't recycle the audio proxies.
  367. if (!m_bSystemInitialized)
  368. {
  369. return;
  370. }
  371. if (AZStd::find(m_apAudioProxiesToBeFreed.begin(), m_apAudioProxiesToBeFreed.end(), audioProxy) != m_apAudioProxiesToBeFreed.end()
  372. || AZStd::find(m_apAudioProxies.begin(), m_apAudioProxies.end(), audioProxy) != m_apAudioProxies.end())
  373. {
  374. AZ_Warning("AudioSystem", false, "Attempting to free an already freed audio proxy");
  375. return;
  376. }
  377. if (m_apAudioProxies.size() < Audio::CVars::s_AudioObjectPoolSize)
  378. {
  379. m_apAudioProxies.push_back(audioProxy);
  380. }
  381. else
  382. {
  383. m_apAudioProxiesToBeFreed.push_back(audioProxy);
  384. }
  385. }
  386. ///////////////////////////////////////////////////////////////////////////////////////////////////
  387. TAudioSourceId CAudioSystem::CreateAudioSource(const SAudioInputConfig& sourceConfig)
  388. {
  389. return m_oATL.CreateAudioSource(sourceConfig);
  390. }
  391. ///////////////////////////////////////////////////////////////////////////////////////////////////
  392. void CAudioSystem::DestroyAudioSource(TAudioSourceId sourceId)
  393. {
  394. m_oATL.DestroyAudioSource(sourceId);
  395. }
  396. ///////////////////////////////////////////////////////////////////////////////////////////////////
  397. #if !defined(AUDIO_RELEASE)
  398. void CAudioSystem::DrawGlobalDebugInfo()
  399. {
  400. AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::DrawGlobalDebugInfo - called from non-Main thread!");
  401. if (CVars::s_debugDrawOptions.GetRawFlags() != 0)
  402. {
  403. Audio::SystemRequest::DrawDebug drawDebug;
  404. AZ::Interface<IAudioSystem>::Get()->PushRequestBlocking(AZStd::move(drawDebug));
  405. }
  406. }
  407. #endif // !AUDIO_RELEASE
  408. } // namespace Audio