Audiografieken

In dit artikel wordt beschreven hoe u de API's gebruikt in de Windows. Media.Audio naamruimte om audiografieken te maken voor scenario's voor audioroutering, mixen en verwerken.

Een audiografiek is een set onderling verbonden audioknooppunten waarmee audiogegevens stromen.

  • Audio-invoerknooppunten leveren audiogegevens aan de grafiek van audio-invoerapparaten, audiobestanden of van aangepaste code.

  • Audio-uitvoerknooppunten zijn de bestemming voor audio die door de grafiek wordt verwerkt. Audio kan uit de grafiek worden gerouteerd naar audio-uitvoerapparaten, audiobestanden of aangepaste code.

  • Submix-knooppunten nemen audio van een of meer knooppunten over en combineren ze in één uitvoer die kan worden gerouteerd naar andere knooppunten in de grafiek.

Nadat alle knooppunten zijn gemaakt en de verbindingen tussen deze knooppunten zijn ingesteld, start u gewoon de audiografiek en de audiogegevens stromen van de invoerknooppunten, via submixknooppunten, naar de uitvoerknooppunten. Dit model maakt scenario's zoals opnemen van de microfoon van een apparaat naar een audiobestand, het afspelen van audio van een bestand naar de luidspreker van een apparaat of het mixen van audio uit meerdere bronnen snel en eenvoudig te implementeren.

Aanvullende scenario's worden ingeschakeld met het toevoegen van audio-effecten aan de audiografiek. Elk knooppunt in een audiografiek kan worden gevuld met nul of meer audio-effecten die audioverwerking uitvoeren op de audio die via het knooppunt wordt doorgegeven. Er zijn verschillende ingebouwde effecten zoals echo, equalizer, beperken en reverb die met slechts enkele regels code aan een audioknooppunt kunnen worden gekoppeld. U kunt ook uw eigen aangepaste audio-effecten maken die precies hetzelfde werken als de ingebouwde effecten.

Kiezen tussen Windows Runtime AudioGraph en XAudio2

De Windows Runtime API's voor audiografiek bieden functionaliteit die ook kan worden geïmplementeerd met behulp van com-gebaseerde XAudio2-API's. Hieronder vindt u functies van het Windows Runtime framework voor audiografiek die verschillen van XAudio2.

De Windows Runtime API's voor audiografiek:

  • Zijn aanzienlijk gemakkelijker te gebruiken dan XAudio2.
  • Kan vanuit C# worden gebruikt en wordt ook ondersteund voor C++.
  • Kan audiobestanden, inclusief gecomprimeerde bestandsindelingen, rechtstreeks gebruiken. XAudio2 werkt alleen op audiobuffers en biedt geen I/O-mogelijkheden voor bestanden.
  • Kan de audiopijplijn met lage latentie gebruiken in Windows.
  • Ondersteuning voor automatisch schakelen tussen eindpunten wanneer standaardeindpuntparameters worden gebruikt. Als de gebruiker bijvoorbeeld overschakelt van de luidspreker van een apparaat naar een headset, wordt de audio automatisch omgeleid naar de nieuwe uitvoer.

AudioGraph-klasse

