目录

Wwise SDK 2018.1.11
如何创建 Wwise 声音引擎源插件

源插件接口实现

源插件用合成方的方式产生音频内容,并提供给输出缓冲区。这些合成方法包括物理建模、调制合成、采样合成等。可以按照本文档所述的方法实现 AK::IAkSourcePlugin 接口,来编写源插件。这里仅介绍 AK::IAkSourcePlugin 接口的特有函数。请参阅 如何创建 Wwise 声音引擎插件 了解与其他插件共用的接口组件的相关信息。请参阅随附的 Sine(正弦波)插件示例了解详情(示例 )。

AK::IAkSourcePlugin::Init()

此方法让源插件为数据处理做好准备,分配内存和设置初始条件。

此插件通过指针传递给内存分配器接口(AK::IAkPluginMemAlloc)。您应该通过此接口来执行所有动态内存分配,并使用随附的内存分配宏(请参阅 在音频插件中分配/取消分配内存 )。对于最常见的内存分配需求,即在初始化时分配内存和在终止时释放内存的情况下,插件将不需要保留指向分配器的指针。在终止时也会将此指针提供给插件。

AK::IAkSourcePluginContext 接口可以用于获取循环迭代次数等信息,或者有关源插件运行环境的其他信息。该接口还可以通过 AK::IAkPluginContextBase::GlobalContext() 访问全局上下文。

插件还接收指向其相关参数节点接口(AK::IAkPluginParam)的指针。大多数插件希望保留对相关参数节点的引用,以便能够在运行时获取参数。请参阅 参数节点与插件之间的通信。 了解更多详情。

所有这些接口将在插件的生命周期内有效,因此必要时在内部引用它们是安全的。

插件运行平台的原生音频格式作为 AK::IAkSourcePlugin::Init() 函数的参数传递。强烈建议插件以平台原生格式输出,以避免音频格式转换造成性能损失。如果源插件不适合以此格式输出,那么也可以输出 16 位带符号采样的交错声道信号。在多声道配置中,源插件还必须指定将输出交错还是非交错数据( AkAudioFormat::uInterleaveID)。默认的原生设置是 AK_NONINTERLEAVED。当输出非交错数据时,源插件应使用 AkAudioBuffer::GetChannel() 方法来访问每个声道的缓冲区。当输出交错数据时,应使用 AkAudioBuffer::GetInterleavedData()。在7.1的情况下,源插件交错数据的声道顺序是 L-R-C-LFE-BL-BR-SL-SR。请参阅 访问使用 AkAudioBuffer 结构的数据 了解更多详情。

输入的声道掩码将默认使用单(仅中置扬声器)声道配置。您可以将此声道掩码更改为希望源插件输出的声道配置。以下代码示例中,声道掩码将改为立体声。在每次执行时收到的音频缓冲区将适配至初始化时插件指定的格式,并且在插件的整个生命周期内保持不变。因此对于稍后在初始化例程中进行处理所需的格式信息,也可以在缓冲区中安全存储。

AKRESULT CAkDCOffset::Init( AK::IAkPluginMemAlloc * in_pAllocator, // 内存分配接口。
AK::IAkSourcePluginContext * in_pSourcePluginContext, // 源插件上下文
AK::IAkPluginParam * in_pParams, // 效果器参数。
AkAudioFormat & io_rFormat // 支持的音频输出格式。
)
{
// 保留指向相关参数节点的指针。
m_pParams = reinterpret_cast<CAkMyPluginParams*>( in_pParams );
// 进行辅助设置来处理循环,并可能更改合成持续时间
m_DurationHandler.Setup( m_pParams->fDuration, in_pSourceFXContext->GetNumLoops(), io_rFormat.uSampleRate );
// 在默认情况下,输出格式设置为单声道(输入)原生格式。更改为立体声输出。
...
return AK_Success;
}
Note: 每次效果器实例化时,都会调用 AK::IAkSourcePlugin::Init(),当声部开始播放或混音总线实例化时,就会实例化效果器。典型情况下,其他声音已经在播放,因此实例化需要在合理的时间段内进行。如果您需要初始化通用/全局数据结构,那么在注册插件库时就应该这样做。请参阅 在插件中使用全局声音引擎回调 了解更多详情。

