Przetwarzanie klatek dźwiękowych za pomocą MediaFrameReader

W tym artykule pokazano, jak używać elementu MediaFrameReader z funkcją MediaCapture do pobierania danych audio ze źródła ramki multimedialnej. Aby dowiedzieć się więcej o korzystaniu z elementu MediaFrameReader w celu uzyskiwania danych obrazu, na przykład z kamery kolorowej, kamery na podczerwień lub kamery głębi, zobacz Przetwarzanie ramek multimedialnych za pomocą elementu MediaFrameReader. Ten artykuł zawiera ogólne omówienie wzorca użycia czytnika ramek i omówienie niektórych dodatkowych funkcji klasy MediaFrameReader , takich jak używanie elementu MediaFrameSourceGroup do pobierania ramek z wielu źródeł w tym samym czasie.

Uwaga / Notatka

Funkcje omówione w tym artykule są dostępne tylko od Windows 10 w wersji 1803.

Konfigurowanie projektu

Proces uzyskiwania ramek audio jest w dużej mierze taki sam jak uzyskiwanie innych typów ramek multimedialnych. Podobnie jak w przypadku każdej aplikacji korzystającej z aplikacji MediaCapture, należy zadeklarować, że aplikacja korzysta z możliwości kamery internetowej przed podjęciem próby uzyskania dostępu do dowolnego urządzenia fotograficznego. Jeśli aplikacja będzie przechwytywać dźwięk z urządzenia audio, należy również zadeklarować funkcję urządzenia mikrofon.

Wybieranie źródeł klatek i grup źródeł klatek

Pierwszym krokiem przechwytywania ramek audio jest zainicjowanie elementu MediaFrameSource reprezentującego źródło danych audio, takie jak mikrofon lub inne urządzenie do przechwytywania dźwięku. W tym celu należy utworzyć nowe wystąpienie obiektu MediaCapture . W tym przykładzie jedynym ustawieniem inicjalizacji dla MediaCapture jest ustawienie właściwości StreamingCaptureMode, aby wskazać, że chcemy przesyłać strumieniowo dźwięk z urządzenia przechwytującego.

Po wywołaniu metody MediaCapture.InitializeAsync można pobrać listę dostępnych źródeł ramek multimedialnych za pomocą właściwości FrameSources . Ten przykład wybiera wszystkie źródła ramek, w których element MediaFrameSourceInfo opisujący źródło ramki ma wartość MediaStreamType równą Audio, co wskazuje, że źródło multimediów generuje dane audio.

Jeśli zapytanie zwraca co najmniej jedno źródło ramki, możesz sprawdzić właściwość CurrentFormat , aby sprawdzić, czy źródło obsługuje pożądany format audio — w tym przykładzie dane audio zmiennoprzecinkowe. Sprawdź element AudioEncodingProperties, aby upewnić się, że żądane kodowanie dźwięku jest obsługiwane przez źródło.

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

Utwórz i uruchom obiekt MediaFrameReader

Uzyskaj nowe wystąpienie MediaFrameReader, wywołując metodę MediaCapture.CreateFrameReaderAsync i przekazując obiekt MediaFrameSource wybrany w poprzednim kroku. Domyślnie ramki audio są pobierane w trybie buforowanym, co zmniejsza prawdopodobieństwo ich gubienia, chociaż nadal może do tego dojść, jeśli nie przetwarzasz ramek audio wystarczająco szybko i zapełnisz przydzielony przez system bufor pamięci.

Zarejestruj program obsługi zdarzenia MediaFrameReader.FrameArrived , który jest zgłaszany przez system, gdy jest dostępna nowa ramka danych dźwiękowych. Wywołaj StartAsync, aby rozpocząć przechwytywanie ramek audio. Jeśli nie można uruchomić czytnika ramek, wartość statusu zwrócona przez wywołanie będzie inna niż 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.");
}

W procedurze obsługi zdarzenia FrameArrived wywołaj metodę TryAcquireLatestFrame na obiekcie MediaFrameReader przekazanym do procedury obsługi jako argument sender, aby spróbować pobrać odwołanie do najnowszej ramki multimedialnej. Należy pamiętać, że ten obiekt może mieć wartość null, dlatego należy zawsze sprawdzać przed użyciem obiektu. Typy ramek multimedialnych opakowanych w obiekcie MediaFrameReference zwracanym przez TryAcquireLatestFrame zależą od typu źródła ramek lub źródeł, z których skonfigurowano czytnik ramek do pobierania. Ponieważ czytnik ramek w tym przykładzie został skonfigurowany do uzyskiwania ramek dźwiękowych, pobiera ramkę bazową przy użyciu właściwości AudioMediaFrame .

Ta metoda pomocnika ProcessAudioFrame w poniższym przykładzie pokazuje, jak uzyskać element AudioFrame , który dostarcza informacje, takie jak znacznik czasu ramki i czy jest on nieciągły z obiektu AudioMediaFrame . Aby odczytać lub przetworzyć przykładowe dane audio, należy pobrać obiekt AudioBuffer z obiektu AudioMediaFrame , utworzyć element IMemoryBufferReference, a następnie wywołać metodę COM IMemoryBufferByteAccess::GetBuffer , aby pobrać dane. Zobacz notatkę poniżej listy kodu, aby uzyskać więcej informacji na temat uzyskiwania dostępu do buforów natywnych.

Format tych danych zależy od źródła ramki. W tym przykładzie podczas wybierania źródła ramki multimedialnej jawnie upewniliśmy się, że wybrane źródło ramki używało dwóch kanałów danych zmiennoprzecinkowych. W pozostałej części przykładowego kodu pokazano, jak określić czas trwania i przykładową liczbę danych audio w ramce.

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;

    }
}

Uwaga / Notatka

Aby można było wykonywać operacje na danych audio, należy uzyskać dostęp do natywnego buforu pamięci. W tym celu należy użyć interfejsu IMemoryBufferByteAccess COM, uwzględniając poniższy kod. Operacje na buforze natywnym muszą być wykonywane w metodzie używającej niebezpiecznego słowa kluczowego. Należy również zaznaczyć to pole wyboru, aby zezwolić na kod niebezpieczny na karcie Kompilacja w oknie dialogowym Projekt -> Właściwości.

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

Dodatkowe informacje na temat korzystania z elementu MediaFrameReader z danymi audio

Możesz pobrać obiekt AudioDeviceController skojarzony ze źródłem ramki audio, uzyskując dostęp do właściwości MediaFrameSource.Controller. Ten obiekt może służyć do pobierania lub ustawiania właściwości strumienia urządzenia przechwytywania lub kontrolowania poziomu przechwytywania. Poniższy przykład wycisza urządzenie audio, tak aby czytnik ramek nadal przechwytywał ramki, ale wszystkie próbki miały wartość 0.

m_audioDeviceController.Muted = true;

Możesz użyć obiektu AudioFrame , aby przekazać dane audio przechwycone przez źródło ramki multimedialnej do audiograph. Przekaż ramkę do metody AddFrame w elemencie AudioFrameInputNode. Aby uzyskać więcej informacji na temat używania grafów audio do przechwytywania, przetwarzania i mieszania sygnałów dźwiękowych, zobacz Audio graphs (Wykresy audio).