FFTによる声紋分析
FFTを使ったスペクトルの強度を色をで表現しました。
これは評価のため5500Hzの正弦波を測定したグラフです。

縦軸:
 サンプリング周波数44100HzをFFT長1024でFFT処理をしました。
縦軸の512が22050Hzになります。そして5500Hzの正弦波では、縦軸の4分の1の高さになります。正しそうです。今度は肉声で試して見ます。

「科捜研の女」で見るような画像です。「あいうえお」と喋りました。
ソースです。

import sys

import numpy as np
from PyQt5 import QtWidgets
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
#音声関係のライブラリ
import pyaudio

class DrawFFT:
    def __init__(self):

        self.FFT_LEN = 1024
        self.RATE= 44100 #サンプリング周波数

        # スペクトル表示バッファ
        self.tick_max_x = 200
        self.spct = np.full((self.tick_max_x, int(self.FFT_LEN / 2)), -150 )

        self.window = pg.GraphicsLayoutWidget(size=(500, 450), show=True)
        # 画像オブジェクト作成 & 画像をセット
        self.image = pg.ImageItem(border="y")
        self.image.setImage(self.spct)

        # カラーマップ取得・セット
        cmap = pg.colormap.getFromMatplotlib("jet")
        bar = pg.ColorBarItem( cmap=cmap, values=(-150, 0))
        bar.setImageItem(self.image) 

        # 画像を格納するボックス作成 & 画像オブジェクトをセット
        self.view_box = pg.ViewBox()
        # self.view_box.setAspectLocked(lock=True)
        self.view_box.addItem(self.image)
        # プロットオブジェクト作成、view_boxをセット
        self.plot = pg.PlotItem(viewBox=self.view_box)
        # ウィンドウにplotを追加
        self.window.addItem(self.plot)
        # ウィンドウ表示
        self.window.show()

        # PCM録音設定
        #音声データの格納場所(プロットデータ)
        self.fft_data=np.zeros(self.FFT_LEN)
        self.fft_cnt = 0
        self.received = False
        self.win_hamming = np.hamming(self.FFT_LEN)
       
        # self.CHUNK=1024  #1回の受信で読み取るデータ量
        self.audio=pyaudio.PyAudio()
        self.stream=self.audio.open(format=pyaudio.paInt16,
                                    channels=1,
                                    rate=self.RATE,
                                    input=True,
                                    frames_per_buffer=self.FFT_LEN,
                                    stream_callback=self.callback)

        self.stream.start_stream()


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

    def callback(self, in_data, frame_count, time_info, status):
        self.fft_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 = np.fft.fft(self.fft_data * self.win_hamming)
            f = fft[0 : int(self.FFT_LEN / 2)]
            power_sp = 20 * np.log10(np.abs(f))
            # print(power_sp.max(), power_sp.min())
            if self.fft_cnt >= self.tick_max_x:
                temp =self.spct[1:]
                self.spct = np.insert(temp, -1, power_sp, axis=0)
            else:
                self.spct[self.fft_cnt] = power_sp
                self.fft_cnt += 1
            self.image.setImage(self.spct)
            self.received = False
     
    # デストラクタ
    def __del__(self):
       print('terminated.')
       self.stream.stop_stream()
       self.stream.close()
       self.audio.terminate()

if __name__=="__main__":
    plotwin=DrawFFT()
    if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
手こずったのがupdate()でどうやって再描画するのか?何度か試行錯誤した結果、
self.image.setImage(self.spct)
これだけでOK。
次に一番手こずったのが、表示データを移動するところです。

                temp =self.spct[1:]
                self.spct = np.insert(temp, -1, power_sp, axis=0)
	
self.spct[1]からself.spct[199]の199個のデータをtempにコピーします。
次にtempに1データ追加した結果をself.spctに代入します。
最初はnp.append()でやっていました。エラーになったりして思ったように動きません。いりいろ試した結果、np.insert()で解決しました。

numpyとPyQtGraphの組み合わせは本当に高速だと関心しきりです。