はじめに
「音」の勉強を始めたビギナーにとっては、
そもそも音響関係のPython モジュールを使ったプログラミングの経験も少ないだろうと思われる。
本記事はそのようなビギナー向けにプログラムの実例をひとつ提供するものである。
さて以前、こんな記事を書いたことがあった。
tam5917.hatenablog.com
記事の中で、発話区間 検出(Voice Activity Detection)をしながら音声認識 をかけるスクリプト を紹介している。
実行イメージを下図に示す。音声の短時間パワーおよび認識された発話内容が表示されている。
図1:asr_streaming_vad.pyの実行例
音声入力を活用したアプリケーションの実装を考えた場合、音声認識 機能をひとまず除外し、パワー(音量)だけではなく声の高さ(基本周波数)もモニタリングできたほうが色々と捗るのでは、と考えた。
そこで本記事ではそのようなデモンストレーションを行うスクリプト を実装したので、紹介する。
実装
実装へのリンクを示す。音量監視機能付き音声認識 スクリプト の大元がKoji Inoue 氏のコードであるため、コピーライトを書いておいた。
主に使うモジュールはsounddeviceである。このモジュールを活用することで、マイクから音声データを取得し、自身のプログラム内で活用できる。
さて音声データから音声の短時間パワーを計算しているのはこのあたり:
audio = struct.unpack(f"{len(indata) / 2:.0f}h" , indata)
audio = np.array(audio).astype(np.float64)
rms = math.sqrt(np.square(audio).mean())
power = 20 * math.log10(rms) if rms > 0.0 else -math.inf
indata
は ストリームから取得した生のバイナリデータであり、それをstructモジュールのunpack関数を用いて short int へと変換する。
unpackした直後はtupleになっているため、numpyのarray へと変換する。パワーの計算方法については音響学・音声分析の教科書を見て欲しい。
基本周波数を計算しているのはこのあたり:
fo, _ = pyworld.dio(audio, self.rate)
nonzero_ind = np.nonzero(fo.astype(int ))[0 ]
fo = fo[nonzero_ind]
if len (fo) > 0 :
fo = fo.mean()
else :
fo = 0.0
PyWorldのdio 関数により基本周波数を計算している。基本周波数が非ゼロのフレームのみを取り出して、フレーム平均により推定している。
音声をマイクから取得し、パワーと基本周波数を表示するやり方は以下の通り。
sounddeviceの作法は少し独特だが、「入力ストリーム」から音声取得→音声分析→パワーたちを表示、をひたすら繰り返す動作となっている。
try :
print ("<収録開始>" )
mic_stream.open_stream()
with mic_stream.input_stream:
audio_generator = mic_stream.generator()
for data in audio_generator:
power, fo = mic_stream.compute_power_fo(data)
print (
" \r " + f"音声パワー {power:5.1f}[dB] " + f"基本周波数 {fo:5.1f}[Hz]" ,
end="" ,
)
continue
except KeyboardInterrupt :
print (" \n <収録終了>" )
なおプログラム実行終了時のイメージはこちら。実行中は音量と声の高さに応じて、表示が逐次変わっていく。
図2:短時間パワーと基本周波数をリアルタイムでモニタリングした様子(プログラム終了時)
今回のプログラムではCtrl-Cにより強制終了する形とした。本記事の読者ならば、さらに改良できるだろう。
おわりに
sounddeviceモジュールの活用例として、パワー(音量)と声の高さ(基本周波数)をモニタリングするPython スクリプト の実装を紹介した。件のモジュールの仕様を筆者はすべて把握しているわけではないのだが、公式のサンプルプログラムを動かしたりすることで、マイクやスピーカーからの音声入出力がリアルタイムで行われるのはやはり面白いと感じる。
sounddevice 公式の 実装例たち を見ると、リアルタイムで音声波形をグラフにプロットしたりするスクリプト が紹介されている。ぜひ試してみると良いだろう。またasyncioモジュールを活用した実装例も紹介されている。 今回の実装をasyncioモジュールによってリファクタリング することは今後の課題としたい。
おまけ
sounddeviceを用いた先人の記事たち。matplotlibのFuncAnimationと組み合わせるのが良いらしい。