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 mais sobre como usar um MediaFrameReader para obter dados de imagem, como de uma câmera colorida, infravermelha ou de profundidade, consulte Processar quadros de mídia com MediaFrameReader. Esse artigo fornece uma visão geral do padrão de uso do leitor de quadros e discute alguns recursos adicionais da classe MediaFrameReader , como o uso de MediaFrameSourceGroup para recuperar quadros de várias fontes ao mesmo tempo.
Observação
Os recursos discutidos 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 de MediaFrameReader para exibir quadros de diferentes fontes de quadros, incluindo cores, profundidade e câmeras infravermelhas. Para obter mais informações, consulte Exemplo de quadros de câmera.
Configurar seu projeto
O processo de aquisição de quadros de áudio é basicamente o mesmo que a aquisição de outros tipos de quadros de mídia. Assim como acontece com qualquer aplicativo que usa MediaCapture, você deve declarar que seu aplicativo usa o recurso de webcam antes de tentar acessar qualquer dispositivo de câmera. Se o aplicativo for capturar de um dispositivo de áudio, você também deverá declarar a funcionalidade do dispositivo de microfone .
Adicionar funcionalidades ao manifesto do aplicativo
- No Microsoft Visual Studio, no Gerenciador de Soluções, abra o designer do manifesto do aplicativo clicando duas vezes no item package.appxmanifest.
- Selecione a guia Funcionalidades.
- Marque a caixa da Webcam e a caixa do Microfone.
- Para acessar a biblioteca de Imagens e Vídeos, marque as caixas Biblioteca de Imagens e a caixa Biblioteca de Vídeos.
Selecionar fontes de quadros e grupos de fontes de quadros
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 . Para este exemplo, a única configuração de inicialização para o MediaCapture é definir o StreamingCaptureMode para indicar que queremos transmitir áudio do dispositivo de captura.
Depois de chamar MediaCapture.InitializeAsync, você pode obter a lista de fontes de quadro 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 Audio, indicando que a fonte de mídia produz dados de áudio.
Se a consulta retornar uma ou mais fontes de quadro, você poderá verificar a propriedade CurrentFormat para ver se a fonte dá suporte ao formato de áudio desejado - neste exemplo, dados de áudio flutuantes. Verifique o AudioEncodingProperties para garantir que a codificação de áudio desejada seja compatível com a origem.
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 chamando 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 que os quadros sejam descartados, embora isso ainda possa ocorrer se você não estiver processando quadros de áudio rápido o suficiente e preencher o buffer de memória alocado do sistema.
Registre um manipulador para o evento MediaFrameReader.FrameArrived , que é gerado pelo sistema quando um novo quadro de dados de áudio está disponível. Chame StartAsync para iniciar a aquisição de quadros de áudio. Se o leitor de quadros não for iniciado, o valor de status retornado da chamada terá um valor diferente de Êxito.
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.");
}
No manipulador de eventos FrameArrived, chame TryAcquireLatestFrame no objeto MediaFrameReader passado como o remetente para o manipulador para tentar recuperar uma referência ao quadro de mídia mais recente. Observe que esse objeto pode ser nulo, portanto, você deve sempre verificar antes de usar o objeto. Os tipos de quadro de mídia encapsulados no MediaFrameReference retornado de TryAcquireLatestFrame dependem do tipo de fonte de quadro ou fontes que você configurou o leitor de quadro para adquirir. Como o leitor de quadros neste exemplo foi configurado para adquirir quadros de áudio, ele obtém o quadro subjacente usando a propriedade AudioMediaFrame.
Esse método auxiliar ProcessAudioFrame no exemplo a seguir mostra como obter um AudioFrame que fornece informações como o carimbo de data/hora do quadro e se ele é descontínuo do objeto AudioMediaFrame. Para ler ou processar os dados de exemplo de áudio, você precisará obter o objeto AudioBuffer do objeto AudioMediaFrame, criar um IMemoryBufferReference e chamar o método COM IMemoryBufferByteAccess::GetBuffer para recuperar os dados. Consulte a observação abaixo da listagem de código para obter mais informações sobre como acessar buffers nativos.
O formato dos dados depende da origem do quadro. Neste exemplo, ao selecionar uma fonte de quadro de mídia, garantimos explicitamente que a fonte de quadro selecionada usava um único canal de dados flutuantes. O restante do código de exemplo mostra como determinar a duração e a contagem de exemplo 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 nos dados de áudio, você deve acessar um buffer de memória nativo. Para fazer isso, você deve usar a interface COM IMemoryBufferByteAccess incluindo a listagem de código abaixo. As operações no buffer nativo devem ser executadas em um método que usa a palavra-chave unsafe . 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 MediaFrameReader com dados de áudio
Você pode recuperar o AudioDeviceController associado à origem do quadro de áudio acessando a propriedade MediaFrameSource.Controller. Esse objeto pode ser usado para obter ou definir as propriedades de fluxo do dispositivo de captura ou para controlar o nível de captura. O exemplo a seguir silencia o dispositivo de áudio para que os quadros continuem a ser adquiridos pelo leitor de quadros, mas todos os exemplos têm o valor de 0.
audioDeviceController.Muted = true;
Você pode usar um objeto AudioFrame para passar dados de áudio capturados por uma fonte de quadro de mídia para um AudioGraph. Passe o quadro para o método AddFrame de um AudioFrameInputNode. Para obter mais informações sobre como usar gráficos de áudio para capturar, processar e mixar sinais de áudio, consulte Gráficos de áudio.