カクタスソフトウェア
カクタスソフトウェア
サウンド MIDI マルチメディア アプリケーション

acm関数の利用

このセクションではacm関数を用いたmp3ファイルの取り扱いについて解説します。

acmはオーディオファイルのフォーマット変換を行う関数群です。使い方によって多くの場面で利用できますが、ここではmp3フォーマットをPCMフォーマットへ変換するために用います。以下に変換手順を示します。

前処理としてフォーマット変換が可能かどうか調べます。

ストリームのオープンからクローズまで、一連の操作でフォーマット変換を行います。

ACMSTREAMHEADERは変換に関連するパラメータを格納する構造体です。acmStreamConvert()関数で用います。

acmFormatSuggest

acmFormatSuggest()関数でフォーマット変換が可能かどうか調べます。

MMRESULT acmFormatSuggest(
    HACMDRIVER      had,
    LPWAVEFORMATEX  pwfxSrc,
    LPWAVEFORMATEX  pwfxDst,
    DWORD           cbwfxDst,
    DWORD           fdwSuggest)

had

変換を行うドライバーを指定します。NULLを指定すると、システムは最も適当と思われるドライバーを検索します。

pwfxSrc

変換元のウェーブフォーマットを指定します。ここでは読み込んだmp3ファイルの内容を指定します。

pwfxDst

変換先のウェーブフォーマットを指定します。Win32APIのwave関数、あるいはDirectSoundを用いて再生するために、PCMフォーマットを指定します。

cbwfxDst

変換先のウェーブフォーマット構造体のサイズを指定します。ここではWAVEFORMATEX構造体を使うので、sizeof(WAVEFORMATEX)を指定します。

fdwSuggest

ウェーブフォーマットのどのパラメータを変換したいのかを指定します。ここではACM_FORMATSUGGESTF_WFORMATTAGを指定します

fdwSuggest 説明
ACM_FORMATSUGGESTF_NCHANNELS チャンネル数を変更する。
ACM_FORMATSUGGESTF_NSAMPLESPERSEC サンプリング周波数を変更する。
ACM_FORMATSUGGESTF_WBITSPERSAMPLE ビットあたりの分解能を変更する。
ACM_FORMATSUGGESTF_WFORMATTAG ウェーブフォーマットを変更する。

サンプルコード

MPEGLAYER3WAVEFORMAT構造体に変換元のmp3情報を設定します。

mp3のフォーマットおよび構造体に関する情報はセクション「フレームヘッダー」、「mpegに関連する構造体」を参照して下さい。

mp3ファイルを読み込んだら、フレームヘッダーを解析し、MPEGLAYER3WAVEFORMAT構造体にパラメータを設定します。以下にコード例を示します。ここではバージョン1を仮定しています。

    MPEGLAYER3WAVEFORMAT    mwf;        // Source MP3

    mwf.wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
    mwf.wfx.nChannels = channels;
    mwf.wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
    mwf.wfx.nBlockAlign = 1;
    mwf.wfx.wBitsPerSample = 0;
    mwf.wfx.nSamplesPerSec = samplesPerSec;
    mwf.wfx.nAvgBytesPerSec = bitRate * 1000 / 8;

    mwf.wID = MPEGLAYER3_ID_MPEG;
    mwf.fdwFlags = padding ? MPEGLAYER3_FLAG_PADDING_ON : MPEGLAYER3_FLAG_PADDING_OFF;
    mwf.nBlockSize = (WORD)(144000 * bitRate / samplesPerSec) + padding;
    mwf.nCodecDelay = 0x571;
    mwf.nFramesPerBlock = 1;

次に変換先のWAVEFORMATEX構造体を用意します。PCMフォーマットに変換したいので、wFormatTagにWAVE_FORMAT_PCMを指定します。

    WAVEFORMATEX            wfx;        // Decoded WAV
    wfx.wFormatTag = WAVE_FORMAT_PCM;

acmFormatSuggest()関数をコールします。

    MMRESULT result = acmFormatSuggest(
        NULL,
        (LPWAVEFORMATEX)&mwf,
        &wfx,
        sizeof(WAVEFORMATEX),
        ACM_FORMATSUGGESTF_WFORMATTAG);
    if (result != MMSYSERR_NOERROR)     return FALSE;

変換が可能であれば、acmFormatSuggest()関数は変換先のWAVEFORMATEX構造体にパラメータをセットし、MMSYSERR_NOERRORを戻します。ここで得られたWAVEFORMATEX構造体は、フォーマット変換およびPCMデータの再生で使用します。

acmStreamOpen

フォーマット変換を行うストリームをオープンします。

MMRESULT acmStreamOpen(
    LPHACMSTREAM    phas,
    HACMDRIVER      had,
    LPWAVEFORMATEX  pwfxSrc,
    LPWAVEFORMATEX  pwfxDst,
    LPWAVEFILTER    pwfltr,
    DWORD           dwCallback,
    DWORD           dwInstance,
    DWORD           fdwOpen)

