目次

Wwise SDK 2018.1.11
Wwise サウンドエンジン エフェクトプラグインの作成方法

エフェクトプラグイン インタフェースの実装

エフェクトプラグインは、入力オーディオデータとして供給された既存サウンドにDSPアルゴリズムを適用します。エフェクトプラグインの記述は、AK::IAkInPlaceEffectPlugin または AK::IAkOutOfPlaceEffectPlugin インターフェースのいずれかを実装することより構成されています。ここでは、これらのインターフェースに固有の関数のみがカバーされています。他のプラグインタイプと共有されるインターフェースコンポーネント(AK::IAkPlugin インターフェース)に関する情報については、Wwise サウンドエンジン プラグインの作り方 を参照してください。詳細は、提供のAkDelay プラグインを参照してください(samplecode)。

AK::IAkEffectPlugin::Init()

このメソッドは、データ処理のためにエフェクトプラグインを準備し、メモリを割り当て、初期条件を設定します。

プラグインは、メモリアロケータインターフェース(AK::IAkPluginMemAlloc)へのポインタで渡されます。提供されているメモリ割り当てマクロを使用して、全ての動的メモリ割り当てをこのインターフェースを介して行う必要があります(Allocating/De-allocating オーディオプラグインにおけるメモリ 参照)。最も一般的なメモリ割り当てのニーズ、つまり初期化時の割り当ておよび終了時の解放では、プラグインがアロケータへのポインタを保持する必要がありません。終了時にもこのポインタがプラグインに提供されるためです。

AK::IAkEffectPluginContext インターフェースは、バイパス状態や、その他のエフェクトプラグインが操作されるコンテキストに関する情報の取得を可能にします。これはまた、AK::IAkPluginContextBase::GlobalContext()を介してグローバルコンテキストにアクセスできます。

また、プラグインは、関連するパラメータノードインターフェースへのポインタを受け取ります( AK::IAkPluginParam )。ほとんどのプラグインは、関連するパラメータノードへの参照を保持して、実行時にパラメータを取得することになるでしょう。詳細は、Communication Between Parameter Nodes and Plug-ins. を参照してください。

これらのインターフェースは全てプラグインのライフスパンを通じて有効なので、必要に応じてこれらへの内部参照を保持しておくのが安全です。

エフェクトプラグインはまた、所定のチャンネル構成に対してメモリとセットアップ処理を割り当てられるよう(プラグインの寿命を通じて変わらない)入力/出力オーディオ形式を受け取ります。

Note: AK::IAkEffectPlugin::Init() は、エフェクトがインスタンスを生成するたびに呼び出されます。これはボイスの再生開始時、またはミキシングバスがインスタンス化された場合に起きます。他のサウンドが既に再生されていることが常なので、これは合理的な時間の間に発生する必要があります。大きな共通/グローバルなデータ構造を初期化する必要がある場合には、プラグインライブラリを登録している時に行うのがよいでしょう。詳細は、プラグインからグローバル サウンド エンジン コールバックを使用する を参照してください。

AK::IAkPluginEffect::Execute()

エフェクトプラグインは、次の2つのインターフェースのいずれかを実装することもできます:AK::IAkInPlaceEffectPlugin または AK::IAkOutOfPlaceEffectPlugin 。一般的には、(入力および出力データの両方に対して同じオーディオバッファを使用する)インプレース効果を、ほとんどのエフェクトに対して使用する必要があります。ただし、データフローの変化(たとえば、タイムストレッチ効果など)がある場合には、アウトオブプレースインターフェースを代わりに実装する必要があります。

Caution: 異なる入力/出力チャンネル構成のアウトオブプレース効果を、マスターミキサー階層に挿入することができます。しかし、ミキシングバスにレートが変化するエフェクトをかけることはできません。異なる入力/出力バッファ長のエフェクトは、アクターミキサー階層にのみ挿入することができます (ソースエフェクトとして)。

IAkInPlaceEffectPlugin::Execute

このメソッドは、プラグインの信号処理アルゴリズムを指定されたオーディオバッファに対してインプレースで実行します(詳細は、AkAudioBuffer構造体を使用するデータにアクセスする を参照してください)。この構造体は、入力サンプルがいくつ有効かに関する情報(AkAudioBuffer::uValidFrames)およびバッファが収容可能な最大オーディオサンプルフレーム数に関する情報(AkAudioBuffer::MaxFrames() メソッド)をプラグインに提供します。AkAudioBuffer::eState 構造体メンバは、最後の実行であるか(AK_NoMoreData)否か(AK_DataReady)をプラグインに通知します。

AK::IAkInPlaceEffectPlugin::TimeSkip() は、バーチャルボイスが経過時間から再生された時にプラグインが必要に応じてその内部状態を更新し続けることができるように、Execute() に置換されます。

