FFTによる声紋分析に操作ボタンを付けてみたのがこれ!
これは声紋表示を停止した状態です。表示再開ボタンを押せば再度動的に表示します。停止中はpngファイル出力ボタンにより画像を出力することができます。
PyQtGraphはグラフ表示だけてなくTkinterのようなウィジェットを扱うことができます。つまりPyQtGraphだけで画像アプリができてしまいます。
ソースです。
import sys
import numpy as np
from PyQt5 import QtWidgets
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
#音声関係のライブラリ
import pyaudio
import pyqtgraph.exporters
class DrawFFT:
def __init__(self):
self.FFT_LEN = 1024
self.RATE= 44100 #サンプリング周波数
# 表示の停止
self.stop = False
# スペクトル表示バッファ
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)
prox = QtGui.QGraphicsProxyWidget()
layout = pg.LayoutWidget()
self.btnStop = QtGui.QPushButton("停止")
self.btnStop.clicked.connect(self.clicked)
self.btnPng = QtGui.QPushButton("pngファイル出力")
self.btnPng.clicked.connect(self.outPng)
self.btnPng.setEnabled(False)
layout.addWidget(self.btnStop, row=0, col=0)
layout.addWidget(self.btnPng, row=0, col=1)
prox.setWidget(layout)
self.window.nextRow()
self.window.addItem(prox)
# ウィンドウ表示
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 clicked(self):
if self.stop == False:
self.stop = True
self.btnStop.setText("表示再開")
self.btnPng.setEnabled(True)
else:
self.stop = False
self.btnStop.setText("停止")
self.btnPng.setEnabled(False)
def outPng(self):
# pngファイルにグラフを出力
# exportersの直前に pg.QtGui.QApplication.processEvents() を呼ぶ!
pg.QtGui.QApplication.processEvents()
exporter = pg.exporters.ImageExporter(self.window.scene())
exporter.export("sono_graph.png")
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.stop == True:
return
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_()
PyQtGraphのウィジェットは情報が少なく時間がかかります。
先ずはボタンの生成と配置 ボタンを1つだけならもっと簡単にできたのですが、同じ行に2個のボタンを作りました。
self.btnStop = QtGui.QPushButton("停止")
self.btnStop.clicked.connect(self.clicked)
self.btnPng = QtGui.QPushButton("pngファイル出力")
self.btnPng.clicked.connect(self.outPng)
self.btnPng.setEnabled(False)
ボタンはQtGui.QPushButtonオブジェクトで実装します。
clicked.connect()でイベントハンドラーをセットします。 ボタンの使用不可をsetEnabled()でセットします。
layout.addWidget(self.btnStop, row=0, col=0)
layout.addWidget(self.btnPng, row=0, col=1)
次に2個のボタンを layout.addWidget()でセットします。
prox.setWidget(layout)
self.window.nextRow()
self.window.addItem(prox)
layoutをproxにセットします。 最後にFFT表示の下の行に進めて、self.windowのアイテム追加をします。
ボタンの操作
ボタンの名称変更: setText("表示再開")
ボタンの使用不可: setEnabled(False)
|