まるもの雑記

なにか作ったりとかゲームとか。

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と同じフォーマットを使っているようです。

サンプルデータ

普通の音声データだと正しく読み込めたかわからないので

テスト信号発生ソフト WaveGene

を使って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();
        }

    }

でてきた出力をエクセルに貼り付けると

f:id:marumorix:20141224151428j:plain

1kHzの信号で44.1kHzのFsなので1周期は44サンプル

16bitなのでピーク値は2^15-1=32767

というのが期待値ですが

こんな感じになっているのでちゃんと読めているように見えます。

 

BIgEndianのファイルがないのでそれは出来ているか確認できないのと

サンプル精度が8bitの倍数でないとおかしなことになりますが

16bitと24bitくらいしか目にしないので

とりあえずあとにします。