Audiodiagramme

In diesem Artikel wird gezeigt, wie Sie die APIs im Windows.Media.Audio-Namespace verwenden, um Audiodiagramme für Audiorouting-, Misch- und Verarbeitungsszenarien zu erstellen.

Ein Audiodiagramm ist eine Reihe miteinander verbundener Audioknoten, durch die Audiodaten fließen.

  • Audioeingabeknoten liefern dem Diagramm Audiodaten von Audioeingabegeräten, Audiodateien oder benutzerdefiniertem Code. lat

  • Audioausgabeknoten sind das Ziel für Audiodaten, die vom Diagramm verarbeitet werden. Audio kann aus dem Diagramm an Audioausgabegeräte, Audiodateien oder benutzerdefinierten Code weitergeleitet werden.

  • Submixknoten nehmen Audio von einem oder mehreren Knoten ab und kombinieren sie in einer einzigen Ausgabe, die an andere Knoten im Diagramm weitergeleitet werden kann.

Nachdem alle Knoten erstellt wurden und die Verbindungen zwischen ihnen eingerichtet wurden, starten Sie einfach das Audiodiagramm und die Audiodaten fließen von den Eingabeknoten über alle Submixknoten zu den Ausgabeknoten. Dieses Modell macht Szenarien wie das Aufzeichnen des Mikrofons eines Geräts in eine Audiodatei, das Wiedergeben von Audio aus einer Datei an den Lautsprecher eines Geräts oder das Mischen von Audio aus mehreren Quellen schnell und einfach zu implementieren.

Zusätzliche Szenarien sind mit dem Hinzufügen von Audioeffekten zum Audiodiagramm aktiviert. Jeder Knoten in einem Audiodiagramm kann mit null oder mehr Audioeffekten aufgefüllt werden, die die Audioverarbeitung für die Audiodaten ausführen, die den Knoten durchlaufen. Es gibt mehrere integrierte Effekte wie Echo, Equalizer, Begrenzung und Hall, die mit nur wenigen Codezeilen an einen Audioknoten angefügt werden können. Sie können auch eigene benutzerdefinierte Audioeffekte erstellen, die genau mit den integrierten Effekten funktionieren.

Hinweis

Im AudioGraph UWP-Beispiel wird der in dieser Übersicht erläuterte Code implementiert. Sie können das Beispiel herunterladen, um den verwendeten Code im Kontext zu sehen oder als Ausgangspunkt für Ihre eigene Anwendung verwenden.

Auswählen von Windows-Runtime AudioGraph oder XAudio2

Die Windows-Runtime Audiodiagramm-APIs bieten Funktionen, die auch mithilfe der COM-basierten XAudio2-APIs implementiert werden können. Im Folgenden finden Sie Features des Windows-Runtime Audiodiagrammframeworks, das sich von XAudio2 unterscheidet.

Die Windows-Runtime Audiodiagramm-APIs:

  • Die Verwendung ist wesentlich einfacher als XAudio2.
  • Kann von C# zusätzlich zur Unterstützung für C++ verwendet werden.
  • Kann Audiodateien, einschließlich komprimierter Dateiformate, direkt verwenden. XAudio2 funktioniert nur auf Audiopuffern und stellt keine Datei-E/A-Funktionen bereit.
  • Kann die Audiopipeline mit geringer Latenz in Windows 10 verwenden.
  • Unterstützen Sie den automatischen Endpunktwechsel, wenn Standardendpunktparameter verwendet werden. Wenn der Benutzer beispielsweise vom Lautsprecher eines Geräts zu einem Headset wechselt, wird das Audio automatisch an die neue Eingabe umgeleitet.

AudioGraph-Klasse

Die AudioGraph-Klasse ist das übergeordnete Element aller Knoten, aus denen das Diagramm besteht. Verwenden Sie dieses Objekt, um Instanzen aller Audioknotentypen zu erstellen. Erstellen Sie eine Instanz der AudioGraph-Klasse, indem Sie ein AudioGraph-Einstellungen Objekt mit Konfigurationseinstellungen für das Diagramm initialisieren und dann AudioGraph.CreateAsync aufrufen. Das zurückgegebene CreateAudioGraphResult gewährt Zugriff auf das erstellte Audiodiagramm oder gibt einen Fehlerwert an, wenn die Erstellung von Audiodiagrammen fehlschlägt.

