> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# リアルタイム オーディオ操作でカスタム ボイス エフェクトを適用する

> PlayFab Party のリアルタイム オーディオ操作ストリームを使用してボイス チャット バッファーをインターセプトし、空間オーディオやボイス フィルターなどのカスタム エフェクトを適用します。

PlayFab Party はリアルタイム ネットワーキングおよびボイス チャット ソリューションです。ボイス チャット用に構成されると、PlayFab Party はマイク オーディオを送信し、変更せずに再生します。一部のゲームでは、空間オーディオやボイス フィルターなどのカスタム オーディオ エフェクトを実装するために、ボイス チャットのオーディオ バッファーにアクセスする必要があります。このドキュメントでは、PlayFab Party のリアルタイム オーディオ操作機能を使用して、ボイス チャット オーディオをインターセプトして変更する方法を説明します。

## 前提条件

このウォークスルーでは、[PlayFab Party のボイス チャット](/services/playfab/community/voice-communications/concepts-chat) に関する基本的な理解を前提としています。

## プラットフォームのサポート

リアルタイム オーディオ操作はすべてのプラットフォームで利用できるわけではありません。リアルタイム オーディオ操作に関連するメソッドは、統一されたクロスプラットフォームのヘッダーに存在しますが、現在は Windows、XBOX、および PlayStation® 5 でのみ実装されています。他のプラットフォームではメソッドはエラーを返します。

## オーディオ ストリーム

リアルタイム オーディオ操作では、ライブラリからオーディオを取得または送信するためのオーディオ ストリームの概念が導入されます。オーディオ ストリームには 2 種類あります。1 つ目は **ソース ストリーム** です。ソース ストリームは、チャット コントロールからオーディオを取得するために使用されます。各チャット コントロールは、**ボイス ストリーム** と呼ばれるソース ストリームを 1 つだけ持つことができます。ローカル チャット コントロールの場合、これはマイク入力を取得するために使用されます。リモート チャット コントロールの場合、これは着信ボイス オーディオを取得するために使用されます。チャット コントロールにボイス ストリームが存在する場合、ライブラリは自動的にオーディオを処理する代わりに、そのチャット コントロールのソース オーディオをそのボイス ストリームにリダイレクトします。ローカル チャット コントロールの場合、これはマイク オーディオを自動的にエンコードして送信する代わりにボイス ストリームにリダイレクトすることを意味します。リモート チャット コントロールの場合、これは着信ボイス オーディオを自動的に各ローカル チャット コントロールに送信して再生する代わりに、ボイス ストリームにリダイレクトすることを意味します。ソース ストリームは [`PartyAudioManipulationSourceStream`](/services/playfab/multiplayer/networking/reference/classes/PartyAudioManipulationSourceStream/partyaudiomanipulationsourcestream) で表されます。

2 つ目の種類のストリームは **シンク ストリーム** です。シンク ストリームは、チャット コントロールにオーディオを送信するために使用されます。ローカル チャット コントロールのみがシンク ストリームを持つことができ、それぞれ 2 つのシンク ストリームを持つことができます。これらは **キャプチャ ストリーム** と **レンダー ストリーム** と呼ばれます。チャット コントロールにキャプチャ ストリームが存在する場合、ライブラリはマイクの代わりにキャプチャ ストリームからオーディオを取得して、他のチャット コントロールにエンコードして送信します。チャット コントロールにレンダー ストリームが存在する場合、ライブラリはレンダー ストリームからオーディオを取得し、リモート チャット コントロールから自動的に再生されるボイス チャット オーディオに *加えて* 再生します。キャプチャ ストリームに送信されたオーディオは、ローカル チャット コントロールのマイク入力として使用されます。レンダー ストリームに送信されたオーディオは、ローカル チャット コントロールのオーディオ出力デバイスで再生 (「レンダリング」) されます。シンク ストリームは [`PartyAudioManipulationSinkStream`](/services/playfab/multiplayer/networking/reference/classes/PartyAudioManipulationSinkStream/partyaudiomanipulationsinkstream) で表されます。

### オーディオ ストリームの構成

