Звук искажается после умножения частотного спектра на постоянное - PullRequest
0 голосов
/ 27 декабря 2018

Я делаю простой звуковой эквалайзер, который работает в частотной области и позволяет пользователю регулировать частоты звука, используя 4 ползунка.Первый отвечает за 0 - 5 кГц, четвертый - за 15-20 кГц.

Шаги следующие:

  1. Я читаю wav-файл и сохраняю его в массиве с плавающей запятой
  2. Я выполняю сложное FFT для этого массива (отдельно для левого и правого канала)
  3. Я умножаю действительные и мнимые части бинов, представляющих частоты 0-5 кГц (как положительные, так и отрицательные), на 1.1 3.981 для увеличения этих низких частот на 10% 12дБ в конечном звуке.
  4. Я выполняю ifft на массиве
  5. Я чередую реальные части левого и правого каналов (возвращаемые 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

1 Ответ

0 голосов
/ 27 декабря 2018

Если вы усиливаете в любом домене, вы можете в конечном итоге обрезать сигнал (что может звучать ужасно).

Таким образом, вам может потребоваться проверить результаты ifft, чтобы увидеть, превышают ли какие-либо значения семплов допустимый диапазон (обычно от -32768 до 32768 или от -1.0 до 1.0), что позволяет ваша аудиосистема.Чтобы избежать какого-либо найденного отсечения, нужно либо уменьшить усиление, применяемое к ячейкам БПФ, либо уменьшить амплитуду исходного входного сигнала или итогового результата.

Условием поиска для процесса динамического управления усилением являетсяAGC (автоматическое управление усилением), что нетривиально, чтобы преуспеть.

Например, если громкость для любого конкретного частотного бина уже находится в "10", ручка вашего компьютера не имеет "11".

...