De klasse AudioGraph is het bovenliggende element van alle knooppunten waaruit de grafiek bestaat. Gebruik dit object om exemplaren van alle typen audioknooppunten te maken. Maak een exemplaar van de klasse AudioGraph door een AudioGraphSettings-object met configuratie-instellingen voor de grafiek te initialiseren, en roept u AudioGraph.CreateAsync aan. De geretourneerde CreateAudioGraphResult geeft toegang tot de gemaakte audiografiek of geeft een foutwaarde als het maken van een audiografiek mislukt.

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

    audioGraph = result.Graph;
}
  • Alle typen audioknooppunten worden gemaakt met behulp van de methoden Create* van de klasse AudioGraph .

  • De methode AudioGraph.Start zorgt ervoor dat de audiografiek begint met het verwerken van audiogegevens. De methode AudioGraph.Stop stopt de audioverwerking. Elk knooppunt in de grafiek kan onafhankelijk van elkaar worden gestart en gestopt terwijl de grafiek wordt uitgevoerd, maar er zijn geen knooppunten actief wanneer de grafiek wordt gestopt. ResetAllNodes zorgt ervoor dat alle knooppunten in de graaf alle data die zich momenteel in hun audiobuffers bevinden, weggooien.

  • De gebeurtenis QuantumStarted vindt plaats wanneer de grafiek de verwerking van een nieuw kwantum van audiogegevens start. De gebeurtenis QuantumProcessed vindt plaats wanneer de verwerking van een kwantum is voltooid.

  • De enige eigenschap AudioGraphSettings vereist is AudioRenderCategory. Als u deze waarde opgeeft, kan het systeem de audiopijplijn voor de opgegeven categorie optimaliseren.

  • De kwantumgrootte van de audiografiek bepaalt het aantal voorbeelden dat tegelijk wordt verwerkt. Standaard is de kwantumgrootte 10 ms gebaseerd op de standaard samplefrequentie. Als u een aangepaste kwantumgrootte opgeeft door de eigenschap DesiredSamplesPerQuantum in te stellen, moet u ook de eigenschap QuantumSizeSelectionMode instellen op ClosestToDesired of de opgegeven waarde wordt genegeerd. Als deze waarde wordt gebruikt, kiest het systeem zo dicht mogelijk bij de kwantumgrootte die u opgeeft. Als u de werkelijke kwantumgrootte wilt bepalen, controleert u de SamplesPerQuantum van de AudioGraph nadat deze is gemaakt.

  • Als u alleen van plan bent om de audiografiek met bestanden te gebruiken en niet van plan bent om naar een audioapparaat uit te voeren, is het raadzaam om de standaard kwantumgrootte te gebruiken door de eigenschap DesiredSamplesPerQuantum niet in te stellen.

  • De eigenschap DesiredRenderDeviceAudioProcessing bepaalt de hoeveelheid verwerking die het primaire renderapparaat uitvoert op de uitvoer van de audiografiek. Met de standaardinstelling kan het systeem de standaardaudioverwerking gebruiken voor de opgegeven categorie voor audioweergave. Deze verwerking kan het geluid van audio op sommige apparaten aanzienlijk verbeteren, met name mobiele apparaten met kleine luidsprekers. De raw-instelling kan de prestaties verbeteren door de hoeveelheid uitgevoerde signaalverwerking te minimaliseren, maar kan leiden tot inferieure geluidskwaliteit op sommige apparaten.

  • Als QuantumSizeSelectionMode is ingesteld op LowestLatency, gebruikt de audiografiek automatisch Raw voor DesiredRenderDeviceAudioProcessing.

  • U kunt de eigenschap AudioGraphSettings.MaxPlaybackSpeedFactor instellen op een maximumwaarde die wordt gebruikt voor de eigenschappen AudioFileInputNode.PlaybackSpeedFactor, AudioFrameInputNode.PlaybackSpeedFactor en MediaSourceInputNode.PlaybackSpeedFactor . Wanneer een audiografiek ondersteuning biedt voor een afspeelsnelheidfactor die groter is dan 1, moet het systeem extra geheugen toewijzen om een voldoende buffer van audiogegevens te behouden. Daarom wordt het geheugenverbruik van uw app verminderd door MaxPlaybackSpeedFactor in te stellen op de laagste waarde die door uw app is vereist. Als uw app alleen inhoud op normale snelheid afspeelt, wordt u aangeraden MaxPlaybackSpeedFactor in te stellen op 1.

  • De EncodingProperties bepaalt de audio-indeling die door de grafiek wordt gebruikt. Er worden alleen 32-bits floatindelingen ondersteund.

  • De PrimaryRenderDevice stelt het primaire renderapparaat voor de audiografiek in. Als u dit niet instelt, wordt het standaardsysteemapparaat gebruikt. Het primaire renderapparaat wordt gebruikt om de kwantumgrootten voor andere knooppunten in de grafiek te berekenen. Als er geen audio-renderapparaten aanwezig zijn op het systeem, mislukt het maken van audiografiek.

