You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp

633 lines
24 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 <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 <AzCore/Utils/Utils.h>
#include <ACETypes.h>
#include <AudioSystemControl_wwise.h>
#include <Common_wwise.h>
#include <QDir>
void InitWwiseResources()
{
Q_INIT_RESOURCE(EditorWwise);
}
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 "";
}
//-------------------------------------------------------------------------------------------//
CAudioSystemEditor_wwise::CAudioSystemEditor_wwise()
{
InitWwiseResources();
}
//-------------------------------------------------------------------------------------------//
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(AZ::rapidxml::xml_node<char>* node, EACEControlType atlControlType)
{
if (node)
{
AZStd::string_view element(node->name());
TImplControlType type = TagToType(element);
if (type != AUDIO_IMPL_INVALID_TYPE)
{
AZStd::string name;
AZStd::string_view localized;
if (auto nameAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false);
nameAttr != nullptr)
{
name = nameAttr->value();
}
if (auto localizedAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseLocalizedAttribute, 0, false);
localizedAttr != nullptr)
{
localized = localizedAttr->value();
}
bool isLocalized = AZ::StringFunc::Equal(localized, "true");
// If the control wasn't found, create a placeholder.
// We want to see that connection even if it's not in the middleware.
// User could be viewing the editor without a middleware 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 connect to one of the states within the switch
if (type == eWCT_WWISE_SWITCH_GROUP || type == eWCT_WWISE_GAME_STATE_GROUP)
{
if (auto childNode = node->first_node();
childNode != nullptr)
{
AZStd::string childName;
if (auto childNameAttr = childNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false);
childNameAttr != nullptr)
{
childName = childNameAttr->value();
}
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;
}
}
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 (auto multAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseMutiplierAttribute, 0, false);
multAttr != nullptr)
{
mult = AZStd::stof(AZStd::string(multAttr->value()));
}
if (auto shiftAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseShiftAttribute, 0, false);
shiftAttr != nullptr)
{
shift = AZStd::stof(AZStd::string(shiftAttr->value()));
}
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 (auto valueAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseValueAttribute, 0, false);
valueAttr != nullptr)
{
value = AZStd::stof(AZStd::string(valueAttr->value()));
}
connection->m_value = value;
return connection;
}
}
}
else
{
return AZStd::make_shared<IAudioConnection>(control->GetId());
}
}
}
}
return nullptr;
}
//-------------------------------------------------------------------------------------------//
AZ::rapidxml::xml_node<char>* CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType)
{
const IAudioSystemControl* control = GetControl(connection->GetID());
if (control)
{
XmlAllocator& xmlAllocator(AudioControls::s_xmlAllocator);
switch (control->GetType())
{
case AudioControls::eWCT_WWISE_SWITCH:
[[fallthrough]];
case AudioControls::eWCT_WWISE_SWITCH_GROUP:
[[fallthrough]];
case AudioControls::eWCT_WWISE_GAME_STATE:
[[fallthrough]];
case AudioControls::eWCT_WWISE_GAME_STATE_GROUP:
{
const IAudioSystemControl* parent = control->GetParent();
if (parent)
{
AZStd::string_view parentType = TypeToTag(parent->GetType());
auto switchNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(parentType.data())
);
auto switchNameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(parent->GetName().c_str())
);
auto stateNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
Audio::WwiseXmlTags::WwiseValueTag
);
auto stateNameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
switchNode->append_attribute(switchNameAttr);
stateNode->append_attribute(stateNameAttr);
switchNode->append_node(stateNode);
return switchNode;
}
break;
}
case AudioControls::eWCT_WWISE_RTPC:
{
auto connectionNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
);
auto nameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
connectionNode->append_attribute(nameAttr);
if (atlControlType == eACET_RTPC)
{
AZStd::shared_ptr<const CRtpcConnection> rtpcConnection = AZStd::static_pointer_cast<const CRtpcConnection>(connection);
if (rtpcConnection->m_mult != 1.f)
{
auto multAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseMutiplierAttribute,
xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_mult).c_str())
);
connectionNode->append_attribute(multAttr);
}
if (rtpcConnection->m_shift != 0.f)
{
auto shiftAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseShiftAttribute,
xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_shift).c_str())
);
connectionNode->append_attribute(shiftAttr);
}
}
else if (atlControlType == eACET_SWITCH_STATE)
{
AZStd::shared_ptr<const CStateToRtpcConnection> stateConnection = AZStd::static_pointer_cast<const CStateToRtpcConnection>(connection);
auto valueAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseValueAttribute,
xmlAllocator.allocate_string(AZStd::to_string(stateConnection->m_value).c_str())
);
connectionNode->append_attribute(valueAttr);
}
return connectionNode;
}
case AudioControls::eWCT_WWISE_EVENT:
[[fallthrough]];
case AudioControls::eWCT_WWISE_AUX_BUS:
{
auto connectionNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
);
auto nameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
connectionNode->append_attribute(nameAttr);
return connectionNode;
}
case AudioControls::eWCT_WWISE_SOUND_BANK:
{
auto connectionNode = xmlAllocator.allocate_node(
AZ::rapidxml::node_element,
xmlAllocator.allocate_string(TypeToTag(control->GetType()).data())
);
auto nameAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseNameAttribute,
xmlAllocator.allocate_string(control->GetName().c_str())
);
connectionNode->append_attribute(nameAttr);
if (control->IsLocalized())
{
auto locAttr = xmlAllocator.allocate_attribute(
Audio::WwiseXmlTags::WwiseLocalizedAttribute,
xmlAllocator.allocate_string("true")
);
connectionNode->append_attribute(locAttr);
}
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;
}
//-------------------------------------------------------------------------------------------//
AZ::IO::FixedMaxPath CAudioSystemEditor_wwise::GetDataPath() const
{
auto projectPath = AZ::IO::FixedMaxPath{ AZ::Utils::GetProjectPath() };
return (projectPath / "sounds" / "wwise_project");
}
} // namespace AudioControls