我目前正在开发一个项目,该项目使用 JavaScript 的 WebAudio API 来显示音频频率图。 AnalyserNode.getByteFrequencyData() 填充一个数组,该数组分为“容器”,每个容器都有一个频率范围。我想知道这些“频率”对应的频率(以赫兹为单位)。
我尝试将每个 bin 解释为 1Hz 步长,但这显然是不正确的。
来自 MDN 文档
AnalyserNode.getByteFrequencyData()
:
数组中的每一项代表特定频率的分贝值。频率从采样率的 0 到 1/2 线性扩展。例如,对于
采样率,数组的最后一项将表示48000
Hz 的分贝值。24000
采样率可以从
sampleRate
上的AudioContext
属性获取(继承自BaseAudioContext
):
接口的sampleRate
属性返回一个浮点数,表示采样率(以每秒样本数为单位),供此音频上下文中的所有节点使用。 (文档)BaseAudioContext
您可以在创建
AudioContext
时通过构造函数的 options
参数设置其采样率。
您可以在创建时通过构造函数的
AudioAnalyzer
参数以及之后的任何时间通过 options
属性设置 fftSize
的 FFT 窗口大小。
来自 AnalyserNode.fftSize 的文档:
fftSize
接口的AnalyserNode
属性是一个无符号长整型值,表示在执行快速傅里叶变换(FFT)以获取频域数据时使用的样本窗口大小。
k
个 bin 的频率为
k/N*sr
,其中
sr
是采样率,
N
是 FFT 中的点数。我认为 MDN 文档是不正确的,除非他们用 FFT 做了一些非常不标准的事情。如果采样率为 48000 并进行 128 点 FFT,则
getByteFrequencyData()
中的最后一个值的索引为 63,频率为
63/128*48000 = 23625Hz
。一般而言,
N
点 FFT1 会获取
N
时域样本,并为您提供
N
频域样本。如果您的采样频率为
sr
,则每个 bin 的 bin 间距为
sr/N
2。因此,如果您的采样率为 8kHz 并进行了 8 点 FFT,则 bin 频率将为
[0, 1k, 2k, 3k, 4k, 5k, 6k, 7k]
您还可以将“奈奎斯特”(sr/2
) 以上的频率视为负频率3,因此您可以将该列表重新编号为:
[0, 1k, 2k, 3k, 4k, -3k, -2k, -1k]
如果时域信号是实值(音频就是这种情况),则频谱是对称的4,因此负频率中没有新信息。处理音频信号的 DSP 工程师经常只使用非负频率,并且大多数 FFT 库都具有用于此目的的 rfft
函数。对于 N 点 FFT,
rfft
函数通常会给出长度为
N/2+1
的结果,从而避免需要计算负频率。由于某种原因,WebAudio 设计者放弃了最后一个频率仓,其频率为
sr/2
。在实践中,在奈奎斯特频率上通常不会发生太多有趣的事情,并且分析器节点只为您提供频谱的幅度,因此它更适合可视化而不是花哨的频域处理。
1 在数学中通常称为DFT(离散傅里叶变换)。 FFT(快速傅立叶变换)是一种计算 DFT 的特殊算法。
2 如果您查看 DFT 的 定义,对于第 k
个谱仓,您使用正弦曲线
w(n) = exp(-i*2*pi*k/N*n)
,其频率为
k/N
。采样率为 1,因为我们正在测量样本中的时间。
3 从概念上讲,DFT 结果是周期性的,周期为 N
,因此
X[k] = X[k+N]
始终为真。请记住
k
是这里的索引,而不是 bin 频率,并且我们允许负索引从末尾开始环绕,numpy 风格。
4 特别共轭对称所以 X[-k] = conj(X[k])