首页 > 解决方案 > 将频谱乘以常数后声音失真

问题描述

我制作了一个在频域中运行的简单声音均衡器,让用户可以使用 4 个滑块来调整声音中的频率。第一个负责 0 - 5kHz,第四个负责 15-20kHz。

步骤如下:

  1. 我读取 wav 文件并将其存储在浮点数组中
  2. 我在该阵列上执行复杂的 fft(分别用于左声道和右声道)
  3. 我将代表 0-5kHz 频率(正和负)的 bin 的实部和虚部乘以1.1 3.981 ,以在最终声音中将这些低频增加10% 12dB
  4. 我在数组上执行 ifft
  5. 我交替左右声道的真实部分(由 ifft 返回)以创建最终音频

问题是在这个过程之后声音会失真。听起来扬声器没有正确插入。我发现如果我将 ifft 返回的值除以任意常数,那么最终的声音是正确的,但要安静得多。我根据 ifft 的结果在时域中进行划分。

如果我将频率乘以小于 1 的数字,则不会出现问题。因此,如果频率衰减,则不需要在时域中进一步划分。

我想整个过程都有错误。但是如果所有步骤都很好,我应该如何处理失真的声音?在时域中划分是一个合适的解决方案吗?我应该用什么数字来划分结果,这样声音才不会失真?

编辑

这是我用来执行所示步骤的代码。我使用 FFT 的 Apache Commons 数学实现和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

标签: javaaudiosignal-processingfftaudio-processing

解决方案


如果您在任一域中放大,您最终可能会削波信号(这听起来很可怕)。

因此,您可能需要检查您的 ifft 结果,以查看是否有任何样本值超出您的音频系统允许的允许范围(通常是 -32768 到 32768,或 -1.0 到 1.0)。避免任何发现的削波的方法是要么降低应用于 fft bin 的增益,要么降低原始输入信号的幅度或总 ifft 结果。

动态增益控制过程的搜索词是 AGC(自动增益控制),这并非易事。

例如,如果任何特定频段的音量已经为“10”,则您的计算机旋钮没有“11”。


推荐阅读