前回はSnowboy
を使って中二魂全開のDXでない日輪刀を作りましたが、そのあたりの処理に関してはあまり触れていませんでした。
なので、久しぶりにpython
のコードについて記述していこうと思います。
記述は本家のSnowboy
のサイトを参考にしています。
【参考】 snowboy.kitt.ai
事前準備
ではまずはGitHub
からCloneしたコードをパーツとして使用します。全く0から作成するよりもベースを作るのが簡単です。
今回はmy_snowboy
というディレクトリを作成してコードを作ってみることにします。認識モデルなどに関しては先ほどのディレクトリに
シンボリックリンクを作成してアクセスします。
以下の処理を行えば、基本的にはsnowboy処理と同じ処理のひな型が完成します。
ソースコードの雛型の準備
$ mkdir my_snowboy $ cd my_snowboy/ $ ln -s ~/snowboy/resources resources $ cp ~/snowboy/examples/Python3/snowboydecoder.py . $ ln -s ~/snowboy/swig/Python3/snowboydetect.py snowboydetect.py $ ln -s ~/snowboy/swig/Python3/_snowboydetect.so _snowboydetect.so $ cp ~/snowboy/examples/Python3/demo.py .
ここまでできたら、実行実験を行います。これが動けは無事にソースコードの雛型の準備ができました。
$ python3 demo.py resources/models/snowboy.umdl
Snowboy
認識部分だけの抽出したソースプログラム
先ほどのひな型ができたら、Snowboy
の認識部分のみを抽出したコードを作ってみます。
内容としては、
Ctrl+C
でのinterrupt
のコールバックの処理- Hotword検知モデルの引数読み込み
- Hotword処理の設定と実行
を設定していきます。
import snowboydecoder import sys import signal interrupted = False def signal_handler(signal, frame): global interrupted interrupted = True def interrupt_callback(): global interrupted return interrupted if len(sys.argv) == 1: print("Error: need to specify model name") print("Usage: python demo.py your.model") sys.exit(-1) model = sys.argv[1] signal.signal(signal.SIGINT, signal_handler) detector = snowboydecoder.HotwordDetector(model, sensitivity=0.5) print('Listening... Press Ctrl+C to exit') detector.start(detected_callback=snowboydecoder.play_audio_file, interrupt_check=interrupt_callback, sleep_time=0.03) detector.terminate()
こちらのコードを改良していきます。
SnowboyからLEDを光らせてみるソースプログラム
まずはLチカを行うソースプログラム
先ほどのサンプルコードではSnowboyのコールバック設定で音声出力をコールバックに設定していました。 今回はLEDを点灯する処理をコールバックとして設定することで実現を行っていきます。
まずは、LEDを点灯する処理を単体で作成してみます。
RPi.GPIO
を使用してRaspberryPiのGPIOを操作して、LEDを点滅するものになります。
単体での実行(__main__
の処理)ではGPIOの17ピンを使用してLEDを点滅させています。(0.3秒点灯、0.7秒消灯)
import RPi.GPIO as GPIO import time class Light(object): def __init__(self, port): self.port = port GPIO.setmode(GPIO.BCM) GPIO.setup(self.port, GPIO.OUT) self.on_state = GPIO.HIGH self.off_state = not self.on_state def set_on(self): GPIO.output(self.port, self.on_state) def set_off(self): GPIO.output(self.port, self.off_state) def is_on(self): return GPIO.input(self.port) == self.on_state def is_off(self): return GPIO.input(self.port) == self.off_state def toggle(self): if self.is_on(): self.set_off() else: self.set_on() def blink(self, t=0.3): self.set_off() self.set_on() time.sleep(t) self.set_off() if __name__ == "__main__": light = Light(17) while True: light.blink() time.sleep(0.7)
実行は以下のように行います。特にsudo
を付加しなくても実行できました。
$ python3 light.py
LEDが点滅すれば準備はOKです。
動画
RaspberryPiで実行したPythonでLチカしてみた
SnowboyからLEDを点滅させる
先ほどの認識部分の最小コードを変更して以下のようにします。 変更部分は3行程度です。
一番のポイントはdetector.start()
の引数であるdetected_callback
の値をLED点灯にしている点です。
my_blink_demo.py
import snowboydecoder import sys import signal from light import Light # 追加部分 interrupted = False def signal_handler(signal, frame): global interrupted interrupted = True def interrupt_callback(): global interrupted return interrupted if len(sys.argv) == 1: print("Error: need to specify model name") print("Usage: python demo.py your.model") sys.exit(-1) model = sys.argv[1] signal.signal(signal.SIGINT, signal_handler) detector = snowboydecoder.HotwordDetector(model, sensitivity=0.5) print('Listening... Press Ctrl+C to exit') # 変更点 ここから led = Light(17) detector.start(detected_callback=led.blink, interrupt_check=interrupt_callback, sleep_time=0.03) # 変更点 ここまで detector.terminate()
先ほど作成したlight.py
をimportしてHotword検知した際にLEDの点灯を呼び出すようにしています。とても簡単です。
あとは同様に、認識モデルを引数として与えます。下で指定しているのはオリジナルの全集中
のHotwordになります。
$ python3 myblinkdemo.py resources/models/zensyuucyuu.pmdl
動画
Lチカの代わりに音声を鳴らしてみる
先ほどはGPIOを使用しましたが、今回は認識時の音声を変更してみます。音声の出力に関しては、すでにSnowboyのサンプルコード(snowboydecoder.py
)に含まれていますが、音声の指定がちょっと面倒なので少し変更して、ソースコードに含めます。基本はpyaudio
の機能を使用して音声の再生をしているので、そこまで複雑ではありません。
今回使用した音声はRaspberryPi
インストール時にすでに入っているalsaのサンプルファイルである"/usr/share/sounds/alsa/Front_Center.wav"を使っています。カレントディレクトリに音声がある場合には、ファイル名の指定だけで大丈夫です。
my_sound_demo.py
import snowboydecoder import sys import signal #---追加開始 import pyaudio import wave import time #---追加終了 #---追加開始 SOUNDFILE = "/usr/share/sounds/alsa/Front_Center.wav" # サウンドファイルの指定 def play_audio_file(fname=SOUNDFILE): sound_wav = wave.open(fname, 'rb') sound_data = sound_wav.readframes(sound_wav.getnframes()) audio = pyaudio.PyAudio() stream_out = audio.open( format=audio.get_format_from_width(sound_wav.getsampwidth()), channels=sound_wav.getnchannels(), rate=sound_wav.getframerate(), input=False, output=True) stream_out.start_stream() stream_out.write(sound_data) time.sleep(0.2) stream_out.stop_stream() stream_out.close() audio.terminate() #---追加終了 interrupted = False def signal_handler(signal, frame): global interrupted interrupted = True def interrupt_callback(): global interrupted return interrupted if len(sys.argv) == 1: print("Error: need to specify model name") print("Usage: python demo.py your.model") sys.exit(-1) model = sys.argv[1] signal.signal(signal.SIGINT, signal_handler) detector = snowboydecoder.HotwordDetector(model, sensitivity=0.5) print('Listening... Press Ctrl+C to exit') detector.start(detected_callback=play_audio_file, interrupt_check=interrupt_callback, sleep_time=0.03) detector.terminate()
実行するには先ほどの例と同じように引数に認識モデルを指定する必要があります。
$ python3 my_sounddemo.py resources/models/snowboy.umdl
おわりに
折角RaspberryPi
を使用してのホットワード検知を行っているので、GPIO
なんかも使っていかないと面白くないかなと思って試してみました。
自分も久しぶりにpython
のコードを触ったので、たまに詰まるところもありましたが何とかなりました。
Snowboy
を使用すれば、小型の音声リモコンの実現なんかも可能ですしね。
【参考】 snowboy.kitt.ai