将频谱乘以常数后

时间:2018-12-26 21:14:26

标签: java audio signal-processing fft audio-processing

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

步骤如下:

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

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

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

我认为整个过程中有一个错误。但是,如果所有步骤都很好,我应该如何处理失真的声音?在时域划分是正确的解决方案吗?那我应该用哪个数字来划分结果,以免声音失真?

编辑

这是我用来执行所介绍步骤的代码。我使用FFT的Apache Commons数学实现和从http://stackoverflow.com/a/26824664/2891664那里获取的SimpleAudioConversion

// 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 :(得分:4)

如果您在任何一个域中进行放大,都有可能最终截断信号(听起来很可怕)。

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

动态增益控制过程的搜索项是AGC(自动增益控制),要做好就不容易了。

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