まるもの雑記

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

Javaによるwavファイルの書き出し

書き出しのしかた

javax.sound.sampled.AudioSystem

のwriteを使えば書けるようです。

書き出す前に長さを決めてデータを流し込むイメージで

ちょっと調べてみた感じだとstopまで書き出すみたいな使い方は出来なそうです。

内部的には指定した長さまでfor分を回しているようなのですが

その関数はprivateになっている。

 

AudioFormatを指定してから

audioInputStream = new AudioInputStream(bufInput, format, frameLenght)

AudioSystem.write(audioInputStream, Type.WAVE, outputFile)

という順番。

 

前作ったときはバイナリで中間ファイルを作ってから

ByteArrayInputStreamを使って出力してたのですが

OutputStreamとInputStreamを繋げる

PipedInputStream/PipedOutputStream

というのがあるようなので使ってみます

 

PipedInputStream/PipedOutputStream

こんな風に使う模様。

PipedOutputStream outputStream = new PipedOutputStream();
PipedInputStream inputStream = new PipedInputStream();
outputStream.connect(inputStream);

そのままだと遅いとのことなのでバッファリングします。

bufOutput = new BufferedOutputStream(outputStream);
bufInput = new BufferedInputStream(inputStream);

入出力両方行う必要はたぶんないと思いますが

これで無事動いているのであまり深く考えないようにする。

 

出力方法

readerを全ch1サンプルずつ読むように作ったので

書くほうも読んだ分書いていけるようにする。

 

形式を決めて new してから

put(values) していって

最後にclose

 

とりあえずこれでテスト信号が出来るようになったので

まずはよしとします。

 

public class WavFileWriter extends Thread {
    private AudioInputStream audioInputStream;
    private File outputFile;

    private BufferedOutputStream bufOutput = null;
    private BufferedInputStream bufInput = null;

    private int MAX_VALUE;
    private int MIN_VALUE;

    private ByteBuffer bb;

    private int byteOffset;
    private int sampleSizeInByte;

    private static final int INT_BYTE_LENGTH = 4;

    private int n;// write values

    public WavFileWriter(File output, AudioFormat format, long frameLenght) {
        
        this.outputFile = output;

        try {
            PipedOutputStream outputStream = new PipedOutputStream();
            PipedInputStream inputStream = new PipedInputStream();
            outputStream.connect(inputStream);
            bufOutput = new BufferedOutputStream(outputStream);
            bufInput = new BufferedInputStream(inputStream);

            int sampleSizeInBit = format.getSampleSizeInBits();
            this.sampleSizeInByte = sampleSizeInBit / 8;

            this.bb = ByteBuffer.allocate(INT_BYTE_LENGTH);
            if (format.isBigEndian()) {
                this.bb.order(ByteOrder.BIG_ENDIAN);
                this.byteOffset = INT_BYTE_LENGTH - sampleSizeInByte;
            } else {
                this.bb.order(ByteOrder.LITTLE_ENDIAN);
                this.byteOffset = 0;
            }

            MAX_VALUE = (int) (Math.pow(2, sampleSizeInBit) - 1);
            MIN_VALUE = (int) (-Math.pow(2, sampleSizeInBit));

            audioInputStream = new AudioInputStream(bufInput, format, frameLenght);
            this.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            bufOutput.flush();
            bufOutput.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void putFrame(int values) throws IOException {
        for (int ch = 0; ch < values.length; ch++) {
            n = Math.min(MAX_VALUE, values[ch]);
            n = Math.max(MIN_VALUE, n);
            this.bb.putInt(0, n);
            bufOutput.write(bb.array(), byteOffset, sampleSizeInByte);
        }
    }

    public void run() {
        try {
            AudioSystem.write(audioInputStream, Type.WAVE, outputFile);
            audioInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

 

確認

自分で作ったビューアーとライターで試すのも不安なので

フリーの波形表示ソフトを探したのですが

フリーでマルチチャンネルの波形表示が出来るソフトはよいのがなさそうでした。

 

こんな感じでサイン波を作れます(たぶん)。

long  = (long) (lengthInSec * sampleFreq);
double w = signalFreq / sampleFreq * 2 * Math.PI;
int[] values = new int[chCount];
AudioFormat format = new AudioFormat((float) sampleFreq, SAMPLE_SIZE_IN_BIT, chCount, SIGNED, ISBIGENDIAN);

WavFileWriter writer = new WavFileWriter(output, format, frameLength);
double a = Math.pow(10, gainInDB / 20) * (Math.pow(2, SAMPLE_SIZE_IN_BIT - 1) - 1);

try {
    int n;
    for (long i = 0; i < frameLength; i++) {
        n = (int) (Math.sin(w * i) * a);
        for (int c = 0; c < chCount; c++) {
            values[c] = n;
        }
        writer.putFrame(values);
    }
    writer.close();
} catch (Exception e) {
    e.printStackTrace();
}

再生するとそれっぽく聞こえるので良しとします。

 

人に見せられるようなコードは書けませんが

コード例がないと検索から来ても役に立たないので

乗せるようにしました。