U kunt de audiografiek het standaardaudioweergaveapparaat laten gebruiken of de klasse Windows.Devices.Enumeration.DeviceInformation gebruiken om een lijst op te halen van de audioweergaveapparaten die beschikbaar zijn op het systeem door FindAllAsync aan te roepen en de audioweergaveapparaatselector door te geven die wordt geretourneerd door Windows.Media.Devices.MediaDevice.GetAudioRenderSelector. U kunt programmatisch een van de geretourneerde DeviceInformation-objecten kiezen of de gebruikersinterface weergeven om de gebruiker toe te staan een apparaat te selecteren en vervolgens te gebruiken om de eigenschap PrimaryRenderDevice in te stellen.

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;

Apparaatinvoerknooppunt

Een apparaatinvoerknooppunt voert audio door in de grafiek van een audio-opnameapparaat dat is verbonden met het systeem, zoals een microfoon. Maak een DeviceInputNode dat gebruikmaakt van het standaardapparaat voor audioopname van het systeem door CreateDeviceInputNodeAsync aan te roepen. Geef een MediaCategory op zodat het systeem de audiopijplijn voor de opgegeven categorie kan optimaliseren.

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

Als u voor het apparaatinvoerknooppunt een specifiek audio-opnameapparaat wilt opgeven, kunt u de klasse Windows.Devices.Enumeration.DeviceInformation gebruiken om een lijst op te halen van de audio-opnameapparaten die op het systeem beschikbaar zijn, door FindAllAsync aan te roepen en de apparaatselector voor audio-opname door te geven die wordt geretourneerd door Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector. U kunt een van de geretourneerde DeviceInformation-objecten programmatisch kiezen of de gebruikersinterface weergeven om de gebruiker toe te staan een apparaat te selecteren en dit vervolgens door te geven aan CreateDeviceInputNodeAsync.

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

Uitvoerknooppunt van apparaat

Een uitvoerknooppunt van een apparaat pusht audio van de grafiek naar een audio-renderapparaat, zoals luidsprekers of een headset. Maak een DeviceOutputNode door CreateDeviceOutputNodeAsync aan te roepen. Het uitvoerknooppunt maakt gebruik van de PrimaryRenderDevice van de audiografiek.

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

Bestandsinvoerknooppunt

Met een bestandsinvoerknooppunt kunt u gegevens uit een audiobestand invoeren in de grafiek. Maak een AudioFileInputNode door CreateFileInputNodeAsync aan te roepen.

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;
    WinRT.Interop.InitializeWithWindow.Initialize(filePicker, _hwnd);
    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());
        return;
    }

    fileInputNode = result.FileInputNode;
}
  • Bestandsinvoerknooppunten ondersteunen de volgende bestandsindelingen: mp3, wav, wma, m4a.
  • Stel de eigenschap StartTime in om de tijdsverschil op te geven in het bestand waar het afspelen moet beginnen. Als deze eigenschap null is, wordt het begin van het bestand gebruikt. Stel de eigenschap EndTime in om de tijdsverschil op te geven in het bestand waar het afspelen moet eindigen. Als deze eigenschap null is, wordt het einde van het bestand gebruikt. De begintijdwaarde moet lager zijn dan de eindtijdwaarde en de eindtijdwaarde moet kleiner zijn dan of gelijk zijn aan de duur van het audiobestand. Dit kan worden bepaald door de waarde van de eigenschap Duur te controleren.
  • Zoek naar een positie in het audiobestand door zoeken aan te roepen en de tijdsverschil op te geven in het bestand waarnaar de afspeelpositie moet worden verplaatst. De opgegeven waarde moet binnen het bereik StartTime en EndTime vallen. Haal de huidige afspeelpositie van het knooppunt op met de alleen-lezeneigenschap Position.
  • Schakel lusing van het audiobestand in door de eigenschap LoopCount in te stellen. Als deze waarde niet null is, geeft deze waarde het aantal keren aan dat het bestand wordt afgespeeld na het initiële afspelen. Als u LoopCount bijvoorbeeld instelt op 1, wordt het bestand in totaal 2 keer afgespeeld en wordt het bestand ingesteld op 5, waardoor het bestand in totaal 6 keer wordt afgespeeld. Door LoopCount in te stellen op null, wordt het bestand oneindig herhaald. Om het herhalen te stoppen, stelt u de waarde in op 0.
  • Pas de snelheid aan waarmee het audiobestand wordt afgespeeld door de PlaybackSpeedFactor in te stellen. Een waarde van 1 geeft de oorspronkelijke snelheid van het bestand aan, .5 is halve snelheid en 2 is dubbele snelheid.