IAkOutOfPlaceEffectPlugin::Execute

このメソッドは、アウトオブプレース アルゴリズム用のプラグインの信号処理を実行します。AkAudioBuffer 構造体は、入力バッファ用に1つ、出力バッファ用に1つ、全部で2つ使用されます。パイプラインは、現在のエフェクトの状態を判断するために出力オーディオバッファで eState を使用します。エフェクトは、入力バッファを全て消費した場合(そして後により多くのデータで実行を続けて行くために AK_DataNeeded を返す場合)、または全出力バッファが充填された時に AK_DataReady を出力した場合にのみに返されます。エフェクトが内部状態のフラッシュを完了するまで(AK_NoMoreData が返される必要のある時点まで)、AK_DataReady により受信される AK_NoMoreData を変更することにより、エフェクトテールをアウトオブプレースエフェクトに実装することも可能です。

入力バッファは、完全に消費されるまでパイプラインにより解放されることはありません。従って、最後の Execute() コールが中断されたところでデータの読み取りを開始するために in_uInOffset オフセットパラメータを使用することが重要です。以下のサンプルはこれを実現する方法を示しています。

void CAkSimpleUpsampler::Execute(
AkAudioBuffer * io_pInBuffer,
AkUInt32 in_uInOffset,
AkAudioBuffer * io_pOutBuffer )
{
assert( io_pInBuffer->NumChannels() == io_pOutBuffer->NumChannels() );
const AkUInt32 uNumChannels = io_pInBuffer->NumChannels();
AkUInt32 uFramesConsumed; // Track how much data is consumed from input buffer
AkUInt32 uFramesProduced; // Track how much data is produced to output buffer
for ( AkUInt32 i = 0; i < uNumChannels; i++ )
{
AkReal32 * AK_RESTRICT pInBuf = (AkReal32 * AK_RESTRICT) io_pInBuffer->GetChannel( i ) + in_uInOffset;
AkReal32 * AK_RESTRICT pfOutBuf = (AkReal32 * AK_RESTRICT) io_pOutBuffer->GetChannel( i ) + io_pOutBuffer->uValidFrames;
uFramesConsumed = 0; // Reset for every channel
uFramesProduced = 0;
while ( (uFramesConsumed < io_pInBuffer->uValidFrames) && (uFramesProduced < io_pOutBuffer->MaxFrames()) )
{
// Do some processing that consumes input and produces output at different rate (e.g. time-stretch or resampling)
*pfOutBuf++ = *pInBuf;
*pfOutBuf++ = *pInBuf++;
uFramesConsumed++;
uFramesProduced += 2;
}
}
// Update AkAudioBuffer structure to continue processing
io_pInBuffer->uValidFrames -= uFramesConsumed;
io_pOutBuffer->uValidFrames += uFramesProduced;
if ( io_pInBuffer->eState == AK_NoMoreData && io_pInBuffer->uValidFrames == 0 )
io_pOutBuffer->eState = AK_NoMoreData; // Input entirely consumed and nothing more to output, the effect is done
else if ( io_pOutBuffer->uValidFrames == io_pOutBuffer->MaxFrames() )
io_pOutBuffer->eState = AK_DataReady; // A complete audio buffer is ready
else
io_pOutBuffer->eState = AK_DataNeeded; // We need more data to continue processing
}

AK::IAkOutOfPlaceEffectPlugin::TimeSkip() は、バーチャルボイスが経過時間から再生された時にプラグインが必要に応じてその内部状態を更新し続けることができるように、Execute() に置換されます。従って、この関数は、指定数の出力フレームを生成するのに通常どのぐらいの入力サンプルが消費されるかをパイプラインに知らせる役割を担います。

Implementing エフェクトプラグイン テール

エフェクトの一部(特にディレイラインを持つエフェクト)は、入力が正常に減衰の再生を終了した後に出力される必要のある内部状態を持っています。エフェクトAPI は、有効な入力データがなくても実行を継続することを可能にします。AkAudioBuffer 構造体の eState フラグが AK_NoMoreData になると、パイプラインは、現在の実行後に有効な入力サンプルフレームをプラグインに供給しなくなります。その後、プラグインは、入力信号が終了した後にディレイラインを空にできるよう、新しい(後続の)フレームをバッファ内に(MaxFrames() が返す値まで)自由に書き込むことができるようになります。オーディオパイプラインには、適切に uValidFrames フィールドを更新することによって出力されたフレーム数を常に知らせる必要があります。エフェクトテールのフラッシュを完了するためにプラグイン Execute() 関数が再び呼び出される必要がある場合には、eState メンバが AK_DataReady に設定されなければなりません。エフェクトが、eState フィールドに AK_NoMoreData を設定した場合には、パイプラインはプラグイン Execute() の呼び出しのみを停止します。