phas

変換に用いるストリームハンドルへのポインターです。ストリームがオープンされるとハンドルが戻ります。

had

変換を行うドライバーを指定します。NULLを指定すると、システムは最も適当と思われるドライバーを検索します。

pwfxSrc

変換元のウェーブフォーマットを指定します。acmFormatSuggest()に渡したものと同じものを使います。

pwfxDst

変換先のウェーブフォーマットを指定します。acmFormatSuggest()によって設定されたものを使います。

pwfltr

変換操作で用いるフィルターを指定します。フォーマット変換の時は用いないのでNULLを指定します。

dwCallback

変換操作を非同期で行う場合、アプリケーションは変換処理の終了を知ることができます。以下に可能な方法を示します。非同期でオープンするには、fdwOpenにACM_STREAMOPENF_ASYNCを指定します。

方式 dwCallback
コールバックなし NULL
コールバック関数 コールバック関数のアドレス
ウィンドウハンドル ウィンドウのハンドル
イベントハンドル イベントハンドルのアドレス

dwInstance

コールバック関数で渡されるユーザーインスタンスデータです。コールバック関数以外の方式では使いませんのでNULLを指定します。

fdwOpen

変換ストリームを開くためのフラグで以下のものが定義されています。

フラグ 数値 説明
ACM_STREAMOPENF_ASYNC 0x00000002 変換処理は非同期に実行されます。アプリケーションは「オープン」、「クローズ」、「変換処理終了」のタイミングで通知を受けます。
ACM_STREAMOPENF_NONREALTIME 0x00000004 変換処理は同期式で実行されます。変換処理が終了するまでアプリケーションに制御が戻りません。
ACM_STREAMOPENF_QUERY 0x00000001 指定された変換が可能かどうか調べます。ストリームはオープンされません。
CALLBACK_EVENT 0x00050000 dwCallbackはイベントハンドルです。
CALLBACK_FUNCTION 0x00030000 dwCallbackはコールバック関数です。コールバック関数は「acmStreamConvertCallback」の引数並びを持つ必要があります。
CALLBACK_WINDOW 0x00010000 dwCallbackはウィンドウハンドルです。

acmStreamConvertCallbackのテンプレートを示します。

void CALLBACK acmStreamConvertCallback(
    HACMSTREAM  has,
    UINT        uMsg,
    DWORD       dwInstance,
    LPARAM      lParam1,
    LPARAM      lParam2)
ファイルサイズが小さければバッファ変換は一瞬で終わりますが、サイズが大きいと数秒かかる場合があります。メインスレッドの中から同期式処理を行うと、変換中はアプリケーションが操作不能になります。変換処理は、非同期で行うかサブスレッドで行うと良いでしょう。

acmStreamSize

多くの場合、フォーマット変換によってデータサイズが変化します。この関数は変換前後のデータサイズやバッファサイズを計算します。計算方法は2つあり、fdwSizeフラグで示されます。

MMRESULT acmStreamSize(
    HACMSTREAM  has,
    DWORD       cbInput,
    LPDWORD     pdwOutputBytes,
    DWORD       fdwSize)

has

acmStreamOpen()関数で取得したストリームハンドルです。

cbInput

入力サイズを指定します。内容は、fdwSizeにより異なります。単位は「Byte」です。

pdwOutputBytes

結果を受け取るDWORDへのポインターです。戻されたサイズの単位は「Byte」です。

fdwSize

動作に応じて以下のフラグを指定します。

fdwSize 説明 使い方
ACM_STREAMSIZEF_DESTINATION 出力バッファサイズを固定とし、出力がオーバーフローを起こさない最大の入力データサイズを求めます。これは、大きなサウンドファイルを分割しながら変換する時に用います。 cbInputに変換先のバッファサイズを指定します。pdwOutputBytesに推奨される変換元のデータサイズが戻ります。
ACM_STREAMSIZEF_SOURCE 入力データサイズを固定とし、すべての変換出力を受け取るのに必要な出力バッファサイズを求めます。これは、サウンドファイルを一回で変換する時に用います。 cbInputに変換元のデータサイズを指定します。pdwOutputBytesに推奨される変換先のバッファサイズが戻ります。
ACM_STREAMSIZEF_SOURCEで変換後のバッファサイズを求める場合の注意点
変換後のデータサイズは、実際に変換してみるまで分からない場合があります。安全のため推奨されるサイズには実際のサイズより多めの値が戻ります。変換後の正しいサイズは、ACMSTREAMHEADER構造体のcbDstLengthUsedで示されます。

ACMSTREAMHEADER構造体

ACMSTREAMHEADERは、変換ストリームで扱うパラメータを格納するための構造体です。以下に詳細を示します。

