はじめに
自宅不在時の来客を通知してくれるシステムが欲しいなーと思い実装することにしました。
(コロナの影響で外出の機会が極端に減ったが...)
検知方法は、家に設置しているラズパイにマイクを接続し、生活音の中からチャイム音を検出しようと考えています。
また、使用言語はpython、パッケージPyAudioを使って生活音を取得します。
本記事では、PyAudioを使ってチャイム音を取得し、FFTでチャイム音の特徴を抽出するところまで記載します。
実装
ラズパイでいきなり動かす前に、macbookでPyAudio使ってみました。
その際の手順、コードを記載します。
環境
$ brew --version
Homebrew 2.2.15-35-g11f50cf
$ pip3 -V
pip 19.0.3
$ python3 -V
Python 3.7.3
PyAudioのインストール
pyaudioのインストールにportaudioが必要なため、先にportaudioをインストールします。
$ brew install portaudio
$ pip3 install pyaudio
接続中の入力デバイスの確認
PyAudioを使う際に、入力デバイス(マイク)の情報を設定する必要があります。
下記のコードで入力デバイスの情報を確認できます。
import pyaudio
audio = pyaudio.PyAudio()
for i in range(audio.get_device_count()):
print(audio.get_device_info_by_index(i))
↓実行した結果
$ python3 check_audio_device.py
{'index': 0, 'structVersion': 2, 'name': 'Built-in Microphone', 'hostApi': 0, 'maxInputChannels': 2, 'maxOutputChannels': 0, 'defaultLowInputLatency': 0.0029478458049886623, 'defaultLowOutputLatency': 0.01, 'defaultHighInputLatency': 0.01310657596371882, 'defaultHighOutputLatency': 0.1, 'defaultSampleRate': 44100.0}
{'index': 1, 'structVersion': 2, 'name': 'Built-in Output', 'hostApi': 0, 'maxInputChannels': 0, 'maxOutputChannels': 2, 'defaultLowInputLatency': 0.01, 'defaultLowOutputLatency': 0.010929705215419501, 'defaultHighInputLatency': 0.1, 'defaultHighOutputLatency': 0.02108843537414966, 'defaultSampleRate': 44100.0}
{'index': 2, 'structVersion': 2, 'name': 'USB PnP Audio Device', 'hostApi': 0, 'maxInputChannels': 1, 'maxOutputChannels': 0, 'defaultLowInputLatency': 0.0057083333333333335, 'defaultLowOutputLatency': 0.01, 'defaultHighInputLatency': 0.015041666666666667, 'defaultHighOutputLatency': 0.1, 'defaultSampleRate': 48000.0}
seakanoMacBook-Pro:chime_recognition seaka$
今回はUSB接続した(ラズパイでも使用する)マイクで音声データを取得するため、indexが2のデバイス情報を参照します。
- index ... 2
- maxInputChannels ... 1
- defaultSampleRate ... 48000.0
使用するデバイスの情報を取得したので、早速コードを書いてみましょう。
下記のコードを実行すると、音声データを取得しはじめます。
Ctrl+Cで停止させると、実行開始から終了までの音声データをプロットし、その後FFTした結果が表示されます。
(横軸は時間(秒)、縦軸は振幅)
import pyaudio
import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import fft
FORMAT = pyaudio.paInt16
CHANNELS = 1 # maxInputChannels
RATE = 48000 # defaultSampleRate
CHUNK = 1024
IDINDEX = 2 # index
count = 0
x = []
y = []
audio = pyaudio.PyAudio()
stream = audio.open(
format = FORMAT
,channels = CHANNELS
,rate = RATE
,frames_per_buffer=CHUNK
,input=True
,output=False
)
while stream.is_active():
try:
input = stream.read(CHUNK, exception_on_overflow=False)
ndarray = np.frombuffer(input, dtype='int16')
for i in ndarray:
count = count + 1
data = i.item()
seconds = count/RATE # 経過時間を取得
x.append(seconds)
y.append(data)
# print(seconds, data)
except KeyboardInterrupt:
break
plt.plot(x, y)
plt.show()
freq = np.linspace(0, RATE, count)
yf = np.abs(fft(y))
yf = yf * yf
plt.plot(freq[0:int(count/2)], yf[0:int(count/2)])
plt.show()
stream.stop_stream()
stream.close()
stream.terminate()
チャンネル数やサンプリング数を正確に設定しないと、エラーが発生するため注意が必要です。
私はここで少しつまづきました。
作成後、テストで1000Hzの音声を流してプロット結果を表示してみました。以下がその結果です。
図1より音声を取得できてそうなことがわかります。
図2より、1000Hz(横軸)らへんでピークが立っていることが分かります。正常にFFTできてそうです。
次に、チャイム音を取得してプロットしてみました。
チャイムぽい波形が出てます。
ちょっと見にくいですが、1500Hz、2000Hz、3500Hzらへんでピークが立っています。
この特徴を使えば、生活音からチャイム音を抽出できそうです。
今後
本記事では、ラズパイとマイクを使って、生活音からチャイム音を抽出する方法について書きました。
次は、今回わかった特徴を元に、実際にチャイム検知システムを作りたいと思います。
最終的には、チャイム検出→Line等で通知まで実装する予定です。