タイトル通り、Pepperのマイクで拾った音を手元のPCにストリーミングし、リアルタイム再生させる方法の紹介です。
先に言っておきますが、本記事の中身は以下の2記事の内容をベタっと接着しただけです。
- 引用その1: Pepper - 音声データをストリーミングで取得する。
- 引用その2: WAVEファイルの再生
私の存在意義は接着剤、たとえば木工用ボンド程度…っていうと割と存在意義あるような気もしてきますが。
一つ目の記事ではPepperのマイク音を波形データローカルPCに引っ張ってファイルへ保存する方法を紹介しています。二つ目の記事は純粋なPythonのTipsでPyAudioというモジュールを使うとストリーミングによる音声再生が可能であることが紹介されています。
そこで「ファイル保存に使ってるストリームを音声再生用のストリームに差し替えれば音のリアルタイム再生が出来そう」という方針でスクリプトを組合せました。Windowsでしか動作確認してませんがLinuxやMacでも動くハズです。
実行に必要なものは以下の通り。
※Choregrapheではないので注意。「欲しいけど手元にない」という場合アトリエ秋葉原で直接問い合わせてください。
そしてスクリプトはこちら。
# -*- coding: utf-8 -*- #Pepperから拾った音をそのままローカルPCで再生するためのスクリプト import time from argparse import ArgumentParser from pyaudio import PyAudio from naoqi import (ALProxy, ALBroker, ALModule) parser = ArgumentParser(description='play sound from pepper.') parser.add_argument('IP', metavar='i', type=str) parser.add_argument('port', metavar='p', type=int, default=9559) args = parser.parse_args() PEPPER_IP = args.IP PORT = args.port PepperModule = None class PepperModuleClass(ALModule): def __init__(self, name): ALModule.__init__(self, name) self.BIND_PYTHON(self.getName(), "callback") def startRecord(self): print("PepperModuleClass: now start to stream sound...") self.pyaudio = PyAudio() #引数の意味について: #formatの"2"は量子化ビット数が16bit=2byte #channelsの"1"はモノラルマイク #rateの"16000"はサンプリングレート self.audiostream = self.pyaudio.open( format=self.pyaudio.get_format_from_width(2), channels=1, rate=16000, output=True ) self.pepperMicrophone = ALProxy("ALAudioDevice", PEPPER_IP, PORT) # 16KHzのモノラル音声で前方マイク:3を指定 # ALL_Channels: 0 # AL::LEFTCHANNEL: 1 # AL::RIGHTCHANNEL: 2 # AL::FRONTCHANNEL: 3 # AL::REARCHANNEL: 4. self.pepperMicrophone.setClientPreferences(self.getName(), 16000, 3, 0) # 開始 self.pepperMicrophone.subscribe(self.getName()) def processRemote(self, inputChannels, inputSamples, timeStamp, inputBuff): # wavを手元の再生用ストリームにそのまま流す self.audiostream.write(inputBuff) def stopRecord(self): # 終了 self.pepperMicrophone.unsubscribe(self.getName()) time.sleep(1) self.audiostream.close() self.pyaudio.terminate() print("PepperModuleClass: streaming ended.") def main(): myBroker = ALBroker("myBroker","0.0.0.0",0,PEPPER_IP,9559) global PepperModule PepperModule = PepperModuleClass("PepperModule") try: print("start streaming. to stop, please use Ctrl + C") PepperModule.startRecord() while True: time.sleep(1) except KeyboardInterrupt: print("stop streaming...") PepperModule.stopRecord() myBroker.shutdown() if __name__ == "__main__": main()
スクリプトの実行方法ですが、コマンドライン引数でPepperのIPアドレスと接続ポート番号を入れる必要があります。例えば上記スクリプトを"hoge.py"で保存したなら
python hoge.py "192.168.xx.xx" 9559
のように呼び出します。
使ってるモジュールとか関数については公式ドキュメンテーションで調べてもらうとして、特に注意というべきは一つ目の引用記事にある通りPYTHON_BIND関数を呼び出すこと、あとALBroker型変数をきちんと作らないとエラーで怒られる事くらいでしょうか。
最後に一点だけ追加で注意です。このスクリプトは一見するとどこもおかしくない(ハズ)ですが、なぜか長時間実行(4,5分とか)し続けるとPepper側がシステムエラーを起こす場合があることを確認しています。エラーの詳細や再現条件までは検証してませんが、気をつけて使って下さい。というか原因/対策分かった人居たらぜひ教えて下さいお願いしますm( )m