Elaborare fotogrammi audio con MediaFrameReader

Questo articolo illustra come usare MediaFrameReader con MediaCapture per ottenere dati audio da un'origine di fotogrammi multimediali. Per informazioni sull'uso di un MediaFrameReader per ottenere dati di immagine, ad esempio da una fotocamera a colori, infrarossi o profondità, vedere Elaborare fotogrammi multimediali con MediaFrameReader. Questo articolo offre una panoramica generale del modello di utilizzo del lettore di fotogrammi e illustra alcune funzionalità aggiuntive della classe MediaFrameReader , ad esempio l'uso di MediaFrameSourceGroup per recuperare fotogrammi da più origini contemporaneamente.

Annotazioni

Le funzionalità descritte in questo articolo sono disponibili solo a partire da Windows 10 versione 1803.

Configurazione del progetto

Il processo di acquisizione di fotogrammi audio è in gran parte uguale all'acquisizione di altri tipi di fotogrammi multimediali. Come per qualsiasi app che usa MediaCapture, devi dichiarare che l'app usa la funzionalità webcam prima di tentare di accedere a qualsiasi dispositivo fotocamera. Se l'app acquisisce da un dispositivo audio, devi anche dichiarare la funzionalità del dispositivo microfono .

Selezionare le origini dei frame e i gruppi di origine dei frame

Il primo passaggio nell'acquisizione di fotogrammi audio consiste nell'inizializzare un Oggetto MediaFrameSource che rappresenta l'origine dei dati audio, ad esempio un microfono o un altro dispositivo di acquisizione audio. A tale scopo, è necessario creare una nuova istanza dell'oggetto MediaCapture . Per questo esempio, l'unica impostazione di inizializzazione per MediaCapture è l'impostazione di StreamingCaptureMode per indicare che si vuole trasmettere l'audio dal dispositivo di acquisizione.

Dopo aver chiamato MediaCapture.InitializeAsync, è possibile ottenere l'elenco delle origini dei fotogrammi multimediali accessibili con la proprietà FrameSources . In questo esempio vengono selezionate tutte le sorgenti di frame la cui MediaFrameSourceInfo, che descrive la sorgente di frame, ha un MediaStreamType di Audio, a indicare che la sorgente multimediale produce dati audio.

Se la query restituisce una o più origini frame, è possibile controllare la proprietà CurrentFormat per verificare se l'origine supporta il formato audio desiderato, in questo esempio i dati audio float. Controllare AudioEncodingProperties per assicurarsi che la codifica audio desiderata sia supportata dall'origine.

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

var audioFrameSources = m_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 != 2
    || format.AudioEncodingProperties.SampleRate != 48000)
{
    return;
}

Creare e avviare il MediaFrameReader

Ottenere una nuova istanza di MediaFrameReader chiamando MediaCapture.CreateFrameReaderAsync, passando l'oggetto MediaFrameSource selezionato nel passaggio precedente. Per impostazione predefinita, i fotogrammi audio vengono ottenuti in modalità memorizzata nel buffer, rendendo meno probabile che i fotogrammi vengano eliminati, anche se ciò può verificarsi ancora se non si elaborano fotogrammi audio abbastanza veloci e riempiono il buffer di memoria allocato del sistema.

Registrare un gestore per l'evento MediaFrameReader.FrameArrived , generato dal sistema quando è disponibile un nuovo frame di dati audio. Chiama StartAsync per avviare l'acquisizione di fotogrammi audio. Se l'avvio del lettore di fotogrammi non riesce, il valore di stato restituito dalla chiamata avrà un valore diverso da Success.

m_mediaFrameReader = await m_mediaCapture.CreateFrameReaderAsync(frameSource);

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

m_mediaFrameReader.FrameArrived += MediaFrameReader_AudioFrameArrived;

var status = await m_mediaFrameReader.StartAsync();

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

Nel gestore eventi FrameArrived chiamare TryAcquireLatestFrame sull'oggetto MediaFrameReader passato come mittente al gestore per tentare di recuperare un riferimento al fotogramma multimediale più recente. Si noti che questo oggetto può essere Null, quindi è consigliabile controllare sempre prima di usare l'oggetto . I tipi di frame multimediali contenuti in MediaFrameReference restituito da TryAcquireLatestFrame dipendono dal tipo di origine del frame, o dalle origini del frame, che hai configurato nel lettore di frame per l'acquisizione. Poiché il lettore di fotogrammi in questo esempio è stato configurato per acquisire fotogrammi audio, ottiene il fotogramma sottostante usando la proprietà AudioMediaFrame .

Questo metodo helper ProcessAudioFrame nell'esempio seguente mostra come ottenere un AudioFrame che fornisce informazioni come il timestamp del frame e se è discontinuo dall'oggetto AudioMediaFrame . Per leggere o elaborare i dati di esempio audio, è necessario ottenere l'oggetto AudioBuffer dall'oggetto AudioMediaFrame , creare un oggetto IMemoryBufferReference e quindi chiamare il metodo COM IMemoryBufferByteAccess::GetBuffer per recuperare i dati. Per ulteriori informazioni sull'accesso ai buffer nativi, consulta la nota sotto il listato del codice.

Il formato dei dati dipende dall'origine del frame. In questo esempio, quando si seleziona un'origine frame multimediale, è stato specificato in modo esplicito che l'origine frame selezionata ha usato due canali di dati float. Il resto del codice di esempio mostra come determinare la durata e il conteggio dei campioni per i dati audio nel frame.

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;

        var memoryBuffer = reference.As<IMemoryBufferByteAccess>();
        memoryBuffer.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;

    }
}

Annotazioni

Per operare sui dati audio, è necessario accedere a un buffer di memoria nativo. A tale scopo, è necessario usare l'interfaccia COM IMemoryBufferByteAccess includendo l'elenco di codice riportato di seguito. Le operazioni sul buffer nativo devono essere eseguite in un metodo che usa la parola chiave unsafe . È anche necessario selezionare la casella per consentire il codice non sicuro nella scheda Compilazione della finestra di dialogo Project -> Proprietà.

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

Altre informazioni sull'uso di MediaFrameReader con dati audio

È possibile recuperare l'oggetto AudioDeviceController associato all'origine dei fotogrammi audio accedendo alla proprietà MediaFrameSource.Controller . Questo oggetto può essere usato per ottenere o impostare le proprietà del flusso del dispositivo di acquisizione o per controllare il livello di acquisizione. Nell'esempio seguente il dispositivo audio viene disattivato in modo che i fotogrammi continuino a essere acquisiti dal lettore di fotogrammi, ma tutti gli esempi hanno il valore 0.

m_audioDeviceController.Muted = true;

È possibile utilizzare un oggetto AudioFrame per passare i dati audio acquisiti da un'origine di fotogrammi multimediali in un oggetto AudioGraph. Passa il frame al metodo AddFrame di un AudioFrameInputNode. Per altre informazioni sull'uso di grafici audio per acquisire, elaborare e combinare segnali audio, vedi Grafici audio.