デフォルトでは、ライブラリはオーディオの取得、転送、再生を処理します。したがって、チャット コントロールはオーディオ ストリームなしで作成されます。ストリーム構成メソッド ([`PartyLocalChatControl::ConfigureAudioManipulationCaptureStream()`](/services/playfab/multiplayer/networking/reference/classes/PartyLocalChatControl/methods/partylocalchatcontrol_configureaudiomanipulationcapturestream)、[`PartyLocalChatControl::ConfigureAudioManipulationRenderStream()`](/services/playfab/multiplayer/networking/reference/classes/PartyLocalChatControl/methods/partylocalchatcontrol_configureaudiomanipulationrenderstream)、[`PartyChatControl::ConfigureAudioManipulationVoiceStream()`](/services/playfab/multiplayer/networking/reference/classes/PartyChatControl/methods/partychatcontrol_configureaudiomanipulationvoicestream)) を使用して、チャット コントロールに 1 つ以上のストリームを作成できます。構成されると、ストリームは [`PartyLocalChatControl::GetAudioManipulationCaptureStream()`](/services/playfab/multiplayer/networking/reference/classes/PartyLocalChatControl/methods/partylocalchatcontrol_getaudiomanipulationcapturestream)、[`PartyLocalChatControl::GetAudioManipulationRenderStream()`](/services/playfab/multiplayer/networking/reference/classes/PartyLocalChatControl/methods/partylocalchatcontrol_getaudiomanipulationrenderstream)、[`PartyChatControl::GetAudioManipulationVoiceStream()`](/services/playfab/multiplayer/networking/reference/classes/PartyChatControl/methods/partychatcontrol_getaudiomanipulationvoicestream) を介して取得できます。

各ストリーム構成メソッドを使用すると、ストリームから取得またはストリームに送信するオーディオのフォーマットを指定できます。サポートされているフォーマットの詳細については、各ストリーム構成メソッドのリファレンス ドキュメントを参照してください。

### ソース ストリームからオーディオを取得する

[`PartyAudioManipulationSourceStream::GetNextBuffer()`](/services/playfab/multiplayer/networking/reference/classes/PartyAudioManipulationSourceStream/methods/partyaudiomanipulationsourcestream_getnextbuffer) を介してソース ストリームからオーディオを取得できます。音声アクティビティが検出されると、約 40 ms ごとに新しいバッファーが利用可能になります。利用可能なバッファーがない場合、呼び出しは成功し、長さゼロのバッファーが提供されます。瞬間的に利用可能なバッファーの総数は、[`PartyAudioManipulationSourceStream::GetAvailableBufferCount()`](/services/playfab/multiplayer/networking/reference/classes/PartyAudioManipulationSourceStream/methods/partyaudiomanipulationsourcestream_getavailablebuffercount) を介して取得できます。

効率のため、`GetNextBuffer()` はバッファー全体をコピーする代わりに、ライブラリのメモリを指すバッファーを提供します。オプションで、その場で変更できます。バッファーの処理が完了したら、[`PartyAudioManipulationSourceStream::ReturnBuffer()`](/services/playfab/multiplayer/networking/reference/classes/PartyAudioManipulationSourceStream/methods/partyaudiomanipulationsourcestream_returnbuffer) を介してバッファーを解放して、ライブラリがそのメモリを再利用できるようにする必要があります。返却する前に複数のバッファーを取得でき、バッファーは取得した順序で返却する必要はありません。

### シンク ストリームにオーディオを送信する

[`PartyAudioManipulationSinkStream::SubmitBuffer()`](/services/playfab/multiplayer/networking/reference/classes/PartyAudioManipulationSinkStream/methods/partyaudiomanipulationsinkstream_submitbuffer) を介してシンク ストリームにオーディオを送信できます。バッファーはライブラリによってコピーされ、呼び出しの完了後にすぐに解放できます。

40 ms ごとに、ライブラリはシンク ストリームに送信されたオーディオの 40 ms を消費します。オーディオの途切れを防ぐため、オーディオは一定のレートで送信する必要があります。

## シナリオ

### マイク オーディオ操作 (別名 プリエンコード バッファー操作)

マイク オーディオ操作は、マイク オーディオが他のチャット コントロールに送信される前にインターセプトして変更する操作です。マイク オーディオがエンコードされて他のチャット コントロールに送信される前に変更されるため、「プリエンコード バッファー操作」と呼ばれることもあります。このシナリオをローカル チャット コントロール用に実装したい場合、まずローカル チャット コントロール用にボイス ストリームとキャプチャ ストリームを構成します。構成された後、単一のチャット コントロールのマイク オーディオを処理するために専用のオーディオ スレッドの各ティックで呼び出される関数は、以下のようになります。