AudioGraph audioGraph;
private async Task InitAudioGraph()
{

    AudioGraphSettings settings = new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Media);

    CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);
    if (result.Status != AudioGraphCreationStatus.Success)
    {
        ShowErrorMessage("AudioGraph creation error: " + result.Status.ToString());
    }

    audioGraph = result.Graph;

}
  • Alle Audioknotentypen werden mithilfe der Create*-Methoden der AudioGraph-Klasse erstellt.

  • Die AudioGraph.Start-Methode bewirkt, dass das Audiodiagramm mit der Verarbeitung von Audiodaten beginnt. Die AudioGraph.Stop-Methode beendet die Audioverarbeitung. Jeder Knoten im Diagramm kann unabhängig gestartet und beendet werden, während das Diagramm ausgeführt wird, aber keine Knoten sind aktiv, wenn das Diagramm beendet wird. ResetAllNodes bewirkt, dass alle Knoten im Diagramm alle Daten verwerfen, die sich aktuell in ihren Audiopuffern befinden.

  • Das QuantumStarted-Ereignis tritt auf, wenn das Diagramm die Verarbeitung eines neuen Quantums von Audiodaten startet. Das QuantumProcessed-Ereignis tritt auf, wenn die Verarbeitung eines Quantums abgeschlossen ist.

  • Die einzige AudioGraph Einstellungen-Eigenschaft, die erforderlich ist, ist AudioRenderCategory. Wenn Sie diesen Wert angeben, kann das System die Audiopipeline für die angegebene Kategorie optimieren.

  • Die Quantengröße des Audiodiagramms bestimmt die Anzahl der Proben, die gleichzeitig verarbeitet werden. Standardmäßig ist die Quantumgröße 10 ms basierend auf der Standard-Samplerate. Wenn Sie eine benutzerdefinierte Quantumgröße angeben, indem Sie die DesiredSamplesPerQuantum-Eigenschaft festlegen, müssen Sie auch die QuantumSizeSelectionMode-Eigenschaft auf ClosestToDesired festlegen oder der angegebene Wert ignoriert wird. Wenn dieser Wert verwendet wird, wählt das System eine Quantengröße so nah wie möglich an dem von Ihnen angegebenen Wert aus. Um die tatsächliche Quantengröße zu ermitteln, überprüfen Sie die SamplesPerQuantum des AudioGraph, nachdem es erstellt wurde.

  • Wenn Sie nur das Audiodiagramm mit Dateien verwenden möchten und keine Ausgabe an ein Audiogerät planen, empfiehlt es sich, die Standardmäßige Quantumgröße zu verwenden, indem Sie die DesiredSamplesPerQuantum-Eigenschaft nicht festlegen.

  • Die DesiredRenderDeviceAudioProcessing-Eigenschaft bestimmt die Verarbeitungsmenge, die das primäre Rendergerät für die Ausgabe des Audiodiagramms ausführt. Die Standardeinstellung ermöglicht es dem System, die Standard-Audioverarbeitung für die angegebene Audiowiedergabekategorie zu verwenden. Diese Verarbeitung kann den Sound von Audio auf einigen Geräten erheblich verbessern, insbesondere bei mobilen Geräten mit kleinen Lautsprechern. Die Einstellung Raw kann die Leistung verbessern, indem die Menge der ausgeführten Signalverarbeitung minimiert wird, aber auf einigen Geräten zu einer schlechteren Soundqualität führen kann.

  • Wenn QuantumSizeSelectionMode auf LowestLatencyfestgelegt ist, wird das Audiodiagramm automatisch Raw für DesiredRenderDeviceAudioProcessing verwendet.

  • Ab Windows 10, Version 1803, können Sie die Eigenschaften AudioGraphSettings.MaxPlaybackSpeedFactor festlegen, um einen Maximalwert für die Eigenschaften AudioFileInputNode.PlaybackSpeedFactor, AudioFrameInputNode.PlaybackSpeedFactor und MediaSourceInputNode.PlaybackSpeedFactor festlegen. Wenn ein Audiodiagramm einen Wiedergabegeschwindigkeitsfaktor größer als 1 unterstützt, muss das System zusätzlichen Speicher zuweisen, um einen ausreichenden Puffer an Audiodaten aufrechtzuerhalten. Aus diesem Grund verringert sich der Speicherverbrauch Ihrer App, wenn Sie MaxPlaybackSpeedFactor auf den niedrigsten von Ihrer App benötigten Wert setzen. Wenn Ihre App Inhalte nur mit normaler Geschwindigkeit wiedergibt, wird empfohlen, MaxPlaybackSpeedFactor auf 1 zu setzen.

  • Die EncodingProperties bestimmen das vom Diagramm verwendete Audioformat. Es werden nur 32-Bit-Float-Formate unterstützt.

  • PrimaryRenderDevice legt das primäre Rendergerät für das Audiodiagramm fest. Wenn Sie dies nicht festlegen, wird das Standardsystemgerät verwendet. Das primäre Rendergerät wird verwendet, um die Quantengrößen für andere Knoten im Diagramm zu berechnen. Wenn keine Audiowiedergabegeräte auf dem System vorhanden sind, schlägt die Erstellung von Audiodiagrammen fehl.

Sie können das Audiodiagramm das standardmäßige Audio-Rendergerät verwenden lassen oder die Klasse Windows.Devices.Enumeration.DeviceInformationverwenden, um eine Liste der verfügbaren Audio-Rendergeräte des Systems abzurufen. Rufen Sie dazu FindAllAsync auf und übergeben Sie den von Windows.Media.Devices.MediaDevice.GetAudioRenderSelectorzurückgegebenen Selektor für das Audio-Rendergerät.. Sie können eines der zurückgegebenen DeviceInformation Objekte programmgesteuert auswählen oder die Benutzeroberfläche anzeigen, um dem Benutzer die Auswahl eines Geräts zu ermöglichen und es dann zum Festlegen der Eigenschaft PrimaryRenderDevice verwenden.

Windows.Devices.Enumeration.DeviceInformationCollection devices =
 await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Media.Devices.MediaDevice.GetAudioRenderSelector());

// Show UI to allow the user to select a device
Windows.Devices.Enumeration.DeviceInformation selectedDevice = ShowMyDeviceSelectionUI(devices);


settings.PrimaryRenderDevice = selectedDevice;

Geräteeingabeknoten

Ein Geräteeingabeknoten leitet Audio aus einem audioaufnahmegerät, das mit dem System verbunden ist, z. B. ein Mikrofon, in das Diagramm ein. Erstellen Sie ein DeviceInputNode-Objekt, das das standardmäßige Audioaufnahmegerät des Systems verwendet, indem Sie CreateDeviceInputNodeAsync aufrufen. Stellen Sie eine AudioRenderCategory bereit, damit das System die Audiopipeline für die angegebene Kategorie optimieren kann.

