録音した音の大きさを調べる
ここでは、ラズパイに接続されているマイクを使って周囲の音を録音し、その音の大きさ(ボリューム)を調べる方法を説明します。つまり、音がしないのか(無音なのか)、大きい音がしたのか、小さい音がしたのか、を調べます。
準備
まずは、マイクをラズパイに接続して録音ができるように準備してください:
次に、Terminalで以下のコマンドを実行してください。
sudo pip3 install matplotlib
続いて、以下のコマンドを実行:
sudo pip3 install numpy --upgrade
続いて以下を実行:
sudo apt install libatlas-base-dev -y
さらに以下を実行してください:
sudo apt install python3-scipy -y
そして、soundanalysis.pyをラズパイにコピーしてください。
音の大きさ
音とは、空気の震え(振動)で、その振動は空気中を「波」として伝わります(「PyAudioを使って音を作り、スピーカーから流す」を参照)。
振動が空気中を伝わる様子を絵やグラフにすると「波」になります。音は、目に見えない波(音波)として空気中を伝わって進む(伝搬する)もののことです。その波を耳(の中の鼓膜)がキャッチすると、人は「あ、音がする」と感じます。
以下は、単純な音波をグラフにした例です。
音の大きさ(volume, intensity)は、音波の縦方向への動きで表されます。縦方向に大きく(大幅に)振れていれば大きな音で、縦方向に小さく(小幅に)振れていれば小さな音です。ですから、音波の中の縦軸方向の最大値を調べれば、その音が大きいのか小さいのかが分かります。上の例で、縦軸方向の最大値は1.0です。この値のことを「振幅(amplitude)」といいます。
音波から音の大きさ(振幅)を調べる
音の大きさ(音波の振幅)を調べるプログラムがsound-intensity.pyです。先ほどコピーしたsoundanalysis.pyと同じフォルダに置いてください。
このプログラムは、同じフォルダの中にあるsound.wavという録音ファイルを読んで、そこから音波のデータを作成し、縦軸方向の最大値(振幅)を調べます。もし、録音ファイルが手元にない場合には、record-sound-intensity.pyを使ってください。このプログラムでは、まず録音をしてsound.wavという録音ファイルを作り、音波の振幅を調べます。
プログラム中の以下の部分で、wavToSamples()という関数に録音ファイル(WAVファイル)の名前を渡しています。
soundWaveData = wavToSamples(fileName)
この関数は、録音ファイルから音波のデータを作成し、そのデータを返します。このデータがsoundWaveDataという変数(variable)に入ります。このデータは、時間が経つにつれて音波の縦方向の値がどう変化するかを表しています。そして、このデータを関数max()に渡すと、音波の縦方向の値のうち最大のものを探すことができます。これが音波の振幅で、変数maxVolLevelに入ります。
maxVolLevel = max(soundWaveData)
wavToSamples()が音波のデータを返す時、全ての値は0と1の間の値になっています。なので、縦方向の値の最大値(振幅)も0と1の間の値になっています。0は音がしないこと(無音)を意味し、大きい数字ほど大きな音を意味します。1は、マイクで録音できる範囲内で最大の音量を意味します。
音波のデータ(関数wavToSamples()が返す値)に関する詳細は、以下を参照してください。
このプログラムの応用
さて、このプログラムを応用すると、「大きな音がしたら何かをする」というようなプログラムを作れます(例えば、LEDを点灯する、写真を撮る、動画を撮るなど)。こんなようなプログラムになります:
if maxVolLevel > 0.1:
// 何かするためのコードをここに書く
このように、音の大きさが「ある数値」を超えたら何かをする、というロジックになります。この数値にどんな値を使うとよいかですが、0.1くらいで始めてみてください。何がベストな値かは、どういうプログラムを作りたいかにもよりますし、マイクの設定(集音・録音能力の設定)にも依存します。試行錯誤が必要なので、実際に自分の声や物音を録音してmaxVolLevelを調べて、0.1より大きくしたいか小さくしたいか、またどのくらい大きく・小さくしたらよいかを決めるようにして下さい。
録音する時間を1秒未満にするには
これまでマイクで録音するときにはarecordというコマンドを使ってきました。これは便利なコマンドですが、録音時間が1秒以上という制約があります(-dのオプションに指定できるのは1以上の数値)。
録音時間を1秒未満にしたい場合には、arecordを使うのではなく、soundin.pyを使ってください。これはPyAudioを利用して録音します。
soundin.pyを使って録音をするサンプル・プログラムがrecord-sound-intensity-pyaudio.pyです。両方のファイルを同じフォルダの中に置いてrecord-sound-intensity-pyaudio.pyを実行してください。
soundin.pyを使って録音をするには、プログラムの先頭部分に
import soundin
と書いて、
stream = soundin.init()
とします(record-sound-intensity-pyaudio.py先頭部分を参照)。関数soundin.init()を呼ぶと、PyAudioを使って録音をする準備がされます。
ここまでの2行は、PyAudioを使う際には必ずやらねばならないことなので、「おまじない」のようなものだと思ってください。init()という関数が何をしているのかを詳しく知る必要はありません。一つだけ知っておくとよいのは、この関数が「ストリーム」というモノを返す、ということです。ストリームというのは、録音した音を受け取るための受信口です。record-sound-intensity-pyaudio.pyでは、soundin.init()が返したストリームを、streamという名前の変数に入れています。
ストリームを作ったら、soundin.capture()という関数を呼んで録音を開始します。
soundWaveData = soundin.capture(stream, duration=0.5)
soundin.capture()には現在使用中のストリームと、録音時間を渡します。録音時間は秒数で指定し、小数、分数も可です。上の例では0.5秒間録音しています。
録音が終わると、soundin.capture()は録音した音の(音波の)データを返します。このデータがsoundWaveDataという変数に入ります。このデータは、時間が経つにつれて音波の縦方向の値がどう変化するかを表しています。そして、このデータを関数max()に渡すと、音波の縦方向の値のうち最大のものを探すことができます。これが音波の振幅で、変数maxVolLevelに入ります。
maxVolLevel = max(soundWaveData)
soundin.capture()が音波データを返す時、縦方向の値は全ての値は0と1の間の値になっています。なので、縦方向の値の最大値(振幅)も0と1の間の値になっています。0は音がしないこと(無音)を意味し、大きい数字ほど大きな音を意味します。1は、マイクで録音できる範囲内で最大の音量を意味します。
record-sound-intensity-pyaudio.pyは、0.5秒間録音してその音波の振幅を求め、また0.5秒録音して・・・というふうに、録音と振幅計算を永遠に繰り返します。プログラムを止めるにはCtrl-Cしてください。
なお、いったん作ったストリームは、必要なくなったら「閉じる」必要があります。プログラムの最後などで、以下のように閉じるようにしてください。
soundin.close(stream)
次のサンプル・プログラムがrecord-sound-pyaudio.pyです。これは、録音した音の大きさを調べた後にその音をWAV形式のファイルに保存します。
soundWaveData = soundin.capture(stream, duration=3)
print(max(soundWaveData))
soundin.saveSamplesAsWav(soundWaveData, "output.wav")
ここでは3秒間録音し、録音データの振幅を求め、最後に関数soundin.saveSamplesAsWav()を読んで録音データを保存しています。この関数には、録音データと保存先ファイル名を渡します。