代码之家  ›  专栏  ›  技术社区  ›  WangYudong

将FFT谱幅度归一化为0db

  •  8
  • WangYudong  · 技术社区  · 6 年前

    我正在使用FFT从音频文件中提取每个频率分量的振幅。实际上,Audacity中已经有一个叫做plot spectrum的函数可以帮助解决这个问题。这样做 示例音频文件 由3kHz正弦和6kHz正弦组成,频谱结果如下图所示。您可以看到峰值在3kHz和6kHz,没有额外的频率。

    现在我需要实现相同的函数,并在Python中绘制类似的结果。在 rfft 的帮助下,我接近大胆的结果,但在得到这个结果后,我仍然有问题要解决。

    1. 第二幅图中振幅的物理意义是什么?
    2. 如何像Audacity那样将振幅归一化为0dB?
    3. 为什么6KHz以上的频率有如此高的振幅(90)?我能把这些频率调整到相对较低的水平吗?
      1. 相关代码:

        导入numpy as np 从Pylab导入绘图,显示 从scipy.io导入wavfile 采样率,x=wavfile.read('sine3k6k.wav') fs=44100.0 rfft=np.abs(np.fft.rfft(x))。 P=20*np.log10(rfft) f=np.linspace(0,fs/2,len(p))。 绘图(F,P) 显示()

        更新

        我把汉宁窗和全长信号相乘(对吗?)拿着这个。裙板的大部分振幅低于40。

        并将Y轴缩放为分贝,如@mateen ulhaqsaid。结果更接近大胆的结果。我能把低于-90db的振幅处理得如此之低以至于可以忽略吗?

        更新的代码:

        fs,x=wavfile.read('input/sine3k6k.wav')
        x=x*np.汉宁(len(x))。
        
        rfft=np.abs(np.fft.rfft(x))。
        rfft_max=最大值(rfft)
        p=20*np.log10(rfft/rfft_max)
        f=np.linspace(0,fs/2,len(p))。
        


        关于赏金

        有了上面更新的代码,我可以用分贝测量频率分量。可能的最大值为0db。但该方法仅适用于特定的音频文件,因为它使用了该音频的rfft_max->code>。我想在一个标准规则中测量多个音频文件的频率分量,就像Audacity一样。

        我还在Audacity Forum中开始了讨论我胆大妄为,能帮助解决这个问题。拿着这个example audio file由3kHz正弦和6kHz正弦组成,频谱结果如下图所示。你可以看到峰值是3kHz和6kHz,没有额外的频率。

        enter image description here

        现在我需要实现相同的函数,并在Python中绘制类似的结果。在…的帮助下,我接近大胆的结果。rfft但得到这个结果后,我仍然有问题要解决。

        enter image description here

        1. 第二幅图中振幅的物理意义是什么?
        2. 如何像Audacity那样将振幅归一化为0dB?
        3. 为什么6KHz以上的频率有如此高的振幅(90)?我能把这些频率调整到相对较低的水平吗?

        相关代码:

        import numpy as np
        from pylab import plot, show
        from scipy.io import wavfile
        
        sample_rate, x = wavfile.read('sine3k6k.wav')
        fs = 44100.0
        
        rfft = np.abs(np.fft.rfft(x))
        p = 20*np.log10(rfft)
        f = np.linspace(0, fs/2, len(p))
        
        plot(f, p)
        show()
        

        更新

        我把汉宁窗和全长信号相乘(对吗?)拿着这个。裙板的振幅大多在40以下。

        enter image description here

        并将Y轴缩放为分贝@Mateen Ulhaq说。结果更接近大胆的结果。我能把低于-90db的振幅处理得如此之低以至于可以忽略吗?

        更新代码:

        fs, x = wavfile.read('input/sine3k6k.wav')
        x = x * np.hanning(len(x))
        
        rfft = np.abs(np.fft.rfft(x))
        rfft_max = max(rfft)
        p = 20*np.log10(rfft/rfft_max)
        f = np.linspace(0, fs/2, len(p))
        

        enter image description here


        关于赏金

        有了上面更新的代码,我可以用分贝测量频率分量。可能的最大值为0db。但是该方法只适用于特定的音频文件,因为它使用rfft_max这个音频。我想像Audacity一样,在一个标准规则中测量多个音频文件的频率分量。

        我也是started a discussion在大胆的论坛上,我仍然不清楚如何实现我的目标。

    1 回复  |  直到 6 年前
        1
  •  4
  •   igrinis    6 年前
    段大小=512 window='hanning',要使用的窗口类型 noverlap=noverlap)段间重叠 参考值=(1/np.sqrt(2)**2)仅0.5;) 填充_至=-150*(np.一个如(p))低于-150db的值不相关。 plt.ylim([-90,6]) plt.ylabel('功率谱,db')
    
    
  • 输出窗口的框架遵循大胆的风格。他们扔掉了第一个低频垃圾箱,把所有东西都切到-90db以下。
  • 它基本上是频率箱中的能量。

    import numpy as np
    from scipy.io import wavfile
    from scipy import signal
    from matplotlib import pyplot as plt
    
    segment_size = 512
    
    fs, x = wavfile.read('sine3k6k.wav')
    x = x / 32768.0  # scale signal to [-1.0 .. 1.0]
    
    noverlap = segment_size / 2
    f, Pxx = signal.welch(x,                        # signal
                          fs=fs,                    # sample rate
                          nperseg=segment_size,     # segment size
                          window='hanning',         # window type to use
                          nfft=segment_size,        # num. of samples in FFT
                          detrend=False,            # remove DC part
                          scaling='spectrum',       # return power spectrum [V^2]
                          noverlap=noverlap)        # overlap between segments
    
    # set 0 dB to energy of sine wave with maximum amplitude
    ref = (1/np.sqrt(2)**2)   # simply 0.5 ;)
    p = 10 * np.log10(Pxx/ref)
    
    fill_to = -150 * (np.ones_like(p))  # anything below -150dB is irrelevant
    plt.fill_between(f, p, fill_to )
    plt.xlim([f[2], f[-1]])
    plt.ylim([-90, 6])
    # plt.xscale('log')   # uncomment if you want log scale on x-axis
    plt.xlabel('f, Hz')
    plt.ylabel('Power spectrum, dB')
    plt.grid(True)
    plt.show()
    

    • segment_sizeSize
    • 默认窗口类型为“Hanning”,如果需要,可以更改它。
    • segment_size/2
    • 输出窗口的框架遵循大胆的风格。他们扔掉了第一个低频垃圾箱,把所有东西都切到-90db以下。

    enter image description here

    基本上就是频率仓中的能量。

    你需要选择一些参考点。以分贝为单位的图表总是与某件事有关的。当您选择最大能量箱作为参考时,您的0db点是最大能量(显然)。可接受设置为最大振幅正弦波的参考能量。参见reflog10是10而不是20,这是因为我们要处理的是信号的功率而不是振幅。

    是的,任何低于-40dB的信号通常被认为是不可忽视的。