AudioDeviceInputNode deviceInputNode;
private async Task CreateDeviceInputNode()
{
    // Create a device output node
    CreateAudioDeviceInputNodeResult result = await audioGraph.CreateDeviceInputNodeAsync(Windows.Media.Capture.MediaCategory.Media);

    if (result.Status != AudioDeviceNodeCreationStatus.Success)
    {
        // Cannot create device output node
        ShowErrorMessage(result.Status.ToString());
        return;
    }

    deviceInputNode = result.DeviceInputNode;
}

Wenn Sie ein bestimmtes Audioaufnahmegerät für den Geräteeingabeknoten angeben möchten, können Sie die Klasse Windows.Devices.Enumeration.DeviceInformation verwenden, um eine Liste der verfügbaren Audioaufnahmegeräte des Systems abzurufen, indem Sie FindAllAsync aufrufen und den von Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector zurückgegebenen Selektor für das Audiorenderinggerät übergeben. Sie können eines der zurückgegebenen DeviceInformation-Objekte programmgesteuert auswählen oder die Benutzeroberfläche anzeigen, damit der Benutzer ein Gerät auswählen und es dann an CreateDeviceInputNodeAsync übergeben kann.

Windows.Devices.Enumeration.DeviceInformationCollection devices =
 await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector());

// Show UI to allow the user to select a device
Windows.Devices.Enumeration.DeviceInformation selectedDevice = ShowMyDeviceSelectionUI(devices);

CreateAudioDeviceInputNodeResult result =
    await audioGraph.CreateDeviceInputNodeAsync(Windows.Media.Capture.MediaCategory.Media, audioGraph.EncodingProperties, selectedDevice);

Geräteausgabeknoten

Ein Geräteausgabeknoten verschiebt Audio aus dem Diagramm an ein Audiowiedergabegerät, z. B. Lautsprecher oder ein Headset. Erstellen Sie einen DeviceOutputNode , indem Sie CreateDeviceOutputNodeAsync aufrufen . Der Ausgabeknoten verwendet die PrimaryRenderDevice des Audiodiagramms.

AudioDeviceOutputNode deviceOutputNode;
private async Task CreateDeviceOutputNode()
{
    // Create a device output node
    CreateAudioDeviceOutputNodeResult result = await audioGraph.CreateDeviceOutputNodeAsync();

    if (result.Status != AudioDeviceNodeCreationStatus.Success)
    {
        // Cannot create device output node
        ShowErrorMessage(result.Status.ToString());
        return;
    }

    deviceOutputNode = result.DeviceOutputNode;
}

Dateieingabeknoten

Mit einem Dateieingabeknoten können Sie Daten aus einer Audiodatei in das Diagramm übertragen. Erstellen Sie einen AudioFileInputNode, indem Sie CreateFileInputNodeAsync aufrufen.

AudioFileInputNode fileInputNode;
private async Task CreateFileInputNode()
{
    if (audioGraph == null)
        return;

    FileOpenPicker filePicker = new FileOpenPicker();
    filePicker.SuggestedStartLocation = PickerLocationId.MusicLibrary;
    filePicker.FileTypeFilter.Add(".mp3");
    filePicker.FileTypeFilter.Add(".wav");
    filePicker.FileTypeFilter.Add(".wma");
    filePicker.FileTypeFilter.Add(".m4a");
    filePicker.ViewMode = PickerViewMode.Thumbnail;
    StorageFile file = await filePicker.PickSingleFileAsync();

    // File can be null if cancel is hit in the file picker
    if (file == null)
    {
        return;
    }
    CreateAudioFileInputNodeResult result = await audioGraph.CreateFileInputNodeAsync(file);

    if (result.Status != AudioFileNodeCreationStatus.Success)
    {
        ShowErrorMessage(result.Status.ToString());
    }

    fileInputNode = result.FileInputNode;
}
  • Dateieingabeknoten unterstützen die folgenden Dateiformate: mp3, wav, wma, m4a.
  • Legen Sie die StartTime-Eigenschaft fest, um den Zeitoffset in der Datei anzugeben, in der die Wiedergabe beginnen soll. Wenn diese Eigenschaft Null ist, wird der Anfang der Datei verwendet. Legen Sie die EndTime-Eigenschaft fest, um den Zeitoffset in der Datei anzugeben, in der die Wiedergabe beendet werden soll. Wenn diese Eigenschaft Null ist, wird das Ende der Datei verwendet. Der Startzeitwert muss niedriger als der Endzeitwert sein, und der Endzeitwert muss kleiner oder gleich der Dauer der Audiodatei sein, die durch Überprüfen des Werts der Dauer-Eigenschaft bestimmt werden kann.
  • Suchen Sie zu einer Position in der Audiodatei, indem Sie Seek aufrufen und den zeitlichen Versatz in der Datei angeben, zu dem die Abspielposition verschoben werden soll. Der angegebene Wert muss sich innerhalb des StartTime- und EndTime-Bereichs befinden. Rufen Sie die aktuelle Wiedergabeposition des Knotens mit der schreibgeschützten Position-Eigenschaft ab.
  • Aktivieren Sie die Schleife der Audiodatei, indem Sie die Eigenschaft LoopCount festlegen. Wenn dieser Wert nicht Null ist, gibt dieser Wert an, wie oft die Datei nach der ersten Wiedergabe wiedergegeben wird. Wenn Sie beispielsweise LoopCount auf 1 festlegen, wird die Datei insgesamt 2 Mal wiedergegeben, und wenn Sie sie auf 5 festlegen, wird die Datei insgesamt 6 Mal wiedergegeben. Das Festlegen von LoopCount auf Null bewirkt, dass die Datei unbegrenzt in einer Schleife wiedergegeben wird. Um die Schleife zu beenden, legen Sie den Wert auf 0 fest.
  • Passen Sie die Geschwindigkeit an, mit der die Audiodatei wiedergegeben wird, indem Sie PlaybackSpeedFactor festlegen. Ein Wert von 1 gibt die ursprüngliche Geschwindigkeit der Datei an, 5 ist die halbe Geschwindigkeit, und 2 ist doppelte Geschwindigkeit.

