Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel wird gezeigt, wie Sie die APIs im Windows verwenden. Media.Audio Namespace zum Erstellen von Audiodiagrammen für Audiorouting-, Misch- und Verarbeitungsszenarien.
Ein Audiodiagramm ist eine Reihe miteinander verbundener Audioknoten, über die Audiodaten fließen.
Audioeingabeknoten liefern Audiodaten aus Audioeingabegeräten, Audiodateien oder aus benutzerdefiniertem Code an das Diagramm.
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, Limiter und Hall, die mit nur wenigen Codezeilen einem Audioknoten zugewiesen werden können. Sie können auch eigene benutzerdefinierte Audioeffekte erstellen, die genau mit den integrierten Effekten funktionieren.
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 zusätzlich zur Unterstützung für C++ auch in C# verwendet werden.
- Kann Audiodateien, einschließlich komprimierter Dateiformate, direkt verwenden. XAudio2 funktioniert nur auf Audiopuffern und bietet keine Datei-E/A-Funktionen.
- Kann die Audiopipeline mit geringer Latenz in Windows 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 Ausgabe umgeleitet.
AudioGraph-Klasse
Die klasse AudioGraph 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 AudioGraphKlasse, indem Sie eine AudioGraphSettingsobjekt mit Konfigurationseinstellungen für das Diagramm initialisieren, und dann AudioGraph.CreateAsync aufrufen. Die 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);
return;
}
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 derzeit 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 AudioGraphSettings-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, prüfen Sie SamplesPerQuantum von AudioGraph, nachdem es erstellt wurde.
Wenn Sie nur das Audiodiagramm mit Dateien verwenden möchten und nicht für ein Audiogerät ausgeben möchten, 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 Standardaudioverarbeitung für die angegebene Audiowiedergabekategorie zu verwenden. Diese Verarbeitung kann den Sound von Audio auf einigen Geräten erheblich verbessern, insbesondere mobile Geräte 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 LowestLatency festgelegt ist, verwendet das Audiodiagramm automatisch Raw für DesiredRenderDeviceAudioProcessing.
Sie können die Eigenschaft "AudioGraphSettings.MaxPlaybackSpeedFactor " festlegen, um einen Maximalwert festzulegen, der für die Eigenschaften AudioFileInputNode.PlaybackSpeedFactor, AudioFrameInputNode.PlaybackSpeedFactor und MediaSourceInputNode.PlaybackSpeedFactor verwendet wird. Wenn ein Audiodiagramm einen Wiedergabegeschwindigkeitsfaktor unterstützt, der größer als 1 ist, muss das System zusätzlichen Arbeitsspeicher zuweisen, um einen ausreichenden Puffer von Audiodaten aufrechtzuerhalten. Aus diesem Grund wird das Festlegen von MaxPlaybackSpeedFactor auf den niedrigsten Wert, der für Ihre App erforderlich ist, den Speicherverbrauch Ihrer App verringern. Wenn Ihre App Inhalte nur mit normaler Geschwindigkeit wiedergibt, empfiehlt es sich, MaxPlaybackSpeedFactor auf 1 festzulegen.
Die EncodingProperties bestimmt 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 Standard-Audiowiedergabegerät verwenden lassen oder die Klasse Windows.Devices.Enumeration.DeviceInformation verwenden, um eine Liste der im System verfügbaren Audiowiedergabegeräte abzurufen, indem Sie FindAllAsync aufrufen und den von Windows.Media.Devices.MediaDevice.GetAudioRenderSelector zurückgegebenen Audiowiedergabegeräte-Selektor übergeben. Sie können eins der zurückgegebenen DeviceInformation-Objekte programmgesteuert auswählen oder die Benutzeroberfläche anzeigen, damit der Benutzer ein Gerät auswählen und es dann verwenden kann, um die PrimaryRenderDevice-Eigenschaft festzulegen.
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;
Eingabeknoten des Geräts
Ein Geräteeingangsknoten führt dem Graphen Audiodaten von einem mit dem System verbundenen Audioaufnahmegerät zu, beispielsweise von einem Mikrofon. Erstellen Sie ein DeviceInputNode-Objekt, das das Standardaudioaufnahmegerät des Systems verwendet, indem Sie CreateDeviceInputNodeAsync aufrufen. Stellen Sie eine MediaCategory 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 für den Geräteeingabeknoten ein bestimmtes Audioaufnahmegerät angeben möchten, können Sie die Klasse Windows.Devices.Enumeration.DeviceInformation verwenden, um eine Liste der im System verfügbaren Audioaufnahmegeräte abzurufen, indem Sie FindAllAsync aufrufen und den von Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector zurückgegebenen Selektor für Audioaufnahmegeräte ü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 eine DeviceOutputNode, indem Sie CreateDeviceOutputNodeAsync aufrufen. Der Ausgabeknoten verwendet das PrimaryRenderDevice des Audiographen.
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;
}
Datei-Eingabeknoten
Mit einem Dateieingabeknoten können Sie Daten aus einer Audiodatei in das Diagramm übertragen. Erstellen Sie eine 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;
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;
}
- 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 enden 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 Duration-Eigenschaft bestimmt werden kann.
- Suchen Sie eine Position in der Audiodatei, indem Sie Seek aufrufen und den Zeitversatz in die Datei angeben, in die die Wiedergabeposition 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 Wiedergabe der Audiodatei in Schleife, indem Sie die Eigenschaft LoopCount festlegen. Wenn kein Nullwert 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 klasse MediaSource bietet eine gemeinsame Möglichkeit, auf Medien aus verschiedenen Quellen zu verweisen und ein allgemeines Modell für den Zugriff auf Mediendaten verfügbar zu machen, unabhängig vom zugrunde liegenden Medienformat, bei dem es sich um eine Datei auf einem Datenträger, einem Stream oder einer adaptiven Streaming-Netzwerkquelle handeln kann. Mit einem MediaSourceAudioInputNode-Knoten können Sie Audiodaten aus einer MediaSource in das Audiodiagramm leiten. Erstellen Sie eine MediaSourceAudioInputNode durch Aufrufen von CreateMediaSourceAudioInputNodeAsync, indem Sie 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 die erstellte MediaSourceAudioInputNode abrufen, indem Sie auf die Node-Eigenschaft zugreifen. Das folgende Beispiel zeigt die Erstellung eines Knotens aus einem AdaptiveMediaSource-Objekt, das das Streaming von Inhalten über das Netzwerk darstellt.
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;
}
Um eine Benachrichtigung zu erhalten, wenn die Wiedergabe das Ende des MediaSource-Inhalts erreicht hat, registrieren Sie einen Handler für das MediaSourceCompleted-Ereignis .
mediaSourceInputNode.MediaSourceCompleted += MediaSourceInputNode_MediaSourceCompleted;
private void MediaSourceInputNode_MediaSourceCompleted(MediaSourceAudioInputNode sender, object args)
{
audioGraph.Stop();
}
Während die Wiedergabe einer Datei vom Datenträger wahrscheinlich immer erfolgreich abgeschlossen wird, schlägt die Wiedergabe von Medien, die von einer Netzwerkquelle gestreamt wurden, möglicherweise aufgrund einer Änderung der Netzwerkverbindung oder anderer Probleme fehl, die außerhalb der Kontrolle des Audiodiagramms liegen. Wenn eine MediaSource während der Wiedergabe nicht mehr wiedergegeben werden kann, löst der Audiograph 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.
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();
}
}
Dateiausgabeknoten
Mit einem Dateiausgabeknoten können Sie Audiodaten aus dem Diagramm in eine Audiodatei leiten. Erstellen Sie eine AudioFileOutputNode, indem Sie CreateFileOutputNodeAsync aufrufen.
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;
}
- Dateiausgabeknoten unterstützen die folgenden Dateiformate: mp3, wav, wma, m4a.
- Sie müssen AudioFileOutputNode.Stop aufrufen, um die Verarbeitung des Knotens zu beenden, bevor AudioFileOutputNode.FinalizeAsync aufgerufen wird oder eine Ausnahme ausgelöst wird.
Audio-Frame-Eingabeknoten
Mit einem Audio-Frame-Eingabeknoten können Sie Audiodaten, die Sie in Ihrem eigenen Code generieren, in den Audiographen einspeisen. Dies ermöglicht Szenarien wie das Erstellen eines benutzerdefinierten Softwaresynthesizers. Erstellen Sie eine AudioFrameInputNode, indem Sie CreateFrameInputNode aufrufen.
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 stellen diesem Ereignis im Handler Ihre benutzerdefiniert generierten Audiodaten bereit.
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 FrameInputNodeQuantumStartedEventArgs-Objekt, das an den QuantumStarted-Ereignishandler übergeben wird, stellt die RequiredSamples-Eigenschaft bereit, die angibt, wie viele Samples der Audiograph benötigt, um das zu verarbeitende Quantum zu füllen.
- Rufen Sie AudioFrameInputNode.AddFrame auf, um ein AudioFrame-Objekt zu übergeben, das mit Audiodaten in das Diagramm gefüllt ist.
- Sie können MediaFrameReader mit Audiodaten verwenden, um AudioFrame-Objekte aus einer Medienframequelle abzurufen, die mithilfe der AddFrame-Methode an einen FrameInputNode übergeben werden kann.
- Nachfolgend sehen Sie eine Beispielimplementierung der GenerateAudioData-Hilfsmethode .
Zum Auffüllen eines AudioFrame mit Audiodaten müssen Sie Zugriff auf den zugrunde liegenden Speicherpuffer des Audioframes erhalten. Initialisieren Sie dazu die IMemoryBufferByteAccess COM-Schnittstelle wie unten dargestellt.
[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 eine AudioFrame erstellt und mit Audiodaten auffü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 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 Schlüsselworts unsafe deklariert werden. Sie müssen Ihr Projekt außerdem in Microsoft Visual Studio so konfigurieren, dass die Kompilierung von unsicherem Code zugelassen wird, indem Sie die Seite Properties des Projekts öffnen, auf die Eigenschaftenseite Build klicken und das Kontrollkästchen Allow Unsafe Code aktivieren.
- Initialisieren Sie eine neue Instanz von AudioFrame im Windows. Media Namespace, indem die gewünschte Puffergröße an den Konstruktor übergeben wird. Die Puffergröße ist die Anzahl der Stichproben, die mit der Größe der einzelnen Stichproben multipliziert werden.
- Rufen Sie die AudioBuffer des Audioframes ab, indem Sie LockBuffer aufrufen.
- Rufen Sie eine Instanz der IMemoryBufferByteAccess-COM-Schnittstelle aus dem Audiopuffer ab, indem Sie CreateReference aufrufen.
- Rufen Sie einen Zeiger auf unformatierte Audiopufferdaten ab, indem Sie IMemoryBufferByteAccess.GetBuffer aufrufen und in den Beispieldatentyp der Audiodaten umwandeln.
- Füllen Sie den Puffer mit Daten, und geben Sie den AudioFrame zur Übergabe an den Audiographen zurück.
Audio-Frame-Ausgabeknoten
Mit einem Audioframe-Ausgabeknoten können Sie die aus dem Audiographen ausgegebenen Audiodaten mit benutzerdefiniertem Code, den Sie erstellen, empfangen und verarbeiten. Ein Beispielszenario hierfür ist das Ausführen einer Signalanalyse für die Audioausgabe. Erstellen Sie eine AudioFrameOutputNode, indem Sie CreateFrameOutputNode aufrufen.
AudioFrameOutputNode frameOutputNode;
private void CreateFrameOutputNode()
{
frameOutputNode = audioGraph.CreateFrameOutputNode();
audioGraph.QuantumStarted += AudioGraph_QuantumStarted;
}
Das ereignis AudioGraph.QuantumStarted 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.
Note
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 QuantumProcessed-Ereignis nicht für die synchronisierte Verarbeitung von Audioframedaten verwenden.
private void AudioGraph_QuantumStarted(AudioGraph sender, object args)
{
AudioFrame frame = frameOutputNode.GetFrame();
ProcessFrameOutput(frame);
}
- Rufen Sie GetFrame auf, um ein AudioFrame-Objekt abzurufen, das mit Audiodaten aus dem Diagramm gefüllt ist.
- 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.
- Rufen Sie die AudioBuffer des Audioframes ab, indem Sie LockBuffer aufrufen.
- Rufen Sie eine Instanz der IMemoryBufferByteAccess-COM-Schnittstelle aus dem Audiopuffer ab, indem Sie CreateReference aufrufen.
- Rufen Sie einen Zeiger auf die Rohdaten des Audiopuffers ab, indem Sie IMemoryBufferByteAccess.GetBuffer aufrufen und ihn in den Datentyp der Audiosamples umwandeln.
Knotenverbindungen und Submixknoten
Alle Eingabeknotentypen stellen die Methode AddOutgoingConnection bereit, mit der das vom Knoten erzeugte Audiosignal an den Knoten weitergeleitet wird, der der Methode übergeben wird. Im folgenden Beispiel wird ein AudioFileInputNode mit einer AudioDeviceOutputNode verbunden. Dies ist ein einfaches Setup für die Wiedergabe einer Audiodatei auf dem Lautsprecher des Geräts.
fileInputNode.AddOutgoingConnection(deviceOutputNode);
Sie können mehrere Verbindungen von einem Eingabeknoten zu anderen Knoten erstellen. Im folgenden Beispiel wird eine weitere Verbindung vom AudioFileInputNode zu einem AudioFileOutputNode hinzugefügt. Jetzt wird das Audio aus der Audiodatei über den Lautsprecher des Geräts wiedergegeben und ebenfalls 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 mit dem 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 die 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 von einem Datei-Eingabeknoten und einem Frame-Eingabeknoten 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 Beenden von Audiodiagrammknoten
Wenn AudioGraph.Start aufgerufen wird, beginnt das Audiodiagramm mit der Verarbeitung von Audiodaten. Jeder Knotentyp stellt Start- und Stop-Methoden bereit, die dazu führen, dass der einzelne Knoten die Verarbeitung von Daten startet oder beendet. 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 das Diagramm beendet wird, und dann AudioGraph.Start aufrufen, und der einzelne Knoten verbleibt im angehaltenen Zustand.
Alle Knotentypen verfügen über die Eigenschaft ConsumeInput, die es dem Knoten, wenn sie auf false gesetzt ist, ermöglicht, die Audioverarbeitung fortzusetzen, ihn jedoch daran hindert, von anderen Knoten eingespeiste Audiodaten zu verarbeiten.
Alle Knotentypen machen die Reset-Methode verfügbar, die bewirkt, dass der Knoten alle Audiodaten verwirft, die sich derzeit im Puffer befinden.
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 Klassen für Effektdefinitionen, die im Namespace Windows.Media.Audio bereitgestellt werden. Dazu gehören:
- Sie können eigene Audioeffekte erstellen, die IAudioEffectDefinition implementieren und auf einen beliebigen Knoten in einem Audiodiagramm anwenden.
- Jeder Knotentyp macht eine DisableEffectsByDefinition-Methode verfügbar, die alle Effekte in der EffectDefinitions-Liste des Knotens deaktiviert, die mithilfe der angegebenen Definition hinzugefügt wurden. EnableEffectsByDefinition aktiviert die Effekte mit der angegebenen Definition.
Räumliche Audiowiedergabe
AudioGraph unterstützt 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, mit der Doppler die Audiodaten des Knotens verschiebt, und ein Verfallsmodell definieren, das beschreibt, wie das Audio 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 klasse AudioNodeEmitterShape 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.
Note
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 AudioNodeEmitterobjekt 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 Vector3(10, 0, 5);
CreateAudioFileInputNodeResult result = await audioGraph.CreateFileInputNodeAsync(file, emitter);
if (result.Status != AudioFileNodeCreationStatus.Success)
{
ShowErrorMessage(result.Status.ToString());
return;
}
fileInputNode = result.FileInputNode;
Die AudioDeviceOutputNode, die Audiodaten aus dem Diagramm an den Benutzer ausgibt, verfügt über ein Listenerobjekt, zugriff mit der eigenschaft Listener, 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), der entlang der Z-Achse nach vorne gerichtet ist, Sie können jedoch seine Position und Ausrichtung mit den Eigenschaften Position und Ausrichtung festlegen.
deviceOutputNode.Listener.Position = new Vector3(100, 0, 0);
deviceOutputNode.Listener.Orientation = 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.
AudioNodeEmitter 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 mit dem Head-Related Transfer Function (HRTF)-Algorithmus von Microsoft berechnet, um den Klang abhängig von 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.
Siehe auch
Windows developer