Bakulog

獏の夢日記的な何か。

【Python】Pepperマイク to PCスピーカー【Pepper】

タイトル通り、Pepperのマイクで拾った音を手元のPCにストリーミングし、リアルタイム再生させる方法の紹介です。

 

先に言っておきますが、本記事の中身は以下の2記事の内容をベタっと接着しただけです。

私の存在意義は接着剤、たとえば木工用ボンド程度…っていうと割と存在意義あるような気もしてきますが。

一つ目の記事ではPepperのマイク音を波形データローカルPCに引っ張ってファイルへ保存する方法を紹介しています。二つ目の記事は純粋なPythonのTipsでPyAudioというモジュールを使うとストリーミングによる音声再生が可能であることが紹介されています。

 

そこで「ファイル保存に使ってるストリームを音声再生用のストリームに差し替えれば音のリアルタイム再生が出来そう」という方針でスクリプトを組合せました。Windowsでしか動作確認してませんがLinuxMacでも動くハズです。

 

実行に必要なものは以下の通り。

※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