前回Waveファイルのサンプリングレート変換らしいことをしてみたので、次はビットレートの変換もできるようにしました。ビットレートというよりはビット深度ですか。
前回軽く解説的なことをしたので、今回もメモ的に残してみます。とはいえややこしいところは少ないので、周波数の変換に比べればだいぶ楽ですね。
Waveファイルのフォーマットではだいたい8ビット、16ビット、24ビット、32ビットの4種類が使われていて、
それぞれsigned char, short, int, floatの型で表すことができます(32ビット整数型もありますがややこしいのでスルー)。
それぞれのビットレートで持てる値の範囲は以下の通り。
- 8bit
- 0 ~ 255
- 16bit
- -32768 ~ 32767
- 24bit
- -8388608 ~ 8388607
- 32bit float
- -1.0 ~ 1.0(範囲外の値も持てる)
基本的に値が0の時に無音なのですが、8ビットのみ128で無音になるので注意が必要ですね。
バイト列から値を取り出していく場合、8、16、32ビットの場合については、それぞれの型にキャストするだけで値を取り出すことができます(リトルエンディアンの場合)。
24ビットの場合はひとつのデータが3バイトになるので、読み込みにひと手間必要になります。
読み込む3つのバイトをそれぞれb[0],b[1],b[2]とするとき、データはリトルエンディアンなのでb[2]が最上位バイトになります。
b[2]の一番上のビットが符合を表すので、まず右シフトb[2] >> 7したりして符合を見ます。読み込む値の絶対値は
- 符号が0(正の数)の場合
- b[0] + (b[1] << 8) + (b[2] << 16)
- 符号が1(府の数)の場合(~はビット反転)
- ~b[0] + (~b[1] << 8) + (~b[2] << 16) + 1
となります。負の値の場合はこれをマイナスにすれば完成。
ビットレートを変換するときは、こうして読み込んだ値を一度-1.0~1.0の範囲にしてから各々の型に変換していけばいいわけですね。こちらも基本的に型変換だけでいけますが、やはり24ビットは面倒。
さて変換したデータをWaveファイルとして書き出そうとした場合、16→8や24→16の変換は問題なさそうなのですが
16→32の変換をしたときにひどい音量で再生されるファイルができてしまいました。
変換したデータがおかしいのかと思ったらそうではなく、しばらく考えた後に書き込み時のFormatに問題があることが判明。
fmt チャンクに書き込むWAVEFORMAT(EX)構造体のwFormatTagは、32bit float形式の時はWAVE_FORMAT_PCM(1)ではなくWAVE_FORMAT_IEEE_FLOAT(3)を入れるのが正しいのでした。
WAVE_FORMAT_IEEE_FLOATはmmreg.hに定義されています。
未確認ですが32ビットでWAVE_FORMAT_PCMだと32bit int形式になるものと思われるので、floatの代わりにintで保存すれば正常に再生されそうですね。
ともあれ再生環境に合わせた音声データの変換がある程度できるようになったので、これで音楽ゲームの製作が進む……といいですが果たして。