JavaでWAVファイル読み込み
久しぶりに時間が出来たので
音声ファイルを触ってみます。
フォーマットの確認
とりあえずwindows7のログオンの時の音を読み込んでみる
AudioInputStream input = AudioSystem.getAudioInputStream(file);
AudioFormat format = input.getFormat();
でフォーマットを取れるので
これをこんな関数に入れてみる。
public static String getFormatString(AudioFormat format) {
StringBuilder sb = new StringBuilder();
sb.append("[AudioFormat] frameSize:" + format.getFrameSize() + "byte");
sb.append(" Fs:" + format.getSampleRate() + "Hz");
sb.append(" " + format.getSampleSizeInBits() + "bit");
sb.append(" " + format.getChannels() + "ch");
if (format.isBigEndian()) {
sb.append(" BigEndian");
} else {
sb.append(" LittleEndian");
}
if (format.getEncoding() == AudioFormat.Encoding.PCM_SIGNED) {
sb.append(" SignedPCM");
} else if (format.getEncoding() == AudioFormat.Encoding.PCM_UNSIGNED) {
sb.append(" UnsignedPCM");
} else {
sb.append(" NoPCM");
}
return sb.toString();
}
[AudioFormat] frameSize:4byte Fs:44100.0Hz 16bit 2ch LittleEndian SignedPCM
これがWindowsの標準フォーマットらしい。
CDから取り込んだwavファイルも同じ結果になったので
CDと同じフォーマットを使っているようです。
サンプルデータ
普通の音声データだと正しく読み込めたかわからないので
を使って0dBのサイン派を作ってそれを読み込みます。
コード
javaのintは4byteですが
サンプリングデータは16bitなので2byte
この変換がめんどくさいですが試行錯誤の末下記になりました。
public void readWavFile(File f) {
try {
AudioInputStream input = AudioSystem.getAudioInputStream(f);
AudioFormat format = input.getFormat();
BufferedInputStream bufInput = new BufferedInputStream(input);
byte frameBuf = new byte[format.getFrameSize()];
int sampleSizeInBytes = format.getSampleSizeInBits() / 8;
int chCount = format.getChannels();
byte buf = new byte[BYTE_LENGTH_OF_INT];
ByteBuffer bb;
ByteOrder order;
if (format.isBigEndian()) {
order = ByteOrder.BIG_ENDIAN;
} else {
order = ByteOrder.LITTLE_ENDIAN;
}
int offset = BYTE_LENGTH_OF_INT - sampleSizeInBytes;
int shift = BYTE_LENGTH_OF_INT * 8 - format.getSampleSizeInBits();
int count = 0;
int value = 0;
while (bufInput.read(frameBuf) > 0) {
for (int ch = 0; ch < chCount; ch++) {
for (int i = 0; i < sampleSizeInBytes; i++) {
buf[offset + i] = frameBuf[ch * sampleSizeInBytes + i];
}
bb = ByteBuffer.wrap(buf);
bb.order(order);
value = bb.getInt() >> shift;
if (ch == 0 && count < 100) {
System.out.println(value);
count++;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
でてきた出力をエクセルに貼り付けると
1kHzの信号で44.1kHzのFsなので1周期は44サンプル
16bitなのでピーク値は2^15-1=32767
というのが期待値ですが
こんな感じになっているのでちゃんと読めているように見えます。
BIgEndianのファイルがないのでそれは出来ているか確認できないのと
サンプル精度が8bitの倍数でないとおかしなことになりますが
16bitと24bitくらいしか目にしないので
とりあえずあとにします。