Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra come utilizzare le API nello spazio dei nomi Windows.Media.Audio per creare grafi audio per scenari di instradamento, missaggio ed elaborazione audio.
Un grafico audio è un set di nodi audio interconnessi attraverso i quali i dati audio vengono trasmessi.
I nodi di input audio forniscono dati audio al grafico da dispositivi di input audio, file audio o da codice personalizzato.
I nodi di output audio sono la destinazione per l'audio elaborato dal grafico. L'audio può essere instradato dal grafico a dispositivi di output audio, file audio o codice personalizzato.
I nodi submix accettano l'audio da uno o più nodi e li combinano in un singolo output che può essere instradato ad altri nodi nel grafico.
Dopo che tutti i nodi sono stati creati e le connessioni tra di esse configurate, è sufficiente avviare il grafico audio e i dati audio passano dai nodi di input, attraverso qualsiasi nodo submix, ai nodi di output. Questo modello rende scenari come la registrazione dal microfono di un dispositivo a un file audio, la riproduzione di audio da un file all'altoparlante di un dispositivo o la combinazione di audio da più origini in modo rapido e facile da implementare.
Altri scenari sono abilitati con l'aggiunta di effetti audio al grafico audio. Ogni nodo in un grafico audio può essere popolato con zero o più effetti audio che eseguono l'elaborazione audio sul passaggio dell'audio attraverso il nodo. Esistono diversi effetti predefiniti, ad esempio echo, equalizer, limitazione e riverbero, che possono essere collegati a un nodo audio con poche righe di codice. È anche possibile creare effetti audio personalizzati che funzionano esattamente come gli effetti predefiniti.
Scelta di Windows Runtime AudioGraph o XAudio2
Le API del grafo audio di Windows Runtime offrono funzionalità che possono essere implementate anche tramite le API XAudio2 basate su COM. Di seguito sono riportate le funzionalità del framework del grafico audio Windows Runtime che differiscono da XAudio2.
Le API del grafo audio di Windows Runtime:
- Sono notevolmente più facili da usare rispetto a XAudio2.
- Può essere usato da C# oltre a essere supportato per C++.
- Può usare file audio, inclusi i formati di file compressi, direttamente. XAudio2 funziona solo su buffer audio e non offre funzionalità di I/O di file.
- Può usare la pipeline audio a bassa latenza in Windows.
- Supportare il cambio automatico di endpoint quando vengono usati i parametri endpoint predefiniti. Ad esempio, se l'utente passa dall'altoparlante di un dispositivo a un visore VR, l'audio viene reindirizzato automaticamente al nuovo output.
Classe AudioGraph
La classe AudioGraph è l'elemento padre di tutti i nodi che costituiscono il grafico. Utilizzare questo oggetto per creare istanze di tutti i tipi di nodo audio. Creare un'istanza della classe AudioGraph inizializzando un oggetto AudioGraphSettings contenente le impostazioni di configurazione per il grafico, e quindi chiamando AudioGraph.CreateAsync. L'oggetto restituito CreateAudioGraphResult consente l'accesso al grafico audio creato o fornisce un valore di errore se la creazione del grafico audio non riesce.
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;
}
Tutti i tipi di nodi audio vengono creati usando i metodi Create* della classe AudioGraph .
Il metodo AudioGraph.Start determina l'avvio dell'elaborazione dei dati audio da parte del grafico audio. Il metodo AudioGraph.Stop arresta l'elaborazione audio. Ogni nodo del grafico può essere avviato e arrestato in modo indipendente mentre il grafico è in esecuzione, ma non sono attivi nodi quando il grafico viene arrestato. ResetAllNodes fa sì che tutti i nodi del grafico eliminino tutti i dati attualmente presenti nei buffer audio.
L'evento QuantumStarted si verifica quando il grafico avvia l'elaborazione di un nuovo quantum di dati audio. L'evento QuantumProcessed si verifica quando viene completata l'elaborazione di un quantum.
L'unica proprietà AudioGraphSettings necessaria è AudioRenderCategory. Se si specifica questo valore, il sistema può ottimizzare la pipeline audio per la categoria specificata.
La dimensione quantistica del grafico audio determina il numero di campioni elaborati contemporaneamente. Per impostazione predefinita, la dimensione quantistica è di 10 ms in base alla frequenza di campionamento predefinita. Se si specifica una dimensione quantistica personalizzata impostando la proprietà DesiredSamplesPerQuantum , è necessario impostare anche la proprietà QuantumSizeSelectionMode su ClosestToDesired oppure il valore fornito viene ignorato. Se questo valore viene usato, il sistema sceglierà una dimensione quantistica il più vicina possibile a quella specificata. Per determinare la dimensione quantistica effettiva, controllare SamplesPerQuantum di AudioGraph dopo la creazione.
Se si prevede di usare solo il grafico audio con file e non si prevede di eseguire l'output in un dispositivo audio, è consigliabile usare le dimensioni quantistiche predefinite non impostando la proprietà DesiredSamplesPerQuantum .
La proprietà DesiredRenderDeviceAudioProcessing determina la quantità di elaborazione eseguita dal dispositivo di rendering primario nell'output del grafico audio. L'impostazione Predefinita consente al sistema di usare l'elaborazione audio predefinita per la categoria di rendering audio specificata. Questa elaborazione può migliorare significativamente il suono dell'audio in alcuni dispositivi, in particolare i dispositivi mobili con altoparlanti di piccole dimensioni. L'impostazione Raw può migliorare le prestazioni riducendo al minimo la quantità di elaborazione dei segnali eseguita, ma può comportare una qualità audio inferiore in alcuni dispositivi.
Se QuantumSizeSelectionMode è impostato su LowestLatency, il grafico audio userà automaticamente Raw per DesiredRenderDeviceAudioProcessing.
È possibile impostare la proprietà AudioGraphSettings.MaxPlaybackSpeedFactor per impostare un valore massimo utilizzato per le proprietà AudioFileInputNode.PlaybackSpeedFactor, AudioFrameInputNode.PlaybackSpeedFactor e MediaSourceInputNode.PlaybackSpeedFactor . Quando un grafico audio supporta un fattore di velocità di riproduzione maggiore di 1, il sistema deve allocare memoria aggiuntiva per mantenere un buffer sufficiente di dati audio. Per questo motivo, l'impostazione di MaxPlaybackSpeedFactor sul valore più basso richiesto dall'app ridurrà il consumo di memoria dell'app. Se l'app riproduce il contenuto solo a velocità normale, è consigliabile impostare MaxPlaybackSpeedFactor su 1.
EncodingProperties determina il formato audio usato dal grafico. Sono supportati solo i formati float a 32 bit.
PrimaryRenderDevice imposta il dispositivo di rendering primario per il grafico audio. Se non si imposta questa impostazione, viene usato il dispositivo di sistema predefinito. Il dispositivo di rendering primario viene usato per calcolare le dimensioni quantistiche per altri nodi nel grafico. Se nel sistema non sono presenti dispositivi di rendering audio, la creazione del grafico audio avrà esito negativo.
È possibile consentire al grafico audio di usare il dispositivo di rendering audio predefinito o usare il Windows. Classe Devices.Enumeration.DeviceInformation per ottenere un elenco dei dispositivi di rendering audio disponibili del sistema chiamando FindAllAsync e passando il selettore del dispositivo di rendering audio restituito da Windows. Media.Devices.MediaDevice.GetAudioRenderSelector. Puoi scegliere uno degli oggetti DeviceInformation restituiti a livello di codice o mostrare l'interfaccia utente per consentire all'utente di selezionare un dispositivo e quindi usarlo per impostare la proprietà PrimaryRenderDevice .
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;
Nodo di input del dispositivo
Un nodo di input del dispositivo inserisce audio nel grafico da un dispositivo di acquisizione audio connesso al sistema, ad esempio un microfono. Creare un oggetto DeviceInputNode che usa il dispositivo di acquisizione audio predefinito del sistema chiamando CreateDeviceInputNodeAsync. Specificare un MediaCategory per consentire al sistema di ottimizzare la pipeline audio per la categoria specificata.
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;
}
Se vuoi specificare un dispositivo di acquisizione audio specifico per il nodo di input del dispositivo, puoi usare il Windows. Classe Devices.Enumeration.DeviceInformation per ottenere un elenco dei dispositivi di acquisizione audio disponibili del sistema chiamando FindAllAsync e passando il selettore del dispositivo di acquisizione audio restituito da Windows. Media.Devices.MediaDevice.GetAudioCaptureSelector. Puoi scegliere uno degli oggetti DeviceInformation restituiti a livello di codice o mostrare l'interfaccia utente per consentire all'utente di selezionare un dispositivo e quindi passarlo a 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);
Nodo di output del dispositivo
Un nodo di output del dispositivo esegue il push dell'audio dal grafico a un dispositivo di rendering audio, ad esempio altoparlanti o visori VR. Creare un DeviceOutputNode chiamando CreateDeviceOutputNodeAsync. Il nodo di output usa il PrimaryRenderDevice del grafo audio.
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;
}
Nodo di input del file
Un nodo di input di file consente di inserire dati da un file audio nel grafico. Creare un AudioFileInputNode chiamando CreateFileInputNodeAsync.
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;
}
- I nodi di input dei file supportano i formati di file seguenti: mp3, wav, wma, m4a.
- Impostare la proprietà StartTime per specificare l'offset temporale nel file in cui deve iniziare la riproduzione. Se questa proprietà è null, viene utilizzata l'inizio del file. Impostare la proprietà EndTime per specificare l'offset di tempo nel file in cui deve terminare la riproduzione. Se questa proprietà è Null, viene utilizzata la fine del file. Il valore dell'ora di inizio deve essere inferiore al valore dell'ora di fine e il valore dell'ora di fine deve essere minore o uguale alla durata del file audio, che può essere determinato controllando il valore della proprietà Duration .
- Cercare una posizione nel file audio chiamando Seek e specificando l'offset di tempo nel file in cui deve essere spostata la posizione di riproduzione. Il valore specificato deve essere compreso nell'intervallo StartTime e EndTime . Ottiene la posizione di riproduzione corrente del nodo con la proprietà Position di sola lettura.
- Abilitare il ciclo del file audio impostando la proprietà LoopCount . Se non null, questo valore indica il numero di volte in cui il file verrà riprodotto dopo la riproduzione iniziale. Ad esempio, impostando LoopCount su 1, il file verrà riprodotto 2 volte in totale e impostandolo su 5 il file verrà riprodotto 6 volte in totale. Se si imposta LoopCount su Null, il file viene eseguito a ciclo continuo per un periodo illimitato. Per arrestare il ciclo, impostare il valore su 0.
- Regolare la velocità di riproduzione del file audio impostando PlaybackSpeedFactor. Il valore 1 indica la velocità originale del file, .5 è a metà velocità e 2 è doppia velocità.
Nodo di input MediaSource
La classe MediaSource fornisce un modo comune per fare riferimento ai supporti da origini diverse ed espone un modello comune per accedere ai dati multimediali indipendentemente dal formato multimediale sottostante, che può essere un file su disco, un flusso o un'origine di rete di streaming adattiva. Un nodo MediaSourceAudioInputNode consente di indirizzare i dati audio da un oggetto MediaSource al grafico audio. Creare un oggetto MediaSourceAudioInputNode chiamando CreateMediaSourceAudioInputNodeAsync, passando un oggetto MediaSource che rappresenta il contenuto che si desidera riprodurre. Viene restituito un oggetto CreateMediaSourceAudioInputNodeResult che è possibile usare per determinare lo stato dell'operazione controllando la proprietà Status . Se lo stato è Success, è possibile ottenere l'oggetto MediaSourceAudioInputNode creato accedendo alla proprietà Node . Nell'esempio seguente viene illustrata la creazione di un nodo da un oggetto AdaptiveMediaSource che rappresenta lo streaming del contenuto in rete.
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;
}
Per ricevere una notifica quando la riproduzione raggiunge la fine del contenuto di MediaSource, registrare un gestore dell'evento MediaSourceCompleted.
mediaSourceInputNode.MediaSourceCompleted += MediaSourceInputNode_MediaSourceCompleted;
private void MediaSourceInputNode_MediaSourceCompleted(MediaSourceAudioInputNode sender, object args)
{
audioGraph.Stop();
}
Sebbene la riproduzione di un file dal disco si concluda probabilmente sempre correttamente, la riproduzione di contenuti multimediali in streaming da una fonte di rete potrebbe non andare a buon fine a causa di una modifica della connessione di rete o di altri problemi che sono al di fuori del controllo del grafo audio. Se un MediaSource non è più riproducibile durante la riproduzione, il grafo audio genererà l'evento UnrecoverableErrorOccurred. È possibile usare il gestore per questo evento per arrestare e eliminare il grafico audio e quindi reinizializzare il grafico.
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();
}
}
Nodo di output dei file
Un nodo di uscita file consente di inviare i dati audio dal grafo a un file audio. Creare un AudioFileOutputNode chiamando CreateFileOutputNodeAsync.
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;
}
- I nodi di output dei file supportano i formati di file seguenti: mp3, wav, wma, m4a.
- È necessario chiamare AudioFileOutputNode.Stop per arrestare l'elaborazione del nodo prima di chiamare AudioFileOutputNode.FinalizeAsync o verrà generata un'eccezione.
Nodo di ingresso dei frame audio
Un nodo di input dei frame audio consente di inserire nel grafo audio i dati audio generati nel tuo codice. Ciò consente scenari come la creazione di un sintetizzatore software personalizzato. Creare un AudioFrameInputNode chiamando 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;
}
L'evento FrameInputNode.QuantumStarted viene generato quando il grafico audio è pronto per iniziare a elaborare il successivo quantum di dati audio. Si forniscono a questo evento i dati audio personalizzati generati dall'interno del gestore.
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);
}
}
- L'oggetto FrameInputNodeQuantumStartedEventArgs passato al gestore eventi QuantumStarted espone la proprietà RequiredSamples, che indica quanti campioni sono necessari al grafo audio per completare il quantum da elaborare.
- Chiamare AudioFrameInputNode.AddFrame per passare un oggetto AudioFrame riempito con dati audio nel grafico.
- Puoi usare MediaFrameReader con dati audio per ottenere oggetti AudioFrame a partire da un'origine di frame multimediali, che possono essere passati a un FrameInputNode usando il metodo AddFrame.
- Di seguito è illustrata un'implementazione di esempio del metodo helper GenerateAudioData .
Per popolare un AudioFrame con dati audio, è necessario accedere al buffer di memoria sottostante del frame audio. A tale scopo, inizializzare l'interfaccia COM IMemoryBufferByteAccess , come illustrato di seguito.
[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
void GetBuffer(out byte* buffer, out uint capacity);
}
Il codice seguente illustra un'implementazione di esempio di un metodo helper GenerateAudioData che crea un metodo helper AudioFrame e lo popola con dati audio.
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;
}
- Poiché questo metodo accede al buffer non elaborato sottostante i tipi di Windows Runtime, deve essere dichiarato usando la parola chiave unsafe. È inoltre necessario configurare il progetto in Microsoft Visual Studio per consentire la compilazione di codice non sicuro aprendo la pagina delle proprietà Properties, facendo clic sulla casella di controllo Build e selezionando la casella di controllo Allow Unsafe Code.
- Inizializzare una nuova istanza di AudioFrame, nello spazio dei nomi Windows.Media, specificando al costruttore la dimensione del buffer desiderata. La dimensione del buffer è il numero di campioni moltiplicati per le dimensioni di ogni campione.
- Ottieni il AudioBuffer del frame audio chiamando LockBuffer.
- Ottenere un'istanza dell'interfaccia COM IMemoryBufferByteAccess dal buffer audio chiamando CreateReference.
- Ottenere un puntatore ai dati del buffer audio non elaborato chiamando IMemoryBufferByteAccess.GetBuffer ed eseguirne il cast al tipo di dati di esempio dei dati audio.
- Riempire il buffer con i dati e restituire il AudioFrame per l'invio nel grafico audio.
Nodo di uscita del frame audio
Un nodo di output dei frame audio consente di ricevere ed elaborare i dati audio in uscita dal grafo audio tramite codice personalizzato creato dall'utente. Uno scenario di esempio è l'esecuzione dell'analisi dei segnali sull'output audio. Creare un AudioFrameOutputNode chiamando CreateFrameOutputNode.
AudioFrameOutputNode frameOutputNode;
private void CreateFrameOutputNode()
{
frameOutputNode = audioGraph.CreateFrameOutputNode();
audioGraph.QuantumStarted += AudioGraph_QuantumStarted;
}
L'evento AudioGraph.QuantumStarted viene generato quando il grafico audio inizia a elaborare un quantum di dati audio. È possibile accedere ai dati audio dall'interno del gestore per questo evento.
Annotazioni
Se si desidera recuperare fotogrammi audio a cadenza regolare, sincronizzati con il grafico audio, chiamare AudioFrameOutputNode.GetFrame dall'interno del gestore eventi QuantumStarted sincrono. L'evento QuantumProcessed viene generato in modo asincrono dopo che il motore audio ha completato l'elaborazione audio, il che significa che la frequenza potrebbe essere irregolare. Pertanto, non è consigliabile usare l'evento QuantumProcessed per l'elaborazione sincronizzata dei dati dei frame audio.
private void AudioGraph_QuantumStarted(AudioGraph sender, object args)
{
AudioFrame frame = frameOutputNode.GetFrame();
ProcessFrameOutput(frame);
}
- Chiamare GetFrame per ottenere un oggetto AudioFrame riempito di dati audio dal grafico.
- Di seguito è illustrata un'implementazione di esempio del metodo helper 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;
}
}
- Come nell'esempio precedente del nodo di input dei fotogrammi audio, dovrai dichiarare l'interfaccia COM IMemoryBufferByteAccess e configurare il progetto per consentire il codice non sicuro per accedere al buffer audio sottostante.
- Ottieni il AudioBuffer del fotogramma audio chiamando il metodo LockBuffer.
- Ottenere un'istanza dell'interfaccia COM IMemoryBufferByteAccess dal buffer audio chiamando CreateReference.
- Ottenere un puntatore ai dati del buffer audio non elaborato chiamando IMemoryBufferByteAccess.GetBuffer ed eseguirne il cast al tipo di dati di esempio dei dati audio.
Connessioni dei nodi e nodi submix
Tutti i tipi di nodo di input espongono il metodo AddOutgoingConnection che instrada l'audio prodotto dal nodo al nodo passato al metodo . L'esempio seguente connette un AudioFileInputNode a un AudioDeviceOutputNode, una configurazione semplice per la riproduzione di un file audio nell'altoparlante del dispositivo.
fileInputNode.AddOutgoingConnection(deviceOutputNode);
È possibile creare più connessioni da un nodo di input ad altri nodi. Nell'esempio seguente viene aggiunta un'altra connessione dal AudioFileInputNode a un AudioFileOutputNode. Ora, l'audio del file audio viene riprodotto nell'altoparlante del dispositivo e viene scritto anche in un file audio.
fileInputNode.AddOutgoingConnection(fileOutputNode);
I nodi di output possono anche ricevere più connessioni da altri nodi. Nell'esempio seguente viene stabilita una connessione da un AudioDeviceInputNode al nodo AudioDeviceOutput. Poiché il nodo di output ha connessioni dal nodo di input del file e dal nodo di input del dispositivo, l'output conterrà una combinazione di audio da entrambe le origini. AddOutgoingConnection dispone di un overload che consente di specificare un valore di guadagno per il segnale che attraversa la connessione.
deviceInputNode.AddOutgoingConnection(deviceOutputNode, .5);
Anche se i nodi di output possono accettare connessioni da più nodi, è possibile creare una combinazione intermedia di segnali da uno o più nodi prima di passare la combinazione a un output. Ad esempio, è possibile impostare il livello o applicare effetti a un subset dei segnali audio in un grafico. A tale scopo, usare il AudioSubmixNode. È possibile connettersi a un nodo submix da uno o più nodi di input o da altri nodi submix. Nell'esempio seguente viene creato un nuovo nodo submix con AudioGraph.CreateSubmixNode. Le connessioni vengono quindi aggiunte da un nodo di input di file e da un nodo di input frame al nodo submix. Infine, il nodo submix è connesso a un nodo di output del file.
private void CreateSubmixNode()
{
AudioSubmixNode submixNode = audioGraph.CreateSubmixNode();
fileInputNode.AddOutgoingConnection(submixNode);
frameInputNode.AddOutgoingConnection(submixNode);
submixNode.AddOutgoingConnection(fileOutputNode);
}
Avvio e arresto dei nodi del grafico audio
Quando viene chiamato AudioGraph.Start , il grafico audio inizia a elaborare i dati audio. Ogni tipo di nodo fornisce metodi Start e Stop che causano l'avvio o l'interruzione dell'elaborazione dei dati da parte del singolo nodo. Quando viene chiamato AudioGraph.Stop , tutte le elaborazioni audio in tutti i nodi vengono arrestate indipendentemente dallo stato dei singoli nodi, ma lo stato di ogni nodo può essere impostato mentre il grafico audio viene arrestato. Ad esempio, è possibile chiamare Stop su un singolo nodo mentre il grafico viene arrestato e quindi chiamare AudioGraph.Start e il singolo nodo rimarrà nello stato arrestato.
Tutti i tipi di nodo espongono la proprietà ConsumeInput che, se impostata su false, consente al nodo di continuare l'elaborazione audio, ma impedisce l'utilizzo di dati audio provenienti da altri nodi.
Tutti i tipi di nodo espongono il metodo Reset che fa sì che il nodo elimini tutti i dati audio attualmente presenti nel buffer.
Aggiungere effetti audio
L'API grafico audio consente di aggiungere effetti audio a ogni tipo di nodo in un grafico. I nodi di output, i nodi di input e i nodi submix possono avere un numero illimitato di effetti audio, limitati solo dalle funzionalità dell'hardware. Nell'esempio seguente viene illustrato l'aggiunta dell'effetto echo predefinito a un nodo submix.
EchoEffectDefinition echoEffect = new EchoEffectDefinition(audioGraph);
echoEffect.Delay = 1000.0;
echoEffect.Feedback = .2;
echoEffect.WetDryMix = .5;
submixNode.EffectDefinitions.Add(echoEffect);
- Tutti gli effetti audio implementano IAudioEffectDefinition. Ogni nodo espone una proprietà EffectDefinitions che rappresenta l'elenco di effetti applicati a tale nodo. Aggiungere un effetto aggiungendo il relativo oggetto definizione all'elenco.
- Esistono diverse classi per la definizione degli effetti disponibili nello spazio dei nomi Windows.Media.Audio. Questi includono:
- È possibile creare effetti audio personalizzati che implementano IAudioEffectDefinition e applicarli a qualsiasi nodo in un grafico audio.
- Ogni tipo di nodo espone un metodo DisableEffectsByDefinition che disabilita tutti gli effetti nell'elenco EffectDefinitions del nodo aggiunto usando la definizione specificata. EnableEffectsByDefinition abilita gli effetti con la definizione specificata.
Audio spaziale
AudioGraph supporta l'audio spaziale, che consente di specificare la posizione nello spazio 3D da cui viene generato l'audio da qualsiasi nodo input o submix. È anche possibile specificare una forma e una direzione in cui viene generato l'audio, una velocità che verrà usata per spostare l'audio del nodo e definire un modello di decadimento che descrive come l'audio viene attenuato con la distanza.
Per creare un emettitore, è prima possibile creare una forma in cui il suono viene proiettato dall'emettitore, che può essere un cono o omnidirectionale. La classe AudioNodeEmitterShape fornisce metodi statici per la creazione di ognuna di queste forme. Creare quindi un modello di decadimento. Questo definisce il modo in cui il volume dell'audio dell'emettitore diminuisce man mano che aumenta la distanza dal listener. Il metodo CreateNatural crea un modello di decadimento che emula il decadimento naturale del suono basato su un modello di attenuazione proporzionale al quadrato della distanza. Creare infine un oggetto AudioNodeEmitterSettings. Attualmente, questo oggetto viene usato solo per abilitare e disabilitare l'attenuazione doppler basata sulla velocità dell'audio dell'emettitore. Chiamare il costruttore AudioNodeEmitter , passando gli oggetti di inizializzazione appena creati. Per impostazione predefinita, l'emettitore viene posizionato all'origine, ma è possibile impostare la posizione dell'emettitore con la proprietà Position .
Annotazioni
Gli emettitori di nodi audio possono elaborare solo l'audio formattato in mono con una frequenza di campionamento di 48 kHz. Il tentativo di utilizzare audio stereo oppure audio con una frequenza di campionamento diversa genererà un'eccezione.
L'emettitore viene assegnato a un nodo audio quando lo si crea usando il metodo di creazione di overload per il tipo di nodo desiderato. In questo esempio, CreateFileInputNodeAsync viene usato per creare un nodo di input di file da un file specificato e l'oggetto AudioNodeEmitter da associare al nodo.
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;
Il AudioDeviceOutputNode che restituisce audio dal grafico all'utente ha un oggetto listener, accesso con la proprietà Listener, che rappresenta la posizione, l'orientamento e la velocità dell'utente nello spazio 3D. Le posizioni di tutti gli emettitori nel grafico sono relative alla posizione e all'orientamento dell'oggetto listener. Per impostazione predefinita, il listener si trova all'origine (0,0,0) rivolta in avanti lungo l'asse Z, ma è possibile impostarne la posizione e l'orientamento con le proprietà Position e Orientation .
deviceOutputNode.Listener.Position = new Vector3(100, 0, 0);
deviceOutputNode.Listener.Orientation = Quaternion.CreateFromYawPitchRoll(0, (float)Math.PI, 0);
È possibile aggiornare la posizione, la velocità e la direzione degli emettitori in fase di esecuzione per simulare lo spostamento di una sorgente audio attraverso lo spazio 3D.
AudioNodeEmitter emitter = fileInputNode.Emitter;
emitter.Position = newObjectPosition;
emitter.DopplerVelocity = newObjectPosition - oldObjectPosition;
È anche possibile aggiornare la posizione, la velocità e l'orientamento dell'oggetto listener in fase di esecuzione per simulare lo spostamento dell'utente attraverso lo spazio 3D.
deviceOutputNode.Listener.Position = newUserPosition;
Per impostazione predefinita, l'audio spaziale viene calcolato usando l'algoritmo HRTF (Head-Relative Transfer Function) di Microsoft per attenuare l'audio in base alla forma, alla velocità e alla posizione rispetto al listener. Puoi impostare la proprietà SpatialAudioModel su FoldDown per usare un semplice metodo di combinazione stereo per simulare l'audio spaziale meno accurato, ma richiede meno risorse di CPU e memoria.