Procesar tramas de audio con MediaFrameReader

En este artículo se muestra cómo usar un objeto MediaFrameReader con MediaCapture para obtener datos de audio de un origen de fotogramas multimedia. Para obtener información sobre el uso de MediaFrameReader para obtener datos de imagen, como desde un color, infrarrojos o una cámara de profundidad, consulte Procesar fotogramas multimedia con MediaFrameReader. En este artículo se proporciona información general sobre el patrón de uso del lector de fotogramas y se describen algunas características adicionales de la clase MediaFrameReader , como el uso de MediaFrameSourceGroup para recuperar fotogramas de varios orígenes al mismo tiempo.

Nota:

Las características descritas en este artículo solo están disponibles a partir de Windows 10, versión 1803.

Nota:

Hay un ejemplo de aplicación universal de Windows que muestra el uso de MediaFrameReader para mostrar fotogramas de diferentes orígenes de fotogramas, como cámaras de color, profundidad e infrarrojos. Para obtener más información, consulte Ejemplo de fotogramas de cámara.

Configurar tu proyecto

El proceso de adquisición de fotogramas de audio es en gran medida el mismo que la adquisición de otros tipos de fotogramas multimedia. Al igual que con cualquier aplicación que use MediaCapture, debes declarar que la aplicación usa la funcionalidad de cámara web antes de intentar acceder a cualquier dispositivo de cámara. Si la aplicación se capturará desde un dispositivo de audio, también debe declarar la funcionalidad del dispositivo de micrófono .

Adición de funcionalidades al manifiesto de la aplicación

  1. En Microsoft Visual Studio, en el Explorador de soluciones, abra el diseñador para el manifiesto de aplicación haciendo doble clic en el elemento package.appxmanifest.
  2. Seleccione la pestaña Funcionalidades.
  3. Active la casilla Cámara web y la casilla Micrófono.
  4. Para obtener acceso a la biblioteca de imágenes y vídeos, active las casillas Biblioteca de imágenes y Biblioteca de vídeos.

Selección de orígenes de fotogramas y grupos de orígenes de fotogramas

El primer paso para capturar fotogramas de audio es inicializar un objeto MediaFrameSource que representa el origen de los datos de audio, como un micrófono u otro dispositivo de captura de audio. Para ello, debe crear una nueva instancia del objeto MediaCapture. En este ejemplo, la única configuración de inicialización de MediaCapture es establecer StreamingCaptureMode para indicar que queremos transmitir audio desde el dispositivo de captura.

Después de llamar a MediaCapture.InitializeAsync, puede obtener la lista de orígenes de fotogramas multimedia accesibles con la propiedad FrameSources. En este ejemplo se usa una consulta Linq para seleccionar todos los orígenes de fotogramas en los que mediaFrameSourceInfo que describe el origen de fotogramas tiene un valor MediaStreamType de Audio, lo que indica que el origen multimedia genera datos de audio.

Si la consulta devuelve uno o varios orígenes de fotogramas, puede comprobar la propiedad CurrentFormat para ver si el origen admite el formato de audio que desea, en este ejemplo, datos de audio flotantes. Compruebe AudioEncodingProperties para asegurarse de que la codificación de audio que desee sea compatible con el origen.

mediaCapture = new MediaCapture();
MediaCaptureInitializationSettings settings = new MediaCaptureInitializationSettings()
{
    StreamingCaptureMode = StreamingCaptureMode.Audio,
};
await mediaCapture.InitializeAsync(settings);

var audioFrameSources = mediaCapture.FrameSources.Where(x => x.Value.Info.MediaStreamType == MediaStreamType.Audio);

if (audioFrameSources.Count() == 0)
{
    Debug.WriteLine("No audio frame source was found.");
    return;
}

MediaFrameSource frameSource = audioFrameSources.FirstOrDefault().Value;

MediaFrameFormat format = frameSource.CurrentFormat;
if (format.Subtype != MediaEncodingSubtypes.Float)
{
    return;
}

if (format.AudioEncodingProperties.ChannelCount != 1
    || format.AudioEncodingProperties.SampleRate != 48000)
{
    return;
}

Crear e iniciar MediaFrameReader

Obtenga una nueva instancia de MediaFrameReader llamando a MediaCapture.CreateFrameReaderAsync y pasando el objeto MediaFrameSource seleccionado en el paso anterior. De forma predeterminada, los fotogramas de audio se obtienen en modo almacenado en búfer, lo que hace menos probable que se quiten los fotogramas, aunque esto puede ocurrir si no está procesando fotogramas de audio lo suficientemente rápido como para rellenar el búfer de memoria alloted del sistema.

Registre un controlador para el evento MediaFrameReader.FrameArrived , que genera el sistema cuando hay disponible un nuevo fotograma de datos de audio. Llame a StartAsync para comenzar la adquisición de fotogramas de audio. Si el lector de fotogramas no se inicia, el valor de estado devuelto desde la llamada tendrá un valor distinto de Correcto.

