Pythonにおけるリアルタイム音楽信号処理アプリのデモ/教材として,音の広がりや位相を見るフェーズスコープをPyQtGraphとPyAudioで実装してみました.
起動すると,PC上で音楽を再生しながらフェーズスコープを描画することができます.
全ソースコードは以下にあります.
少し複雑なソースコードなので,この記事ではソースコード理解の助けとなる実装方法の要点とノウハウを書きたいと思います.
実装方法の要点・ノウハウ
Pythonにおけるリアルタイム音楽再生・分析方法
Pythonでリアルタイムに音楽信号を再生・分析する方法としては,以下の3つが考えられます.
- Pythonで音楽再生部(音楽プレーヤー)を実装
- PCに接続したマイクで収音した音を入力信号とする
- ループバック機能を利用し,PC上の音を入力信号とする
今回はPC上で再生している音を可視化ので,3 の方針で実装しました.
3 を実現するためのモジュールとしては PyAudio もしくは pysoundcard がありますが,今回は PyAudio を使いました.
ループバック機能を利用した,PC上の音のリアルタイム取得
ループバックには,VB-Audio社の仮想ミキサ "Voice Meeter" を使いました.
上記の記事でPyAudioを使って実装したAudioInputStream
クラスを利用しています.
データのプロット方法
今回は滑らかな描画がしたいので,30fps以上の更新が必要と考え,PyQtGraphを採用しました.
PyQtGraph にはたくさんのプロットの種類がありますが,今回は比較的多くのデータ点でもリアルタイムでプロットできる実装方法を採用しています.
詳しくは以下の記事をご覧ください.
フェーズスコープの計算アルゴリズム
ステレオ信号のL,Rチャネルの信号に対して,サンプルごとにデータをプロットするような実装となっています.
具体的な計算方法は以下の記事をご参照ください.
実行方法
実行スクリプトは以下のようになっています.
# -*- coding: utf-8 -*- from rasp_audio_stream import AudioInputStream from pqg_phasescope import PQGPhaseScope # PyAudioストリーム入力取得クラス ais = AudioInputStream(CHUNK=1024) #, input_device_keyword="Real") # フェイズスコープ用クラス phasescope = PQGPhaseScope( (ais.CHANNELS, ais.CHUNK) ) # AudioInputStreamは別スレッドで動かす import threading thread = threading.Thread(target=ais.run, args=(phasescope.callback_sigproc,)) thread.daemon=True thread.start() # フェイズスコープ起動 phasescope.run_app()
各機能の実行順序ですが,PQGPhaseScope
クラス,すなわちPyQtGraphのGUIアプリ起動を最後にしてください.
なお,GUIアプリをメインスレッドとするため,threadding
を使って入力信号取得は別スレッドで行っています.
フェーズスコープ描画のノウハウ
- 振幅値 (
rad
) の生の値は強弱が激しすぎる- ⇒ 平方根(0.5乗)とかで鈍らせる
- 振幅値が小さいときに点が重なって色が濃く見える
- ⇒ 振幅値が小さいときは透明度
alpha
を下げる
- ⇒ 振幅値が小さいときは透明度
- 位相差0度の時にとなるので分かりにくい
- ⇒ となるように45度(0.25π)回転する(見やすくするため)
pqg_phasescope.py
def update(self): ... # 値をいい感じに調整 rad **= 0.5 phase += 0.25 * np.pi alpha = np.mean(rad)**2 alpha_max = 0.2 alpha = alpha_max if alpha_max > 0.2 else alpha ...
動作テスト
Audacityを使って,位相やLRの音量バランスが異なるサイン波を手動で制御・出力し,正しく動作することを目検で確認しました.