Freigeben über


Verwenden der Echtzeit-Audiobearbeitung zum Anwenden von benutzerdefinierten Spracheffekten

PlayFab Party ist eine Echtzeit-Netzwerk- und Sprachchatlösung. Bei der Konfiguration für Sprachchat überträgt PlayFab Party Mikrofonaudio und gibt sie unverändert wieder. Einige Spiele benötigen Zugriff auf die Sprachchat-Audiopuffer, um benutzerdefinierte Audioeffekte wie räumliche Audio- oder Sprachfilter zu implementieren. Dieses Dokument enthält eine exemplarische Vorgehensweise zur Verwendung der Echtzeit-Audiobearbeitungsfunktion zum Abfangen und Ändern von Sprachchataudio in PlayFab Party.

Voraussetzungen

In dieser exemplarischen Vorgehensweise wird vorausgesetzt, dass Sie über grundlegende Kenntnisse mit Sprachchats in PlayFab Party verfügen.

Plattformunterstützung

Die Echtzeit-Audiobearbeitung ist nicht auf allen Plattformen verfügbar. Während die Methoden im Zusammenhang mit der Echtzeit-Audiobearbeitung im einheitlichen, plattformübergreifenden Header vorhanden sind, sind sie derzeit nur für Windows, Xbox und PlayStation® 5 implementiert. Die Methoden geben Fehler auf anderen Plattformen zurück.

Audiostreams

Die Echtzeit-Audiobearbeitung führt das Konzept von Audiostreams ein, um Audiodaten aus der Bibliothek abzurufen oder an diese zu übermitteln. Es gibt zwei Arten von Audiostreams. Die erste ist der Quellstream. Ein Quellstream wird verwendet, um Audio aus einem Chat-Steuerelement abzurufen. Jedes Chatsteuerelement kann nur über einen einzelnen Quelldatenstrom verfügen, der als Sprachstream bezeichnet wird. Bei einem lokalen Chat-Steuerelement wird dies verwendet, um die Mikrofoneingabe abzurufen. für eine Remotechatsteuerung wird diese verwendet, um eingehende Sprachaudiodaten abzurufen. Wenn der Sprachstream für ein Chat-Steuerelement vorhanden ist, leitet die Bibliothek die Quellaudio für dieses Chatsteuerelement an den zugehörigen Sprachdatenstrom um, anstatt die Audiodaten automatisch zu verarbeiten. Für ein lokales Chat-Steuerelement bedeutet dies, dass Mikrofonaudio an den Sprachstream umgeleitet wird, anstatt ihn automatisch zu codieren und zu übertragen. Für eine Remotechatsteuerung bedeutet dies, dass eingehende Sprachaudiodaten an den Sprachstream umgeleitet werden, anstatt sie automatisch an jedes lokale Chatsteuerelement zur Wiedergabe zu übermitteln. Quellstreams werden durch PartyAudioManipulationSourceStreamdargestellt.

Der zweite Streamtyp ist der Senkendatenstrom. Ein Senkenstream wird verwendet, um Audiodaten an ein Chatsteuerelement zu übermitteln. Nur lokale Chatsteuerelemente können Senkenstreams enthalten, und sie können jeweils zwei haben. Sie werden als Aufnahme- und Renderdatenstrom bezeichnet. Wenn der Aufzeichnungsdatenstrom für ein Chatsteuerelement vorhanden ist, ruft die Bibliothek Audiodaten aus dem Aufnahmedatenstrom ab, um sie zu codieren und an andere Chatsteuerelemente anstelle des Mikrofons zu übertragen. Wenn der Renderstream für ein Chat-Steuerelement vorhanden ist, ruft die Bibliothek Audiodaten aus dem Renderdatenstrom ab und gibt ihn zusätzlich zu den Sprachchataudios wieder, die automatisch von Remotechatsteuerungen wiedergegeben werden. An den Aufnahmedatenstrom übermittelte Audio wird als Mikrofoneingabe des lokalen Chat-Steuerelements verwendet. An den Renderstream übermittelte Audiodaten werden wiedergegeben oder an das Audioausgabegerät des lokalen Chat-Steuerelements "gerendert". Senkenstreams werden durch PartyAudioManipulationSinkStreamdargestellt.

Konfigurieren von Audiostreams

Standardmäßig verarbeitet die Bibliothek Audioabruf, -transport und -wiedergabe. Daher werden Chatsteuerelemente ohne Audiostreams erstellt. Sie können einen oder mehrere Streams für ein Chatsteuerelement über die Streamkonfigurationsmethoden PartyLocalChatControl::ConfigureAudioManipulationCaptureStream(), PartyLocalChatControl::ConfigureAudioManipulationRenderStream()und PartyChatControl::ConfigureAudioManipulationVoiceStream()erstellen. Nach der Konfiguration kann ein Stream anschließend über PartyLocalChatControl::GetAudioManipulationCaptureStream(), PartyLocalChatControl::GetAudioManipulationRenderStream()und abgerufen werden. PartyChatControl::GetAudioManipulationVoiceStream()

Mit jeder Streamkonfigurationsmethode können Sie das Format der Audiodaten angeben, die Sie aus dem Stream abrufen oder an diesen übermitteln. Weitere Informationen zu unterstützten Formaten finden Sie in der Referenzdokumentation zu den einzelnen Streamkonfigurationsmethoden.

Abrufen von Audiodaten aus einem Quelldatenstrom

Sie können Audiodaten aus einem Quelldatenstrom über PartyAudioManipulationSourceStream::GetNextBuffer()abrufen. Wenn die Sprachaktivität erkannt wird, ist ungefähr alle 40 ms ein neuer Puffer verfügbar. Wenn keine Puffer verfügbar sind, wird der Aufruf erfolgreich ausgeführt und stellt einen Puffer der Länge 0 (null) bereit. Die Gesamtzahl der momentan verfügbaren Puffer kann über PartyAudioManipulationSourceStream::GetAvailableBufferCount()abgerufen werden.

