前回でwavファイルから各サンプルの音量、レベルを読みだして、最大レベルも取得しました。今度はこのレベルを最大になるように調整して別のwavファイルに書き出すようにします。
係数を求める
ファイルの最大レベルがわかればその値がフォーマットん許す限りの最大値になるような係数を求めてすべてのサンプルに掛け合わせるようにします。
Math.pow(2, sampleSize * 8 - 1) - 1;
この計算式を使うとサンプルサイズが16ビットの場合は32767、24ビットの場合は8388607です。フォーマットが変わることとあまり使われないことのため低音質の8ビットや高音質の32ビットは対象外とします。
double coef = (double)(Math.pow(2, sampleSize * 8 - 1) - 1) / (double)max;
バイト配列に戻す
int型で計算した値はまた書き出し時にバイト配列に戻す必要があります。以下戻すメソッドです。
int型は4バイト使うのでそれをサンプルサイズに合わせて配列を作り直しています。また配列に戻す時にエンディアンを意識する必要もあります。
public static byte[] toData(int value, int sampleSize, boolean isBigEndian){ final ByteBuffer byteBuffer = ByteBuffer.allocate(4); byteBuffer.putInt(value); byte[] buffer = byteBuffer.array(); byte[] ret = new byte[sampleSize]; for (int i = 0; i < sampleSize; i++) { int index = isBigEndian ? i + 1 : sampleSize - i; ret[i] = buffer[index]; } return ret; }
あとはAUdioSystemによる書き出しに任せますがこの場合使うクラスはInputStreamを継承する必要がありreadメソッドを実装しなくてはいけません。以下はreadメソッドの概要です。
byte[] data; int index; @Override public int read() throws IOException { if (index >= data.length) { if (bis.read(data, 0, data.length) <= 0) { return -1; } int value = toValue(data, isBigEndian); value *= coef; data = toData(value, sampleSize, isBigEndian); index = 0; } return Byte.toUnsignedInt(data[index++]); }
wavファイルのサンプルサイズに合わせてサンプル一つを読み込み係数を掛けてバイト配列に戻しておきます。このバイト配列に読み込んでないデータがある限りそれを返しなくなったら次のサンプルを読むようにします。