```cpp theme={null}
// An app-defined function that takes a microphone buffer and generates a new
// buffer that should be transmitted to other chat controls.
std::vector<uint8_t>
ProcessLocalVoiceBuffer(
    PartyMutableDataBuffer* inputBuffer
    );

void
ProcessLocalMicrophoneAudioForSingleChatControl(
    PartyLocalChatControl* chatControl
    )
{
    // Get the voice stream from which we want to retrieve audio. This provides
    // the audio generated by the chat control's input device.
    PartyAudioManipulationSourceStream* voiceStream;
    RETURN_VOID_IF_FAILED(chatControl->GetAudioManipulationVoiceStream(&voiceStream));

    // Get the capture stream to which we want to submit audio. This is used to
    // submit audio that will be transmitted to other chat controls.
    PartyAudioManipulationSinkStream* captureStream;
    RETURN_VOID_IF_FAILED(chatControl->GetAudioManipulationCaptureStream(&captureStream));

    // Get the next audio buffer from the voice stream.
    PartyMutableDataBuffer buffer;
    RETURN_VOID_IF_FAILED(voiceStream->GetNextBuffer(&buffer));

    // If we retrieved a buffer, process it.
    if (buffer.bufferByteCount > 0)
    {
        // Use the buffer we retrieved to generate a new buffer that will be
        // treated as the "real" capture input and transmitted to other chat
        // controls.
        std::vector<uint8_t> processedBuffer = ProcessLocalVoiceBuffer(&buffer);

        // Convert the buffer to a Party type.
        PartyDataBuffer partyBuffer;
        partyBuffer.bufferByteCount = static_cast<uint32_t>(processedBuffer.size());
        partyBuffer.buffer = processedBuffer.data();

        // Submit the processed buffer to the capture stream.
        PartyError error = captureStream->SubmitBuffer(&partyBuffer);
        if (PARTY_FAILED(error))
        {
            printf("Failed to submit buffer to sink stream! error = 0x%08x", error);
        }

        // Return the original buffer back to the voice stream.
        error = voiceStream->ReturnBuffer(buffer.buffer);
        if (PARTY_FAILED(error))
        {
            printf("Failed to return buffer to source stream! error = 0x%08x", error);
        }
    }
}
```

### リモート オーディオ操作 (別名 ポストデコード バッファー操作)

リモート オーディオ操作は、着信オーディオが各ローカル チャット コントロールにレンダリングされる前にインターセプトして変更する操作です。着信オーディオがデコードされた後、レンダリングされる前に変更されるため、「ポストデコード バッファー操作」と呼ばれることもあります。このシナリオを実装したい場合、まず各リモート チャット コントロール用にボイス ストリームを、各ローカル チャット コントロール用にレンダー ストリームを構成します。次に、オーディオ スレッドの各ティックで、各ボイス ストリームからオーディオを取得し、オプションでエフェクトを適用しながらオーディオを単一のストリームにミックスし、ミックスされたバッファーを各レンダー ストリームに送信する必要があります。ゲーム シナリオによっては、各ローカル チャット コントロール用にバッファーを異なるストリームにミックスする必要がある場合があります。着信ボイス オーディオを処理するために専用のオーディオ スレッドの各ティックで呼び出される関数は、以下のようになります。