MediaSource-Eingabeknoten

Die MediaSource-Klasse bietet eine gängige Möglichkeit, auf Medien aus unterschiedlichen Quellen zu verweisen und ein gemeinsames Modell für den Zugriff auf Mediendaten verfügbar zu machen, unabhängig vom zugrunde liegenden Medienformat, das eine Datei auf dem Datenträger, einem Stream oder einer adaptiven Streaming-Netzwerkquelle sein kann. Mit einem **MediaSourceAudioInputNode-Knoten können Sie Audiodaten aus einer MediaSource in das Audiodiagramm leiten. Erstellen Sie einen MediaSourceAudioInputNode, indem Sie CreateMediaSourceAudioInputNodeAsync aufrufen und ein MediaSource-Objekt übergeben, das den Inhalt darstellt, den Sie wiedergeben möchten. Ein **CreateMediaSourceAudioInputNodeResult wird zurückgegeben, mit dem Sie den Status des Vorgangs ermitteln können, indem Sie die Statuseigenschaft überprüfen. Wenn der Status Erfolg lautet, können Sie den erstellten MediaSourceAudioInputNode abrufen, indem Sie auf die Eigenschaft Node zugreifen. Das folgende Beispiel zeigt die Erstellung eines Knotens aus einem AdaptiveMediaSource-Objekt, das das Streaming von Inhalten über das Netzwerk darstellt.S Weitere Informationen zum Arbeiten mit MediaSource finden Sie unter Medienelemente, Wiedergabelisten und Titel. Weitere Informationen zum Streamen von Medieninhalten über das Internet finden Sie unter Adaptives Streaming.

MediaSourceAudioInputNode mediaSourceInputNode;
private async Task CreateMediaSourceInputNode(System.Uri contentUri)
{
    if (audioGraph == null)
        return;

    var adaptiveMediaSourceResult = await AdaptiveMediaSource.CreateFromUriAsync(contentUri);
    if(adaptiveMediaSourceResult.Status != AdaptiveMediaSourceCreationStatus.Success)
    {
        Debug.WriteLine("Failed to create AdaptiveMediaSource");
        return;
    }

    var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(adaptiveMediaSourceResult.MediaSource);
    CreateMediaSourceAudioInputNodeResult mediaSourceAudioInputNodeResult =
        await audioGraph.CreateMediaSourceAudioInputNodeAsync(mediaSource);

    if (mediaSourceAudioInputNodeResult.Status != MediaSourceAudioInputNodeCreationStatus.Success)
    {
        switch (mediaSourceAudioInputNodeResult.Status)
        {
            case MediaSourceAudioInputNodeCreationStatus.FormatNotSupported:
                Debug.WriteLine("The MediaSource uses an unsupported format");
                break;
            case MediaSourceAudioInputNodeCreationStatus.NetworkError:
                Debug.WriteLine("The MediaSource requires a network connection and a network-related error occurred");
                break;
            case MediaSourceAudioInputNodeCreationStatus.UnknownFailure:
            default:
                Debug.WriteLine("An unknown error occurred while opening the MediaSource");
                break;
        }
        return;
    }

    mediaSourceInputNode = mediaSourceAudioInputNodeResult.Node;
}

Um eine Benachrichtigung zu erhalten, wenn die Wiedergabe das Ende des MediaSource-Inhalts erreicht hat, registrieren Sie einen Handler für das Ereignis MediaSourceCompleted.

mediaSourceInputNode.MediaSourceCompleted += MediaSourceInputNode_MediaSourceCompleted;
private void MediaSourceInputNode_MediaSourceCompleted(MediaSourceAudioInputNode sender, object args)
{
    audioGraph.Stop();
}

Während die Wiedergabe einer Datei von Datenträgern wahrscheinlich immer erfolgreich abgeschlossen wird, schlägt das von einer Netzwerkquelle gestreamte Medien aufgrund einer Änderung der Netzwerkverbindung oder anderer Probleme, die sich außerhalb der Kontrolle des Audiodiagramms befinden, möglicherweise fehl. Wenn eine MediaSource während der Wiedergabe nicht wiedergegeben werden kann, löst das Audiodiagramm das UnrecoverableErrorOccurred-Ereignis aus. Sie können den Handler für dieses Ereignis verwenden, um das Audiodiagramm zu beenden und zu löschen und dann Ihr Diagramm neu zu initialisieren.

audioGraph.UnrecoverableErrorOccurred += AudioGraph_UnrecoverableErrorOccurred;
private void AudioGraph_UnrecoverableErrorOccurred(AudioGraph sender, AudioGraphUnrecoverableErrorOccurredEventArgs args)
{
    if (sender == audioGraph && args.Error != AudioGraphUnrecoverableError.None)
    {
        Debug.WriteLine("The audio graph encountered and unrecoverable error.");
        audioGraph.Stop();
        audioGraph.Dispose();
        InitAudioGraph();
    }
}

Dateiausgabeknoten

Mit einem Dateiausgabeknoten können Sie Audiodaten aus dem Diagramm in eine Audiodatei leiten. Erstellen Sie einen AudioFileOutputNode, indem Sie CreateFileOutputNodeAsync aufrufen.

