目次

Speaker Matrix Callbackを使用した高度なミックスカスタマイズ

ゲームの中には、Wwiseですぐに利用できない非常に特定のルーティングやパニングがあるものがあります。この限界を回避する一つの方法は、"speaker matrix callback" (スピーカー マトリックス コールバック) への登録です。このコールバックは、ボイスやバスが他のバスにミックスされる際に呼び出します。このコールバックから、ボイスやバスのグローバルならびにチャンネルに特化したレベルを変更できるので、ミックスやパニングの修正ができます。

以下の例では、ボイスのコールバックを登録する方法を示しています。これはこのボイスを再生するイベントをポストした際に発生します。

AK::SoundEngine::PostEvent( AK::EVENTS::PLAY_HELLO, GAME_OBJECT_HUMAN, AK_SpeakerVolumeMatrix, VoiceCallback );

VoiceCallback コールバックは、そのように出力バスの特定、ボイスを出力バスにミックスする際のベースボリュームの変更、そして異なる発生角度をシミュレートするためのパニングボリュームの変更ができるかを示しています。AkSpeakerVolumeMatrixCallbackInfopContext は、ボイスに関連する情報を、そして AkSpeakerVolumeMatrixCallbackInfo::pMixerContext はそのボイスがミックスされるバスに関連する情報を示します (ドライ出力バスまたはAUXセンド)。

static void VoiceCallback( 
    AkCallbackType in_eType,                            // Callback type.
    AkCallbackInfo* in_pCallbackInfo                    // Structure containing desired information. You can cast it to the proper sub-type, depending on the callback type.
    )
{
    // We know this is a speaker volume matrix callback. Cast to proper subtype.
    AKASSERT( in_eType == AK_SpeakerVolumeMatrix );
    AkSpeakerVolumeMatrixCallbackInfo* pVolumeCallbackInfo = (AkSpeakerVolumeMatrixCallbackInfo*)in_pCallbackInfo;

    // Event, game object and playing ID correspond to the IDs passed to / returned from PostEvent().
    AkUniqueID eventID = pVolumeCallbackInfo->eventID;
    AkGameObjectID objID = pVolumeCallbackInfo->gameObjID;
    AkPlayingID playingID = pVolumeCallbackInfo->playingID;
    
    AKASSERT( eventID == AK::EVENTS::PLAY_HELLO );
    
    // This callback gets called for each bus into which the voice is routed. In this example, we want to customize mix into the "My_Dry_Bus" bus only.
    if ( pVolumeCallbackInfo->pMixerContext->GetBusID() != AK::SoundEngine::GetIDFromString( "My_Dry_Bus" ) )
        return;

        
    // Apply arbitrary changes to "base" volumes (i.e. common to all channels).
    // ---------------------------------------------------------------------------------
    
    // Double the volume (+6dB) of the voice into "My_Dry_Bus".
    *pVolumeCallbackInfo->pfBaseVolume *= 2.f;
    
    // Undo distance attenuation of the voice into "My_Dry_Bus" (in the single position case).
    *pVolumeCallbackInfo->pfEmitterListenerVolume = 1.f;
    
    
    // Change panning of the voice into "My_Dry_Bus". Say the sound uses 3D positioning.
    // ---------------------------------------------------------------------------------
    AkUInt32 uNumPosition = pVolumeCallbackInfo->pContext->GetNum3DPositions();

    // A 3D sound may have multiple positions (see AK::SoundEngine::SetMultiplePositions()). この例では、最初のみを考慮します。
    if ( uNumPosition > 0 )
    {
        // Query the listener position. 通常、ボイスをミックスする先のバスは、リスナーに紐づいています。このため、ゲームオブジェクトをミキサーに関連づけてください。
        AkTransform posListener;
        AK::IAkGameObjectPluginInfo * pBusObject = pVolumeCallbackInfo->pMixerContext->GetGameObjectInfo();
        if (pBusObject)
        {
            pBusObject->GetGameObjectPosition(0, posListener);

            // リスナーを上下逆にして、遊んでみます。
            AkVector listenerTop = posListener.OrientationTop();
            listenerTop.X = -listenerTop.X;
            listenerTop.Y = -listenerTop.Y;
            listenerTop.Z = -listenerTop.Z;
            posListener.SetOrientation(posListener.OrientationFront(), listenerTop);

            // Get the emitter game object position.
            AkTransform posEmitter;
            pVolumeCallbackInfo->pContext->GetVoiceInfo()->GetGameObjectPosition(0, posEmitter);

            // Use mixing services of the bus/mixer to compute new panning volumes.
            pVolumeCallbackInfo->pMixerContext->Compute3DPositioning(
                posEmitter,                                     // Emitter transform.
                posListener,                                    // Listener transform.
                pVolumeCallbackInfo->pContext->GetCenterPerc(), // Center percentage. モノ入力から、センターのある出力に入力した場合に限定して、適用できます。
                pVolumeCallbackInfo->pContext->GetSpread(0),    // Spread.
                pVolumeCallbackInfo->pContext->GetFocus(0),     // Focus.
                pVolumeCallbackInfo->inputConfig,               // Channel configuration of the input.
                pVolumeCallbackInfo->inputConfig.uChannelMask,  // Mask of input channels selected for panning (excluded input channels don't contribute to the output).
                pVolumeCallbackInfo->outputConfig,              // Desired output configuration.
                pVolumeCallbackInfo->pVolumes                   // Returned volumes matrix. AK::SpeakerVolumes::Matrix::GetRequiredSize()を使って、事前にアロケーションします(AK::SpeakerVolumes::Matrixサービスを参照)
                );
        }
    }
}

