java - 将频谱乘以常数后声音失真
问题描述
我制作了一个在频域中运行的简单声音均衡器,让用户可以使用 4 个滑块来调整声音中的频率。第一个负责 0 - 5kHz,第四个负责 15-20kHz。
步骤如下:
- 我读取 wav 文件并将其存储在浮点数组中
- 我在该阵列上执行复杂的 fft(分别用于左声道和右声道)
- 我将代表 0-5kHz 频率(正和负)的 bin 的实部和虚部乘以
1.13.981 ,以在最终声音中将这些低频增加10%12dB。 - 我在数组上执行 ifft
- 我交替左右声道的真实部分(由 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
解决方案
如果您在任一域中放大,您最终可能会削波信号(这听起来很可怕)。
因此,您可能需要检查您的 ifft 结果,以查看是否有任何样本值超出您的音频系统允许的允许范围(通常是 -32768 到 32768,或 -1.0 到 1.0)。避免任何发现的削波的方法是要么降低应用于 fft bin 的增益,要么降低原始输入信号的幅度或总 ifft 结果。
动态增益控制过程的搜索词是 AGC(自动增益控制),这并非易事。
例如,如果任何特定频段的音量已经为“10”,则您的计算机旋钮没有“11”。
推荐阅读
- office-js - 在 iOS 或 Android Outlook 插件上确定 `accountType == office365`
- php - php-ffi:返回类型 const char* 是一个字符串
- php - 调用未定义的方法 Omnipay\Quickpay\Message\Notification::send()
- firebase - 退货清单
来自 Firestore 颤振 - javascript - NextJS Image 组件没有响应我的内联 CSS
- java - 通过 String.toCharArray() 对字符串进行迭代在一种情况下提高了性能,但在另一种类似情况下则恶化了
- selenium - Python selenium getting text from text area
- python - 如何在python中停止for循环的迭代?(滑窗问题)
- python - Create channel in Category discord.py
- python - relative path not work with credentials.Certificate()