Windows ACMでPCMをリサンプリングする

WindowsアプリケーションからPCMデータをリサンプリングする方法についてのメモ書きです。

リサンプリング処理はプログラマが直に書いても良いですが、出来れば楽したいのでAPIを用いて実現できないかどうかと考えていました。
結果、ACMを用いれば実現できることが分かりました。
ACM自体はMP3データのデコードでも使用されます。

リサンプリングを行うためには、ACM側にリサンプリング前と後のWAVEFORMATEX構造体を指定する必要があります。
まず、acmFormatSuggest()関数にこれらの情報を指定します。

第1引数はNULLで構いません。
第2引数にリサンプリング前のWAVEFORMATEX構造体データ、第3引数にリサンプリング後のWAVEFORMATEX構造体データを指定します。
第4引数には第3引数に指定した変数のサイズを指定します。
第5引数にはリサンプリング前のWAVEFORMATEX構造体変数のどのメンバを見てリサンプリング後のWAVEFORMATEX構造体のデータを決定するかを指定します。
今回はPCMフォーマット、チャネル数、サンプリングレート、量子化ビット数すべてを対象にします。

関数の実行が成功すると、m_dstFotmatに適切なデータに書き換えられます。
ここでふと疑問に思った方もいらっしゃるかもしれません。
それは、リサンプリング前と後のWAVEFORMATEX構造体をユーザで指定するにもかかわらず、リサンプリング後のデータが書き換えられることです。
これは、WAVEFORMATEX構造体にはnAvgBytesPerSecメンバやnBlockAlignメンバの存在があるためです。
nAvgBytesPerSecの値はnSamplesPerSecとnBlockAlignとの積に等しくなければなりません。
nBlockAlignの値はnChannelsとwBitsPerSampleの積を8で割った値に等しくなければなりません。
これらの値をacmFormatSuggest()が適切に指定します。

リサンプリングできないフォーマットと判断した場合、acmFormatSuggest()は失敗します。

リサンプリング前と後のWAVEFORMATEX構造体データが定まったら、acmStreamOpen()関数でACMストリームを開きます。

第1引数にはACMハンドラを受け取る変数のポインタを指定します。
第2引数はNULLで構いません。
第3引数、第4引数にはそれぞれリサンプリング前と後のWAVEFORMATEX構造体変数のポインタを指定します。
第5~8引数は使用しないので、NULLや0を指定します。(上記ソースコード参照)

ACMストリームを開いたら、acmStreamPrepareHeader()関数でリサンプリング前と後のデータを受け取るバッファを指定します。

ACMSTREAMHEADER構造体のpbSrc、cbSrcLengthにはそれぞれリサンプリング前データが格納されたバッファへのポインタ、バッファのサイズを指定します。
pbDst、cbDstLengthにはそれぞれリサンプリング後データが格納されるバッファへのポインタ、バッファのサイズを指定します。

関数が成功したら、acmStreamConvert()関数でリサンプリングを実行します。

引数は上記コードのように指定すれば良いです。

リサンプリングが不要になったら、後始末を行ってリソースを開放します。

これで、アプリケーション上でPCMデータのリサンプリングが実行できたことになります。
最後に、WAVEファイルのサンプリングレートを変換するサンプルソースを記します。
なお、PcmDataクラスはこちらのPcmData.hとPcmData.cppで定義されているものを流用しています。

PcmConverter.h

PcmConverter.cpp

Main.cpp

test.wavファイルを読み込み、96kHzにリサンプリングしてout.wavに保存するプログラムです。
Main.cppの次の部分を変更することで、リサンプル後のフォーマットをお好みのものに変えられます。

PCMデータのリサンプリング方法はACMを使う以外にも色々ありますが、あくまで一例として捉えていただければ結構です。