Stellt aus Effizienzgründen einen Puffer bereit, GetNextBuffer() der auf den Speicher der Bibliothek verweist, anstatt den gesamten Puffer zu kopieren. Sie kann optional an Ort und Stelle geändert werden. Sobald Sie die Verarbeitung eines Puffers abgeschlossen haben, sollten Sie ihn über PartyAudioManipulationSourceStream::ReturnBuffer() freigeben, damit die Bibliothek ihren Arbeitsspeicher freigeben kann. Mehrere Puffer können abgerufen werden, bevor sie zurückgegeben werden, und Puffer müssen nicht in der Reihenfolge zurückgegeben werden, in der sie abgerufen wurden.

Übermitteln von Audiodaten an einen Senkendatenstrom

Sie können Audiodaten über PartyAudioManipulationSinkStream::SubmitBuffer()an einen Senkendatenstrom übermitteln. Der Puffer wird von der Bibliothek kopiert und kann sofort freigegeben werden, nachdem der Aufruf abgeschlossen ist.

Alle 40 ms verbraucht die Bibliothek 40 ms Audio, das an den Senkenstream übermittelt wurde. Um Audiohäufigkeiten zu vermeiden, sollten Audiodaten mit einer konstanten Rate übermittelt werden.

Szenarien

Mikrofon-Audiobearbeitung a.k.a. vorcodierende Pufferbearbeitung

Mikrofon-Audiomanipulation ist der Akt des Abfangens und Änderns der Mikrofonaudios, bevor sie an andere Chatsteuerelemente übertragen werden. Dies wird manchmal als "vorcodierende Puffermanipulation" bezeichnet, da das Mikrofonaudio geändert wird, bevor es codiert und an andere Chatsteuerelemente übertragen wird. Wenn Sie dieses Szenario für ein lokales Chat-Steuerelement implementieren möchten, konfigurieren Sie zunächst einen Sprach- und Aufnahmedatenstrom für das lokale Chatsteuerelement. Nach der Konfiguration kann eine Funktion, die von jedem Tick eines dedizierten Audiothreads aufgerufen wird, um die Mikrofonaudiodaten dieses einzelnen Chatsteuerelements zu verarbeiten, wie folgt aussehen.

// 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);
        }
    }
}

Remote-Audiobearbeitung a.k.a. Pufferbearbeitung nach der Decodierung

Die Remote-Audiobearbeitung ist der Vorgang, in dem eingehende Audiodaten abgefangen und geändert werden, bevor sie in jedem lokalen Chatsteuerelement gerendert werden. Dies wird manchmal als "Pufferbearbeitung nach der Decodierung" bezeichnet, da die eingehende Audiowiedergabe nach der Decodierung, aber vor dem Rendern geändert wird. Wenn Sie dieses Szenario implementieren möchten, konfigurieren Sie zunächst den Sprachdatenstrom für jede Remotechatsteuerung und den Renderstream für jedes lokale Chatsteuerelement. Anschließend sollte jeder Teil ihres Audiothreads Audiodaten aus jedem Sprachstream pullen, die Audiodaten in einem einzelnen Stream mischen und optional Effekte anwenden und den gemischten Puffer an jeden Renderdatenstrom übermitteln. Abhängig von Ihrem Spielszenario müssen Sie möglicherweise die Puffer für jedes lokale Chatsteuerelement in verschiedene Streams mischen. Eine Funktion, die von jedem Tick eines dedizierten Audiothreads zur Verarbeitung der eingehenden Sprachaudio aufgerufen wird, könnte wie folgt aussehen.

// 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);
        }
    }
}

Überlegungen zu Datenschutz und Mischung

Die Bibliothek stellt Audio über den Sprachstream eines Remotechat-Steuerelements bereit, solange das Remotechat-Steuerelement Audio- und Chatberechtigungen generiert und stummgeschaltete Konfigurationen ermöglichen, dass die Audiodaten von mindestens einem lokalen Chat-Steuerelement wiedergegeben werden können. Wenn das Audio für ein lokales Chat-Steuerelement wiedergegeben werden soll, aber nicht für ein anderes, müssen Sie es aus dem Audiomix für das letztere Chat-Steuerelement weglassen.

Überlegungen zum Chatindikator

Das Konfigurieren eines Sprachstreams für eine Remotechatsteuerung wirkt sich nicht auf dessen Chatindikator aus. Möglicherweise müssen Sie Logik implementieren, um die Unterschiede zwischen dem Chatindikator und Ihrer Mischlogik zu vereinbaren, um den richtigen UI-Indikator auszuwählen. Für instance kann der Chatindikator angeben, dass das Chat-Steuerelement spricht, aber die benutzerdefinierte Mischlogik kann die Audiowiedergabe löschen.

Gemischte Szenarien

In einigen Szenarien können Sie die Audiobearbeitung für einige Chatsteuerelemente aktivieren, andere jedoch nicht. Für instance möchten Sie vielleicht Effekte auf Gegner anwenden, die während eines Spiels auftreten, aber nicht auf Spieler im selben Team. In einem solchen Szenario können Sie die zuvor beschriebenen Schritte für die Remote-Audiobearbeitung ausführen, während Sie nur einen Sprachstream für die Chatsteuerelemente konfigurieren, auf die Sie Audioeffekte anwenden möchten. Die Audiodaten der verbleibenden Remotechatsteuerungen werden automatisch in den lokalen Chat-Steuerelementen gerendert, sofern stummgeschaltet und Berechtigungskonfigurationen dies zulassen.