为什么我的FFT显示器输出与Windows Media Player不同?

7
我正在尝试使用Visualizer类在Android中实现音频频谱分析器。
我在OnDataCaptureListener()事件的onFftDataCapture()方法中获取FFT数据,并使用drawLines()在画布上绘制。
但是频谱显示不正确。我只能看到图形左侧的变化。但是在Windows Media Player中,同一首歌曲的输出是不同的。我错过了什么吗?
有人可以给我提供一个示例或链接帮助我吗? 代码
mVisualizer.setDataCaptureListener(
            new Visualizer.OnDataCaptureListener() {

                public void onWaveFormDataCapture(Visualizer visualizer,
                        byte[] bytes, int samplingRate) {}

                public void onFftDataCapture(Visualizer visualizer,
                        byte[] bytes, int samplingRate) {
                    mVisualizerView.updateVisualizer(bytes, samplingRate);
                }
            }, Visualizer.getMaxCaptureRate() / 2, false, true);

onPaint()

    for (int i = 0; i < mBytes.length / 2; i++) {
        mPoints[i * 4] = i * 8;
        mPoints[i * 4 + 1] = 0;
        mPoints[i * 4 + 2] = i * 8;
        byte rfk = mBytes[2 * i];
        byte ifk = mBytes[2 * i + 1];
        magnitude = (float) (rfk * rfk + ifk * ifk);
        int dbValue = (int) (10 * Math.log10(magnitude));
        mPoints[i * 4 + 3] = (float) (dbValue * 7);
    }       
    canvas.drawLines(mPoints, mForePaint);

其中mVisualizer是Visualizer类的对象,mBytes是从onFftDataCapture事件中获取的FFT数据。

您可以在这里阅读有关事件返回的FFT数据的更多信息。

这是我从onFftDataCapture()获得的值:

[90,-1,-27,102,13,-18,40,33,-7,16,-23,-23,-2,-8,-11,-9,-8,-33,-29,44,4,-9,-15,-1,-2,-17,-7,1,1,0,3,-11,-5,10,-24,-6,-23,1,-9,-21,-2,4,9,-10,-14,-5,-16,8,6,-16,14,3,7,15,10,-2,-15,-14,-5,10,8,23,-1,-16,-2,-6,4,9,-1,0,0,9,1,4,-2,6,-6,-6,8,-4,6,6,-4,-5,-5,-2,3,0,-1,0,-7,0,2,1,0,1,-1,0,-1,1,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,-1]

任何想法,链接都会很有帮助。

Partial value drawing

full value drawing

@Chris Stratton更新

现在我正在播放1000 Hz的方波文件,并截取了屏幕截图。你有什么建议?

1 KHz spectrum

在@ruhalde的建议后更新

现在我正在播放频率扫描(20-20000 Hz)文件,这个文件生成了以下输出。

Frequency sweep (20-20000 Hz) output


你每次重绘频谱之前都清除画布了吗?我只是(猜测)这些错误碎片是上一个绘制留下的...... - Guy Sirton
我在updateVisualizer()方法中调用了invalidate()。它正在执行该任务。 - Raj
我认为你的dB和幅度公式不正确。首先,幅度是通过乘以SQR(2)来计算的。此外,在dB中,您需要除以一个限制,因为dB始终是与某些内容相关的度量。您没有按照限制进行划分,因此您的限制为1 dB,这就是为什么您的图形如此奇怪的原因。请查看此处的公式 http://zone.ni.com/devzone/cda/tut/p/id/4278 - ruhalde
你不想快速扫描,因为为了调试FFT分析仪,你希望它只查看一个频率的输入。如果该频率在操作过程中发生变化,将得到更复杂的结果,使问题更难以理解。- 如果必须使用文件而不是实时生成器,则最好使用每个文件具有一个频率的十个文件。此外,这个最新的图形根本不是有效的输出 - 也许你的屏幕截图捕捉到在两个不同频率的扫描显示之间重新绘制的东西。 - Chris Stratton
2个回答

2

我看到你的代码中有一些缺陷,主要是在这里 >>

Visualizer.getMaxCaptureRate() / 2

不需要使用最大捕获速率/2,只需将值放在每秒10到30次之间(根据文档,这是毫赫兹,即在10000和30000之间),这就足以避免闪烁并减少对Visualizer内部资源的压力。此外,在代码中仅绘制20到20KHz之间的幅度,即可听到的频谱。在您的代码中,您正在绘制0到捕获速率/2之间的每个频率,即最大速率/2,谁知道哪个频率是更高的...

除此之外,您需要一个纯正的正弦波,不断从0扫描到20Khz,以查看其外观,最好是没有压缩的原始文件。我不会使用任何OGG、MP3或PCM文件,我会尝试使用未压缩的WAV,也不要使用方波,因为它会产生很多谐波引起表计中的尖峰。

如果您想要,可以从这里获取扫描文件。

您是否尝试过在另一个线程运行,轮询getFft()而不是使用OnDataCaptureListener进行操作?我会尝试在Runnable内使用这种方法,并通过runOnUtiThread()方法更新UI。


测试单一频率输入在整个范围内很可能是调试的关键,但自动扫描可能会使分析变得困难,除非扫描足够缓慢以便能够理解每个频率所看到的内容。问题情况需要使用稳定的输入进行测试。 - Chris Stratton

2
可能有必要弄清楚行为与预期的差异(希望这会导致对原因的理解),通过在正在开发的android应用程序和窗口参考上播放已知幅度正弦波来测试。一次测试一个合成频率,并查看它在每个屏幕上的绘图位置,幅度和特异性以及表观幅度。
例如,您可能会发现覆盖频率范围的差异,或者一个版本可能将频率绘制在对数轴(十年或八度)上而不是线性轴上。
如果您的数据源是麦克风,则输入电路或设置中也可能存在滚降。
链接的文档没有说明使用哪种窗口函数。此外,对于原始FFT输出,您可以在相邻的bin之间分配能量,因此将每个点显示为两个或三个相邻点的平均值可以产生更一致的结果。

提供信息,我需要为我的媒体播放器获取频谱,并将尝试使用“已知幅度正弦波”的建议。 - Raj
我建议不要使用方波。你知道方波的频谱是什么样子吗?话虽如此,你可以看到一些有趣的东西——第三个峰值存在着我提到的相邻频率间能量分配问题。看起来你在15千赫兹左右有一个合理的响应,然后可能由于某个滤波器的原因出现了一些衰减。10千赫兹左右的“洞”很有趣。建议你通过设置一个可以调节正弦波频率的东西来更详细地探索这个问题,这样你就只会有一个恒定输入幅度的频率。 - Chris Stratton
1
我现在看到你正在使用一个ogg文件 - 不要这么做。即使该文件解码后在Audacity中绘制的频谱似乎还不错,但你所使用的解码器可能是高频滚降和噪声底部缺失的源头。为了评估你的FFT和显示实现,应该不仅使用正弦波而非方波进行测试,而且还要使用线性PCM采样 - 如果必须使用声音文件而非原始数据,则它们应该是.wav文件。 - Chris Stratton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接