mediaFrameReader = await mediaCapture.CreateFrameReaderAsync(frameSource);

// Optionally set acquisition mode. Buffered is the default mode for audio.
mediaFrameReader.AcquisitionMode = MediaFrameReaderAcquisitionMode.Buffered;

mediaFrameReader.FrameArrived += MediaFrameReader_AudioFrameArrived;

var status = await mediaFrameReader.StartAsync();

if (status != MediaFrameReaderStartStatus.Success)
{
    Debug.WriteLine("The MediaFrameReader couldn't start.");
}

En el controlador de eventos FrameArrived, llame a TryAcquireLatestFrame en el objeto MediaFrameReader pasado como remitente al controlador para intentar recuperar una referencia al marco multimedia más reciente. Tenga en cuenta que este objeto puede ser NULL, por lo que siempre debe comprobar antes de usar el objeto . Los errores tipográficos del marco multimedia encapsulado en la clase MediaFrameReference de TryAcquireLatestFrame dependen del tipo de origen de fotogramas o orígenes que configuró el lector de fotogramas para adquirir. Dado que el lector de fotogramas de este ejemplo se configuró para adquirir fotogramas de audio, obtiene el marco subyacente mediante la propiedad AudioMediaFrame.

Este método auxiliar ProcessAudioFrame en el ejemplo siguiente muestra cómo obtener un AudioFrame que proporciona información como la marca de tiempo del marco y si es discontinua del objeto AudioMediaFrame. Para leer o procesar los datos de ejemplo de audio, deberá obtener el objeto AudioBuffer del objeto AudioMediaFrame , crear un IMemoryBufferReference y, a continuación, llamar al método COM IMemoryBufferByteAccess::GetBuffer para recuperar los datos. Consulte la nota debajo de la lista de código para obtener más información sobre el acceso a los búferes nativos.

El formato de los datos depende del origen del marco. En este ejemplo, al seleccionar un origen de fotogramas multimedia, se ha hecho explícitamente que el origen de fotograma seleccionado usó un único canal de datos flotantes. El resto del código de ejemplo muestra cómo determinar la duración y el recuento de muestras de los datos de audio en el marco.

private void MediaFrameReader_AudioFrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
    using (MediaFrameReference reference = sender.TryAcquireLatestFrame())
    {
        if (reference != null)
        {
            ProcessAudioFrame(reference.AudioMediaFrame);
        }
    }
}
unsafe private void ProcessAudioFrame(AudioMediaFrame audioMediaFrame)
{

    using (AudioFrame audioFrame = audioMediaFrame.GetAudioFrame())
    using (AudioBuffer buffer = audioFrame.LockBuffer(AudioBufferAccessMode.Read))
    using (IMemoryBufferReference reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacityInBytes;
        float* dataInFloat;


        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacityInBytes);
        
        // The requested format was float
        dataInFloat = (float*)dataInBytes;

        // Get the number of samples by multiplying the duration by sampling rate: 
        // duration [s] x sampling rate [samples/s] = # samples 

        // Duration can be gotten off the frame reference OR the audioFrame
        TimeSpan duration = audioMediaFrame.FrameReference.Duration;

        // frameDurMs is in milliseconds, while SampleRate is given per second.
        uint frameDurMs = (uint)duration.TotalMilliseconds;
        uint sampleRate = audioMediaFrame.AudioEncodingProperties.SampleRate;
        uint sampleCount = (frameDurMs * sampleRate) / 1000;

    }
}

Nota:

Para poder operar en los datos de audio, debe acceder a un búfer de memoria nativa. Para ello, debe usar la interfaz COM IMemoryBufferByteAccess incluyendo la lista de código siguiente. Las operaciones en el búfer nativo deben realizarse en un método que use la palabra clave unsafe . También debe activar la casilla para permitir código no seguro en la pestaña Compilar del cuadro de diálogo Proyecto -> Propiedades .

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

Información adicional sobre el uso de MediaFrameReader con datos de audio

Puede recuperar el objeto AudioDeviceController asociado al origen de fotogramas de audio accediendo a la propiedad MediaFrameSource.Controller. Este objeto se puede usar para obtener o establecer las propiedades de secuencia del dispositivo de captura o para controlar el nivel de captura. En el ejemplo siguiente semuta el dispositivo de audio para que el lector de fotogramas continúe adquirido, pero todas las muestras tienen un valor de 0.

audioDeviceController.Muted = true;

Puede usar un objeto AudioFrame para pasar datos de audio capturados por un origen de fotogramas multimedia a un AudioGraph. Pase el marco al método AddFrame de un AudioFrameInputNode. Para obtener más información sobre el uso de gráficos de audio para capturar, procesar y mezclar señales de audio, consulte Gráficos de audio.