Share via


Processar quadros de áudio com o MediaFrameReader

Este artigo mostra como usar um MediaFrameReader com MediaCapture para obter dados de áudio de uma fonte de quadro de mídia. Para saber como usar um MediaFrameReader para obter dados de imagem, como cor, infravermelho ou profundidade da câmera, consulte Processar quadros de mídia com o MediaFrameReader. Esse artigo fornece uma visão geral do padrão de uso do leitor de quadros e descreve alguns recursos adicionais da classe MediaFrameReader, como o uso de MediaFrameSourceGroup para recuperar os quadros de várias fontes ao mesmo tempo.

Observação

Os recursos abordados neste artigo só estão disponíveis a partir do Windows 10, versão 1803.

Observação

Há um exemplo de aplicativo Universal do Windows que demonstra o uso do MediaFrameReader para exibir quadros de origens diferentes, incluindo câmeras em cores, de profundidade e infravermelho. Para obter mais informações, consulte Exemplo de quadros de câmera.

Configurando seu projeto

O processo de aquisição dos quadros de áudio é basicamente o mesmo para outros tipos de quadros de mídia. Como com qualquer aplicativo que usa MediaCapture, você deve declarar que seu aplicativo usa a funcionalidade de webcam antes de tentar acessar qualquer dispositivo de câmera. Se seu aplicativo fizer a captura de um dispositivo de áudio, você deve declarar também a funcionalidade do dispositivo microfone.

Adicionar funcionalidades ao manifesto do aplicativo

  1. No Microsoft Visual Studio, no Gerenciador de Soluções, abra o designer do manifesto do aplicativo clicando duas vezes no item package.appxmanifest.
  2. Selecione a guia Funcionalidades.
  3. Marque a caixa da Webcam e a caixa do Microfone.
  4. Para acessar a biblioteca de Imagens e Vídeos, marque as caixas da Biblioteca de imagens e da Biblioteca de vídeos.

Selecionar origens de quadro e grupos de origens de quadro

A primeira etapa na captura de quadros de áudio é inicializar um MediaFrameSource, que representa a origem dos dados de áudio, como um microfone ou outro dispositivo de captura de áudio. Para fazer isso, você deve criar uma nova instância do objeto MediaCapture. Nesse exemplo, a única configuração de inicialização do MediaCapture é StreamingCaptureMode para indicar que queremos fazer o streaming de áudio do dispositivo de captura.

Depois de chamar MediaCapture.InitializeAsync, você pode obter a lista de fontes de quadros de mídia acessíveis com a propriedade FrameSources. Este exemplo usa uma consulta Linq para selecionar todas as fontes de quadro em que o MediaFrameSourceInfo que descreve a origem do quadro tem um MediaStreamType de Áudio, indicando que a fonte de mídia produz dados de áudio.

Se a consulta retorna uma ou mais fontes de quadros, você pode verificar a propriedade CurrentFormat para ver se a fonte é compatível com o formato de áudio desejado. Nesse exemplo, os dados de áudio flutuantes. Verifique AudioEncodingProperties para garantir que a codificação de áudio desejada seja compatível com a fonte.

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;
}

Criar e iniciar o MediaFrameReader

Obtenha uma nova instância de MediaFrameReader ao chamar MediaCapture.CreateFrameReaderAsync, passando o objeto MediaFrameSource selecionado na etapa anterior. Por padrão, os quadros de áudio são obtidos no modo de buffer, tornando menos provável o descarte dos quadros, embora isso ainda possa ocorrer se você não estiver processando quadros áudio rápidos o suficiente e preencher o buffer de memória alocado do sistema.

Registre um manipulador para o evento MediaFrameReader.FrameArrived, que é acionado pelo sistema quando um novo quadro de dados de áudio está disponível. Chame StartAsync para começar a aquisição de quadro de áudio. Se o leitor de quadro não for iniciado, o valor do status retornado da chamada terá um valor diferente de Success.

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.");
}

Na manipulador de evento FrameArrived, chame TryAcquireLatestFrame no objeto MediaFrameReader passado como remetente para o manipulador tentar recuperar uma referência ao quadro de mídia mais recente. Observe que esse objeto pode ser nulo, portanto, você sempre deve verificar antes de usar o objeto. O tipo de quadro de mídia encapsulado no MediaFrameReference retornado de TryAcquireLatestFrame depende do tipo de fonte ou fontes de quadro que você configurou o leitor de quadro para obter. Como o leitor de quadro do exemplo foi configurado para obter quadros de áudio, ele obtém o quadro subjacente usando a propriedade AudioMediaFrame.

Esse método auxiliar ProcessAudioFrame no exemplo abaixo mostra como obter um AudioFrame que fornece informações, como o carimbo de data e hora do quadro e se é descontínuo em relação ao objeto AudioMediaFrame. Para ler ou processar os dados de amostra de áudio, você precisa obter o objeto AudioBuffer do objeto AudioMediaFrame, criar um IMemoryBufferReference e chamar o método COM IMemoryBufferByteAccess::GetBuffer para recuperar os dados. Veja a nota abaixo da lista de código para obter mais informações sobre como acessar buffers nativos.

O formato dos dados depende da fonte de quadros. Neste exemplo, ao selecionar uma fonte de quadros de mídia, nos certificamos que a origem do quadro selecionado usou um único canal de dados de ponto flutuante. O restante do código de exemplo mostra como determinar a duração e a contagem da amostra para os dados de áudio no quadro.

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;

    }
}

Observação

Para operar os dados de áudio, você deve acessar um buffer de memória nativa. Para fazer isso, você deve usar a interface COM IMemoryBufferByteAccess ao incluir a lista de código abaixo. As operações no buffer nativo devem ser realizadas em um método que usa a palavra-chave inseguro. Você também precisa marcar a caixa para permitir código não seguro na guia Compilar da caixa de diálogo Projeto –> Propriedades.

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

Informações adicionais sobre como usar o MediaFrameReader com dados de áudio

Você pode recuperar o AudioDeviceController associado à fonte do quadro de áudio ao acessar a propriedade MediaFrameSource.Controller. Esse objeto pode ser usado para obter ou definir as propriedades de streaming do dispositivo de captura ou para controlar o nível de captura. O exemplo a seguir ativa o mudo no dispositivo de áudio para que os quadros continuem a ser adquiridos pelo leitor de quadro, mas todas as amostras têm o valor 0.

audioDeviceController.Muted = true;

Você pode usar um objeto AudioFrame a fim de passar dados de áudio captador por uma fonte de quadro de mídia em um AudioGraph. Passe o quadro para o método AddFrame de um AudioFrameInputNode. Para obter mais informações sobre como usar os gráficos de áudio para capturar, processar e misturar sinais de áudio, consulte Gráficos de áudio.