ではサンプルの音量を計算したいと思います。
音量の計算
サンプルデータを扱うに当たって知るべきことは以下の2点です。両方とも前回取得できるようになりました。
- サンプルサイズ
- エンディアン
サンプルデータが以下のような形であるとします。sampleSizeはバイト単位で2か3を想定しています。
byte[] data = new byte[sampleSize];
ストリームからバイトデータを読み込みます。
bytes_read = bis.read(data, 0, data.length);
音量を最初は0で定義して配列をfor文で回します。
int value = 0;
for(int i = 0;i < sampleSize;i++){
データを桁の小さい順から見ることにします。この場合ビッグ・エンディアンは桁が大きい方から始まるため変数iとは逆の順序にします。リトル・エンディアンは小さい桁から始まるので変数iに一致します。
int index = isBigEndian?sampleSize - 1 - i:i;
javaのbyte型は正負があります。一番最大の桁は正負はそのままにして計算します。
if(i == sampleSize - 1){
value += data[index] * Math.pow(2, i * 8);
一方小さい桁は常に正の数なので-128から-1までの数字は128から255として計算されます。Byteクラスにこれを自動で変換するメソッドがあるのでこれを利用します。
value += Byte.toUnsignedInt(data[index]) * Math.pow(2, i * 8);
最大値を保持する
このように各サンプルを読み込みながら最大値を保持するようにします。最大値の初期設定は0にします。
int max = 0;
毎回読み込むたびに読み込んだサンプルの値と比較していき最大値を保持するようにします。
max = Math.max(max, Math.abs(value));
ファイルを最後まで読めば最大値を取得できたことになります。
サンプルサイズと音量の関係は以前記事にしました。
この結果をもとに次回は音声ファイルの音量を最大にしたいと思います。
ソース
ここまで作ったコードをまとめておきます。まずはサンプルの音量を求めるメソッドです。
public static int toValue(byte[] data, boolean isBigEndian){
int value = 0;
for (int i = 0; i < data.length; i++) {
int index = isBigEndian ? data.length - 1 - i : i;
if (i == data.length - 1) {
value += data[index] * Math.pow(2, i * 8);
} else {
value += Byte.toUnsignedInt(data[index]) * Math.pow(2, i * 8);
}
}
return value;
}
メインの処理です。ファイルパスは適宜変更してください。
AudioInputStream ais = AudioSystem.getAudioInputStream(new File("wav/sine.wav"));
format = ais.getFormat();
System.out.println("frame length : " + ais.getFrameLength());
System.out.println("sample rate : " + format.getSampleRate());
System.out.println("samble size : " + format.getSampleSizeInBits());
System.out.println("channels : " + format.getChannels());
System.out.println("big endian : " + format.isBigEndian());
System.out.println("mark supported : " + ais.markSupported());
Map<String, Object> properties = format.properties();
for (String key : properties.keySet()) {
System.out.println("property : " + key + "big endian = " + properties.get(key));
}
int sampleSize = format.getSampleSizeInBits() / 8;
boolean isBigEndian = format.isBigEndian();
byte[] data = new byte[sampleSize];
int bytes_read = 0;
BufferedInputStream bis = new BufferedInputStream(ais);
int max = 0;
while (true) {
bytes_read = bis.read(data, 0, data.length);
if (bytes_read <= 0) {
break;
}
int value = toValue(data, isBigEndian);
max = Math.max(max, Math.abs(value));
}
bis.close();
System.out.println("max level(abs) : " + max);