MediaFrameReader を使ったオーディオ フレームの処理

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

注意

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

Note

MediaFrameReader を使用して、色、深度、赤外線カメラなど、さまざまなフレーム ソースのフレームを表示するユニバーサル Windows アプリサンプルがあります。 詳しくは、「カメラ フレームのサンプル」をご覧ください。

プロジェクトの設定

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

アプリ マニフェストに機能を追加する

  1. Microsoft Visual Studio のソリューション エクスプローラーで、package.appxmanifest 項目をダブルクリックしてアプリケーション マニフェストのデザイナーを開きます。
  2. [機能] タブを選択します。
  3. [Web カメラ] のボックスと [マイク] のボックスをオンにします。
  4. 画像ライブラリとビデオ ライブラリにアクセスするには、[画像ライブラリ] のボックスと[ビデオ ライブラリ] のボックスをオンにします。

フレーム ソースとフレーム ソース グループを選択する

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

MediaCapture.InitializeAsync の呼び出し後は、アクセス可能なメディア フレーム ソースの一覧を FrameSources プロパティによって取得できます。 この例では、Linq クエリを使用して、フレーム ソースを記述している MediaFrameSourceInfoMediaStreamTypeAudio に設定されている (メディア ソースによってオーディオ データが生成されることを示す) すべてのフレーム ソースを選択します。

クエリが 1 つまたは複数のフレーム ソースを返す場合は、CurrentFormat プロパティで、ソースが目的のオーディオ形式 (この例では、浮動小数点型のオーディオ データ) をサポートしているかどうかを確認します。 AudioEncodingProperties で、目的のオーディオ エンコードがサポートされていることを確認します。

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

MediaFrameReader を作成して開始する

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

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

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

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

データの形式は、フレーム ソースに依存します。 この例では、メディア フレーム ソースの選択時に、選択したフレーム ソースが 1 つの浮動小数点型のデータ チャネルを使用していることを明示的に確認しました。 以下の最後のコード例では、フレーム内のオーディオ データの継続期間とサンプル数を確認する方法を示します。

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;

    }
}

注意

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

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

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

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

audioDeviceController.Muted = true;

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