wav 的 Audio Visualizer 看起来不对
Audio Visualizer from wav looks wrong
我在使音频可视化工具看起来准确时遇到了问题。具有大量声音的箱子往往会正确绘制,但我遇到的问题是所有没有明显声音的频率似乎都以通常在 -60dB 和 -40dB 之间反弹的值返回。这形成一条平坦的弹跳线(通常在较高频率)。
我想以每秒 30 帧的速度显示 512 个 bin 或更少。几周来我一直在不停地阅读 FFT 和音频,到目前为止我的过程是:
- 从 wav 文件加载 pcm 数据。这是每秒 44100 个样本,范围为 -/+ 32767。我假设我在将它们传递给 FFT 时将它们视为实数。
- 将这些样本分成每帧 1470 个。 (忽略 446 个)
- 取 1024 个样本并应用 Hann window。
- 将样本作为实部数组[1024] 以及虚部用零填充的另一个相同大小的数组传递给 FFT。
- 通过遍历 (samples/2) 个 bin 并执行 sqrt(real[i]*real[i] + img[i]*img[i]).
- 取 20 * log(magnitude) 得到每个 bin 的分贝级别
- 为每个箱子画一个矩形。为每一帧绘制这些箱子。
我已经用几首歌曲和我生成的 wav 文件对其进行了测试,该文件仅以 440Hz 播放音调。对于 wav 文件,我确实在 440 bin 处出现尖峰,但所有其他 bin 形成的线并不比 440 bin 短多少。同样每隔一帧,除了 440 之外的箱子看起来像一个图形对数函数,在其他箱子上有下降。
我正在用 C++ 编写这个。使用 STK 仅从音频文件加载左声道:
//put every sample in the song into a temporary vector
for (int i = 0; i < stkObject->getSize(); i++)
{
standardVector.push_back(stkObject->tick(LEFT));
}
我正在使用 FFTReal 执行 FFT:
std::vector<std::vector <double> > leftChannelData;
int numberOfFrames = stkObject->getSize()/samplesPerFrame;
leftChannelData.resize(numberOfFrames);
for(int i = 0; i < numberOfFrames; i++)
{
for(int j = 0; j < FFT_SAMPLE_LENGTH; j++)
{
real[j] = standardVector[j + (i*samplesPerFrame)];
}
applyHannWindow(real, FFT_SAMPLE_LENGTH);
fft_object.do_fft(imaginary,real);
//FFTReal instructions say to run this after an fft
fft_object.rescale(real);
leftChannelData[i].resize(FFT_SAMPLE_LENGTH/2);
for (int j = 0; j < FFT_SAMPLE_LENGTH/2; j++)
{
double magnitude = sqrt(real[j]*real[j] + imaginary[j]*imaginary[j]);
double dbValue = 20 * log(magnitude/maxMagnitude);
leftChannelData[i].at(j) = dbValue;
}
}
我不知道是什么原因造成的。我尝试了各种方法来提取我忽略的那 446 个样本,但结果似乎没有改变。我想我可能做错了一些根本性的事情。我试过在将 pcm 数据交给 fft 之前对其进行归一化,并且在找到分贝之前尝试过对幅度进行归一化,但它似乎没有用。有什么想法吗?
编辑:我看不出 log(magnitude) 和 log(magnitude/maxMagnitude) 之间有什么区别。它所做的一切似乎就是将 bin 的所有值均匀向下移动。
编辑2:
这是他们获得视觉效果的样子:
Song playing low sounds - 对数(mag)
Song playing low sounds - 相同但有 log(mag/maxMag)
同样,log(mag) 和 log(mag/maxMag) 通常看起来相同,但值跨度为负。正如 MSalters 所说,分贝可以接近-infinite,所以我可以将这些值限制在-100dB。然后取 log(mag/maxMag) 并加 100。这样矩形的高度范围从 0 到 100 而不是 -100 到 0。
这是我应该做的吗?我已经试过了,但它看起来仍然不对。也许这只是一个缩放问题?当我这样做时,很多小节并没有在听起来应该的时候超过线。如果他们确实做到了高于 0,他们也只是勉强做到了。
您必须明白,您不是在对无限信号进行傅立叶变换,而是对其进行 windowed 版本的 FT。而且你的 window 甚至不是普通的 Hann window。丢弃 446 个点实际上是一个矩形 window 函数。 window 函数的 FT 都将显示在您的输出中。
其次,dB 标度是对数的。这确实意味着它可以在没有信号的情况下变得非常低。您提到 -60 dB,但实际上它可能达到负无穷大。唯一能让你免于此的是 window 函数,它会在大约 -110 dB 处引入拖影。
长度为 1024 的量化 Von Hann window 产生的噪声(阻带纹波)很可能在 -40 到 -60 dB 左右。因此,一种策略是只设置一个阈值,并忽略(不绘制)低于该阈值的所有值。
此外,请尝试删除 rescale(real) 函数,因为这可能会在您获取对数幅度之前扭曲您的复向量。
此外,请确保您确实将音频样本正确加载到您的真实向量中(符号、位数和字节顺序)。
我在使音频可视化工具看起来准确时遇到了问题。具有大量声音的箱子往往会正确绘制,但我遇到的问题是所有没有明显声音的频率似乎都以通常在 -60dB 和 -40dB 之间反弹的值返回。这形成一条平坦的弹跳线(通常在较高频率)。
我想以每秒 30 帧的速度显示 512 个 bin 或更少。几周来我一直在不停地阅读 FFT 和音频,到目前为止我的过程是:
- 从 wav 文件加载 pcm 数据。这是每秒 44100 个样本,范围为 -/+ 32767。我假设我在将它们传递给 FFT 时将它们视为实数。
- 将这些样本分成每帧 1470 个。 (忽略 446 个)
- 取 1024 个样本并应用 Hann window。
- 将样本作为实部数组[1024] 以及虚部用零填充的另一个相同大小的数组传递给 FFT。
- 通过遍历 (samples/2) 个 bin 并执行 sqrt(real[i]*real[i] + img[i]*img[i]).
- 取 20 * log(magnitude) 得到每个 bin 的分贝级别
- 为每个箱子画一个矩形。为每一帧绘制这些箱子。
我已经用几首歌曲和我生成的 wav 文件对其进行了测试,该文件仅以 440Hz 播放音调。对于 wav 文件,我确实在 440 bin 处出现尖峰,但所有其他 bin 形成的线并不比 440 bin 短多少。同样每隔一帧,除了 440 之外的箱子看起来像一个图形对数函数,在其他箱子上有下降。
我正在用 C++ 编写这个。使用 STK 仅从音频文件加载左声道:
//put every sample in the song into a temporary vector
for (int i = 0; i < stkObject->getSize(); i++)
{
standardVector.push_back(stkObject->tick(LEFT));
}
我正在使用 FFTReal 执行 FFT:
std::vector<std::vector <double> > leftChannelData;
int numberOfFrames = stkObject->getSize()/samplesPerFrame;
leftChannelData.resize(numberOfFrames);
for(int i = 0; i < numberOfFrames; i++)
{
for(int j = 0; j < FFT_SAMPLE_LENGTH; j++)
{
real[j] = standardVector[j + (i*samplesPerFrame)];
}
applyHannWindow(real, FFT_SAMPLE_LENGTH);
fft_object.do_fft(imaginary,real);
//FFTReal instructions say to run this after an fft
fft_object.rescale(real);
leftChannelData[i].resize(FFT_SAMPLE_LENGTH/2);
for (int j = 0; j < FFT_SAMPLE_LENGTH/2; j++)
{
double magnitude = sqrt(real[j]*real[j] + imaginary[j]*imaginary[j]);
double dbValue = 20 * log(magnitude/maxMagnitude);
leftChannelData[i].at(j) = dbValue;
}
}
我不知道是什么原因造成的。我尝试了各种方法来提取我忽略的那 446 个样本,但结果似乎没有改变。我想我可能做错了一些根本性的事情。我试过在将 pcm 数据交给 fft 之前对其进行归一化,并且在找到分贝之前尝试过对幅度进行归一化,但它似乎没有用。有什么想法吗?
编辑:我看不出 log(magnitude) 和 log(magnitude/maxMagnitude) 之间有什么区别。它所做的一切似乎就是将 bin 的所有值均匀向下移动。
编辑2: 这是他们获得视觉效果的样子:
Song playing low sounds - 对数(mag)
Song playing low sounds - 相同但有 log(mag/maxMag)
同样,log(mag) 和 log(mag/maxMag) 通常看起来相同,但值跨度为负。正如 MSalters 所说,分贝可以接近-infinite,所以我可以将这些值限制在-100dB。然后取 log(mag/maxMag) 并加 100。这样矩形的高度范围从 0 到 100 而不是 -100 到 0。
这是我应该做的吗?我已经试过了,但它看起来仍然不对。也许这只是一个缩放问题?当我这样做时,很多小节并没有在听起来应该的时候超过线。如果他们确实做到了高于 0,他们也只是勉强做到了。
您必须明白,您不是在对无限信号进行傅立叶变换,而是对其进行 windowed 版本的 FT。而且你的 window 甚至不是普通的 Hann window。丢弃 446 个点实际上是一个矩形 window 函数。 window 函数的 FT 都将显示在您的输出中。
其次,dB 标度是对数的。这确实意味着它可以在没有信号的情况下变得非常低。您提到 -60 dB,但实际上它可能达到负无穷大。唯一能让你免于此的是 window 函数,它会在大约 -110 dB 处引入拖影。
长度为 1024 的量化 Von Hann window 产生的噪声(阻带纹波)很可能在 -40 到 -60 dB 左右。因此,一种策略是只设置一个阈值,并忽略(不绘制)低于该阈值的所有值。
此外,请尝试删除 rescale(real) 函数,因为这可能会在您获取对数幅度之前扭曲您的复向量。
此外,请确保您确实将音频样本正确加载到您的真实向量中(符号、位数和字节顺序)。