typedef struct tACMSTREAMHEADER
{
    DWORD           cbStruct;               // sizeof(ACMSTREAMHEADER)
    DWORD           fdwStatus;              // ACMSTREAMHEADER_STATUSF_*
    DWORD_PTR       dwUser;                 // user instance data for hdr
    LPBYTE          pbSrc;
    DWORD           cbSrcLength;
    DWORD           cbSrcLengthUsed;
    DWORD_PTR       dwSrcUser;              // user instance data for src
    LPBYTE          pbDst;
    DWORD           cbDstLength;
    DWORD           cbDstLengthUsed;
    DWORD_PTR       dwDstUser;              // user instance data for dst
    DWORD           dwReservedDriver[_DRVRESERVED];   // driver reserved work space

} ACMSTREAMHEADER, *PACMSTREAMHEADER, FAR *LPACMSTREAMHEADER;

cbStruct

構造体のサイズ「sizeof(ACMSTREAMHEADER)」を指定します。

pbSrc

変換元データのアドレスを指定します。いったんacmStreamPrepareHeader()関数を実行したら、以後アドレスは変更できません。変換元アドレスを移動させる必要がある場合は、再度、acmStreamUnprepareHeader()関数、acmStreamPrepareHeader()関数を実行します。

cbSrcLength

変換元のデータサイズを指定します。単位は「Byte」です。変換に使われるデータは、最大ここで指定したサイズになります。常にすべてのデータが変換されるわけではありません。

acmStreamConvert()関数のfdwConvertにACM_STREAMCONVERTF_BLOCKALIGNを指定すると、MPEGLAYER3WAVEFORMATのnBlockSizeで指定したサイズ単位で処理されることを期待したのですが、筆者の実験ではそうなっていないようです。cbSrcLengthにどんな数値を入力しても、変換後はcbSrcLengthUsedにcbSrcLengthと同じ値が入ります。

変換を複数ブロックに分けて行う時は、フレームの先頭アドレスとフレームサイズを正確に求め、pbSrcとcbSrcLengthに設定して下さい。

cbSrcLengthUsed

変換で消費されたデータのサイズが戻ります。単位は「Byte」です。

pbDst

変換先バッファのアドレスを指定します。いったんacmStreamPrepareHeader()関数を実行したら、以後アドレスは変更できません。バッファアドレスを変更する場合は、再度、acmStreamUnprepareHeader()関数、acmStreamPrepareHeader()関数を実行します。

cbDstLength

変換先のバッファサイズを指定します。単位は「Byte」です。

cbDstLengthUsed

変換で出力されたデータのサイズが戻ります。単位は「Byte」です。変換に失敗すると「0」が戻ります。

acmStreamPrepareHeader・acmStreamUnprepareHeader

acmStreamPrepareHeader()関数は、ACMSTREAMHEADER構造体を準備します。変換作業が終わったらacmStreamUnprepareHeader()関数で後処理を行います。

acmStreamUnprepareHeader()関数を実行する前に変換で用いたバッファを開放しないで下さい。

変換作業で用いるバッファアドレスを変更する時は、再度、acmStreamUnprepareHeader()関数、acmStreamPrepareHeader()関数を実行しなければなりません。

acmStreamConvert

フォーマット変換を行います。

MMRESULT acmStreamConvert(
    HACMSTREAM          has,
    LPACMSTREAMHEADER   pash,
    DWORD               fdwConvert)

has

acmStreamOpen()関数で取得したストリームハンドルです。

pash

ACMSTREAMHEADER構造体のアドレスを指定します。構造体はacmStreamPrepareHeader()関数で準備しておく必要があります。

fdwConvert

変換に関するフラグを指定します。

fdwConvert 説明
ACM_STREAMCONVERTF_BLOCKALIGN 変換をブロック単位で行います。
ACM_STREAMCONVERTF_END 最後のブロックであることを示します。
ACM_STREAMCONVERTF_START 最初のブロックであることを示します。

残念ながら執筆現在、fdwConvertに関する詳細な資料が見つかっていません。

固定ビットレート(CBR)のmp3は各フレームが独立しているため、ACM_STREAMCONVERTF_START指定は意味がないと思われます。可変ビットレート(VBR)では、直前のフレームが次のフレーム変換に影響する場合があり、ACM_STREAMCONVERTF_STARTフラグは、この影響をリセットする働きがあると思われます。ACM_STREAMCONVERTF_BLOCKALIGNについては動作が確認できていません。

非同期で変換を行う場合、変換が成功したかどうかはACMSTREAMHEADER構造体のcbDstLengthUsedの値で行います。変換に成功するとゼロ以上の値が戻ります。

acmStreamClose

変換が終了したらストリームをクローズします。

MMRESULT acmStreamClose(
    HACMSTREAM  has,
    DWORD       fdwClose)

has

acmStreamOpen()関数で取得したストリームハンドルです。

fdwClose

未使用です。「0」を指定して下さい。

ドキュメントの先頭へ

カクタスソフトウェア 技術協力 資料室 資料室の広場 SourceForge.jp お問い合わせ