AK::IAkSourcePlugin::GetDuration()

在初始化阶段之后,声音引擎调用此方法来确定声源的大致持续时间,以便在声音过渡期间处理交叉淡变。返回声源的预计持续时间,单位:毫秒。如果循环的迭代次数有限,则返回的持续时间将与总体持续时间相符,即播放声音的迭代次数将被考虑在内。当选择了无限循环或者声源的持续时间未知时,返回的持续时间应为零。注意,当选择了无限循环时,通过 AK::IAkSourcePluginContext::GetNumLoops() 获得的循环迭代次数总是为零。

// 获取声源的持续时间,单位:毫秒。
AkTimeMs CAkDCOffset::GetDuration( ) const
{
return m_DurationHandler.GetDuration() * 1000.f;
}
Note: 如果使用了 RTPC 参数对插件的持续时间进行更改,交叉渐变的过渡时间可能不符合预期长度。
Note: 对于源插件,处理大多数耗时管理(包括循环)的最简单方法是使用 AkFXDurationHandler 服务,如正弦插件示例中所示(示例 )。

AK::IAkSourcePlugin::StopLooping()

声音引擎在收到中断动作时将调用此方法。中断动作是一个流畅的停止操作。 插件可在此函数中实现一种流畅终止声音播放的方法。通常它仅停止循环,如果有释音将会播放释音。 如果此函数忽略或者处理了中断命令,须返回 AK_Success。 如果插件不能处理命令,则函数应返回 AK_Fail,这会让声源停止播放。

