Я делаю простой звуковой эквалайзер, который работает в частотной области и позволяет пользователю регулировать частоты звука, используя 4 ползунка.Первый отвечает за 0 - 5 кГц, четвертый - за 15-20 кГц.
Шаги следующие:
- Я читаю wav-файл и сохраняю его в массиве с плавающей запятой
- Я выполняю сложное FFT для этого массива (отдельно для левого и правого канала)
- Я умножаю действительные и мнимые части бинов, представляющих частоты 0-5 кГц (как положительные, так и отрицательные), на 1.1 3.981 для увеличения этих низких частот на 10% 12дБ в конечном звуке.
- Я выполняю ifft на массиве
- Я чередую реальные части левого и правого каналов (возвращаемые ifft) для создания окончательного звука
Проблема в том, что после этого процесса звук искажается.Похоже, динамики были подключены неправильно.Я обнаружил, что если я разделю значения, возвращаемые ifft, на произвольную константу, то окончательный звук будет правильным, но намного тише.Я делаю во временной области, по результатам ifft.
Проблема не возникает, если я умножаю частоты на число меньше 1. Поэтому, если частоты ослаблены, дальнейшее деление во временной области не требуется,
Полагаю, во всем процессе произошла ошибка.Но если все шаги в порядке, как я должен бороться с искаженным звуком?Является ли разделение во временной области правильным решением?Какое число я должен использовать для разделения результатов, чтобы звук не искажался?
РЕДАКТИРОВАТЬ
Это код, который я использую для выполнения представленных шагов.Я использую математическую реализацию Apache Commons для FFT и SimpleAudioConversion
класса, взятого оттуда http://stackoverflow.com/a/26824664/2891664
// read file and store playable content in byte array
File file = new File("/home/kamil/Downloads/Glory.wav");
AudioInputStream in = AudioSystem.getAudioInputStream(file);
AudioFormat fmt = in.getFormat();
byte[] bytes = new byte[in.available()];
int result = in.read(bytes);
// convert bytes to float array
float[] samples = new float[bytes.length * 8 / fmt.getSampleSizeInBits()];
int validSamples = SimpleAudioConversion.decode(bytes, samples, result, fmt);
// find nearest power of 2 to zero-pad array in order to use fft
int power = 0;
while (Math.pow(2, power) < samples.length / 2)
power++;
// divide data into left and right channels
double[][] left = new double[2][(int) Math.pow(2, power)];
double[][] right = new double[2][(int) Math.pow(2, power)];
for (int i = 0; i < samples.length / 2; i++) {
left[0][i] = samples[2 * i];
right[0][i] = samples[2 * i + 1];
}
//fft
FastFourierTransformer.transformInPlace(left, DftNormalization.STANDARD, TransformType.FORWARD);
FastFourierTransformer.transformInPlace(right, DftNormalization.STANDARD, TransformType.FORWARD);
// here I amplify the 0-4kHz frequencies by 12dB
// 0-4kHz is 1/5 of whole spectrum, and since there are negative frequencies in the array
// I iterate over 1/10 and multiply frequencies on both sides of the array
for (int i = 1; i < left[0].length / 10; i++) {
double factor = 3.981d; // ratio = 10^(12dB/20)
//positive frequencies 0-4kHz
left[0][i] *= factor;
right[0][i] *= factor;
left[1][i] *= factor;
right[1][i] *= factor;
// negative frequencies 0-4kHz
left[0][left[0].length - i] *= factor;
right[0][left[0].length - i] *= factor;
left[1][left[0].length - i] *= factor;
right[1][left[0].length - i] *= factor;
}
//ifft
FastFourierTransformer.transformInPlace(left, DftNormalization.STANDARD, TransformType.INVERSE);
FastFourierTransformer.transformInPlace(right, DftNormalization.STANDARD, TransformType.INVERSE);
// put left and right channel into array
float[] samples2 = new float[(left[0].length) * 2];
for (int i = 0; i < samples2.length / 2; i++) {
samples2[2 * i] = (float) left[0][i];
samples2[2 * i + 1] = (float) right[0][i];
}
// convert back to byte array which can be played
byte[] bytes2 = new byte[bytes.length];
int validBytes = SimpleAudioConversion.encode(samples2, bytes2, validSamples, fmt);
Вы можете прослушать звук здесь https://vocaroo.com/i/s095uOJZiewf