簡體   English   中英

從PyAudio接收的數據的FFT給出了錯誤的頻率

[英]FFT of data received from PyAudio gives wrong frequency

我的主要任務是實時識別人從麥克風發出的嗡嗡聲。 一般來說,作為識別信號的第一步,我記錄了從手機上的應用程序生成的440 Hz信號的5秒鍾,並嘗試檢測相同的頻率。

我使用Audacity從同一440Hz wav文件繪制並驗證了頻譜,我得到了這一結果,這表明440Hz確實是主要頻率:( https://i.imgur.com/2UImEkR.png

為此,我使用PyAudio庫並參考此博客 到目前為止,我使用wav文件運行的代碼是:

"""PyAudio Example: Play a WAVE file."""

import pyaudio
import wave
import sys
import struct
import numpy as np
import matplotlib.pyplot as plt

CHUNK = 1024

if len(sys.argv) < 2:
    print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
    sys.exit(-1)

wf = wave.open(sys.argv[1], 'rb')

p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True)

data = wf.readframes(CHUNK)

i = 0
while data != '':
    i += 1
    data_unpacked = struct.unpack('{n}h'.format(n= len(data)/2 ), data) 
    data_np = np.array(data_unpacked) 
    data_fft = np.fft.fft(data_np)
    data_freq = np.abs(data_fft)/len(data_fft) # Dividing by length to normalize the amplitude as per https://www.mathworks.com/matlabcentral/answers/162846-amplitude-of-signal-after-fft-operation
    print("Chunk: {} max_freq: {}".format(i,np.argmax(data_freq)))

    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)
    ax.plot(data_freq)
    ax.set_xscale('log')
    plt.show()

    stream.write(data)
    data = wf.readframes(CHUNK)

stream.stop_stream()
stream.close()

p.terminate()

在輸出中,我得到所有塊的最大頻率為10,其中一個圖的示例為:( https://i.imgur.com/zsAXME5.png

我曾預計所有塊的該值為440,而不是10。 我承認我對FFT的理論了解甚少,也感謝我為解決這個問題提供的幫助。

編輯:采樣率為44100。 的通道數是2,樣本寬度也是2。

前言

正如xdurch0指出的,您正在讀取一種索引而不是頻率。 如果要自己進行所有計算,則在繪制圖形之前,如果要獲得一致的結果,則需要計算自己的頻率向量。 閱讀此答案可能有助於您找到解決方案。

FFT(半平面)的頻率向量為:

 f = np.linspace(0, rate/2, N_fft/2)

或(全平面):

 f = np.linspace(-rate/2, rate/2, N_fft)

另一方面,我們可以將大部分工作委托給出色的scipy.signal工具箱,該工具箱旨在解決此類問題(以及更多問題)。

MCVE

使用scipy包,可以直接為單個頻率( )的簡單WAV文件獲得所需的結果:

import numpy as np
from scipy import signal
from scipy.io import wavfile
import matplotlib.pyplot as plt

# Read the file (rate and data):
rate, data = wavfile.read('tone.wav') # See source

# Compute PSD:
f, P = signal.periodogram(data, rate) # Frequencies and PSD

# Display PSD:
fig, axe = plt.subplots()
axe.semilogy(f, P)
axe.set_xlim([0,500])
axe.set_ylim([1e-8, 1e10])
axe.set_xlabel(r'Frequency, $\nu$ $[\mathrm{Hz}]$')
axe.set_ylabel(r'PSD, $P$ $[\mathrm{AU^2Hz}^{-1}]$')
axe.set_title('Periodogram')
axe.grid(which='both')

基本上:

輸出:

在此處輸入圖片說明

尋找高峰

然后,我們可以使用find_peaks找到第一個最高峰值的頻率( P>1e-2 ,此准則有待調整):

idx = signal.find_peaks(P, height=1e-2)[0][0]
f[idx] # 440.0 Hz

將所有內容放在一起僅歸結為:

def freq(filename, setup={'height': 1e-2}):
    rate, data = wavfile.read(filename)
    f, P = signal.periodogram(data, rate)
    return f[signal.find_peaks(P, **setup)[0][0]]

處理多個渠道

我用wav文件嘗試了此代碼,並得到以下行axe.semilogy(f,Pxx_den)的錯誤:ValueError:x和y必須具有相同的第一維。 我檢查了形狀,並且f具有(2,),而Pxx_den具有(220160,2)。 同樣,Pxx_den數組似乎只有全零。

Wav文件可以容納多個通道,主要是單聲道或立體聲文件(最多2**16 - 1通道)。 您強調的問題是由於多個通道文件( 立體聲樣本 )而發生的。

rate, data = wavfile.read('aaaah.wav') # Shape: (46447, 2), Rate: 48 kHz

在此處輸入圖片說明

它沒有很好的文檔記錄 ,但是signal.periodogram方法也對矩陣執行,並且其輸入與wavfile.read輸出並不直接一致(默認情況下,它們在不同的軸上執行)。 因此,在執行PSD時,我們需要仔細調整尺寸方向(使用axis開關):

f, P = signal.periodogram(data, rate, axis=0, detrend='linear')

它也適用於Transposition data.T但隨后我們需要對結果進行反向轉置。

指定軸可解決以下問題:頻率矢量正確且PSD並非在所有位置都為空(在長度為2axis=1上執行之前,在您的情況下,它對2個樣本的信號執行了220160 PSD,我們希望進行相反的處理)。

detrend開關確保該信號具有零平均值和其線性趨勢被去除。

實際應用

如果大塊中包含足夠的數據,則該方法應適用於實際的大塊樣本(請參閱Nyquist-Shannon采樣定理 )。 然后,數據是信號的子樣本(塊),並且速率保持恆定,因為在此過程中它不會改變。

擁有大小為2**10塊似乎可行,我們可以從中識別出特定的頻率:

f, P = signal.periodogram(data[:2**10,:], rate, axis=0, detrend='linear') # Shapes: (513,) (513, 2)
idx0 = signal.find_peaks(P[:,0], threshold=0.01, distance=50)[0] # Peaks: [46.875, 2625., 13312.5, 16921.875] Hz

fig, axe = plt.subplots(2, 1, sharex=True, sharey=True)
axe[0].loglog(f, P[:,0])
axe[0].loglog(f[idx0], P[idx0,0], '.')
# [...]

在此處輸入圖片說明

在這一點上,最棘手的部分是微調“ find-peaks方法以捕獲所需的頻率。 您可能需要考慮對信號進行預濾波或對PSD進行后處理,以便於識別。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM