RIFFフォーマットファイルの読み込み
DelphiでWaveファイルを扱うためにRIFFフォーマットのファイルを読み込む必要がでてきたんで色々とメモ書き。
まず必要なものの説明。
MMSystemライブラリ
マルチメディア関連のAPIが各種はいっている。
uses句にこれを追加する必要あり。
MMRESULT型
MMSystem内の関数は一部、戻り値をこの型で返してくる。
中身はエラーコード。
TWaveFormatEx構造体
Wave形式のオーディオデータフォーマットが定義されている構造体。
この構造体のメンバは
メンバ名 | データ型 | 詳細 |
---|---|---|
wFromatTag | WORD | ウェーブフォームオーディオフォーマットのタイプ |
nChannel | WORD | チャンネル数、モノラルなら1Ch、ステレオなら2Ch | |
nSamplesPerSec | DWORD | サンプリング周波数、標本化周波数とも |
nAvgBytesPerSec | DWORD | フォーマットタグで必要な平均データ転送速度 |
nBlockAlign | WORD | ブロックアライメント、フォーマットタイプのデータの最小単位 |
wBitesPerSample | WORD | 1サンプリングあたりのビット数 |
cbSize | WORD | WaveFormatEx構造体の後ろに追加されるフォーマット情報のサイズ |
hMMIO型
オープンされているファイルのハンドルを格納するためのもの。
TMMCKINFO構造体
RIFFファイル内のチャンクに関する情報が定義されている。
TMMCKINFO構造体のメンバは
メンバ名 | 型 | 概要 |
---|---|---|
ckid | FOURCC | チャンク識別子 |
cksize | DWORD | チャンクのサイズ |
fccType | FOURCC | フォームのタイプ |
dwDataOffset | DWORD | Discend、Ascendを行ったあとのファイル上での位置 |
dwFlags | DWORD | 原則0が格納 |
mmioOpen関数
入出力をバッファリングしてファイルを開くための関数。
パラメーターは全部で3つ。
mmioOpen(szFilename,lpmmioinfo,dwOpenFlags);
パラメータ | データ型 | 概要 |
---|---|---|
szFilename | LPSTR | 開くファイルのファイル名がはいった文字列のアドレスを指定 |
lpmmioinfo | LPMMIOINFO | ファイルを開くためにインストールされていないI/Oプロシージャを指定する場合以外はnil指定 |
dwOpenGlags | DWORD | オープン操作のためのフラグを指定 MMIO_ALLOCBUF : ファイルを不バッファリングされた入出力用に開く MMIO_COMPAT : ファイルを互換モードで開き、指定されたマシン上の全てのプロセスでそのファイルを何度も開くことができるようにする MMIO_CREATE : 新規ファイルを作成する MMIO_DELETE : ファイルを削除する MMIO_DENYONE : 他のプロセスからのファイルの読み書きを拒否せず開く MMIO_DENYWRITE : ファイルに対する他のプロセスからの読み取りを拒否して開く MMIO_EXCLUSIVE : ファイルに対する他のプロセスからの読み書きアクセスを拒否して開く MMIO_EXIST : 指定されたファイルが存在するかどうかを調べ、szFilenameパラメータで指定したパスからファイル名を作成 MMIO_GETTEMP : szFilenameパラメータに渡されるパラメータを暫定的に使いテンポラリファイル名を作成 MMIO_PARSE : MMIO_EXISTに近いが存在するかどうかを調べない MMIO_READ : ファイルを読み取り専用として開く MMIO_READWRITE : ファイルを読み書き用に開く MMIO_WRITE : ファイルを書き込み専用として開く |
関数が成功すると、開いたファイルのハンドルが返ってくる。
mmioStringToFOURCC関数
NULLで終わる文字列を4文字コードに変換する。
第1引数に変換したい文字列、第2引数はフラグを指定するが「MMIO_TOUPPER」の1種のみ。
フラグを指定するとすべての文字が大文字に変換される。変換が必要ない場合、パラメータは0でよい。
mmioAscend関数
mmioDescend関数で侵入、またはmmioCreateChunk関数で作成したRIFFファイルのチャンクから退出する関数。
mmioAscend(hmmio,lpck,wFlags);
パラメータ | データ型 | 概要 |
---|---|---|
hmmio | HMMIO | 開いてるRIFFファイルのハンドルを指定 |
lpck | LPMMCKINFO | mmioDescend関数またはmmioCreateChunk関数で値が書き込まれている、アプリケーション定義のMMCKINFO構造体のアドレスを指定 |
wFlags | UNIT | 予約されているので0を指定 |
関数が成功するとMMSYSERR_NOERRORが返る。
mmioDescend関数
mmioOpen関数で開いたRIFFファイルのチャンクへ進入する。または指定されたチャンクを検索する。
mmioDescend(hmmio,lpck,lpckParent,wFlags);
パラメータ | データ型 | 概要 |
---|---|---|
hmmio | HMMIO | 開いてるRIFFファイルのハンドルを指定 |
lpck | LPMMCKINFO | アプリケーション定義のMMCKINFO構造体のアドレスを指定 |
lpckParent | LPMMCKINFO | 検索するチャンクの親を識別するためのMMCKINFO構造体のアドレスを指定 親チャンクが指定されてない場合nil指定 |
wFlags | UNIT | 検索フラグを指定する MMIO_FINDCHUNK : 指定されたチャンク識別子のチャンクを検索 MMIO_FINDLIST : チャンク識別子がLISTで指定されたフォームタイプのチャンクを検索 MMIO_FINDRIFF : チャンク識別子がRIFFで指定されたフォームタイプのチャンクを検索 |
関数が成功するとMMSYSERR_NOERRORが返る。
mmioRead関数
mmioOpen関数で開いたファイルから指定されたバイト数を読み取る。
mmioRead(hmmio,pch,cch);
パラメータ | データ型 | 概要 |
---|---|---|
hmmio | HMMIO | 読み取るファイルのハンドルを指定 |
pch | HPSTR | ファイルから読み取られたデータが入るバッファのアドレスを指定 |
cch | LONG | ファイルから読み取るバイト数を指定 |
と、この辺を使えば読み込みが可能になる。
関数などのパラメータ等はかなり簡略化してまとめてある。
詳細はMSDNなどで関数名を調べれば出てくる。C系の書き方がしてあるが、書式を変えればそのまんまDelphiでも使える。
今書いてるソースの中から該当部分だけをピックアップするとこんな具合。
uses MMSystem; //==============================// // FileOpenDialog呼び出し // //==============================// procedure TForm1.BtOpenClick(Sender: TObject); begin if OpenDialog1.Execute then begin wav_open(OpenDialog1.FileName); end; end; //========================================// // waveファイル読み込みプロシージャ // //========================================// procedure TForm1.wav_open(name:string); var x : integer; mmres : MMRESULT; PCM : TWaveFormatEx; hIO : hMMIO; RIFF_INFO : TMMCKINFO; DATA_INFO : TMMCKINFO; FMT_INFO : TMMCKINFO; buf : array[0 .. (1024 -1)] of Smallint; sz : DWORD; remain : DWORD; read_size : DWORD; total_size : DWORD; limit : DWORD; WaveData1 : array of Smallint; WaveData2 : array of Smallint; begin Ch1Index := 0; Ch2Index := 0; // wavファイル読み込み hIO := mmioOpen(PWideChar(name),nil,MMIO_READ); try RIFF_INFO.fccType := mmioStringToFOURCC('WAVE',0); mmres := mmioDescend(hIO, @RIFF_INFO, nil, MMIO_FINDRIFF); if(mmres <> MMSYSERR_NOERROR) then begin Memo1.Lines.Add('ERROR'); Exit; end; FMT_INFO.ckid := mmioStringTOFOURCC('fmt', 0); mmres := mmioDescend(hIO, @FMT_INFO, @RIFF_INFO, MMIO_FINDCHUNK); if(mmres <> MMSYSERR_NOERROR) then begin Memo1.Lines.Add('FMT CHUNK ERROR'); Exit; end; sz := mmioRead(hIO, @PCM, FMT_INFO.cksize); if sz <> FMT_INFO.cksize then begin Memo1.Lines.Add('Wave Format ERROR'); Exit; end; DATA_INFO.ckid := mmioStringTOFOURCC('data',0); mmres := mmioDescend(hIO, @DATA_INFO, @RIFF_INFO, MMIO_FINDCHUNK); if (mmres <> MMSYSERR_NOERROR) then begin Memo1.Lines.Add('Data Chunk Error'); exit; end; remain := DATA_INFO.cksize; // waveファイルのデータ数 read_size := Length(buf) * sizeof(Smallint); total_size := 0; // 読み込んだデータを格納するための配列の長さを決定 // 2chあるためデータ総数div2 SetLength(WaveData1,DATA_INFO.cksize div 2); SetLength(WaveData2,DATA_INFO.cksize div 2); while remain > 0 do begin if read_size > remain then read_size := remain; mmioRead(hIO, @buf, read_size); for x := 0 to (read_size div (sizeof(Smallint))) - 1 do begin if (x mod 2) = 0 then begin WaveData1[Ch1Index] := buf[x]; inc(Ch1Index); end else begin WaveData2[Ch2Index] := buf[x]; inc(Ch2Index); end; end; inc(total_size, read_size); dec(remain, read_size); if limit > 0 then begin if total_size >= limit then Break; end; end; mmioAscend(hIO, @DATA_INFO, 0); mmioAscend(hIO, @RIFF_INFO, 0); finally mmioClose(hIO, 0); end; end;
ざっくり流れとしては
1.ファイル展開
2.チャンクを検索してWaveファイルかどうか調べる
3.チャンクを検索してフォーマットが正しいか調べる
3.チャンクを検索してデータフォーマットが正しいか調べる
4.配列の長さをデータ数に合わせる
5.チャンネルごとにデータを分けて格納していく
6.チャンクから退出
7.ファイルを閉じる
という感じ。
これだとWaveファイルをただ開いてデータを取り込むだけなので、追加で色々実装する必要はあり。
TWaveFormatEx構造体のメンバは結構良く使う。
ディスカッション
コメント一覧
まだ、コメントがありません