Wwise 插件 DLL 包括以下内容:
|
Note: 一个 DLL 中可以包含多个插件。更多信息请参阅 导出函数 和 Wwise 插件 XML 描述文件 。 |
建议您组织好工程,在一个静态库中实现声音引擎效果器;此静态库在插件 DLL 内链接,并于游戏链接时复用。作为范例,可以查看 Wwise 自带的插件示例(示例 )是如何组织的。
您需要创建一个基于 AK::Wwise::IAudioPlugin
的子类,在其中实现所有用户界面的方法。这个类将用来管理与插件 UI 和音频包生成相关的一切任务。
#include <AK/Wwise/AudioPlugin.h> class SinePlugin : public AK::Wwise::IAudioPlugin { public: // 所有 AK::Wwise::IAudioPlugin 用户界面方法... (...) // 在 XML 文件中定义的 CompanyID 和 PluginID static const short CompanyID; static const short PluginID; private: AK::Wwise::IPluginPropertySet * m_pPSet; };
实现 AK::Wwise::IAudioPlugin
中的各个函数将决定您的插件如何响应 Wwise 中的各种情况。这些函数允许插件响应以下情况:
以上主题中有部分将在后续章节中介绍,您也可以参阅 AK::Wwise::IAudioPlugin
的参考信息,来了解想要实现的接口和函数的完整说明。
当 音频设计师创建一个源插件或效果器插件时,Wwise 调用 DLL(导出函数 )中的 AkCreatePlugin()
函数来创建新的实例。当用户 删除插件时,Wwise 将针对该实例调用 AK::Wwise::IPluginBase::Destroy()
方法。此方法必须释放对象可能会消耗的所有内存或其他资源,然后删除对象本身。
如果使用运算符 new
创建实例, AK::Wwise::IPluginBase::Destroy()
的典型实现方式如下:
void SinePlugin::Destroy() { delete this; }
针对每个插件将自动创建一个 AK::Wwise::IPluginPropertySet,因此您无需自己实现属性支持。在创建插件时,会通过指向
AK::Wwise::IPluginPropertySet
实例的指针来调用 AK::Wwise::IAudioPlugin::SetPluginPropertySet()
。
您的插件代码中,应将此指针保留为对象成员,以便在需要时进行查询。例如在您实现 AK::Wwise::IAudioPlugin::GetBankParameters()
时,可以允许查询该成员。更多信息请参阅 生成声音包 。
|
Note: 在针对插件实例调用 AK::Wwise::IPluginBase::Destroy() 之前, AK::Wwise::IAudioPlugin::SetPluginPropertySet() 接收到的 AK::Wwise::IPluginPropertySet 将一直存在。 |
如果您的插件使用了复杂数据,例如曲线、坐标图等,而非简单属性,则无法保存在属性集中。您需要将它们保留在插件类中,并使用 AK::Wwise::IPluginPropertySet::NotifyInternalDataChanged()
通知 Wwise 哪些特定数据已经发生变化。这将告诉 Wwise,您的数据需要保存或者传递给声音引擎。不应在插件的 XML 中定义此类数据。当调用 NotifyInternalDataChanged 时, 您可以指定 ParamID 来只通知一部分数据发生了变化。使用 AK::IAkPluginParam::ALL_PLUGIN_DATA_ID
表明插件的所有数据均已发生变化。
|
Note: 请勿针对 XML 中声明的简单属性使用 NotifyInternalDataChanged。当使用 AK::Wwise::IPluginPropertySet::SetValue 时,将自动执行通知。 |
当用户修改复杂属性并调用 NotifyInternalDataChanged 时,Wwise 将通过 AK::Wwise::IAudioPlugin::GetPluginData
进行回调,并将得到的数据块传递给插件的声音引擎部分的。您的声音引擎插件将通过 AK::IAkPluginParam::SetParam
接收数据块,其 ParamID 已在 NotifyInternalDataChanged 中指定。
|
Note: 如果您使用了复杂属性,则必须在 AK::IAkPluginParam::SetParam 和 AK::Wwise::IAudioPlugin::GetPluginData 中处理 AK::IAkPluginParam::ALL_PLUGIN_DATA_ID。该 ID 至少要在首次运行插件时使用一次。 |
当插件在 XML 中声明其属性时,将自动执行插件参数的加载和保存。但对于复杂数据,您必须自行编写数据持久化代码。为此需要重写 AK::Wwise::IAudioPlugin::Save
和 AK::Wwise::IAudioPlugin::Load
。请参阅 AK::IXmlTextReader
和 AK::IXmlTextWriter
了解详情。如果您的工程采取了版本控制管理,那么建议您将数据保存到 XML 中,以便合并文件。插件代码还应负责处理其数据的版本管理。请确保您的插件知道哪些版本的数据可以加载,哪些不可以,以避免崩溃和数据损坏。
音频设计师可以通过对话框中的控件直接更改属性值,也可以使用撤消/恢复命令来更改属性值。当用户更改属性值时,插件将调用 AK::Wwise::IAudioPlugin::NotifyPropertyChanged()
。如果此时对话框仍被显示,就可以在对话框中指定要执行的操作,例如启用或禁用控件等。
当 音频设计师使用下拉列表或键盘快捷键更改当前平台时,将调用 AK::Wwise::IAudioPlugin::NotifyCurrentPlatformChanged()
方法来通知您的插件。如果此时对话框仍被显示,您可以将它适配至当前平台。
您必须为插件编写代码,以便在需要的情况下将其当前设置存储到声音包中。可以通过实现 AK::Wwise::IAudioPlugin::GetBankParameters()
方法来做到。
插件参数将作为数据块写入到声音包中。数据块将被直接从声音包加载到声音引擎插件的参数结构中。因此,在您写入参数时,必须保证其顺序与它们在声音引擎插件的参数结构中的声明顺序是一致的。更多信息请参阅 参数节点接口的实现 。
例如,考虑以下参数结构:
// 此效果器的参数结构。 struct AkFXSrcSineParams { AkReal32 fFrequency; // 频率(单位:赫兹)。 AkReal32 fGain; // 增益(单位:dBFS)。 AkReal32 fDuration; // 持续时间(仅当为有限值时才有效)。 };
以下 AK::Wwise::IAudioPlugin::GetBankParameters()
的实现将获取每个属性的当前值,然后使用适当方法将值写入,其中 AK::Wwise::IWriteData
对象作为接收的参数。这些操作按照参数结构中各成员的定义顺序执行。
bool SinePlugin::GetBankParameters( const GUID & in_guidPlatform, AK::Wwise::IWriteData* in_pDataWriter ) const { CComVariant varProp; m_pPSet->GetValue( in_guidPlatform, szSineFreq, varProp ); in_pDataWriter->WriteReal32( varProp.fltVal ); m_pPSet->GetValue( in_guidPlatform, szSineGain, varProp ); in_pDataWriter->WriteReal32( varProp.fltVal ); m_pPSet->GetValue( in_guidPlatform, szSineDuration, varProp ); in_pDataWriter->WriteReal32( varProp.fltVal ); return true; }
|
Note: AK::Wwise::IPluginPropertySet 实例会在调用 AK::Wwise::IAudioPlugin::SetPluginPropertySet() 时指派给插件,要获取属性的当前值,请为该实例调用 AK::Wwise::IPluginPropertySet::GetValue() 。 |
关于写入不同数据类型的可用方法,更多信息请参阅 AK::Wwise::IWriteData
。
为了定义供 音频设计师访问并编辑插件属性的对话框,您的 DLL 中可能也需要包含其相关资源(请参阅 插件对话框资源 了解更多信息)。必须在 AK::Wwise::IAudioPlugin
对象中实现与这些对话框相关的方法。
AK::Wwise::IAudioPlugin::GetResourceHandle()
方法将告知 Wwise 应在何处查找对话框资源。以下是使用 MFC 的标准实现:
// 获得访问 UI 资源的句柄。 HINSTANCE SinePlugin::GetResourceHandle() const { return AfxGetStaticModuleState()->m_hCurrentResourceHandle; }
还可以为您的插件对象实现 AK::Wwise::IAudioPlugin::GetDialog()
,让 Wwise 能够获取有关对话框的信息。其中包括对话框资源 ID 以及可选的 AK::Wwise::PopulateTableItem
。
|
Note: 效果器插件只有一个对话框,它将显示在 Effect Editor 的 Effect Settings 选项卡中。但源插件可以在 Contents Editor 和 Source Plug-in Property Editor 两个不同位置进行编辑。使用 AK::Wwise::IAudioPlugin::eDialog 参数来返回相应的信息。 |
以下是源插件中 AK::Wwise::IAudioPlugin::GetDialog()
的典型实现:
// 将非静态文本 UI 控件绑定到属性视图中的属性上 AK_BEGIN_POPULATE_TABLE(ToneGenProp) AK_POP_ITEM(IDC_CHECK_SWEEPFREQ, szSweepFreq) AK_POP_ITEM(IDC_RADIO_FREQSWEEPLIN, szSweepFreqType) AK_POP_ITEM(IDC_RADIO_FIXLENGTH, szDurMode) AK_END_POPULATE_TABLE() // 查看已调用哪个对话框,并将属性名称存储至 UI 控件绑定表。 bool ToneGenPlugin::GetDialog( eDialog in_eDialog, UINT & out_uiDialogID, PopulateTableItem *& out_pTable ) const { CComVariant varProp; switch ( in_eDialog ) { case SettingsDialog: out_uiDialogID = IDD_TONEGENPLUGIN_BIG; out_pTable = ToneGenProp; return true; case ContentsEditorDialog: out_uiDialogID = IDD_TONEGENPLUGIN_SMALL; out_pTable = NULL; return true; } return false; }
以下是为效果器插件实现的 AK::Wwise::IAudioPlugin::GetDialog()
:
// 将属性名称存储至 UI 控件绑定表。 bool DelayPlugin::GetDialog( eDialog in_eDialog, UINT & out_uiDialogID, PopulateTableItem *& out_pTable ) const { assert( in_eDialog == SettingsDialog ); out_uiDialogID = IDD_DELAY; out_pTable = NULL; return true; }
对于效果器和音频设备插件,可以不用实现专用对话框。对于这两种插件,如果未提供对话框,Wwise 将根据 XML 插件定义文件的内容动态地创建对话框。
// 如果未提供对话框资源,Wwise 将根据 XML 插件定义文件来显示默认对话框。 bool AkSinkPlugin::GetDialog( eDialog in_eDialog, UINT & out_uiDialogID, PopulateTableItem *& out_pTable ) const { // 返回 false 仅对效果器和音频设备有效。 // 其他插件类型将无法显示。 return false; }
请参阅 插件对话框资源 ,了解有关对话框实际资源的详情,包括格式、控件等。
请参阅 如何将常规控件绑定到属性 ,了解插件对话框何时使用 AK::Wwise::PopulateTableItem
、使用方法及相关宏的信息。
如果 UI 变更需要执行特定操作,例如启用或禁用控件,可以通过解析 AK::Wwise::IAudioPlugin::WindowProc()
函数中收到的窗口消息来执行这些操作:
// 在实现 UI 行为时,标准窗口函数可以让用户拦截任何感兴趣的消息。 bool ToneGenPlugin::WindowProc( eDialog in_eDialog, HWND in_hWnd, UINT in_message, WPARAM in_wParam, LPARAM in_lParam, LRESULT & out_lResult ) { if ( in_message == WM_INITDIALOG ) { // 在对话框窗口初始化时执行您需要的任何操作... } // 例如,捕获窗口命令动作(仅针对主要对话框)来启用/禁用控件 else if ( in_eDialog == SettingsDialog && in_message == WM_COMMAND ) { // 通知代码 switch ( HIWORD( in_wParam ) ) { case BN_CLICKED: // 检查哪个按钮被点击 switch ( LOWORD( in_wParam ) ) { case IDC_CHECK_SWEEPFREQ: // 验证是否启用了复选框 if ( IsDlgButtonChecked( in_hWnd, IDC_CHECK_SWEEPFREQ ) == BST_CHECKED ) { // 启用某些控件... } else if ( IsDlgButtonChecked( in_hWnd, IDC_CHECK_SWEEPFREQ ) == BST_UNCHECKED ) { // 禁用某些控件... } break; } } // 结束切换开关 hi 语句(通知代码) } // 结束命令窗口事件 // 返回 False,让父窗口处理消息。Return True // 对于不希望父窗口处理的消息,返回 True。 return false; }
实现 AK::Wwise::IAudioPlugin::DisplayNameForProp()
函数可以让 Wwise 获取该属性的用户容易理解的名称,此名称将显示在界面的多个位置。这些位置包括 RTPC Manager、改变属性后的撤消/恢复命令编辑菜单等。
in_szPropertyName
参数对应于插件 XML 定义文件(Wwise 插件 XML 描述文件 )中指定的属性名称。该方法的实现示例如下:
bool SinePlugin::DisplayNameForProp( LPCWSTR in_szPropertyName, LPWSTR out_szDisplayName, UINT in_unCharCount ) const { // 获取资源句柄 HINSTANCE hInst = AfxGetStaticModuleState()->m_hCurrentResourceHandle; if ( !wcscmp( in_szPropertyName, szSineFreq ) ) { ::LoadString( hInst, IDS_SINEFREQ, out_szDisplayName, in_unCharCount ); return true; } else if ( !wcscmp( in_szPropertyName, szSineGain ) ) { ::LoadString( hInst, IDS_SINEGAIN, out_szDisplayName, in_unCharCount ); return true; } // ... return false; }
对于非数值属性,例如取值为 On/Off 或 Yes/No 的布尔值,或者曲线类型这样的枚举值,或者 具有特殊的含义的值,您可以在实现 AK::Wwise::IAudioPlugin::DisplayNamesForPropValues()
方法时,为属性的部分或所有可能值指定要显示的文本。这些自定义文本将用在 RTPC 坐标图视图中,标示 Y 轴上的坐标值。
|
Note: 您可以为对话框的 Combo Box 控件指定“Options”属性格式,
对于 |
在以下代码示例中,名称从插件资源中获取的:
// 允许 Wwise 为该属性值获取用户可见的名称(例如 RTPC)。 bool DelayPlugin::DisplayNamesForPropValues( LPCWSTR in_szPropertyName, LPWSTR out_szValuesName, UINT in_unCharCount ) const { bool bFound = false; if ( !wcscmp( in_szPropertyName, szFeedbackEnabled ) ) { WCHAR szValueName[128]; CString csValuesName( L"0:" ); ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_FEEDBACKENABLED_OFF, szValueName, 128 ); csValuesName += szValueName; csValuesName += L",1:"; ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_FEEDBACKENABLED_ON, szValueName, 128 ); csValuesName += szValueName; _tcsncpy( out_szValuesName, csValuesName, in_unCharCount ); bFound = true; } else if ( !wcscmp( in_szPropertyName, szWetDryMix ) ) { WCHAR szValueName[128]; CString csValuesName( L"0:" ); ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_WETDRYMIX_DRY, szValueName, 128 ); csValuesName.AppendFormat( L"(%s) 0", szValueName ); csValuesName += L",100:"; ::LoadString( AfxGetStaticModuleState()->m_hCurrentResourceHandle, IDS_PROPVALUENAME_WETDRYMIX_WET, szValueName, 128 ); csValuesName.AppendFormat( L"(%s) 100", szValueName ); _tcsncpy( out_szValuesName, csValuesName, in_unCharCount ); bFound = true; } return bFound; }
当 音频设计师在插件的对话框标题栏中单击“?”图标时,将调用该插件的 AK::Wwise::IAudioPlugin::Help()
。您可以使用包括 HTMLHelp、WinHelp 或第三方帮助浏览器等各种工具来提供帮助。此函数用于接收窗口句柄,此句柄可以用作您要显示的任何窗口的父类。 AK::Wwise::IAudioPlugin::eDialog
参数可以让您选择具体的帮助主题,来匹配 音频设计师当前打开的对话框。如果您已处理帮助请求,此函数则必须返回 true,否则返回
false,在后一种情况下,Wwise
将显示与 Plug-in Manager 相关的帮助主题。
|
Note: 如前所述,效果器插件只有一个对话框,而源插件则有两个对话框。如果源插件两个对话框的帮助主题不同,则应使用 AK::Wwise::IAudioPlugin::eDialog 参数。 |
使用 HTML 帮助的源插件示例如下:
// 在用户单击“?”图标时提供联机帮助 bool ToneGenPlugin::Help( HWND in_hWnd, eDialog in_eDialog ) const { AFX_MANAGE_STATE( ::AfxGetStaticModuleState() ) ; DWORD dwTopic = ONLINEHELP::Tone_Generator_Settings; if ( in_eDialog == AK::Wwise::IAudioPlugin::ContentsEditorDialog ) dwTopic = ONLINEHELP::Tone_Generator_ContentsEditor; // 注:请勿调用 AfxGetApp()->HtmlHelp(),因为它将从错误的 // 父窗口中启动帮助窗口,导致悬浮窗口 // 行为表现异常。 ::HtmlHelp( NULL, AfxGetApp()->m_pszHelpFilePath, HH_HELP_CONTEXT, dwTopic ); return true; }
|
Caution: Help() 方法不应调用 AfxGetApp()->HtmlHelp()。它将从错误的父窗口中启动帮助窗口,导致悬浮窗口行为异常。应使用带 Null 窗口语柄的 HtmlHelp(),如上文示例中所示。 |
您的 DLL 必须包含插件对话框的资源。每个对话框的资源 ID 就是 AK::Wwise::IAudioPlugin::GetDialog() 返回的 ID。请参阅对话框相关代码 了解更多信息。
请参阅 Wwise 插件对话框参考文档 ,了解您可以在对话框中使用的控件说明,以及如何将它们绑定到属性等信息。
每个插件 DLL 必须导出两个(或三个)符号:
AkCreatePlugin():用于创建
AK::Wwise::IPluginBase
对象实例。g_pAKPluginList:使用宏
DEFINE_PLUGIN_REGISTER_HOOK
来定义此符号。AkGetSinkPluginDevices():
仅适用于 Audio Device 插件(Sink 插件)。列出此插件可用的所有 Audio Device。请参阅 适用于音频设备插件的 Wwise 设计工具 DLL。AkCreatePlugin()
函数接收公司 ID 和插件 ID 两个参数。这些 ID 对应于插件 XML 定义文件中定义的 ID(请参阅 Wwise 插件 XML 描述文件 和 Wwise 插件 ID 了解更多信息)。如果您的 DLL 和 XML 文件包含多个插件,这些参数将让告知您正在请求哪个插件。
以下是包含一个插件的 DLL 中已导出函数的示例:
#include <AK/Wwise/Utilities.h> #include <AK/SoundEngine/Common/IAkPlugin.h> #include <assert.h> // 创建 Wwise UI 插件 AK::Wwise::IPluginBase* __stdcall AkCreatePlugin( unsigned short in_usCompanyID, unsigned short in_usPluginID ) { if ( in_usCompanyID == SinePlugin::CompanyID && in_usPluginID == SinePlugin::PluginID ) return new MyPlugin; // 对于源插件,返回 AK::Wwise::IAudioPlugin-derived 对象。 // 如果调用此函数时使用的公司/插件 ID 有误,是因为 // 插件 XML 定义文件中的 ID 与代码中的 ID 不匹配。 assert( false ); return NULL; } DEFINE_PLUGIN_REGISTER_HOOK AK_STATIC_LINK_PLUGIN(SineSource); // 静态链接插件的声音引擎端。
在库描述文件(.def)中,您应该显式地指定要导出的两个函数:
LIBRARY "MyPlugin"
EXPORTS
AkCreatePlugin
只有插件在声音引擎中正确注册,Wwise 才能使用 DLL 中的插件。要正确注册您的插件,必须为其定义 AK::PluginRegistration
类。游戏内插件库中应包含此代码(请参阅 Wwise 声音引擎插件概述 )。在 DLL 的设计工具部分,您还需要在其中的 DllMain()
或 InitInstance()
函数中调用一次 AK::Wwise::RegisterWwisePlugin()
。确保设计工具端 DLL 代码包含对 AK_STATIC_LINK_PLUGIN(YourPlugin)
的引用(这样可以查找之前定义的 PluginRegistration
)。
用户第一次向工程中添加插件时,Wwise 设计工具可以显示一条消息,提醒用户需要承担的法律义务。可以将此文本嵌入到您的插件中,但这不是强制要求。如果不想使用此功能,可以跳过本节。
在您的插件代码中,导出函数 AkGetFirstTimeCreationMessage()。与其他导出函数一样,此函数接收公司 ID 和插件 ID 参数(请参阅上面的 导出函数 ) 。此函数还有两个输出参数:要显示的消息字符串,以及注册表键值字符串,可以用于查询是否已经创建该插件。
此函数的实现示例如下:
// 首次创建插件时显示的消息。 bool __stdcall AkGetFirstTimeCreationMessage( unsigned short in_usCompanyID, unsigned short in_usPluginID, BSTR& out_message, BSTR& out_key ) { assert( in_usCompanyID == SinePlugin::CompanyID && in_usPluginID == SinePlugin::PluginID ); AFX_MANAGE_STATE( AfxGetStaticModuleState() ); CString csText; csText.LoadString( IDS_SINEPLUGIN_LICENSEREMINDER ); out_message = csText.AllocSysString(); csText = _T("SinePlugin"); out_key = csText.AllocSysString(); return true; }
本例中,许可证提示文本存储在插件字符串表的 IDS_SINEPLUGIN_LICENSEREMINDER 下面。
通过 out_key 返回的文本“SinePlugin”是第一次调用此函数时 Wwise 创建的注册表键值名称: HKEY_CURRENT_USER/Software/Audiokinetic Inc./Wwise/PluginUserAcknowledge/Projects/{current project}/SinePlugin。显然,您要创建的名称对于插件和公司都应该不重名。当 Wwise 再次调用此函数并在注册表中找到此键值时,将不会再次显示您的消息。
在库描述文件(.def)中,将此函数添加到需要导出的函数列表中:
LIBRARY "MyPlugin"
EXPORTS
AkCreatePlugin
AkGetFirstTimeCreationMessage
主混音器层级结构内,总线上的效果器插件可以从其在声音引擎的运行时组件中接收监控信息。关于如何通过插件的声音引擎部分发布监控数据,请参阅 发布 Wwise 插件的监控数据 了解详情。
当接收到监控数据块(Wwise 中须启用监控功能)时,插件可以正确地解析数据,并触发统计编译或 UI 事件(例如 VU音量计)等操作。以下代码显示了在 IAudioPlugin::NotifyMonitorData() 的实现中,如何对监控数据做出反应的简单示例。当发布数据的平台与 Wwise 插件平台的字节顺序不同时,将进行必要的字节交换。
void MyPlugin::NotifyMonitorData( void * in_pData, unsigned int in_uDataSize, bool in_bNeedsByteSwap ) { // 解析从声音引擎插件发送来的数据块,且如果发现 // 平台的字节顺序与 PC 不同,则执行字节交换。 unsigned int * pData = (unsigned int *) in_pData; unsigned int uNumChannels = in_bNeedsByteSwap ?_byteswap_ulong( *pData++ ) : *pData++; float fChannelPeaks[MAX_NUM_CHANNELS]; if ( in_bNeedsByteSwap ) { for ( unsigned int i = 0; i < uNumChannels; ++i ) { fChannelPeaks[i] = (float) _byteswap_ulong( *pData++ ); } } else { memcpy( fChannelPeaks, pData, uNumChannels*sizeof(float) ); } // 创建针对输入数据的 UI 响应... }
在生成 SoundBank 的过程中,Wwise 会查询插件的授权状态。插件可以返回 AK::Wwise::LicenseStatus 中的一个枚举值。另外,插件还可返回一条信息及信息的严重性级别,以便显示在 SoundBank 生成日志中。
通过返回 LicenseStatus_Unlicensed 或 LicenseStatus_Expired,可以避免将相应插件包含在 SoundBank 中。
插件可以应用自有的授权验证方案。请注意,切勿在此函数中同步查询服务器。该函数需要立即返回答复。
若所用插件已在我方注册,则可使用 Wwise 的授权系统。使用以下代码查询 Wwise Project License 中的授权状态:
AK::Wwise::LicenseStatus MyPlugin::GetLicenseStatus(const GUID & in_guidPlatform, AK::Wwise::Severity& out_eSeverity, LPWSTR out_pszMessage, unsigned int in_uiBufferSize) { AK::Wwise::LicenseType eType; AK::Wwise::LicenseStatus eStatus; UINT32 uDaysToExpiry; m_pPSet->GetLicenseStatus(in_guidPlatform, eType, eStatus, uDaysToExpiry); return eStatus; }
请参阅 示例 查看插件工程示例的列表,从中您可以了解更多信息。
如果您遇到任何问题,可以参阅 Wwise 源插件和效果器插件故障排除指南 获取帮助。