我制作了一个简单的声音均衡器,该均衡器在频域中运行,并允许用户使用4个滑块调整声音的频率。第一个负责0-5kHz,第四个负责15-20kHz。
步骤如下:
问题在于,在此过程之后,声音会失真。听起来扬声器未正确插入。我发现,如果将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
答案 0 :(得分:4)
如果您在任何一个域中进行放大,都有可能最终截断信号(听起来很可怕)。
因此,您可能需要检查ifft结果,以查看是否有任何采样值超出音频系统允许的允许范围(通常为-32768至32768或-1.0至1.0)。避免发现任何削波的方法是减小施加到fft bin的增益,或者减小原始输入信号的幅度或总的ifft结果。
动态增益控制过程的搜索项是AGC(自动增益控制),要做好就不容易了。
例如如果任何特定频率仓的音量已经为“ 10”,则您计算机的旋钮没有“ 11”。