MediaSource-invoerknooppunt

De klasse MediaSource biedt een algemene manier om te verwijzen naar media uit verschillende bronnen en biedt een algemeen model voor toegang tot mediagegevens, ongeacht de onderliggende media-indeling, die een bestand op schijf, een stroom of een adaptieve streamingnetwerkbron kan zijn. Met een MediaSourceAudioInputNode-knooppunt kunt u audiogegevens van een MediaSource omsturen naar de audiografiek. Maak een MediaSourceAudioInputNode door CreateMediaSourceAudioInputNodeAsync aan te roepen en door te geven in een MediaSource-object dat de inhoud vertegenwoordigt die u wilt afspelen. Er wordt een CreateMediaSourceAudioInputNodeResult geretourneerd die u kunt gebruiken om de status van de bewerking te bepalen door de eigenschap Status te controleren. Als de status Geslaagd is, kunt u de gemaakte MediaSourceAudioInputNode ophalen door toegang te krijgen tot de eigenschap Node . In het volgende voorbeeld ziet u het maken van een knooppunt van een AdaptiveMediaSource-object dat inhoudstreaming via het netwerk vertegenwoordigt.

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

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

    MediaSource 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;
}

Als u een melding wilt ontvangen wanneer het afspelen het einde van de MediaSource-inhoud heeft bereikt, registreert u een handler voor de gebeurtenis MediaSourceCompleted .

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

Hoewel het afspelen van een bestand vanaf schijf waarschijnlijk altijd is voltooid, kunnen media die vanuit een netwerkbron worden gestreamd tijdens het afspelen mislukken vanwege een wijziging in de netwerkverbinding of andere problemen die buiten het beheer van de audiografiek vallen. Als een MediaSource tijdens het afspelen niet meer kan worden afgespeeld, activeert de audiograf de gebeurtenis UnrecoverableErrorOccurred. U kunt de handler voor deze gebeurtenis gebruiken om de audiografiek te stoppen en te verwijderen en vervolgens uw grafiek opnieuw te initialiseren.

if (audioGraph != null)
{
    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();
    }
}

Knooppunt voor bestandsuitvoer

Met een uitvoerknooppunt voor bestanden kunt u audiogegevens uit de grafiek omsturen naar een audiobestand. Maak een AudioFileOutputNode door CreateFileOutputNodeAsync aan te roepen.

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

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

    MediaEncodingProfile mediaEncodingProfile;
    switch (file.FileType.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;
}

Invoerknooppunt voor audioframes

Met een invoerknooppunt voor audioframes kunt u audiogegevens die u in uw eigen code genereert, naar de audiografiek pushen. Dit maakt scenario's mogelijk, zoals het maken van een aangepaste softwaresynthesizer. Maak een AudioFrameInputNode door CreateFrameInputNode aan te roepen.

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

De gebeurtenis FrameInputNode.QuantumStarted wordt gegenereerd wanneer de audiografiek klaar is om de volgende kwantum van audiogegevens te verwerken. U levert uw aangepaste gegenereerde audiogegevens van binnen de handler aan deze gebeurtenis.

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);
    }
}
  • Het FrameInputNodeQuantumStartedEventArgs-object dat aan de gebeurtenishandler voor QuantumStarted wordt doorgegeven, biedt toegang tot de eigenschap RequiredSamples, die aangeeft hoeveel samples de audiografiek nodig heeft om het te verwerken quantum te vullen.
  • Roep AudioFrameInputNode.AddFrame aan om een AudioFrame-object door te geven dat is gevuld met audiogegevens in de grafiek.
  • U kunt MediaFrameReader met audiogegevens gebruiken om AudioFrame-objecten op te halen uit een mediaframebron, die kunnen worden doorgegeven aan een FrameInputNode met behulp van de methode AddFrame .
  • Hieronder ziet u een voorbeeld van de implementatie van de helpermethode GenerateAudioData .

Als u een AudioFrame met audiogegevens wilt vullen, moet u toegang krijgen tot de onderliggende geheugenbuffer van het audioframe. Hiervoor initialiseert u de IMemoryBufferByteAccess COM-interface, zoals hieronder wordt weergegeven.

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

