Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se muestra cómo utilizar las API del espacio de nombres Windows.Media.Audio para crear grafos de audio para escenarios de enrutamiento, mezcla y procesamiento de audio.
Un gráfico de audio es un conjunto de nodos de audio interconectados a través de los cuales fluyen los datos de audio.
Los nodos de entrada de audio proporcionan datos de audio al grafo desde dispositivos de entrada de audio, archivos de audio o código personalizado.
Los nodos de salida de audio son el destino del audio procesado por el gráfico. El audio se puede enrutar fuera del grafo a dispositivos de salida de audio, archivos de audio o código personalizado.
Los nodos de submezcla toman audio de uno o varios nodos y los combinan en una única salida que se puede enrutar a otros nodos del gráfico.
Una vez creados todos los nodos y configuradas las conexiones entre ellos, basta con iniciar el grafo de audio y los datos de audio fluyen desde los nodos de entrada, a través de cualquier submezclado, a los nodos de salida. Este modelo hace escenarios como la grabación desde el micrófono de un dispositivo a un archivo de audio, la reproducción de audio desde un archivo al altavoz de un dispositivo o la combinación de audio de varios orígenes de forma rápida y fácil de implementar.
Se habilitan escenarios adicionales con la adición de efectos de audio al grafo de audio. Cada nodo de un grafo de audio se puede rellenar con cero o más efectos de audio que realizan el procesamiento de audio en el audio que pasa por el nodo. Hay varios efectos integrados, como eco, ecualizador, limitación y reverberación que se pueden conectar a un nodo de audio con solo unas pocas líneas de código. También puede crear sus propios efectos de audio personalizados que funcionen exactamente igual que los efectos integrados.
Elegir Windows Runtime AudioGraph o XAudio2
Las API de gráfico de audio de Windows Runtime ofrecen una funcionalidad que también puede implementarse mediante las API de XAudio2 basadas en COM XAudio2 APIs. A continuación se muestran las características del marco de grafo de audio de Windows Runtime que lo diferencian de XAudio2.
Las API de grafos de audio de Windows Runtime:
- Son significativamente más fáciles de usar que XAudio2.
- Se puede usar desde C# además de ser compatible con C++.
- Puede usar archivos de audio, incluidos los formatos de archivo comprimidos, directamente. XAudio2 solo funciona en búferes de audio y no proporciona ninguna funcionalidad de E/S de archivos.
- Puede usar la canalización de audio de baja latencia en Windows.
- Admite la conmutación automática de puntos de conexión cuando se usan parámetros de punto de conexión predeterminados. Por ejemplo, si el usuario cambia del altavoz de un dispositivo a un casco, el audio se redirige automáticamente a la nueva salida.
Clase AudioGraph
La clase AudioGraph es el elemento primario de todos los nodos que componen el gráfico. Use este objeto para crear instancias de todos los tipos de nodo de audio. Cree una instancia de la clase AudioGraph inicializando un objeto AudioGraphSettings que contiene los valores de configuración del gráfico, y, a continuación, llama a AudioGraph.CreateAsync. El valor devuelto CreateAudioGraphResult proporciona acceso al grafo de audio creado o proporciona un valor de error si se produce un error en la creación del grafo de audio.
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;
}
Todos los tipos de nodo de audio se crean mediante los métodos Create* de la clase AudioGraph .
El método AudioGraph.Start hace que el grafo de audio empiece a procesar datos de audio. El método AudioGraph.Stop detiene el procesamiento de audio. Cada nodo del grafo se puede iniciar y detener de forma independiente mientras se ejecuta el grafo, pero no hay nodos activos cuando se detiene el grafo. ResetAllNodes hace que todos los nodos del grafo descarten los datos que se encuentran actualmente en sus búferes de audio.
El evento QuantumStarted se produce cuando el gráfico inicia el procesamiento de un nuevo quantum de datos de audio. El evento QuantumProcessed se produce cuando se completa el procesamiento de un quantum.
La única propiedad AudioGraphSettings necesaria es AudioRenderCategory. Especificar este valor permite al sistema optimizar la canalización de audio para la categoría especificada.
El tamaño cuántico del gráfico de audio determina el número de muestras que se procesan al mismo tiempo. De forma predeterminada, el tamaño cuántico se basa en 10 ms en la frecuencia de muestreo predeterminada. Si especifica un tamaño cuántico personalizado estableciendo la propiedad DesiredSamplesPerQuantum , también debe establecer la propiedad QuantumSizeSelectionMode en ClosestToDesired o se omite el valor proporcionado. Si se usa este valor, el sistema elegirá un tamaño cuántico lo más cercano posible al especificado. Para determinar el tamaño cuántico real, compruebe SamplesPerQuantum del AudioGraph una vez creado.
Si solo tiene previsto usar el grafo de audio con archivos y no planea generar resultados en un dispositivo de audio, se recomienda usar el tamaño cuántico predeterminado al no establecer la propiedad DesiredSamplesPerQuantum .
La propiedad DesiredRenderDeviceAudioProcessing determina la cantidad de procesamiento que realiza el dispositivo de representación principal en la salida del grafo de audio. El valor Predeterminado permite al sistema usar el procesamiento de audio predeterminado para la categoría de representación de audio especificada. Este procesamiento puede mejorar significativamente el sonido del audio en algunos dispositivos, especialmente dispositivos móviles con altavoces pequeños. La configuración Raw puede mejorar el rendimiento minimizando la cantidad de procesamiento de señales realizada, pero puede dar lugar a una calidad de sonido inferior en algunos dispositivos.
Si QuantumSizeSelectionMode se establece en LowestLatency, el gráfico de audio usará automáticamente Raw para DesiredRenderDeviceAudioProcessing.
Puede establecer la propiedad AudioGraphSettings.MaxPlaybackSpeedFactor para establecer un valor máximo utilizado para las propiedades AudioFileInputNode.PlaybackSpeedFactor, AudioFrameInputNode.PlaybackSpeedFactor y MediaSourceInputNode.PlaybackSpeedFactor . Cuando un gráfico de audio admite un factor de velocidad de reproducción mayor que 1, el sistema debe asignar memoria adicional para mantener un búfer suficiente de datos de audio. Por este motivo, establecer MaxPlaybackSpeedFactor en el valor más bajo requerido por la aplicación reducirá el consumo de memoria de la aplicación. Si la aplicación solo reproducirá contenido a velocidad normal, se recomienda establecer MaxPlaybackSpeedFactor en 1.
EncodingProperties determina el formato de audio usado por el grafo. Solo se admiten formatos float de 32 bits.
PrimaryRenderDevice establece el dispositivo de representación principal para el grafo de audio. Si no establece esto, se usa el dispositivo del sistema predeterminado. El dispositivo de representación principal se usa para calcular los tamaños cuánticos de otros nodos del gráfico. Si no hay ningún dispositivo de representación de audio presente en el sistema, se producirá un error en la creación del grafo de audio.
Puede permitir que el grafo de audio use el dispositivo de representación de audio predeterminado o use el Windows. Devices.Enumeration.DeviceInformation clase para obtener una lista de los dispositivos de representación de audio disponibles del sistema llamando a FindAllAsync y pasando el selector de dispositivos de representación de audio devuelto por Windows. Media.Devices.MediaDevice.GetAudioRenderSelector. Puede elegir uno de los objetos DeviceInformation devueltos mediante programación o mostrar la interfaz de usuario para permitir que el usuario seleccione un dispositivo y después úselo para establecer la propiedad 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 de entrada del dispositivo
Un nodo de entrada de dispositivo alimenta el audio en el gráfico desde un dispositivo de captura de audio conectado al sistema, como un micrófono. Cree un objeto DeviceInputNode que use el dispositivo de captura de audio predeterminado del sistema llamando a CreateDeviceInputNodeAsync. Proporcione un MediaCategory para permitir que el sistema optimice el flujo de audio para la categoría especificada.
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;
}
Si desea especificar un dispositivo de captura de audio específico para el nodo de entrada del dispositivo, puede usar el Windows. Devices.Enumeration.DeviceInformation clase para obtener una lista de los dispositivos de captura de audio disponibles del sistema llamando a FindAllAsync y pasando el selector de dispositivos de captura de audio devuelto por Windows. Media.Devices.MediaDevice.GetAudioCaptureSelector. Puede elegir uno de los objetos DeviceInformation devueltos mediante programación o mostrar la interfaz de usuario para permitir al usuario seleccionar un dispositivo y, a continuación, pasarlo 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 de salida del dispositivo
Un nodo de salida de dispositivo inserta audio del grafo en un dispositivo de representación de audio, como altavoces o auriculares. Cree un DeviceOutputNode mediante una llamada a CreateDeviceOutputNodeAsync. El nodo de salida utiliza el PrimaryRenderDevice del grafo de 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 de entrada de archivo
Un nodo de entrada de archivo permite alimentar datos de un archivo de audio en el grafo. Cree un AudioFileInputNode mediante una llamada a 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;
}
- Los nodos de entrada de archivo admiten los siguientes formatos de archivo: mp3, wav, wma, m4a.
- Establezca la propiedad StartTime para especificar el desplazamiento de tiempo en el archivo donde debe comenzar la reproducción. Si esta propiedad es null, se usa el principio del archivo. Establezca la propiedad EndTime para especificar el desplazamiento de tiempo en el archivo donde debe finalizar la reproducción. Si esta propiedad es null, se usa el final del archivo. El valor de hora de inicio debe ser inferior al valor de hora de finalización y el valor de hora de finalización debe ser menor o igual que la duración del archivo de audio, que se puede determinar comprobando el valor de la propiedad Duration .
- Busque una posición en el archivo de audio llamando a Seek y especificando el desplazamiento de tiempo en el archivo al que se debe mover la posición de reproducción. El valor especificado debe estar dentro del intervalo StartTime y EndTime . Obtenga la posición de reproducción actual del nodo con la propiedad Position de solo lectura.
- Habilite el bucle del archivo de audio estableciendo la propiedad LoopCount . Cuando no es null, este valor indica el número de veces que se reproducirá el archivo después de la reproducción inicial. Por lo tanto, por ejemplo, establecer LoopCount en 1 hará que el archivo se reproduzca 2 veces en total y establecerlo en 5 hará que el archivo se reproduzca 6 veces en total. Establecer LoopCount en null hace que el archivo se bucle indefinidamente. Para detener el bucle, establezca el valor en 0.
- Ajuste la velocidad a la que se reproduce el archivo de audio estableciendo PlaybackSpeedFactor. Un valor de 1 indica la velocidad original del archivo, .5 es de media velocidad y 2 es doble velocidad.
Nodo de entrada de MediaSource
La clase MediaSource proporciona una manera común de hacer referencia a medios de diferentes orígenes y expone un modelo común para acceder a datos multimedia independientemente del formato multimedia subyacente, que podría ser un archivo en disco, una secuencia o un origen de red de streaming adaptable. Un nodo MediaSourceAudioInputNode le permite dirigir datos de audio desde un objeto MediaSource al grafo de audio. Cree un mediaSourceAudioInputNode llamando a CreateMediaSourceAudioInputNodeAsync y pase un objeto MediaSource que represente el contenido que desea reproducir. Se devuelve un CreateMediaSourceAudioInputNodeResult que puede utilizar para determinar el estado de la operación consultando la propiedad Status. Si el estado es Success, puede obtener el MediaSourceAudioInputNode creado accediendo a la propiedad Node. En el ejemplo siguiente se muestra la creación de un nodo a partir de un objeto AdaptiveMediaSource que representa el streaming de contenido a través de la red.
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;
}
Para recibir una notificación cuando la reproducción haya llegado al final del contenido de MediaSource , registre un controlador para el evento MediaSourceCompleted .
mediaSourceInputNode.MediaSourceCompleted += MediaSourceInputNode_MediaSourceCompleted;
private void MediaSourceInputNode_MediaSourceCompleted(MediaSourceAudioInputNode sender, object args)
{
audioGraph.Stop();
}
Aunque reproducir un archivo desde el disco probablemente se complete correctamente, es posible que los medios transmitidos desde un origen de red produzcan un error durante la reproducción debido a un cambio en la conexión de red u otros problemas que están fuera del control del gráfico de audio. Si una MediaSource deja de poder reproducirse durante la reproducción, el grafo de audio generará el evento UnrecoverableErrorOccurred. Puede usar el controlador de este evento para detener y liberar el grafo de audio y, a continuación, reinicializar su grafo.
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 de salida de archivo
Un nodo de salida de archivo permite dirigir datos de audio desde el grafo a un archivo de audio. Cree un AudioFileOutputNode mediante una llamada a 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;
}
- Los nodos de salida de archivo admiten los siguientes formatos de archivo: mp3, wav, wma, m4a.
- Debe llamar a AudioFileOutputNode.Stop para detener el procesamiento del nodo antes de llamar a AudioFileOutputNode.FinalizeAsync o se producirá una excepción.
Nodo de entrada de tramas de audio
Un nodo de entrada de tramas de audio permite introducir en el grafo de audio los datos de audio que genere en su propio código. Esto permite escenarios como la creación de un sintetizador de software personalizado. Cree un AudioFrameInputNode llamando a 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;
}
El evento FrameInputNode.QuantumStarted se genera cuando el grafo de audio está listo para comenzar a procesar la siguiente cuántica de datos de audio. Usted proporciona los datos de audio personalizados generados desde el controlador a este evento.
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);
}
}
- El objeto FrameInputNodeQuantumStartedEventArgs que se pasa al controlador de eventos QuantumStarted expone la propiedad RequiredSamples, que indica cuántas muestras necesita el grafo de audio para completar el quantum que se va a procesar.
- Llame a AudioFrameInputNode.AddFrame para pasar un objeto AudioFrame rellenado con datos de audio en el grafo.
- Puede usar MediaFrameReader con datos de audio para obtener objetos AudioFrame de un origen de marco multimedia, que se puede pasar a FrameInputNode mediante el método AddFrame .
- A continuación se muestra una implementación de ejemplo del método auxiliar GenerateAudioData .
Para rellenar un AudioFrame con datos de audio, debe obtener acceso al búfer de memoria subyacente del marco de audio. Para ello, inicialice la interfaz COM IMemoryBufferByteAccess como se muestra a continuación.
[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
void GetBuffer(out byte* buffer, out uint capacity);
}
El código siguiente muestra una implementación de ejemplo de un método auxiliar GenerateAudioData que crea un método auxiliar AudioFrame y lo rellena con datos de 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;
}
- Dado que este método accede al búfer sin procesar subyacente a los tipos de Windows Runtime, debe declararse mediante la palabra clave unsafe. También debe configurar el proyecto en Microsoft Visual Studio para permitir la compilación de código no seguro abriendo la página de propiedades Properties, haciendo clic en la Build y activando la casilla Allow Unsafe Code.
- Inicialice una nueva instancia de AudioFrame, en el Windows. Media espacio de nombres, pasando el tamaño de búfer deseado al constructor. El tamaño del búfer es el número de muestras multiplicadas por el tamaño de cada muestra.
- Obtenga el AudioBuffer del fotograma de audio mediante una llamada a LockBuffer.
- Obtenga una instancia de la interfaz COM IMemoryBufferByteAccess desde el búfer de audio mediante una llamada a CreateReference.
- Obtenga un puntero a los datos del búfer de audio sin procesar llamando a IMemoryBufferByteAccess.GetBuffer y conviértalo al tipo de datos de muestra del audio.
- Rellene el búfer con datos y devuelva el AudioFrame para su envío al gráfico de audio.
Nodo de salida de tramas de audio
Un nodo de salida de tramas de audio le permite recibir y procesar los datos de audio de salida del grafo de audio con código personalizado que usted cree. Un escenario de ejemplo para esto es realizar análisis de señal en la salida de audio. Cree un AudioFrameOutputNode llamando a CreateFrameOutputNode.
AudioFrameOutputNode frameOutputNode;
private void CreateFrameOutputNode()
{
frameOutputNode = audioGraph.CreateFrameOutputNode();
audioGraph.QuantumStarted += AudioGraph_QuantumStarted;
}
El evento AudioGraph.QuantumStarted se genera cuando el gráfico de audio comienza a procesar un cuántico de datos de audio. Puede acceder a los datos de audio desde dentro del controlador de este evento.
Note
Si desea recuperar fotogramas de audio a intervalos regulares, sincronizados con el grafo de audio, llame a AudioFrameOutputNode.GetFrame desde dentro del controlador del evento sincrónico QuantumStarted. El evento QuantumProcessed se genera de forma asincrónica después de que el motor de audio haya completado el procesamiento de audio, lo que significa que su cadencia puede ser irregular. Por lo tanto, no debe usar el evento QuantumProcessed para el procesamiento sincronizado de datos de tramas de audio.
private void AudioGraph_QuantumStarted(AudioGraph sender, object args)
{
AudioFrame frame = frameOutputNode.GetFrame();
ProcessFrameOutput(frame);
}
- Llame a GetFrame para obtener un objeto AudioFrame rellenado con datos de audio del gráfico.
- A continuación se muestra una implementación de ejemplo del método auxiliar 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;
}
}
- Al igual que el ejemplo anterior del nodo de entrada de fotogramas de audio, deberá declarar la interfaz COM IMemoryBufferByteAccess y configurar el proyecto para permitir código no seguro para acceder al búfer de audio subyacente.
- Obtenga el AudioBuffer del fotograma de audio mediante una llamada a LockBuffer.
- Obtenga una instancia de la interfaz COM IMemoryBufferByteAccess desde el búfer de audio mediante una llamada a CreateReference.
- Obtenga un puntero a los datos sin procesar del búfer de audio llamando a IMemoryBufferByteAccess.GetBuffer y conviértalo al tipo de datos de las muestras de audio.
Conexiones de nodos y nodos de submezcla
Todos los tipos de nodo de entrada exponen el método AddOutgoingConnection que enruta el audio generado por el nodo al nodo que se pasa al método . En el ejemplo siguiente se conecta un AudioFileInputNode a un AudioDeviceOutputNode, que es una configuración sencilla para reproducir un archivo de audio en el altavoz del dispositivo.
fileInputNode.AddOutgoingConnection(deviceOutputNode);
Puede crear más de una conexión desde un nodo de entrada a otros nodos. En el ejemplo siguiente se agrega otra conexión desde el AudioFileInputNode a un AudioFileOutputNode. Ahora, el audio del archivo de audio se reproduce en el altavoz del dispositivo y también se escribe en un archivo de audio.
fileInputNode.AddOutgoingConnection(fileOutputNode);
Los nodos de salida también pueden recibir más de una conexión de otros nodos. En el ejemplo siguiente se realiza una conexión desde un AudioDeviceInputNode al nodo AudioDeviceOutput. Dado que el nodo de salida tiene conexiones desde el nodo de entrada de archivo y el nodo de entrada del dispositivo, la salida contendrá una combinación de audio de ambos orígenes. AddOutgoingConnection proporciona una sobrecarga que permite especificar un valor de ganancia para la señal que pasa por la conexión.
deviceInputNode.AddOutgoingConnection(deviceOutputNode, .5);
Aunque los nodos de salida pueden aceptar conexiones de varios nodos, es posible que desee crear una combinación intermedia de señales de uno o varios nodos antes de pasar la combinación a una salida. Por ejemplo, puede que desee establecer el nivel o aplicar efectos a un subconjunto de las señales de audio de un gráfico. Para ello, use el AudioSubmixNode. Puede conectarse a un nodo de submezcla desde uno o varios nodos de entrada u otros nodos de submezcla. En el ejemplo siguiente, se crea un nuevo nodo de submezcla con AudioGraph.CreateSubmixNode. A continuación, se añaden conexiones desde un nodo de entrada de archivo y desde un nodo de entrada de fotograma al nodo de submezcla. Por último, el nodo de submezcla está conectado a un nodo de salida de archivo.
private void CreateSubmixNode()
{
AudioSubmixNode submixNode = audioGraph.CreateSubmixNode();
fileInputNode.AddOutgoingConnection(submixNode);
frameInputNode.AddOutgoingConnection(submixNode);
submixNode.AddOutgoingConnection(fileOutputNode);
}
Inicio y parada de los nodos del grafo de audio
Cuando se llama a AudioGraph.Start , el grafo de audio comienza a procesar datos de audio. Cada tipo de nodo proporciona métodos Start y Stop que hacen que el nodo individual inicie o detenga el procesamiento de datos. Cuando se llama a AudioGraph.Stop , todo el procesamiento de audio en todos los nodos se detiene independientemente del estado de los nodos individuales, pero el estado de cada nodo se puede establecer mientras se detiene el gráfico de audio. Por ejemplo, podría llamar a Stop en un nodo individual mientras se detiene el grafo y, a continuación, llamar a AudioGraph.Start, y el nodo individual permanecerá en estado detenido.
Todos los tipos de nodo exponen la propiedad ConsumInput que, cuando se establece en false, permite al nodo continuar el procesamiento de audio, pero impide que consuma datos de audio que se introduzcan desde otros nodos.
Todos los tipos de nodo exponen el método Reset que hace que el nodo descarte los datos de audio que se encuentran actualmente en su búfer.
Adición de efectos de audio
La API de grafos de audio permite agregar efectos de audio a cada tipo de nodo de un grafo. Los nodos de salida, los nodos de entrada y los nodos de submezcla pueden tener un número ilimitado de efectos de audio, limitado solo por las funcionalidades del hardware. En el ejemplo siguiente se muestra cómo agregar el efecto de eco integrado a un nodo de submezcla.
EchoEffectDefinition echoEffect = new EchoEffectDefinition(audioGraph);
echoEffect.Delay = 1000.0;
echoEffect.Feedback = .2;
echoEffect.WetDryMix = .5;
submixNode.EffectDefinitions.Add(echoEffect);
- Todos los efectos de audio implementan IAudioEffectDefinition. Cada nodo expone una propiedad EffectDefinitions que representa la lista de efectos aplicados a ese nodo. Agregue un efecto agregando su objeto de definición a la lista.
- Hay varias clases de definición de efecto que se proporcionan en el espacio de nombres Windows.Media.Audio. Estos incluyen:
- Puede crear sus propios efectos de audio que implementen IAudioEffectDefinition y aplicarlos a cualquier nodo de un gráfico de audio.
- Cada tipo de nodo expone un método DisableEffectsByDefinition que deshabilita todos los efectos de la lista EffectDefinitions del nodo que se agregaron mediante la definición especificada. EnableEffectsByDefinition habilita los efectos con la definición especificada.
Audio espacial
AudioGraph admite audio espacial, que permite especificar la ubicación en el espacio 3D desde el que se emite audio desde cualquier nodo de entrada o submezcla. También puede especificar una forma y una dirección en las que se emite el sonido, una velocidad que se utilizará para aplicar un desplazamiento Doppler al sonido del nodo y definir un modelo de atenuación que describa cómo se atenúa el sonido con la distancia.
Para crear un emisor, primero puede crear una forma en la que el sonido se proyecta desde el emisor, que puede ser un cono o omnidirectional. La clase AudioNodeEmitterShape proporciona métodos estáticos para crear cada una de estas formas. A continuación, cree un modelo de decaimiento. Esto define cómo disminuye el volumen del audio del emisor a medida que aumenta la distancia desde el agente de escucha. El método CreateNatural crea un modelo de atenuación que emula la atenuación natural del sonido mediante un modelo de atenuación proporcional al cuadrado de la distancia. Por último, cree un objeto AudioNodeEmitterSettings. Actualmente, este objeto solo se utiliza para habilitar y deshabilitar la atenuación Doppler basada en la velocidad del audio del emisor. Llame al constructor AudioNodeEmitter y pase los objetos de inicialización que acaba de crear. De forma predeterminada, el emisor se coloca en el origen, pero puede establecer la posición del emisor con la propiedad Position .
Note
Los emisores de nodos de audio solo pueden procesar audio con formato mono con una frecuencia de muestreo de 48kHz. Si intenta usar audio estéreo o audio con una frecuencia de muestreo diferente, se producirá una excepción.
El emisor se asigna a un nodo de audio al crearlo mediante el método de creación sobrecargado para el tipo de nodo que desee. En este ejemplo, CreateFileInputNodeAsync se usa para crear un nodo de entrada de archivo a partir de un archivo especificado y el AudioNodeEmitter objeto que desea asociar con el 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;
El AudioDeviceOutputNode que genera audio del grafo al usuario tiene un objeto de escucha, a la que se accede con la propiedad Listener, que representa la ubicación, la orientación y la velocidad del usuario en el espacio 3D. Las posiciones de todos los emisores del gráfico son relativas a la posición y orientación del objeto de escucha. De forma predeterminada, el agente de escucha se encuentra en el origen (0,0,0) orientado hacia delante a lo largo del eje Z, pero puede establecer su posición y orientación con las propiedades Posición y Orientación .
deviceOutputNode.Listener.Position = new Vector3(100, 0, 0);
deviceOutputNode.Listener.Orientation = Quaternion.CreateFromYawPitchRoll(0, (float)Math.PI, 0);
Puede actualizar la ubicación, la velocidad y la dirección de los emisores en tiempo de ejecución para simular el movimiento de un origen de audio a través del espacio 3D.
AudioNodeEmitter emitter = fileInputNode.Emitter;
emitter.Position = newObjectPosition;
emitter.DopplerVelocity = newObjectPosition - oldObjectPosition;
También puede actualizar la ubicación, la velocidad y la orientación del objeto de escucha en tiempo de ejecución para simular el movimiento del usuario a través del espacio 3D.
deviceOutputNode.Listener.Position = newUserPosition;
De forma predeterminada, el audio espacial se calcula mediante el algoritmo de transferencia relativa a la cabeza (HRTF) de Microsoft para atenuar el audio en función de su forma, velocidad y posición en relación con el agente de escucha. Puede establecer la propiedad SpatialAudioModel en FoldDown para usar un método de combinación estéreo simple de simular audio espacial que sea menos preciso, pero requiere menos recursos de CPU y memoria.