```cpp theme={null}
// This is an app-defined function that takes a local chat control and list of remote voice buffers and generates
// a single mixed buffer to submit to the local chat control's audio output.
std::vector<uint8_t>
GetOutputMixBuffer(
    PartyLocalChatControl& localChatControl,
    const std::map<PartyAudioManipulationSourceStream*, PartyMutableDataBuffer>& remoteVoiceBuffers
    );

void
ProcessRemoteVoiceAudio(
    const std::vector<PartyChatControl*>& remoteChatControls,
    const std::vector<PartyLocalChatControl*>& localChatControls
    )
{
    std::map<PartyAudioManipulationSourceStream*, PartyMutableDataBuffer> remoteVoiceBuffers;

    // Acquire voice buffers from each remote chat control.
    for (auto remoteChatControl : remoteChatControls)
    {
        // Get the voice stream for this chat control from which we will retrieve audio.
        PartyAudioManipulationSourceStream* voiceStream;
        PartyError error = remoteChatControl->GetAudioManipulationVoiceStream(&voiceStream);
        if (PARTY_FAILED(error))
        {
            printf("Failed to get voice stream! error = 0x%08x", error);
            continue;
        }

        // Get the next audio buffer from the voice stream.
        PartyMutableDataBuffer buffer;
        error = voiceStream->GetNextBuffer(&buffer);
        if (PARTY_FAILED(error))
        {
            printf("Failed to get next buffer! error = 0x%08x", error);
            continue;
        }

        // If we retrieved a buffer, cache it in the map for mixing.
        if (buffer.bufferByteCount > 0)
        {
            remoteVoiceBuffers[voiceStream] = buffer;
        }
    }

    // If we didn't acquire any source buffers, we don't have anything to mix.
    if (remoteVoiceBuffers.empty())
    {
        return;
    }

    // Mix the voice buffers and submit to each render stream.
    for (auto localChatControl : localChatControls)
    {
        // Get the render stream for this chat control to which we will submit audio.
        PartyAudioManipulationSinkStream* renderStream;
        PartyError error = localChatControl->GetAudioManipulationRenderStream(&renderStream);
        if (PARTY_FAILED(error))
        {
            printf("Failed to get render stream! error = 0x%08x", error);
            continue;
        }

        // Mix the buffers the buffers to generate a new, mixed buffer.
        std::vector<uint8_t> mixedBuffer = GetOutputMixBuffer(*localChatControl, remoteVoiceBuffers);

        // Convert the buffer to a party type.
        PartyDataBuffer partyBuffer;
        partyBuffer.bufferByteCount = static_cast<uint32_t>(mixedBuffer.size());
        partyBuffer.buffer = mixedBuffer.data();

        // Submit the mixed buffer to the render stream.
        error = renderStream->SubmitBuffer(&partyBuffer);
        if (PARTY_FAILED(error))
        {
            printf("Failed to submit buffer to render stream! error = 0x%08x", error);
        }
    }

    // Release the voice buffers.
    for (auto voiceBuffer : remoteVoiceBuffers)
    {
        // Return the voice buffer that we had cached from this voice stream.
        PartyError error = voiceBuffer.first->ReturnBuffer(voiceBuffer.second.buffer);
        if (PARTY_FAILED(error))
        {
            printf("Failed to return buffer! error = 0x%08x", error);
        }
    }
}
```

#### プライバシーとミックスに関する考慮事項

ライブラリは、リモート チャット コントロールがオーディオを生成しており、[チャット許可](/services/playfab/community/voice-communications/concepts-chat-permissions-and-muting#chat-permissions) と [ミュート](/services/playfab/community/voice-communications/concepts-chat-permissions-and-muting#muting) の構成で少なくとも 1 つのローカル チャット コントロールでオーディオの再生が許可されている限り、リモート チャット コントロールのボイス ストリームを介してオーディオを提供します。オーディオが 1 つのローカル チャット コントロールで再生され、別のローカル チャット コントロールでは再生されるべきでない場合、後者のチャット コントロールのオーディオ ミックスから除外する必要があります。

#### チャット インジケーターに関する考慮事項

リモート チャット コントロールのボイス ストリームを構成しても、そのチャット コントロールの [チャット インジケーター](/services/playfab/community/voice-communications/concepts-audio-troubleshooting#check-the-chat-indicators) には影響しません。適切な UI インジケーターを選択するために、チャット インジケーターとミックス ロジックの違いを調整するロジックを実装する必要がある場合があります。たとえば、チャット インジケーターはチャット コントロールが話していることを示している場合がありますが、カスタム ミックス ロジックはオーディオをドロップすることを選択する場合があります。

### 混合シナリオ

シナリオによっては、一部のチャット コントロールに対してオーディオ操作を有効にし、他のチャット コントロールでは有効にしたくない場合があります。たとえば、ゲーム マッチ中に遭遇する敵チームのプレイヤーにエフェクトを適用し、同じチームのプレイヤーには適用しない場合などです。このようなシナリオでは、リモート オーディオ操作について前に説明した手順に従い、オーディオ エフェクトを適用したいチャット コントロールに対してのみボイス ストリームを構成できます。残りのリモート チャット コントロールのオーディオは、ミュートと許可の構成が許可する限り、自動的にローカル チャット コントロールにレンダリングされます。