De volgende code toont een voorbeeldimplementatie van een GenerateAudioData helpermethode waarmee een AudioFrame wordt gemaakt en gevuld met audiogegevens.

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 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;
}
  • Omdat deze methode toegang heeft tot de onbewerkte buffer onder de Windows Runtime typen, moet deze worden gedeclareerd met behulp van het trefwoord unsafe. U moet uw project ook configureren in Microsoft Visual Studio om de compilatie van onveilige code toe te staan door de eigenschappenpagina Eigenschap van het project te openen, op de eigenschappenpagina Build te klikken en het selectievakje Allow Onveilige code in te schakelen.
  • Initialiseer een nieuw exemplaar van AudioFrame in de Windows. Media naamruimte door de gewenste buffergrootte door te geven aan de constructor. De buffergrootte is het aantal monsters vermenigvuldigd met de grootte van elke steekproef.
  • Haal de AudioBuffer van het audioframe op door LockBuffer aan te roepen.
  • Haal een exemplaar van de IMemoryBufferByteAccess COM-interface op uit de audiobuffer door CreateReference aan te roepen.
  • Haal een aanwijzer naar onbewerkte audiobufferdata op door IMemoryBufferByteAccess.GetBuffer aan te roepen en cast deze naar het samplegegevenstype van de audiogegevens.
  • Vul de buffer met gegevens en retourneer de AudioFrame voor verzending in de audiografiek.

Uitvoerknooppunt voor audioframes

Met een audioframe-uitvoerknooppunt kunt u audiogegevensuitvoer ontvangen en verwerken vanuit de audiografiek met aangepaste code die u maakt. Een voorbeeldscenario hiervoor is het uitvoeren van signaalanalyse op de audio-uitvoer. Maak een AudioFrameOutputNode door CreateFrameOutputNode aan te roepen.

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

De gebeurtenis AudioGraph.QuantumStarted wordt gegenereerd wanneer de audiografiek begint met het verwerken van een kwantum van audiogegevens. U hebt toegang tot de audiogegevens vanuit de handler voor deze gebeurtenis.

Opmerking

Als u audioframes met regelmatige tussenpozen wilt ophalen, gesynchroniseerd met de audiografiek, roept u AudioFrameOutputNode.GetFrame aan in de synchrone gebeurtenishandler QuantumStarted. De QuantumProcessed-gebeurtenis wordt asynchroon gegenereerd nadat de audio-engine de audioverwerking heeft voltooid, wat betekent dat de frequentie onregelmatig kan zijn. Daarom moet u de QuantumProcessed-gebeurtenis niet gebruiken voor gesynchroniseerde verwerking van audioframegegevens.

private void AudioGraph_QuantumStarted(AudioGraph sender, object args)
{
    AudioFrame frame = frameOutputNode.GetFrame();
    ProcessFrameOutput(frame);
}
  • Roep GetFrame aan om een AudioFrame object met audiogegevens uit de grafiek op te halen.
  • Hieronder ziet u een voorbeeld van de implementatie van de helpermethode ProcessFrameOutput .
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;
    }
}
  • Net als in het bovenstaande voorbeeld van het invoerknooppunt voor audioframes moet u de IMemoryBufferByteAccess COM-interface declareren en uw project zo configureren dat onveilige code is toegestaan om toegang te krijgen tot de onderliggende audiobuffer.
  • Haal de AudioBuffer van het audioframe op door LockBuffer aan te roepen.
  • Haal een exemplaar van de IMemoryBufferByteAccess COM-interface op uit de audiobuffer door CreateReference aan te roepen.
  • Haal een aanwijzer naar ruwe audiobuffergegevens op door IMemoryBufferByteAccess.GetBuffer aan te roepen en cast deze naar het samplegegevenstype van de audiogegevens.

Knooppuntverbindingen en submixknooppunten

Alle invoerknooppunttypen maken de methode AddOutgoingConnection beschikbaar waarmee de audio die door het knooppunt wordt geproduceerd, wordt gerouteerd naar het knooppunt dat wordt doorgegeven aan de methode. In het volgende voorbeeld wordt een AudioFileInputNode verbonden met een AudioDeviceOutputNode, wat een eenvoudige configuratie is om een audiobestand af te spelen via de luidspreker van het apparaat.

fileInputNode.AddOutgoingConnection(deviceOutputNode);

