Wwise SDK 2023.1.9
|
Effect 效果器插件接收已有的声音作为输入音频数据,将 DSP 算法作用于这些输入数据。编写效果器插件的工作主要包括实现 AK::IAkInPlaceEffectPlugin 或者 AK::IAkOutOfPlaceEffectPlugin 接口中的一个。在此只介绍与这些接口相关的函数。请参阅 创建声音引擎插件 了解与其他插件类型(AK::IAkPlugin 接口)共享的接口组件的信息。另请参阅随附的 AkDelay 插件了解详情( 示例 )。
此方法为效果器插件用于处理数据做准备,分配内存并设置初始条件。
The plug-in is passed in a pointer to a memory allocator interface (AK::IAkPluginMemAlloc
). We recommend that you allocate all dynamic memory through this interface using the provided memory allocation macros (refer to 在音频插件中分配/取消分配内存) so that Wwise can track the memory, and so that any underlying memory hooks in the game engine can use it. For the most common memory allocation needs, specifically allocation at initialization and release at termination, the plug-in does not need to retain a pointer to the allocator. It is also provided to the plug-in on termination.
The AK::IAkEffectPluginContext
interface can access the global context through AK::IAkPluginContextBase::GlobalContext()
.
The plug-in also receives a pointer to its associated parameter node interface (AK::IAkPluginParam
). Most plug-ins keep a reference to the associated parameter node to be able to retrieve parameters at runtime. 请参阅 参数节点与插件之间的通信。 了解更多详情。
All of these interfaces remain valid throughout the plug-in's lifespan so it is safe to keep an internal reference to them when necessary.
Effect plug-ins also receive the input/output audio format (which stays the same during the lifespan of the plug-in) to be able to allocate memory and set up processing for a given channel configuration.
备注: AK::IAkEffectPlugin::Init() is called every time the effect is instantiated, which happens when a voice starts playing or a mixing bus is instantiated. Because other sounds are typically playing already, this must occur within a reasonable amount of time. If you need to initialize large common/global data structures, do so when registering the plug-in library. 请参阅 在插件中使用全局声音引擎回调 了解更多详情。 |
效果器插件可选择实现以下 2 个接口之一:AK::IAkInPlaceEffectPlugin或AK::IAkOutOfPlaceEffectPlugin。一般大多数效果器应该采用原地效果(它们对输入和输出数据使用相同的音频缓冲区)。然而,当数据流有变时(例如 time-stretching 时间伸缩效果器),则必须实现非原地接口。
注意: 具有不同输入/输出声道配置的非原地效果器可插入 Master-Mixer hierarchy(主混音器层级结构)中。然而,无法将变速率效果器放置在混音总线上。具有不同输入/输出缓冲长度的效果器只可插入 Actor-Mixer hierarchy(用作源效果器)。 |
此方法原地执行给定音频缓冲区上的插件信号处理算法(请参阅 访问使用 AkAudioBuffer 结构的数据 了解详情)。此结构向插件提供有关输入采样点中有多少点有效(AkAudioBuffer::uValidFrames)和缓冲区可容纳的最大音频采样帧数(AkAudioBuffer::MaxFrames() method)的信息。AkAudioBuffer::eState结构成员向插件指示"这是不是最后一次执行":AK_NoMoreData 表示"是",AK_DataReady 表示"不是"。
当虚声部从已用时间播放(Play from Elapsed Time)时,AK::IAkInPlaceEffectPlugin::TimeSkip() 将替代 Execute(),以便插件能够在需要的情况下保持更新它们的内部状态。
此方法对非原地算法执行插件的信号处理。使用两个 AkAudioBuffer 结构,一个用于输入缓冲区,另一个用于输出缓冲区。管线使用输出音频缓冲区的 eState 来确定效果器的当前状态。只有当效果器在以下两种情况之一下才需要返回:一是处理完输入缓冲区时(会返回 AK_DataNeeded,以便稍后处理更多的数据),二是在填满整个输出缓冲区时(返回 AK_DataReady)。在非原地效果中还可实现效果器尾音,方法是将收到的 AK_NoMoreData 更改为 AK_DataReady,直至效果器排空其内部状态(此时应返回 AK_NoMoreData)。
直接处理完整个输入缓冲区,管线才会释放输入缓冲区。因此,一定要使用 in_uInOffset 编置参数开始读取上次 Execute() 调用停止时未读完的数据。下面示例介绍如何实现这一点。
当虚声部采用 Play from Elapsed Time 时,AK::IAkOutOfPlaceEffectPlugin::TimeSkip() 将替代 Execute(),以便插件能够在需要的情况下能一直更新其内部状态。然后此函数负责告诉管线,产生给定数量的输出帧通常需要处理多少输入采样点。
某些效果器具有内部状态,在输入完成播放后,必须输出该内部状态,以便正确进行衰减,最典型的是带延时线(delay line)的效果器。效果器 API 使得即使在没有任何有效输入数据的情况下也可继续执行。当 AkAudioBuffer 结构的 eState 标志变成 AK_NoMoreData 时,在完成当前执行后,管线不会再将有效的输入采样帧送到插件。然后插件可以自由在缓冲区中写入新帧(最大帧数为 MaxFrames() 返回的值),以便在完成输入信号后清空延时线。应始终告诉音频管线已经输出了多少帧,方法是正确更新 uValidFrames 字段。如果需要再次调用插件 Execute() 函数来完成效果器尾音刷新,则应将 eState 成员应为 AK_DataReady。只有当效果器为 eState 字段设置了 AK_NoMoreData 时,管线才会停止调用插件 Execute()。
处理尾音的最简单方法是使用 SDK 中提供的 AkFXTailHandler 服务类。由于一份 AkFXTailHandler 用作插件的类成员,因此我们在原位效果器中唯一需要做的是调用 AkFXTailHandler::HandleTail(),并将它和输出完成时要输出的音频样本总数(根据参数的不同,各个执行之间存在差异)传输给 AkAudioBuffer。请参阅 AkDelay 插件源代码了解详情(示例 )。
备注: 有关执行效果器插件的重要说明
|
在 Wwise 或游戏中可通过各种机制(例如 UI、事件和 RTPC)旁通插件。在这些情况下,不会调用插件 Execute() 函数。当解除旁通的时候,插件继续运行并再次调用 Execute() 函数时,插件将重新启动它的处理工作。旁通时将调用插件的 Reset() 函数,以清除延时线和其他状态信息,从而在最终解除旁通时能够有一个全新的开始。请参阅 AK::IAkPlugin::Reset() 了解详情。
注意: 运行时旁通和解除旁通插件可能导致信号中断,具体取决于插件和被处理的声音素材。 |
有关更多信息,请参阅以下各节:
“监控”是指在播放期间察看音频插件状态的流程。
在通过通信模块连接设计工具时,允许声音引擎插件实例将状态作为任意大小的数据缓冲区来传给插件的设计工具端。 此数据既可以是执行过程中计算得出的性能指标,也可以是在不同处理阶段测得的信号电平。
本节主要介绍声音引擎端的监控。 有关“接收”和“反序列化”流程的详细信息,请参阅 设计工具端的监控 章节。
发送监控数据
“发送”由插件的声音引擎部分完成。以下为此流程的步骤:
long
来说,其大小对 64 位平台来说就各不相同。有些使用 4 字节 (LLP64),有些则使用 8 字节 (LP64)。另外还要注意,若各个平台具有不同的对齐要求,则可能会因封装策略而导致 struct
布局互不相同。您可以使用 pack
pragma 来指定自定义封装对齐值。AK::IAkPluginContextBase::PostMonitorData()
函数,来通过性能分析器通信通道发送数据。此函数应在 Execute
函数之内调用。备注: 因为监控数据采用异步形式发送,所以 AK::IAkPluginContextBase::PostMonitorData() 会复制声音引擎插件提供的缓冲区。也就是说,缓冲区可安全地分配到堆栈上。在调用 PostMonitorData 后,还可将其释放。 |
有时可能无法发送监控数据:仅在 Wwise 设计工具执行性能分析时可使用监控数据。若设计工具的性能分析处于非活跃状态或确实无法对游戏实施性能分析,则没有必要准备和发送该数据。我们可以通过以下两种方式来避免这种不必要的资源浪费:
AK::IAkPluginContextBase::CanPostMonitorData()
函数。若性能分析处于非活跃状态或不支持发送监控数据,则该函数将会返回 false。在调用 AK::IAkPluginContextBase::PostMonitorData() 之前,一定要使用上述函数来检查是否支持发送监控数据。AK_OPTIMIZED
进行预处理器检查,来从编译中移除用来准备和发送监控数据的代码段。对于 Release 版本,会在使用 Wwise Plug-in 开发工具 wp.py
时默认定义 AK_OPTIMIZED
。以下代码示例展示了如何序列化并发送监控数据:
对象处理器由音频插件衍生而来,其可在 Execute
函数中以显式方式处理一组不同的音频信号(各自对应有单独的对象)。Execute
会接收拥有一系列 AkAudioBuffer
实例的 Ak3DAudioObjects
封装器对象(每个对象对应一个实例),而非单个 AkAudioBuffer
实例。
因为一个对象处理器插件就要处理一组音频缓冲区,所以此插件发送的监控数据必须囊括与其处理的各个对象相关的所有监控数据。最简单的方式是发送对象的数量,并将与各个信号相关的监控数据序列化为数组。
有关对象处理器的详细信息,请参阅 创建声音引擎对象处理器插件 章节。