Platform SDK: DirectX

ウェーブ ファイルへの書き込み

[Visual Basic]

DirectSoundBuffer.SaveToFile を使うと、短いサウンドをセカンダリ バッファからウェーブ ファイルに保存できる。このメソッドはバッファの内容全体をファイルにコピーする。

多くの場合、サウンドの保存はキャプチャ バッファから始める。次のコードは、キャプチャ バッファ dscb から、ファイルに保存できるセカンダリ バッファにデータを移動する方法を示す。

Dim ByteBuffer() As Integer 
Dim dsc As DirectSoundCapture 
Dim dscd As DSCBUFFERDESC
Dim dsd As DSBUFFERDESC
Dim capCURS As DSCURSORS
.
.
.
' dscd はキャプチャ バッファのフォーマットで初期化され、
' dsb が同じサイズのセカンダリ バッファを
' 表すと仮定する。
 
' キャプチャ バッファと同じフォーマットで
' セカンダリ バッファを作成する。
 
Set dsb = ds.CreateSoundBuffer(dsd, dscd.fxFormat)
 
' キャプチャ バッファの書き込みカーソルまでの内容を保持できる
' 大きさでプライベート バッファを作成する。
 
dscb.GetCurrentPosition capCURS
ReDim ByteBuffer(capCURS.lWrite + 1)
 
' キャプチャ バッファの内容をプライベート バッファに読み込む。
 
dscb.ReadBuffer 0, capCURS.lWrite * dscd.fxFormat.nBlockAlign, _
        ByteBuffer(0), DSCBLOCK_DEFAULT
 
' プライベート バッファをセカンダリ バッファに書き込む。
 
dsb.WriteBuffer 0, capCURS.lWrite, ByteBuffer(0), DSBLOCK_DEFAULT

キャプチャ バッファの長さを超えてしまうサウンドに対しては、開発者自身がウェーブ ファイルを作成し、そのファイルにデータをストリーミングしなければならない。キャプチャされたデータをウェーブ ファイルにストリーミングするのは、次のステップで行う。

  1. ファイルを作成し、ファイル ヘッダー、フォーマット チャンク、およびデータ チャンクのヘッダーを書き込む。フォーマット チャンクは、DirectSoundCaptureBuffer.GetFormat で取得できるキャプチャ バッファのフォーマットを記述しなければならない。ただし、フォーマットの要素は WAVEFORMATEX 型と同じ順番ではないことに注意すること。ヘッダーとチャンクの編成についての情報は、「ウェーブ ファイルの読み込み」を参照すること。
  2. キャプチャ バッファからプライベート バッファまで、またそのバッファからファイルまでのデータのストリーミングを行う。このとき、書き込まれたバイトの数を追跡する。詳細については、「キャプチャ バッファの使い方」を参照すること。
  3. キャプチャが停止し、すべてのデータが書き込まれたら、データ チャンクの長さとファイルの合計サイズを計算し、これらの値をデータ チャンク ヘッダーとファイル ヘッダーのそれぞれの適切な位置に書き込む。
[C++]

ウェーブ ファイルへの書き込みを準備するには、アプリケーションは最初に、Wavwrite.cpp 内の関数に渡される 4 つの変数を宣言しなければならない。

WAVEFORMATEX  wfx;            // ウェーブ フォーマット情報。
HMMIO         hmmio;          // ファイル ハンドル。
MMCKINFO      mmckinfoData;   // チャンク情報。
MMCKINFO      mmckinfoParent; // 親チャンク (ファイル) 情報。
 

キャプチャ バッファの情報を使って WAVEFORMATEX 構造体の初期化も行わなければならない。

次に WaveOpenFile 関数を呼び出し、希望のファイル名とグローバル変数のアドレスを渡す。この関数は指定されたファイルを作成し、何らかのヘッダー情報を書き込む。Wavwrite.cpp 内の他の関数と同様、WaveOpenFile は成功した場合に 0 を返す。

if (WaveOpenFile(pszFileName, &hmmio, &wfx, 
        &mmckinfoData, &mmckinfoParent))
{
    // 失敗。
}
 

次に WaveStartDataWrite 関数を呼び出す。この関数は、データ チャンクを初期化する。

if (WaveStartDataWrite(&hmmio, &mmckinfoData, &mmioinfo))
{
     // 失敗。
}
 

これでファイルはデータを受け取る準備ができた。次の部分的なコードは、キャプチャ バッファからファイルへデータをコピーする方法を示す。

/* 次の変数に有効な値が割り当てられていることを
   想定する。
LPDIRECTSOUNDCAPTUREBUFFER lpdscb;    // キャプチャ バッファ。
DSCBUFFERDESC    dscbDesc;            // キャプチャ バッファの記述。
DWORD            dwReadCursor;        // バッファ内の内部カーソル。
DWORD            dwNumBytes;          // 利用可能なバイト数。
DWORD            dwTotalBytesWritten; // 現時点までにファイル内に書き込まれた合計バイト数。
*/
 
LPBYTE pbInput1, pbInput2; // バッファ内のデータへのポインタ。
DWORD  cbInput1, cbInput2; // ロックされた部分のバイト カウント。
UINT   BytesWritten;       // ファイルに書き込まれたバイト カウント。
 
if FAILED(hr = lpdscb->Lock(dwReadCursor, dwNumBytes,
                      (LPVOID *)&pbInput1, &cbInput1, 
                      (LPVOID *)&pbInput2, &cbInput2, 0))
{
    // 失敗。
}
else
{
    if (WaveWriteFile(hmmio, cbInput1, pbInput1, &mmckinfoData,
                    &dwBytesWritten, &mmioinfo))
    {
        // 失敗。
    }
    else dwTotalBytesWritten += BytesWritten; 
    if (pbInput2 != NULL) 
    {
        if (WaveWriteFile(hmmio, cbInput2, pbInput2, &mmckinfoData,
                    &BytesWritten, &mmioinfo))
        {
            // 失敗。
        }
        else dwTotalBytesWritten += BytesWritten; 
 
    }
    lpdscb->Unlock(pbInput1, cbInput1, pbInput2, cbInput2);
 
    // 内部カーソルをインクリメントし、ラップ アラウンド (先頭へ戻る) を補正する。
    dwReadCursor += dwNumBytes;
    while (dwReadCursor >= dscbDesc.dwBufferBytes)
                dwMyReadCursor -= dscbDesc.dwBufferBytes;
 
}
 

データのキャプチャリングが終わった後で、ファイルを閉じる。

WaveCloseWriteFile(&hmmio, &mmckinfoData, 
        &mmckinfoParent, &mmioinfo,
        dwTotalBytesWritten / (wfx.wBitsPerSample / 8));
 

WaveCloseWriteFile 関数は、ファイル内のサンプルの合計数を計算し、その値をデータ チャンクのヘッダーに書き込む。