U kunt meer dan één verbinding maken van een invoerknooppunt naar andere knooppunten. In het volgende voorbeeld wordt een andere verbinding van de AudioFileInputNode toegevoegd aan een AudioFileOutputNode. Nu wordt het geluid van het audiobestand afgespeeld op de luidspreker van het apparaat en wordt het ook naar een audiobestand geschreven.

fileInputNode.AddOutgoingConnection(fileOutputNode);

Uitvoerknooppunten kunnen ook meer dan één verbinding van andere knooppunten ontvangen. In het volgende voorbeeld wordt een verbinding gemaakt van een AudioDeviceInputNode naar het knooppunt AudioDeviceOutput. Omdat het uitvoerknooppunt verbindingen heeft van het bestandsinvoerknooppunt en het invoerknooppunt van het apparaat, bevat de uitvoer een combinatie van audio uit beide bronnen. AddOutgoingConnection biedt een overbelasting waarmee u een versterkingswaarde kunt opgeven voor het signaal dat via de verbinding wordt doorgegeven.

deviceInputNode.AddOutgoingConnection(deviceOutputNode, .5);

Hoewel uitvoerknooppunten verbindingen van meerdere knooppunten kunnen accepteren, kunt u een tussenliggende combinatie van signalen van een of meer knooppunten maken voordat u de mix doorgeeft aan een uitvoer. U kunt bijvoorbeeld het niveau instellen of effecten toepassen op een subset van de audiosignalen in een grafiek. Gebruik hiervoor de AudioSubmixNode. U kunt verbinding maken met een submixknooppunt vanaf een of meer invoerknooppunten of andere submixknooppunten. In het volgende voorbeeld wordt een nieuw submixknooppunt gemaakt met AudioGraph.CreateSubmixNode. Vervolgens worden verbindingen van een bestandsinvoerknooppunt en een frameinvoerknooppunt toegevoegd aan het submixknooppunt. Ten slotte is het submixknooppunt verbonden met een bestandsuitvoerknooppunt.

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

Starten en stoppen van audiografiekknooppunten

Wanneer AudioGraph.Start wordt aangeroepen, begint de audiografiek met het verwerken van audiogegevens. Elk knooppunttype biedt de methoden Start en Stop, waarmee het individuele knooppunt de verwerking van gegevens kan starten of stoppen. Wanneer AudioGraph.Stop wordt aangeroepen, wordt alle audioverwerking op alle knooppunten gestopt, ongeacht de status van afzonderlijke knooppunten, maar kan de status van elk knooppunt worden ingesteld terwijl de audiografiek wordt gestopt. U kunt bijvoorbeeld Stoppen aanroepen op een afzonderlijk knooppunt terwijl de grafiek is gestopt en vervolgens AudioGraph.Start aanroepen en het afzonderlijke knooppunt de status Gestopt blijft.

Alle knooppunttypen hebben de eigenschap ConsumeInput, die, wanneer deze is ingesteld op false, het knooppunt toestaat de audioverwerking voort te zetten, maar verhindert dat het audiogegevens consumeert die door andere knooppunten worden ingevoerd.

Alle knooppunttypen maken de methode Reset beschikbaar die ervoor zorgt dat het knooppunt alle audiogegevens die zich momenteel in de buffer bevinden, negeert.

Audio-effecten toevoegen

Met de API voor audiografiek kunt u audio-effecten toevoegen aan elk type knooppunt in een grafiek. Uitvoerknooppunten, invoerknooppunten en submixknooppunten kunnen elk een onbeperkt aantal audio-effecten hebben, beperkt door de mogelijkheden van de hardware. In het volgende voorbeeld ziet u hoe u het ingebouwde echo-effect toevoegt aan een submixknooppunt.

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

submixNode.EffectDefinitions.Add(echoEffect);
  • Alle audio-effecten implementeren IAudioEffectDefinition. Elk knooppunt toont een eigenschap EffectDefinitions die de lijst met effecten vertegenwoordigt die op dat knooppunt zijn toegepast. Voeg een effect toe door het definitieobject toe te voegen aan de lijst.
  • Er zijn verschillende effectdefinitieklassen die worden opgegeven in de Windows. Media.Audio naamruimte. Dit zijn onder andere:
  • U kunt uw eigen audio-effecten maken die IAudioEffectDefinition implementeren en toepassen op elk knooppunt in een audiografiek.
  • Elk knooppunttype maakt een DisableEffectsByDefinition-methode beschikbaar waarmee alle effecten in de lijst EffectDefinitions van het knooppunt worden uitgeschakeld die zijn toegevoegd met behulp van de opgegeven definitie. EnableEffectsByDefinition schakelt de effecten in met de opgegeven definitie.

