拙网论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 229|回复: 0

FFT to spectrum in decibel

[复制链接]

949

主题

1001

帖子

3736

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3736
发表于 2019-1-9 21:08:22 | 显示全部楼层 |阅读模式
https://dsp.stackexchange.com/questions/32076/fft-to-spectrum-in-decibel

Here is a 10 seconds-long 440hz sine wave normalized at <span class="MathJax" id="MathJax-Element-1-Frame" tabindex="0" data-mathml="0&#xA0;dBFS" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">0 dBFS0 dBFS.
When computing the STFT (with the code below) of this audio file, I noticed that max(abs(STFT)) is around 248.33. (more generally, it seems to be approximately fftsize/4 for this particular file).
Even after doing magdB = 20 * math.log10(abs(STFT)) to have decibel values, we get a max of 47.9 dB.
But this <span class="MathJax" id="MathJax-Element-2-Frame" tabindex="0" data-mathml="47.9&#xA0;dB" role="presentation" style="border: 0px; font-style: normal; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">47.9 dB47.9 dB doesn't mean anything! We would like to have ~ <span class="MathJax" id="MathJax-Element-3-Frame" tabindex="0" data-mathml="0&#xA0;dB" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">0 dB0 dB instead because this audio only contains 1 sine wave component (1 "harmonic") at <span class="MathJax" id="MathJax-Element-4-Frame" tabindex="0" data-mathml="0&#xA0;dB" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">0 dB0 dB volume.
Audio tools display it properly, here is the spectrum display of this sine wave:
  • Question: how to have an absolute, canonical <span class="MathJax" id="MathJax-Element-5-Frame" tabindex="0" data-mathml="dB" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">dBdB values from a FFT, that makes that a pure <span class="MathJax" id="MathJax-Element-6-Frame" tabindex="0" data-mathml="0&#xA0;dBFS" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">0 dBFS0 dBFS sinewave has a peak of <span class="MathJax" id="MathJax-Element-7-Frame" tabindex="0" data-mathml="0&#xA0;dB" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">0 dB0 dB in the spectrum display ?
  • More generally, is there a canonical way to go from FFT values to <span class="MathJax" id="MathJax-Element-8-Frame" tabindex="0" data-mathml="dB" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">dBdB in order to display a spectrum analysis?

NOTE: I know that <span class="MathJax" id="MathJax-Element-9-Frame" tabindex="0" data-mathml="dB" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">dBdB are for ratio between 2 things, so all I mentioned here is referred to <span class="MathJax" id="MathJax-Element-10-Frame" tabindex="0" data-mathml="dBFS" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">dBFSdBFS(full-scale)

import scipy, numpy as npimport scipy.io.wavfile as wavfiledef stft(x, fftsize=1024, overlap=4):     hop = fftsize / overlap    w = scipy.hanning(fftsize)    return np.array([np.fft.rfft(w*x[i:i+fftsize]) for i in range(0, len(x)-fftsize, hop)])
[color=rgb(187, 192, 196) !important][color=rgb(106, 115, 124) !important][size=1.61538]6
[color=rgb(69, 161, 99) !important]



Definitely you will have to calibrate your system. You need to know what is the relationship between dBFS (Decibel Full-Scale) and dB scale you want to measure.
In case of digital microphones, you will find sensitivity given in dBFS. This corresponds to dBFS level, given 94 dB SPL (Sound Pressure Level). For example this microphone for input <span class="MathJax" id="MathJax-Element-12-Frame" tabindex="0" data-mathml="94dBSPL" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">94dBSPL94dBSPL will produce signal at <span class="MathJax" id="MathJax-Element-13-Frame" tabindex="0" data-mathml="&#x2212;26dBFS" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">−26dBFS−26dBFS. Therefore the calibration factor for spectrum will be equal to <span class="MathJax" id="MathJax-Element-14-Frame" tabindex="0" data-mathml="94+26=120dB" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">94+26=120dB94+26=120dB.
Secondly, keep in mind scaling of the spectrum while doing windowing and DFT itself. More specifically, given amplitude spectrum (abs(sp)):
  • Total energy of the signal is spread over frequencies below and above Nyquist frequency. Naturally we are interested only in half of the spectrum. That is why, it important to multiply it by 2 and ignore everything above the Nyquist frequency.
  • Whenever doing windowing, it is necessary to compensate for loss of energy due to multiplication by that window. This is defined as division by sum of window samples (sum(win)). In case of rectangular window (or now window), it is as simple as division by N, where N is the DFT length.


Here is some example source code in Python. I am sure that you can take it from here.
#!/usr/bin/env pythonimport numpy as npimport matplotlib.pyplot as pltimport scipy.io.wavfile as wfplt.close('all')def dbfft(x, fs, win=None, ref=32768):    """    Calculate spectrum in dB scale    Args:        x: input signal        fs: sampling frequency        win: vector containing window samples (same length as x).             If not provided, then rectangular window is used by default.        ref: reference value used for dBFS scale. 32768 for int16 and 1 for float    Returns:        freq: frequency vector        s_db: spectrum in dB scale    """    N = len(x)  # Length of input sequence    if win is None:        win = np.ones(1, N)    if len(x) != len(win):            raise ValueError('Signal and window must be of the same length')    x = x * win    # Calculate real FFT and frequency vector    sp = np.fft.rfft(x)    freq = np.arange((N / 2) + 1) / (float(N) / fs)    # Scale the magnitude of FFT by window and factor of 2,    # because we are using half of FFT spectrum.    s_mag = np.abs(sp) * 2 / np.sum(win)    # Convert to dBFS    s_dbfs = 20 * np.log10(s_mag/ref)    return freq, s_dbfsdef main():    # Load the file    fs, signal = wf.read('Sine_440hz_0dB_10seconds_44.1khz_16bit_mono.wav')    # Take slice    N = 8192    win = np.hamming(N)    freq, s_dbfs = dbfft(signal[0:N], fs, win)    # Scale from dBFS to dB    K = 120    s_db = s_dbfs + K    plt.plot(freq, s_db)    plt.grid(True)    plt.xlabel('Frequency [Hz]')    plt.ylabel('Amplitude [dB]')    plt.show()if __name__ == "__main__":    main()
Lastly, there is no better source on this type of spectrum scaling than brilliant publication by G. Heinzel et al. Just keep in mind that if you want to proper RMS scaling, then full-scale sinosuidal signal will be <span class="MathJax" id="MathJax-Element-15-Frame" tabindex="0" data-mathml="&#x2212;3dBFS" role="presentation" style="border: 0px; font-variant: inherit; font-stretch: inherit; line-height: normal; font-family: inherit; vertical-align: baseline; box-sizing: inherit; display: inline; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; position: relative;">−3dBFS−3dBFS.
You might also find my previous answer being helpful.




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|抱朴守拙BBS

GMT+8, 2025-5-26 02:55 , Processed in 0.193434 second(s), 18 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表