在当前循环迭代后停止播放 AKRESULT CAkDCOffset::StopLooping() { m_DurationHandler.SetLooping( 1 ); // 不再循环。 return AK_Success; }

Note: 此函数是可选的。如果不实现,声源将忽略中断命令并一直正常播放至结束。

AK::IAkSourcePlugin::GetEnvelope()

调用此方法可以获取预估的振幅包络值(标准化值,位于 0 和 1 之间),该值将于源插件下次调用 Execute() 时生成。在 Wwise 的当前版本中,HDR 处理算法使用该方法来获取包络线的预估值,从而对比其更轻的声音进行相应衰减。此功能是可选的:在 HDR 总线下使用您的插件时,如果返回 1(默认值),则更轻声音的衰减值将保持恒定。

如果选择实现,请确保其返回标准化包络值,即您所知道的即将生成波形的峰值,或者对振幅包络和增益解耦,那么仅返回包络线的当前值即可。

AK::IAkSourcePlugin::Execute()

此方法执行源插件音频信号处理算法,并填充指定的音频输出缓冲区(访问使用 AkAudioBuffer 结构的数据 )。进入函数时,输出缓冲区的 AkAudioBuffer::uValidFrames 字段将为零,即声道缓冲区中不含有效的音频采样帧。AkAudioBuffer::MaxFrames() 方法返回声源应填充的最大音频采样帧数。插件需确定要产生多少个输出采样帧,这可能取决于持续时间等参数值。在执行 DSP 后,源插件须通过设置音频缓冲区的 AkAudioBuffer::uValidFrames 字段,告诉音频管线已经生成了多少有效采样帧。

Note: 一般而言,算法应尽量完整填充缓冲区,以避免管线资源不足。

只要源插件将 AkAudioBuffer 结构的 eState 字段设置为 AK_DataReady,就会调用 Execute() 例程。返回 AK_NoMoreData 时,音频管线将终止且不再调用插件。 Wwise 的当前版本支持多达 6 声道输入的源插件(有关 5.1 设置,请参阅 声道顺序 )。

源插件执行的重要注意事项

  • 源插件须保持生成完整的采样帧,即生成指定输出格式中每个声道的采样。
  • 确保插件在运行时检查 RTPC 所需参数。应该在共享参数接口中实现存取方法,从参数节点获取值,并在运行时按照所需的频率调用它们。更多信息请参阅 参数节点与插件之间的通信。 。对于大多数参数值,每帧检查一次就够了。请查看代码示例来了解如何使用参数值渐变(例如渐增)来避免信号不连续(示例 )。
  • 在 Wwise 中使用插件时,无论参数是否与 RTPC 关联,参数变化都会发送至参数节点。这样必要时,插件就可以在运行时更改非 RTPC 值,从而方便 Wwise 使用。如果不希望插件支持此功能,应在初始化时复制这些参数值,确保它们在插件的存续期间保持不变。请参阅 CAkToneGen::Init 来查看相关示例。
  • 您应对 AK::IAkSourcePlugin::Execute() 方法中执行的处理加以优化,以便在缓冲区时间内能够完成,不然可能导致此声源和其他声源管线出现 Source starvation,还可能对声音引擎性能产生巨大影响。

DC offset (DC偏置)插件的 Execute() 函数如下所示。请参阅 CAkSrcSine::Execute() 和各种乐音发生器 DSP 例程了解更多详情。

// 此示例中是一个简单应用,通过使用 RTPC 参数来输出稳定 DC 偏置信号。
void CAkDCOffset::Execute( AkAudioBuffer * io_pBuffer )
{
// 在更改(如 RTPC 参数) 时设置新的持续时间;
m_DurationHandler.SetDuration( m_pParams->fDuration );
// 确定执行期间需要多少采样帧,并根据当前状态设置 uValidFrames 和 eState
m_DurationHandler.ProduceBuffer( io_pBuffer );
// 获取 RTPC DC 偏置参数
AkReal32 fDCOffset = m_pParams->GetDCOffset( );
// DC 偏置输出 DSP(支持任意数量的声道)
for ( unsigned int i = 0; i < m_uNumChannels; ++i )
{
AkSampleType * pBufOut = io_pBuffer->GetChannel(i); // AkSampleType 取决于平台(在软件平台上使用 AkReal32)。
AkUInt32 uFrameCount = io_pBuffer->uValidFrames;
while ( uFrameCount-- )
{
*pBufOut++ = AK_FLOAT_TO_SAMPLETYPE( fDCOffset ); // DC-offset 输出,会将标准浮点数转化为支持的格式。
}
}
}

AK::IAkSourcePlugin::TimeSkip()

当虚声部设置为“Play from elapsed time”时,将使用 AK::IAkSourcePlugin::TimeSkip() 替代 Execute(),来让源插件在必要时更新其内部状态(例如高级合成时间)。它可以用来模拟将要进行的处理,同时避免在执行插件时占用大量 CPU。根据要求的帧数,通过 io_uFrames 参数来调整调用 Execute() 将要产生的帧数,并根据此时是否存在音频输出来返回 AK_DataReady 或 AK_NoMoreData。

返回 AK_NotImplemented 将导致声部正常处理(就像不是虚声部一样),因此“Play from elapsed time”将不能起到节省 CPU 的作用。

// 此示例显示当虚声部设置为“Play from elapsed time”时应如何跳过某些帧的处理。
AKRESULT CAkDCOffset::TimeSkip( AkUInt32 &io_uFrames )
{
AkUInt16 uValidFrames = (AkUInt16)io_uFrames;
AkUInt16 uMaxFrames = (AkUInt16)io_uFrames;
AKRESULT eResult = m_DurationHandler.ProduceBuffer( uMaxFrames, uValidFrames );
io_uFrames = uValidFrames;
return eResult;
}
\

更多信息请参阅以下部分: Wwise 声音引擎插件概述效果器插件接口实现编写音频插件的 Wwise 设计工具部件