FFTによるスペクトルアナライザ
FFT(高速フーリエ変換)は結構時間のかかる処理だったのですが、numpyとPyQtGraphを使うと高速に動作します。 Matplotlibでやった人がいましったが、動作が遅いようです。


Audacityで2000Hzの正弦波をスピーカから出力し、マイクで入力しました。 ちょうど2000Hzのところにピークがでています。
ソースです。

# -*- coding:utf-8 -*-

#pyqtgraph関係のライブラリ
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import sys

#音声関係のライブラリ
import pyaudio


class DrawFFT:
    def __init__(self):
        #プロット初期設定
        self.win=pg.GraphicsWindow(size=(500, 450), show=True)
        self.win.setWindowTitle(u"スペクトラムアナライザ")
        self.plt=self.win.addPlot() #プロットのビジュアル関係
        self.plt.setYRange(0,10)    #y軸の上限、下限の設定
        self.curve=self.plt.plot()  #プロットデータを入れる場所
        self.received = False

        #マイクインプット設定
        self.CHUNK=1024  #1回の受信で読み取るデータ量
        self.RATE=22050  #サンプリング周波数
        self.audio=pyaudio.PyAudio()
        self.stream=self.audio.open(format=pyaudio.paInt16,
                                    channels=1,
                                    rate=self.RATE,
                                    input=True,
                                    frames_per_buffer=self.CHUNK,
                                    stream_callback=self.callback)

        self.stream.start_stream()

        #アップデート時間設定
        self.timer=QtCore.QTimer()
        self.timer.timeout.connect(self.update)
        self.timer.start(10)

        #音声データの格納場所(プロットデータ)
        self.data=np.zeros(self.CHUNK)
        # x軸
        wave_x = np.linspace(0, self.RATE, self.CHUNK)
        self.chunk2 = int(self.CHUNK/2)
        self.wave_x2 = wave_x[0:self.chunk2]
        self.win_hamming = np.hamming(self.CHUNK)

    
    # デストラクタ
    def __del__(self):
       print('terminated.')
       self.stream.stop_stream()
       self.stream.close()
       self.audio.terminate()

    def callback(self, in_data, frame_count, time_info, status):
        self.data=np.frombuffer(in_data, dtype="int16")/32768.0
        self.received = True
        return (in_data,pyaudio.paContinue)

    def update(self):
        if self.received == True:
            # FFT
            wave_y = np.fft.fft(self.data * self.win_hamming)
            wave_y = np.abs(wave_y)
            wave_y2 = wave_y[0:self.chunk2]
            self.curve.setData(self.wave_x2, wave_y2)   #プロットデータを格納
            self.received = False


if __name__=="__main__":
    plotwin=DrawFFT()
    if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
クラスを使ってコーディングしました。
クラスを使えばデストラクタによりオブジェクトのクローズな後始末をまとめてできます。

    # デストラクタ
    def __del__(self):
       print('terminated.')
       self.stream.stop_stream()
       self.stream.close()
self.audio.terminate()
また 、PyAudioではデータの取り込み完了で呼び出されるコールバック関数を使うと無駄のない処理ができます。

FFTの実装は、タイマーのところで処理しています。



    def update(self):
        if self.received == True:
            # FFT
            wave_y = np.fft.fft(self.data * self.win_hamming)
            wave_y = np.abs(wave_y)
            wave_y2 = wave_y[0:self.chunk2]
            self.curve.setData(self.wave_x2, wave_y2)   #プロットデータを格納
            self.received = False
FFTの処理
wave_y = np.fft.fft(self.data * self.win_hamming)
実測データにハミング窓を掛けて演算します。

wave_y2 = wave_y[0:self.chunk2]
はFFTをした半分を使っています。(サンプリング定理)