矩形波のwavファイルはこれまでなんども作ってますが少しずつ洗練したプログラムになって来ています。最適化といってこの先発展した処理をするにあたってここでの作業は無駄にはなりません。
Javaで矩形波のWavファイルを作成する(1)
Javaで矩形波のWavファイルを作成する(2)
Javaで矩形波のオシレーターを作る
各処理を見直す
クラス名とフィールド変数
汎用のwav्ファイル作成クラスとするために名前も汎用的なSampleWavという名前にしました。サンプルレートはどこからでも静的に参照できるようstaticにし変更できないようにfinalにしてあります。
また今までオシレータークラスを持つようにしていましたがここではOscillatableという前回作ったインターフェイスに変更しました。これでこのインタフェースを実装しているクラスであればこのクラスのオシレータとして利用できるようになります。他の処理はサイン波作成のときと同じです。
public class SampleWav extends InputStream { public static final float SAMPLE_RATE = 48000; boolean signed = true; boolean big_endian = true; int sample_size_byte = 2; int channels = 1; double freq; double seconds; ArrayList list; double volume; Oscillatable oscillatable; long index = 0;
コンストラクタ
Oscillatableを引数に追加しました。
public SampleWav(Oscillatable oscillatable, double freq, double seconds){ this.freq = freq; this.seconds = seconds; list = new ArrayList<>(); volume = Math.pow(2, sample_size_byte * 8 - 1) - 1; this.oscillatable = oscillatable; }
readメソッド他
他の処理はオシレーターのクラスからインタフェースの参照値oscillatableに変更された以外は同じです。
@Override public int read() throws IOException { if(list.isEmpty()){ double value = oscillatable.read(freq, volume); ByteBuffer buffer = ByteBuffer.allocate(8); buffer.putLong((long) value); byte[] array = buffer.array(); for (int i = 8 - sample_size_byte; i < 8; i++) { list.add(array[i]); } } int ret = Byte.toUnsignedInt(list.remove(0)); return ret; } public long length(){ return (long)(SAMPLE_RATE * channels * seconds); } public AudioFormat getFormat(){ return new AudioFormat(SAMPLE_RATE, sample_size_byte * 8, channels, signed, big_endian); }
メインの処理
メインの処理ではSampleWavを作成してオシレーターを作成して引数として指定しています。このSquareOscillatorを他のOscillatable実装クラスに変更すると音色を変えることができます。
public static void main(String[] arg) throws IOException { SampleWav ss = new SampleWav(new SquareOscillator(SAMPLE_RATE), 440, 3); AudioSystem.write( new AudioInputStream(ss, ss.getFormat(), ss.length()), AudioFileFormat.Type.WAVE, new File("/Users/myaccount/mywork/square_440.wav")); }
実行結果
音声ファイルは前回と全く同じものです。
ソース
前回のものより洗練されてきました。
package mocha.sound; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; public class SampleWav extends InputStream { public static final float SAMPLE_RATE = 48000; boolean signed = true; boolean big_endian = true; int sample_size_byte = 2; int channels = 1; double freq; double seconds; ArrayList list; double volume; Oscillatable oscillatable; long index = 0; public SampleWav(Oscillatable oscillatable, double freq, double seconds) { this.freq = freq; this.seconds = seconds; list = new ArrayList<>(); volume = Math.pow(2, sample_size_byte * 8 - 1) - 1; this.oscillatable = oscillatable; } @Override public int read() throws IOException { if (list.isEmpty()) { double value = oscillatable.read(freq, volume); ByteBuffer buffer = ByteBuffer.allocate(8); buffer.putLong((long) value); byte[] array = buffer.array(); for (int i = 8 - sample_size_byte; i < 8; i++) { list.add(array[i]); } } int ret = Byte.toUnsignedInt(list.remove(0)); return ret; } public long length() { return (long) (SAMPLE_RATE * channels * seconds); } public AudioFormat getFormat() { return new AudioFormat(SAMPLE_RATE, sample_size_byte * 8, channels, signed, big_endian); } public static void main(String[] arg) throws IOException { SampleWav ss = new SampleWav(new SquareOscillator(SAMPLE_RATE), 440, 3); AudioSystem.write( new AudioInputStream(ss, ss.getFormat(), ss.length()), AudioFileFormat.Type.WAVE, new File("/Users/myaccount/mywork/square_440.wav")); } }