WAVファイル


音声ファイルにはいろいろな形式があります。wav、mp3、wma等ですね。

wavと言われる音声ファイルにも実はいろいろあります。そもそもwavというのは音声の形式ではなくファイル書き込みの様式です。
wavはRIFFと呼ばれる書式に従って書かれたファイルなわけですが、RIFFは音声データの記録形式というわけではありません。 いろいろなデータを単一ファイルに収めるための汎用書式です。 リニアの音声データは16bitステレオであれば2バイトづつ、L_R_L_R_…と書き込まれていきます。8bitモノラルであれば1バイトづつ、LLLL…ですね。
しかしこれだけではただのバイトの羅列にすぎないので、書くときはよくても読むときに困ります。 このデータはどんなサンプリング周波数で再生すればよいのか、モノラルなのかステレオなのか等はデータだけ見てもわかりません。
そこでこの音声データ本体とは別に、そのデータの音声形式も記録する必要があります。これらの複数のデータをまとめて1ファイルに収める書式がRIFFであり、 拡張子wavのファイルはRIFFのWAV形式という規則にのっとって書かれています。それぞれのデータはチャンクという単位で書かれ、 RIFFのファイルデータはチャンクの集合体です。 普段wavと呼ばれるファイルは「リニアwav」であり、リニアデータではなくmpeg音声をRIFF-WAV形式で書き込めば「mpeg wav」ですね。 Windows標準のサウンドレコーダーでは「wma wav」を作成することが出来ます。
圧縮音声は音声データ内にサンプリング周波数などを示すフラグなどを持っていますが、 そのファイル自体がどの音声形式であるかはソフト側からははっきりとは断定できません。RIFF-WAV形式に収めると音声形式を明言することができます。

RIFFにはwavのほかにaviがあります。aviファイルでも中身がwmvだったりdivxだったりいろいろですよね。

最も基本的なリニアwavファイルの構造を示します。
チャンク名称バイト数内容
-4文字データで「RIFF」。このファイルがRIFFであることを示します。
-432bit整数。このRIFFのサイズ。大抵はファイルサイズ-8になります。
-4文字データで「WAVE」。このファイルがWAVEであることを示します。
fmt 4文字データで「fmt 」。これ以下に音声形式を記録することを示します。
fmt 432bit整数。fmtチャンクの中身のサイズ。この場合は16です。
fmt 216bit整数。音声データ形式を表す番号。リニアwavは1です。
fmt 216bit整数。チャンネル数。モノラルは1でステレオは2です。
fmt 432bit整数。サンプリング周波数。
fmt 432bit整数。1秒あたりのバイト数。サンプリング周波数×チャンネル数×量子化bit数÷8
fmt 216bit整数。1サンプルあたりのバイト数。チャンネル数×量子化bit数÷8
fmt 216bit整数。量子化bit数。16bitなら16
data4文字データで「data」。これ以下が音声データであることを示します。
data432bit整数。dataチャンクの中身のサイズ。
data-実際の音声データ

dataチャンクの前にfactチャンクというのが入る場合があります。あまり使用されませんがサウンドレコーダーではつけられるようです。
fact4文字データで「fact」
fact432bit整数。factチャンクのサイズ。4です。
fact432bit整数。dataチャンクの音声データに何サンプル含まれるか。

このようにRIFFを利用すれば別の意味合いのデータをまとめて保存することが出来ます。
BWFという拡張規格ではコメント文字列なども追加することが出来ます。
さらにこれの拡張規格であるBWF-Jではコメント文字列、キューポイント、プレイリスト、添付ファイル等の記録が可能です。

(追記)

実際にファイルを扱う方法はどこかよそで探してもらうとして、そのコツを少し。
WindowsのAPIにmmioReadなどのmmio系の関数があります。
これでwavファイルの読み書きができるのですが、それほど便利ではない上にイレギュラーなwavファイルに対してどういう挙動をとるか不明なのであまりオススメではありません。
CreateFileなどの一般ファイルIOを使用してもほとんど同じ手間で出来ますし、細かい操作も出来ます。

まず先頭4バイトと9バイト目から4バイトを読んでwavファイルであることを確認。
チャンク名称は4バイト文字列ですが面倒なので "RIFF"は1179011410、"WAVE"は1163280727という具合に4バイト整数で定義してやると楽です。
あとは各チャンク名称とサイズを読んで、適当な構造体に格納していきます。
fmtチャンクはWAVEFORMAT構造体に読みます。fmtチャンクが先頭とは限らないので、チャンク名称を読んでからどの処理を行うか分岐したほうがよいです。
読んだらチャンクサイズ分ファイルポインタを進めます。

チャンク実データは必ず偶数バイトで書くというきまりがあるようなので要注意です。
チャンクサイズに5と記録されていてもデータは6バイト格納されているはずです。最後の余った1バイトは無効データです。 チャンクサイズに6と記録されていればデータも6バイト格納されています。つまりチャンクサイズに奇数の値が書き込まれている場合は1バイト余分にファイルポインタを進めるということです。
標準的なwavでは8bitモノラル以外では奇数バイトチャンクは発生しないので大抵の場合はチャンクサイズ値とチャンクデータ実バイト数は一致するはずです。
書くときも同様に奇数サイズのチャンクには実データにヌルを1バイト付加して書かないといけないのですが、面倒がないようにはじめから奇数バイトチャンクを作らないのが一番無難かと思います。
チャンクサイズは符号ナシ32bit整数のはずなので上限は4GBなのですが、2GB以上を扱えないソフトが多いです。書くときも2GB以内にしておくほうが無難でしょう。

dataチャンクを読むときも構造体に読むと便利です。
例えば
Type Stereo16DataType
      (符号付16bit整数) Lch
      (符号付16bit整数) Rch
として
この構造体の配列Stereo16Data[100]というようなのを作ってそこに読むと1サンプル単位で処理するのが楽です。