PythonのPyAudioで音声録音する簡単な方法

  • このエントリーをはてなブックマークに追加

Pythonを使ってPCのマイク入力から音声を入力するプログラムはPyAudioを使って簡単に書くことができます。ここでは実際の音をデジタルデータへ変換する際の注意点を踏まえてサンプルコードを示します。

こんにちは。wat(@watlablog)です。
信号処理プログラマを目指すとしたら簡単に録音くらい朝飯前になりたいですね。ここではPyAudioを使って音声信号を取得します

Pythonプログラミングの基本文法や問題解決には体系的な学習プログラムが効果的です。手っ取り早く基礎を覚えるために僕は「PyQ(パイキュー)」に登録してみました。気になる方は「PyQでPython学習!実際に登録してみた感想と気になる料金」という記事を参照下さい!

PyAudioインストールの注意点

Windows版のインストールについて

筆者の環境はPython3.7を使っていますが、2019年6月現在はインストール時にエラーが発生するようです。詳細はPython3.7でPyAudioがインストールできない時の解決法の記事を確認して頂ければと思います。

Python3.9や3.11では特に問題ないようです。

Mac版のインストールについて

このページはWindowsPCで主に書きましたが、Macでも動きます。筆者のMacによる環境は「macOSにPython3をインストールする方法をまとめてみた」で構築した通りですが、MacでPyAudioを使う場合、ターミナルで、

brew install portaudio

…を実行した後に、

pip3 install pyaudio

…で問題無くインストール可能でした。

録音に使用する機材

これからPythonプログラムを使って録音のプログラムを書きますが、不幸な事に僕のPCに初期から付いていた内蔵マイクが壊れてしまっていたので、このページではUSBマイクを使って例を紹介します。

USBマイクであっても、プログラム自体は何も変化することはありません!それでは、早速コードを書いていきましょう!

Python/PyAudioで録音するコード

今回は「習うより慣れろ!」ということで、コードを書きながら内容を説明していきたいと思います。

インポートするパッケージ

今回使うライブラリパッケージは、音声録音をするためのpyaudio, 配列処理のnumpy, グラフ表示のmatplotlibです。

設定する値

計測のための設定は以下のコードです。

計測時間は全体で何秒録音するか、サンプリングレートは1秒間に収集するデータの個数です。

データは1つずつ収集するのではなく、フレーム単位で収集します。そのためそのフレームのサイズを指定します(この値は2のべき乗(512, 1024, 2048...等))。

マイクのチャンネル指標とは、PCの何番目のチャンネルが録音用のデバイスかを指定するものです。この番号はオーディオインデックスの番号と呼び、「Python/PyAudioでマイクのチャンネルを確認する方法!」で記載した方法で調べることができます。

録音の関数

それではここでメインの録音関数を説明します。

以下のコードが録音関数です。

ストリームとは、ストリーミング動画等に代表されるように「流れ」を意味する単語ですが、PCのサウンドデバイスが準備開始する、というイメージで構わないと思います。

「format=pyaudio.paInt16」は16bitの量子化ビット数でデータを収集するという意味で、後程後述します。

このサンプルコードでは1チャンネルのモノラル録音をしています。

「int(((time / dt) / fs))」の部分は、計測時間timeを指定しましたが、結局はフレーム単位で録音をするため、フレームの整数倍でデータを収集するためのループ回数計算が必要なため、そのループ回数を計算しています。

dataはリスト型で、さらに「b'\xfd\xff\xfa…'」といったデータで作られていますが、リストの中身の、さらにb''の中身の文字列を連結させるために「b''''.join(data)」と.join関数を使います。

「np.frombuffer」部分では、データをNumpy配列に変換し、数値として扱えるように変換するものですが、これがちょっとわかりにくいかも知れませんので、図解を作りました。

但し、今回僕がこのコードを作るために勉強して理解した内容であるため、もしかしたら間違いがあるかも知れません。ご利用には細心の注意をお願いします。

以下の図はPyaudioで録音したデータ構造のイメージを示しています。

量子化データ構造

コンピュータはアナログな連続した値を扱えないので、図中に描いたようなマス目に録音したデータを格納していくことをやっています。

横軸はデータサンプル数で、フレームサイズ単位でループ数に応じて増えていくマスです。

一方縦軸はデータの物理値の量を示す軸ですが、コンピュータは音を測定するからといって音圧[Pa]を直接計測することができません。

本ページでは16bitの量子化ビット数で録音していると先に書きましたが、このビット数によって縦軸のマスの個数が決まると認識しています。

量子化についてもっと詳しく

コンピュータは連続した値を扱えず、ビット数に応じたマスにデータを格納して行きます。

今回は縦軸が16bitなので、表現できる値の数は\(2^{16}\)です。

\(2^{16}\)は65536個のマスを用意できることを意味しますが、音圧のデータは正の値もあれば負の値もあり、さらには0も表現する必要があります。

正負両方の値を表現するためには割る2をしなければいけません。\(2^{16}\)の半分は65536/2=32768となりますが、0をとる必要があるので、32768-1=32767が最大値と最小値になります。

そのため上図は0を中心として±32767の範囲にデータが入ることになります。

ここまでが、「/ float((np.power(2, 16) / 2) - 1) 」としている理由になります。

関数の実行とグラフプロット

ここまでできたら、あとは関数を実行し、グラフにプロットするだけです。

以下のコードのwfmが波形(Waveform)を格納する変数で、先ほど作った関数を実行する部分です。

グラフの横軸tを計算するために、関数内のforループ回数iも取得しています。

実行結果

以下の図が実行結果になります。

これはマイクをテーブルに7回叩きつけた時の音です。綺麗な音圧の減衰自由振動が観測できていますね。

実行結果

おまけ:マイクチャンネルを自動で取得して録音するPythonコード【コピペでそのまま動作可】

こちらはおまけです。コピペでそのまま動作するようにしてみました。また、所々関数化してコンパクトにしたり、マイクチャンネルを自動取得するコードも追記しています。

詳細は「Python/PyAudioでマイクのチャンネルを確認する方法!」に自動でマイクチャンネルを取得するコードを追記しましたので確認ください。これにより手動でマイクを選択する必要がなくなります。

新たにget_mic_index()関数を作成し、index = get_mic_index()[0]で最初のマイクチャンネルを選択しています。もしマイクチャンネルが複数ある場合はユーザーに選択させる処理を別途追加すると良いかもしれません。

さらに、音声をスケーリングする方法も検討してみました。以下のコードを上記全コードに組み込み、dataを変換すれば波形の絶対値の最大値を使ってスケーリングすることが可能です。これはwavファイルにするとき等に音が小さくならないようにすることができます。

また、フーリエ変換やwav保存まで一度にできるコードは以下の記事に記載しました。是非ご参考ください。

現場でPC1つ!簡単に録音・FFT・wav保存するPythonコード

まとめ

本ページではPyAudioを使って音声を取得するコードを紹介しました。

ただサンプルコードを紹介するだけでは(僕自身が)理解に苦しんだので、量子化ビット数やデータ構造等の勉強結果も図にしてみました。

以外と録音には専門用語が沢山あって慣れない操作が多かったけど、なんとかデータを自分のPCで収集することができたぞ!

Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

コメント

コメントを残す

*