wav 的 Audio Visualizer 看起来不对

Posted

技术标签:

【中文标题】wav 的 Audio Visualizer 看起来不对【英文标题】:Audio Visualizer from wav looks wrong 【发布时间】:2015-01-23 00:59:34 【问题描述】:

我无法使音频可视化器看起来准确。具有大量声音的分档倾向于正确绘制,但我遇到的问题是所有没有明显声音的频率似乎都以通常在 -60dB 和 -40dB 之间反弹的值返回。这形成了一条平坦的弹跳线(通常在较高频率中)。

我想以每秒 30 帧的速度显示 512 个或更少的 bin。几周以来,我一直在不停地阅读 FFT 和音频,到目前为止,我的过程是:

从 wav 文件加载 pcm 数据。这以每秒 44100 个样本的形式出现,范围为 -/+ 32767。我假设在将它们传递给 FFT 时将它们视为实数。 将这些样本分成每帧 1470 个。 (446 被忽略) 采集 1024 个样本并应用 Hann 窗。 将样本作为实数数组 [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 之外的 bin 看起来像一个图形日志函数,在其他 bin 上有一个下降。

我是用 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,他们只是勉强做到了。

【问题讨论】:

你的过程本身听起来是正确的 你能发一张输出的图片吗 您不应该采用绝对值。通常你会看到类似 mag = 20*log(abs(fft)) 的代码,在这种情况下,abs 正在执行 sqrt(re^2+im^2)。 对不起,我误读了您的评论,这里有几张输出的图片。 Picture of normal song 和 a tone at 440 哦,是的,你是对的。我忘了删除那个。我补充说,当我在玩一些没有成功的想法时。我会编辑它。 【参考方案1】:

您必须了解,您不是在进行无限信号的傅立叶变换,而是对其加窗版本进行 FT。而且您的窗户甚至不是普通的汉恩窗户。丢弃 446 个点实际上是一个矩形窗口函数。窗口函数的 FT 都将显示在您的输出中。

其次,dB 刻度是对数的。这确实意味着在没有信号的情况下它可能会变得非常低。您提到-60 dB,但实际上它可能会达到负无穷大。唯一可以让您摆脱这种情况的是窗口函数,它会在大约 -110 dB 处引入拖影。

【讨论】:

我尝试过采集 2048 个样本并用零填充剩余的 600 个样本,并从下一帧中采集样本,但我仍然遇到同样的问题。另外关于分贝水平,我用一些截图编辑了我的帖子。【参考方案2】:

长度为 1024 的量化 Von Hann 窗口产生的噪声(阻带纹波)可能在 -40 到 -60 dB 左右。因此,一种策略是只设置一个阈值,并忽略(不绘制)低于该阈值的所有值。

另外,请尝试删除 rescale(real) 函数,因为这可能会在您获取对数幅度之前扭曲您的复数向量。

另外,请确保您实际上将音频样本正确加载到您的真实向量中(符号、位数和字节序)。

【讨论】:

已经有一段时间了,但这对我有用。设置一个阈值并且不绘制任何比 -40 到 -60 dB 更安静的东西,在较高的频率上进行一些手动微调就足够了。

以上是关于wav 的 Audio Visualizer 看起来不对的主要内容,如果未能解决你的问题,请参考以下文章

[Audio processing] wav音频文件合并

gstreamer:没有可用于“audio/x-wav”类型的解码器/找不到合适的插件

无法在 chrome、firexfox 浏览器中使用 html <audio> 播放压缩的 wav 文件

[Audio processing] wav音频文件读取int和double数组的关系

多浏览器播放wav格式的音频文件

Web Audio API - Javascript 创建的 WAV 文件长度不正确且无声