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, un infrarrojo 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 una muestra de aplicación universal de Windows que muestra el uso de MediaFrameReader para mostrar fotogramas de distintos orígenes de fotogramas, lo que incluye cámaras a color, de profundidad y de infrarrojos. Para obtener más información, consulta Camera frames sample (Muestra 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 tu aplicación usa la funcionalidad cámara web antes de intentar acceder a cualquier dispositivo de cámara. Si la aplicación captura desde un dispositivo de audio, también debes declarar la funcionalidad micrófono del dispositivo.

Agregar funcionalidades al manifiesto de la aplicación

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

Seleccionar orígenes de fotogramas y grupos de orígenes de fotogramas

El primer paso para capturar fotogramas de audio consiste en inicializar un objeto MediaFrameSource que represente 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 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

Para obtener una nueva instancia de MediaFrameReader , llame a MediaCapture.CreateFrameReaderAsync y pase 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 que sea menos probable que se quiten los fotogramas, aunque esto todavía puede ocurrir si no está procesando fotogramas de audio lo suficientemente rápido como para rellenar el búfer de memoria alloted del sistema.

Registra un controlador para el evento MediaFrameReader.FrameArrived , que genera el sistema cuando hay disponible una nueva trama 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 por 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 fotograma multimedia más reciente. Tenga en cuenta que este objeto puede ser NULL, por lo que siempre debe comprobarlo antes de usar el objeto . Los errores tipográficos del marco multimedia encapsulado en la clase MediaFrameReference devuelta desde TryAcquireLatestFrame dependen del tipo de origen de fotogramas o de los orígenes que configuró el lector de fotogramas para adquirir. Puesto que el lector de fotogramas de este ejemplo se configuró para adquirir fotogramas de audio, obtiene el fotograma 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 fotograma y si no 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 de la trama. En este ejemplo, al seleccionar un origen de fotogramas multimedia, hemos determinado 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 fotograma.

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 funcionar en los datos de audio, debe tener acceso a un búfer de memoria nativa. Para ello, debes usar la interfaz COM IMemoryBufferByteAccess incluyendo la lista de código siguiente. Las operaciones en el búfer nativo se deben realizar 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 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 siga adquiriendo fotogramas, 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.