MediaFrameReader を使用してオーディオ フレームを処理する

この記事では、メディア フレーム ソースからオーディオ データを取得するために、MediaFrameReaderMediaCapture と共に使用する方法について説明します。 MediaFrameReader を使用して色、赤外線、深度カメラなどの画像データを取得する方法については、「MediaFrameReader を使用したメディア フレームの処理」を参照してください。 この記事では、フレーム リーダーの使用パターンの一般的な概要について説明し、MediaFrameSourceGroup を使用して複数のソースから同時にフレームを取得するなど、MediaFrameReader クラスのいくつかの追加機能について説明します。

Note

この記事で説明する機能は、Windows 10 バージョン 1803 以降でのみ使用できます。

プロジェクトの設定

オーディオ フレームを取得するプロセスは、他の種類のメディア フレームを取得する場合とほとんど同じです。 MediaCapture を使用するアプリと同様に、カメラ デバイスへのアクセスを試みる前に、アプリで Web カメラ機能を使用することを宣言する必要があります。 アプリがオーディオ デバイスからキャプチャする場合は、 マイク デバイスの機能も宣言する必要があります。

フレーム ソースとフレーム ソース グループの選択

オーディオ フレームをキャプチャする最初の手順は、マイクやその他のオーディオ キャプチャ デバイスなどのオーディオ データのソースを表す MediaFrameSource を初期化することです。 これを行うには、 MediaCapture オブジェクトの新しいインスタンスを作成する必要があります。 この例では、 MediaCapture の唯一の初期化設定は、キャプチャ デバイスからオーディオをストリーミングすることを示す StreamingCaptureMode を設定することです。

MediaCapture.InitializeAsync を呼び出した後、FrameSources プロパティを使用してアクセス可能なメディア フレーム ソースの一覧を取得できます。 次の使用例は、フレーム ソースを記述する MediaFrameSourceInfoAudio の MediaStreamType があり、メディア ソースが オーディオ データを生成することを示す、すべてのフレーム ソースを選択します。

クエリから 1 つ以上のフレーム ソースが返された場合は、 CurrentFormat プロパティを調べて、ソースが必要なオーディオ形式 (この例では float オーディオ データ) をサポートしているかどうかを確認できます。 AudioEncodingProperties を調べて、必要なオーディオ エンコードがソースでサポートされていることを確認します。

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

MediaFrameReader を作成して起動する

MediaCapture.CreateFrameReaderAsync を呼び出し、前の手順で選択した MediaFrameSource オブジェクトを渡して、MediaFrameReader の新しいインスタンスを取得します。 既定では、オーディオ フレームはバッファー モードで取得されるため、フレームがドロップされる可能性は低くなりますが、オーディオ フレームを十分に高速に処理せず、システムの割り当てられたメモリ バッファーがいっぱいになった場合にも発生する可能性があります。

MediaFrameReader.FrameArrived イベントのハンドラーを登録します。これは、オーディオ データの新しいフレームが使用可能になったときにシステムによって発生します。 StartAsync を呼び出して、オーディオ フレームの取得を開始します。 フレーム リーダーの起動に失敗した場合、呼び出しから返される状態の値は 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.");
}

FrameArrived イベント ハンドラーで、送信者としてハンドラーに渡された MediaFrameReader オブジェクトで TryAcquireLatestFrame を呼び出して、最新のメディア フレームへの参照の取得を試みます。 このオブジェクトは null になる可能性があるため、オブジェクトを使用する前に常に確認する必要があります。 TryAcquireLatestFrame から返される MediaFrameReference でラップされたメディア フレームの種類は、フレーム リーダーが取得するように構成したフレーム ソースの種類によって異なります。 この例のフレーム リーダーはオーディオ フレームを取得するように設定されているため、 AudioMediaFrame プロパティを使用して基になるフレームを取得します。

次の例の ProcessAudioFrame ヘルパー メソッドは、フレームのタイムスタンプや AudioMediaFrame オブジェクトから不連続かどうかなどの情報を提供する AudioFrame を取得する方法を示しています。 オーディオ サンプル データを読み取りまたは処理するには、AudioMediaFrame オブジェクトから AudioBuffer オブジェクトを取得し、IMemoryBufferReference を作成してから、COM メソッド IMemoryBufferByteAccess::GetBuffer を呼び出してデータを取得する必要があります。 ネイティブ バッファーへのアクセスの詳細については、コードの一覧の下の注を参照してください。

データの形式は、フレーム ソースによって異なります。 この例では、メディア フレーム ソースを選択するときに、選択したフレーム ソースで 2 つの浮動小数点データ チャネルが使用されていることを明示的に確認しました。 残りのコード例は、フレーム内のオーディオ データの期間とサンプル数を決定する方法を示しています。

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;

    }
}

Note

オーディオ データを操作するには、ネイティブ メモリ バッファーにアクセスする必要があります。 これを行うには、以下のコード リストを含めることで 、IMemoryBufferByteAccess COM インターフェイスを使用する必要があります。 ネイティブ バッファーに対する操作は、 unsafe キーワードを使用するメソッドで実行する必要があります。 Project > ダイアログの [ビルド] タブで、安全でないコードを許可するチェック ボックスもオンにする必要があります。

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

オーディオ データでの MediaFrameReader の使用に関する追加情報

MediaFrameSource.Controller プロパティにアクセスすることで、オーディオ フレーム ソースに関連付けられている AudioDeviceController を取得できます。 このオブジェクトは、キャプチャ デバイスのストリーム プロパティを取得または設定したり、キャプチャ レベルを制御したりするために使用できます。 次の例では、フレーム リーダーによってフレームが引き続き取得されるようにオーディオ デバイスをミュートしますが、すべてのサンプルの値は 0 です。

m_audioDeviceController.Muted = true;

AudioFrame オブジェクトを使用すると、メディア フレーム ソースによってキャプチャされたオーディオ データを AudioGraph に渡すことができます。 AudioFrameInputNodeAddFrame メソッドにフレームを渡します。 オーディオ グラフを使用してオーディオ信号をキャプチャ、処理、およびミックスする方法の詳細については、「 オーディオ グラフ」を参照してください。