AudioFileOutputNode fileOutputNode;
private async Task CreateFileOutputNode()
{
    FileSavePicker saveFilePicker = new FileSavePicker();
    saveFilePicker.FileTypeChoices.Add("Pulse Code Modulation", new List<string>() { ".wav" });
    saveFilePicker.FileTypeChoices.Add("Windows Media Audio", new List<string>() { ".wma" });
    saveFilePicker.FileTypeChoices.Add("MPEG Audio Layer-3", new List<string>() { ".mp3" });
    saveFilePicker.SuggestedFileName = "New Audio Track";
    StorageFile file = await saveFilePicker.PickSaveFileAsync();

    // File can be null if cancel is hit in the file picker
    if (file == null)
    {
        return;
    }

    Windows.Media.MediaProperties.MediaEncodingProfile mediaEncodingProfile;
    switch (file.FileType.ToString().ToLowerInvariant())
    {
        case ".wma":
            mediaEncodingProfile = MediaEncodingProfile.CreateWma(AudioEncodingQuality.High);
            break;
        case ".mp3":
            mediaEncodingProfile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High);
            break;
        case ".wav":
            mediaEncodingProfile = MediaEncodingProfile.CreateWav(AudioEncodingQuality.High);
            break;
        default:
            throw new ArgumentException();
    }


    // Operate node at the graph format, but save file at the specified format
    CreateAudioFileOutputNodeResult result = await audioGraph.CreateFileOutputNodeAsync(file, mediaEncodingProfile);

    if (result.Status != AudioFileNodeCreationStatus.Success)
    {
        // FileOutputNode creation failed
        ShowErrorMessage(result.Status.ToString());
        return;
    }

    fileOutputNode = result.FileOutputNode;
}

Audioframeeingabeknoten

Mit einem Audioframeeingabeknoten können Sie Audiodaten, die Sie in Ihrem eigenen Code generieren, in das Audiodiagramm übertragen. Dies ermöglicht Szenarien wie das Erstellen eines benutzerdefinierten Softwaresynthesizers. Erstellen Sie einen AudioFrameInputNode durch aufrufen von CreateFrameInputNode.

AudioFrameInputNode frameInputNode;
private void CreateFrameInputNode()
{
    // Create the FrameInputNode at the same format as the graph, except explicitly set mono.
    AudioEncodingProperties nodeEncodingProperties = audioGraph.EncodingProperties;
    nodeEncodingProperties.ChannelCount = 1;
    frameInputNode = audioGraph.CreateFrameInputNode(nodeEncodingProperties);

    // Initialize the Frame Input Node in the stopped state
    frameInputNode.Stop();

    // Hook up an event handler so we can start generating samples when needed
    // This event is triggered when the node is required to provide data
    frameInputNode.QuantumStarted += node_QuantumStarted;
}

Das FrameInputNode.QuantumStarted-Ereignis wird ausgelöst, wenn das Audiodiagramm bereit ist, mit der Verarbeitung des nächsten Quantums von Audiodaten zu beginnen. Sie geben Ihre benutzerdefinierten generierten Audiodaten innerhalb des Handlers für dieses Ereignis an.

private void node_QuantumStarted(AudioFrameInputNode sender, FrameInputNodeQuantumStartedEventArgs args)
{
    // GenerateAudioData can provide PCM audio data by directly synthesizing it or reading from a file.
    // Need to know how many samples are required. In this case, the node is running at the same rate as the rest of the graph
    // For minimum latency, only provide the required amount of samples. Extra samples will introduce additional latency.
    uint numSamplesNeeded = (uint)args.RequiredSamples;

    if (numSamplesNeeded != 0)
    {
        AudioFrame audioData = GenerateAudioData(numSamplesNeeded);
        frameInputNode.AddFrame(audioData);
    }
}
  • Das an den Ereignishandler FrameInputNodeQuantumStartedEventArgs übergebene Objekt QuantumStarted stellt die Eigenschaft RequiredSamples bereit, die angibt, wie viele Samples der Audiograph benötigt, um das zu verarbeitende Quantum aufzufüllen.
  • Rufen Sie AudioFrameInputNode.AddFrameauf, um ein Objekt AudioFrame zu übergeben, das mit Audiodaten im Diagramm gefüllt ist.
  • In Windows 10, Version 1803, wurde ein neuer Satz von APIs zur Verwendung von MediaFrameReader mit Audiodaten eingeführt. Diese APIs ermöglichen Ihnen, AudioFrame-Objekte aus einer Medienrahmenquelle abzurufen, die mit der Methode AddFrame an einen FrameInputNode übergeben werden können. Weitere Informationen finden Sie unter Verarbeiten von Audioframes mit MediaFrameReader.
  • Nachfolgend sehen Sie eine Beispielimplementierung der GenerateAudioData-Hilfsmethode .

Um einen AudioFrame mit Audiodaten aufzufüllen, müssen Sie Zugriff auf den zugrunde liegenden Speicherpuffer des Audioframes erhalten. Zu diesem Zweck müssen Sie die IMemoryBufferByteAccess-COM-Schnittstelle initialisieren, indem Sie den folgenden Code im Namespace hinzufügen.

