Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,202 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AudioEngineWwiseGemSystemComponent.h>
#include <AzCore/PlatformDef.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <ISystem.h>
#include <AudioAllocators.h>
#include <AudioLogger.h>
#include <AudioSystemImplCVars.h>
#include <AudioSystemImpl_wwise.h>
#include <Common_wwise.h>
#include <Config_wwise.h>
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
#include <AudioSystemEditor_wwise.h>
#endif // AUDIO_ENGINE_WWISE_EDITOR
namespace Audio
{
CAudioLogger g_audioImplLogger_wwise;
#if AZ_TRAIT_AUDIOENGINEWWISE_PROVIDE_IMPL_SECONDARY_POOL
TMemoryPoolReferenced g_audioImplMemoryPoolSecondary_wwise;
#endif // AZ_TRAIT_AUDIOENGINEWWISE_PROVIDE_IMPL_SECONDARY_POOL
namespace Platform
{
void* InitializeSecondaryMemoryPool(size_t& secondarySize);
}
} // namespace Audio
namespace AudioEngineWwiseGem
{
void AudioEngineWwiseGemSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<AudioEngineWwiseGemSystemComponent, AZ::Component>()
->Version(0)
;
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<AudioEngineWwiseGemSystemComponent>("Audio Engine Wwise Gem", "Wwise implementation of the Audio Engine interfaces")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
Audio::Wwise::ConfigurationSettings::Reflect(context);
}
void AudioEngineWwiseGemSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("AudioEngineService"));
}
void AudioEngineWwiseGemSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("AudioEngineService"));
}
void AudioEngineWwiseGemSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("AudioSystemService"));
}
void AudioEngineWwiseGemSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC("AudioSystemService"));
}
void AudioEngineWwiseGemSystemComponent::Init()
{
}
void AudioEngineWwiseGemSystemComponent::Activate()
{
Audio::Gem::AudioEngineGemRequestBus::Handler::BusConnect();
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
AudioControlsEditor::EditorImplPluginEventBus::Handler::BusConnect();
#endif // AUDIO_ENGINE_WWISE_EDITOR
}
void AudioEngineWwiseGemSystemComponent::Deactivate()
{
Audio::Gem::AudioEngineGemRequestBus::Handler::BusDisconnect();
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
AudioControlsEditor::EditorImplPluginEventBus::Handler::BusDisconnect();
#endif // AUDIO_ENGINE_WWISE_EDITOR
}
bool AudioEngineWwiseGemSystemComponent::Initialize(const SSystemInitParams* initParams)
{
bool success = false;
// Check memory-related Wwise Cvars...
const AZ::u64 memorySubpartitionSizes = Audio::Wwise::Cvars::s_StreamDeviceMemorySize
#if !defined(WWISE_RELEASE)
+ Audio::Wwise::Cvars::s_MonitorQueueMemorySize
#endif // !WWISE_RELEASE
+ Audio::Wwise::Cvars::s_CommandQueueMemorySize;
AZ_Assert(Audio::Wwise::Cvars::s_PrimaryMemorySize > memorySubpartitionSizes,
"Wwise memory sizes of sub-categories add up to more than the primary memory pool size!")
// Initialize memory block for Wwise to use...
if (!AZ::AllocatorInstance<Audio::AudioImplAllocator>::IsReady())
{
const size_t poolSize = Audio::Wwise::Cvars::s_PrimaryMemorySize << 10;
Audio::AudioImplAllocator::Descriptor allocDesc;
// Generic Allocator:
allocDesc.m_allocationRecords = true;
allocDesc.m_heap.m_numFixedMemoryBlocks = 1;
allocDesc.m_heap.m_fixedMemoryBlocksByteSize[0] = poolSize;
allocDesc.m_heap.m_fixedMemoryBlocks[0] = AZ::AllocatorInstance<AZ::OSAllocator>::Get().Allocate(
allocDesc.m_heap.m_fixedMemoryBlocksByteSize[0], allocDesc.m_heap.m_memoryBlockAlignment);
AZ::AllocatorInstance<Audio::AudioImplAllocator>::Create(allocDesc);
}
m_engineWwise = AZStd::make_unique<Audio::CAudioSystemImpl_wwise>(initParams->assetsPlatform);
if (m_engineWwise)
{
#if AZ_TRAIT_AUDIOENGINEWWISE_PROVIDE_IMPL_SECONDARY_POOL
size_t secondarySize = 0;
void* secondaryMemoryPtr = Audio::Platform::InitializeSecondaryMemoryPool(secondarySize);
Audio::g_audioImplMemoryPoolSecondary_wwise.InitMem(secondarySize, static_cast<uint8*>(secondaryMemoryPtr));
#endif // AZ_TRAIT_AUDIOENGINEWWISE_PROVIDE_IMPL_SECONDARY_POOL
Audio::g_audioImplLogger_wwise.Log(Audio::eALT_ALWAYS, "AudioEngineWwise created!");
Audio::SAudioRequest oAudioRequestData;
oAudioRequestData.nFlags = (Audio::eARF_PRIORITY_HIGH | Audio::eARF_EXECUTE_BLOCKING);
Audio::SAudioManagerRequestData<Audio::eAMRT_INIT_AUDIO_IMPL> oAMData;
oAudioRequestData.pData = &oAMData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
success = true;
}
else
{
Audio::g_audioImplLogger_wwise.Log(Audio::eALT_ALWAYS, "Could not create AudioEngineWwise!");
}
return success;
}
void AudioEngineWwiseGemSystemComponent::Release()
{
m_engineWwise.reset();
if (AZ::AllocatorInstance<Audio::AudioImplAllocator>::IsReady())
{
AZ::AllocatorInstance<Audio::AudioImplAllocator>::Destroy();
}
}
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
void AudioEngineWwiseGemSystemComponent::InitializeEditorImplPlugin()
{
m_editorImplPlugin.reset(new AudioControls::CAudioSystemEditor_wwise());
}
void AudioEngineWwiseGemSystemComponent::ReleaseEditorImplPlugin()
{
m_editorImplPlugin.reset();
}
AudioControls::IAudioSystemEditor* AudioEngineWwiseGemSystemComponent::GetEditorImplPlugin()
{
return m_editorImplPlugin.get();
}
#endif // AUDIO_ENGINE_WWISE_EDITOR
} // namespace AudioEngineWwiseGem
@@ -0,0 +1,78 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <IAudioSystem.h>
#include <IAudioSystemImplementation.h>
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <IAudioSystemEditor.h>
#endif // AUDIO_ENGINE_WWISE_EDITOR
struct SSystemInitParams;
namespace AudioEngineWwiseGem
{
class AudioEngineWwiseGemSystemComponent
: public AZ::Component
, protected Audio::Gem::AudioEngineGemRequestBus::Handler
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
, protected AudioControlsEditor::EditorImplPluginEventBus::Handler
#endif // AUDIO_ENGINE_WWISE_EDITOR
{
public:
AZ_COMPONENT(AudioEngineWwiseGemSystemComponent, "{521FA289-DC8B-4BC1-BBA0-A7D35EAC656E}", AZ::Component);
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
////////////////////////////////////////////////////////////////////////
// Audio::Gem::AudioEngineGemRequestBus interface implementation
bool Initialize(const SSystemInitParams* initParams) override;
void Release() override;
////////////////////////////////////////////////////////////////////////
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
////////////////////////////////////////////////////////////////////////
// AudioControlsEditor::EditorImplPluginEventBus interface implementation
void InitializeEditorImplPlugin() override;
void ReleaseEditorImplPlugin() override;
AudioControls::IAudioSystemEditor* GetEditorImplPlugin() override;
////////////////////////////////////////////////////////////////////////
#endif // AUDIO_ENGINE_WWISE_EDITOR
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
private:
AZStd::unique_ptr<Audio::AudioSystemImplementation> m_engineWwise;
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
AZStd::unique_ptr<AudioControls::IAudioSystemEditor> m_editorImplPlugin;
#endif // AUDIO_ENGINE_WWISE_EDITOR
};
} // namespace AudioEngineWwiseGem
@@ -0,0 +1,62 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/PlatformDef.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <IGem.h>
#include <AudioEngineWwiseGemSystemComponent.h>
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
#include <AudioControlBuilderComponent.h>
#include <WwiseBuilderComponent.h>
#endif // AUDIO_ENGINE_WWISE_EDITOR
namespace AudioEngineWwiseGem
{
class AudioEngineWwiseModule
: public CryHooksModule
{
public:
AZ_RTTI(AudioEngineWwiseModule, "{303B0192-D866-4378-9342-728AA6E66F74}", CryHooksModule);
AZ_CLASS_ALLOCATOR(AudioEngineWwiseModule, AZ::SystemAllocator, 0);
AudioEngineWwiseModule()
: CryHooksModule()
{
m_descriptors.insert(m_descriptors.end(), {
AudioEngineWwiseGemSystemComponent::CreateDescriptor(),
#if defined(AUDIO_ENGINE_WWISE_EDITOR)
AudioControlBuilder::BuilderPluginComponent::CreateDescriptor(),
WwiseBuilder::BuilderPluginComponent::CreateDescriptor(),
#endif // AUDIO_ENGINE_WWISE_EDITOR
});
}
/**
* Add required SystemComponents to the SystemEntity.
*/
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList {
azrtti_typeid<AudioEngineWwiseGemSystemComponent>(),
};
}
};
} // namespace AudioEngineWwiseGem
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
// The first parameter should be GemName_GemIdLower
// The second should be the fully qualified name of the class above
AZ_DECLARE_MODULE_CLASS(Gem_AudioEngineWwise, AudioEngineWwiseGem::AudioEngineWwiseModule)
@@ -0,0 +1,15 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Module/Module.h>
AZ_DECLARE_MODULE_CLASS(Gem_AudioEngineWwise, AZ::Module)
@@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Builder/AudioControlBuilderComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
namespace AudioControlBuilder
{
void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AudioControlBuilder::BuilderPluginComponent, AZ::Component>()
->Version(1)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
;
}
}
void BuilderPluginComponent::Activate()
{
// Register Audio Control builder
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
builderDescriptor.m_name = "Audio Control Builder";
// pattern finds all Audio Control xml files in the libs/gameaudio folder and any of its subfolders.
builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("(.*libs\\/gameaudio\\/).*\\.xml", AssetBuilderSDK::AssetBuilderPattern::PatternType::Regex));
builderDescriptor.m_busId = azrtti_typeid<AudioControlBuilderWorker>();
builderDescriptor.m_version = 2;
builderDescriptor.m_createJobFunction = AZStd::bind(&AudioControlBuilderWorker::CreateJobs, &m_audioControlBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_processJobFunction = AZStd::bind(&AudioControlBuilderWorker::ProcessJob, &m_audioControlBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
// (optimization) this builder does not emit source dependencies:
builderDescriptor.m_flags |= AssetBuilderSDK::AssetBuilderDesc::BF_EmitsNoDependencies;
m_audioControlBuilder.BusConnect(builderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Events::RegisterBuilderInformation, builderDescriptor);
}
void BuilderPluginComponent::Deactivate()
{
m_audioControlBuilder.BusDisconnect();
}
} // namespace AudioControlBuilder
@@ -0,0 +1,50 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <Builder/AudioControlBuilderWorker.h>
namespace AudioControlBuilder
{
class BuilderPluginComponent
: public AZ::Component
{
public:
AZ_COMPONENT(BuilderPluginComponent, "{4C0E0008-3D09-4628-8CEE-E9C6475AFB62}");
BuilderPluginComponent() = default;
~BuilderPluginComponent() override = default;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("AudioControlBuilderService"));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("AudioControlBuilderService"));
}
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
private:
AudioControlBuilderWorker m_audioControlBuilder;
};
} // namespace AudioControlBuilder
@@ -0,0 +1,554 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Builder/AudioControlBuilderWorker.h>
#include <AzCore/AzCore_Traits_Platform.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/JSON/document.h>
#include <AzCore/PlatformId/PlatformId.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <ATLCommon.h>
#include <Common_wwise.h>
#include <Config_wwise.h>
namespace AudioControlBuilder
{
namespace Internal
{
const char JsonEventsKey[] = "includedEvents";
const char SoundbankDependencyFileExtension[] = ".bankdeps";
const char NodeDoesNotExistMessage[] = "%s node does not exist. Please be sure that you have defined at least one %s for this Audio Control file.\n";
const char MalformedNodeMissingAttributeMessage[] = "%s node is malformed: does not have an attribute %s defined. This is likely the result of manual editing. Please resave the Audio Control file.\n";
const char MalformedNodeMissingChildNodeMessage[] = "%s node does not contain a child %s node. This is likely the result of manual editing. Please resave the Audio Control file.\n";
namespace Legacy
{
using AtlConfigGroupMap = AZStd::unordered_map<AZStd::string, const AZ::rapidxml::xml_node<char>*>;
AZStd::string GetAtlPlatformName(const AZStd::string& requestPlatform)
{
AZStd::string atlPlatform;
AZStd::string platform = requestPlatform;
// When debugging a builder using a debug task, it replaces platform tags with "debug platform". Make sure the builder
// actually uses the host platform identifier when going through this function in this case.
if (platform == "debug platform")
{
atlPlatform = AZ_TRAIT_OS_PLATFORM_NAME;
AZStd::to_lower(atlPlatform.begin(), atlPlatform.end());
}
else
{
if (platform == "pc")
{
atlPlatform = "windows";
}
else if (platform == "es3")
{
atlPlatform = "android";
}
else if (platform == "osx_gl")
{
atlPlatform = "mac";
}
else
{
atlPlatform = AZStd::move(platform);
}
}
return AZStd::move(atlPlatform);
}
AZ::Outcome<void, AZStd::string> BuildConfigGroupMap(const AZ::rapidxml::xml_node<char>* preloadRequestNode, AtlConfigGroupMap& configGroupMap)
{
AZ_Assert(preloadRequestNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request");
auto configGroupNode = preloadRequestNode->first_node(Audio::ATLXmlTags::ATLConfigGroupTag);
while (configGroupNode)
{
// Populate the config group map by iterating over all ATLConfigGroup nodes, and place each one in the map keyed by the group's atl_name attribute...
if (const auto configGroupNameAttr = configGroupNode->first_attribute(Audio::ATLXmlTags::ATLNameAttribute))
{
configGroupMap.emplace(configGroupNameAttr->value(), configGroupNode);
}
else
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
Audio::ATLXmlTags::ATLConfigGroupTag, Audio::ATLXmlTags::ATLNameAttribute));
}
configGroupNode = configGroupNode->next_sibling(Audio::ATLXmlTags::ATLConfigGroupTag);
}
// If no config groups are defined, this is an empty preload request with no banks referenced, which is valid.
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> GetBanksFromAtlPreloads(const AZ::rapidxml::xml_node<char>* preloadsNode, const AZStd::string& atlPlatformIdentifier, AZStd::vector<AZStd::string>& banksReferenced)
{
AZ_Assert(preloadsNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::PreloadsNodeTag, "preload request");
auto preloadRequestNode = preloadsNode->first_node(Audio::ATLXmlTags::ATLPreloadRequestTag);
if (!preloadRequestNode)
{
return AZ::Failure(AZStd::string::format(NodeDoesNotExistMessage,
Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request"));
}
// For each preload request in the control file, determine which config group is used for this platform and register each
// bank listed in that preload request as a dependency.
while (preloadRequestNode)
{
AtlConfigGroupMap configGroupMap;
auto configGroupMapResult = BuildConfigGroupMap(preloadRequestNode, configGroupMap);
// If the returned map is empty, there are not banks referenced in the preload request, return the result here.
if (!configGroupMapResult.IsSuccess() || configGroupMap.size() == 0)
{
return configGroupMapResult;
}
const auto platformsNode = preloadRequestNode->first_node(Audio::ATLXmlTags::ATLPlatformsTag);
if (!platformsNode)
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingChildNodeMessage,
Audio::ATLXmlTags::ATLPreloadRequestTag, Audio::ATLXmlTags::ATLPlatformsTag));
}
auto platformNode = platformsNode->first_node(Audio::ATLXmlTags::PlatformNodeTag);
if (!platformNode)
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingChildNodeMessage,
Audio::ATLXmlTags::ATLPlatformsTag, Audio::ATLXmlTags::PlatformNodeTag));
}
AZStd::string configGroupName;
// For each platform node in the platform list, check the atl_name to see if it matches the platform the request is
// intended for. If it is, grab the name of the config group that is used for that platform to load it.
while (platformNode)
{
const auto atlNameAttr = platformNode->first_attribute(Audio::ATLXmlTags::ATLNameAttribute);
if (!atlNameAttr)
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
Audio::ATLXmlTags::PlatformNodeTag, Audio::ATLXmlTags::ATLNameAttribute));
}
else if (atlPlatformIdentifier == atlNameAttr->value())
{
// We've found the right platform that matches the request, so grab the group name and stop looking through
// the list
const auto configGroupNameAttr = platformNode->first_attribute(Audio::ATLXmlTags::ATLConfigGroupAttribute);
if (!configGroupNameAttr)
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
Audio::ATLXmlTags::PlatformNodeTag, Audio::ATLXmlTags::ATLConfigGroupAttribute));
}
configGroupName = configGroupNameAttr->value();
break;
}
platformNode = platformNode->next_sibling(Audio::ATLXmlTags::PlatformNodeTag);
}
const AZ::rapidxml::xml_node<char>* configGroupNode = configGroupMap[configGroupName];
if (!configGroupNode)
{
// The config group this platform uses isn't defined in the control file. This might be intentional, so just
// generate a warning and keep going to the next preload node.
AZ_TracePrintf("Audio Control Builder", "%s node for config group %s is not defined, so no banks are referenced.",
Audio::ATLXmlTags::ATLConfigGroupTag, configGroupName.c_str());
}
else
{
auto wwiseFileNode = configGroupNode->first_node(Audio::WwiseXmlTags::WwiseFileTag);
if (!wwiseFileNode)
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingChildNodeMessage,
Audio::ATLXmlTags::ATLConfigGroupTag, Audio::WwiseXmlTags::WwiseFileTag));
}
// For each WwiseFile (soundbank) referenced in the config group, grab the file name and add it to the reference list
while (wwiseFileNode)
{
const auto bankNameAttribute = wwiseFileNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute);
if (!bankNameAttribute)
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
Audio::WwiseXmlTags::WwiseFileTag, Audio::WwiseXmlTags::WwiseNameAttribute));
}
// Prepend the bank name with the relative path to the wwise sounds folder to get relative path to the bank from
// the @assets@ alias and push that into the list of banks referenced.
AZStd::string soundsPrefix = Audio::Wwise::DefaultBanksPath;
banksReferenced.emplace_back(soundsPrefix + bankNameAttribute->value());
wwiseFileNode = wwiseFileNode->next_sibling(Audio::WwiseXmlTags::WwiseFileTag);
}
}
preloadRequestNode = preloadRequestNode->next_sibling(Audio::ATLXmlTags::ATLPreloadRequestTag);
}
return AZ::Success();
}
} // namespace Legacy
AZ::Outcome<void, AZStd::string> BuildAtlEventList(const AZ::rapidxml::xml_node<char>* triggersNode, AZStd::vector<AZStd::string>& eventNames)
{
AZ_Assert(triggersNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::TriggersNodeTag, "trigger");
auto triggerNode = triggersNode->first_node(Audio::ATLXmlTags::ATLTriggerTag);
while (triggerNode)
{
// For each audio trigger, push the name of the Wwise event (if assigned) into the list.
// It's okay for an ATLTrigger node to not have a Wwise event associated with it.
if (const auto eventNode = triggerNode->first_node(Audio::WwiseXmlTags::WwiseEventTag))
{
if (const auto eventNameAttr = eventNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute))
{
eventNames.push_back(eventNameAttr->value());
}
else
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
Audio::WwiseXmlTags::WwiseEventTag, Audio::WwiseXmlTags::WwiseNameAttribute));
}
}
triggerNode = triggerNode->next_sibling(Audio::ATLXmlTags::ATLTriggerTag);
}
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> GetBanksFromAtlPreloads(const AZ::rapidxml::xml_node<char>* preloadsNode, AZStd::vector<AZStd::string>& banksReferenced)
{
AZ_Assert(preloadsNode != nullptr, NodeDoesNotExistMessage, Audio::ATLXmlTags::PreloadsNodeTag, "preload request");
auto preloadRequestNode = preloadsNode->first_node(Audio::ATLXmlTags::ATLPreloadRequestTag);
if (!preloadRequestNode)
{
return AZ::Failure(AZStd::string::format(NodeDoesNotExistMessage, Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request"));
}
// Loop through the ATLPreloadRequest nodes...
// Find any Wwise banks listed and add them to the banksReferenced vector.
while (preloadRequestNode)
{
// Attempt to find the child node in the New Xml format...
if (auto wwiseFileNode = preloadRequestNode->first_node(Audio::WwiseXmlTags::WwiseFileTag))
{
while (wwiseFileNode)
{
const auto bankNameAttr = wwiseFileNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute);
if (bankNameAttr)
{
AZStd::string soundsPrefix = Audio::Wwise::DefaultBanksPath;
banksReferenced.emplace_back(soundsPrefix + bankNameAttr->value());
}
else
{
return AZ::Failure(AZStd::string::format(MalformedNodeMissingAttributeMessage,
Audio::WwiseXmlTags::WwiseFileTag, Audio::WwiseXmlTags::WwiseNameAttribute));
}
wwiseFileNode = wwiseFileNode->next_sibling(Audio::WwiseXmlTags::WwiseFileTag);
}
}
else
{
return AZ::Failure(AZStd::string::format("Preloads Xml appears to be in an older format, trying Legacy parsing.\n"));
}
preloadRequestNode = preloadRequestNode->next_sibling(Audio::ATLXmlTags::ATLPreloadRequestTag);
}
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> GetEventsFromBankMetadata(const rapidjson::Value& rootObject, AZStd::set<AZStd::string>& eventNames)
{
if (!rootObject.IsObject())
{
return AZ::Failure(AZStd::string("The root of the metadata file is not an object. Please regenerate the metadata for this soundbank."));
}
// If the file doesn't define an events field, then there are no events in the bank
if (!rootObject.HasMember(JsonEventsKey))
{
return AZ::Success();
}
const rapidjson::Value& eventsArray = rootObject[JsonEventsKey];
if (!eventsArray.IsArray())
{
return AZ::Failure(AZStd::string("Events field is not an array. Please regenerate the metadata for this soundbank."));
}
for (rapidjson::SizeType eventIndex = 0; eventIndex < eventsArray.Size(); ++eventIndex)
{
eventNames.emplace(eventsArray[eventIndex].GetString());
}
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> GetEventsFromBank(const AZStd::string& bankMetadataPath, AZStd::set<AZStd::string>& eventNames)
{
if (!AZ::IO::SystemFile::Exists(bankMetadataPath.c_str()))
{
return AZ::Failure(AZStd::string::format("Failed to find the soundbank metadata file %s. Full dependency information cannot be determined without the metadata file. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
}
AZ::u64 fileSize = AZ::IO::SystemFile::Length(bankMetadataPath.c_str());
if (fileSize == 0)
{
return AZ::Failure(AZStd::string::format("Soundbank metadata file at path %s is an empty file. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
}
AZStd::vector<char> buffer(fileSize + 1);
buffer[fileSize] = 0;
if (!AZ::IO::SystemFile::Read(bankMetadataPath.c_str(), buffer.data()))
{
return AZ::Failure(AZStd::string::format("Failed to read the soundbank metadata file at path %s. Please make sure the file is not open or being edited by another program.", bankMetadataPath.c_str()));
}
rapidjson::Document bankMetadataDoc;
bankMetadataDoc.Parse(buffer.data());
if (bankMetadataDoc.GetParseError() != rapidjson::ParseErrorCode::kParseErrorNone)
{
return AZ::Failure(AZStd::string::format("Failed to parse soundbank metadata at path %s into JSON. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
}
return GetEventsFromBankMetadata(bankMetadataDoc, eventNames);
}
} // namespace Internal
AudioControlBuilderWorker::AudioControlBuilderWorker()
: m_globalScopeControlsPath("libs/gameaudio/")
, m_isShuttingDown(false)
{
AZ::StringFunc::Path::Normalize(m_globalScopeControlsPath);
}
void AudioControlBuilderWorker::ShutDown()
{
m_isShuttingDown = true;
}
void AudioControlBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
{
if (m_isShuttingDown)
{
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
return;
}
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
{
if (info.m_identifier == "server")
{
continue;
}
AssetBuilderSDK::JobDescriptor descriptor;
descriptor.m_jobKey = "Audio Control";
descriptor.m_critical = true;
descriptor.SetPlatformIdentifier(info.m_identifier.c_str());
descriptor.m_priority = 0;
response.m_createJobOutputs.push_back(descriptor);
}
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
}
void AudioControlBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "AudioControlBuilderWorker Starting Job.\n");
if (m_isShuttingDown)
{
AZ_TracePrintf(AssetBuilderSDK::WarningWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
AZStd::string fileName;
AZ::StringFunc::Path::GetFullFileName(request.m_fullPath.c_str(), fileName);
AssetBuilderSDK::JobProduct jobProduct(request.m_fullPath);
if (!ParseProductDependencies(request, jobProduct.m_dependencies, jobProduct.m_pathDependencies))
{
AZ_Error(AssetBuilderSDK::ErrorWindow, false, "Error during parsing product dependencies for asset %s.\n", fileName.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return;
}
response.m_outputProducts.push_back(jobProduct);
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
}
bool AudioControlBuilderWorker::ParseProductDependencies(
const AssetBuilderSDK::ProcessJobRequest& request,
AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
AssetBuilderSDK::ProductPathDependencySet& pathDependencies)
{
AZ::IO::FileIOStream fileStream;
if (!fileStream.Open(request.m_fullPath.c_str(), AZ::IO::OpenMode::ModeRead))
{
return false;
}
AZ::IO::SizeType length = fileStream.GetLength();
if (length == 0)
{
return false;
}
AZStd::vector<char> charBuffer;
charBuffer.resize_no_construct(length + 1);
fileStream.Read(length, charBuffer.data());
charBuffer.back() = 0;
// Get the XML root node
AZ::rapidxml::xml_document<char> xmlDoc;
if (!xmlDoc.parse<AZ::rapidxml::parse_no_data_nodes>(charBuffer.data()))
{
return false;
}
AZ::rapidxml::xml_node<char>* xmlRootNode = xmlDoc.first_node();
if (!xmlRootNode)
{
return false;
}
ParseProductDependenciesFromXmlFile(xmlRootNode,
request.m_fullPath,
request.m_sourceFile,
request.m_platformInfo.m_identifier,
productDependencies,
pathDependencies);
return true;
}
void AudioControlBuilderWorker::ParseProductDependenciesFromXmlFile(
const AZ::rapidxml::xml_node<char>* node,
const AZStd::string& fullPath,
[[maybe_unused]] const AZStd::string& sourceFile,
const AZStd::string& platformIdentifier,
[[maybe_unused]] AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
AssetBuilderSDK::ProductPathDependencySet& pathDependencies)
{
AZ_Assert(node != nullptr, "AudioControlBuilderWorker::ParseProductDependenciesFromXmlFile - null xml root node!\n");
const auto preloadsNode = node->first_node(Audio::ATLXmlTags::PreloadsNodeTag);
if (!preloadsNode)
{
// No preloads were defined in this control file, so we can return. If triggers are defined in this preload file, we can't
// validate that they'll be playable because we are unsure of what other control files for the given scope are defined.
return;
}
// Collect any references to soundbanks, initially use the newer parsing format...
AZStd::vector<AZStd::string> banksReferenced;
AZ::Outcome<void, AZStd::string> gatherBankReferencesResult = Internal::GetBanksFromAtlPreloads(preloadsNode, banksReferenced);
if (!gatherBankReferencesResult)
{
// Legacy...
// Convert platform name to platform name that is used by wwise and ATL.
AZStd::string atlPlatformName = AZStd::move(Internal::Legacy::GetAtlPlatformName(platformIdentifier));
gatherBankReferencesResult = Internal::Legacy::GetBanksFromAtlPreloads(preloadsNode, atlPlatformName, banksReferenced);
}
if (!gatherBankReferencesResult)
{
AZ_Warning("Audio Control Builder", false, "Failed to gather product dependencies for Audio Control file %s. %s\n",
sourceFile.c_str(), gatherBankReferencesResult.GetError().c_str());
return;
}
if (banksReferenced.size() == 0)
{
// If there are no banks referenced, then there are no dependencies to register, so return.
return;
}
for (const AZStd::string& relativeBankPath : banksReferenced)
{
pathDependencies.emplace(relativeBankPath, AssetBuilderSDK::ProductPathDependencyType::ProductFile);
}
// For each bank figure out what events are included in the bank, then run through every event referenced in the file and
// make sure it is in the list gathered from the banks.
const auto triggersNode = node->first_node(Audio::ATLXmlTags::TriggersNodeTag);
if (!triggersNode)
{
// No triggers were defined in this file, so we don't need to do any event validation.
return;
}
AZStd::vector<AZStd::string> eventsReferenced;
AZ::Outcome<void, AZStd::string> gatherEventReferencesResult = Internal::BuildAtlEventList(triggersNode, eventsReferenced);
if (!gatherEventReferencesResult.IsSuccess())
{
AZ_Warning("Audio Control Builder", false, "Failed to gather list of events referenced by Audio Control file %s. %s",
sourceFile.c_str(), gatherEventReferencesResult.GetError().c_str());
return;
}
AZStd::string projectSourcePath = fullPath;
AZ::u64 firstSubDirectoryIndex = AZ::StringFunc::Find(projectSourcePath, m_globalScopeControlsPath);
AZ::StringFunc::LKeep(projectSourcePath, firstSubDirectoryIndex);
AZStd::set<AZStd::string> wwiseEventsInReferencedBanks;
// Load all bankdeps files for all banks referenced and aggregate the list of events in those files.
for (const AZStd::string& relativeBankPath : banksReferenced)
{
// Create the full path to the bankdeps file from the bank file.
AZStd::string bankMetadataPath;
AZ::StringFunc::Path::Join(projectSourcePath.c_str(), relativeBankPath.c_str(), bankMetadataPath);
AZ::StringFunc::Path::ReplaceExtension(bankMetadataPath, Internal::SoundbankDependencyFileExtension);
AZ::Outcome<void, AZStd::string> getReferencedEventsResult = Internal::GetEventsFromBank(bankMetadataPath, wwiseEventsInReferencedBanks);
if (!getReferencedEventsResult.IsSuccess())
{
// only warn if we couldn't get info from a bankdeps file. Won't impact registering dependencies, but used to help
// customers potentially debug issues.
AZ_Warning("Audio Control Builder", false, "Failed to gather list of events referenced by soundbank %s. %s", relativeBankPath.c_str(), getReferencedEventsResult.GetError().c_str());
}
}
// Confirm that each event referenced by the file exists in the list of events available from the banks referenced.
for (const AZStd::string& eventInControlFile : eventsReferenced)
{
if (wwiseEventsInReferencedBanks.find(eventInControlFile) == wwiseEventsInReferencedBanks.end())
{
AZ_Warning("Audio Control Builder", false, "Failed to find Wwise event %s in the list of events contained in banks referenced by %s. Event may fail to play properly.", eventInControlFile.c_str(), sourceFile.c_str());
}
}
}
} // namespace AudioControlBuilder
@@ -0,0 +1,58 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/XML/rapidxml.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
namespace AudioControlBuilder
{
//! The Audio Control Builder Worker handles scanning XML files that are output by the Audio Controls editor
//! for asset references to audio files and registers those files as product dependencies.
class AudioControlBuilderWorker
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
AZ_RTTI(AudioControlBuilderWorker, "{3AD18978-9025-482A-B06A-17EF0EB4D7CA}");
AudioControlBuilderWorker();
~AudioControlBuilderWorker() = default;
//! Asset Builder Callback Functions
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
//! AssetBuilderSDK::AssetBuilderCommandBus interface
void ShutDown() override;
bool ParseProductDependencies(
const AssetBuilderSDK::ProcessJobRequest& request,
AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
AssetBuilderSDK::ProductPathDependencySet& pathDependencies);
private:
void ParseProductDependenciesFromXmlFile(
const AZ::rapidxml::xml_node<char>* node,
const AZStd::string& fullPath,
const AZStd::string& sourceFile,
const AZStd::string& platformIdentifier,
AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
AssetBuilderSDK::ProductPathDependencySet& pathDependencies);
AZStd::string m_globalScopeControlsPath;
AZStd::atomic_bool m_isShuttingDown;
};
} // namespace AudioControlBuilder
@@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Builder/WwiseBuilderComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
namespace WwiseBuilder
{
void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<WwiseBuilder::BuilderPluginComponent, AZ::Component>()
->Version(1)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
;
}
}
void BuilderPluginComponent::Activate()
{
// Register Wwise builder
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
builderDescriptor.m_name = "Wwise Builder";
builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.bnk", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.wem", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
builderDescriptor.m_busId = azrtti_typeid<WwiseBuilderWorker>();
builderDescriptor.m_version = 2;
builderDescriptor.m_createJobFunction = AZStd::bind(&WwiseBuilderWorker::CreateJobs, &m_wwiseBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_processJobFunction = AZStd::bind(&WwiseBuilderWorker::ProcessJob, &m_wwiseBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
// (optimization) this builder does not emit source dependencies:
builderDescriptor.m_flags |= AssetBuilderSDK::AssetBuilderDesc::BF_EmitsNoDependencies;
m_wwiseBuilder.BusConnect(builderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Events::RegisterBuilderInformation, builderDescriptor);
}
void BuilderPluginComponent::Deactivate()
{
m_wwiseBuilder.BusDisconnect();
}
} // namespace WwiseBuilder
@@ -0,0 +1,50 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <Builder/WwiseBuilderWorker.h>
namespace WwiseBuilder
{
class BuilderPluginComponent
: public AZ::Component
{
public:
AZ_COMPONENT(BuilderPluginComponent, "{8630414A-0BA6-4759-809A-C6903994AE30}");
BuilderPluginComponent() = default;
~BuilderPluginComponent() override = default;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("WwiseBuilderService"));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("WwiseBuilderService"));
}
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
private:
WwiseBuilderWorker m_wwiseBuilder;
};
} // namespace WwiseBuilder
@@ -0,0 +1,290 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Builder/WwiseBuilderWorker.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/StringFunc/StringFunc.h>
namespace WwiseBuilder
{
const char WwiseBuilderWindowName[] = "WwiseBuilder";
namespace Internal
{
const char SoundbankDependencyFileExtension[] = ".bankdeps";
const char JsonDependencyKey[] = "dependencies";
AZ::Outcome<AZStd::string, AZStd::string> GetDependenciesFromMetadata(const rapidjson::Value& rootObject, AZStd::vector<AZStd::string>& fileNames)
{
if (!rootObject.IsObject())
{
return AZ::Failure(AZStd::string("The root of the metadata file is not an object. Please regenerate the metadata for this soundbank."));
}
// If the file doesn't define a dependency field, then there are no dependencies.
if (!rootObject.HasMember(JsonDependencyKey))
{
AZStd::string addingDefaultDependencyWarning = AZStd::string::format(
"Dependencies array does not exist. The file was likely manually edited. Registering a default "
"dependency on %s. Please regenerate the metadata for this bank.",
Audio::Wwise::InitBank);
return AZ::Success(addingDefaultDependencyWarning);
}
const rapidjson::Value& dependenciesArray = rootObject[JsonDependencyKey];
if (!dependenciesArray.IsArray())
{
return AZ::Failure(AZStd::string("Dependency field is not an array. Please regenerate the metadata for this soundbank."));
}
for (rapidjson::SizeType dependencyIndex = 0; dependencyIndex < dependenciesArray.Size(); ++dependencyIndex)
{
fileNames.push_back(dependenciesArray[dependencyIndex].GetString());
}
// The dependency array is empty, which likely means it was modified by hand. However, every bank is dependent
// on init.bnk (other than itself), so just force add it as a dependency here. and emit a warning.
if (fileNames.size() == 0)
{
AZStd::string addingDefaultDependencyWarning = AZStd::string::format(
"Dependencies array is empty. The file was likely manually edited. Registering a default "
"dependency on %s. Please regenerate the metadata for this bank.",
Audio::Wwise::InitBank);
return AZ::Success(addingDefaultDependencyWarning);
}
// Make sure init.bnk is in the dependency list. Force add it if it's not
else if (AZStd::find(fileNames.begin(), fileNames.end(), Audio::Wwise::InitBank) == fileNames.end())
{
AZStd::string addingDefaultDependencyWarning = AZStd::string::format(
"Dependencies does not contain the initialization bank. The file was likely manually edited to remove "
"it, however it is necessary for all banks to have the initialization bank loaded. Registering a "
"default dependency on %s. Please regenerate the metadata for this bank.",
Audio::Wwise::InitBank);
fileNames.push_back(Audio::Wwise::InitBank);
return AZ::Success(addingDefaultDependencyWarning);
}
return AZ::Success(AZStd::string());
}
}
WwiseBuilderWorker::WwiseBuilderWorker()
: m_isShuttingDown(false)
{
}
void WwiseBuilderWorker::ShutDown()
{
// This will be called on a different thread than the process job thread
m_isShuttingDown = true;
}
void WwiseBuilderWorker::Initialize()
{
AZ::IO::Path configFile("@devassets@");
configFile /= Audio::Wwise::DefaultBanksPath;
configFile /= Audio::Wwise::ConfigFile;
if (AZ::IO::FileIOBase::GetInstance()->Exists(configFile.c_str()))
{
m_wwiseConfig.Load(configFile.Native());
}
m_initialized = true;
}
// This happens early on in the file scanning pass.
// This function should always create the same jobs and not do any checking whether the job is up to date.
void WwiseBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
{
if (m_isShuttingDown)
{
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
return;
}
if (!m_initialized)
{
Initialize();
}
AZStd::string jobKey = "Wwise";
if (AZ::StringFunc::EndsWith(request.m_sourceFile, Audio::Wwise::MediaExtension))
{
jobKey.append(" Media");
}
else if (AZ::StringFunc::EndsWith(request.m_sourceFile, Audio::Wwise::BankExtension))
{
jobKey.append(" Bank");
}
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
{
// If there are no platform mappings (i.e. there was no config file), we want to
// process the job anyways.
bool createJob = m_wwiseConfig.m_platformMappings.empty();
// If the config file was parsed, need to filter out jobs that don't apply.
for (const auto& platformConfig : m_wwiseConfig.m_platformMappings)
{
// Check if the job request should go through.
if (info.m_identifier == platformConfig.m_assetPlatform
|| info.m_identifier == platformConfig.m_altAssetPlatform)
{
AZStd::string sourceFile(request.m_sourceFile);
AZStd::string_view banksPath(Audio::Wwise::DefaultBanksPath);
if (AZ::StringFunc::StartsWith(sourceFile, banksPath))
{
// Remove the leading banks path from the source file...
AZ::StringFunc::RKeep(sourceFile, banksPath.length(), true);
}
// If the source file now begins with the right Wwise platform folder, create the job...
if (AZ::StringFunc::StartsWith(sourceFile, platformConfig.m_wwisePlatform, true))
{
createJob = true;
break;
}
}
}
if (createJob)
{
AssetBuilderSDK::JobDescriptor descriptor;
descriptor.m_jobKey = jobKey;
descriptor.m_critical = true;
descriptor.SetPlatformIdentifier(info.m_identifier.c_str());
descriptor.m_priority = 0;
response.m_createJobOutputs.push_back(descriptor);
}
}
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
}
// The request will contain the CreateJobResponse you constructed earlier, including any keys and
// values you placed into the hash table
void WwiseBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Starting Job.\n");
AZ::IO::PathView fullPath(request.m_fullPath);
if (m_isShuttingDown)
{
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
else
{
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
AssetBuilderSDK::JobProduct jobProduct(request.m_fullPath);
// if the file is a bnk
AZ::IO::PathView requestExtension = fullPath.Extension();
if (requestExtension.Native() == Audio::Wwise::BankExtension)
{
AssetBuilderSDK::ProductPathDependencySet dependencyPaths;
// Push assets back into the response's product list
// Assets you created in your temp path can be specified using paths relative to the temp path
// since that is assumed where you're writing stuff.
AZ::Outcome<AZStd::string, AZStd::string> gatherProductDependenciesResponse = GatherProductDependencies(request.m_fullPath, request.m_sourceFile, dependencyPaths);
if (!gatherProductDependenciesResponse.IsSuccess())
{
AZ_Error(WwiseBuilderWindowName, false, "Dependency gathering for %s failed. %s",
request.m_fullPath.c_str(), gatherProductDependenciesResponse.GetError().c_str());
}
else
{
if (gatherProductDependenciesResponse.GetValue().empty())
{
AZ_Warning(WwiseBuilderWindowName, false, gatherProductDependenciesResponse.GetValue().c_str());
}
jobProduct.m_pathDependencies = AZStd::move(dependencyPaths);
}
}
response.m_outputProducts.push_back(jobProduct);
}
}
AZ::Outcome<AZStd::string, AZStd::string> WwiseBuilderWorker::GatherProductDependencies(const AZStd::string& fullPath, const AZStd::string& relativePath, AssetBuilderSDK::ProductPathDependencySet& dependencies)
{
AZ::IO::Path bankMetadataPath(fullPath);
bankMetadataPath.ReplaceExtension(Internal::SoundbankDependencyFileExtension);
AZ::IO::Path relativeSoundsPath(relativePath, AZ::IO::PosixPathSeparator);
relativeSoundsPath.RemoveFilename();
AZStd::string success_message;
// Look for the corresponding .bankdeps file next to the bank itself.
if (!AZ::IO::SystemFile::Exists(bankMetadataPath.c_str()))
{
// If this is the init bank, skip it. Otherwise, register the init bank as a dependency, and warn that a full
// dependency graph can't be created without a .bankdeps file for the bank.
AZ::IO::PathView requestFileName = AZ::IO::PathView(fullPath).Filename();
if (requestFileName != Audio::Wwise::InitBank)
{
success_message = AZStd::string::format("Failed to find the metadata file %s for soundbank %s. Full dependency information cannot be determined without the metadata file. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str(), fullPath.c_str());
}
return AZ::Success(success_message);
}
AZ::u64 fileSize = AZ::IO::SystemFile::Length(bankMetadataPath.c_str());
if (fileSize == 0)
{
return AZ::Failure(AZStd::string::format("Soundbank metadata file at path %s is an empty file. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
}
AZStd::vector<char> buffer(fileSize + 1);
buffer[fileSize] = 0;
if (!AZ::IO::SystemFile::Read(bankMetadataPath.c_str(), buffer.data()))
{
return AZ::Failure(AZStd::string::format("Failed to read the soundbank metadata file at path %s. Please make sure the file is not open or being edited by another program.", bankMetadataPath.c_str()));
}
// load the file
rapidjson::Document bankMetadataDoc;
bankMetadataDoc.Parse(buffer.data());
if (bankMetadataDoc.GetParseError() != rapidjson::ParseErrorCode::kParseErrorNone)
{
return AZ::Failure(AZStd::string::format("Failed to parse soundbank metadata at path %s into JSON. Please regenerate the metadata for this soundbank.", bankMetadataPath.c_str()));
}
AZStd::vector<AZStd::string> wwiseFiles;
AZ::Outcome<AZStd::string, AZStd::string> gatherDependenciesResult = Internal::GetDependenciesFromMetadata(bankMetadataDoc, wwiseFiles);
if (!gatherDependenciesResult.IsSuccess())
{
return AZ::Failure(AZStd::string::format("Failed to gather dependencies for %s from metadata file %s. %s", fullPath.c_str(), bankMetadataPath.c_str(), gatherDependenciesResult.GetError().c_str()));
}
else if (!gatherDependenciesResult.GetValue().empty())
{
success_message = AZStd::string::format("Dependency information for %s was unavailable in the metadata file %s. %s", fullPath.c_str(), bankMetadataPath.c_str(), gatherDependenciesResult.GetValue().c_str());
}
// Register dependencies stored in the file to the job response. (they'll be relative to the bank itself.)
for (const AZStd::string& wwiseFile : wwiseFiles)
{
dependencies.emplace((relativeSoundsPath / wwiseFile).Native(), AssetBuilderSDK::ProductPathDependencyType::ProductFile);
}
return AZ::Success(success_message);
}
} // namespace WwiseBuilder
@@ -0,0 +1,50 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/parallel/atomic.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <Engine/Config_wwise.h>
namespace WwiseBuilder
{
//! Wwise Builder is responsible for processing encoded audio media such as sound banks
class WwiseBuilderWorker
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
AZ_RTTI(WwiseBuilderWorker, "{85224E40-9211-4C05-9397-06E056470171}");
WwiseBuilderWorker();
~WwiseBuilderWorker() = default;
//! Asset Builder Callback Functions
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
//! AssetBuilderSDK::AssetBuilderCommandBus interface
void ShutDown() override;
AZ::Outcome<AZStd::string, AZStd::string> GatherProductDependencies(const AZStd::string& fullPath, const AZStd::string& relativePath, AssetBuilderSDK::ProductPathDependencySet& dependencies);
private:
void Initialize();
AZStd::atomic_bool m_isShuttingDown;
bool m_initialized = false;
Audio::Wwise::ConfigurationSettings m_wwiseConfig;
};
} // WwiseBuilder
@@ -0,0 +1,23 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <AudioSystemControl_wwise.h>
namespace AudioControls
{
//-------------------------------------------------------------------------------------------//
IAudioSystemControl_wwise::IAudioSystemControl_wwise(const AZStd::string& name, CID id, TImplControlType type)
: IAudioSystemControl(name, id, type)
{
}
} // namespace AudioControls
@@ -0,0 +1,44 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <IAudioInterfacesCommonData.h>
#include <IAudioSystemControl.h>
namespace AudioControls
{
enum EWwiseControlTypes
{
eWCT_INVALID = 0,
eWCT_WWISE_EVENT = AUDIO_BIT(0),
eWCT_WWISE_RTPC = AUDIO_BIT(1),
eWCT_WWISE_SWITCH = AUDIO_BIT(2),
eWCT_WWISE_AUX_BUS = AUDIO_BIT(3),
eWCT_WWISE_SOUND_BANK = AUDIO_BIT(4),
eWCT_WWISE_GAME_STATE = AUDIO_BIT(5),
eWCT_WWISE_SWITCH_GROUP = AUDIO_BIT(6),
eWCT_WWISE_GAME_STATE_GROUP = AUDIO_BIT(7),
};
//-------------------------------------------------------------------------------------------//
class IAudioSystemControl_wwise
: public IAudioSystemControl
{
public:
IAudioSystemControl_wwise() {}
IAudioSystemControl_wwise(const AZStd::string& name, CID id, TImplControlType type);
~IAudioSystemControl_wwise() override {}
};
} // namespace AudioControls
@@ -0,0 +1,550 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <AudioSystemEditor_wwise.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <ACETypes.h>
#include <AudioSystemControl_wwise.h>
#include <Common_wwise.h>
#include <ISystem.h>
#include <CryFile.h>
#include <CryPath.h>
#include <Util/PathUtil.h>
namespace AudioControls
{
//-------------------------------------------------------------------------------------------//
TImplControlType TagToType(const AZStd::string_view tag)
{
if (tag == Audio::WwiseXmlTags::WwiseEventTag)
{
return eWCT_WWISE_EVENT;
}
else if (tag == Audio::WwiseXmlTags::WwiseRtpcTag)
{
return eWCT_WWISE_RTPC;
}
else if (tag == Audio::WwiseXmlTags::WwiseAuxBusTag)
{
return eWCT_WWISE_AUX_BUS;
}
else if (tag == Audio::WwiseXmlTags::WwiseFileTag)
{
return eWCT_WWISE_SOUND_BANK;
}
else if (tag == Audio::WwiseXmlTags::WwiseSwitchTag)
{
return eWCT_WWISE_SWITCH_GROUP;
}
else if (tag == Audio::WwiseXmlTags::WwiseStateTag)
{
return eWCT_WWISE_GAME_STATE_GROUP;
}
return eWCT_INVALID;
}
//-------------------------------------------------------------------------------------------//
const AZStd::string_view TypeToTag(const TImplControlType type)
{
switch (type)
{
case eWCT_WWISE_EVENT:
return Audio::WwiseXmlTags::WwiseEventTag;
case eWCT_WWISE_RTPC:
return Audio::WwiseXmlTags::WwiseRtpcTag;
case eWCT_WWISE_SWITCH:
return Audio::WwiseXmlTags::WwiseValueTag;
case eWCT_WWISE_AUX_BUS:
return Audio::WwiseXmlTags::WwiseAuxBusTag;
case eWCT_WWISE_SOUND_BANK:
return Audio::WwiseXmlTags::WwiseFileTag;
case eWCT_WWISE_GAME_STATE:
return Audio::WwiseXmlTags::WwiseValueTag;
case eWCT_WWISE_SWITCH_GROUP:
return Audio::WwiseXmlTags::WwiseSwitchTag;
case eWCT_WWISE_GAME_STATE_GROUP:
return Audio::WwiseXmlTags::WwiseStateTag;
}
return "";
}
//-------------------------------------------------------------------------------------------//
void CAudioSystemEditor_wwise::Reload()
{
// set all the controls as placeholder as we don't know if
// any of them have been removed but still have connections to them
for (const auto& idControlPair : m_controls)
{
TControlPtr control = idControlPair.second;
if (control)
{
control->SetPlaceholder(true);
}
}
// reload data
m_loader.Load(this);
m_connectionsByID.clear();
UpdateConnectedStatus();
}
//-------------------------------------------------------------------------------------------//
IAudioSystemControl* CAudioSystemEditor_wwise::CreateControl(const SControlDef& controlDefinition)
{
AZStd::string fullName = controlDefinition.m_name;
IAudioSystemControl* parent = controlDefinition.m_parentControl;
if (parent)
{
AZ::StringFunc::Path::Join(controlDefinition.m_parentControl->GetName().c_str(), fullName.c_str(), fullName);
}
if (!controlDefinition.m_path.empty())
{
AZ::StringFunc::Path::Join(controlDefinition.m_path.c_str(), fullName.c_str(), fullName);
}
CID id = GetID(fullName);
IAudioSystemControl* control = GetControl(id);
if (control)
{
if (control->IsPlaceholder())
{
control->SetPlaceholder(false);
if (parent && parent->IsPlaceholder())
{
parent->SetPlaceholder(false);
}
}
return control;
}
else
{
TControlPtr newControl = AZStd::make_shared<IAudioSystemControl_wwise>(controlDefinition.m_name, id, controlDefinition.m_type);
if (!parent)
{
parent = &m_rootControl;
}
parent->AddChild(newControl.get());
newControl->SetParent(parent);
newControl->SetLocalized(controlDefinition.m_isLocalized);
m_controls[id] = newControl;
return newControl.get();
}
}
//-------------------------------------------------------------------------------------------//
IAudioSystemControl* CAudioSystemEditor_wwise::GetControl(CID id) const
{
if (id != ACE_INVALID_CID)
{
auto it = m_controls.find(id);
if (it != m_controls.end())
{
return it->second.get();
}
}
return nullptr;
}
//-------------------------------------------------------------------------------------------//
IAudioSystemControl* CAudioSystemEditor_wwise::GetControlByName(AZStd::string name, bool isLocalized, IAudioSystemControl* parent) const
{
if (parent)
{
AZ::StringFunc::Path::Join(parent->GetName().c_str(), name.c_str(), name);
}
if (isLocalized)
{
AZ::StringFunc::Path::Join(m_loader.GetLocalizationFolder().c_str(), name.c_str(), name);
}
return GetControl(GetID(name));
}
//-------------------------------------------------------------------------------------------//
TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionToControl(EACEControlType atlControlType, IAudioSystemControl* middlewareControl)
{
if (middlewareControl)
{
middlewareControl->SetConnected(true);
++m_connectionsByID[middlewareControl->GetId()];
if (middlewareControl->GetType() == eWCT_WWISE_RTPC)
{
switch (atlControlType)
{
case EACEControlType::eACET_RTPC:
{
return AZStd::make_shared<CRtpcConnection>(middlewareControl->GetId());
}
case EACEControlType::eACET_SWITCH_STATE:
{
return AZStd::make_shared<CStateToRtpcConnection>(middlewareControl->GetId());
}
}
}
return AZStd::make_shared<IAudioConnection>(middlewareControl->GetId());
}
return nullptr;
}
//-------------------------------------------------------------------------------------------//
TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType)
{
if (node)
{
const AZStd::string tag(node->getTag());
TImplControlType type = TagToType(tag);
if (type != AUDIO_IMPL_INVALID_TYPE)
{
AZStd::string name(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute));
AZStd::string localized(node->getAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute));
// Legacy Preload support
if (localized.empty())
{
localized = node->getAttr(Audio::WwiseXmlTags::Legacy::WwiseLocalizedAttribute);
}
bool isLocalized = AZ::StringFunc::Equal(localized.c_str(), "true");
// If control not found, create a placeholder.
// We want to keep that connection even if it's not in the middleware.
// The user could be using the engine without the wwise project
IAudioSystemControl* control = GetControlByName(name, isLocalized);
if (!control)
{
control = CreateControl(SControlDef(name, type));
if (control)
{
control->SetPlaceholder(true);
control->SetLocalized(isLocalized);
}
}
// If it's a switch we actually connect to one of the states within the switch
if (type == eWCT_WWISE_SWITCH_GROUP || type == eWCT_WWISE_GAME_STATE_GROUP)
{
if (node->getChildCount() == 1)
{
node = node->getChild(0);
if (node)
{
AZStd::string childName(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute));
IAudioSystemControl* childControl = GetControlByName(childName, false, control);
if (!childControl)
{
childControl = CreateControl(SControlDef(childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control));
}
control = childControl;
}
}
else
{
CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "Audio Controls Editor (Wwise): Error reading connection to Wwise control %s", name.c_str());
}
}
if (control)
{
control->SetConnected(true);
++m_connectionsByID[control->GetId()];
if (type == eWCT_WWISE_RTPC)
{
switch (atlControlType)
{
case EACEControlType::eACET_RTPC:
{
TRtpcConnectionPtr connection = AZStd::make_shared<CRtpcConnection>(control->GetId());
float mult = 1.0f;
float shift = 0.0f;
if (node->haveAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute))
{
const AZStd::string multProperty(node->getAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute));
mult = AZStd::stof(multProperty);
}
if (node->haveAttr(Audio::WwiseXmlTags::WwiseShiftAttribute))
{
const AZStd::string shiftProperty(node->getAttr(Audio::WwiseXmlTags::WwiseShiftAttribute));
shift = AZStd::stof(shiftProperty);
}
connection->m_mult = mult;
connection->m_shift = shift;
return connection;
}
case EACEControlType::eACET_SWITCH_STATE:
{
TStateConnectionPtr connection = AZStd::make_shared<CStateToRtpcConnection>(control->GetId());
float value = 0.0f;
if (node->haveAttr(Audio::WwiseXmlTags::WwiseValueAttribute))
{
const AZStd::string valueProperty(node->getAttr(Audio::WwiseXmlTags::WwiseValueAttribute));
value = AZStd::stof(valueProperty);
}
connection->m_value = value;
return connection;
}
}
}
else
{
return AZStd::make_shared<IAudioConnection>(control->GetId());
}
}
}
}
return nullptr;
}
//-------------------------------------------------------------------------------------------//
XmlNodeRef CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType)
{
const IAudioSystemControl* control = GetControl(connection->GetID());
if (control)
{
switch (control->GetType())
{
case AudioControls::eWCT_WWISE_SWITCH:
case AudioControls::eWCT_WWISE_SWITCH_GROUP:
case AudioControls::eWCT_WWISE_GAME_STATE:
case AudioControls::eWCT_WWISE_GAME_STATE_GROUP:
{
const IAudioSystemControl* parent = control->GetParent();
if (parent)
{
XmlNodeRef switchNode = GetISystem()->CreateXmlNode(TypeToTag(parent->GetType()).data());
switchNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, parent->GetName().c_str());
XmlNodeRef stateNode = switchNode->createNode(Audio::WwiseXmlTags::WwiseValueTag);
stateNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
switchNode->addChild(stateNode);
return switchNode;
}
break;
}
case AudioControls::eWCT_WWISE_RTPC:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
if (atlControlType == eACET_RTPC)
{
AZStd::shared_ptr<const CRtpcConnection> rtpcConnection = AZStd::static_pointer_cast<const CRtpcConnection>(connection);
if (rtpcConnection->m_mult != 1.0f)
{
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute, rtpcConnection->m_mult);
}
if (rtpcConnection->m_shift != 0.0f)
{
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseShiftAttribute, rtpcConnection->m_shift);
}
}
else if (atlControlType == eACET_SWITCH_STATE)
{
AZStd::shared_ptr<const CStateToRtpcConnection> stateConnection = AZStd::static_pointer_cast<const CStateToRtpcConnection>(connection);
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseValueAttribute, stateConnection->m_value);
}
return connectionNode;
}
case AudioControls::eWCT_WWISE_EVENT:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
return connectionNode;
}
case AudioControls::eWCT_WWISE_AUX_BUS:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
return connectionNode;
}
case AudioControls::eWCT_WWISE_SOUND_BANK:
{
XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data());
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str());
if (control->IsLocalized())
{
connectionNode->setAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute, "true");
}
return connectionNode;
}
}
}
return nullptr;
}
//-------------------------------------------------------------------------------------------//
const AZStd::string_view CAudioSystemEditor_wwise::GetTypeIcon(TImplControlType type) const
{
switch (type)
{
case eWCT_WWISE_EVENT:
return ":/Editor/WwiseIcons/event_nor.svg";
case eWCT_WWISE_RTPC:
return ":/Editor/WwiseIcons/gameparameter_nor.svg";
case eWCT_WWISE_SWITCH:
return ":/Editor/WwiseIcons/switch_nor.svg";
case eWCT_WWISE_AUX_BUS:
return ":/Editor/WwiseIcons/auxbus_nor.svg";
case eWCT_WWISE_SOUND_BANK:
return ":/Editor/WwiseIcons/soundbank_nor.svg";
case eWCT_WWISE_GAME_STATE:
return ":/Editor/WwiseIcons/state_nor.svg";
case eWCT_WWISE_SWITCH_GROUP:
return ":/Editor/WwiseIcons/switchgroup_nor.svg";
case eWCT_WWISE_GAME_STATE_GROUP:
return ":/Editor/WwiseIcons/stategroup_nor.svg";
default:
// should make a "default"/empty icon...
return ":/Editor/WwiseIcons/switchgroup_nor.svg";
}
}
const AZStd::string_view CAudioSystemEditor_wwise::GetTypeIconSelected(TImplControlType type) const
{
switch (type)
{
case eWCT_WWISE_EVENT:
return ":/Editor/WwiseIcons/event_nor_hover.svg";
case eWCT_WWISE_RTPC:
return ":/Editor/WwiseIcons/gameparameter_nor_hover.svg";
case eWCT_WWISE_SWITCH:
return ":/Editor/WwiseIcons/switch_nor_hover.svg";
case eWCT_WWISE_AUX_BUS:
return ":/Editor/WwiseIcons/auxbus_nor_hover.svg";
case eWCT_WWISE_SOUND_BANK:
return ":/Editor/WwiseIcons/soundbank_nor_hover.svg";
case eWCT_WWISE_GAME_STATE:
return ":/Editor/WwiseIcons/state_nor_hover.svg";
case eWCT_WWISE_SWITCH_GROUP:
return ":/Editor/WwiseIcons/switchgroup_nor_hover.svg";
case eWCT_WWISE_GAME_STATE_GROUP:
return ":/Editor/WwiseIcons/stategroup_nor_hover.svg";
default:
// should make a "default"/empty icon...
return ":/Editor/WwiseIcons/switchgroup_nor_hover.svg";
}
}
//-------------------------------------------------------------------------------------------//
EACEControlType CAudioSystemEditor_wwise::ImplTypeToATLType(TImplControlType type) const
{
switch (type)
{
case eWCT_WWISE_EVENT:
return eACET_TRIGGER;
case eWCT_WWISE_RTPC:
return eACET_RTPC;
case eWCT_WWISE_SWITCH:
case eWCT_WWISE_GAME_STATE:
return eACET_SWITCH_STATE;
case eWCT_WWISE_AUX_BUS:
return eACET_ENVIRONMENT;
case eWCT_WWISE_SOUND_BANK:
return eACET_PRELOAD;
case eWCT_WWISE_GAME_STATE_GROUP:
case eWCT_WWISE_SWITCH_GROUP:
return eACET_SWITCH;
}
return eACET_NUM_TYPES;
}
//-------------------------------------------------------------------------------------------//
TImplControlTypeMask CAudioSystemEditor_wwise::GetCompatibleTypes(EACEControlType atlControlType) const
{
switch (atlControlType)
{
case eACET_TRIGGER:
return eWCT_WWISE_EVENT;
case eACET_RTPC:
return eWCT_WWISE_RTPC;
case eACET_SWITCH:
return AUDIO_IMPL_INVALID_TYPE;
case eACET_SWITCH_STATE:
return (eWCT_WWISE_SWITCH | eWCT_WWISE_GAME_STATE | eWCT_WWISE_RTPC);
case eACET_ENVIRONMENT:
return (eWCT_WWISE_AUX_BUS | eWCT_WWISE_SWITCH | eWCT_WWISE_GAME_STATE | eWCT_WWISE_RTPC);
case eACET_PRELOAD:
return eWCT_WWISE_SOUND_BANK;
}
return AUDIO_IMPL_INVALID_TYPE;
}
//-------------------------------------------------------------------------------------------//
CID CAudioSystemEditor_wwise::GetID(const AZStd::string_view name) const
{
return Audio::AudioStringToID<CID>(name.data());
}
//-------------------------------------------------------------------------------------------//
AZStd::string CAudioSystemEditor_wwise::GetName() const
{
return "Wwise";
}
//-------------------------------------------------------------------------------------------//
void CAudioSystemEditor_wwise::UpdateConnectedStatus()
{
for (const auto& idCountPair : m_connectionsByID)
{
if (idCountPair.second > 0)
{
IAudioSystemControl* control = GetControl(idCountPair.first);
if (control)
{
control->SetConnected(true);
}
}
}
}
//-------------------------------------------------------------------------------------------//
void CAudioSystemEditor_wwise::ConnectionRemoved(IAudioSystemControl* control)
{
int connectionCount = m_connectionsByID[control->GetId()] - 1;
if (connectionCount <= 0)
{
connectionCount = 0;
control->SetConnected(false);
}
m_connectionsByID[control->GetId()] = connectionCount;
}
//-------------------------------------------------------------------------------------------//
AZStd::string CAudioSystemEditor_wwise::GetDataPath() const
{
AZStd::string path(Path::GetEditingGameDataFolder());
AZ::StringFunc::Path::Join(path.c_str(), "sounds/wwise_project/", path);
return path;
}
} // namespace AudioControls
@@ -0,0 +1,125 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <IAudioSystemEditor.h>
#include <IAudioConnection.h>
#include <IAudioSystemControl.h>
#include <AudioWwiseLoader.h>
namespace AudioControls
{
//-------------------------------------------------------------------------------------------//
class CRtpcConnection
: public IAudioConnection
{
public:
explicit CRtpcConnection(CID id)
: IAudioConnection(id)
, m_mult(1.0f)
, m_shift(0.0f)
{}
~CRtpcConnection() override = default;
bool HasProperties() override { return true; }
void Serialize(Serialization::IArchive& ar) override
{
ar(m_mult, "mult", "Multiply");
ar(m_shift, "shift", "Shift");
}
float m_mult;
float m_shift;
};
using TRtpcConnectionPtr = AZStd::shared_ptr<CRtpcConnection>;
//-------------------------------------------------------------------------------------------//
class CStateToRtpcConnection
: public IAudioConnection
{
public:
explicit CStateToRtpcConnection(CID id)
: IAudioConnection(id)
, m_value(0.0f)
{}
~CStateToRtpcConnection() override = default;
bool HasProperties() override { return true; }
void Serialize(Serialization::IArchive& ar) override
{
ar(m_value, "value", "Value");
}
float m_value;
};
using TStateConnectionPtr = AZStd::shared_ptr<CStateToRtpcConnection>;
//-------------------------------------------------------------------------------------------//
class CAudioSystemEditor_wwise
: public IAudioSystemEditor
{
friend class CAudioWwiseLoader;
public:
CAudioSystemEditor_wwise() = default;
~CAudioSystemEditor_wwise() override = default;
//////////////////////////////////////////////////////////
// IAudioSystemEditor implementation
/////////////////////////////////////////////////////////
void Reload() override;
IAudioSystemControl* CreateControl(const SControlDef& controlDefinition) override;
IAudioSystemControl* GetRoot() override { return &m_rootControl; }
IAudioSystemControl* GetControl(CID id) const override;
EACEControlType ImplTypeToATLType(TImplControlType type) const override;
TImplControlTypeMask GetCompatibleTypes(EACEControlType atlControlType) const override;
TConnectionPtr CreateConnectionToControl(EACEControlType atlControlType, IAudioSystemControl* middlewareControl) override;
TConnectionPtr CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) override;
XmlNodeRef CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override;
const AZStd::string_view GetTypeIcon(TImplControlType type) const override;
const AZStd::string_view GetTypeIconSelected(TImplControlType type) const override;
AZStd::string GetName() const override;
AZStd::string GetDataPath() const;
void DataSaved() override {}
void ConnectionRemoved(IAudioSystemControl* control) override;
//////////////////////////////////////////////////////////
private:
IAudioSystemControl* GetControlByName(AZStd::string name, bool isLocalized = false, IAudioSystemControl* parent = nullptr) const;
// Gets the ID of the control given its name. As controls can have the same name
// if they're under different parents, the name of the parent is also needed (if there is one)
CID GetID(const AZStd::string_view name) const;
void UpdateConnectedStatus();
IAudioSystemControl m_rootControl;
using TControlPtr = AZStd::shared_ptr<IAudioSystemControl>;
using TControlMap = AZStd::unordered_map<CID, TControlPtr>;
TControlMap m_controls;
using TConnectionsMap = AZStd::unordered_map<CID, int>;
TConnectionsMap m_connectionsByID;
CAudioWwiseLoader m_loader;
};
} // namespace AudioControls
@@ -0,0 +1,196 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <AudioWwiseLoader.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <IAudioSystemControl.h>
#include <IAudioSystemEditor.h>
#include <AudioSystemEditor_wwise.h>
#include <AudioFileUtils.h>
#include <Config_wwise.h>
#include <ISystem.h>
#include <CryFile.h>
#include <CryPath.h>
#include <Util/PathUtil.h>
using namespace PathUtil;
namespace AudioControls
{
namespace WwiseStrings
{
// Wwise Project Folders
static constexpr const char GameParametersFolder[] = "Game Parameters";
static constexpr const char GameStatesFolder[] = "States";
static constexpr const char SwitchesFolder[] = "Switches";
static constexpr const char EventsFolder[] = "Events";
static constexpr const char EnvironmentsFolder[] = "Master-Mixer Hierarchy";
// Wwise Xml Tags
static constexpr const char GameParameterTag[] = "GameParameter";
static constexpr const char EventTag[] = "Event";
static constexpr const char AuxBusTag[] = "AuxBus";
static constexpr const char SwitchGroupTag[] = "SwitchGroup";
static constexpr const char StateGroupTag[] = "StateGroup";
static constexpr const char ChildrenListTag[] = "ChildrenList";
static constexpr const char NameAttribute[] = "Name";
} // namespace WwiseStrings
//-------------------------------------------------------------------------------------------//
void CAudioWwiseLoader::Load(CAudioSystemEditor_wwise* audioSystemImpl)
{
m_audioSystemImpl = audioSystemImpl;
const AZStd::string wwiseProjectFullPath(m_audioSystemImpl->GetDataPath());
LoadControlsInFolder(wwiseProjectFullPath + WwiseStrings::GameParametersFolder);
LoadControlsInFolder(wwiseProjectFullPath + WwiseStrings::GameStatesFolder);
LoadControlsInFolder(wwiseProjectFullPath + WwiseStrings::SwitchesFolder);
LoadControlsInFolder(wwiseProjectFullPath + WwiseStrings::EventsFolder);
LoadControlsInFolder(wwiseProjectFullPath + WwiseStrings::EnvironmentsFolder);
LoadSoundBanks(Audio::Wwise::GetBanksRootPath(), "", false);
}
//-------------------------------------------------------------------------------------------//
void CAudioWwiseLoader::LoadSoundBanks(const AZStd::string_view rootFolder, const AZStd::string_view subPath, bool isLocalized)
{
auto foundFiles = Audio::FindFilesInPath(rootFolder, "*");
bool isLocalizedLoaded = isLocalized;
for (const auto& filePath : foundFiles)
{
AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", filePath.c_str());
AZStd::string fileName;
AZ::StringFunc::Path::GetFullFileName(filePath.c_str(), fileName);
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str()))
{
if (fileName != Audio::Wwise::ExternalSourcesPath && !isLocalizedLoaded)
{
// each sub-folder represents a different language,
// we load only one as all of them should have the
// same content (in the future we want to have a
// consistency report to highlight if this is not the case)
m_localizationFolder = fileName;
LoadSoundBanks(rootFolder, m_localizationFolder, true);
isLocalizedLoaded = true;
}
}
else if (AZ::StringFunc::Find(fileName.c_str(), Audio::Wwise::BankExtension) != AZStd::string::npos
&& !AZ::StringFunc::Equal(fileName.c_str(), Audio::Wwise::InitBank))
{
m_audioSystemImpl->CreateControl(SControlDef(fileName, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath));
}
}
}
//-------------------------------------------------------------------------------------------//
void CAudioWwiseLoader::LoadControlsInFolder(const AZStd::string_view folderPath)
{
auto foundFiles = Audio::FindFilesInPath(folderPath, "*");
for (const auto& filePath : foundFiles)
{
AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", filePath.c_str());
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str()))
{
LoadControlsInFolder(filePath);
}
else
{
// Open the file, read into an xmlDoc, and call LoadControls with the root xml node...
AZ_TracePrintf("AudioWwiseLoader", "Loading Xml from '%s'", filePath.c_str());
Audio::ScopedXmlLoader xmlFileLoader(filePath);
if (!xmlFileLoader.HasError())
{
LoadControl(xmlFileLoader.GetRootNode());
}
}
}
}
//-------------------------------------------------------------------------------------------//
void CAudioWwiseLoader::ExtractControlsFromXML(const AZ::rapidxml::xml_node<char>* xmlNode, EWwiseControlTypes type, const AZStd::string_view controlTag, const AZStd::string_view controlNameAttribute)
{
AZStd::string_view xmlTag(xmlNode->name());
if (xmlTag == controlTag)
{
if (auto nameAttr = xmlNode->first_attribute(controlNameAttribute.data()))
{
m_audioSystemImpl->CreateControl(SControlDef(nameAttr->value(), type));
}
}
}
//-------------------------------------------------------------------------------------------//
void CAudioWwiseLoader::LoadControl(const AZ::rapidxml::xml_node<char>* xmlNode)
{
if (!xmlNode)
{
return;
}
ExtractControlsFromXML(xmlNode, eWCT_WWISE_RTPC, WwiseStrings::GameParameterTag, WwiseStrings::NameAttribute);
ExtractControlsFromXML(xmlNode, eWCT_WWISE_EVENT, WwiseStrings::EventTag, WwiseStrings::NameAttribute);
ExtractControlsFromXML(xmlNode, eWCT_WWISE_AUX_BUS, WwiseStrings::AuxBusTag, WwiseStrings::NameAttribute);
AZStd::string_view xmlTag(xmlNode->name());
bool isSwitchTag = (xmlTag == WwiseStrings::SwitchGroupTag);
bool isStateTag = (xmlTag == WwiseStrings::StateGroupTag);
if (isSwitchTag || isStateTag)
{
if (auto nameAttr = xmlNode->first_attribute(WwiseStrings::NameAttribute))
{
const AZStd::string parentName(nameAttr->value());
IAudioSystemControl* group = m_audioSystemImpl->GetControlByName(parentName);
if (!group)
{
group = m_audioSystemImpl->CreateControl(SControlDef(parentName, isSwitchTag ? eWCT_WWISE_SWITCH_GROUP : eWCT_WWISE_GAME_STATE_GROUP));
}
auto childrenNode = xmlNode->first_node(WwiseStrings::ChildrenListTag);
if (childrenNode)
{
auto childNode = childrenNode->first_node();
while (childNode)
{
if (auto childNameAttr = childNode->first_attribute(WwiseStrings::NameAttribute))
{
m_audioSystemImpl->CreateControl(SControlDef(childNameAttr->value(), isSwitchTag ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, group));
}
childNode = childNode->next_sibling();
}
}
}
}
auto childNode = xmlNode->first_node();
while (childNode)
{
LoadControl(childNode);
childNode = childNode->next_sibling();
}
}
//-------------------------------------------------------------------------------------------//
const AZStd::string& CAudioWwiseLoader::GetLocalizationFolder() const
{
return m_localizationFolder;
}
} // namespace AudioControls
@@ -0,0 +1,44 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <ACETypes.h>
#include <AzCore/std/string/string_view.h>
#include <AudioSystemControl_wwise.h>
#include <AzCore/XML/rapidxml.h>
namespace AudioControls
{
class CAudioSystemEditor_wwise;
//-------------------------------------------------------------------------------------------//
class CAudioWwiseLoader
{
public:
CAudioWwiseLoader() = default;
void Load(CAudioSystemEditor_wwise* audioSystemImpl);
const AZStd::string& GetLocalizationFolder() const;
private:
void LoadSoundBanks(const AZStd::string_view rootFolder, const AZStd::string_view subPath, bool isLocalized);
void LoadControlsInFolder(const AZStd::string_view folderPath);
void LoadControl(const AZ::rapidxml::xml_node<char>* xmlNode);
void ExtractControlsFromXML(const AZ::rapidxml::xml_node<char>* xmlNode, EWwiseControlTypes type, const AZStd::string_view controlTag, const AZStd::string_view controlNameAttribute);
private:
AZStd::string m_localizationFolder;
CAudioSystemEditor_wwise* m_audioSystemImpl = nullptr;
};
} // namespace AudioControls
@@ -0,0 +1,20 @@
<RCC>
<qresource prefix="/Editor/WwiseIcons">
<file alias="auxbus_nor.svg">WwiseIcons/auxbus_nor.svg</file>
<file alias="auxbus_nor_hover.svg">WwiseIcons/auxbus_nor_hover.svg</file>
<file alias="event_nor.svg">WwiseIcons/event_nor.svg</file>
<file alias="event_nor_hover.svg">WwiseIcons/event_nor_hover.svg</file>
<file alias="gameparameter_nor.svg">WwiseIcons/gameparameter_nor.svg</file>
<file alias="gameparameter_nor_hover.svg">WwiseIcons/gameparameter_nor_hover.svg</file>
<file alias="soundbank_nor.svg">WwiseIcons/soundbank_nor.svg</file>
<file alias="soundbank_nor_hover.svg">WwiseIcons/soundbank_nor_hover.svg</file>
<file alias="state_nor.svg">WwiseIcons/state_nor.svg</file>
<file alias="state_nor_hover.svg">WwiseIcons/state_nor_hover.svg</file>
<file alias="stategroup_nor.svg">WwiseIcons/stategroup_nor.svg</file>
<file alias="stategroup_nor_hover.svg">WwiseIcons/stategroup_nor_hover.svg</file>
<file alias="switch_nor.svg">WwiseIcons/switch_nor.svg</file>
<file alias="switch_nor_hover.svg">WwiseIcons/switch_nor_hover.svg</file>
<file alias="switchgroup_nor.svg">WwiseIcons/switchgroup_nor.svg</file>
<file alias="switchgroup_nor_hover.svg">WwiseIcons/switchgroup_nor_hover.svg</file>
</qresource>
</RCC>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / auxbus_nor</title>
<g id="icon-/-General-/-WWise-controls-/-auxbus_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<g id="Group" transform="translate(2.500000, 2.500000)" fill="#FFFFFF">
<rect id="Rectangle-Copy-65" x="7.85015167" y="4.85500591" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-68" transform="translate(1.500000, 1.650384) rotate(-90.000000) translate(-1.500000, -1.650384) " x="0" y="1" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-73" transform="translate(3.497744, 1.650384) rotate(-90.000000) translate(-3.497744, -1.650384) " x="1.99774393" y="1" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-74" fill-opacity="0.5" transform="translate(1.500000, 3.805871) rotate(-90.000000) translate(-1.500000, -3.805871) " x="0.844513191" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-75" fill-opacity="0.5" transform="translate(3.497744, 3.805871) rotate(-90.000000) translate(-3.497744, -3.805871) " x="2.84225712" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-76" fill-opacity="0.5" transform="translate(5.495488, 3.805871) rotate(-90.000000) translate(-5.495488, -3.805871) " x="4.84000105" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-77" transform="translate(5.495488, 1.650384) rotate(-90.000000) translate(-5.495488, -1.650384) " x="3.99548786" y="1" width="3" height="1.30076785"></rect>
<polygon id="Rectangle-Copy-72" points="0.850151669 4.15044404 6.85015167 4.15044404 6.85015167 10.150444 3.850212 10.150444 0.850151669 7.25011322"></polygon>
<rect id="Rectangle-Copy-66" x="7.85015167" y="6.83454936" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-69" fill-opacity="0.5" x="6.53917805" y="4.85888821" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-70" fill-opacity="0.5" x="6.53917805" y="6.83454936" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-71" fill-opacity="0.5" x="6.53917805" y="8.84967619" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-67" x="7.85015167" y="8.84967619" width="3" height="1.30076785"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / auxbus_nor - hover</title>
<g id="icon-/-General-/-WWise-controls-/-auxbus_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<g id="Group" transform="translate(2.500000, 2.500000)" fill="#FFFFFF">
<rect id="Rectangle-Copy-65" x="7.85015167" y="4.85500591" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-68" transform="translate(1.500000, 1.650384) rotate(-90.000000) translate(-1.500000, -1.650384) " x="0" y="1" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-73" transform="translate(3.497744, 1.650384) rotate(-90.000000) translate(-3.497744, -1.650384) " x="1.99774393" y="1" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-74" fill-opacity="0.5" transform="translate(1.500000, 3.805871) rotate(-90.000000) translate(-1.500000, -3.805871) " x="0.844513191" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-75" fill-opacity="0.5" transform="translate(3.497744, 3.805871) rotate(-90.000000) translate(-3.497744, -3.805871) " x="2.84225712" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-76" fill-opacity="0.5" transform="translate(5.495488, 3.805871) rotate(-90.000000) translate(-5.495488, -3.805871) " x="4.84000105" y="3.15548681" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-77" transform="translate(5.495488, 1.650384) rotate(-90.000000) translate(-5.495488, -1.650384) " x="3.99548786" y="1" width="3" height="1.30076785"></rect>
<polygon id="Rectangle-Copy-72" points="0.850151669 4.15044404 6.85015167 4.15044404 6.85015167 10.150444 3.850212 10.150444 0.850151669 7.25011322"></polygon>
<rect id="Rectangle-Copy-66" x="7.85015167" y="6.83454936" width="3" height="1.30076785"></rect>
<rect id="Rectangle-Copy-69" fill-opacity="0.5" x="6.53917805" y="4.85888821" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-70" fill-opacity="0.5" x="6.53917805" y="6.83454936" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-71" fill-opacity="0.5" x="6.53917805" y="8.84967619" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-67" x="7.85015167" y="8.84967619" width="3" height="1.30076785"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
<title>WWise controls icon </title>
<desc>Created with Sketch.</desc>
<g id="WWise-controls-icon-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
<g id="Group" transform="translate(1.500000, 4.000000)" fill="#FFFFFF">
<rect id="Rectangle" x="0" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
<rect id="Rectangle-Copy-65" x="9.93618454" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
<rect id="Rectangle-Copy-72" x="3.2944383" y="0" width="5.53787113" height="7.67747148"></rect>
<rect id="Rectangle-Copy-66" x="9.93618454" y="3.27125666" width="2.19056317" height="1.09528159"></rect>
<rect id="Rectangle-Copy-68" fill-opacity="0.5" x="2.19056317" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-69" fill-opacity="0.5" x="8.83230942" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-70" fill-opacity="0.5" x="8.83230942" y="3.27125666" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-71" fill-opacity="0.5" x="8.83230942" y="5.44723173" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-67" x="9.93618454" y="5.44723173" width="2.19056317" height="1.09528159"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
<title>WWise controls icon hover</title>
<desc>Created with Sketch.</desc>
<g id="WWise-controls-icon--hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
<g id="Group" transform="translate(1.500000, 4.000000)" fill="#FFFFFF">
<rect id="Rectangle" x="0" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
<rect id="Rectangle-Copy-65" x="9.93618454" y="1.09528159" width="2.19056317" height="1.09528159"></rect>
<rect id="Rectangle-Copy-72" x="3.2944383" y="0" width="5.53787113" height="7.67747148"></rect>
<rect id="Rectangle-Copy-66" x="9.93618454" y="3.27125666" width="2.19056317" height="1.09528159"></rect>
<rect id="Rectangle-Copy-68" fill-opacity="0.5" x="2.19056317" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-69" fill-opacity="0.5" x="8.83230942" y="1.09528159" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-70" fill-opacity="0.5" x="8.83230942" y="3.27125666" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-71" fill-opacity="0.5" x="8.83230942" y="5.44723173" width="1.10387512" height="1.09528159"></rect>
<rect id="Rectangle-Copy-67" x="9.93618454" y="5.44723173" width="2.19056317" height="1.09528159"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / gameparameter_nor</title>
<g id="icon-/-General-/-WWise-controls-/-gameparameter_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<path d="M9.49786987,3.50689788 L9.497,4.38189788 L14.4704694,4.38189788 L14.4704694,5.63189788 L9.497,5.63189788 L9.49786987,6.50689788 L8.625,6.50689788 L8.625,9.46089788 L9.5,9.46186067 L9.5,12.4618607 L6.5,12.4618607 L6.5,11.5858979 L1.375,11.5868607 L1.375,10.3368607 L6.5,10.3358979 L6.5,9.46186067 L7.375,9.46089788 L7.375,6.50689788 L6.49786987,6.50689788 L6.49786987,3.50689788 L9.49786987,3.50689788 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / gameparameter_nor - hover</title>
<g id="icon-/-General-/-WWise-controls-/-gameparameter_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<path d="M9.49786987,3.50689788 L9.497,4.38189788 L14.4704694,4.38189788 L14.4704694,5.63189788 L9.497,5.63189788 L9.49786987,6.50689788 L8.625,6.50689788 L8.625,9.46089788 L9.5,9.46186067 L9.5,12.4618607 L6.5,12.4618607 L6.5,11.5858979 L1.375,11.5868607 L1.375,10.3368607 L6.5,10.3358979 L6.5,9.46186067 L7.375,9.46089788 L7.375,6.50689788 L6.49786987,6.50689788 L6.49786987,3.50689788 L9.49786987,3.50689788 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / soundbank_nor</title>
<g id="icon-/-General-/-WWise-controls-/-soundbank_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<rect id="Rectangle-Copy-65" fill="#FFFFFF" x="12.2531833" y="5.30992926" width="2.60153569" height="1.30076785"></rect>
<g id="Group" transform="translate(3.091323, 8.309929) scale(-1, 1) translate(-3.091323, -8.309929) translate(1.091323, 5.309929)" fill="#FFFFFF">
<rect id="Rectangle-Copy-68" x="1.31097362" y="1.11022302e-16" width="2.60153569" height="1.30076785"></rect>
<rect id="Rectangle-Copy-73" x="1.31097362" y="2.03968682" width="2.60153569" height="1.30076785"></rect>
<rect id="Rectangle-Copy-74" fill-opacity="0.5" x="-2.9798386e-13" y="1.11022302e-16" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-75" fill-opacity="0.5" x="-2.9798386e-13" y="2.03968682" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-76" fill-opacity="0.5" x="-2.9798386e-13" y="4.07937363" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-77" x="1.31097362" y="4.07937363" width="2.60153569" height="1.30076785"></rect>
</g>
<path d="M11.5,3.5 L11.5,12.5 L4.45636666,12.5 L4.45636666,3.5 L11.5,3.5 Z M8.10401855,4.32750739 C7.75245605,4.32750739 7.42937662,4.36738369 7.13478026,4.44713629 C6.84018391,4.5268889 6.58627766,4.6448902 6.37306151,4.8011402 C6.15984537,4.9573902 5.99464355,5.15026129 5.87745605,5.37975348 C5.76026855,5.60924567 5.7016748,5.87373135 5.7016748,6.17321051 C5.7016748,6.46617926 5.75782714,6.7168303 5.87013183,6.92516364 C5.98243651,7.13349697 6.12973469,7.31171963 6.31202636,7.45983161 C6.49431803,7.60794358 6.70265136,7.7324553 6.93702636,7.83336676 C7.17140136,7.93427822 7.40984537,8.02623786 7.65235839,8.10924567 C7.89487141,8.19225348 8.13331542,8.27119228 8.36769042,8.34606207 C8.60206542,8.42093187 8.81039875,8.50475348 8.99269042,8.59752692 C9.17498209,8.69030036 9.32228026,8.80016364 9.43458495,8.92711676 C9.54688964,9.05406989 9.60304198,9.20869228 9.60304198,9.39098395 C9.60304198,9.59931728 9.56397948,9.77428473 9.48585448,9.91588629 C9.40772948,10.0574879 9.30356282,10.1722339 9.17335448,10.2601246 C9.04314615,10.3480152 8.89259276,10.4114918 8.72169433,10.4505543 C8.55079589,10.4896168 8.37257323,10.509148 8.18702636,10.509148 C7.97543782,10.509148 7.78175292,10.4708993 7.60597167,10.3944019 C7.43019042,10.3179045 7.27719563,10.2096688 7.1469873,10.0696949 C7.01677896,9.92972093 6.91179849,9.7628915 6.83204589,9.56920661 C6.77223144,9.42394293 6.73026977,9.26723517 6.70616088,9.09908331 L6.68800292,8.92711676 L5.51124511,8.92711676 C5.52101073,9.32425218 5.60320474,9.67988369 5.75782714,9.99401129 C5.91244954,10.3081389 6.11427245,10.5758798 6.36329589,10.7972339 C6.61231933,11.0185881 6.89470865,11.1878589 7.21046386,11.3050464 C7.52621907,11.4222339 7.8517399,11.4808277 8.18702636,11.4808277 C8.56137532,11.4808277 8.9056136,11.4368824 9.2197412,11.3489918 C9.53386881,11.2611011 9.8056787,11.130079 10.0351709,10.9559254 C10.2646631,10.7817717 10.4436995,10.5644866 10.5722803,10.3040699 C10.700861,10.0436532 10.7651514,9.73929124 10.7651514,9.39098395 C10.7651514,9.10452562 10.708999,8.85957119 10.5966943,8.65612067 C10.4843896,8.45267015 10.3370915,8.2785165 10.1547998,8.13365973 C9.97250813,7.98880296 9.7641748,7.86510504 9.5297998,7.76256598 C9.2954248,7.66002692 9.05616698,7.56725348 8.81202636,7.48424567 C8.56788573,7.40123786 8.32862792,7.32148525 8.09425292,7.24498786 C7.85987792,7.16849046 7.65154459,7.08222744 7.46925292,6.98619879 C7.28696125,6.89017015 7.13966308,6.77786546 7.02735839,6.64928473 C6.9150537,6.520704 6.85890136,6.3620126 6.85890136,6.17321051 C6.85890136,6.0104501 6.89552245,5.87291754 6.96876464,5.76061286 C7.04200683,5.64830817 7.13722167,5.55716233 7.25440917,5.48717536 C7.37159667,5.41718838 7.50424641,5.36673265 7.65235839,5.33580817 C7.80047037,5.30488369 7.95102375,5.28942145 8.10401855,5.28942145 C8.27328938,5.28942145 8.43279459,5.31464931 8.58253417,5.36510504 C8.73227375,5.41556077 8.86492349,5.49287197 8.98048339,5.59703864 C9.09604329,5.7012053 9.19125813,5.83304124 9.26612792,5.99254645 C9.32228026,6.11217536 9.36286864,6.24828375 9.38789306,6.40087164 L9.40772948,6.5589527 L10.5844873,6.5589527 C10.5714665,6.38968187 10.5454248,6.21715582 10.5063623,6.04137457 C10.4672998,5.86559332 10.4078922,5.69469489 10.3281396,5.52867926 C10.248387,5.36266364 10.1466618,5.20722744 10.0229639,5.06237067 C9.89926594,4.9175139 9.74708495,4.79056077 9.56642089,4.68151129 C9.38575683,4.57246181 9.17498209,4.48619879 8.93409667,4.42272223 C8.69321125,4.35924567 8.41651855,4.32750739 8.10401855,4.32750739 Z" id="Rectangle-Copy-78" fill="#FFFFFF"></path>
<rect id="Rectangle-Copy-66" fill="#FFFFFF" x="12.2531833" y="7.34961608" width="2.60153569" height="1.30076785"></rect>
<rect id="Rectangle-Copy-69" fill-opacity="0.5" fill="#FFFFFF" x="10.9422097" y="5.30992926" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-70" fill-opacity="0.5" fill="#FFFFFF" x="10.9422097" y="7.34961608" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-71" fill-opacity="0.5" fill="#FFFFFF" x="10.9422097" y="9.38930289" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-67" fill="#FFFFFF" x="12.2531833" y="9.38930289" width="2.60153569" height="1.30076785"></rect>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / soundbank_nor - hover</title>
<g id="icon-/-General-/-WWise-controls-/-soundbank_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<rect id="Rectangle-Copy-65" fill="#FFFFFF" x="12.2531833" y="5.30992926" width="2.60153569" height="1.30076785"></rect>
<g id="Group" transform="translate(3.091323, 8.309929) scale(-1, 1) translate(-3.091323, -8.309929) translate(1.091323, 5.309929)" fill="#FFFFFF">
<rect id="Rectangle-Copy-68" x="1.31097362" y="1.11022302e-16" width="2.60153569" height="1.30076785"></rect>
<rect id="Rectangle-Copy-73" x="1.31097362" y="2.03968682" width="2.60153569" height="1.30076785"></rect>
<rect id="Rectangle-Copy-74" fill-opacity="0.5" x="-2.9798386e-13" y="1.11022302e-16" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-75" fill-opacity="0.5" x="-2.9798386e-13" y="2.03968682" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-76" fill-opacity="0.5" x="-2.9798386e-13" y="4.07937363" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-77" x="1.31097362" y="4.07937363" width="2.60153569" height="1.30076785"></rect>
</g>
<path d="M11.5,3.5 L11.5,12.5 L4.45636666,12.5 L4.45636666,3.5 L11.5,3.5 Z M8.10401855,4.32750739 C7.75245605,4.32750739 7.42937662,4.36738369 7.13478026,4.44713629 C6.84018391,4.5268889 6.58627766,4.6448902 6.37306151,4.8011402 C6.15984537,4.9573902 5.99464355,5.15026129 5.87745605,5.37975348 C5.76026855,5.60924567 5.7016748,5.87373135 5.7016748,6.17321051 C5.7016748,6.46617926 5.75782714,6.7168303 5.87013183,6.92516364 C5.98243651,7.13349697 6.12973469,7.31171963 6.31202636,7.45983161 C6.49431803,7.60794358 6.70265136,7.7324553 6.93702636,7.83336676 C7.17140136,7.93427822 7.40984537,8.02623786 7.65235839,8.10924567 C7.89487141,8.19225348 8.13331542,8.27119228 8.36769042,8.34606207 C8.60206542,8.42093187 8.81039875,8.50475348 8.99269042,8.59752692 C9.17498209,8.69030036 9.32228026,8.80016364 9.43458495,8.92711676 C9.54688964,9.05406989 9.60304198,9.20869228 9.60304198,9.39098395 C9.60304198,9.59931728 9.56397948,9.77428473 9.48585448,9.91588629 C9.40772948,10.0574879 9.30356282,10.1722339 9.17335448,10.2601246 C9.04314615,10.3480152 8.89259276,10.4114918 8.72169433,10.4505543 C8.55079589,10.4896168 8.37257323,10.509148 8.18702636,10.509148 C7.97543782,10.509148 7.78175292,10.4708993 7.60597167,10.3944019 C7.43019042,10.3179045 7.27719563,10.2096688 7.1469873,10.0696949 C7.01677896,9.92972093 6.91179849,9.7628915 6.83204589,9.56920661 C6.77223144,9.42394293 6.73026977,9.26723517 6.70616088,9.09908331 L6.68800292,8.92711676 L5.51124511,8.92711676 C5.52101073,9.32425218 5.60320474,9.67988369 5.75782714,9.99401129 C5.91244954,10.3081389 6.11427245,10.5758798 6.36329589,10.7972339 C6.61231933,11.0185881 6.89470865,11.1878589 7.21046386,11.3050464 C7.52621907,11.4222339 7.8517399,11.4808277 8.18702636,11.4808277 C8.56137532,11.4808277 8.9056136,11.4368824 9.2197412,11.3489918 C9.53386881,11.2611011 9.8056787,11.130079 10.0351709,10.9559254 C10.2646631,10.7817717 10.4436995,10.5644866 10.5722803,10.3040699 C10.700861,10.0436532 10.7651514,9.73929124 10.7651514,9.39098395 C10.7651514,9.10452562 10.708999,8.85957119 10.5966943,8.65612067 C10.4843896,8.45267015 10.3370915,8.2785165 10.1547998,8.13365973 C9.97250813,7.98880296 9.7641748,7.86510504 9.5297998,7.76256598 C9.2954248,7.66002692 9.05616698,7.56725348 8.81202636,7.48424567 C8.56788573,7.40123786 8.32862792,7.32148525 8.09425292,7.24498786 C7.85987792,7.16849046 7.65154459,7.08222744 7.46925292,6.98619879 C7.28696125,6.89017015 7.13966308,6.77786546 7.02735839,6.64928473 C6.9150537,6.520704 6.85890136,6.3620126 6.85890136,6.17321051 C6.85890136,6.0104501 6.89552245,5.87291754 6.96876464,5.76061286 C7.04200683,5.64830817 7.13722167,5.55716233 7.25440917,5.48717536 C7.37159667,5.41718838 7.50424641,5.36673265 7.65235839,5.33580817 C7.80047037,5.30488369 7.95102375,5.28942145 8.10401855,5.28942145 C8.27328938,5.28942145 8.43279459,5.31464931 8.58253417,5.36510504 C8.73227375,5.41556077 8.86492349,5.49287197 8.98048339,5.59703864 C9.09604329,5.7012053 9.19125813,5.83304124 9.26612792,5.99254645 C9.32228026,6.11217536 9.36286864,6.24828375 9.38789306,6.40087164 L9.40772948,6.5589527 L10.5844873,6.5589527 C10.5714665,6.38968187 10.5454248,6.21715582 10.5063623,6.04137457 C10.4672998,5.86559332 10.4078922,5.69469489 10.3281396,5.52867926 C10.248387,5.36266364 10.1466618,5.20722744 10.0229639,5.06237067 C9.89926594,4.9175139 9.74708495,4.79056077 9.56642089,4.68151129 C9.38575683,4.57246181 9.17498209,4.48619879 8.93409667,4.42272223 C8.69321125,4.35924567 8.41651855,4.32750739 8.10401855,4.32750739 Z" id="Rectangle-Copy-78" fill="#FFFFFF"></path>
<rect id="Rectangle-Copy-66" fill="#FFFFFF" x="12.2531833" y="7.34961608" width="2.60153569" height="1.30076785"></rect>
<rect id="Rectangle-Copy-69" fill-opacity="0.5" fill="#FFFFFF" x="10.9422097" y="5.30992926" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-70" fill-opacity="0.5" fill="#FFFFFF" x="10.9422097" y="7.34961608" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-71" fill-opacity="0.5" fill="#FFFFFF" x="10.9422097" y="9.38930289" width="1.31097362" height="1.30076785"></rect>
<rect id="Rectangle-Copy-67" fill="#FFFFFF" x="12.2531833" y="9.38930289" width="2.60153569" height="1.30076785"></rect>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / state_nor</title>
<defs>
<path d="M7.97092008,3.98274536 C7.96690592,4.36330533 7.97319095,5.36748326 7.98977517,8.01719254 C7.97410642,10.5206355 7.9768247,11.5552026 7.97635312,11.9827454 C10.1830707,11.975596 11.9697467,10.1874775 11.9697467,7.98274536 C11.9697467,5.7762181 10.1801373,3.98697455 7.97092008,3.98274536 L7.97092008,3.98274536 Z M8,13.5 C4.96243388,13.5 2.5,11.0375661 2.5,8 C2.5,4.96243388 4.96243388,2.5 8,2.5 C11.0375661,2.5 13.5,4.96243388 13.5,8 C13.5,11.0375661 11.0375661,13.5 8,13.5 Z" id="path-1"></path>
</defs>
<g id="icon-/-General-/-WWise-controls-/-state_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Shape" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-1"></use>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / state_nor - hover</title>
<defs>
<path d="M7.97092008,3.98274536 C7.96690592,4.36330533 7.97319095,5.36748326 7.98977517,8.01719254 C7.97410642,10.5206355 7.9768247,11.5552026 7.97635312,11.9827454 C10.1830707,11.975596 11.9697467,10.1874775 11.9697467,7.98274536 C11.9697467,5.7762181 10.1801373,3.98697455 7.97092008,3.98274536 L7.97092008,3.98274536 Z M8,13.5 C4.96243388,13.5 2.5,11.0375661 2.5,8 C2.5,4.96243388 4.96243388,2.5 8,2.5 C11.0375661,2.5 13.5,4.96243388 13.5,8 C13.5,11.0375661 11.0375661,13.5 8,13.5 Z" id="path-1"></path>
</defs>
<g id="icon-/-General-/-WWise-controls-/-state_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Shape" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-1"></use>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / stategroup_nor</title>
<g id="icon-/-General-/-WWise-controls-/-stategroup_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<g id="Group" transform="translate(1.500000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
<path d="M6.49154389,1 C6.48953681,1.19027999 6.49267932,1.69236895 6.50097143,3.01722359 C6.49313705,4.26894508 6.4944962,4.78622861 6.49426041,5 C7.59761917,4.9964253 8.49095719,4.10236608 8.49095719,3 C8.49095719,1.89673637 7.59615251,1.00211459 6.49154389,1 L6.49154389,1 Z M6.49095719,6 C4.83410294,6 3.49095719,4.65685425 3.49095719,3 C3.49095719,1.34314575 4.83410294,0 6.49095719,0 C8.14781143,0 9.49095719,1.34314575 9.49095719,3 C9.49095719,4.65685425 8.14781143,6 6.49095719,6 Z" id="Shape-Copy"></path>
<path d="M9.49154389,7 C9.48953681,7.19027999 9.49267932,7.69236895 9.50097143,9.01722359 C9.49313705,10.2689451 9.4944962,10.7862286 9.49426041,11 C10.5976192,10.9964253 11.4909572,10.1023661 11.4909572,9 C11.4909572,7.89673637 10.5961525,7.00211459 9.49154389,7 L9.49154389,7 Z M9.49095719,12 C7.83410294,12 6.49095719,10.6568542 6.49095719,9 C6.49095719,7.34314575 7.83410294,6 9.49095719,6 C11.1478114,6 12.4909572,7.34314575 12.4909572,9 C12.4909572,10.6568542 11.1478114,12 9.49095719,12 Z" id="Shape-Copy-2"></path>
<path d="M3.0005867,7 C2.99857962,7.19027999 3.00172214,7.69236895 3.01001424,9.01722359 C3.00217987,10.2689451 3.00353901,10.7862286 3.00330322,11 C4.10666199,10.9964253 5,10.1023661 5,9 C5,7.89673637 4.10519532,7.00211459 3.0005867,7 L3.0005867,7 Z M3,12 C1.34314575,12 0,10.6568542 0,9 C0,7.34314575 1.34314575,6 3,6 C4.65685425,6 6,7.34314575 6,9 C6,10.6568542 4.65685425,12 3,12 Z" id="Shape-Copy-3"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / stategroup_nor - hover</title>
<g id="icon-/-General-/-WWise-controls-/-stategroup_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<g id="Group" transform="translate(1.500000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
<path d="M6.49154389,1 C6.48953681,1.19027999 6.49267932,1.69236895 6.50097143,3.01722359 C6.49313705,4.26894508 6.4944962,4.78622861 6.49426041,5 C7.59761917,4.9964253 8.49095719,4.10236608 8.49095719,3 C8.49095719,1.89673637 7.59615251,1.00211459 6.49154389,1 L6.49154389,1 Z M6.49095719,6 C4.83410294,6 3.49095719,4.65685425 3.49095719,3 C3.49095719,1.34314575 4.83410294,0 6.49095719,0 C8.14781143,0 9.49095719,1.34314575 9.49095719,3 C9.49095719,4.65685425 8.14781143,6 6.49095719,6 Z" id="Shape-Copy"></path>
<path d="M9.49154389,7 C9.48953681,7.19027999 9.49267932,7.69236895 9.50097143,9.01722359 C9.49313705,10.2689451 9.4944962,10.7862286 9.49426041,11 C10.5976192,10.9964253 11.4909572,10.1023661 11.4909572,9 C11.4909572,7.89673637 10.5961525,7.00211459 9.49154389,7 L9.49154389,7 Z M9.49095719,12 C7.83410294,12 6.49095719,10.6568542 6.49095719,9 C6.49095719,7.34314575 7.83410294,6 9.49095719,6 C11.1478114,6 12.4909572,7.34314575 12.4909572,9 C12.4909572,10.6568542 11.1478114,12 9.49095719,12 Z" id="Shape-Copy-2"></path>
<path d="M3.0005867,7 C2.99857962,7.19027999 3.00172214,7.69236895 3.01001424,9.01722359 C3.00217987,10.2689451 3.00353901,10.7862286 3.00330322,11 C4.10666199,10.9964253 5,10.1023661 5,9 C5,7.89673637 4.10519532,7.00211459 3.0005867,7 L3.0005867,7 Z M3,12 C1.34314575,12 0,10.6568542 0,9 C0,7.34314575 1.34314575,6 3,6 C4.65685425,6 6,7.34314575 6,9 C6,10.6568542 4.65685425,12 3,12 Z" id="Shape-Copy-3"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / switch_nor</title>
<g id="icon-/-General-/-WWise-controls-/-switch_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<path d="M12.5,3.5 L12.5,12.5 L3.5,12.5 L3.5,3.5 L12.5,3.5 Z M10.5,5.5 L5.5,5.5 L5.5,10.5 L10.5,10.5 L10.5,5.5 Z" id="Combined-Shape-Copy-2" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 914 B

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / switch_nor - hover</title>
<g id="icon-/-General-/-WWise-controls-/-switch_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<path d="M12.5,3.5 L12.5,12.5 L3.5,12.5 L3.5,3.5 L12.5,3.5 Z M10.5,5.5 L5.5,5.5 L5.5,10.5 L10.5,10.5 L10.5,5.5 Z" id="Combined-Shape-Copy-2" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 930 B

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / switchgroup_nor</title>
<g id="icon-/-General-/-WWise-controls-/-switchgroup_nor" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#333333" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<path d="M10.5,10.5 L10.5,13.5 L7.5,13.5 L7.5,10.5 L10.5,10.5 Z M6.5,2.5 L6.5,13.5 L5.25,13.5 L5.25,2.5 L6.5,2.5 Z M9.5,11.5 L8.5,11.5 L8.5,12.5 L9.5,12.5 L9.5,11.5 Z M10.5,6.5 L10.5,9.5 L7.5,9.5 L7.5,6.5 L10.5,6.5 Z M9.5,7.5 L8.5,7.5 L8.5,8.5 L9.5,8.5 L9.5,7.5 Z M10.5,2.5 L10.5,5.5 L7.5,5.5 L7.5,2.5 L10.5,2.5 Z M9.5,3.5 L8.5,3.5 L8.5,4.5 L9.5,4.5 L9.5,3.5 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #565656;">
<title>icon / General / WWise controls / switchgroup_nor - hover</title>
<g id="icon-/-General-/-WWise-controls-/-switchgroup_nor---hover" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect fill="#565656" x="0" y="0" width="16" height="16"></rect>
<g id="Icons-/-main-window-/-WWise-controls-">
<rect id="Rectangle" fill="#565656" x="0" y="0" width="16" height="16"></rect>
<rect id="Rectangle" fill="#17A3CD" x="0" y="0" width="16" height="16" rx="2"></rect>
</g>
<path d="M10.5,10.5 L10.5,13.5 L7.5,13.5 L7.5,10.5 L10.5,10.5 Z M6.5,2.5 L6.5,13.5 L5.25,13.5 L5.25,2.5 L6.5,2.5 Z M9.5,11.5 L8.5,11.5 L8.5,12.5 L9.5,12.5 L9.5,11.5 Z M10.5,6.5 L10.5,9.5 L7.5,9.5 L7.5,6.5 L10.5,6.5 Z M9.5,7.5 L8.5,7.5 L8.5,8.5 L9.5,8.5 L9.5,7.5 Z M10.5,2.5 L10.5,5.5 L7.5,5.5 L7.5,2.5 L10.5,2.5 Z M9.5,3.5 L8.5,3.5 L8.5,4.5 L9.5,4.5 L9.5,3.5 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,215 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <ATLEntityData.h>
#include <IAudioInterfacesCommonData.h>
#include <AudioAllocators.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/vector.h>
#include <AK/SoundEngine/Common/AkTypes.h>
#include <AK/AkWwiseSDKVersion.h>
namespace Audio
{
using TAKUniqueIDVector = AZStd::vector<AkUniqueID, Audio::AudioImplStdAllocator>;
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLAudioObjectData_wwise
: public IATLAudioObjectData
{
// convert to ATLMapLookupType
using TEnvironmentImplMap = AZStd::map<AkAuxBusID, float, AZStd::less<AkAuxBusID>, Audio::AudioImplStdAllocator>;
SATLAudioObjectData_wwise(const AkGameObjectID nPassedAKID, const bool bPassedHasPosition)
: bNeedsToUpdateEnvironments(false)
, bHasPosition(bPassedHasPosition)
, nAKID(nPassedAKID)
{}
~SATLAudioObjectData_wwise() override {}
bool bNeedsToUpdateEnvironments;
const bool bHasPosition;
const AkGameObjectID nAKID;
TEnvironmentImplMap cEnvironmentImplAmounts;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLListenerData_wwise
: public IATLListenerData
{
explicit SATLListenerData_wwise(const AkGameObjectID passedObjectId)
: nAKListenerObjectId(passedObjectId)
{}
~SATLListenerData_wwise() override {}
const AkGameObjectID nAKListenerObjectId = AK_INVALID_GAME_OBJECT;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLTriggerImplData_wwise
: public IATLTriggerImplData
{
explicit SATLTriggerImplData_wwise(const AkUniqueID nPassedAKID)
: nAKID(nPassedAKID)
{}
~SATLTriggerImplData_wwise() override {}
const AkUniqueID nAKID;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLRtpcImplData_wwise
: public IATLRtpcImplData
{
SATLRtpcImplData_wwise(const AkRtpcID nPassedAKID, const float m_fPassedMult, const float m_fPassedShift)
: m_fMult(m_fPassedMult)
, m_fShift(m_fPassedShift)
, nAKID(nPassedAKID)
{}
~SATLRtpcImplData_wwise() override {}
const float m_fMult;
const float m_fShift;
const AkRtpcID nAKID;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
enum EWwiseSwitchType : TATLEnumFlagsType
{
eWST_NONE = 0,
eWST_SWITCH = 1,
eWST_STATE = 2,
eWST_RTPC = 3,
};
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLSwitchStateImplData_wwise
: public IATLSwitchStateImplData
{
SATLSwitchStateImplData_wwise(
const EWwiseSwitchType ePassedType,
const AkUInt32 nPassedAKSwitchID,
const AkUInt32 nPassedAKStateID,
const float fPassedRtpcValue = 0.0f)
: eType(ePassedType)
, nAKSwitchID(nPassedAKSwitchID)
, nAKStateID(nPassedAKStateID)
, fRtpcValue(fPassedRtpcValue)
{}
~SATLSwitchStateImplData_wwise() override {}
const EWwiseSwitchType eType;
const AkUInt32 nAKSwitchID;
const AkUInt32 nAKStateID;
const float fRtpcValue;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
enum EWwiseAudioEnvironmentType : TATLEnumFlagsType
{
eWAET_NONE = 0,
eWAET_AUX_BUS = 1,
eWAET_RTPC = 2,
};
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLEnvironmentImplData_wwise
: public IATLEnvironmentImplData
{
explicit SATLEnvironmentImplData_wwise(const EWwiseAudioEnvironmentType ePassedType)
: eType(ePassedType)
{}
SATLEnvironmentImplData_wwise(const EWwiseAudioEnvironmentType ePassedType, const AkAuxBusID nPassedAKBusID)
: eType(ePassedType)
, nAKBusID(nPassedAKBusID)
{
AZ_Assert(ePassedType == eWAET_AUX_BUS, "SATLEnvironmentImplData_wwise - type is incorrect, expected an Aux Bus!");
}
SATLEnvironmentImplData_wwise(
const EWwiseAudioEnvironmentType ePassedType,
const AkRtpcID nPassedAKRtpcID,
const float fPassedMult,
const float fPassedShift)
: eType(ePassedType)
, nAKRtpcID(nPassedAKRtpcID)
, fMult(fPassedMult)
, fShift(fPassedShift)
{
AZ_Assert(ePassedType == eWAET_RTPC, "SATLEnvironmentImplData_wwise - type is incorrect, expected an RTPC!");
}
~SATLEnvironmentImplData_wwise() override {}
const EWwiseAudioEnvironmentType eType;
union
{
// Aux Bus implementation
struct
{
AkAuxBusID nAKBusID;
};
// Rtpc implementation
struct
{
AkRtpcID nAKRtpcID;
float fMult;
float fShift;
};
};
};
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLEventData_wwise
: public IATLEventData
{
explicit SATLEventData_wwise(const TAudioEventID nPassedID)
: audioEventState(eAES_NONE)
, nAKID(AK_INVALID_UNIQUE_ID)
, nATLID(nPassedID)
, nSourceId(INVALID_AUDIO_SOURCE_ID)
{}
~SATLEventData_wwise() override {}
EAudioEventState audioEventState;
AkUniqueID nAKID;
const TAudioEventID nATLID;
TAudioSourceId nSourceId;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
struct SATLAudioFileEntryData_wwise
: public IATLAudioFileEntryData
{
SATLAudioFileEntryData_wwise()
: nAKBankID(AK_INVALID_BANK_ID)
{}
~SATLAudioFileEntryData_wwise() override {}
AkBankID nAKBankID;
};
} // namespace Audio
@@ -0,0 +1,224 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AudioInput/AudioInputFile.h>
#include <AudioInput/WavParser.h>
#include <Common_wwise.h>
#include <AzCore/IO/FileIO.h>
#include <AK/SoundEngine/Common/AkStreamMgrModule.h>
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////
// Audio Input File
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
AudioInputFile::AudioInputFile(const SAudioInputConfig& sourceConfig)
{
m_config = sourceConfig;
switch (sourceConfig.m_sourceType)
{
case AudioInputSourceType::WavFile:
m_parser.reset(aznew WavFileParser());
break;
case AudioInputSourceType::PcmFile:
break;
default:
return;
}
LoadFile();
}
///////////////////////////////////////////////////////////////////////////////////////////////
AudioInputFile::~AudioInputFile()
{
UnloadFile();
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool AudioInputFile::LoadFile()
{
bool result = false;
// Filename should be relative to the project assets root e.g.: 'sounds/files/my_sound.wav'
AZ::IO::FileIOStream fileStream(m_config.m_sourceFilename.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary);
if (fileStream.IsOpen())
{
m_dataSize = fileStream.GetLength();
if (m_dataSize > 0)
{
// Here if a parser is available, can pass the file stream forward
// so it can parse header information.
// It will return the number of header bytes read, that is an offset to
// the beginning of the real signal data.
if (m_parser)
{
size_t headerBytesRead = m_parser->ParseHeader(fileStream);
if (headerBytesRead > 0 && m_parser->IsHeaderValid())
{
// Update the size...
m_dataSize = m_parser->GetDataSize();
// Set the format configuration obtained from the file...
m_config.m_bitsPerSample = m_parser->GetBitsPerSample();
m_config.m_numChannels = m_parser->GetNumChannels();
m_config.m_sampleRate = m_parser->GetSampleRate();
m_config.m_sampleType = m_parser->GetSampleType();
}
}
if (IsOk())
{
// Allocate a new buffer to hold the data...
m_dataPtr = new AZ::u8[m_dataSize];
// Read file into internal buffer...
size_t bytesRead = fileStream.Read(m_dataSize, m_dataPtr);
ResetBookmarks();
// Verify we read the full amount...
result = (bytesRead == m_dataSize);
}
}
fileStream.Close();
}
return result;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputFile::UnloadFile()
{
if (m_dataPtr)
{
delete [] m_dataPtr;
m_dataPtr = nullptr;
}
m_dataSize = 0;
m_dataCurrentPtr = nullptr;
m_dataCurrentReadSize = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputFile::ReadInput([[maybe_unused]] const AudioStreamData& data)
{
// Don't really need this for File-based sources, the whole file is read in the constructor.
// However, we may need to implement this for asynchronous loading of the file (streaming).
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputFile::WriteOutput(AkAudioBuffer* akBuffer)
{
AZ::u16 numSampleFramesRequested = (akBuffer->MaxFrames() - akBuffer->uValidFrames);
if (m_config.m_sampleType == AudioInputSampleType::Int)
{
void* outBuffer = akBuffer->GetInterleavedData();
AZ::u16 numSampleFramesCopied = static_cast<AZ::u16>(CopyData(numSampleFramesRequested, outBuffer));
akBuffer->uValidFrames += numSampleFramesCopied;
akBuffer->eState = (numSampleFramesCopied > 0) ? AK_DataReady
: (IsEof() ? AK_NoMoreData : AK_NoDataReady);
}
else if (m_config.m_sampleType == AudioInputSampleType::Float)
{
// Not Implemented yet!
akBuffer->eState = AK_NoMoreData;
// Implementing this for files will likely involve de-interleaving the samples.
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool AudioInputFile::IsOk() const
{
bool ok = (m_dataSize > 0);
ok &= IsFormatValid();
if (m_parser)
{
ok &= m_parser->IsHeaderValid();
ok &= (m_dataSize == m_parser->GetDataSize());
}
return ok;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputFile::OnDeactivated()
{
if (m_config.m_autoUnloadFile)
{
UnloadFile();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
size_t AudioInputFile::CopyData(size_t numSampleFrames, void* toBuffer)
{
// Copies data to an output buffer.
// Size requested is in sample frames, not bytes!
// Number of frames actually copied is returned. This is useful if more
// frames were requested than can be copied.
if (!toBuffer || !numSampleFrames)
{
return 0;
}
const size_t frameBytes = (m_config.m_numChannels * m_config.m_bitsPerSample) >> 3; // bits --> bytes
size_t copySize = numSampleFrames * frameBytes;
// Check if request is larger than remaining, trim off excess.
if (m_dataCurrentReadSize + copySize > m_dataSize)
{
size_t excess = (m_dataCurrentReadSize + copySize) - m_dataSize;
copySize -= excess;
numSampleFrames = (copySize / frameBytes);
}
if (copySize > 0)
{
::memcpy(toBuffer, m_dataCurrentPtr, copySize);
m_dataCurrentReadSize += copySize;
m_dataCurrentPtr += copySize;
}
return numSampleFrames;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputFile::ResetBookmarks()
{
m_dataCurrentPtr = m_dataPtr;
m_dataCurrentReadSize = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool AudioInputFile::IsEof() const
{
return (m_dataCurrentReadSize == m_dataSize);
}
} // namespace Audio
@@ -0,0 +1,127 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AudioSourceManager.h>
namespace Audio
{
/**
* Base class for audio file parser.
* Any supported audio file types will have a parser implementation
* that will parse header information to extract the audio format.
*/
class AudioFileParser
{
public:
AUDIO_IMPL_CLASS_ALLOCATOR(AudioFileParser)
AudioFileParser() = default;
virtual ~AudioFileParser() = default;
AudioFileParser(const AudioFileParser&) = delete;
AudioFileParser& operator=(const AudioFileParser&) = delete;
/**
* Parse header from a file stream.
* Parses header of an audio file and returns the byte-offset into the file where the audio data begins.
* @param fileStream An opened file stream on the audio file.
* @return Byte-offset into the file where audio data begins.
*/
virtual size_t ParseHeader(AZ::IO::FileIOStream& fileStream) = 0;
/**
* Check validity of the header info.
* This should only return true if the header was parsed and user can expect to see valid format data.
* @return True if the header was parsed without error.
*/
virtual bool IsHeaderValid() const = 0;
virtual AudioInputSampleType GetSampleType() const = 0;
virtual AZ::u32 GetNumChannels() const = 0;
virtual AZ::u32 GetSampleRate() const = 0;
virtual AZ::u32 GetByteRate() const = 0;
virtual AZ::u32 GetBitsPerSample() const = 0;
virtual AZ::u32 GetDataSize() const = 0;
};
/**
* A type of AudioInputSource representing an audio file.
* Contains audio file data, holds a pointer to the raw data and provides methods to read chunks of data at a time
* to an output (AkAudioBuffer).
*/
class AudioInputFile
: public AudioInputSource
{
public:
AUDIO_IMPL_CLASS_ALLOCATOR(AudioInputFile)
AudioInputFile(const SAudioInputConfig& sourceConfig);
~AudioInputFile() override;
/**
* Load file into buffer.
* Use an AudioFileParser if needed to parse header information, then proceed to load the audio data
* to the internal buffer.
* @return True upon successful load, false otherwise.
*/
bool LoadFile();
/**
* Unload the file data.
* Release the internal buffer of file data.
*/
void UnloadFile();
void ReadInput(const AudioStreamData& data) override;
void WriteOutput(AkAudioBuffer* akBuffer) override;
bool IsOk() const override;
void OnDeactivated() override;
/**
* Copy data from the internal buffer to an output buffer.
* Copies a specified number of sample frames to an output buffer. If more frames are requested
* than can be copied, only allowable frames are copied and number of frames that were copied is returned.
* @param numSampleFrames Number of sample frames requested for copy.
* @param toBuffer Output buffer to copy to.
* @return Number of sample frames actually copied.
*/
size_t CopyData(size_t numSampleFrames, void* toBuffer); // frames, not bytes!
private:
/**
* Resets internal bookmarking.
* Bookmarks are used internally to keep track of where we are in the buffer during
* chunk-copying to output.
*/
void ResetBookmarks();
/**
* Checks whether data copying has reached the end of the file data.
* @return True if end of file has been reached, false otherwise.
*/
bool IsEof() const;
AZStd::unique_ptr<AudioFileParser> m_parser = nullptr;
AZ::u8* m_dataPtr = nullptr; ///< The internal data buffer.
size_t m_dataSize = 0; ///< The internal data size.
// Bookmarks
AZ::u8* m_dataCurrentPtr = nullptr; ///< The internal bookmark pointer.
size_t m_dataCurrentReadSize = 0; ///< The internal bookmark indicating how much data has been read so far.
};
} // namespace Audio
@@ -0,0 +1,81 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AudioInput/AudioInputMicrophone.h>
#include <AzCore/Casting/numeric_cast.h>
#include <MicrophoneBus.h>
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////
// Audio Input Source : Microphone
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
AudioInputMicrophone::AudioInputMicrophone(const SAudioInputConfig& sourceConfig)
{
m_config = sourceConfig;
}
///////////////////////////////////////////////////////////////////////////////////////////////
AudioInputMicrophone::~AudioInputMicrophone()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputMicrophone::ReadInput([[maybe_unused]] const AudioStreamData& data)
{
// ReadInput only used when PUSHing source data in, and would need an internal buffer to store
// the data temporarily. For Microphone, the microphone impl has its own internal buffer, so
// we only need to PULL data in WriteOutput.
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputMicrophone::WriteOutput(AkAudioBuffer* akBuffer)
{
AZ::u32 numSampleFramesRequested = (akBuffer->MaxFrames() - akBuffer->uValidFrames);
AkSampleType* channelData[2] = { nullptr, nullptr };
for (AZ::u32 channel = 0; channel < akBuffer->NumChannels(); ++channel)
{
channelData[channel] = akBuffer->GetChannel(channel);
}
size_t numSampleFramesCopied = 0;
MicrophoneRequestBus::BroadcastResult(numSampleFramesCopied, &MicrophoneRequestBus::Events::GetData, reinterpret_cast<void**>(channelData), numSampleFramesRequested, m_config, true);
akBuffer->uValidFrames += aznumeric_cast<AkUInt16>(numSampleFramesCopied);
akBuffer->eState = (numSampleFramesCopied > 0) ? AK_DataReady : AK_NoDataReady;
// handle the AK_NoMoreData condition?
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputMicrophone::OnDeactivated()
{
m_config.m_numChannels = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool AudioInputMicrophone::IsOk() const
{
// Mono and Stereo only
bool ok = (m_config.m_numChannels == 1 || m_config.m_numChannels == 2);
// 32-bit float or 16-bit int only
ok &= (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample == 32)
|| (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample == 16);
return ok;
}
} // namespace Audio
@@ -0,0 +1,34 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AudioSourceManager.h>
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////
class AudioInputMicrophone
: public AudioInputSource
{
public:
AudioInputMicrophone(const SAudioInputConfig& sourceConfig);
~AudioInputMicrophone() override;
void ReadInput(const AudioStreamData& data) override;
void WriteOutput(AkAudioBuffer* akBuffer) override;
bool IsOk() const override;
void OnDeactivated() override;
};
} // namespace Audio
@@ -0,0 +1,131 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AudioInput/AudioInputStream.h>
#include <Common_wwise.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AK/SoundEngine/Common/AkStreamMgrModule.h>
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////
// Audio Streaming Input
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
AudioInputStreaming::AudioInputStreaming(const SAudioInputConfig& sourceConfig)
: m_framesReady(0)
{
m_config = sourceConfig;
size_t bytesPerSample = (m_config.m_bitsPerSample >> 3);
size_t numSamples = m_config.m_sampleRate * m_config.m_numChannels; // <-- This gives a 1 second buffer based on the configuration.
m_config.m_bufferSize = static_cast<AZ::u32>(numSamples * bytesPerSample);
if (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample == 32)
{
m_buffer.reset(new RingBuffer<float>(numSamples));
}
else if (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample == 16)
{
m_buffer.reset(new RingBuffer<AZ::s16>(numSamples));
}
else
{
AZ_Error("AudioInputStreaming", false, "Audio Stream Format Unsupported! Bits Per Sample = %d, Sample Type = %d",
m_config.m_bitsPerSample, static_cast<int>(m_config.m_sampleType));
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
AudioInputStreaming::~AudioInputStreaming()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
size_t AudioInputStreaming::ReadStreamingInput(const AudioStreamData& data)
{
size_t numFrames = data.m_sizeBytes / (m_config.m_bitsPerSample >> 3) / m_config.m_numChannels;
size_t framesAdded = m_buffer->AddData(data.m_data, numFrames, m_config.m_numChannels);
m_framesReady += framesAdded;
return framesAdded;
}
///////////////////////////////////////////////////////////////////////////////////////////////
size_t AudioInputStreaming::ReadStreamingMultiTrackInput(AudioStreamMultiTrackData& data)
{
size_t numFrames = data.m_sizeBytes / (m_config.m_bitsPerSample >> 3);
size_t framesAdded = m_buffer->AddMultiTrackDataInterleaved(data.m_data, numFrames, m_config.m_numChannels);
m_framesReady += framesAdded;
return framesAdded;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputStreaming::FlushStreamingInput()
{
m_buffer->ResetBuffer();
m_framesReady = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
size_t AudioInputStreaming::GetStreamingInputNumFramesReady() const
{
return m_framesReady;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputStreaming::ReadInput([[maybe_unused]] const AudioStreamData& data)
{
// Intentionally left as an empty implementation.
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputStreaming::WriteOutput(AkAudioBuffer* akBuffer)
{
AZ::u16 numSampleFramesRequested = (akBuffer->MaxFrames() - akBuffer->uValidFrames);
AkSampleType* channelData[6] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
for (AZ::u32 channel = 0; channel < akBuffer->NumChannels(); ++channel)
{
channelData[channel] = akBuffer->GetChannel(channel);
}
bool deinterleave = (m_config.m_sampleType == AudioInputSampleType::Float);
size_t numSampleFramesCopied = m_buffer->ConsumeData(reinterpret_cast<void**>(channelData), numSampleFramesRequested, akBuffer->NumChannels(), deinterleave);
akBuffer->uValidFrames += aznumeric_cast<AkUInt16>(numSampleFramesCopied);
m_framesReady -= numSampleFramesCopied;
akBuffer->eState = (numSampleFramesCopied > 0) ? AK_DataReady : AK_NoDataReady;
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool AudioInputStreaming::IsOk() const
{
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputStreaming::OnActivated()
{
AZ_Assert(m_config.m_sourceId != INVALID_AUDIO_SOURCE_ID, "AudioInputStreaming - Being activated but no valid Source Id!\n");
AudioStreamingRequestBus::Handler::BusConnect(m_config.m_sourceId);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputStreaming::OnDeactivated()
{
AudioStreamingRequestBus::Handler::BusDisconnect();
}
} // namespace Audio
@@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include "AudioSourceManager.h"
#include <IAudioSystem.h>
#include <AudioRingBuffer.h>
namespace Audio
{
/**
* A type of AudioInputSource representing an audio stream.
* holds a buffer of the raw data and provides methods to read chunks of data at a time
* to an output (AkAudioBuffer).
*/
class AudioInputStreaming
: public AudioInputSource
, public AudioStreamingRequestBus::Handler
{
public:
AUDIO_IMPL_CLASS_ALLOCATOR(AudioInputStreaming)
AudioInputStreaming(const SAudioInputConfig& sourceConfig);
~AudioInputStreaming() override;
// AudioInputSource Interface
void ReadInput(const AudioStreamData& data) override;
void WriteOutput(AkAudioBuffer* akBuffer) override;
bool IsOk() const override;
void OnDeactivated() override;
void OnActivated() override;
// AudioStreamingRequestBus::Handler Interface
size_t ReadStreamingInput(const AudioStreamData& data) override;
size_t ReadStreamingMultiTrackInput(AudioStreamMultiTrackData& data) override;
void FlushStreamingInput();
size_t GetStreamingInputNumFramesReady() const;
private:
AZStd::unique_ptr<RingBufferBase> m_buffer = nullptr;
size_t m_framesReady;
};
} // namespace Audio
@@ -0,0 +1,165 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AudioInput/WavParser.h>
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////
/*static*/ const AZ::u8 WavFileParser::riff_tag[4] = { 'R', 'I', 'F', 'F' };
/*static*/ const AZ::u8 WavFileParser::wave_tag[4] = { 'W', 'A', 'V', 'E' };
/*static*/ const AZ::u8 WavFileParser::fmt__tag[4] = { 'f', 'm', 't', ' ' };
/*static*/ const AZ::u8 WavFileParser::data_tag[4] = { 'd', 'a', 't', 'a' };
///////////////////////////////////////////////////////////////////////////////////////////////
WavFileParser::WavFileParser()
{
::memset(&m_header, 0, sizeof(m_header));
}
///////////////////////////////////////////////////////////////////////////////////////////////
WavFileParser::~WavFileParser()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
size_t WavFileParser::ParseHeader(AZ::IO::FileIOStream& fileStream)
{
if (IsHeaderValid())
{
// Header was already parsed then, no work needed.
return 0;
}
AZ_Assert(fileStream.IsOpen(), "WavFileParser::ParseHeader - FileIOStream is not open!\n");
// Parsers are allowed to seek into the stream if they want in order to perform their task
// of gathering file information. Will return the byte-offset into the file where the
// data starts.
fileStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
// Begin parsing, start with the RIFF + WAVE tags...
AZ::u8* writePtr = reinterpret_cast<AZ::u8*>(&m_header);
size_t copySize = sizeof(m_header.riff) + sizeof(m_header.wave);
fileStream.Read(copySize, writePtr);
if (!ValidTag(m_header.riff.tag, WavFileParser::riff_tag))
{
AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Not a 'RIFF'!\n");
return 0;
}
if (!ValidTag(m_header.wave, WavFileParser::wave_tag))
{
AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Not a 'RIFF / WAVE'!\n");
return 0;
}
writePtr += copySize;
bool formatTagFound = false;
bool dataTagFound = false;
while (!dataTagFound)
{
// read the next tag, check what it is...
ChunkHeader header;
copySize = sizeof(header);
fileStream.Read(copySize, &header);
if (ValidTag(header.tag, WavFileParser::fmt__tag))
{
m_header.fmt.header = header;
writePtr = reinterpret_cast<AZ::u8*>(&m_header.fmt);
writePtr += sizeof(m_header.fmt.header); // skip forward because it was already read into the temp chunkheader.
copySize = sizeof(m_header.fmt) - sizeof(m_header.fmt.header);
fileStream.Read(copySize, writePtr);
formatTagFound = true;
}
else if (ValidTag(header.tag, WavFileParser::data_tag))
{
m_header.data = header;
dataTagFound = true;
}
else
{
// Unknown tag, skip by the size specified
// It is possible that we want to read certain tag data in the future.
// Tools/encoders may embed extra data in various sections.
fileStream.Seek(header.size, AZ::IO::GenericStream::ST_SEEK_CUR);
}
// Check for Eof (premature)...
if (fileStream.GetCurPos() == fileStream.GetLength())
{
AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Got to end of file and did not locate a 'data' chunk!\n");
return 0;
}
}
if (!ValidTag(m_header.fmt.header.tag, WavFileParser::fmt__tag))
{
AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Did not find a 'fmt' tag!\n");
}
if (!ValidTag(m_header.data.tag, WavFileParser::data_tag))
{
AZ_Error("WavFileParser", false, "WavFileParser::ParseHeader - Did not find a 'data' tag!\n");
}
#ifdef AZ_DEBUG_BUILD
if (formatTagFound)
{
AZ_TracePrintf("WavFileParser", "Format: %u\n", static_cast<AZ::u32>(GetSampleType()));
AZ_TracePrintf("WavFileParser", "Channels: %u\n", GetNumChannels());
AZ_TracePrintf("WavFileParser", "SampleRate: %u\n", GetSampleRate());
AZ_TracePrintf("WavFileParser", "ByteRate: %u\n", GetByteRate());
AZ_TracePrintf("WavFileParser", "BitsPerSample: %u\n", GetBitsPerSample());
AZ_TracePrintf("WavFileParser", "DataSize: %u\n", GetDataSize());
}
#endif // AZ_DEBUG_BUILD
if (dataTagFound && formatTagFound)
{
m_headerIsValid = true;
return fileStream.GetCurPos();
}
else
{
return 0;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
AudioInputSampleType WavFileParser::GetSampleType() const
{
switch (m_header.fmt.audioFormat)
{
case 1:
return AudioInputSampleType::Int;
case 3:
return AudioInputSampleType::Float;
default:
return AudioInputSampleType::Unsupported;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// static
AZ_FORCE_INLINE bool WavFileParser::ValidTag(const AZ::u8 tag[4], const AZ::u8 name[4])
{
return (tag[0] == name[0] && tag[1] == name[1] && tag[2] == name[2] && tag[3] == name[3]);
}
} // namespace Audio
@@ -0,0 +1,131 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AudioInput/AudioInputFile.h>
namespace Audio
{
/**
* A RIFF format chunk header.
*/
struct ChunkHeader
{
AZ::u8 tag[4];
AZ::u32 size;
};
/**
* A WAVE format "fmt" chunk.
*/
struct FmtChunk
{
ChunkHeader header;
AZ::u16 audioFormat;
AZ::u16 numChannels;
AZ::u32 sampleRate;
AZ::u32 byteRate;
AZ::u16 blockAlign;
AZ::u16 bitsPerSample;
};
/**
* A WAVE format header.
*/
struct WavHeader
{
ChunkHeader riff;
AZ::u8 wave[4];
FmtChunk fmt;
ChunkHeader data;
static const size_t MinSize = 44;
};
static_assert(sizeof(WavHeader) == WavHeader::MinSize, "WavHeader struct size is not 44 bytes!");
/**
* Type of AudioFileParser for Wav File Format.
* Parses header information from Wav files and stores it for retrieval.
*/
class WavFileParser
: public AudioFileParser
{
public:
AUDIO_IMPL_CLASS_ALLOCATOR(WavFileParser)
WavFileParser();
~WavFileParser() override;
size_t ParseHeader(AZ::IO::FileIOStream& fileStream) override;
bool IsHeaderValid() const override;
AudioInputSampleType GetSampleType() const override;
AZ::u32 GetNumChannels() const override;
AZ::u32 GetSampleRate() const override;
AZ::u32 GetByteRate() const override;
AZ::u32 GetBitsPerSample() const override;
AZ::u32 GetDataSize() const override;
private:
static bool ValidTag(const AZ::u8 tag[4], const AZ::u8 name[4]);
WavHeader m_header;
bool m_headerIsValid = false;
static const AZ::u8 riff_tag[4];
static const AZ::u8 wave_tag[4];
static const AZ::u8 fmt__tag[4];
static const AZ::u8 data_tag[4];
};
///////////////////////////////////////////////////////////////////////////////////////////////
AZ_INLINE bool WavFileParser::IsHeaderValid() const
{
return m_headerIsValid;
}
///////////////////////////////////////////////////////////////////////////////////////////////
AZ_INLINE AZ::u32 WavFileParser::GetNumChannels() const
{
return m_header.fmt.numChannels;
}
///////////////////////////////////////////////////////////////////////////////////////////////
AZ_INLINE AZ::u32 WavFileParser::GetSampleRate() const
{
return m_header.fmt.sampleRate;
}
///////////////////////////////////////////////////////////////////////////////////////////////
AZ_INLINE AZ::u32 WavFileParser::GetByteRate() const
{
return m_header.fmt.byteRate;
}
///////////////////////////////////////////////////////////////////////////////////////////////
AZ_INLINE AZ::u32 WavFileParser::GetBitsPerSample() const
{
return m_header.fmt.bitsPerSample;
}
///////////////////////////////////////////////////////////////////////////////////////////////
AZ_INLINE AZ::u32 WavFileParser::GetDataSize() const
{
return m_header.data.size;
}
} // namespace Audio
@@ -0,0 +1,375 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AudioSourceManager.h>
#include <AudioInput/AudioInputFile.h>
#include <AudioInput/AudioInputMicrophone.h>
#include <AudioInput/AudioInputStream.h>
#include <AzCore/std/parallel/lock.h>
#include <AK/AkWwiseSDKVersion.h>
#include <AK/Plugin/AkAudioInputPlugin.h>
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////
// Audio Input Source
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
bool AudioInputSource::IsFormatValid() const
{
// Audio Input Source has restrictions on the formats that are supported:
// 16-bit Integer samples, interleaved samples
// 32-bit Float samples, non-interleaved samples
// The Parser doesn't care about such restrictions and is only responsible for
// reading the header information and validating it.
bool valid = true;
if (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample != 16)
{
valid = false;
}
if (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample != 32)
{
valid = false;
}
if (m_config.m_sampleType == AudioInputSampleType::Unsupported)
{
valid = false;
}
if (!valid)
{
AZ_TracePrintf("AudioInputFile", "The file format is NOT supported! Only 16-bit integer or 32-bit float sample types are allowed!\n"
"Current Format: (%s / %d)\n", m_config.m_sampleType == AudioInputSampleType::Int ? "Int"
: (m_config.m_sampleType == AudioInputSampleType::Float ? "Float" : "Unknown"),
m_config.m_bitsPerSample);
}
return valid;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputSource::SetFormat(AkAudioFormat& format)
{
AkUInt32 speakerConfig = 0;
switch (m_config.m_numChannels)
{
case 1:
{
speakerConfig = AK_SPEAKER_SETUP_MONO;
break;
}
case 2:
{
speakerConfig = AK_SPEAKER_SETUP_STEREO;
break;
}
case 6:
{
speakerConfig = AK_SPEAKER_SETUP_5POINT1;
break;
}
default:
{
// TODO: Test more channels
return;
}
}
AkUInt32 sampleType = 0;
AkUInt32 sampleInterleaveType = 0;
switch (m_config.m_bitsPerSample)
{
case 16:
{
sampleType = AK_INT;
sampleInterleaveType = AK_INTERLEAVED;
break;
}
case 32:
{
sampleType = AK_FLOAT;
sampleInterleaveType = AK_NONINTERLEAVED;
break;
}
default:
{
// Anything else and Audio Input Source doesn't support it.
// But we've already checked the format when parsing the header, so we shouldn't get here.
break;
}
}
AkChannelConfig akChannelConfig(m_config.m_numChannels, speakerConfig);
format.SetAll(
m_config.m_sampleRate,
akChannelConfig,
m_config.m_bitsPerSample,
m_config.m_numChannels * m_config.m_bitsPerSample >> 3, // shift converts bits->bytes, this is the frame size
sampleType,
sampleInterleaveType
);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioInputSource::SetSourceId(TAudioSourceId sourceId)
{
m_config.m_sourceId = sourceId;
}
///////////////////////////////////////////////////////////////////////////////////////////////
TAudioSourceId AudioInputSource::GetSourceId() const
{
return m_config.m_sourceId;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Audio Input Source Manager
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
AudioSourceManager::AudioSourceManager()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
AudioSourceManager::~AudioSourceManager()
{
Shutdown();
}
///////////////////////////////////////////////////////////////////////////////////////////////
// static
AudioSourceManager& AudioSourceManager::Get()
{
static AudioSourceManager s_manager;
return s_manager;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// static
void AudioSourceManager::Initialize()
{
// Wwise Api call to setup the callbacks used by Audio Input Sources.
SetAudioInputCallbacks(AudioSourceManager::ExecuteCallback, AudioSourceManager::GetFormatCallback);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioSourceManager::Shutdown()
{
AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
m_activeAudioInputs.clear();
m_inactiveAudioInputs.clear();
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool AudioSourceManager::CreateSource(const SAudioInputConfig& sourceConfig)
{
AZStd::unique_ptr<AudioInputSource> ptr = nullptr;
switch (sourceConfig.m_sourceType)
{
case AudioInputSourceType::PcmFile:
case AudioInputSourceType::WavFile:
//case AudioInputSourceType::OggFile:
//case AudioInputSourceType::OpusFile:
{
if (!sourceConfig.m_sourceFilename.empty())
{
ptr.reset(aznew AudioInputFile(sourceConfig));
}
break;
}
case AudioInputSourceType::Microphone:
{
ptr.reset(aznew AudioInputMicrophone(sourceConfig));
break;
}
case AudioInputSourceType::ExternalStream:
{
ptr.reset(aznew AudioInputStreaming(sourceConfig));
break;
}
case AudioInputSourceType::Synthesis: // Will need to allow setting a user-defined Generate callback.
default:
{
AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::CreateSource - The type of AudioInputSource requested is not supported yet!\n");
return INVALID_AUDIO_SOURCE_ID;
}
}
if (!ptr || !ptr->IsOk())
{ // this check could change in the future as we add asynch loading.
return false;
}
AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
ptr->SetSourceId(sourceConfig.m_sourceId);
m_inactiveAudioInputs.emplace(sourceConfig.m_sourceId, AZStd::move(ptr));
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioSourceManager::ActivateSource(TAudioSourceId sourceId, AkPlayingID playingId)
{
AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
if (m_inactiveAudioInputs.find(sourceId) != m_inactiveAudioInputs.end())
{
if (m_activeAudioInputs.find(playingId) == m_activeAudioInputs.end())
{
m_inactiveAudioInputs[sourceId]->SetSourceId(sourceId);
m_activeAudioInputs[playingId] = AZStd::move(m_inactiveAudioInputs[sourceId]);
m_inactiveAudioInputs.erase(sourceId);
m_activeAudioInputs[playingId]->OnActivated();
}
else
{
AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::ActivateSource - Active source with playing Id %u already exists!\n", playingId);
}
}
else
{
AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::ActivateSource - Source with Id %u not found!\n", sourceId);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioSourceManager::DeactivateSource(AkPlayingID playingId)
{
AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
if (m_activeAudioInputs.find(playingId) != m_activeAudioInputs.end())
{
TAudioSourceId sourceId = m_activeAudioInputs[playingId]->GetSourceId();
if (m_inactiveAudioInputs.find(sourceId) == m_inactiveAudioInputs.end())
{
m_inactiveAudioInputs[sourceId] = AZStd::move(m_activeAudioInputs[playingId]);
m_activeAudioInputs.erase(playingId);
// Signal to the audio input source that it was deactivated! It might unload it's resources.
m_inactiveAudioInputs[sourceId]->OnDeactivated();
if (!m_inactiveAudioInputs[sourceId]->IsOk())
{
m_inactiveAudioInputs.erase(sourceId);
}
}
else
{
AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::DeactivateSource - Source with Id %u was already inactive!\n", sourceId);
}
}
else
{
AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::DeactivateSource - Active source with playing Id %u not found!\n", playingId);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
void AudioSourceManager::DestroySource(TAudioSourceId sourceId)
{
AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
if (m_inactiveAudioInputs.find(sourceId) != m_inactiveAudioInputs.end())
{
m_inactiveAudioInputs.erase(sourceId);
}
else
{
AZ_TracePrintf("AudioSourceManager", "AudioSourceManager::DestroySource - No source with Id %u was found!\nDid you call DeactivateSource first on the playingId??\n", sourceId);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
AkPlayingID AudioSourceManager::FindPlayingSource(TAudioSourceId sourceId)
{
AZStd::lock_guard<AZStd::mutex> lock(m_inputMutex);
for (auto& inputPair : m_activeAudioInputs)
{
if (inputPair.second->GetSourceId() == sourceId)
{
return inputPair.first;
}
}
return AK_INVALID_PLAYING_ID;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// static
void AudioSourceManager::ExecuteCallback(AkPlayingID playingId, AkAudioBuffer* akBuffer)
{
if (!akBuffer->HasData())
{
akBuffer->eState = AK_Fail;
akBuffer->uValidFrames = 0;
return;
}
if (akBuffer->eState == AK_NoDataNeeded)
{
akBuffer->eState = AK_NoDataReady;
akBuffer->uValidFrames = 0;
return;
}
AZStd::lock_guard<AZStd::mutex> lock(Get().m_inputMutex);
auto inputIter = Get().m_activeAudioInputs.find(playingId);
if (inputIter != Get().m_activeAudioInputs.end())
{
auto& audioInput = inputIter->second;
if (audioInput)
{
// this will set the uValidFrames and eState for us.
audioInput->WriteOutput(akBuffer);
}
}
else
{
// signal that the audio input playback should end.
akBuffer->eState = AK_NoMoreData;
akBuffer->uValidFrames = 0;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// static
void AudioSourceManager::GetFormatCallback(AkPlayingID playingId, AkAudioFormat& audioFormat)
{
AZStd::lock_guard<AZStd::mutex> lock(Get().m_inputMutex);
auto inputIter = Get().m_activeAudioInputs.find(playingId);
if (inputIter != Get().m_activeAudioInputs.end())
{
// Set the AkAudioFormat from the AudioInputSource's SAudioInputConfig
auto& audioInput = inputIter->second;
if (audioInput)
{
audioInput->SetFormat(audioFormat);
}
}
}
} // namespace Audio
@@ -0,0 +1,145 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/PlatformIncl.h> // This include is needed to include WinSock2.h before including Windows.h
// As AK/SoundEngine/Common/AkTypes.h eventually includes Windows.h
#include <IAudioInterfacesCommonData.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/IO/FileIO.h>
#include <AudioAllocators.h>
#include <AK/SoundEngine/Common/AkTypes.h>
#include <AK/SoundEngine/Common/IAkPlugin.h>
namespace Audio
{
/**
* Base class for Audio Input Source types.
* Represents an Audio Input Source, which has input/output routines and configuration information.
*/
class AudioInputSource
{
public:
AUDIO_IMPL_CLASS_ALLOCATOR(AudioInputSource)
AudioInputSource() = default;
virtual ~AudioInputSource() = default;
virtual void ReadInput(const AudioStreamData& data) = 0;
virtual void WriteOutput(AkAudioBuffer* akBuffer) = 0;
virtual bool IsOk() const = 0;
virtual bool IsFormatValid() const;
virtual void OnActivated() {}
virtual void OnDeactivated() {}
void SetFormat(AkAudioFormat& format);
void SetSourceId(TAudioSourceId sourceId);
TAudioSourceId GetSourceId() const;
protected:
SAudioInputConfig m_config; ///< Configuration information for the source type.
AkPlayingID m_playingId = AK_INVALID_PLAYING_ID; ///< Playing ID of the source.
};
/**
* Manager class for AudioInputSource.
* Manages lifetime of AudioInputSource objects as they are created, activated, deactivated, and destroyed.
* The lifetime of an Audio Input Source:
* CreateSource (loads resources)
* ActivateSource (once you obtain a playing Id)
* (Running, callbacks being received, also async loading input if enabled)
* DeactivateSource (once it's determined to be done playing)
* DestroySource (unloads resources)
*/
class AudioSourceManager
{
public:
AudioSourceManager();
~AudioSourceManager();
static AudioSourceManager& Get();
static void Initialize();
void Shutdown();
/**
* CreateSource a new AudioInputSource.
* Creates an AudioInputSource, based on the SAudioInputConfig and stores it in an inactive state.
* @param sourceConfig Configuration of the AudioInputSource.
* @return True if the source was created successfully, false otherwise.
*/
bool CreateSource(const SAudioInputConfig& sourceConfig);
/**
* Activates an AudioInputSource.
* Moves a source from the inactive state to an active state by assigning an AkPlayingID.
* @param sourceId ID of the source (returned by CreateSource).
* @param playingId A playing ID of the source that is now playing in Wwise.
*/
void ActivateSource(TAudioSourceId sourceId, AkPlayingID playingId);
/**
* Deactivates an AudioInputSource.
* Moves a source from the active state back to an inactive state, will happen when an end event callback is recieved.
* @param playingId Playing ID of the source that ended.
*/
void DeactivateSource(AkPlayingID playingId);
/**
* Destroy an AudioInputSource.
* Destroys an AudioInputSource from the manager when it is no longer needed.
* @param sourceId Source ID of the object to remove.
*/
void DestroySource(TAudioSourceId sourceId);
/**
* Find the Playing ID of a source.
* Given a Source ID, check if there are sources in the active state and if so, return their Playing ID.
* @param sourceId Source ID to look for in the active sources.
*/
AkPlayingID FindPlayingSource(TAudioSourceId sourceId);
private:
/**
* Wwise Audio Input Plugin "Execute" callback function.
* This will be called whenever a playing Audio Input Source needs to be fed.
* @param playingId The Playing ID of the source.
* @param audioBuffer The buffer to copy samples into.
*/
static void ExecuteCallback(AkPlayingID playingId, AkAudioBuffer* audioBuffer);
/**
* Wwise Audio Input Plugin "GetFormat" callback function.
* This will be called once whenever a new Audio Input Source is starting playback.
* @param playingId The Playing ID of the source.
* @param audioFormat The format structure that should be filled with format information.
*/
static void GetFormatCallback(AkPlayingID playingId, AkAudioFormat& audioFormat);
AZStd::mutex m_inputMutex; ///< Callbacks will come from the Wwise event processing thread.
template <typename KeyType, typename ValueType>
using AudioInputMap = AZStd::unordered_map<KeyType, AZStd::unique_ptr<ValueType>, AZStd::hash<KeyType>, AZStd::equal_to<KeyType>, Audio::AudioImplStdAllocator>;
AudioInputMap<TAudioSourceId, AudioInputSource> m_inactiveAudioInputs; ///< Sources that haven't started playing yet.
AudioInputMap<AkPlayingID, AudioInputSource> m_activeAudioInputs; ///< Sources that are currently playing.
};
}
@@ -0,0 +1,60 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <AudioSystemImplCVars.h>
#include <AudioEngineWwise_Traits_Platform.h>
namespace Audio::Wwise::Cvars
{
AZ_CVAR(AZ::u64, s_PrimaryMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE,
nullptr, AZ::ConsoleFunctorFlags::Null,
"The size in KiB of the primary memory pool used by the Wwise audio integration.\n"
"Usage: s_PrimaryMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "\n");
AZ_CVAR(AZ::u64, s_SecondaryMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_SECONDARY_POOL_SIZE,
nullptr, AZ::ConsoleFunctorFlags::Null,
"The size in KiB of the secondary memory pool. Most platforms do not use this.\n"
"Usage: s_SecondaryMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_PRIMARY_POOL_SIZE_DEFAULT_TEXT "\n");
AZ_CVAR(AZ::u64, s_StreamDeviceMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE,
nullptr, AZ::ConsoleFunctorFlags::Null,
"The size in KiB of the Wwise Stream Device.\n"
"Usage: s_StreamDeviceMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_STREAMER_DEVICE_MEMORY_POOL_SIZE_DEFAULT_TEXT "\n");
AZ_CVAR(AZ::u64, s_CommandQueueMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE,
nullptr, AZ::ConsoleFunctorFlags::Null,
"The size in KiB of the Wwise Command Queue.\n"
"Usage: s_CommandQueueMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_COMMAND_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "\n");
#if !defined(WWISE_RELEASE)
AZ_CVAR(AZ::u64, s_MonitorQueueMemorySize, AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE,
nullptr, AZ::ConsoleFunctorFlags::Null,
"The size in KiB of the Wwise Monitor Queue.\n"
"Not available in Release build.\n"
"Usage: s_MonitorQueueMemorySize=" AZ_TRAIT_AUDIOENGINEWWISE_MONITOR_QUEUE_MEMORY_POOL_SIZE_DEFAULT_TEXT "\n");
AZ_CVAR(bool, s_EnableCommSystem, false,
nullptr, AZ::ConsoleFunctorFlags::Null,
"Enable initialization of the Wwise Comm system, which allows for remote profiling.\n"
"Not available in Release build.\n"
"Usage: s_EnableCommSystem=true (false)\n");
AZ_CVAR(bool, s_EnableOutputCapture, false,
nullptr, AZ::ConsoleFunctorFlags::Null,
"Capture the main audio output to a WAV file.\n"
"Not available in Release build.\n"
"Usage: s_EnableOutputCapture=true (false)\n");
#endif // !WWISE_RELEASE
} // namespace Audio::Wwise::Cvars
@@ -0,0 +1,31 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <AzCore/Console/IConsole.h>
namespace Audio::Wwise::Cvars
{
AZ_CVAR_EXTERNED(AZ::u64, s_PrimaryMemorySize);
AZ_CVAR_EXTERNED(AZ::u64, s_SecondaryMemorySize);
AZ_CVAR_EXTERNED(AZ::u64, s_StreamDeviceMemorySize);
AZ_CVAR_EXTERNED(AZ::u64, s_CommandQueueMemorySize);
#if !defined(WWISE_RELEASE)
AZ_CVAR_EXTERNED(AZ::u64, s_MonitorQueueMemorySize);
AZ_CVAR_EXTERNED(bool, s_EnableCommSystem);
AZ_CVAR_EXTERNED(bool, s_EnableOutputCapture);
#endif // !WWISE_RELEASE
} // namespace Audio::Wwise::Cvars
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,199 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <AudioAllocators.h>
#include <FileIOHandler_wwise.h>
#include <ATLEntities_wwise.h>
#include <IAudioSystemImplementation.h>
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////////
class CAudioSystemImpl_wwise
: public AudioSystemImplementation
{
public:
AUDIO_IMPL_CLASS_ALLOCATOR(CAudioSystemImpl_wwise)
explicit CAudioSystemImpl_wwise(const char* assetsPlatformName);
~CAudioSystemImpl_wwise() override;
// AudioSystemImplementationNotificationBus
void OnAudioSystemLoseFocus() override;
void OnAudioSystemGetFocus() override;
void OnAudioSystemMuteAll() override;
void OnAudioSystemUnmuteAll() override;
void OnAudioSystemRefresh() override;
// ~AudioSystemImplementationNotificationBus
// AudioSystemImplementationRequestBus
void Update(const float updateIntervalMS) override;
EAudioRequestStatus Initialize() override;
EAudioRequestStatus ShutDown() override;
EAudioRequestStatus Release() override;
EAudioRequestStatus StopAllSounds() override;
EAudioRequestStatus RegisterAudioObject(
IATLAudioObjectData* const audioObjectData,
const char* const objectName) override;
EAudioRequestStatus UnregisterAudioObject(IATLAudioObjectData* const audioObjectData) override;
EAudioRequestStatus ResetAudioObject(IATLAudioObjectData* const audioObjectData) override;
EAudioRequestStatus UpdateAudioObject(IATLAudioObjectData* const audioObjectData) override;
EAudioRequestStatus PrepareTriggerSync(
IATLAudioObjectData* const audioObjectData,
const IATLTriggerImplData* const triggerData) override;
EAudioRequestStatus UnprepareTriggerSync(
IATLAudioObjectData* const audioObjectData,
const IATLTriggerImplData* const triggerData) override;
EAudioRequestStatus PrepareTriggerAsync(
IATLAudioObjectData* const audioObjectData,
const IATLTriggerImplData* const triggerData,
IATLEventData* const eventData) override;
EAudioRequestStatus UnprepareTriggerAsync(
IATLAudioObjectData* const audioObjectData,
const IATLTriggerImplData* const triggerData,
IATLEventData* const eventData) override;
EAudioRequestStatus ActivateTrigger(
IATLAudioObjectData* const audioObjectData,
const IATLTriggerImplData* const triggerData,
IATLEventData* const eventData,
const SATLSourceData* const pSourceData) override;
EAudioRequestStatus StopEvent(
IATLAudioObjectData* const audioObjectData,
const IATLEventData* const eventData) override;
EAudioRequestStatus StopAllEvents(
IATLAudioObjectData* const audioObjectData) override;
EAudioRequestStatus SetPosition(
IATLAudioObjectData* const audioObjectData,
const SATLWorldPosition& worldPosition) override;
EAudioRequestStatus SetMultiplePositions(
IATLAudioObjectData* const audioObjectData,
const MultiPositionParams& multiPositionParams) override;
EAudioRequestStatus SetEnvironment(
IATLAudioObjectData* const audioObjectData,
const IATLEnvironmentImplData* const environmentData,
const float amount) override;
EAudioRequestStatus SetRtpc(
IATLAudioObjectData* const audioObjectData,
const IATLRtpcImplData* const rtpcData,
const float value) override;
EAudioRequestStatus SetSwitchState(
IATLAudioObjectData* const audioObjectData,
const IATLSwitchStateImplData* const switchStateData) override;
EAudioRequestStatus SetObstructionOcclusion(
IATLAudioObjectData* const audioObjectData,
const float obstruction,
const float occlusion) override;
EAudioRequestStatus SetListenerPosition(
IATLListenerData* const listenerData,
const SATLWorldPosition& newPosition) override;
EAudioRequestStatus ResetRtpc(
IATLAudioObjectData* const audioObjectData,
const IATLRtpcImplData* const rtpcData) override;
EAudioRequestStatus RegisterInMemoryFile(SATLAudioFileEntryInfo* const audioFileEntry) override;
EAudioRequestStatus UnregisterInMemoryFile(SATLAudioFileEntryInfo* const audioFileEntry) override;
EAudioRequestStatus ParseAudioFileEntry(const AZ::rapidxml::xml_node<char>* audioFileEntryNode, SATLAudioFileEntryInfo* const fileEntryInfo) override;
void DeleteAudioFileEntryData(IATLAudioFileEntryData* const oldAudioFileEntryData) override;
const char* const GetAudioFileLocation(SATLAudioFileEntryInfo* const fileEntryInfo) override;
IATLTriggerImplData* NewAudioTriggerImplData(const AZ::rapidxml::xml_node<char>* audioTriggerNode) override;
void DeleteAudioTriggerImplData(IATLTriggerImplData* const oldTriggerImplData) override;
IATLRtpcImplData* NewAudioRtpcImplData(const AZ::rapidxml::xml_node<char>* audioRtpcNode) override;
void DeleteAudioRtpcImplData(IATLRtpcImplData* const oldRtpcImplData) override;
IATLSwitchStateImplData* NewAudioSwitchStateImplData(const AZ::rapidxml::xml_node<char>* audioSwitchStateNode) override;
void DeleteAudioSwitchStateImplData(IATLSwitchStateImplData* const oldSwitchStateImplData) override;
IATLEnvironmentImplData* NewAudioEnvironmentImplData(const AZ::rapidxml::xml_node<char>* audioEnvironmentNode) override;
void DeleteAudioEnvironmentImplData(IATLEnvironmentImplData* const oldEnvironmentImplData) override;
SATLAudioObjectData_wwise* NewGlobalAudioObjectData(const TAudioObjectID objectId) override;
SATLAudioObjectData_wwise* NewAudioObjectData(const TAudioObjectID objectId) override;
void DeleteAudioObjectData(IATLAudioObjectData* const oldObjectData) override;
SATLListenerData_wwise* NewDefaultAudioListenerObjectData(const TATLIDType objectId) override;
SATLListenerData_wwise* NewAudioListenerObjectData(const TATLIDType objectId) override;
void DeleteAudioListenerObjectData(IATLListenerData* const oldListenerData) override;
SATLEventData_wwise* NewAudioEventData(const TAudioEventID eventId) override;
void DeleteAudioEventData(IATLEventData* const oldEventData) override;
void ResetAudioEventData(IATLEventData* const eventData) override;
const char* const GetImplSubPath() const override;
void SetLanguage(const char* const language) override;
// Functions below are only used when WWISE_RELEASE is not defined
const char* const GetImplementationNameString() const override;
void GetMemoryInfo(SAudioImplMemoryInfo& memoryInfo) const override;
AZStd::vector<AudioImplMemoryPoolInfo> GetMemoryPoolInfo() override;
bool CreateAudioSource(const SAudioInputConfig& sourceConfig) override;
void DestroyAudioSource(TAudioSourceId sourceId) override;
void SetPanningMode(PanningMode mode) override;
// ~AudioSystemImplementationRequestBus
protected:
void SetBankPaths();
AZStd::string m_soundbankFolder;
AZStd::string m_localizedSoundbankFolder;
AZStd::string m_assetsPlatform;
private:
static const char* const WwiseImplSubPath;
static const char* const WwiseGlobalAudioObjectName;
static const float ObstructionOcclusionMin;
static const float ObstructionOcclusionMax;
struct SEnvPairCompare
{
bool operator()(const AZStd::pair<const AkAuxBusID, float>& pair1, const AZStd::pair<const AkAuxBusID, float>& pair2) const;
};
SATLSwitchStateImplData_wwise* ParseWwiseSwitchOrState(const AZ::rapidxml::xml_node<char>* node, EWwiseSwitchType type);
SATLSwitchStateImplData_wwise* ParseWwiseRtpcSwitch(const AZ::rapidxml::xml_node<char>* node);
void ParseRtpcImpl(const AZ::rapidxml::xml_node<char>* node, AkRtpcID& akRtpcId, float& mult, float& shift);
EAudioRequestStatus PrepUnprepTriggerSync(
const IATLTriggerImplData* const triggerData,
bool prepare);
EAudioRequestStatus PrepUnprepTriggerAsync(
const IATLTriggerImplData* const triggerData,
IATLEventData* const eventData,
bool prepare);
EAudioRequestStatus PostEnvironmentAmounts(IATLAudioObjectData* const audioObjectData);
AkGameObjectID m_globalGameObjectID;
AkGameObjectID m_defaultListenerGameObjectID;
AkBankID m_initBankID;
CFileIOHandler_wwise m_fileIOHandler;
#if !defined(WWISE_RELEASE)
bool m_isCommSystemInitialized;
AZStd::vector<AudioImplMemoryPoolInfo> m_debugMemoryInfo;
AZStd::string m_fullImplString;
AZStd::string m_speakerConfigString;
#endif // !WWISE_RELEASE
};
} // namespace Audio
@@ -0,0 +1,14 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <Common_wwise.h>
@@ -0,0 +1,116 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <AK/SoundEngine/Common/AkMemoryMgr.h>
#include <AK/SoundEngine/Common/AkTypes.h>
#include <AK/AkWwiseSDKVersion.h>
#include <IAudioSystem.h>
#include <AudioEngineWwise_Traits_Platform.h>
#if AZ_TRAIT_AUDIOENGINEWWISE_PROVIDE_IMPL_SECONDARY_POOL
#include <platform.h>
#include <CryPool/PoolAlloc.h>
using TMemoryPoolReferenced = NCryPoolAlloc::CThreadSafe<NCryPoolAlloc::CBestFit<NCryPoolAlloc::CReferenced<NCryPoolAlloc::CMemoryDynamic, 4 * 1024, true>, NCryPoolAlloc::CListItemReference>>;
namespace Audio
{
extern TMemoryPoolReferenced g_audioImplMemoryPoolSecondary_wwise;
}
#endif // AZ_TRAIT_AUDIOENGINEWWISE_PROVIDE_IMPL_SECONDARY_POOL
#define WWISE_IMPL_VERSION_STRING "Wwise " AK_WWISESDK_VERSIONNAME
#define ASSERT_WWISE_OK(x) (AKASSERT((x) == AK_Success))
#define IS_WWISE_OK(x) ((x) == AK_Success)
namespace Audio
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// Wwise Xml Element Names
namespace WwiseXmlTags
{
static constexpr const char* WwiseEventTag = "WwiseEvent";
static constexpr const char* WwiseRtpcTag = "WwiseRtpc";
static constexpr const char* WwiseSwitchTag = "WwiseSwitch";
static constexpr const char* WwiseStateTag = "WwiseState";
static constexpr const char* WwiseRtpcSwitchTag = "WwiseRtpc";
static constexpr const char* WwiseFileTag = "WwiseFile";
static constexpr const char* WwiseAuxBusTag = "WwiseAuxBus";
static constexpr const char* WwiseValueTag = "WwiseValue";
static constexpr const char* WwiseNameAttribute = "wwise_name";
static constexpr const char* WwiseValueAttribute = "wwise_value";
static constexpr const char* WwiseMutiplierAttribute = "atl_mult";
static constexpr const char* WwiseShiftAttribute = "atl_shift";
static constexpr const char* WwiseLocalizedAttribute = "wwise_localized";
namespace Legacy
{
static constexpr const char* WwiseLocalizedAttribute = "wwise_localised";
}
} // namespace WwiseXmlTags
///////////////////////////////////////////////////////////////////////////////////////////////////
// Wwise-specific helper functions
///////////////////////////////////////////////////////////////////////////////////////////////////
inline AkVector AZVec3ToAkVector(const AZ::Vector3& vec3)
{
// swizzle Y <--> Z
AkVector akVec;
akVec.X = vec3.GetX();
akVec.Y = vec3.GetZ();
akVec.Z = vec3.GetY();
return akVec;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
inline AkTransform AZVec3ToAkTransform(const AZ::Vector3& position)
{
AkTransform akTransform;
akTransform.SetOrientation(0.0, 0.0, 1.0, 0.0, 1.0, 0.0); // May add orientation support later.
akTransform.SetPosition(AZVec3ToAkVector(position));
return akTransform;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
inline void ATLTransformToAkTransform(const SATLWorldPosition& atlTransform, AkTransform& akTransform)
{
akTransform.Set(
AZVec3ToAkVector(atlTransform.GetPositionVec()),
AZVec3ToAkVector(atlTransform.GetForwardVec().GetNormalized()), // Wwise SDK requires that the Orientation vectors
AZVec3ToAkVector(atlTransform.GetUpVec().GetNormalized()) // are normalized prior to sending to the apis.
);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
namespace Wwise
{
// See AkMemoryMgr.h
inline static const char* MemoryManagerCategories[]
{
"Object", "Event", "Structure", "Media", "GameObject", "Processing", "ProcessingPlugin", "Streaming", "StreamingIO", "SpatialAudio",
"SpatialAudioGeometry", "SpatialAudioPaths", "GameSim", "MonitorQueue", "Profiler", "FilePackage", "SoundEngine"
};
static_assert(AZ_ARRAY_SIZE(MemoryManagerCategories) == AkMemID_NUM,
"Wwise memory categories have changed, the list of display names needs to be updated.");
} // namespace Wwise
} // namespace Audio
@@ -0,0 +1,110 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Config_wwise.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/FileFunc/FileFunc.h>
// For AZ_Printf statements...
#define WWISE_CONFIG_WINDOW "WwiseConfig"
namespace Audio::Wwise
{
static AZStd::string_view s_configuredBanksPath = DefaultBanksPath;
const AZStd::string_view GetBanksRootPath()
{
return s_configuredBanksPath;
}
void SetBanksRootPath(const AZStd::string_view path)
{
s_configuredBanksPath = path;
}
// static
void ConfigurationSettings::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<PlatformMapping>()
->Version(2)
->Field("assetPlatform", &PlatformMapping::m_assetPlatform)
->Field("altAssetPlatform", &PlatformMapping::m_altAssetPlatform)
->Field("enginePlatform", &PlatformMapping::m_enginePlatform)
->Field("wwisePlatform", &PlatformMapping::m_wwisePlatform)
->Field("bankSubPath", &PlatformMapping::m_bankSubPath)
;
serializeContext->Class<ConfigurationSettings>()
->Version(1)
->Field("platformMaps", &ConfigurationSettings::m_platformMappings)
;
}
}
bool ConfigurationSettings::Load(const AZStd::string& filePath)
{
AZ::IO::Path fileIoPath(filePath);
auto outcome = AzFramework::FileFunc::ReadJsonFile(fileIoPath);
if (!outcome)
{
AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: %s\n", outcome.GetError().c_str());
return false;
}
m_platformMappings.clear();
AZ::JsonDeserializerSettings deserializeSettings;
AZ::ComponentApplicationBus::BroadcastResult(deserializeSettings.m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
auto result = AZ::JsonSerialization::Load(*this, outcome.GetValue(), deserializeSettings);
if (result.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
{
AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: Deserializing Json file '%s'\n", filePath.c_str());
return false;
}
AZ_Printf(WWISE_CONFIG_WINDOW, "Loaded '%s' successfully.\n", filePath.c_str());
return true;
}
bool ConfigurationSettings::Save(const AZStd::string& filePath)
{
AZ::JsonSerializerSettings serializeSettings;
AZ::ComponentApplicationBus::BroadcastResult(serializeSettings.m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
rapidjson::Document jsonDoc;
auto result = AZ::JsonSerialization::Store(jsonDoc, jsonDoc.GetAllocator(), *this, serializeSettings);
if (result.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
{
AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: Serializing Json file '%s'\n", filePath.c_str());
return false;
}
auto outcome = AzFramework::FileFunc::WriteJsonFile(jsonDoc, filePath);
if (!outcome)
{
AZ_Printf(WWISE_CONFIG_WINDOW, "ERROR: %s\n", outcome.GetError().c_str());
return false;
}
AZ_Printf(WWISE_CONFIG_WINDOW, "Saved '%s' successfully.\n", filePath.c_str());
return true;
}
} // namespace Audio::Wwise
@@ -0,0 +1,68 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/string/string.h>
namespace Audio::Wwise
{
static constexpr const char DefaultBanksPath[] = "sounds/wwise/";
static constexpr const char ExternalSourcesPath[] = "external";
static constexpr const char ConfigFile[] = "wwise_config.json";
static constexpr const char BankExtension[] = ".bnk";
static constexpr const char MediaExtension[] = ".wem";
static constexpr const char InitBank[] = "init.bnk";
//! Banks path that's set after reading the configuration settings.
//! This might be different than the DefaultBanksPath.
const AZStd::string_view GetBanksRootPath();
void SetBanksRootPath(const AZStd::string_view path);
/**
* ConfigurationSettings
*/
struct ConfigurationSettings
{
AZ_TYPE_INFO(ConfigurationSettings, "{6BEEC05E-C5AE-4270-AAAD-08E27A6B5341}");
AZ_CLASS_ALLOCATOR(ConfigurationSettings, AZ::SystemAllocator, 0);
struct PlatformMapping
{
AZ_TYPE_INFO(PlatformMapping, "{9D444546-784B-4509-A8A5-8E174E345097}");
AZ_CLASS_ALLOCATOR(PlatformMapping, AZ::SystemAllocator, 0);
PlatformMapping() = default;
~PlatformMapping() = default;
// Serialized Data...
AZStd::string m_assetPlatform; // LY Asset Platform name (i.e. "pc", "osx_gl", "es3", ...)
AZStd::string m_altAssetPlatform; // Some platforms can be run using a different asset platform. Useful for builder worker.
AZStd::string m_enginePlatform; // LY Engine Platform name (i.e. "Windows", "Mac", "Android", ...)
AZStd::string m_wwisePlatform; // Wwise Platform name (i.e. "Windows", "Mac", "Android", ...)
AZStd::string m_bankSubPath; // Wwise Banks Sub-Path (i.e. "windows", "mac", "android", ...)
};
ConfigurationSettings() = default;
~ConfigurationSettings() = default;
static void Reflect(AZ::ReflectContext* context);
bool Load(const AZStd::string& filePath);
bool Save(const AZStd::string& filePath);
// Serialized Data...
AZStd::vector<PlatformMapping> m_platformMappings;
};
} // namespace Audio::Wwise
@@ -0,0 +1,474 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <AzCore/PlatformIncl.h>
#include <FileIOHandler_wwise.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/IStreamer.h>
#include <IAudioInterfacesCommonData.h>
#include <AkPlatformFuncs_Platform.h>
#include <AudioEngineWwise_Traits_Platform.h>
#include <platform.h>
#include <ISystem.h>
#include <AzFramework/Archive/IArchive.h>
#define MAX_NUMBER_STRING_SIZE (10) // 4G
#define ID_TO_STRING_FORMAT_BANK AKTEXT("%u.bnk")
#define ID_TO_STRING_FORMAT_WEM AKTEXT("%u.wem")
#define MAX_EXTENSION_SIZE (4) // .xxx
#define MAX_FILETITLE_SIZE (MAX_NUMBER_STRING_SIZE + MAX_EXTENSION_SIZE + 1) // null-terminated
namespace Audio
{
// AkFileHandle must be able to store our AZ::IO::HandleType
static_assert(sizeof(AkFileHandle) >= sizeof(AZ::IO::HandleType), "AkFileHandle must be able to store at least the size of a AZ::IO::HandleType");
namespace Platform
{
AkFileHandle GetAkFileHandle(AZ::IO::HandleType realFileHandle);
AZ::IO::HandleType GetRealFileHandle(AkFileHandle akFileHandle);
void SetThreadProperties(AkThreadProperties& threadProperties);
}
AkFileHandle GetAkFileHandle(AZ::IO::HandleType realFileHandle)
{
if (realFileHandle == AZ::IO::InvalidHandle)
{
return InvalidAkFileHandle;
}
return Platform::GetAkFileHandle(realFileHandle);
}
AZ::IO::HandleType GetRealFileHandle(AkFileHandle akFileHandle)
{
if (akFileHandle == InvalidAkFileHandle)
{
return AZ::IO::InvalidHandle;
}
return Platform::GetRealFileHandle(akFileHandle);
}
CBlockingDevice_wwise::~CBlockingDevice_wwise()
{
Destroy();
}
bool CBlockingDevice_wwise::Init(size_t poolSize)
{
Destroy();
AkDeviceSettings deviceSettings;
AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings);
deviceSettings.uIOMemorySize = poolSize;
deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_BLOCKING;
Platform::SetThreadProperties(deviceSettings.threadProperties);
m_deviceID = AK::StreamMgr::CreateDevice(deviceSettings, this);
return m_deviceID != AK_INVALID_DEVICE_ID;
}
void CBlockingDevice_wwise::Destroy()
{
if (m_deviceID != AK_INVALID_DEVICE_ID)
{
AK::StreamMgr::DestroyDevice(m_deviceID);
m_deviceID = AK_INVALID_DEVICE_ID;
}
}
bool CBlockingDevice_wwise::Open(const char* filename, AkOpenMode openMode, AkFileDesc& fileDesc)
{
const char* openModeString = nullptr;
switch (openMode)
{
case AK_OpenModeRead:
openModeString = "rbx";
break;
case AK_OpenModeWrite:
openModeString = "wbx";
break;
case AK_OpenModeWriteOvrwr:
openModeString = "w+bx";
break;
case AK_OpenModeReadWrite:
openModeString = "abx";
break;
default:
AZ_Assert(false, "Unknown Wwise file open mode.");
return false;
}
const size_t fileSize = gEnv->pCryPak->FGetSize(filename);
if (fileSize > 0)
{
AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(filename, openModeString, AZ::IO::IArchive::FOPEN_HINT_DIRECT_OPERATION);
if (fileHandle != AZ::IO::InvalidHandle)
{
fileDesc.hFile = GetAkFileHandle(fileHandle);
fileDesc.iFileSize = static_cast<AkInt64>(fileSize);
fileDesc.uSector = 0;
fileDesc.deviceID = m_deviceID;
fileDesc.pCustomParam = nullptr;
fileDesc.uCustomParamSize = 0;
return true;
}
}
return false;
}
AKRESULT CBlockingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics&, void* buffer, AkIOTransferInfo& transferInfo)
{
AZ_Assert(buffer, "Wwise didn't provide a valid buffer to write to.");
AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
const uint64_t currentFileReadPos = gEnv->pCryPak->FTell(fileHandle);
const uint64_t wantedFileReadPos = static_cast<uint64_t>(transferInfo.uFilePosition);
if (currentFileReadPos != wantedFileReadPos)
{
gEnv->pCryPak->FSeek(fileHandle, wantedFileReadPos, SEEK_SET);
}
const size_t bytesRead = gEnv->pCryPak->FReadRaw(buffer, 1, transferInfo.uRequestedSize, fileHandle);
AZ_Assert(bytesRead == static_cast<size_t>(transferInfo.uRequestedSize),
"Number of bytes read (%zu) for Wwise request doesn't match the requested size (%u).", bytesRead, transferInfo.uRequestedSize);
return (bytesRead > 0) ? AK_Success : AK_Fail;
}
AKRESULT CBlockingDevice_wwise::Write(AkFileDesc& fileDesc, const AkIoHeuristics&, void* data, AkIOTransferInfo& transferInfo)
{
AZ_Assert(data, "Wwise didn't provide a valid buffer to read from.");
AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
const uint64_t currentFileWritePos = gEnv->pCryPak->FTell(fileHandle);
const uint64_t wantedFileWritePos = static_cast<uint64_t>(transferInfo.uFilePosition);
if (currentFileWritePos != wantedFileWritePos)
{
gEnv->pCryPak->FSeek(fileHandle, wantedFileWritePos, SEEK_SET);
}
const size_t bytesWritten = gEnv->pCryPak->FWrite(data, 1, static_cast<size_t>(transferInfo.uRequestedSize), fileHandle);
if (bytesWritten != static_cast<size_t>(transferInfo.uRequestedSize))
{
AZ_Error("Wwise", false, "Number of bytes written (%zu) for Wwise request doesn't match the requested size (%u).",
bytesWritten, transferInfo.uRequestedSize);
return AK_Fail;
}
return AK_Success;
}
AKRESULT CBlockingDevice_wwise::Close(AkFileDesc& fileDesc)
{
return gEnv->pCryPak->FClose(GetRealFileHandle(fileDesc.hFile)) ? AK_Success : AK_Fail;
}
AkUInt32 CBlockingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc)
{
// No constraint on block size (file seeking).
return 1;
}
void CBlockingDevice_wwise::GetDeviceDesc(AkDeviceDesc& deviceDesc)
{
deviceDesc.bCanRead = true;
deviceDesc.bCanWrite = true;
deviceDesc.deviceID = m_deviceID;
AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "CryPak", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName);
}
AkUInt32 CBlockingDevice_wwise::GetDeviceData()
{
return 1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
CStreamingDevice_wwise::~CStreamingDevice_wwise()
{
Destroy();
}
bool CStreamingDevice_wwise::Init(size_t poolSize)
{
Destroy();
AkDeviceSettings deviceSettings;
AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings);
deviceSettings.uIOMemorySize = poolSize;
deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_DEFERRED_LINED_UP;
Platform::SetThreadProperties(deviceSettings.threadProperties);
m_deviceID = AK::StreamMgr::CreateDevice(deviceSettings, this);
return m_deviceID != AK_INVALID_DEVICE_ID;
}
void CStreamingDevice_wwise::Destroy()
{
if (m_deviceID != AK_INVALID_DEVICE_ID)
{
AK::StreamMgr::DestroyDevice(m_deviceID);
m_deviceID = AK_INVALID_DEVICE_ID;
}
}
bool CStreamingDevice_wwise::Open(const char* filename, [[maybe_unused]] AkOpenMode openMode, AkFileDesc& fileDesc)
{
AZ_Assert(openMode == AK_OpenModeRead, "Wwise Async File IO - Only supports opening files for reading.\n");
const size_t fileSize = gEnv->pCryPak->FGetSize(filename);
if (fileSize)
{
AZStd::string* filenameStore = azcreate(AZStd::string, (filename));
fileDesc.hFile = AkFileHandle();
fileDesc.iFileSize = static_cast<AkInt64>(fileSize);
fileDesc.uSector = 0;
fileDesc.deviceID = m_deviceID;
fileDesc.pCustomParam = filenameStore;
fileDesc.uCustomParamSize = sizeof(AZStd::string*);
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
streamer->QueueRequest(streamer->CreateDedicatedCache(*filenameStore));
return true;
}
return false;
}
AKRESULT CStreamingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics& heuristics, AkAsyncIOTransferInfo& transferInfo)
{
AZ_Assert(fileDesc.pCustomParam, "Wwise Async File IO - Reading a file before it has been opened.\n");
auto callback = [&transferInfo](AZ::IO::FileRequestHandle request)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Audio);
AZ::IO::IStreamerTypes::RequestStatus status = AZ::Interface<AZ::IO::IStreamer>::Get()->GetRequestStatus(request);
switch (status)
{
case AZ::IO::IStreamerTypes::RequestStatus::Completed:
transferInfo.pCallback(&transferInfo, AK_Success);
break;
case AZ::IO::IStreamerTypes::RequestStatus::Canceled:
transferInfo.pCallback(&transferInfo, AK_Cancelled);
break;
default:
transferInfo.pCallback(&transferInfo, AK_Fail);
break;
}
};
// The priorities for Wwise range from 0 (lowest priority) to 100 (highest priority). AZ::IO::Streamer has
// a similar range except between 0 (lowest) and 255 (highest) so remap from one to the other.
static_assert(AK_MIN_PRIORITY == 0, "The minimum priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
static_assert(AK_DEFAULT_PRIORITY == 50, "The default priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
static_assert(AK_MAX_PRIORITY == 100, "The maximum priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
static_assert(AZ::IO::IStreamerTypes::s_priorityLowest == 0, "The priority range for AZ::IO::Streamer has changed, please update Wwise to match.");
static_assert(AZ::IO::IStreamerTypes::s_priorityHighest == 255, "The priority range for AZ::IO::Streamer has changed, please update Wwise to match.");
AZ::u16 wwisePriority = aznumeric_caster(heuristics.priority);
AZ::u8 priority = aznumeric_caster(
(wwisePriority << 1) // 100 -> 200
+ (wwisePriority >> 1) // 200 -> 250
+ (wwisePriority >> 4) // 250 -> 256
- (wwisePriority >> 6)); // 256 -> 255
auto filename = reinterpret_cast<AZStd::string*>(fileDesc.pCustomParam);
auto offset = aznumeric_cast<size_t>(transferInfo.uFilePosition);
auto readSize = aznumeric_cast<size_t>(transferInfo.uRequestedSize);
auto bufferSize = aznumeric_cast<size_t>(transferInfo.uBufferSize);
AZStd::chrono::microseconds deadline = AZStd::chrono::duration<float, AZStd::milli>(heuristics.fDeadline);
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
AZ::IO::FileRequestPtr request = streamer->Read(*filename, transferInfo.pBuffer, bufferSize, readSize, deadline, priority, offset);
streamer->SetRequestCompleteCallback(request, AZStd::move(callback));
streamer->QueueRequest(AZStd::move(request));
return AK_Success;
}
AKRESULT CStreamingDevice_wwise::Write(AkFileDesc&, const AkIoHeuristics&, AkAsyncIOTransferInfo&)
{
AZ_Assert(false, "Wwise Async File IO - Writing audio data is not supported for AZ::IO::Streamer based device.\n");
return AK_Fail;
}
AKRESULT CStreamingDevice_wwise::Close(AkFileDesc& fileDesc)
{
AZ_Assert(fileDesc.pCustomParam, "Wwise Async File IO - Closing a file before it has been opened.\n");
auto filename = reinterpret_cast<AZStd::string*>(fileDesc.pCustomParam);
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
streamer->QueueRequest(streamer->DestroyDedicatedCache(*filename));
azdestroy(filename);
return AK_Success;
}
AkUInt32 CStreamingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc)
{
// No constraint on block size (file seeking).
return 1;
}
void CStreamingDevice_wwise::GetDeviceDesc(AkDeviceDesc& deviceDesc)
{
deviceDesc.bCanRead = true;
deviceDesc.bCanWrite = false;
deviceDesc.deviceID = m_deviceID;
AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "Streamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName);
}
AkUInt32 CStreamingDevice_wwise::GetDeviceData()
{
return 2;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
CFileIOHandler_wwise::CFileIOHandler_wwise()
: m_useAsyncOpen(false)
{
::memset(m_bankPath, 0, AK_MAX_PATH * sizeof(AkOSChar));
::memset(m_languageFolder, 0, AK_MAX_PATH * sizeof(AkOSChar));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
AKRESULT CFileIOHandler_wwise::Init(size_t poolSize)
{
// If the Stream Manager's File Location Resolver was not set yet, set this object as the
// File Location Resolver (this I/O hook is also able to resolve file location).
if (!AK::StreamMgr::GetFileLocationResolver())
{
AK::StreamMgr::SetFileLocationResolver(this);
}
if (!m_streamingDevice.Init(poolSize))
{
return AK_Fail;
}
if (!m_blockingDevice.Init(poolSize))
{
return AK_Fail;
}
return AK_Success;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
void CFileIOHandler_wwise::ShutDown()
{
if (AK::StreamMgr::GetFileLocationResolver() == this)
{
AK::StreamMgr::SetFileLocationResolver(nullptr);
}
m_blockingDevice.Destroy();
m_streamingDevice.Destroy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
AKRESULT CFileIOHandler_wwise::Open(const AkOSChar* fileName, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc)
{
AKRESULT akResult = AK_Fail;
if (syncOpen || !m_useAsyncOpen)
{
syncOpen = true;
AkOSChar finalFilePath[AK_MAX_PATH] = { '\0' };
AKPLATFORM::SafeStrCat(finalFilePath, m_bankPath, AK_MAX_PATH);
if (flags && openMode == AK_OpenModeRead)
{
// Add language folder if the file is localized.
if (flags->uCompanyID == AKCOMPANYID_AUDIOKINETIC && flags->uCodecID == AKCODECID_BANK && flags->bIsLanguageSpecific)
{
AKPLATFORM::SafeStrCat(finalFilePath, m_languageFolder, AK_MAX_PATH);
}
}
AKPLATFORM::SafeStrCat(finalFilePath, fileName, AK_MAX_PATH);
char* tempStr = nullptr;
CONVERT_OSCHAR_TO_CHAR(finalFilePath, tempStr);
if (openMode == AK_OpenModeRead)
{
return m_streamingDevice.Open(tempStr, openMode, fileDesc) ? AK_Success : AK_Fail;
}
return m_blockingDevice.Open(tempStr, openMode, fileDesc) ? AK_Success : AK_Fail;
}
return akResult;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
AKRESULT CFileIOHandler_wwise::Open(AkFileID fileID, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc)
{
AKRESULT akResult = AK_Fail;
if (flags && (syncOpen || !m_useAsyncOpen))
{
syncOpen = true;
AkOSChar finalFilePath[AK_MAX_PATH] = { '\0' };
AKPLATFORM::SafeStrCat(finalFilePath, m_bankPath, AK_MAX_PATH);
if (openMode == AK_OpenModeRead)
{
// Add language folder if the file is localized.
if (flags->uCompanyID == AKCOMPANYID_AUDIOKINETIC && flags->bIsLanguageSpecific)
{
AKPLATFORM::SafeStrCat(finalFilePath, m_languageFolder, AK_MAX_PATH);
}
}
AkOSChar fileName[MAX_FILETITLE_SIZE] = { '\0' };
const AkOSChar* const filenameFormat = (flags->uCodecID == AKCODECID_BANK ? ID_TO_STRING_FORMAT_BANK : ID_TO_STRING_FORMAT_WEM);
AK_OSPRINTF(fileName, MAX_FILETITLE_SIZE, filenameFormat, static_cast<int unsigned>(fileID));
AKPLATFORM::SafeStrCat(finalFilePath, fileName, AK_MAX_PATH);
char* filePath = nullptr;
CONVERT_OSCHAR_TO_CHAR(finalFilePath, filePath);
if (openMode == AK_OpenModeRead)
{
return m_streamingDevice.Open(filePath, openMode, fileDesc) ? AK_Success : AK_Fail;
}
return m_blockingDevice.Open(filePath, openMode, fileDesc) ? AK_Success : AK_Fail;
}
return akResult;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
void CFileIOHandler_wwise::SetBankPath(const char* const bankPath)
{
const AkOSChar* akBankPath = nullptr;
CONVERT_CHAR_TO_OSCHAR(bankPath, akBankPath);
AKPLATFORM::SafeStrCpy(m_bankPath, akBankPath, AK_MAX_PATH);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
void CFileIOHandler_wwise::SetLanguageFolder(const char* const languageFolder)
{
const AkOSChar* akLanguageFolder = nullptr;
CONVERT_CHAR_TO_OSCHAR(languageFolder, akLanguageFolder);
AKPLATFORM::SafeStrCpy(m_languageFolder, akLanguageFolder, AK_MAX_PATH);
}
} // namespace Audio
@@ -0,0 +1,106 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <FileIOHandler_wwise_Platform.h>
#include <AK/SoundEngine/Common/AkTypes.h>
#include <AK/SoundEngine/Common/AkStreamMgrModule.h>
namespace Audio
{
//! Wwise file IO device that access the Lumberyard file system through standard blocking file IO calls. Wwise will still
//! run these in separate threads so it won't be blocking the audio playback, but it will interfere with the internal
//! file IO scheduling of Lumberyard. This class can also write, so it's intended use is for one-off file reads and
//! for tools to be able to write files.
class CBlockingDevice_wwise
: public AK::StreamMgr::IAkIOHookBlocking
{
public:
~CBlockingDevice_wwise() override;
bool Init(size_t poolSize);
void Destroy();
AkDeviceID GetDeviceID() const { return m_deviceID; }
bool Open(const char* filename, AkOpenMode openMode, AkFileDesc& fileDesc);
AKRESULT Read(AkFileDesc& fileDesc, const AkIoHeuristics& heuristics, void* buffer, AkIOTransferInfo& transferInfo) override;
AKRESULT Write(AkFileDesc& fileDesc, const AkIoHeuristics& heuristics, void* data, AkIOTransferInfo& transferInfo) override;
AKRESULT Close(AkFileDesc& fileDesc) override;
AkUInt32 GetBlockSize(AkFileDesc& fileDesc) override;
void GetDeviceDesc(AkDeviceDesc& deviceDesc) override;
AkUInt32 GetDeviceData() override;
protected:
AkDeviceID m_deviceID = AK_INVALID_DEVICE_ID;
};
//! Wwise file IO device that uses AZ::IO::Streamer to asynchronously handle file requests. By using AZ::IO::Streamer file requests
//! can be scheduled along side other file requests for optimal disk usage. This class can't write and is intended to be used
//! as part of a streaming system.
class CStreamingDevice_wwise
: public AK::StreamMgr::IAkIOHookDeferred
{
public:
~CStreamingDevice_wwise() override;
bool Init(size_t poolSize);
void Destroy();
AkDeviceID GetDeviceID() const { return m_deviceID; }
bool Open(const char* filename, AkOpenMode openMode, AkFileDesc& fileDesc);
AKRESULT Read(AkFileDesc& fileDesc, const AkIoHeuristics& heuristics, AkAsyncIOTransferInfo& transferInfo) override;
AKRESULT Write(AkFileDesc& fileDesc, const AkIoHeuristics& heuristics, AkAsyncIOTransferInfo& transferInfo) override;
void Cancel([[maybe_unused]] AkFileDesc& fileDesc, [[maybe_unused]] AkAsyncIOTransferInfo& transferInfo, [[maybe_unused]] bool& cancelAllTransfersForThisFile) override {}
AKRESULT Close(AkFileDesc& fileDesc) override;
AkUInt32 GetBlockSize(AkFileDesc& fileDesc) override;
void GetDeviceDesc(AkDeviceDesc& deviceDesc) override;
AkUInt32 GetDeviceData() override;
protected:
AkDeviceID m_deviceID = AK_INVALID_DEVICE_ID;
};
class CFileIOHandler_wwise
: public AK::StreamMgr::IAkFileLocationResolver
{
public:
CFileIOHandler_wwise();
~CFileIOHandler_wwise() override = default;
CFileIOHandler_wwise(const CFileIOHandler_wwise&) = delete;
CFileIOHandler_wwise& operator=(const CFileIOHandler_wwise&) = delete;
AKRESULT Init(size_t poolSize);
void ShutDown();
// IAkFileLocationResolver overrides.
AKRESULT Open(const AkOSChar* fileName, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc) override;
AKRESULT Open(AkFileID fileID, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc) override;
// ~IAkFileLocationResolver overrides.
void SetBankPath(const char* const bankPath);
void SetLanguageFolder(const char* const languageFolder);
private:
CStreamingDevice_wwise m_streamingDevice;
CBlockingDevice_wwise m_blockingDevice;
AkOSChar m_bankPath[AK_MAX_PATH];
AkOSChar m_languageFolder[AK_MAX_PATH];
bool m_useAsyncOpen;
};
} // namespace Audio
@@ -0,0 +1,29 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
//
// Use this plugin registration helpers header to auto-register plugin libraries.
// This will give a standard set of plugins, check <AK/Plugin/AllPluginFactories.h> for what it includes.
//
#include <AK/Plugin/AllPluginsRegistrationHelpers.h>
//
// Prior to finalization of a game, it is recommended that you include only the plugin headers used by the game.
// Third party plugins and/or plugins not included in <AK/Plugin/AllPluginFactories.h> should be added below.
//
// For example:
//
// #include <AK/Plugin/AkConvolutionReverbFXFactory.h>
// #include <AK/Plugin/AkReflectFXFactory.h>
// ...