fec77632ef
* Fix issues with seedlist for AutomatedTesting Fixes error reporting so it will show the file hint in the tool. Removes any missing assets from the .seed file. Remove an unnecessary dependency from AutomatedTesting dependencies file. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Make ArchiveComponent use AZ::IO::IArchive Initial changes that will get the sychronous calls in ArchiveComponent to use IArchive interface rather than external zip/7z tools. Some of the asynchronous api are still in place, anything that wasn't being used has been removed for now. This may change later if we move towards all the api being asynchronous. Until then, we can't remove the reliance upon the external archive tools completely. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Updates AZStd::thread constructors Adds a variadic constructor which forwards args to the functor. Because of our thread_desc extension, there was confusion on the arugments, so the args were reordered to take the thread_desc first, before the functor and args. Also the thread_desc is taken as reference rather than by pointer. Update callsites to account for this change. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Async operation of ArchiveComponent api This sets up the ArchiveComponent to operate asynchronously. It uses promise/future to transfer results to caller. This is still broken, there's a few things that need to get fixed up, but this is a good checkpoint for the work as it solidifies the api, cleans up a bunch of unused code, and compiles. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Removes the platform-specific ArchiveComponen These are no longer needed, as they control the direct interaction with host OS tools like 7za.exe or /bin/zip. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Removes the platform-specific files from cmake Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Removes the 7za.exe (and legal notice) This tool is no longer needed in the repo. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fixes usage of IArchive::GetFullPath() This changed to return a PathView, updated to reflect that. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix promises and threads Make sure promises are only set exactly once. This meant reworking some of the initial error checking. Detach threads when created. Adds [[nodiscard]] to the functions that return a future. Since threads are detached, the future is the main way to get communication from the thread. Clean up interface, add comments. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * More edits to thread creation Changes to thread construction to account for parameter change. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix some remaining issues with ArchiveComponent Put created threads inside a container, then join them at Deactivate. Fix asset bundler case when injecting a file with no working directory. Fix thread constructor that applies args to a function. Fix lambdas to take string args by value rather than reference. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fixes some remaining bugs in ArchiveComponent Open archive as read-only during extract & list operations. Fixes paths issues. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix initialize of opaque thread handle in thread_UnixLike.h Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed unused variable in AssetBundleComponent.cpp to fix compiler warning Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fix some issues with archives File paths in the CDR and the local headers need to match, but there were issues with path separators and case that made it possible to get invalid headers errors in some archives. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Adds some new ArchiveComponent unit tests Adds new tests for extraction of archive and adding files from a file list to an archive. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix file data offset issues when opening archives When opening an INestedArchive it would run through the CDR headers to create file entries in the zip cache. The offsets to the compressed data were being calculated incorrectly because they were using the CDR headers rather than jumping to the local file headers and getting offsets from those sizes. Removed and refactored some archive validation flow and zip cache factory init methods to either init default or init w/ additional validation checks. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Addresses PR feedback Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Address more points of feedback in PR Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Address additional PR feedback Fixes up some error checks and uses of strings vs paths. Enable archive component tests on Linux so we can see if they will pass. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Address PR feedback Change the INestedArchive interface to list files as AZ::IO::Path. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Disabling the ArchiveComponent tests on Linux They failed so we will revisit them to attempt a fix. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Rename a member variable to be more accurate Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Address feedback on PR Bump version of Archive Components for serialize context. Improve error messages during archive open and validation. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Revert recent changes Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
720 lines
28 KiB
C++
720 lines
28 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
|
|
#include <AudioSystem.h>
|
|
#include <AudioProxy.h>
|
|
#include <SoundCVars.h>
|
|
#include <AudioSystem_Traits_Platform.h>
|
|
|
|
#include <AzCore/PlatformDef.h>
|
|
#include <AzCore/Debug/Profiler.h>
|
|
#include <AzCore/std/bind/bind.h>
|
|
#include <AzCore/StringFunc/StringFunc.h>
|
|
|
|
namespace Audio
|
|
{
|
|
extern CAudioLogger g_audioLogger;
|
|
static constexpr const char AudioControlsBasePath[]{ "libs/gameaudio/" };
|
|
|
|
// Save off the threadId of the "Main Thread" that was used to connect EBuses.
|
|
AZStd::thread_id g_mainThreadId;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// CAudioThread
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
CAudioThread::~CAudioThread()
|
|
{
|
|
Deactivate();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioThread::Run()
|
|
{
|
|
AZ_Assert(m_audioSystem, "Audio Thread has no Audio System to run!\n");
|
|
m_running = true;
|
|
while (m_running)
|
|
{
|
|
m_audioSystem->InternalUpdate();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioThread::Activate(CAudioSystem* const audioSystem)
|
|
{
|
|
m_audioSystem = audioSystem;
|
|
|
|
AZStd::thread_desc threadDesc;
|
|
threadDesc.m_name = "Audio Thread";
|
|
threadDesc.m_cpuId = AZ_TRAIT_AUDIOSYSTEM_AUDIO_THREAD_AFFINITY;
|
|
|
|
auto threadFunc = AZStd::bind(&CAudioThread::Run, this);
|
|
m_thread = AZStd::thread(threadDesc, threadFunc);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioThread::Deactivate()
|
|
{
|
|
if (m_running)
|
|
{
|
|
m_running = false;
|
|
m_thread.join();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// CAudioSystem
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
CAudioSystem::CAudioSystem()
|
|
: m_bSystemInitialized(false)
|
|
{
|
|
g_mainThreadId = AZStd::this_thread::get_id();
|
|
|
|
m_apAudioProxies.reserve(Audio::CVars::s_AudioObjectPoolSize);
|
|
m_apAudioProxiesToBeFreed.reserve(16);
|
|
m_controlsPath.assign(Audio::AudioControlsBasePath);
|
|
|
|
AudioSystemRequestBus::Handler::BusConnect();
|
|
AudioSystemThreadSafeRequestBus::Handler::BusConnect();
|
|
AudioSystemInternalRequestBus::Handler::BusConnect();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
CAudioSystem::~CAudioSystem()
|
|
{
|
|
AudioSystemRequestBus::Handler::BusDisconnect();
|
|
AudioSystemThreadSafeRequestBus::Handler::BusDisconnect();
|
|
AudioSystemInternalRequestBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::PushRequest(const SAudioRequest& audioRequestData)
|
|
{
|
|
CAudioRequestInternal request(audioRequestData);
|
|
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::PushRequest - called from non-Main thread!");
|
|
AZ_Assert(0 == (request.nFlags & eARF_THREAD_SAFE_PUSH), "AudioSystem::PushRequest - called with flag THREAD_SAFE_PUSH!");
|
|
AZ_Assert(0 == (request.nFlags & eARF_EXECUTE_BLOCKING), "AudioSystem::PushRequest - called with flag EXECUTE_BLOCKING!");
|
|
|
|
AudioSystemInternalRequestBus::QueueBroadcast(&AudioSystemInternalRequestBus::Events::ProcessRequestByPriority, request);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::PushRequestBlocking(const SAudioRequest& audioRequestData)
|
|
{
|
|
// Main Thread!
|
|
AZ_PROFILE_FUNCTION(Audio);
|
|
|
|
CAudioRequestInternal request(audioRequestData);
|
|
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::PushRequestBlocking - called from non-Main thread!");
|
|
AZ_Assert(0 != (request.nFlags & eARF_EXECUTE_BLOCKING), "AudioSystem::PushRequestBlocking - called without EXECUTE_BLOCKING flag!");
|
|
AZ_Assert(0 == (request.nFlags & eARF_THREAD_SAFE_PUSH), "AudioSystem::PushRequestBlocking - called with THREAD_SAFE_PUSH flag!");
|
|
|
|
ProcessRequestBlocking(request);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::PushRequestThreadSafe(const SAudioRequest& audioRequestData)
|
|
{
|
|
CAudioRequestInternal request(audioRequestData);
|
|
|
|
AZ_Assert(0 != (request.nFlags & eARF_THREAD_SAFE_PUSH), "AudioSystem::PushRequestThreadSafe - called without THREAD_SAFE_PUSH flag!");
|
|
AZ_Assert(0 == (request.nFlags & eARF_EXECUTE_BLOCKING), "AudioSystem::PushRequestThreadSafe - called with flag EXECUTE_BLOCKING!");
|
|
|
|
AudioSystemThreadSafeRequestBus::QueueFunction(AZStd::bind(&CAudioSystem::ProcessRequestThreadSafe, this, request));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::AddRequestListener(
|
|
AudioRequestCallbackType func,
|
|
void* const callbackOwner,
|
|
const EAudioRequestType requestType,
|
|
const TATLEnumFlagsType specificRequestMask)
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::AddRequestListener - called from a non-Main thread!");
|
|
|
|
if (func)
|
|
{
|
|
SAudioEventListener listener;
|
|
listener.m_callbackOwner = callbackOwner;
|
|
listener.m_fnOnEvent = func;
|
|
listener.m_requestType = requestType;
|
|
listener.m_specificRequestMask = specificRequestMask;
|
|
m_oATL.AddRequestListener(listener);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::RemoveRequestListener(AudioRequestCallbackType func, void* const callbackOwner)
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RemoveRequestListener - called from a non-Main thread!");
|
|
|
|
SAudioEventListener listener;
|
|
listener.m_callbackOwner = callbackOwner;
|
|
listener.m_fnOnEvent = func;
|
|
m_oATL.RemoveRequestListener(listener);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::ExternalUpdate()
|
|
{
|
|
// Main Thread!
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ExternalUpdate - called from non-Main thread!");
|
|
|
|
// Notify callbacks on the pending callbacks queue...
|
|
// These are requests that were completed then queued for callback processing to happen here.
|
|
ExecuteRequestCompletionCallbacks(m_pendingCallbacksQueue, m_pendingCallbacksMutex);
|
|
|
|
// Notify callbacks from the "thread safe" queue...
|
|
ExecuteRequestCompletionCallbacks(m_threadSafeCallbacksQueue, m_threadSafeCallbacksMutex, true);
|
|
|
|
// Other notifications to be sent out...
|
|
AudioTriggerNotificationBus::ExecuteQueuedEvents();
|
|
|
|
// Free any Audio Proxies that are queued up for deletion...
|
|
for (auto audioProxy : m_apAudioProxiesToBeFreed)
|
|
{
|
|
azdestroy(audioProxy, Audio::AudioSystemAllocator);
|
|
}
|
|
|
|
m_apAudioProxiesToBeFreed.clear();
|
|
|
|
#if !defined(AUDIO_RELEASE)
|
|
DrawAudioDebugData();
|
|
#endif // !AUDIO_RELEASE
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::InternalUpdate()
|
|
{
|
|
// Audio Thread!
|
|
AZ_PROFILE_FUNCTION(Audio);
|
|
|
|
auto startUpdateTime = AZStd::chrono::system_clock::now(); // stamp the start time
|
|
|
|
bool handledBlockingRequests = false;
|
|
{
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_blockingRequestsMutex);
|
|
handledBlockingRequests = ProcessRequests(m_blockingRequestsQueue);
|
|
}
|
|
|
|
if (!handledBlockingRequests)
|
|
{
|
|
// Call the ProcessRequestByPriority events queued up...
|
|
AudioSystemInternalRequestBus::ExecuteQueuedEvents();
|
|
}
|
|
|
|
// Call the ProcessRequestThreadSafe events queued up...
|
|
AudioSystemThreadSafeRequestBus::ExecuteQueuedEvents();
|
|
|
|
m_oATL.Update();
|
|
|
|
#if !defined(AUDIO_RELEASE)
|
|
#if defined(PROVIDE_GETNAME_SUPPORT)
|
|
{
|
|
AZ_PROFILE_SCOPE(Audio, "Sync Debug Name Changes");
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_debugNameStoreMutex);
|
|
m_debugNameStore.SyncChanges(m_oATL.GetDebugStore());
|
|
}
|
|
#endif // PROVIDE_GETNAME_SUPPORT
|
|
#endif // !AUDIO_RELEASE
|
|
|
|
if (!handledBlockingRequests)
|
|
{
|
|
auto endUpdateTime = AZStd::chrono::system_clock::now(); // stamp the end time
|
|
auto elapsedUpdateTime = AZStd::chrono::duration_cast<duration_ms>(endUpdateTime - startUpdateTime);
|
|
if (elapsedUpdateTime < m_targetUpdatePeriod)
|
|
{
|
|
AZ_PROFILE_SCOPE(Audio, "Wait Remaining Time in Update Period");
|
|
m_processingEvent.try_acquire_for(m_targetUpdatePeriod - elapsedUpdateTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool CAudioSystem::Initialize()
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Initialize - called from a non-Main thread!");
|
|
|
|
if (!m_bSystemInitialized)
|
|
{
|
|
m_audioSystemThread.Deactivate();
|
|
m_oATL.Initialize();
|
|
m_audioSystemThread.Activate(this);
|
|
|
|
for (AZ::u64 i = 0; i < Audio::CVars::s_AudioObjectPoolSize; ++i)
|
|
{
|
|
auto audioProxy = azcreate(CAudioProxy, (), Audio::AudioSystemAllocator, "AudioProxy");
|
|
m_apAudioProxies.push_back(audioProxy);
|
|
}
|
|
|
|
m_bSystemInitialized = true;
|
|
}
|
|
|
|
return m_bSystemInitialized;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::Release()
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Release - called from a non-Main thread!");
|
|
|
|
for (auto audioProxy : m_apAudioProxies)
|
|
{
|
|
azdestroy(audioProxy, Audio::AudioSystemAllocator);
|
|
}
|
|
|
|
for (auto audioProxy : m_apAudioProxiesToBeFreed)
|
|
{
|
|
azdestroy(audioProxy, Audio::AudioSystemAllocator);
|
|
}
|
|
|
|
m_apAudioProxies.clear();
|
|
m_apAudioProxiesToBeFreed.clear();
|
|
|
|
// Release the audio implementation...
|
|
SAudioRequest request;
|
|
SAudioManagerRequestData<eAMRT_RELEASE_AUDIO_IMPL> requestData;
|
|
request.nFlags = (eARF_PRIORITY_HIGH | eARF_EXECUTE_BLOCKING);
|
|
request.pData = &requestData;
|
|
PushRequestBlocking(request);
|
|
|
|
m_audioSystemThread.Deactivate();
|
|
m_oATL.ShutDown();
|
|
m_bSystemInitialized = false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
TAudioControlID CAudioSystem::GetAudioTriggerID(const char* const sAudioTriggerName) const
|
|
{
|
|
return m_oATL.GetAudioTriggerID(sAudioTriggerName);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
TAudioControlID CAudioSystem::GetAudioRtpcID(const char* const sAudioRtpcName) const
|
|
{
|
|
return m_oATL.GetAudioRtpcID(sAudioRtpcName);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
TAudioControlID CAudioSystem::GetAudioSwitchID(const char* const sAudioStateName) const
|
|
{
|
|
return m_oATL.GetAudioSwitchID(sAudioStateName);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
TAudioSwitchStateID CAudioSystem::GetAudioSwitchStateID(const TAudioControlID nSwitchID, const char* const sAudioSwitchStateName) const
|
|
{
|
|
return m_oATL.GetAudioSwitchStateID(nSwitchID, sAudioSwitchStateName);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
TAudioPreloadRequestID CAudioSystem::GetAudioPreloadRequestID(const char* const sAudioPreloadRequestName) const
|
|
{
|
|
return m_oATL.GetAudioPreloadRequestID(sAudioPreloadRequestName);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
TAudioEnvironmentID CAudioSystem::GetAudioEnvironmentID(const char* const sAudioEnvironmentName) const
|
|
{
|
|
return m_oATL.GetAudioEnvironmentID(sAudioEnvironmentName);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool CAudioSystem::ReserveAudioListenerID(TAudioObjectID& rAudioObjectID)
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReserveAudioListenerID - called from a non-Main thread!");
|
|
return m_oATL.ReserveAudioListenerID(rAudioObjectID);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool CAudioSystem::ReleaseAudioListenerID(TAudioObjectID const nAudioObjectID)
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReleaseAudioListenerID - called from a non-Main thread!");
|
|
return m_oATL.ReleaseAudioListenerID(nAudioObjectID);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool CAudioSystem::SetAudioListenerOverrideID(const TAudioObjectID nAudioObjectID)
|
|
{
|
|
return m_oATL.SetAudioListenerOverrideID(nAudioObjectID);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::GetInfo([[maybe_unused]] SAudioSystemInfo& rAudioSystemInfo)
|
|
{
|
|
//TODO:
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
const char* CAudioSystem::GetControlsPath() const
|
|
{
|
|
return m_controlsPath.c_str();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::UpdateControlsPath()
|
|
{
|
|
AZStd::string controlsPath{ Audio::AudioControlsBasePath };
|
|
const AZStd::string& subPath = m_oATL.GetControlsImplSubPath();
|
|
if (!subPath.empty())
|
|
{
|
|
controlsPath.append(subPath);
|
|
}
|
|
|
|
if (AZ::StringFunc::RelativePath::Normalize(controlsPath))
|
|
{
|
|
m_controlsPath = controlsPath;
|
|
}
|
|
else
|
|
{
|
|
g_audioLogger.Log(
|
|
eALT_ERROR, "AudioSystem::UpdateControlsPath - failed to normalize the controls path '%s'!", controlsPath.c_str());
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::RefreshAudioSystem([[maybe_unused]] const char* const levelName)
|
|
{
|
|
#if !defined(AUDIO_RELEASE)
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RefreshAudioSystem - called from a non-Main thread!");
|
|
|
|
// Get the controls path and a level-specific preload Id first.
|
|
// This will be passed with the request so that it doesn't have to lookup this data
|
|
// and punch the AudioSystemRequestBus from the Audio Thread.
|
|
const char* audioControlsPath = GetControlsPath();
|
|
|
|
Audio::TAudioPreloadRequestID levelPreloadId = INVALID_AUDIO_PRELOAD_REQUEST_ID;
|
|
if (levelName && levelName[0] != '\0')
|
|
{
|
|
levelPreloadId = GetAudioPreloadRequestID(levelName);
|
|
}
|
|
|
|
Audio::SAudioManagerRequestData<Audio::eAMRT_REFRESH_AUDIO_SYSTEM> requestData(audioControlsPath, levelName, levelPreloadId);
|
|
Audio::SAudioRequest request;
|
|
request.nFlags = (Audio::eARF_PRIORITY_HIGH | Audio::eARF_EXECUTE_BLOCKING);
|
|
request.pData = &requestData;
|
|
PushRequestBlocking(request);
|
|
#endif // !AUDIO_RELEASE
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
IAudioProxy* CAudioSystem::GetFreeAudioProxy()
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetFreeAudioProxy - called from a non-Main thread!");
|
|
CAudioProxy* audioProxy = nullptr;
|
|
|
|
if (!m_apAudioProxies.empty())
|
|
{
|
|
audioProxy = m_apAudioProxies.back();
|
|
m_apAudioProxies.pop_back();
|
|
}
|
|
else
|
|
{
|
|
audioProxy = azcreate(CAudioProxy, (), Audio::AudioSystemAllocator, "AudioProxyEx");
|
|
|
|
#if !defined(AUDIO_RELEASE)
|
|
if (!audioProxy)
|
|
{
|
|
g_audioLogger.Log(eALT_ASSERT, "AudioSystem::GetFreeAudioProxy - failed to create new AudioProxy instance!");
|
|
}
|
|
#endif // !AUDIO_RELEASE
|
|
}
|
|
|
|
return static_cast<IAudioProxy*>(audioProxy);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::FreeAudioProxy(IAudioProxy* const audioProxyI)
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::FreeAudioProxy - called from a non-Main thread!");
|
|
auto const audioProxy = static_cast<CAudioProxy*>(audioProxyI);
|
|
|
|
if (AZStd::find(m_apAudioProxiesToBeFreed.begin(), m_apAudioProxiesToBeFreed.end(), audioProxy) != m_apAudioProxiesToBeFreed.end() || AZStd::find(m_apAudioProxies.begin(), m_apAudioProxies.end(), audioProxy) != m_apAudioProxies.end())
|
|
{
|
|
AZ_Warning("AudioSystem", false, "Attempting to free an already freed audio proxy");
|
|
return;
|
|
}
|
|
|
|
if (m_apAudioProxies.size() < Audio::CVars::s_AudioObjectPoolSize)
|
|
{
|
|
m_apAudioProxies.push_back(audioProxy);
|
|
}
|
|
else
|
|
{
|
|
m_apAudioProxiesToBeFreed.push_back(audioProxy);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
TAudioSourceId CAudioSystem::CreateAudioSource(const SAudioInputConfig& sourceConfig)
|
|
{
|
|
return m_oATL.CreateAudioSource(sourceConfig);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::DestroyAudioSource(TAudioSourceId sourceId)
|
|
{
|
|
m_oATL.DestroyAudioSource(sourceId);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
const char* CAudioSystem::GetAudioControlName([[maybe_unused]] const EAudioControlType controlType, [[maybe_unused]] const TATLIDType atlID) const
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetAudioControlName - called from non-Main thread!");
|
|
const char* sResult = nullptr;
|
|
|
|
#if !defined(AUDIO_RELEASE)
|
|
#if defined(PROVIDE_GETNAME_SUPPORT)
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_debugNameStoreMutex);
|
|
switch (controlType)
|
|
{
|
|
case eACT_AUDIO_OBJECT:
|
|
{
|
|
sResult = m_debugNameStore.LookupAudioObjectName(atlID);
|
|
break;
|
|
}
|
|
case eACT_TRIGGER:
|
|
{
|
|
sResult = m_debugNameStore.LookupAudioTriggerName(atlID);
|
|
break;
|
|
}
|
|
case eACT_RTPC:
|
|
{
|
|
sResult = m_debugNameStore.LookupAudioRtpcName(atlID);
|
|
break;
|
|
}
|
|
case eACT_SWITCH:
|
|
{
|
|
sResult = m_debugNameStore.LookupAudioSwitchName(atlID);
|
|
break;
|
|
}
|
|
case eACT_PRELOAD:
|
|
{
|
|
sResult = m_debugNameStore.LookupAudioPreloadRequestName(atlID);
|
|
break;
|
|
}
|
|
case eACT_ENVIRONMENT:
|
|
{
|
|
sResult = m_debugNameStore.LookupAudioEnvironmentName(atlID);
|
|
break;
|
|
}
|
|
case eACT_SWITCH_STATE: // not handled here, use GetAudioSwitchStateName!
|
|
case eACT_NONE:
|
|
default: // fall-through!
|
|
{
|
|
g_audioLogger.Log(eALT_WARNING, "AudioSystem::GetAudioControlName - called with invalid EAudioControlType!");
|
|
break;
|
|
}
|
|
}
|
|
#endif // PROVIDE_GETNAME_SUPPORT
|
|
#endif // !AUDIO_RELEASE
|
|
|
|
return sResult;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
const char* CAudioSystem::GetAudioSwitchStateName([[maybe_unused]] const TAudioControlID switchID, [[maybe_unused]] const TAudioSwitchStateID stateID) const
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetAudioSwitchStateName - called from non-Main thread!");
|
|
const char* sResult = nullptr;
|
|
|
|
#if !defined(AUDIO_RELEASE)
|
|
#if defined(PROVIDE_GETNAME_SUPPORT)
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_debugNameStoreMutex);
|
|
sResult = m_debugNameStore.LookupAudioSwitchStateName(switchID, stateID);
|
|
#endif // PROVIDE_GETNAME_SUPPORT
|
|
#endif // !AUDIO_RELEASE
|
|
|
|
return sResult;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::ExtractCompletedRequests(TAudioRequests& requestQueue, TAudioRequests& extractedCallbacks)
|
|
{
|
|
auto iter(requestQueue.begin());
|
|
auto iterEnd(requestQueue.end());
|
|
|
|
while (iter != iterEnd)
|
|
{
|
|
const CAudioRequestInternal& refRequest = (*iter);
|
|
if (refRequest.IsComplete())
|
|
{
|
|
// the request has completed, eligible for notification callback.
|
|
// move the request to the extraction queue.
|
|
extractedCallbacks.push_back(refRequest);
|
|
iter = requestQueue.erase(iter);
|
|
iterEnd = requestQueue.end();
|
|
continue;
|
|
}
|
|
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::ExecuteRequestCompletionCallbacks(TAudioRequests& requestQueue, AZStd::mutex& requestQueueMutex, bool tryLock)
|
|
{
|
|
TAudioRequests extractedCallbacks;
|
|
|
|
if (tryLock)
|
|
{
|
|
if (requestQueueMutex.try_lock())
|
|
{
|
|
ExtractCompletedRequests(requestQueue, extractedCallbacks);
|
|
requestQueueMutex.unlock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AZStd::lock_guard<AZStd::mutex> lock(requestQueueMutex);
|
|
ExtractCompletedRequests(requestQueue, extractedCallbacks);
|
|
}
|
|
|
|
// Notify listeners
|
|
for (const auto& callback : extractedCallbacks)
|
|
{
|
|
m_oATL.NotifyListener(callback);
|
|
}
|
|
|
|
extractedCallbacks.clear();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::ProcessRequestBlocking(CAudioRequestInternal& request)
|
|
{
|
|
AZ_PROFILE_FUNCTION(Audio);
|
|
|
|
if (m_oATL.CanProcessRequests())
|
|
{
|
|
{
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_blockingRequestsMutex);
|
|
m_blockingRequestsQueue.push_back(request);
|
|
}
|
|
|
|
m_processingEvent.release();
|
|
m_mainEvent.acquire();
|
|
|
|
ExecuteRequestCompletionCallbacks(m_blockingRequestsQueue, m_blockingRequestsMutex);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::ProcessRequestThreadSafe(CAudioRequestInternal request)
|
|
{
|
|
// Audio Thread!
|
|
AZ_PROFILE_SCOPE(Audio, "Process Thread-Safe Request");
|
|
|
|
if (m_oATL.CanProcessRequests())
|
|
{
|
|
if (request.eStatus == eARS_NONE)
|
|
{
|
|
request.eStatus = eARS_PENDING;
|
|
m_oATL.ProcessRequest(request);
|
|
}
|
|
|
|
AZ_Assert(request.eStatus != eARS_PENDING, "AudioSystem::ProcessRequestThreadSafe - ATL finished processing request, but request is still in pending state!");
|
|
if (request.eStatus != eARS_PENDING)
|
|
{
|
|
// push the request onto a callbacks queue for main thread to process later...
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_threadSafeCallbacksMutex);
|
|
m_threadSafeCallbacksQueue.push_back(request);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CAudioSystem::ProcessRequestByPriority(CAudioRequestInternal request)
|
|
{
|
|
// Todo: This should handle request priority, use request priority as bus Address and process in priority order.
|
|
|
|
AZ_PROFILE_SCOPE(Audio, "Process Normal Request");
|
|
|
|
AZ_Assert(g_mainThreadId != AZStd::this_thread::get_id(), "AudioSystem::ProcessRequestByPriority - called from Main thread!");
|
|
|
|
if (m_oATL.CanProcessRequests())
|
|
{
|
|
if (request.eStatus == eARS_NONE)
|
|
{
|
|
request.eStatus = eARS_PENDING;
|
|
m_oATL.ProcessRequest(request);
|
|
}
|
|
|
|
AZ_Assert(request.eStatus != eARS_PENDING, "AudioSystem::ProcessRequestByPriority - ATL finished processing request, but request is still in pending state!");
|
|
if (request.eStatus != eARS_PENDING)
|
|
{
|
|
// push the request onto a callbacks queue for main thread to process later...
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_pendingCallbacksMutex);
|
|
m_pendingCallbacksQueue.push_back(request);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool CAudioSystem::ProcessRequests(TAudioRequests& requestQueue)
|
|
{
|
|
bool success = false;
|
|
|
|
for (auto& request : requestQueue)
|
|
{
|
|
if (!(request.nInternalInfoFlags & eARIF_WAITING_FOR_REMOVAL))
|
|
{
|
|
AZ_PROFILE_SCOPE(Audio, "Process Blocking Request");
|
|
|
|
if (request.eStatus == eARS_NONE)
|
|
{
|
|
request.eStatus = eARS_PENDING;
|
|
m_oATL.ProcessRequest(request);
|
|
success = true;
|
|
}
|
|
|
|
if (request.eStatus != eARS_PENDING)
|
|
{
|
|
if (request.nFlags & eARF_EXECUTE_BLOCKING)
|
|
{
|
|
request.nInternalInfoFlags |= eARIF_WAITING_FOR_REMOVAL;
|
|
m_mainEvent.release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_audioLogger.Log(eALT_ERROR, "AudioSystem::ProcessRequests - request still in Pending state after being processed by ATL!");
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#if !defined(AUDIO_RELEASE)
|
|
void CAudioSystem::DrawAudioDebugData()
|
|
{
|
|
AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::DrawAudioDebugData - called from non-Main thread!");
|
|
|
|
if (CVars::s_debugDrawOptions.GetRawFlags() != 0)
|
|
{
|
|
SAudioRequest oRequest;
|
|
oRequest.nFlags = (eARF_PRIORITY_HIGH | eARF_EXECUTE_BLOCKING);
|
|
SAudioManagerRequestData<eAMRT_DRAW_DEBUG_INFO> oRequestData;
|
|
oRequest.pData = &oRequestData;
|
|
|
|
PushRequestBlocking(oRequest);
|
|
}
|
|
}
|
|
#endif // !AUDIO_RELEASE
|
|
|
|
} // namespace Audio
|