[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

Der folgende Code zeigt eine Beispielimplementierung einer GenerateAudioData-Hilfsmethode, die einen AudioFrame erstellt und mit Audiodaten füllt.

private double audioWaveTheta = 0;

unsafe private AudioFrame GenerateAudioData(uint samples)
{
    // Buffer size is (number of samples) * (size of each sample)
    // We choose to generate single channel (mono) audio. For multi-channel, multiply by number of channels
    uint bufferSize = samples * sizeof(float);
    AudioFrame frame = new Windows.Media.AudioFrame(bufferSize);

    using (AudioBuffer buffer = frame.LockBuffer(AudioBufferAccessMode.Write))
    using (IMemoryBufferReference reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacityInBytes;
        float* dataInFloat;

        // Get the buffer from the AudioFrame
        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacityInBytes);

        // Cast to float since the data we are generating is float
        dataInFloat = (float*)dataInBytes;

        float freq = 1000; // choosing to generate frequency of 1kHz
        float amplitude = 0.3f;
        int sampleRate = (int)audioGraph.EncodingProperties.SampleRate;
        double sampleIncrement = (freq * (Math.PI * 2)) / sampleRate;

        // Generate a 1kHz sine wave and populate the values in the memory buffer
        for (int i = 0; i < samples; i++)
        {
            double sinValue = amplitude * Math.Sin(audioWaveTheta);
            dataInFloat[i] = (float)sinValue;
            audioWaveTheta += sampleIncrement;
        }
    }

    return frame;
}
  • Da diese Methode auf den rohen Puffer zugreift, der den Windows-Runtime Typen zugrunde liegt, muss sie mithilfe des unsicheren Schlüsselworts (Keyword) deklariert werden. Sie müssen Ihr Projekt auch in Microsoft Visual Studio so konfigurieren, dass die Kompilierung unsicherer Code zugelassen wird, indem Sie die Seite Eigenschaften des Projekts öffnen, auf die Seite Build-Eigenschaften klicken und das Kontrollkästchen Unsicheren Code zulassen aktivieren.
  • Initialisieren Sie eine neue Instanz von AudioFrame im Windows.Media-Namespace , indem Sie die gewünschte Puffergröße an den Konstruktor übergeben. Die Puffergröße ist die Anzahl der Stichproben, die mit der Größe der einzelnen Stichproben multipliziert werden.
  • Holen Sie sich den AudioBuffer des Audioframes, indem Sie LockBuffer aufrufen.
  • Holen Sie sich eine Instanz der COM-Schnittstelle IMemoryBufferByteAccess aus dem Audiopuffer, indem Sie CreateReference aufrufen.
  • Holen Sie sich einen Zeiger auf die Rohdaten des Audiopuffers, indem Sie IMemoryBufferByteAccess.GetBuffer aufrufen und ihn in den Beispieldatentyp der Audiodaten umwandeln.
  • Füllen Sie den Puffer mit Daten und geben Sie den AudioFrame zur Übermittlung in das Audiodiagramm zurück.

Audioframeausgabeknoten

Mit einem Audioframeausgabeknoten können Sie Audiodatenausgabe aus dem Audiodiagramm mit benutzerdefiniertem Code empfangen und verarbeiten. Ein Beispielszenario hierfür ist das Ausführen einer Signalanalyse für die Audioausgabe. Erstellen Sie einen AudioFrameOutputNode durch aufrufen von CreateFrameOutputNode.

AudioFrameOutputNode frameOutputNode;
private void CreateFrameOutputNode()
{
    frameOutputNode = audioGraph.CreateFrameOutputNode();
    audioGraph.QuantumStarted += AudioGraph_QuantumStarted;
}

Das AudioGraph.QuantumStarted-Ereignis wird ausgelöst, wenn das Audiodiagramm mit der Verarbeitung eines Quantums von Audiodaten beginnt. Sie können auf die Audiodaten innerhalb des Handlers für dieses Ereignis zugreifen.

Hinweis

Wenn Sie Audioframes in regelmäßigen Abständen abrufen möchten, die mit dem Audiodiagramm synchronisiert werden, rufen Sie AudioFrameOutputNode.GetFrame aus dem synchronen QuantumStarted-Ereignishandler auf. Das QuantumProcessed-Ereignis wird asynchron ausgelöst, nachdem das Audiomodul die Audioverarbeitung abgeschlossen hat, was bedeutet, dass seine Häufigkeit unregelmäßig sein kann. Daher sollten Sie das Ereignis QuantumProcessed nicht für die synchronisierte Verarbeitung von Audio-Frame-Daten verwenden.

private void AudioGraph_QuantumStarted(AudioGraph sender, object args)
{
    AudioFrame frame = frameOutputNode.GetFrame();
    ProcessFrameOutput(frame);

}
  • Rufen Sie GetFrame auf, um ein AudioFrame-Objekt mit Audiodaten aus dem Diagramm abzurufen.
  • Nachfolgend sehen Sie eine Beispielimplementierung der ProcessFrameOutput-Hilfsmethode .
unsafe private void ProcessFrameOutput(AudioFrame frame)
{
    using (AudioBuffer buffer = frame.LockBuffer(AudioBufferAccessMode.Write))
    using (IMemoryBufferReference reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacityInBytes;
        float* dataInFloat;

        // Get the buffer from the AudioFrame
        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacityInBytes);

        dataInFloat = (float*)dataInBytes;
    }
}
  • Wie im obigen Beispiel für Audioframe-Eingabeknoten müssen Sie die IMemoryBufferByteAccess-COM-Schnittstelle deklarieren und Ihr Projekt so konfigurieren, dass unsicherer Code zugelassen wird, um auf den zugrunde liegenden Audiopuffer zuzugreifen.
  • Holen Sie sich den AudioBuffer des Audioframes, indem Sie LockBuffer aufrufen.
  • Holen Sie sich eine Instanz der COM-Schnittstelle IMemoryBufferByteAccess aus dem Audiopuffer, indem Sie CreateReference aufrufen.
  • Holen Sie sich einen Zeiger auf die Rohdaten des Audiopuffers, indem Sie IMemoryBufferByteAccess.GetBuffer aufrufen und ihn in den Beispieldatentyp der Audiodaten umwandeln.