テールを処理する最も簡単な方法は、SDK で提供の AkFXTailHandler サービスクラスを使用することです。プラグイン用のクラスメンバとして保持されている AkFXTailHandler のインスタンスで、インプレースエフェクト内でする必要のあることは、AkFXTailHandler::HandleTail() を呼び出して、これに AkAudioBuffer といったん出力が終了した時に出力するオーディオサンプル総数(パラメータに基づいて実行ごとに変更される場合あり)を渡すことのみです。詳細は、AkDelay プラグインソースコードを参照してください (samplecode)。

エフェクトプラグイン実行のための重要な注意事項

  • Wwiseでプラグインが使用される場合、パラメータが RTPC をサポートするかどうかに関わらず、パラメータの変更はパラメータノードまで送信されます。これにより、Wwise 使用の必要に応じて、プラグインは非 RTPC 値の実行時の値の変更をサポートすることができます。プラグインがこれをサポートしないようにするには、初期化時にパラメータ値のコピーを作成し、プラグインの存続期間を通じてこれらの値が変化しないようにする必要があります。
  • プラグインはいくつかのチャンネル構成を処理する必要があり(バス上に挿入出来る場合は、少なくともモノラル、ステレオ、5.1)、そうでない場合は初期化時に AK_UnsupportedChannelConfig を返さなければなりません。

バイパス

プラグインは、UI、イベント、RTPC など様々なメカニズムを通じて Wwise やゲーム内でバイパスすることができます。このような場合、プラグイン Execute() ルーチンは呼び出されません。プラグインが unbypass 上で再開し実行関数が再び呼び出されると、プラグインはその処理を再始動します。エフェクトが最終的にバイパスされない場合、新たなスタートのためにディレイラインや他の状態情報をクリアすることができるように、プラグインの Reset() 関数がバイパス上に呼び出されます。詳細は、AK::IAkPlugin::Reset() を参照してください。

Caution: 実行時のバイパスやバイパス解除は、プラグインや処理されているサウンドマテリアルによっては、信号の不連続を引き起こす場合があります。
AKRESULT CAkGain::Init( AK::IAkPluginMemAlloc * in_pAllocator, // Memory allocator interface.
AK::IAkEffectPluginContext * in_pFXCtx, // FX Context.
AK::IAkPluginParam * in_pParams, // Effect parameters.
AkAudioFormat & in_rFormat // Required audio input format.
)
{
if ( in_pFXCtx->IsSendModeEffect() )
// Effect used in environmental (sent) context ...
else
// Effect inserted in DSP chain ...
}

詳細については、以下のセクションを参照してください:

Wwise プラグイン モニタリングデータのポスト

モニタリングのために、プラグインが Wwise プラグインに情報を送信することがあります。よくある例は、オーディオ信号に関連する情報(たとえばVUメーターなど)、消費メモリなど実行時情報の送信です。

ポストするデータがプロファイリングシステムを介して非同期的に送信される前に、まずエフェクトは AK::IAkEffectPluginContext::CanPostMonitorData() へのコールを使用してこのエフェクトプラグインインスタンスのUIカウンターパートにデータを送信できるかどうかを判断する必要があります。 これを行うためには、エフェクトの初期化時にプラグインに渡されたプラグインの実行コンテキストへのポインタをキャッシュする必要があります。 プラグインのインスタンスがバス上にあるときは、そのエフェクト設定ビューと一対一の関係があるので、データのポストは、この場合にのみ可能であることに注意してください。

データの送信が可能で、ビルドターゲットが Wwise と通信可能なものである場合(つまり、リリースターゲットでない場合)、AK::IAkEffectPluginContext::PostMonitorData() へのコールを使用してお好みでオーガナイズされた任意サイズのデータブロックをポストすることができます。モニタリングのためにいったんデータがポストされたら、プラグインからデータブロックを破棄することができます。

void MyPlugin::Execute( AkAudioBuffer * io_pBuffer )
{
// Algorithm tracks signal peaks for all m_uNumChannels channels inside the following array
float fChannelPeaks[MAX_NUM_CHANNELS];
...
#ifndef AK_OPTIMIZED
if ( m_pCtx->CanPostMonitorData() )
{
unsigned int uMonitorDataSize = sizeof( unsigned int ) * m_uNumChannels*sizeof(float);
char * pMonitorData = (char *) AkAlloca( uMonitorDataSize );
*((unsigned int *) pMonitorData ) = m_uNumChannels;
memcpy( pMonitorData+sizeof(unsigned int), fChannelPeaks, m_uNumChannels*sizeof(float) );
m_pCtx->PostMonitorData( pMonitorData, uMonitorDataSize );
// pMonitorData can now be released
}
#endif
...
}