pyqtgraph+PyAudioによるリアルタイムで音声プロット
通常pythonでのプロットと言ったらmatplotlibですがプロッティングがとても遅い欠点があります。その分使いやすくてキレイなんですけどね。
リアルタイムでプロットしようとしたらメモリは喰うし遅いしで最悪(工夫次第ではそうでもないらしいですがその工夫がめんどくさい)ですので、高速なプロッティングが出来るとされるpyqtgraphを使ってリアルタイムで音声をプロッティングしました。
pyqtgraphは
・PyQt(Qtだけかも?)
・numpy
に依存してますのでそれだけ入れてしまえばオッケーです。
また今回は音声のプロットですので音声関係のライブラリを入れておく必要があります。
今回用いたのはPyAudioです。
# -*- coding:utf-8 -*- #プロット関係のライブラリ import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui import numpy as np import sys #音声関係のライブラリ import pyaudio import struct class PlotWindow: def __init__(self): #プロット初期設定 self.win=pg.GraphicsWindow() self.win.setWindowTitle(u"リアルタイムプロット") self.plt=self.win.addPlot() #プロットのビジュアル関係 self.plt.setYRange(-1,1) #y軸の上限、下限の設定 self.curve=self.plt.plot() #プロットデータを入れる場所 #マイクインプット設定 self.CHUNK=1024 #1度に読み取る音声のデータ幅 self.RATE=44100 #サンプリング周波数 self.audio=pyaudio.PyAudio() self.stream=self.audio.open(format=pyaudio.paInt16, channels=1, rate=self.RATE, input=True, frames_per_buffer=self.CHUNK) #アップデート時間設定 self.timer=QtCore.QTimer() self.timer.timeout.connect(self.update) self.timer.start(10) #10msごとにupdateを呼び出し #音声データの格納場所(プロットデータ) self.data=np.zeros(self.CHUNK) def update(self): self.data=self.AudioInput() self.curve.setData(self.data) #プロットデータを格納 def AudioInput(self): ret=self.stream.read(self.CHUNK) #音声の読み取り(バイナリ) #バイナリ → 数値(int16)に変換 #32768.0=2^16で割ってるのは正規化(絶対値を1以下にすること) ret=np.frombuffer(ret, dtype="int16")/32768.0 return ret if __name__=="__main__": plotwin=PlotWindow() if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_()
これを実行すると以下の様な画像になります。
(ごめんなさい、本当は動画を上げたかったのですが動画の上げ方がわからずこのような形になってしまいました。上げ方が分かったら追記で動画を載せます。)
データの入出力あたりはPlotWindowクラスのupdateメソッドとAudioInputメソッドでコントロールしてますので
このあたりをテキトーにいじくってあげたり、FFTしてあげたりすればスペクトルアナライザーにもなります。
self.timer.start(10)を50にしてあげたりすればプロットスピードが落ちたりします。
pyqtgraphのリファレンスがちょっと充実度にかけるのでご参考になれば幸いです。
また、PyAudioを利用されたことのある方にお叱りを受けるかもしれませんが、今回streamなどを明示的に終了させてません。
ちゃんと動いて止まっておかしいことはなかったので大丈夫っちゃ大丈夫かな程度のテキトーなコードです。
ちゃんと明示的に止めるのであれば、PlotWindowクラスのpg.GraphicsWIndowがQtからの継承ですので、ウィンドウを閉じる時におそらくcloseEventを発するはずですので、オーバーライドしてstreamを止めればいいのでは。
ですので上記のコードはかなりテキトーです。