Knotenverbindungen und Submixknoten

Alle Eingabeknotentypen stellen die Methode AddOutgoingConnection bereit, die das vom Knoten erzeugte Audio an den Knoten weiterleitet, der an die Methode übergeben wird. Im folgenden Beispiel wird ein AudioFileInputNode mit einem AudioDeviceOutputNode verbunden, bei dem es sich um ein einfaches Setup für die Wiedergabe einer Audiodatei auf dem Lautsprecher des Geräts handelt.

fileInputNode.AddOutgoingConnection(deviceOutputNode);

Sie können mehrere Verbindungen von einem Eingabeknoten zu anderen Knoten erstellen. Das folgende Beispiel fügt eine weitere Verbindung vom AudioFileInputNode zu einem AudioFileOutputNode hinzu. Jetzt wird die Audiodaten aus der Audiodatei an den Lautsprecher des Geräts wiedergegeben und auch in eine Audiodatei geschrieben.

fileInputNode.AddOutgoingConnection(fileOutputNode);

Ausgabeknoten können auch mehrere Verbindungen von anderen Knoten empfangen. Im folgenden Beispiel wird eine Verbindung von einem AudioDeviceInputNode zum Knoten AudioDeviceOutput hergestellt. Da der Ausgabeknoten Verbindungen vom Dateieingabeknoten und dem Geräteeingabeknoten hat, enthält die Ausgabe eine Mischung aus Audio aus beiden Quellen. AddOutgoingConnection stellt eine Überladung bereit, mit der Sie einen Verstärkungswert für das Signal angeben können, das die Verbindung durchgibt.

deviceInputNode.AddOutgoingConnection(deviceOutputNode, .5);

Obwohl Ausgabeknoten Verbindungen von mehreren Knoten akzeptieren können, möchten Sie möglicherweise eine Zwischenmischung von Signalen von einem oder mehreren Knoten erstellen, bevor Sie die Mischung an eine Ausgabe übergeben. Sie können z. B. die Ebene festlegen oder Effekte auf eine Teilmenge der Audiosignale in einem Diagramm anwenden. Verwenden Sie dazu den AudioSubmixNode. Sie können eine Verbindung mit einem Submixknoten von einem oder mehreren Eingabeknoten oder anderen Submixknoten herstellen. Im folgenden Beispiel wird ein neuer Submixknoten mit AudioGraph.CreateSubmixNode erstellt. Anschließend werden Verbindungen aus einem Dateieingabeknoten und einem Frameausgabeknoten zum Submixknoten hinzugefügt. Schließlich ist der Submixknoten mit einem Dateiausgabeknoten verbunden.

private void CreateSubmixNode()
{
    AudioSubmixNode submixNode = audioGraph.CreateSubmixNode();
    fileInputNode.AddOutgoingConnection(submixNode);
    frameInputNode.AddOutgoingConnection(submixNode);
    submixNode.AddOutgoingConnection(fileOutputNode);
}

Starten und Stoppen von Audiodiagrammknoten

Wenn AudioGraph.Start aufgerufen wird, beginnt das Audiodiagramm mit der Verarbeitung von Audiodaten. Jeder Knotentyp stellt die Methoden Start und Stop bereit, die den einzelnen Knoten veranlassen, die Datenverarbeitung zu starten oder zu stoppen. Wenn AudioGraph.Stop aufgerufen wird, wird die gesamte Audioverarbeitung in allen Knoten unabhängig vom Status einzelner Knoten beendet, aber der Status der einzelnen Knoten kann festgelegt werden, während das Audiodiagramm beendet wird. Sie können beispielsweise Stop für einen einzelnen Knoten aufrufen, während der Graph angehalten ist, und dann AudioGraph.Start aufrufen, woraufhin der einzelne Knoten im angehaltenen Zustand verbleibt.

Alle Knotentypen machen die ConsumeInput-Eigenschaft verfügbar, die es dem Knoten ermöglicht, die Audioverarbeitung fortzusetzen, aber die Verwendung von Audiodaten, die von anderen Knoten eingegeben werden, anhält.

Alle Knotentypen machen die Reset-Methode verfügbar, die bewirkt, dass der Knoten alle Audiodaten, die sich derzeit im Puffer befinden, aufheben Karte.

Hinzufügen von Audioeffekten

Mit der Audiodiagramm-API können Sie jedem Knotentyp in einem Diagramm Audioeffekte hinzufügen. Ausgabeknoten, Eingabeknoten und Submixknoten können jeweils eine unbegrenzte Anzahl von Audioeffekten aufweisen, die nur auf die Funktionen der Hardware beschränkt sind. Das folgende Beispiel veranschaulicht das Hinzufügen des integrierten Echoeffekts zu einem Submixknoten.

EchoEffectDefinition echoEffect = new EchoEffectDefinition(audioGraph);
echoEffect.Delay = 1000.0;
echoEffect.Feedback = .2;
echoEffect.WetDryMix = .5;

submixNode.EffectDefinitions.Add(echoEffect);
  • Alle Audioeffekte implementieren IAudioEffectDefinition. Jeder Knoten macht eine EffectDefinitions-Eigenschaft verfügbar, die die Liste der Effekte darstellt, die auf diesen Knoten angewendet werden. Fügen Sie einen Effekt hinzu, indem Sie der Liste das Definitionsobjekt hinzufügen.
  • Es gibt mehrere Effektdefinitionsklassen, die im Windows.Media.Audio-Namespace bereitgestellt werden. Dazu gehören:
  • Sie können Ihre eigenen Audioeffekte erstellen, die IAudioEffectDefinition implementieren, und diese auf jeden Knoten in einem Audiodiagramm anwenden.
  • Jeder Knotentyp stellt eine Methode DisableEffectsByDefinition bereit, die alle Effekte in der Liste EffectDefinitions des Knotens deaktiviert, die mit der angegebenen Definition hinzugefügt wurden. EnableEffectsByDefinition aktiviert die Effekte mit der angegebenen Definition.

