Files
o3de/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp
T
amzn-phist 5b148b1f40 Convert legacy XML handling to rapidxml
Updates the Audio Controls Editor code to use rapidxml instead of legacy
xml apis.  Further makes improvements to path manipulations away from
strings towards PathView apis and similar.

Fixes some issues encountered with memory management when handling xml
data that did not occur previously.

Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com>
2021-07-22 17:37:44 -05:00

186 lines
8.2 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AudioWwiseLoader.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <IAudioSystemControl.h>
#include <IAudioSystemEditor.h>
#include <AudioSystemEditor_wwise.h>
#include <AudioFileUtils.h>
#include <Config_wwise.h>
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 AZ::IO::FixedMaxPath wwiseProjectFullPath{ m_audioSystemImpl->GetDataPath() };
LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::GameParametersFolder }.Native());
LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::GameStatesFolder }.Native());
LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::SwitchesFolder }.Native());
LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::EventsFolder }.Native());
LoadControlsInFolder(AZ::IO::FixedMaxPath{ wwiseProjectFullPath / WwiseStrings::EnvironmentsFolder }.Native());
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());
AZ::IO::PathView fileName = filePath.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.assign(fileName.Native().data(), fileName.Native().size());
LoadSoundBanks(rootFolder, m_localizationFolder, true);
isLocalizedLoaded = true;
}
}
else if (fileName.Extension() == Audio::Wwise::BankExtension && !AZ::StringFunc::Equal(fileName.Native(), Audio::Wwise::InitBank))
{
m_audioSystemImpl->CreateControl(
SControlDef(AZStd::string{ fileName.Native() }, 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.Native());
}
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.Native());
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