2010/06/07

WAVについて

JAVAでエフェクト・コンプレッサー自作の記録 2
WAV(RIFF waveform Audio Format)ファイルは、非圧縮の音声データ記述ファイル。非圧縮の音声ファイルとしてはWindowsでは一般的。

調べてみて意外だったのは、これだけ広く使われているWAVにもかかわらず、ちゃんとした資料が見つからないこと。とりあえず断片的な資料から必要そうなところだけまとめてみる。

ファイルの中身はヘッダとデータで構成。実際に調べてみた。WAVファイルをバイナリーエディタで開けば16進数の羅列が見られる。以下はヘッダ部分でサンプリング周波数などの情報が4バイトないしは2バイト単位で書かれている。この後に音声データが2バイト単位で書かれている。

52 49 46 46 AC 58 01 00 57 41 56 45 66 6D 74 20
10 00 00 00 01 00 01 00 44 AC 00 00 88 58 01 00
02 00 10 00 64 61 74 61 88 58 01 00 00 この先データ

ビット深度(音量の解像度)が 16bit の場合、データ部分は 2バイト、符号付リトルエンディアン(littel endian)で書かれている。ステレオの場合は、左、右と交互に記録されている。コンプでいじるのはデータ部分のみ。
扱える数値は16進数で 0x0000 ~ 0xFFFF まで。
10進数符号なしでは 0 ~ 65535
符号ありだと -32768 ~ 32767

実際のデータはこんな感じ

96 00 96 00 6D 00 A7 00 45 00 96 00 53 00 B7 00
8A 00 94 00 89 00 7A 00 A5 00 74 00 9A 00 6E 00
B3 00 63 00 A3 00 97 00 8E 00 77 00 A2 00 9A 00
7B 00 BF 00 7A 00 AA 00 78 00 97 00 9D 00 A7 00
AA 00 7B 00 C8 00 A1 00 9F 00 AF 00 B8 00 81 00
B5 00 AE 00 BF 00 A4 00 A9 00 A2 00 93 00 9E 00
・・・・・

little endian
ファイルに 10 FF とあったら、入れ替えて FF 10 として扱うことで Short に変換できる。これをやらないと、当然めちゃくちゃになる。これが分かるまで時間がかかった。

2の補数
10進数にすると、16bit データは 0~65535 の整数で表記している。これをプラスマイナスにするために 2 の補数を使っている。
1 の補数は反転したもの。それに 1 をプラスしたのが 2 の補数。例えば 4bit で表現すると、こんなかんじになる。
1 を 4bit の 2進数にすると、0001
この 2進数の 0 は 1 にして、1 は 0 にする。これを反転という。
0001 は反転すると 1110 となる。これが 1 の補数。さらに 2 の補数にするには 1 を加える必要がある。
1110 + 0001 = 1111
この 1111 が 2の補数。10進数で、-1 をあらわす。マイナスは2進数で一番大きな桁が 1 になっている。
1+(-1) を2進数(2の補数)で計算すると以下のようになる。
0001 + 1111 = 10000
4bitであれば、5桁目は無効になるので、 0000 となり、めでたく 0 となる。 ということで引き算が足し算でできるようになる。

オーディオでよく使われえている16bitの場合
オーディオでは音量(リニア)を -1.0 ~ +1.0 までを扱っているが、16bit-PCMの、その対応は以下のようになっている。奇妙なことに +1 はないようだ。-1 はあるのだが、実際は使わない方がよさそうだ。

オーディオ 16進数 short 2進数
+1.0 存在しない
+0.9999 0x7FFF 32767 0111 1111 1111 1111 プラス最大値
0.0 0x0000 0 0000 0000 0000 0000
-0.000031 0xFFFF 65535 1111 1111 1111 1111 shortの最大値
-0.9999 0x8001 32769 1000 000 0000 0001 プラス最大値と対
-1.0 0x8000 32768 1000 0000 0000 0000 マイナス最小値

実際にマイナス最小値の -1.0(0x8000) と プラス最大値 0.9999(0x7FFF) を足すと 0.000031(0xFFFF) となって 0 にはならない。2進数で見ると明らかになる。

1000 0000 0000 0000 + 0111 1111 1111 1111 = 1111 1111 1111 1111

結果は符号なし short の最大値となり、オーディオとしては 0 よりも1段小さいマイナスの数となる。

Javaでshortを変換していろいろ確かめてみる。プログラムは以下の通り。どうも2進数を表記する方法がない。自作も面倒なので、とりあえず2進数は電卓で計算して求めてみる。Win電卓便利。関数電卓ではやってられない。
public class Wave16bit {
 public static void main(String args[]){
  short x = -32767;
  System.out.printf("10進数 = %d\n", x);
  System.out.printf("16進数 = %x\n", x);
  System.out.printf("8進数 = %o\n", x);
 }
}

ヘッダ部分は理解していなくても今回の作業はできるので、WAVについてはここまで。


コンプ製作の記録
1 Java 具体的に作ってみる
2 WAVについて
3 dBについて
4 コンプに必要な情報
5 Eclipseの導入
6 Nimbus
7 コンプの原理
8 コンプレッサーベータ版完成
9 NAMAGI Compressor