Räumliche Audiowiedergabe

Ab Windows 10, Version 1607, unterstützt AudioGraph räumliches Audio, mit dem Sie den Ort im 3D-Raum angeben können, von dem Audiodaten von jedem Eingabe- oder Submixknoten ausgegeben werden. Sie können auch eine Form und Richtung angeben, in der Audio ausgegeben wird, eine Geschwindigkeit, die zum Verschieben der Audiodaten des Knotens verwendet wird, und ein Verfallsmodell definieren, das beschreibt, wie die Audiowiedergabe mit Abstand abgeschwächt wird.

Um einen Emitter zu erstellen, können Sie zunächst eine Form erstellen, in der der Sound vom Emitter projiziert wird, der ein Kegel oder eine Omnidirektional sein kann. Die AudioNodeEmitterShape-Klasse stellt statische Methoden zum Erstellen dieser Shapes bereit. Erstellen Sie als Nächstes ein Verfallsmodell. Dadurch wird definiert, wie die Lautstärke der Audiodaten vom Emitter verringert wird, wenn der Abstand zum Hörer zunimmt. Die CreateNatural-Methode erstellt ein Verfallsmodell, das den natürlichen Verfall des Klangs mithilfe eines Abstands-Falloffmodells emuliert. Erstellen Sie schließlich ein AudioNodeEmitterSettings-Objekt. Derzeit wird dieses Objekt nur zum Aktivieren und Deaktivieren der geschwindigkeitsbasierten Doppler-Dämpfung der Audiodaten des Emitters verwendet. Rufen Sie den AudioNodeEmitter-Konstruktor auf, und übergeben Sie die soeben erstellten Initialisierungsobjekte. Standardmäßig wird der Emitter am Ursprung platziert, Sie können jedoch die Position des Emitters mit der Position-Eigenschaft festlegen.

Hinweis

Audioknoten-Emitter können nur Audio verarbeiten, das in Mono mit einer Samplerate von 48 kHz formatiert ist. Der Versuch, Stereoaudio oder Audio mit einer anderen Samplingrate zu verwenden, führt zu einer Ausnahme.

Sie weisen den Emitter einem Audioknoten zu, wenn Sie ihn erstellen, indem Sie die überladene Erstellungsmethode für den gewünschten Knotentyp verwenden. In diesem Beispiel wird CreateFileInputNodeAsync verwendet, um einen Dateieingabeknoten aus einer angegebenen Datei und dem AudioNodeEmitter-Objekt zu erstellen, das Sie dem Knoten zuordnen möchten.

var emitterShape = AudioNodeEmitterShape.CreateOmnidirectional();
var decayModel = AudioNodeEmitterDecayModel.CreateNatural(.1, 1, 10, 100);
var settings = AudioNodeEmitterSettings.None;

var emitter = new AudioNodeEmitter(emitterShape, decayModel, settings);
emitter.Position = new System.Numerics.Vector3(10, 0, 5);

CreateAudioFileInputNodeResult result = await audioGraph.CreateFileInputNodeAsync(file, emitter);

if (result.Status != AudioFileNodeCreationStatus.Success)
{
    ShowErrorMessage(result.Status.ToString());
}

fileInputNode = result.FileInputNode;

Der AudioDeviceOutputNode , der Audiodaten aus dem Diagramm ausgibt, verfügt über ein Listenerobjekt, auf das mit der Listener-Eigenschaft zugegriffen wird, die die Position, Ausrichtung und Geschwindigkeit des Benutzers im 3D-Raum darstellt. Die Positionen aller Emitter im Diagramm sind relativ zur Position und Ausrichtung des Listenerobjekts. Standardmäßig befindet sich der Listener am Ursprung (0,0,0) und zeigt nach vorne entlang der Z-Achse. Sie können seine Position und Ausrichtung jedoch mit den Eigenschaften Position und Orientation festlegen.

deviceOutputNode.Listener.Position = new System.Numerics.Vector3(100, 0, 0);
deviceOutputNode.Listener.Orientation = System.Numerics.Quaternion.CreateFromYawPitchRoll(0, (float)Math.PI, 0);

Sie können die Position, Geschwindigkeit und Richtung von Emittern zur Laufzeit aktualisieren, um die Bewegung einer Audioquelle über den 3D-Raum zu simulieren.

var emitter = fileInputNode.Emitter;
emitter.Position = newObjectPosition;
emitter.DopplerVelocity = newObjectPosition - oldObjectPosition;

Sie können auch die Position, Geschwindigkeit und Ausrichtung des Listenerobjekts zur Laufzeit aktualisieren, um die Bewegung des Benutzers über den 3D-Raum zu simulieren.

deviceOutputNode.Listener.Position = newUserPosition;

Standardmäßig wird räumliches Audio mithilfe des HRTF-Algorithmus (Head-Relative Transfer Function) von Microsoft berechnet, um das Audio basierend auf seiner Form, Geschwindigkeit und Position relativ zum Zuhörer zu dämpfen. Sie können die SpatialAudioModel-Eigenschaft auf FoldDown festlegen, um eine einfache Stereomixmethode zum Simulieren von räumlichem Audio zu verwenden, das weniger genau ist, erfordert jedoch weniger CPU- und Arbeitsspeicherressourcen.

Weitere Informationen