それでは毛玉諸君、これにて失敬

日々の精進を備忘録的に綴ります。

pythonでスペクトルアナライザを自作する

夏バテで酸っぱいものばかり食べてます。ko_ya346です。
最近はもずく酢がお気に入り。

f:id:ko_ya346:20200815193835p:plain

再生中の楽曲に合わせてスペクトルがうねうねするあれ。
フーリエ変換の勉強の息抜きにpythonで作ってみました。
コードは下の参考記事そのまんまです。

yukara-13.hatenablog.com

aidiary.hatenablog.com

import wave
import numpy as np
import pyaudio

import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore

def spectrumAnalyzer(filename):
    chunk = 1024
    signal_scale = 1

    #wavファイル読み込み
    wf = wave.open(filename, "r")
    fs = wf.getframerate()
    #ハミング窓設定(なぜかchunkを2倍にしないとエラーが出た)
    hammingWindow = np.hamming(chunk*2)

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

    # ========
    #  Layout
    # ========

    ### アプリケーション作成
    app = QtGui.QApplication([])
    app.quitOnLastWindowClosed()
    ### メインウィンドウ
    mainWindow = QtGui.QMainWindow()
    mainWindow.setWindowTitle("Spectrum Analyzer") # Title
    mainWindow.resize(800, 300) # Size
    ### キャンパス
    centralWid = QtGui.QWidget()
    mainWindow.setCentralWidget(centralWid)
    ### レイアウト!!
    lay = QtGui.QVBoxLayout()
    centralWid.setLayout(lay)
    
    ### スペクトル表示用ウィジット
    specWid = pg.PlotWidget(name="spectrum")
    specItem = specWid.getPlotItem()
    specItem.setMouseEnabled(y=True) # y軸方向に動かせなくする
    specItem.setYRange(0, 200)
    specItem.setXRange(0, chunk/2, padding=0)
    ### Axis
    specAxis = specItem.getAxis("bottom")
    specAxis.setLabel("Frequency [Hz]")
    specAxis.setScale(fs/2./(chunk/2+1))
    hz_interval = 1000
    newXAxis = (np.arange(int(fs/2/hz_interval))+1) * hz_interval
    oriXAxis = newXAxis/(fs/2./(chunk/2+1))
    specAxis.setTicks([zip(oriXAxis, newXAxis)])
    ### キャンパスにのせる
    lay.addWidget(specWid)
    
    ### ウィンドウ表示
    mainWindow.show()
    
    #chunk単位でサンプリング
    x = wf.readframes(chunk)

    while True:
        #再生
        stream.write(x)
        data = np.frombuffer(x, dtype="int16")/32768.0
        if len(data) < chunk*2:
            exit()
        #切り出した波形データ
        wind_data = hammingWindow*data
        #FFT変換
        fftspec = np.fft.fft(wind_data)

        #グラフ出力
        specItem.plot(abs(fftspec*signal_scale), clear=True)
        QtGui.QApplication.processEvents()
        #次のchunkを読み込む
        x = wf.readframes(chunk)

    stream.close()
    p.terminate()

この関数にwavファイルを渡すと実行できます。
pyaudiopython 3.7以降では使用出来ない?ようなのでご注意ください。知らなかったので環境構築時にかなり時間を溶かしました。

while内では
・wavファイルをchunk単位で呼び出す
・データを再生用の関数に渡す
・データを窓関数を通してFFT変換
・スペクトルを出力
の4つの仕事をしています。
numpyの機能を呼び出すだけなのでむっちゃ楽ですね。
とりあえず動いたんですが、肝心のFFTはイマイチ理解出来てないのでもう少し勉強します…
下の資料がとても分かりやすい。
www.ic.is.tohoku.ac.jp あとオススメの参考書があれば是非教えてください。