使用快速傅里叶变换分析音频

时间:2009-03-02 23:13:17

标签: python audio signal-processing fft spectrum

我正在尝试在python中创建一个图形频谱分析器。

我目前正在读取1024字节的16位双通道44,100 Hz采样率音频流,并将两个通道的幅度平均在一起。所以现在我有256个签名短裤阵列。我现在想要使用像numpy这样的模块在该阵列上预先形成一个fft,然后使用结果创建图形频谱分析器,开始时只需32个柱。

我已经阅读了有关快速傅立叶变换和离散傅立叶变换的维基百科文章,但我仍然不清楚结果数组代表什么。这是我使用numpy在我的数组上执行fft之后数组的样子:

   [ -3.37260500e+05 +0.00000000e+00j   7.11787022e+05 +1.70667403e+04j
   4.10040193e+05 +3.28653370e+05j   9.90933073e+04 +1.60555003e+05j
   2.28787050e+05 +3.24141951e+05j   2.09781047e+04 +2.31063376e+05j
  -2.15941453e+05 +1.63773851e+05j  -7.07833051e+04 +1.52467334e+05j
  -1.37440802e+05 +6.28107674e+04j  -7.07536614e+03 +5.55634993e+03j
  -4.31009964e+04 -1.74891657e+05j   1.39384348e+05 +1.95956947e+04j
   1.73613033e+05 +1.16883207e+05j   1.15610357e+05 -2.62619884e+04j
  -2.05469722e+05 +1.71343186e+05j  -1.56779748e+04 +1.51258101e+05j
  -2.08639913e+05 +6.07372799e+04j  -2.90623668e+05 -2.79550838e+05j
  -1.68112214e+05 +4.47877871e+04j  -1.21289916e+03 +1.18397979e+05j
  -1.55779104e+05 +5.06852464e+04j   1.95309737e+05 +1.93876325e+04j
  -2.80400414e+05 +6.90079265e+04j   1.25892113e+04 -1.39293422e+05j
   3.10709174e+04 -1.35248953e+05j   1.31003438e+05 +1.90799303e+05j...

我想知道这些数字究竟代表什么,以及我如何将这些数字转换为32个柱中每一个的高度百分比。另外,我应该将2个频道平均在一起吗?

4 个答案:

答案 0 :(得分:200)

您要显示的数组是音频信号的傅里叶变换系数。这些系数可用于获得音频的频率内容。 FFT是针对复值输入函数定义的,因此即使您的输入都是实数值,您获得的系数也将是虚数。为了获得每个频率的功率量,您需要计算每个频率的FFT系数的幅度。这是只是系数的实部,你需要计算其实部和虚部的平方和的平方根。也就是说,如果你的系数是a + b * j,那么它的大小是sqrt(a ^ 2 + b ^ 2)。

一旦计算出每个FFT系数的幅度,就需要确定每个FFT系数属于哪个音频。 N点FFT将以N等间隔频率为您提供信号的频率成分,从0开始。因为您的采样频率为44100样本/秒。并且FFT中的点数为256,您的频率间隔为44100/256 = 172 Hz(大约)

数组中的第一个系数是0频率系数。这基本上是所有频率的平均功率水平。其余的系数将从0到172的倍数计数,直到达到128.在FFT​​中,您只能测量高达采样点一半的频率。请阅读Nyquist FrequencyNyquist-Shannon Sampling Theorem上的这些链接,如果您是一个贪婪的惩罚者,需要知道原因,但基本结果是您的较低频率将被复制或aliased in更高频率的桶。所以频率从0开始,每个系数增加172 Hz直到N / 2系数,然后减少172 Hz直到N - 1系数。

这应该是足够的信息来帮助您入门。如果您想要比维基百科上提供的更加平易近人的FFT介绍,您可以尝试Understanding Digital Signal Processing: 2nd Ed.。这对我很有帮助。

这就是那些数字所代表的。可以通过将每个频率分量幅度缩放所有分量幅度的总和来转换为高度的百分比。虽然,这只会给出相对频率分布的表示,而不是每个频率的实际功率。您可以尝试按频率分量的最大幅度进行缩放,但我不确定它会显示得非常好。找到可行的缩放因子的最快方法是尝试大声和柔和的音频信号,以找到正确的设置。

最后,如果要整体显示整个音频信号的频率内容,则应将两个通道平均在一起。您正在将立体声音频混合为单声道音频并显示组合频率。如果您需要两个单独的左右频率显示,那么您需要分别在每个通道上执行傅里叶变换。

答案 1 :(得分:26)

虽然这个帖子已有数年之久,但我发现它非常有用。我只想向发现这一点并试图创造类似内容的任何人提供我的意见。

至于分为条形,这不应该像antti所建议的那样,通过根据条形数量平均分割数据。最有用的是将数据分成八度音阶,每个八度音程是前一个频率的两倍。 (即100hz是高于50hz的一个八度音阶,高于25hz的一个八度)。

根据您想要的条数,将整个范围划分为1 / X倍频程范围。 根据条形图上给定的中心频率A,您可以从以下位置获得条形图的上限和下限:

upper limit = A * 2 ^ ( 1 / 2X )
lower limit = A / 2 ^ ( 1 / 2X )

要计算下一个相邻的中心频率,请使用类似的计算:

next lower =  A / 2 ^ ( 1 / X )
next higher = A * 2 ^ ( 1 / X )

然后平均适合这些范围的数据以获得每个条形的振幅。

例如: 我们想要分成1/3倍频程范围,我们从1khz的中心频率开始。

Upper limit = 1000 * 2 ^ ( 1 / ( 2 * 3 ) ) = 1122.5
Lower limit = 1000 / 2 ^ ( 1 / ( 2 * 3 ) ) =  890.9

鉴于44100hz和1024个样本(每个数据点之间为43hz),我们应该将值从21到26平均。(890.9 / 43 = 20.72~21和1122.5 / 43 = 26.10~26)

(1/3倍频程可以让你在~40hz到~20khz之间约30巴)。 正如你现在可以弄清楚的那样,随着我们走得更高,我们将平均更大的数字范围。低条通常仅包括1个或少量数据点。而较高的条形可以是数百个点的平均值。原因是86hz是一个高于43hz的八度音阶......而10086hz听起来几乎与10043hz相同。

答案 2 :(得分:10)

您所拥有的是一个样本,其时间长度为256/44100 = 0.00580499秒。这意味着您的频率分辨率为1 / 0.00580499 = 172 Hz。从Python中获得的256个值对应于频率,基本上从86 Hz到255 * 172 + 86 Hz = 43946 Hz。你得到的数字是复数(因此每秒数字末尾的“j”)。

已编辑:错误信息

您需要通过计算sqrt(i 2 + j 2 )将复数转换为幅度,其中i和j是实部和虚部。

如果你想要32个小节,你应该根据我的理解取四个连续振幅的平均值,得到256/4 = 32个小节你想要的。

答案 3 :(得分:0)

FFT返回N个复数值,您可以计算出module=sqrt(real_part^2+imaginary_part^2)。要获得每个频段的值,您必须对频段内所有谐波的模块求和。您可以在下面看到有关10 bar频谱分析仪的示例。必须包装C代码才能获得pyd python模块。

float *samples_vett;
float *out_filters_vett;
int Nsamples;
float band_power = 0.0;
float harmonic_amplitude=0.0;
int i, out_index;

out_index=0;


for (i = 0; i < Nsamples / 2 + 1; i++)       
        {
            if (i == 1 || i == 2 || i == 4 || i == 8 || i == 17 || i == 33 || i == 66 || i == 132 || i == 264 || i == 511)
            {
                out_filters_vett[out_index] = band_power; 
                band_power = 0; 
                out_index++;  
            }

            harmonic_amplitude = sqrt(pow(ttfr_out_vett[i].r, 2) + pow(ttfr_out_vett[i].i, 2));
            band_power += harmonic_amplitude;

        }

我用Python设计并制作了整个10 led条形频谱分析仪。取而代之的是使用nunmpy库(太大而没有用,无法仅获取FFT),而是创建了一个python pyd模块(仅27KB)来获取FFT并将整个音频频谱拆分为多个频段。

此外,为了读取输出音频,创建了回送WASapi portaudio pyd模块。您可以在图像中看到项目(框图) 10BarsSpectrumAnalyzerWithWASapi.jpg

只需在我的YouTube频道上添加了一个教学视频:how to design and make a very smart Python Spectrum Analyzer 10 Led Bar