次の例は、バスコールバックにどの様に登録するかを示しています。この場合では、AkSpeakerVolumeMatrixCallbackInfopContext が登録をしたバス (「My_Bus」) に関連する情報を、AkSpeakerVolumeMatrixCallbackInfopMixerContext が、ミックスされたその親バス (またはシグナルチェーンの次のミキシングバス) に関連する情報を示します。

static void BusCallback( 
    AkSpeakerVolumeMatrixCallbackInfo* in_pCallbackInfo // Structure containing desired bus information.
    )
{
    // Called from a bus: cannot be associated to a playing ID.
    AKASSERT( in_pCallbackInfo->playingID == AK_INVALID_PLAYING_ID );
    
    // Change panning volumes of the mixdown signal at the output of this bus before it gets mixed into its parent;
    // Using mixing services, transmix input into a 3-stereo configuration, and silence out contribution to all other channels of the parent bus.

    // Allocate a volume matrix to obtain transmix gains (input_config -> 3-stereo).
    AkChannelConfig cfgThreeStereo;
    cfgThreeStereo.SetStandard( AK_SPEAKER_SETUP_3STEREO );
    AkUInt32 uSize = AK::SpeakerVolumes::Matrix::GetRequiredSize( in_pCallbackInfo->inputConfig.uNumChannels, cfgThreeStereo.uNumChannels );
    AK::SpeakerVolumes::MatrixPtr mxTransmix = (AK::SpeakerVolumes::MatrixPtr)AkAlloca( uSize );

    in_pCallbackInfo->pMixerContext->ComputeSpeakerVolumesDirect(
        in_pCallbackInfo->inputConfig,  // Channel configuration of the input.
        cfgThreeStereo,                 // Channel configuration of the mixer output.
        in_pCallbackInfo->pContext->GetCenterPerc(),// Center%: Use center% value computed by Wwise.
        mxTransmix );

    // Copy result into real mixing matrix and clear out the back channels and LFE (if applicable).
    // Warning: we cannot assume that the mixing bus has a center channel, so we need to verify it.
    AkChannelConfig cfgThreeStereoANDBus;
    cfgThreeStereoANDBus.SetStandard( AK_SPEAKER_SETUP_3STEREO & in_pCallbackInfo->outputConfig.uChannelMask );
    AkUInt32 uNumOutputChannelsToCopy = cfgThreeStereoANDBus.uNumChannels;

    for ( AkUInt32 uChanIn = 0; uChanIn < in_pCallbackInfo->inputConfig.uNumChannels; uChanIn++ )
    {
        // Get output volume vector for each input channel.
        AK::SpeakerVolumes::VectorPtr vTransmixOut = AK::SpeakerVolumes::Matrix::GetChannel( mxTransmix, uChanIn, cfgThreeStereo.uNumChannels );
        AK::SpeakerVolumes::VectorPtr vMixOut = AK::SpeakerVolumes::Matrix::GetChannel( in_pCallbackInfo->pVolumes, uChanIn, in_pCallbackInfo->outputConfig.uNumChannels );

        // Copy panning to front channels.
        AkUInt32 uChanOut = 0;
        while ( uChanOut < uNumOutputChannelsToCopy )
        {
            vMixOut[uChanOut] = vTransmixOut[uChanOut];
            ++uChanOut;
        }

        // Clear out other channels.
        while ( uChanOut < in_pCallbackInfo->outputConfig.uNumChannels )
        {
            vMixOut[uChanOut] = 0;
            ++uChanOut;
        }
    }
}

次の例は、そのメータリングデータを問い合わせるための、バスへの登録方法を示しています。

// Register to bus metering. Note: there is a significant cost to registering to metering, especially True Peak and K-Weighted Power.
AK::SoundEngine::RegisterBusMeteringCallback( AK::SoundEngine::GetIDFromString( "My_Bus" ), MeterCallback, (AkMeteringFlags)( AK_EnableBusMeter_TruePeak | AK_EnableBusMeter_KPower ) );
static void MeterCallback(
    AK::IAkMetering * in_pMetering,                     // AK::IAkMetering interface for retrieving metering information.
    AkChannelConfig in_channelConfig,                   // Channel configuration of the bus.
    AkMeteringFlags in_eMeteringFlags                   // Metering flags that were asked for in RegisterBusMeteringCallback(). You may only access corresponding meter values from in_pMeteringInfo. Others will fail.
    )
{
    // At least true peak and K-weighted power metering must be enabled since we registered for this callback.
    AKASSERT( ( in_eMeteringFlags & AK_EnableBusMeter_TruePeak ) && ( in_eMeteringFlags & AK_EnableBusMeter_KPower ) );
    
    // Get K-weighted power and do something with it.
    AkReal32 fPower = in_pMetering->GetKWeightedPower();

    // Get true peak and do something with it.
    // There is one value per channel.
    AK::SpeakerVolumes::ConstVectorPtr vTruePeak = in_pMetering->GetTruePeak();
    for ( AkUInt32 uChannel = 0; uChannel < in_channelConfig.uNumChannels; uChannel++ )
    {
        AkReal32 fChannelPeak = vTruePeak[uChannel];

        ...
    }

    ...
}