/* * 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 #include #include #include #include #include #include #include #include #include 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(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(middlewareControl->GetId()); } case EACEControlType::eACET_SWITCH_STATE: { return AZStd::make_shared(middlewareControl->GetId()); } } } return AZStd::make_shared(middlewareControl->GetId()); } return nullptr; } //-------------------------------------------------------------------------------------------// TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(AZ::rapidxml::xml_node* 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(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(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(control->GetId()); } } } } return nullptr; } //-------------------------------------------------------------------------------------------// AZ::rapidxml::xml_node* 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 rtpcConnection = AZStd::static_pointer_cast(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 stateConnection = AZStd::static_pointer_cast(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(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