Ruimtelijke audio

AudioGraph ondersteunt ruimtelijke audio, waarmee u de locatie in 3D-ruimte kunt opgeven waaruit audio van elk invoer- of submixknooppunt wordt verzonden. U kunt ook een vorm en richting opgeven van waaruit audio wordt uitgezonden, een snelheid die wordt gebruikt om een dopplerverschuiving op de audio van het knooppunt toe te passen, en een vervalmodel definiëren dat beschrijft hoe de audio wordt gedempt naarmate de afstand toeneemt.

Als u een emitter wilt maken, kunt u eerst een vorm maken waarin het geluid van de emitter wordt geprojecteerd. Dit kan een kegel of omnidirectioneel zijn. De klasse AudioNodeEmitterShape biedt statische methoden voor het maken van elk van deze shapes. Maak vervolgens een vervalmodel. Hiermee bepaalt u hoe het volume van de audio van de emitter afneemt naarmate de afstand van de listener toeneemt. De CreateNatural-methode creëert een vervalmodel dat het natuurlijke verval van geluid nabootst met behulp van een model met kwadratische afname met de afstand. Maak ten slotte een AudioNodeEmitterSettings-object. Op dit moment wordt dit object alleen gebruikt om op snelheid gebaseerde Doppler-attenuatie van de audio van de emitter in en uit te schakelen. Roep de AudioNodeEmitter-constructor aan en geef de initialisatieobjecten door die u zojuist hebt gemaakt. De emitter wordt standaard bij de oorsprong geplaatst, maar u kunt de positie van de emitter instellen met de eigenschap Positie .

Opmerking

Emitters van audioknooppunten kunnen alleen audio verwerken die is geformatteerd in mono met een samplefrequentie van 48 kHz. Als u stereogeluid of audio met een andere samplefrequentie probeert te gebruiken, resulteert dit in een uitzondering.

U wijst de emitter toe aan een audioknooppunt wanneer u het maakt met behulp van de overbelaste aanmaakmethode voor het gewenste type knooppunt. In dit voorbeeld wordt CreateFileInputNodeAsync gebruikt om een bestandsinvoerknooppunt te maken op basis van een opgegeven bestand en het AudioNodeEmitter object dat u wilt koppelen aan het knooppunt.

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 Vector3(10, 0, 5);

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

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

fileInputNode = result.FileInputNode;

De AudioDeviceOutputNode die audio van de grafiek naar de gebruiker uitvoert, heeft een listenerobject. toegankelijk met de eigenschap Listener, die de locatie, stand en snelheid van de gebruiker in de 3D-ruimte vertegenwoordigt. De posities van alle emitters in de grafiek zijn relatief ten opzichte van de positie en stand van het listenerobject. Standaard bevindt de listener zich in de oorsprong (0,0,0), gericht naar voren langs de Z-as, maar u kunt de positie en oriëntatie instellen met de eigenschappen Position en Orientation.

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

U kunt de locatie, snelheid en richting van emitters tijdens runtime bijwerken om de beweging van een audiobron te simuleren via 3D-ruimte.

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

U kunt ook de locatie, snelheid en stand van het listenerobject tijdens runtime bijwerken om de verplaatsing van de gebruiker via 3D-ruimte te simuleren.

deviceOutputNode.Listener.Position = newUserPosition;

Ruimtelijke audio wordt standaard berekend met behulp van Microsoft HRTF-algoritme (head-relative transfer function) om de audio te attenueren op basis van de vorm, snelheid en positie ten opzichte van de listener. U kunt de eigenschap SpatialAudioModel instellen op FoldDown om een eenvoudige stereomixmethode te gebruiken om ruimtelijke audio te simuleren die minder nauwkeurig is, maar minder CPU- en geheugenbronnen vereist.

Zie ook