26
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RaspbianのJessieでJuliusを動かし、IBM WATSONとお話した時のTipsとPythonスクリプト(と作業メモ)

Last updated at Posted at 2016-03-09

#経緯
音声認識が流行ってますし、折角RaspberryPiあるし。
以下、私の環境で躓いた、気になった点のみ記載しておきます。
(絶賛Try & Error中なので、生暖かい目で見守って下さい。)

一応IBM WATSON(のText to speech)とも繋げました。後半中盤にあります。(だんだん長くなってきて見づらいのでその内分割しますが、暫くはまだ漸次的に書きます。)

#結論(2016/05/19追記)
面倒くさい事が好きな人は良いんですが、殆どの人には、思っていたのと違う感が有ると思いますので、Julius動かしてみるのは辞めたほうが良いんではないかと思ってます。
それでも、WATSONとかなんかやりたいんだYo!という人は以下読んでみてください。
(多分、手作りするより、MacのSiriとか、Echoとか、Googleのやつとか、そういう製品がもうすぐ出回るので待ったほうが良いかと。)

#前提
[sensei]:http://cubic9.com/Devel/%E9%9B%BB%E5%AD%90%E5%B7%A5%E4%BD%9C/RaspberryPi/%E6%97%A5%E6%9C%AC%E8%AA%9E%E9%9F%B3%E5%A3%B0%E8%AA%8D%E8%AD%98/

#気付き
##最新版のjuliusだとファイル構成が変わってて先生の例が使えない
私の先生は、make後のテストとして以下のようなコマンドでテストされてました。

~/julius-4.2.3/julius/julius -C ~/julius-kits/dictation-kit-v4.2.3/fast.jconf -charconv EUC-JP UTF-8

ただ、最新バージョンだと構造が異なるようで動かず。(もちろんファイルパスは変えてますよ。)
ですので、、

~/julius-4.3.1/julius/julius -C ~/julius-kits/grammar-kit-v4.1/testmic.jconf  -charconv EUC-JP UTF-8

でとりあえず動かしてみました。その場合は辞書がフルーツなので、ほとんど「ぶどう です」としか認識しませんでしたが(笑)

##/etc/modprobe.d/alsa-base.confは作れるし、効く
なので、先生の例の通り、普通に無ければ作っちゃってリブートすればOKです。
こんな感じ。

/etc/modprobe.d/alsa-base.conf
# This sets the index value of the cards but doesn't reorder.
options snd_usb_audio index=0
options snd_bcm2835 index=1

# Does the reordering.
options snd slots=snd-usb-audio,snd-bcm2835

ただ、このせいなのか、これ以降alsamixerでF4をおしてマイクの設定イジろうとしても、alsamixerが強制終了しちゃいます。詳しくは調べてないですが。F5は効くので、結果的にはそれでOK牧場。

##途中にバスパワーのHUB挟むとやっぱりNG

まぁ、これは色んな所に書いてありますが、バスパワーのHUBにDAC挿したらノイズがひどすぎてやってられないので、

  • セルフパワーのHubにぶら下げる
  • Raspiへの給電を2Aとかの太いものにしておいて、DAC直刺し

のどちらかが良いでしょうね。綺麗な電源って大事ですね。

##マイクのパワーは100%にしても全然よさそう
我が家の環境では、テレビの後ろにマイク配置して2m程離れた場所で普通に話しても認識してくれそうです。
まだあんまり色々試したわけでは無いので、エラー率がどの程度かは今後の話ですが。

#以上
また何か気がついたら追記します。

#追記:2016/03/12
なんかjuliusってあんまし使えないんじゃないか疑惑が浮上中。
ということで、早速浮気でIBM watsonのSpeech to TextやGoogle Speech APIなんかを考慮してみるも、WATSONは月1,000分迄は無料でその枠に収めるとなると1日30分ちょい。。Googleだと50回/日か。
そもそもAPI呼び出すのに例えばなんかボタンとか押すのとか嫌だから音声によるトリガーはローカルで実装しなきゃならんとなると、、とりあえずjulius随時起動でWATSONという単語を拾ったらAPI叩くようにするとかそんな事できるのか?

見た資料:

##四の五の言わずに「WATSON」とだけ解らせてみる。
改めて[先生][sensei]を見る。

###単語元帳作成
とりあえず。言われたとおりやりました。

###単語元帳の変換
これも、パス名が変わっている程度で特に問題なく完了。

###設定ファイル(jconf)作成
先生の言うとおりにやってみるも、時代が変わったのか先生が書いてるファイルが無くなってる!しかもなんかちょっとバージョン変わったって感じに見えないのがある。。
仕方ない調べる。
####MonophoneとTriphoneってなんじゃ?
オフィシャル文献

Julius は音響モデルとして,HMM (Hidden Markov Model) を用いることができる.コンテキスト非依存モデル (monophone),およびコンテキスト依存モデルを triphone まで扱える.

なんのこっちゃ。

What is the different between a monophone and a triphone?

Monophone: The pronunciation of a word can be given as a series symbols that correspond to the individual units of sound that make up a word. These are called 'phonemes' or 'phones'. A monophone refers to a single phone.

Triphone: A triphone is simply a group of 3 phones in the form "L-X+R" - where the "L" phone (i.e. the left-hand phone) precedes "X" phone and the "R" phone (i.e. the right-hand phone) follows it.

Below is an example of the conversion of a monophone declaration of the word "TRANSLATE" to a triphone declaration (the first line shows the "monophone" declaration, and the second line shows the "triphone" declaration):

TRANSLATE [TRANSLATE] t r @ n s l e t
TRANSLATE [TRANSLATE] t+r t-r+@ r-@+n @-n+s n-s+l s-l+e l-e+t e-t

つまり、monophoneは音を単語の塊として認識するためのもので、triphoneはある音を「前の音 - 今の音 + 次の音」というような3つ塊で表現/認識するためのもの、ってこと?(前と次ってのがつまりコンテキスト?)
そういえば先日のニューラルネットワーク勉強家の時もなんか似たような構造で解析かけるっぽい事を話してたような気がするので、文法とか色々見るときには便利なんだろうか。
仮にそうだとすると、今回「ワトソン起きろ」とかその程度の決め打ちワードさえわかってくれれば良いという前提ならば、monophoneで良さそうな気がする。

ということで、先に進んで見る。
ここまでを踏まえると

先生
-w sushi.dic
-v model/lang_m/web.60k.htkdic
-h model/phone_m/hmmdefs_ptm_gid.binhmm
-hlist model/phone_m/logicalTri
-n 5
-output 1
-input mic
-zmeanframe
-rejectshort 800
-charconv EUC-JP UTF-8
-w original_words.dic
-v model/lang_m/bccwj.60k.htkdic
-h model/phone_m/jnas-mono-16mix-gid.binhmm
-hlist model/phone_m/logicalTri
-n 5
-output 1
-input mic
-zmeanframe
-rejectshort 800
-charconv EUC-JP UTF-8

となる。-vbingram形式のファイルは1個しか無いのでそれにしました。-hがさっきの話で、monophoneでとりあえず行ってみよう。
あとは大丈夫そう。(全部の意味は意味調べてないけどまぁ先生のTTPで。)
作成したファイルは~/julius-kits/dictation-kit-v4.3.1-linux/org.jconfとする。

早速実行してみたら失敗した。

$ cd ~/julius-kits/dictation-kit-v4.3.1-linux
$ julius -C org.jconf
...
中略
...
Error: rdhmmlist: line 21427: physical HMM "hy+a" not found
Error: rdhmmlist: line 21428: physical HMM "r+e" not found
Error: rdhmmlist: line 21429: physical HMM "g+o" not found
Error: init_phmm: HMMList "model/phone_m/logicalTri" read error
ERROR: m_fusion: failed to initialize AM
ERROR: Error in loading model

意味不明とおもいきや、"g+o" not foundというどこかで見たような記述。これはTriphoneじゃんか。ということは、

-hlist model/phone_m/logicalTri

logicalTriTriってTriphoneTriか!
気を取り直して、

-w original_words.dic
-v model/lang_m/bccwj.60k.htkdic
-h model/phone_m/jnas-tri-3k16-gid.binhmm
-hlist model/phone_m/logicalTri
-n 5
-output 1
-input mic
-zmeanframe
-rejectshort 800
-charconv EUC-JP UTF-8

で再実行。

$ julius -C org.jconf
...
中略
...
### read waveform input
Stat: adin_oss: device name = /dev/dsp (application default)
Stat: adin_oss: sampling rate = 16000Hz
Stat: adin_oss: going to set latency to 50 msec
Stat: adin_oss: audio I/O Latency = 32 msec (fragment size = 512 samples)
STAT: AD-in thread created
<<< please speak >>>

おお、キタキタ!
よし、ではいでよ「ワトソン!」

pass1_best: WATSON
pass1_best_wordseq: WATSON
pass1_best_phonemeseq: silB w a t o s o N silE
pass1_best_score: -1964.247070
sentence1: WATSON
wseq1: WATSON
phseq1: silB w a t o s o N silE
cmscore1: 0.442
score1: -1964.247070

おおおおおお。まずは先に進みました。(スコア低そうですが後で考える。)

[先生][sensei]の記載はここまででしたので、お礼を言って先に進みましょう。

##何かしらコマンドを連携させる!

今考えているのは、

  1. julius起動、随時コマンド待ち
  2. 「WATSON」の認識が発生したら、juliusを停止してマイクを開放させる
  3. WATSONへの接続を行う処理(後で作るとし、仮名watson_kicker)を実行
  4. WATSONへの準備ができたら「私だ、WATSONだ。」とつぶやかせる(それか、LED光らせる。)
  5. WATSONとお話し。その結果の処理はまたベッケンバウアーとし、後で考える。
  6. watson_kickerの処理終了時にもう一度julius起動させる(もしくは、何か全然別の監視プロセス動かしておいて、止まっててかつwatson_kickerが動いてなかったら即起動させる?)
  7. 振り出しに戻る

という感じかしら。果たして動くのかな。パフォーマンスどうなんだろう。

先の先生の方がコマンド連携の赤外線学習リモコンR2-D2という記事を上げてらっしゃいますが、2の箇所の参考になるのか?
というか、どうやってjuliusから結果取ってくるんだ!?と思ったら、
公式ガイドで発見、第10章 モジュールモードなるのが有るわけですね。julius自身がHTTPサーバー的に10500番ポートでお話するみたい。
ということは、HomeAutomationWithVoiceCommand.pyスクリプトはクライアント側だしまんま参考になりそうですね。前言撤回。2はなんとかなる気がする。

まずは、モジュールモードのテスト。

$ julius -C org.jconf  -module
...
中略
...
Stat: server-client: socket ready as server
///////////////////////////////
///  Module mode ready
///  waiting client at 10500
///////////////////////////////

よしよし。ちなみにこの時のCPU使用率はほぼ0%みたいなので、常駐させてもよさそうです。

公式ガイドでは、

Julius にはサンプルのクライアント jcontrol が付属している.これはJuliusから送信されたメッセージをそのまま標準出力に出力し, またいくつかの簡単なコマンドを送ることができるツールである.これを使い, 以下の要領でJuliusのモジュール動作を試すことができる.

(1) サーバ:Julius を通常の起動方法に "-module" オプションを追加して起動

   % julius -C fast.jconf -module

(2) クライアント:jcontrol を以下のように起動

   % jcontrol (1)を実行しているホスト名

この状態で音声認識を行うと,結果が jcontrol 側に出力される.また, jcontrol で "pause" と入力して enter と押すと認識中断,"resume"で認識が再開できる.

とあるので試す。

$ ./julius-4.3.1/jcontrol/jcontrol localhost
connecting to localhost:10500...done
> <STARTPROC/>
> <INPUT STATUS="LISTEN" TIME="1457799541"/>

動いた。
改めて。「WATSON!」

> <INPUT STATUS="STARTREC" TIME="1457799963"/>
> <STARTRECOG/>
> <INPUT STATUS="ENDREC" TIME="1457799964"/>
> <ENDRECOG/>
> <INPUTPARAM FRAMES="115" MSEC="1150"/>
> <RECOGOUT>
>   <SHYPO RANK="1" SCORE="-2450.027588" GRAM="0">
>     <WHYPO WORD="WATSON" CLASSID="WATSON" PHONE="silB w a t o s o N silE" CM="0.765"/>
>   </SHYPO>
> </RECOGOUT>

よさそうね。
しかし、中身をよくよく見ていると、

> <INPUT STATUS="STARTREC" TIME="1457800335"/>
> <STARTRECOG/>
> <INPUT STATUS="ENDREC" TIME="1457800336"/>
> <ENDRECOG/>

なるものがあるです。内部的には一瞬録音したりしているのか?
しているならそのファイルを変換してWATSONに送りつけるという手も有る気がするけど、後ほど調べるとする。

気を取り直して、10500番ポートに繋いで結果を取る処理を行うPythonを作ります。
まずは基本の所だけ。

julius_listener.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import requests
import re

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 10500))

sf = s.makefile('')

reWATSON = re.compile(r'WHYPO WORD="WATSON" .* CM="(\d\.\d*)"')

while True:
    line = sf.readline().decode('utf-8')
    #if line.find('WHYPO') != -1:
    tmp = reWATSON.search( line )
    if tmp:
        print line
        if float(tmp.group(1)) > 0.8:
                print 'call WATSON'

reWATSONは、正規表現を事前コンパイルしたものです。それを使って、結果のWORDの値がWATSONである事の検出と、そのCM値を取れるようにして、その値が仮に0.8以上だった場合はcall WATSONと出力する様にしました。
現段階では、一旦、WATSONが検出された時点での入力行を出力してますので0.8以下の場合とそうでない場合の差がわかるはずです。

chmod u+x julius_listener.py

してから、再度juliusをモジュールとして起動。

$ julius -C org.jconf  -module
...
中略
...
Stat: server-client: socket ready as server
///////////////////////////////
///  Module mode ready
///  waiting client at 10500
///////////////////////////////

よし。では新たに別のTerminal立ち上げてpython動かしてみましょう。

$ ./julius_listener.py

すると、julius側の窓で

$ julius -C org.jconf  -module
...
中略
...
Stat: server-client: socket ready as server
///////////////////////////////
///  Module mode ready
///  waiting client at 10500
///////////////////////////////
///  Stat: server-client: connect from 127.0.0.1
STAT: ###### initialize input device
----------------------- System Information begin ---------------------
...
さらに中略
...
------
### read waveform input
Stat: adin_oss: device name = /dev/dsp (application default)
Stat: adin_oss: sampling rate = 16000Hz
Stat: adin_oss: going to set latency to 50 msec
Stat: adin_oss: audio I/O Latency = 32 msec (fragment size = 512 samples)
STAT: AD-in thread created

よしよし。どうやらつながった様です。
では、改めて、「わとしょん!」とダメなケース、そして「WATSON」とちゃんとしたケースで話しかけてみる。

$ ./julius_listener.py
    <WHYPO WORD="WATSON" CLASSID="WATSON" PHONE="silB w a t o s o N silE" CM="0.591"/>

    <WHYPO WORD="WATSON" CLASSID="WATSON" PHONE="silB w a t o s o N silE" CM="0.835"/>

call WATSON

おお、2回目のがちゃんと閾値クリア出来てるのでcall WATSON出てきた。
よしよし。いいぞ!

ということで、

  1. julius起動、随時コマンド待ち → ほとんどOK
  2. ~~「WATSON」の認識が発生したら、~~juliusを停止してマイクを開放させる → 停止を組み込むぞ!
  3. WATSONへの接続を行う処理(後で作るとし、仮名watson_kicker)を実行
  4. WATSONへの準備ができたら「私だ、WATSONだ。」とつぶやかせる(それか、LED光らせる。)
  5. WATSONとお話し。その結果の処理はまたベッケンバウアーとし、後で考える。
  6. watson_kickerの処理終了時にもう一度julius起動させる(もしくは、何か全然別の監視プロセス動かしておいて、止まっててかつwatson_kickerが動いてなかったら即起動させる?)
  7. 振り出しに戻る

ガイドによると、DIEというコマンドを送付すればjuliusが強制終了するらしいが、実際にやってみても、落ちる気配が無い。killするしか無いか。

続く。

#追記:2016/03/15
##だんだんPythonスクリプトのお勉強になってきた。
とりあえず、

  • juliusを起動して、
  • 10500番ポートに接続してjulius結果を拾って、
  • 「WATSON」のCM値が80%以上だった時に、
  • ダミーでWATSON繋いだ事にするメッセージ出力
  • 再度julius起動(ループ)
  • (一応、Ctrl-cしたら優しく止める)

という処理をするのが以下です。
まだ肝心のWATSON君とのお話部分は全く未完成ですが、動かしてみたい人はjulius_pathjconf_pathを適宜置き換えて実行してみてください。
WATSON君を呼ぶcall WATSONあたりの箇所をカスタマイズすれば普通に使えると思います。juliusを落とさなくていいなら、call WATSON後のkill_juliusdelete_socket部分をコメントアウトしたほうが良いですね。

##サンプルスクリプト

julius_listener.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import socket
import requests
import re
import subprocess
import shlex
import time

julius_path = '/usr/local/bin/julius'
jconf_path = '/home/pi/julius-kits/dictation-kit-v4.3.1-linux/org.jconf'
julius = None
julius_socket = None


def invoke_julius():
    print 'INFO : invoke julius'
    args = julius_path + ' -C ' + jconf_path + ' -module '
    p = subprocess.Popen(
            shlex.split(args),
            stdin=None,
            stdout=None,
            stderr=None
        )
    print 'INFO : invoke julius complete.'
    print 'INFO : wait 2 seconds.'
    time.sleep(3.0)
    print 'INFO : invoke julius complete'
    return p


def kill_julius(julius):
    print 'INFO : terminate julius'
    julius.kill()
    while julius.poll() is None:
        print 'INFO : wait for 0.1 sec julius\' termination'
        time.sleep(0.1)
    print 'INFO : terminate julius complete'


def get_OS_PID(process):
    psef = 'ps -ef | grep ' + process + ' | grep -ve grep -vie python |head -1|awk \'{print($2)}\''
    if sys.version_info.major == 3:
        PID = str(subprocess.check_output(psef, shell=True), encoding='utf-8').rstrip ()
    else:
        PID = subprocess.check_output(psef, shell=True).rstrip ()
    return PID


def create_socket():
    print 'INFO : create a socket to connect julius'
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('localhost', 10500))
    print 'INFO : create a socket to connect julius complete'
    return s


def delete_socket(s):
    print 'INFO : delete a socket'
    s.close()
    print 'INFO : delete a socket complete'
    return True


def invoke_julius_set():
    julius = invoke_julius()
    julius_socket = create_socket()
    sf = julius_socket.makefile('rb')
    return (julius, julius_socket, sf)


def main():
    global julius
    global julius_socket
    julius, julius_socket, sf = invoke_julius_set()

    # ###
    # # re definition
    # ###
    reWATSON = re.compile(r'WHYPO WORD="WATSON" .* CM="(\d\.\d*)"')

    while True:
        if julius.poll() is not None:   # means , julius dead
            delete_socket(julius_socket)
            julius, julius_socket, sf = invoke_julius_set()
        else:
            line = sf.readline().decode('utf-8')
            print line
            tmp = reWATSON.search(line)
            if tmp:
                # print line
                if float(tmp.group(1)) > 0.8:
                    print 'WARN : DIE julius, call WATSON'
                    kill_julius(julius)
                    delete_socket(julius_socket)
                    print '====================================='
                    time.sleep(2.0)
                    print '====================================='
                    time.sleep(2.0)
                    print '====================================='

    print 'WARN : while loop breaked'
    print 'INFO : exit'


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print 'Interrupted. Exit sequence start..'
        kill_julius(julius)
        delete_socket(julius_socket)
        print 'INFO : Exit sequence done.'
        sys.exit(0)

##うーん。。
しかし、juliusの認識率が低い。「WATSON」と近くで言っても中々数字が悪いので、そもそもjuliusでWATSONをKickするのはダメかも。。。まぁでも一通りやってみよう。

続く。(今度こそWATSON君につなげるぞ!)

#追記:2016/03/17
##今日こそWATSONとお話するぞ。
ということで、きっと誰かが良いもの作ってるはず。。。
これか? watson-developer-cloud/speech-to-text-websockets-python
こことか? watson-developer-cloud/raspberry-pi-speech-to-text

ふむふむ。
Raspiの方はドンピシャっぽい気もするけど、でもNode.js入れろとか言ってる。
うーん。あんましNode.js勉強してないのと、既存のアパッチ君との兼ね合いとか考えるの面倒なので、、、上の方つかってみる。

##サンプルスクリプトをインストールするぞ。

mkdir watson
cd watson
git clone https://github.com/watson-developer-cloud/speech-to-text-websockets-python.git
cd speech-to-text-websockets-python
sudo pip install -r requirements.txt

大量のエラー発生(多すぎて載せません)。どうもコンパイルで失敗。Python.hが無いとか言ってる。
先に下のをやらないといけないのか?

sudo apt-get install build-essential python-dev

もちろんこれは難なく完了。
さあ、再挑戦。

$ sudo pip install -r requirements.txt
Traceback (most recent call last):
  File "/usr/bin/pip", line 9, in <module>
    load_entry_point('pip==1.5.6', 'console_scripts', 'pip')()
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 356, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2476, in load_entry_point
    return ep.load()
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2190, in load
    ['__name__'])
  File "/usr/lib/python2.7/dist-packages/pip/__init__.py", line 74, in <module>
    from pip.vcs import git, mercurial, subversion, bazaar  # noqa
  File "/usr/lib/python2.7/dist-packages/pip/vcs/mercurial.py", line 9, in <module>
    from pip.download import path_to_url
  File "/usr/lib/python2.7/dist-packages/pip/download.py", line 25, in <module>
    from requests.compat import IncompleteRead
ImportError: cannot import name IncompleteRead

なんか戦況が悪化した感じ。全く内容が違う。python-dev入れた時になんか変になったのか?pip入れなおしてみる。

$ sudo apt-get purge python-pip
$ curl -kL https://bootstrap.pypa.io/get-pip.py | sudo python
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1487k  100 1487k    0     0   167k      0  0:00:08  0:00:08 --:--:--  188k
Collecting pip
  Downloading pip-8.1.1-py2.py3-none-any.whl (1.2MB)
    66% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺枕          | 798kB 1.3MB/s eta 0:00:0    67% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺幕          | 808kB 1.1MB/s eta 0:00:0    68% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺哩          | 819kB 468kB/s eta 0:00:0    69% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆         楯 829kB 474kB/s eta 0:00    70% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆         鋼 839kB 474kB/s eta 0:00    70% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆         掛 849kB 474kB/s eta 0:00    71% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆         ・ 860kB 335kB/s eta 0:00    72% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺鮪        | 870kB 332kB/s eta 0:    73% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺膜        | 880kB 335kB/s eta 0:    74% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺哩        | 890kB 335kB/s eta 0:    75% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎        | 901kB 255kB/s eta 0:    76% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆       鋼 911kB 274kB/s eta     76% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆       弓 921kB 217kB/s eta     77% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆       ・ 931kB 216kB/s eta     78% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺柾      | 942kB 186kB/s et    79% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺膜      | 952kB 179kB/s et    80% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺槙      | 962kB 215kB/s et    81% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎      | 972kB 177kB/s et    82% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆     旨 983kB 148kB/s     82% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆     芸 993kB 148kB/s     83% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆     榎 1.0MB 136kB/s     84% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆     ・ 1.0MB 136kB/s     85% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺枕    | 1.0MB 170kB/    86% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺幕    | 1.0MB 143kB/    87% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎    | 1.0MB 160kB/    88% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆   楯 1.1MB 121k    88% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆   芸 1.1MB 107k    89% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆   掛 1.1MB 120k    90% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆   ・ 1.1MB 119k    91% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺鮪  | 1.1MB 11    92% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺膜  | 1.1MB 11    93% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺哩  | 1.1MB 10    94% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎  | 1.1MB 10    94% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆 鋼 1.1MB     95% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆 弓 1.1MB     96% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆 ・ 1.2MB     97% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺柾| 1.2M    98% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺膜| 1.2M    99% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺槙| 1.2M    100% |笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎笆遺毎| 1.2MB 34kB/s
Installing collected packages: pip
Successfully installed pip-8.1.1

よしよし入ったぞ。(プログレスバー的なやつがめちゃ文字化けしてると思ったら、Teratermの端末設定をUTF-8にしてなかった。。。)
さて、Retryするぞ。

sudo pip install -r requirements.txt

お、ちゃんと入ってるくさい。結構時間かかります。
入ったー!!バンザイ!

##WATSON使うための情報ゲトするぞ

ちょっと優しくないかもですが、リストで書きます。

  1. bluemixでユーザー登録する(30日間無料です)
  2. 中に入ったら、ダッシュボードあたりで、「サービスまたはAPIの追加」に行き、WATSONの「Speech To Text」をポチる。
  3. 右横の「サービスの追加」でアプリは「アンバインドのまま」(アプリに紐付かせない)にして後はお好きにどうぞ。プランは「標準」で「作成」して下さい。(「標準 最初の 1000 分は無料です」だそうです。)
  4. 再びダッシュボードに行き、作ったばかりの「Speech To Text」を開いて、「サービス資格情報」を確認。すると、JSON形式でcredentials情報が載ってます。ここのユーザー名とパスワードを使いますよ。

##ではWATSON動くかな。。。。
(パスワードとユーザー名は適宜置き換えて下さいね。)

$ python ./sttClient.py -credentials <username>:<password> -model en-US_BroadbandModel
:0: UserWarning: You do not have a working installation of the service_identity module: 'No module named service_identity'.  Please install it from <https://pypi.python.org/pypi/service_identity> and make sure all of its dependencies are satisfied.  Without the service_identity module and a recent enough pyOpenSSL to support it, Twisted can perform only rudimentary TLS client hostname verification.  Many valid certificate/hostname mappings may be rejected.
the output directory "./output" already exists, overwrite? (y/n)? y
2016-03-17 23:59:48+0900 [-] Log opened.
2016-03-17 23:59:48+0900 [-] ./recordings/0001.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0002.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0003.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0004.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0005.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0006.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0007.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0008.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0009.wav
2016-03-17 23:59:48+0900 [-] ./recordings/0010.wav
2016-03-17 23:59:48+0900 [-] Traceback (most recent call last):
2016-03-17 23:59:48+0900 [-]   File "./sttClient.py", line 298, in <module>
2016-03-17 23:59:48+0900 [-]     factory = WSInterfaceFactory(q, summary, args.dirOutput, args.contentType, args.model, url, headers, debug=False)
2016-03-17 23:59:48+0900 [-]   File "./sttClient.py", line 55, in __init__
2016-03-17 23:59:48+0900 [-]     WebSocketClientFactory.__init__(self, url=url, headers=headers, debug=debug)  
2016-03-17 23:59:48+0900 [-]   File "/usr/local/lib/python2.7/dist-packages/autobahn/twisted/websocket.py", line 278, in __init__
2016-03-17 23:59:48+0900 [-]     protocol.WebSocketClientFactory.__init__(self, *args, **kwargs)
2016-03-17 23:59:48+0900 [-] TypeError: __init__() got an unexpected keyword argument 'debug'

ぐう。何かごちゃごちゃ言われてます。。。
前半は無視するとして(大丈夫なのか?)、、、
debugってパラメータが不要っぽい?まさかとは思うけど、取ってみるか。まずは保存してから編集。

cp -ip sttClient.py sttClient.py.org
vi sttClient.py

編集箇所は

#55行目Before
       WebSocketClientFactory.__init__(self, url=url, headers=headers, debug=debug)
#55行目after
       WebSocketClientFactory.__init__(self, url=url, headers=headers)

#299行目Before
    factory = WSInterfaceFactory(q, summary, args.dirOutput, args.contentType, args.model, url, headers, debug=False)
#299行目after
    factory = WSInterfaceFactory(q, summary, args.dirOutput, args.contentType, args.model, url, headers)

上記の2箇所。

よし、再実行!!
##(再挑戦)いでよワトソン!!!!

(中略)
2016-03-18 00:10:09+0900 [-] sendMessage(init)
2016-03-18 00:10:09+0900 [-] ./recordings/0001.wav
2016-03-18 00:10:09+0900 [-] onOpen ends
2016-03-18 00:10:09+0900 [-] Text message received: {
2016-03-18 00:10:09+0900 [-]    "state": "listening"
2016-03-18 00:10:09+0900 [-] }
2016-03-18 00:10:14+0900 [-] Text message received: {
2016-03-18 00:10:14+0900 [-]    "results": [
2016-03-18 00:10:14+0900 [-]       {
2016-03-18 00:10:14+0900 [-]          "alternatives": [
2016-03-18 00:10:14+0900 [-]             {
2016-03-18 00:10:14+0900 [-]                "timestamps": [
2016-03-18 00:10:14+0900 [-]                   [
2016-03-18 00:10:14+0900 [-]                      "several",
2016-03-18 00:10:14+0900 [-]                      1.0,
2016-03-18 00:10:14+0900 [-]                      1.52
2016-03-18 00:10:14+0900 [-]                   ],
2016-03-18 00:10:14+0900 [-]                   [
2016-03-18 00:10:14+0900 [-]                      "to",
2016-03-18 00:10:14+0900 [-]                      1.52,
2016-03-18 00:10:14+0900 [-]                      1.67
2016-03-18 00:10:14+0900 [-]                   ]
2016-03-18 00:10:14+0900 [-]                ],
2016-03-18 00:10:14+0900 [-]                "transcript": "several to "
2016-03-18 00:10:14+0900 [-]             }
2016-03-18 00:10:14+0900 [-]          ],
2016-03-18 00:10:14+0900 [-]          "final": false
2016-03-18 00:10:14+0900 [-]       }
2016-03-18 00:10:14+0900 [-]    ],
2016-03-18 00:10:14+0900 [-]    "result_index": 0
2016-03-18 00:10:14+0900 [-] }
2016-03-18 00:10:14+0900 [-] interim hyp: "several to "
2016-03-18 00:10:14+0900 [-] Text message received: {
2016-03-18 00:10:14+0900 [-]    "results": [
2016-03-18 00:10:14+0900 [-]       {
2016-03-18 00:10:14+0900 [-]          "alternatives": [
2016-03-18 00:10:14+0900 [-]             {
2016-03-18 00:10:14+0900 [-]                "timestamps": [
2016-03-18 00:10:14+0900 [-]                   [
2016-03-18 00:10:14+0900 [-]                      "several",
2016-03-18 00:10:14+0900 [-]                      1.0,
2016-03-18 00:10:14+0900 [-]                      1.52
2016-03-18 00:10:14+0900 [-]                   ],
2016-03-18 00:10:14+0900 [-]                   [
2016-03-18 00:10:14+0900 [-]                      "tornadoes",
2016-03-18 00:10:14+0900 [-]                      1.52,
2016-03-18 00:10:14+0900 [-]                      2.23
2016-03-18 00:10:14+0900 [-]                   ]
2016-03-18 00:10:14+0900 [-]                ],
2016-03-18 00:10:14+0900 [-]                "transcript": "several tornadoes "
2016-03-18 00:10:14+0900 [-]             }
2016-03-18 00:10:14+0900 [-]          ],
2016-03-18 00:10:14+0900 [-]          "final": false
2016-03-18 00:10:14+0900 [-]       }
2016-03-18 00:10:14+0900 [-]    ],
2016-03-18 00:10:14+0900 [-]    "result_index": 0
2016-03-18 00:10:14+0900 [-] }
(この後も大量に。。略)

なんか出た!!ワトソンとお話出来たっぽい!!!
:tada: :bowtie: :tada:

よしよし。とりあえずサンプルの音源10個では出来た。ファイルを送りつけては、その返答が帰ってくる。10個やるのにそこそこ時間掛かってる。WAVファイルのUploadにはそこまで時間掛かって無さそうだけど、返答くるのに時間かかってるみたい。Upしてから20秒位考えてる疑惑。まぁその辺は後で考えるしかないか。。(juliusの方がほぼ実時間だからまだマシなのか。。何がネック・・?)

##あとまだやりたいこと/確認したいこと

  • 日本語モードの指定方法(きっとja-JP_BroadbandModelってとこかな?)
  • JuliusからのKickを経て、WATSON用の音源録音の開始と終了のやりかた(これが全く検討つかないぞ。。)
  • テレビ&AirPlayを鳴らすスピーカーの上にマイク置いてるから、スピーカー音を逆位相とかでオフセットする方法
  • サンプルPythonスクリプトの学習
  • juliusで認識した音のファイルへの保存って出来るのか?(出来るなら、WATSONで始まって、以上で終わらせるとか簡単そうな気がする。)
  • あるいは、julius上げっぱで、マイクで録音する方法があればそれはそれでなんとかなる気がするな。
  • 地味に、上の方で作ったPythonの標準出力の仕方が良くない気がする。ファイルへのリダイレクトでクラッシュした。それもPythonのお勉強すね。

###改めて、全体の整理

  1. julius起動、随時コマンド待ち
  2. 「WATSON」の認識が発生したら、juliusを停止してマイクを開放させる
  3. WATSONへの接続を行う処理(後で作るとし、仮名watson_kicker)を実行 →今ココ。まだサンプル音源を送るモードになってるから、引数でWAVファイルを指定する方法に修正すればなんとかなるだろ。
  4. WATSONへの準備ができたら「私だ、WATSONだ。」とつぶやかせる(それか、LED光らせる。) →これLEDとか適当な音源にするか。。
  5. WATSONとお話し。その結果の処理はまたベッケンバウアーとし、後で考える。
  6. watson_kickerの処理終了時にもう一度julius起動させる(もしくは、何か全然別の監視プロセス動かしておいて、止まっててかつwatson_kickerが動いてなかったら即起動させる?)
  7. 振り出しに戻る

まだ先は長いが、徐々に出来てきた。楽しいねぇ。

続く。

#追記:2016/03/18
さて、1個ずつやっつける。

##日本語モードの指定方法:
アッサリ発見。しかも想定通り。
Mediawen/watson-go-sdk

Here is the list of Models in September, 2015:

  en-US 8000    => en-US_NarrowbandModel
  ja-JP 16000   => ja-JP_BroadbandModel
  es-ES 16000   => es-ES_BroadbandModel
  ja-JP 8000    => ja-JP_NarrowbandModel
  es-ES 8000    => es-ES_NarrowbandModel
  en-US 16000   => en-US_BroadbandModel

おっしゃ。次。

##WATSON用の録音どうするか

  • JuliusからのKickを経て、WATSON用の音源録音の開始と終了のやりかた(これが全く検討つかないぞ。。)
  • juliusで認識した音のファイルへの保存って出来るのか?(出来るなら、WATSONで始まって、以上で終わらせるとか簡単そうな気がする。)
  • あるいは、julius上げっぱで、マイクで録音する方法があればそれはそれでなんとかなる気がするな。

これらもなんとかなりそうだ。
普通に調べてみたら公式ガイドのマイク入力についてになんとも素敵なツールが載ってた。

また,Julius にはマイクから1発話を録音するプログラム adinrec が付属しています.これを用いて,Julius が取り込む音声をチェックすることもできます.

% ./adinrec/adinrec myfile.wav

上記のように実行すると,adinrec はマイクから1回分の発声をファイル myfile.wav に記録します.この adinrec は Julius 本体と同じ取り込みルーチンを使用しているので,この録音ファイルの音質がすなわちJuliusが認識しようとしている音声の音質になります.

ということは、julius止めてからこいつを動かせば良いのか。楽勝じゃん。スクリプトに組み込む。
Juliusで「WATSON」を検知したら、録音モードに変更し最大30秒間録音。
録音完了したら、ファイル名を戻してくれるのでそれをWATSONに送ればよいのだ。

##録音開始と終了タイミングがわからない対策

録音開始と録音終了のタイミングがわからないから、「ピポ」という音を何種類かiPhoneの音系アプリで作って、それを鳴らす様にした。作成したのは

  • julius起動時(ふわーっ)
  • WATSONモード録音開始時(ぴぴ)
  • WATSONモード録音終了時(ぴぴぴ)
  • WATSONモード録音エラー時(ぶぶー)

の4種類。とりあえずこれで良さそう。
何故か(コラ)、aplayだと3.5mmジャックから音出すので、AirPlayを流しながらでも上記のWAVファイルは再生可能でした。(aplayの設定でどこかにそう書いてあるんだろう。その内気が向いたら調べる。)

##WATSON呼び出しスクリプト読んだ

あと、WATSON呼び出しスクリプトをちょっと読んでみた。
どうやら、特定の録音されたWAVファイルを使いたい場合は、-inパラメータの後にSTTしたいWAVファイル名が記載してあるテキストファイルを渡せば良い。(WAVファイル名を渡さぬように。エラーになりますよ。)
なので、録音したファイル名+.txtでそのテキストファイルを作成して、中身は録音したファイル名にする。
これでいける。

##現時点でのスクリプトは以下。
使ってみたい人はCredentialsとWAVファイルはご自分でご用意下さい。(WAVはその内どこかにUpします)あと、ファイルパスなどは最初に集めてあるので適宜ご自分の環境に置き換えてください。

julius_listener.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import socket
import requests
import re
import subprocess
import shlex
import time
import datetime

julius_path = '/usr/local/bin/julius'
jconf_path = '/home/pi/julius-kits/dictation-kit-v4.3.1-linux/org.jconf'
adinrec_path = '/usr/local/bin/adinrec'
aplay_path = '/usr/bin/aplay'
rec_start_sound = '/home/pi/Music/hh2.wav'
rec_stop_sound = '/home/pi/Music/hh3.wav'
rec_error_sound = '/home/pi/Music/hh.wav'
julius_invoke_sound = '/home/pi/Music/hh4.wav'
wav_dafult_path = '/tmp'
watson_stt = '/home/pi/bin/watson/speech-to-text-websockets-python/sttClient.py'
watson_user = ''
watson_pass = ''
watson_model = 'ja-JP_BroadbandModel'
julius = None
julius_socket = None


def call_watson(sound_file):
    print 'INFO : call watson'
    ret_flg = True
    count = 0
    in_file = sound_file + '.txt'
    args = '/usr/bin/python ' + watson_stt
    args += ' -credentials ' + watson_user + ':' + watson_pass
    args += ' -model ' + watson_model
    args += ' -in ' + in_file

    f = open(in_file, 'w')
    f.write(sound_file)
    f.close()

    p = subprocess.Popen(
            shlex.split(args),
            stdin=None,
            stdout=None,
            stderr=None
        )
    while p.poll() is None:
    # while False:  #  always through
        if count >= 60:
            print 'WARN : watson: time over. abort.'
            ret_flg = False
            p.kill()
        time.sleep(0.1)
        count += 0.1
    os.remove(in_file)
    print 'INFO : call watson complete.'
    return ret_flg


def play_wav(sound_file, second=1):
    print 'INFO : play wav'
    args = aplay_path + ' ' + sound_file + ' -d ' + str(int(second))
    p = subprocess.Popen(
            shlex.split(args),
            stdin=None,
            stdout=None,
            stderr=None
        )
    time.sleep(0.5)
    print 'INFO : play wav complete.'
    return True


def invoke_julius():
    print 'INFO : invoke julius'
    args = julius_path + ' -C ' + jconf_path + ' -module '
    p = subprocess.Popen(
            shlex.split(args),
            stdin=None,
            stdout=None,
            stderr=None
        )
    print 'INFO : invoke julius complete.'
    print 'INFO : wait 2 seconds.'
    time.sleep(3.0)
    print 'INFO : invoke julius complete'
    play_wav(julius_invoke_sound)
    return p


def kill_julius(julius):
    print 'INFO : terminate julius'
    julius.kill()
    while julius.poll() is None:
        print 'INFO : wait for 0.1 sec julius\' termination'
        time.sleep(0.1)
    print 'INFO : terminate julius complete'


def rec_one_sentence():
    limit_threshold = 30  # 30 second.
    count = 0
    ret_flg = True  # default return flag is True.
    print 'INFO : start recoding a sentence. invoke adinrec.'
    play_wav(rec_start_sound)
    now = datetime.datetime.now()
    file_name = wav_dafult_path + '/'  # add last '/' in anyway.
    file_name += "watson_sentence_{0:%Y%m%d_%H%M%S}.wav".format(now)
    args = adinrec_path + ' ' + file_name
    try:
        p = subprocess.Popen(
                shlex.split(args),
                stdin=None,
                stdout=None,
                stderr=None
            )
        while p.poll() is None:
            print 'INFO : reconding...'
            if count >= limit_threshold:
                print 'WARN : recoding too long. abort.'
                ret_flg = False
                p.kill()
            time.sleep(1)
            count += 1
    except:
        print "CRIT : Unexpected error in " + sys._getframe().f_code.co_name + " :", sys.exc_info()[0]
        p.kill()
        raise Exception('error in ' + sys._getframe().f_code.co_name)
    if ret_flg:
        print 'INFO ; recording complete'
        play_wav(rec_stop_sound)
    else:
        play_wav(rec_error_sound)
    return (ret_flg, file_name, count)


def get_OS_PID(process):
    psef = 'ps -ef | grep ' + process + ' | grep -ve grep -vie python |head -1|awk \'{print($2)}\''
    if sys.version_info.major == 3:
        PID = str(subprocess.check_output(psef, shell=True), encoding='utf-8').rstrip ()
    else:
        PID = subprocess.check_output(psef, shell=True).rstrip ()
    return PID


def create_socket():
    print 'INFO : create a socket to connect julius'
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('localhost', 10500))
    print 'INFO : create a socket to connect julius complete'
    return s


def delete_socket(s):
    print 'INFO : delete a socket'
    s.close()
    print 'INFO : delete a socket complete'
    return True


def invoke_julius_set():
    julius = invoke_julius()
    julius_socket = create_socket()
    sf = julius_socket.makefile('rb')
    return (julius, julius_socket, sf)


def main():
    global julius
    global julius_socket
    julius, julius_socket, sf = invoke_julius_set()

    # ###
    # # re definition
    # ###
    reWATSON = re.compile(r'WHYPO WORD="WATSON" .* CM="(\d\.\d*)"')

    while True:
        if julius.poll() is not None:   # means , julius dead
            delete_socket(julius_socket)
            julius, julius_socket, sf = invoke_julius_set()
        else:
            line = sf.readline().decode('utf-8')
            print line
            tmp = reWATSON.search(line)
            if tmp:
                # print line
                if float(tmp.group(1)) > 0.8:
                    print 'WARN : DIE julius, call WATSON'
                    kill_julius(julius)
                    delete_socket(julius_socket)
                    rec_return, file_name, rec_time =  rec_one_sentence()
                    if rec_return:
                        # play_wav(file_name, rec_time)
                        print file_name
                        watson_return = call_watson(file_name)
                        os.remove(file_name)

    print 'WARN : while loop breaked'
    print 'INFO : exit'


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print 'Interrupted. Exit sequence start..'
        kill_julius(julius)
        delete_socket(julius_socket)
        print 'INFO : Exit sequence done.'
        sys.exit(0)

これでやっと手放しWATSONが出来るようになりました。
シーケンスとしては

  1. スクリプト実行
  2. 「ふぁーっ」って音がなったらJulius準備OK
  3. 「WATSON」と話しかける。綺麗に伝わったら
  4. 「ピピ」っと音がなるので録音開始
  5. 「電気を消して下さい」とか話してみる
  6. 「ピピピ」ってなると録音完了
  7. WATSONにファイル転送&解析中
  8. 結果ゲット!(以下)
  9. 再度「ふぁーっ」となり、julius準備OK。(3に戻る)

って感じで動いてます。よしよし。一応、下が戻ってきた結果。かなりいい感じ。

##WATSONに「電気を消して下さい」と(丁寧語で)言ってみた

2016-03-19 03:05:46+0900 [-] Text message received: {
2016-03-19 03:05:46+0900 [-]    "results": [
2016-03-19 03:05:46+0900 [-]       {
2016-03-19 03:05:46+0900 [-]          "alternatives": [
2016-03-19 03:05:46+0900 [-]             {
2016-03-19 03:05:46+0900 [-]                "word_confidence": [
2016-03-19 03:05:46+0900 [-]                   [
2016-03-19 03:05:46+0900 [-]                      "電気",
2016-03-19 03:05:46+0900 [-]                      0.772810046140911
2016-03-19 03:05:46+0900 [-]                   ],
2016-03-19 03:05:46+0900 [-]                   [
2016-03-19 03:05:46+0900 [-]                      "を",
2016-03-19 03:05:46+0900 [-]                      1.0
2016-03-19 03:05:46+0900 [-]                   ],
2016-03-19 03:05:46+0900 [-]                   [
2016-03-19 03:05:46+0900 [-]                      "消して",
2016-03-19 03:05:46+0900 [-]                      1.0
2016-03-19 03:05:46+0900 [-]                   ],
2016-03-19 03:05:46+0900 [-]                   [
2016-03-19 03:05:46+0900 [-]                      "下さい",
2016-03-19 03:05:46+0900 [-]                      0.990391454391391
2016-03-19 03:05:46+0900 [-]                   ]
2016-03-19 03:05:46+0900 [-]                ],
2016-03-19 03:05:46+0900 [-]                "confidence": 0.932,
2016-03-19 03:05:46+0900 [-]                "transcript": "電気 を 消して 下さい ",

マジバッチリじゃん。

1. julius起動、随時コマンド待ち
2. 「WATSON」の認識が発生したら、juliusを停止してマイクを開放させる
3. WATSONへの接続を行う処理(後で作るとし、仮名watson_kicker)を実行
4. WATSONへの準備ができたら「私だ、WATSONだ。」とつぶやかせる(それか、LED光らせる。) →これLEDとか適当な音源にするか。。
5. WATSONとお話し。
その結果の処理はまたベッケンバウアーとし、後で考える。
6. watson_kickerの処理終了時にもう一度julius起動させる(もしくは、何か全然別の監視プロセス動かしておいて、止まっててかつwatson_kickerが動いてなかったら即起動させる?)
7. 振り出しに戻る

とまぁ、かなり進みました。あとは何させるかとかだな。

##残課題の整理(と追加):

  • 戻ってきた結果の最終候補だけ取り出して、それを元にコマンドを作る
    • ネットラジオを適当に鳴らす
    • 音楽を止める
    • エアコンを止める
    • エアコンを任意の設定でつける
    • 天気予報を喋らせる
    • テレビを消す
    • 上記のようなイベントをスケジュールさせる
  • テレビ&AirPlayを鳴らすスピーカーの上にマイク置いてるから、スピーカー音を逆位相とかでオフセットする方法
  • サンプルPythonスクリプトの学習
  • 地味に、上の方で作ったPythonの標準出力の仕方が良くない気がする。ファイルへのリダイレクトでクラッシュした。それもPythonのお勉強すね。
  • WAVファイルではなく圧縮した形式で送る場合のパフォーマンス検証
  • 常時起動でどのくらい誤作動がおきうるのか。今のCM80%以上という閾値の妥当性を探る。
  • ログをどの程度保管するか。ハウスキープも検討。
  • 使用量の積算とか考えてみる。
  • ワトソン呼び出しの合言葉、、、「WATSON」ではなくてなんか他の考えるか。「OK Gxxgxx」とか「Hey Sxrx」みたいなの。「ジャー◯ス」とか「TA◯S」とか「CA◯E」とか「タチ◯マ」とかなんかそんなの。
  • そもそも、Watsonの他の機能を学習する

続く。

#追記:2016/03/20
細かい所をやってます。

##標準出力のリダイレクトで落ちる

  • 地味に、上の方で作ったPythonの標準出力の仕方が良くない気がする。ファイルへのリダイレクトでクラッシュした。それもPythonのお勉強すね。

エラーは以下のような感じ。

$ ./julius_listener.py > /tmp/hoge.log
Playing WAVE '/home/pi/Music/hh4.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Traceback (most recent call last):
  File "./julius_listener.py", line 211, in <module>
    main()
  File "./julius_listener.py", line 191, in main
    print line
UnicodeEncodeError: 'ascii' codec can't encode characters in position 17-18: ordinal not in range(128)

これ、ここ見た。(中身深くは理解してないけど)実行前にPYTHONIOENCODING=utf-8を渡せば良いっぽいのでそれで良しとしておこう。
これでリダイレクトは出来たけど、即時書込じゃなかったのでここ見てpython -uとすることにする。
あと、SIGTERMをもらった時にちゃんとJuliusを落としてから落ちたほうが良いのでsignal参考にして仕込む。

##パラメータチューニング
よし、あとは必要なときにちゃんと認識してくれるようにパラメータいじってみる。
[先生][sensei]のやつからはちょっといじって、今のところ以下でTryしてみている。

-quiet
-w original_words2.dic
-v model/lang_m/bccwj.60k.htkdic
-h model/phone_m/jnas-tri-3k16-gid.binhmm
-hlist model/phone_m/logicalTri
-lv 2000
-input mic
-rejectshort 800
-charconv EUC-JP UTF-8

マイクはテレビ横に置いてあって距離が有るので-lv 2000にしてみた。
感度は上がるっぽいが、その分ずっと処理する為CPUが結構高いままになってるな。100%で張り付いてるわけではないので、よく働いてくれているのなら一旦良しとしよう。
後ディクショナリファイルの単語数をゴミ除去用とおもって色々追加した為か、感度は良くなっても認識してくれないことがめちゃ増えた。
まだ、テレビを付けてるだけで「WATSON」の誤認識は幾つか出てくる。例えば全体で2225の単語検出が発生した中で、8件がWATSONだった。呼んでもないのに。
誤認識=WATSONの月使用上限が無駄に削られるという事なので結構気を使う。

###今後のチューニング方針

  • 単語数を減らす。(今2500個程度だから、数百位のイメージ。)
  • 呼び出し音声を長めにする。(某社の「Hey Siri」とかの呼び出し方は、それはそれで色々研究されて日常会話でまず出てこないだろうという事で設定したんだろう。)

引き続き見ていく。

##PulseAudio化
そろそろコマンドを作りたい所だが、ネットラジオの自動再生するにもShairportがデバイス掴んでるから現状では無理。途中にソフトウェアのミキサー入れないと、、と思って探したらPulseAudioだったらイイらしいという話。
pulseaudio を使って Raspberry Pi [から|へ] 音を飛ばす
iPodとPCの音声出力をRaspberry Piに丸投げするまでの作業ログ

ただ、この手の作業は得てして既存環境がメチャメチャになるので気をつけたい。
少なくとも今感じている事前確認事項は

  • マイクがちゃんと動かないんじゃないか疑惑。
  • これまでやったAudio関係設定をおさらいしとく
  • OSバックアップ取っておこう(まだこの作業がめんどい。商用Unixは楽だな。。。)

というあたり。
上から確認したら早速いいのあった。
How can I get line-in microphone working with Skype?
コレやっといたほうが良さそう。一応やらなくてもいいかもテストしないと。

##単語リストチューニング(3/22)
現在、WATSONの読み方3個と2500個のゴミ拾い用単語の合わせて2503個でほったらかしてみている。テレビの音など結構余計な勘違いが有るための仮措置。
それで、3/22の全体の単語検知が4989個で、うちWATSONは13個。ちなみに、WATSONとは一度もつぶやいていない。全て誤検知。一応、「WATSON」モードでは1回最大30秒まで録音してWATSONに聞きに行かせるので、13回×30=6分30秒。
一応、1000分/月まで無料なので、一日30分迄は許容値だな。回数にして60回。そんなに聞くことも無いから良いけど、無駄撃ちが多いのはイケてない。なんとかしたい。

  • 最低限の有用なゴミ拾い用単語だけを登録しておく
  • WATSON誤検知を減らす

を改めて目標としたい。

この日のトップ20を出してみる。

$ grep "WHYPO W" julius_listener_20160322.log |awk -F\" '{print $2}'|sort |uniq -c|sort -rn |head -20
    179 んう
    135 ぬぬ
    100 うん
     95 うう
     94 ほう
     92 んん
     87 ねぬ
     64 んふ
     63 むん
     61 ねん
     59 なん
     59 うふ
     57 にぬ
     55 ぬう
     54 ぬん
     53 ぬふ
     52 ええ
     51 うほ
     46 おほ
     44 んい

ふーん。uとnが多いね。低音のブーンとかをそう捉えちゃうのだろうか。ローパスフィルター噛ませば良いのかな。そんな簡単じゃないか?
また、検知した単語の種類としては682種類だったから1821個の辞書登録は無駄っぽい。それらは省いておいたほうがjuliusの処理としてはきっと軽くなるはず。
諸々、もう少し様子見しよう。

#2016/03/24追記
3日たったので再度検証。
まず、3日通しての多かった単語、トップ 100。

$ grep "WHYPO W" julius_listener_2016032*.log |awk -F\" '{print $2}'|sort |uniq -c|sort -rn |head -100
    479 んう
    463 ぬぬ
    323 うん
    278 うう
    273 んん
    266 ほう
    218 ねぬ
    212 にぬ
    193 んふ
    187 むん
    177 ぬう
    157 ええ
    154 ねん
    154 ぬん
    152 うふ
    151 なん
    150 あん
    139 うほ
    139 ああ
    129 おほ
    128 ぬふ
    125 ぬほ
    125 ぬな
    124 なぬ
    121 おう
    119 ねほ
    116 んほ
    114 んへ
    109 えう
    108 ねふ
    104 おん
    103 ふほ
    100 ねへ
     88 むほ
     88 ふふ
     88 えん
     82 いん
     81 ほん
     79 にん
     77 んい
     77 ぬえ
     76 んむ
     73 ねう
     71 へん
     71 のぬ
     70 はん
     69 ぬね
     69 ぬあ
     68 たぬ
     67 んあ
     65 ほぬ
     65 のん
     64 んお
     64 ほふ
     64 たん
     63 のほ
     60 ぬの
     59 もん
     58 いい
     54 はは
     54 うぬ
     53 んぬ
     53 むふ
     51 ねは
     49 うあ
     47 ひん
     47 えい
     45 なえ
     45 えへ
     44 ふん
     44 なや
     41 んく
     41 ほほ
     40 ひい
     40 ねな
     39 ねに
     39 たえ
     38 ぬに
     38 あは
     37 もう
     37 へぬ
     37 へう
     37 なふ
     36 なな
     36 WATSON
     35 ふぬ
     35 にふ
     34 はへ
     33 もほ
     33 へほ
     33 へい
     32 へふ
     32 ひふ
     32 おふ
     32 えふ
     30 んひ
     30 よぬ
     30 へへ
     30 へえ
     28 んは


そして、最初の1文字のみでの出現数

$ grep "WHYPO W" julius_listener_2016032*.log |awk -F\" '{print $2}'|cut -c 1-3 |sort |uniq -c |perl -nle '$A=$_;$A=~/^\s
*(\d*) .*$/;print "$_"."*"x($A/30)'|sort -rn
   1770 ん***********************************************************
   1742 ぬ**********************************************************
   1126 う*************************************
   1011 ね*********************************
    656 な*********************
    634 ほ*********************
    592 え*******************
    540 に******************
    485 む****************
    481 あ****************
    462 お***************
    419 た*************
    397 ふ*************
    356 へ***********
    343 の***********
    295 は*********
    245 い********
    219 ひ*******
    198 も******
    175 わ*****
    135 か****
    113 て***
    112 や***
     76 ま**
     75 つ**
     72 め**
     69 よ**
     64 す**
     62 と**
     58 し*
     50 け*
     48 せ*
     45 く*
     37 ゆ*
     36 WAT*
     32 き*
     22 み
     20 さ
     15 こ
      9 ち
      9 そ

ということで、「こ」「ち」「そ」から始まる呼び出しコマンドを検討するかなぁ。。

#2016/03/26追記
(単語数を減らした効果の測定ですが、そもそもnmonとか取って無かったからCPUリソース的な改善があったかわからんばい。検知された単語の出現数集計してみて、WATSONが最下位に来ていたのでその点は良しと評価する事にする)

##PulseAudio化を真面目に検討します。
恐ろしいのでまずはBackupしました。Mac経由でDDしたファイルをgzipしてNASに入れておく。DDのブロックサイズは1Mで、8GBのSDカードで370秒程度かかりました。圧縮したサイズは2Gちょい。よし、これで怖くなかろーもん。

##PulseAudio要件定義
想定する使い方を以下で整理してみる。上からアプリケーション、ミドル(PulseAudio)、デバイスとなる。

Shairport  Aplay(wav)  (mpd?)  julius  WATSON
   |Out        |Out      |Out    |In     |In
   ---------------------------------------
   |              Pulse Audio            |
   ---------------------------------------
                       |
                   ALSA(/OSS?)

入出力としては、Shairport経由でAirPlay(iPhoneもしくはMac)、JuliusおよびWatsonコントロールシェルからのWAV等の出力、ネットラジオの再生(MPDとか?)、そしてjuliusとWATSONのマイク入力。(あとシステムの元々の音とかあるのかな。)

これらについて、
(1)juliusで呼び出しコマンド検知後に、Shairportとmpdなど、他の出力レベルを通常の20%程度に下げる。
(2)Aplayでピポ音を出す
(3)静かな状態でWATSON用の録音
(4)終わったらAplayでピポポ音を出す
(5)出力レベルを全て元通りにする。
というような使い方ができればそれっぽい。
なので、基本的にはコマンド(CLI)でミキサー操作が出来る必要が有る。基本PulseAudioはGUIツールっぽいので初期セットアップ位はGUIでも良い。

##下調べ
どうやらarchlinuxのPulseAudioが詳しい。
あと、Ubuntu Forumsのhow to control PulseAudio in command line?も大事そう。

導入が必要と思われるコンポーネントについて、本体のpulseaudioは必須でしょう。
Ubuntuのやつとしては、コマンド操作用でpactlpacmdを使っている用に見える。(archの方見るとどうやらデフォルトで入るモノっぽいぞ)
archlinuxの方にのってるponymix と pamixerはどうもapt-getできなそう。

よし、とりあえず方針としては本体だけ入れて、出力と録音が出来るか調査する事としよう。

しかし、、Raspi 3買っちゃおうかな。でもCPUが80度になるとかちょっと頑張りすぎでしょ。2.5Aて。やっぱりZeroほしいな。Zero沢山動かしてみたいな。

続く。

#2016/03/30追記
Raspi 3買っちゃいました。欲しい人は、Raspberry Pi3が通販で買える店(技適についても)とかご参考下さい。3のセットアップは別記事で載せるです。

さて。PulseAudio化を進めます。

##インストール
さしあたり必要そうなのは、上記でもちょっと調べましたがpulseaudioとpacmdとpactlと思われます。その内pactlとpacmdはpulseaudio-utilsに含まれるです。
かつ、そのpulseaudio-utilsもpulseaudio入れると自動で入るので、やっぱりpulseaudioをまず入れれば大丈夫。

$ sudo apt-get install pulseaudio
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  python-chardet python-colorama python-distlib python-html5lib python-ndg-httpsclient python-openssl
  python-pkg-resources python-pyasn1 python-requests python-setuptools python-six python-urllib3 python-wheel
Use 'apt-get autoremove' to remove them.
The following extra packages will be installed:
  libasound2-plugins libpulsedsp libspeexdsp1 libwebrtc-audio-processing-0 pulseaudio-module-x11
  pulseaudio-utils rtkit
Suggested packages:
  pavumeter pavucontrol paman paprefs
The following NEW packages will be installed:
  libasound2-plugins libpulsedsp libspeexdsp1 libwebrtc-audio-processing-0 pulseaudio pulseaudio-module-x11
  pulseaudio-utils rtkit
0 upgraded, 8 newly installed, 0 to remove and 0 not upgraded.
Need to get 1242 kB of archives.
After this operation, 5522 kB of additional disk space will be used.
Do you want to continue? [Y/n] y

なんかゴチャゴチャ入るっぽいですが、yしましょう。あと、

Suggested packages:
  pavumeter pavucontrol paman paprefs

とあるけど、こいつらは全部GUIツールなので不要?まぁあってもいいけどCUIで行くと思うのであんまり使わない気がする。

Do you want to continue? [Y/n] y
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main libspeexdsp1 armhf 1.2~rc1.2-1 [42.9 kB]
Get:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main libasound2-plugins armhf 1.0.28-1+b1 [62.2 kB]
Get:3 http://mirrordirector.raspbian.org/raspbian/ jessie/main libpulsedsp armhf 5.0-13 [36.4 kB]
Get:4 http://mirrordirector.raspbian.org/raspbian/ jessie/main libwebrtc-audio-processing-0 armhf 0.1-3 [81.6 kB]
Get:5 http://mirrordirector.raspbian.org/raspbian/ jessie/main pulseaudio-utils armhf 5.0-13 [67.5 kB]
Get:6 http://mirrordirector.raspbian.org/raspbian/ jessie/main pulseaudio armhf 5.0-13 [892 kB]
Get:7 http://mirrordirector.raspbian.org/raspbian/ jessie/main pulseaudio-module-x11 armhf 5.0-13 [31.7 kB]
Get:8 http://mirrordirector.raspbian.org/raspbian/ jessie/main rtkit armhf 0.11-2 [27.9 kB]
Fetched 1242 kB in 51s (23.9 kB/s)
Selecting previously unselected package libspeexdsp1:armhf.
(Reading database ... 120859 files and directories currently installed.)
Preparing to unpack .../libspeexdsp1_1.2~rc1.2-1_armhf.deb ...
Unpacking libspeexdsp1:armhf (1.2~rc1.2-1) ...
Selecting previously unselected package libasound2-plugins:armhf.
Preparing to unpack .../libasound2-plugins_1.0.28-1+b1_armhf.deb ...
Unpacking libasound2-plugins:armhf (1.0.28-1+b1) ...
Selecting previously unselected package libpulsedsp:armhf.
Preparing to unpack .../libpulsedsp_5.0-13_armhf.deb ...
Unpacking libpulsedsp:armhf (5.0-13) ...
Selecting previously unselected package libwebrtc-audio-processing-0:armhf.
Preparing to unpack .../libwebrtc-audio-processing-0_0.1-3_armhf.deb ...
Unpacking libwebrtc-audio-processing-0:armhf (0.1-3) ...
Selecting previously unselected package pulseaudio-utils.
Preparing to unpack .../pulseaudio-utils_5.0-13_armhf.deb ...
Unpacking pulseaudio-utils (5.0-13) ...
Selecting previously unselected package pulseaudio.
Preparing to unpack .../pulseaudio_5.0-13_armhf.deb ...
Unpacking pulseaudio (5.0-13) ...
Selecting previously unselected package pulseaudio-module-x11.
Preparing to unpack .../pulseaudio-module-x11_5.0-13_armhf.deb ...
Unpacking pulseaudio-module-x11 (5.0-13) ...
Selecting previously unselected package rtkit.
Preparing to unpack .../rtkit_0.11-2_armhf.deb ...
Unpacking rtkit (0.11-2) ...
Processing triggers for man-db (2.7.0.2-5) ...
Processing triggers for dbus (1.8.20-0+deb8u1) ...
Setting up libspeexdsp1:armhf (1.2~rc1.2-1) ...
Setting up libasound2-plugins:armhf (1.0.28-1+b1) ...
Setting up libpulsedsp:armhf (5.0-13) ...
Setting up libwebrtc-audio-processing-0:armhf (0.1-3) ...
Setting up pulseaudio-utils (5.0-13) ...
Setting up pulseaudio (5.0-13) ...
Adding user pulse to group audio
Setting up pulseaudio-module-x11 (5.0-13) ...
Setting up rtkit (0.11-2) ...
Processing triggers for libc-bin (2.19-18+deb8u1) ...
Processing triggers for dbus (1.8.20-0+deb8u1) ...

よし、入った。
あとやっぱり

$ sudo apt-get install pulseaudio-module-zeroconf

も入れておく。

##まずはインストール直後のテスト
現状裏でShairPort(AirPlayサーバー)が動いているけど、再生してどうなるか。
なる!普通に鳴る。そうなんだね。ホッとした。

当たり前だ。pulseaudio動いてないんだもん。アホか :stuck_out_tongue_closed_eyes:

##設定ファイル系の確認
/etc/pulse/default.paはそのまんまの方が良いとかarchさんに書いてあったけど、オリジナル保管してあるしいじってみる。

/etc/pulse/default.pa
###以下を追加。第三オクテットXXXは適宜入れて下さい。
load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.XXX.0/24
###以下はコメントアウトを外した。Avahi用。
load-module module-zeroconf-publish
###以下はコメントアウトした。サスペンドさせない。
#load-module module-suspend-on-idle

Shairport対策

~/.asoundrc
pcm.pluse {
	type pulse
}
ctl.pulse {
	type pulse
}
/etc/asound.conf
pcm.!default {
    type pulse
    # If defaults.namehint.showall is set to off in alsa.conf, then this is
    # necessary to make this pcm show up in the list returned by
    # snd_device_name_hint or aplay -L
    hint.description "Default Audio Device"
}
ctl.!default {
    type pulse
}

その他、ラズパイ用。(オススメらしい。)

/etc/pulse/daemon.conf
resample-method = trivial
default-sample-rate = 44100 ##デフォルトだからそのままでも良いか。。。
alternate-sample-rate = 48000 ##デフォルトだからそのままでも良いか。。。

これで、デーモン化コマンドを叩く。

pulseaudio -D

さて、OS再起動してみるか。

$ ps -ef|grep pulse
pi        1277     1  2 00:53 ?        00:00:00 /usr/bin/pulseaudio --start
pi        1328     1  0 00:53 ?        00:00:00 /bin/sh /usr/bin/start-pulseaudio-x11

よし、動いてる。

##再び稼動確認
ShairPort経由でiPhoneから音を出してみる。
音が流れた!わーい。

CPU的にも全然食わない。よし。

この後の確認ポイント:

  • マイク入力テスト(Juliusと録音)
  • 再起動時(実際は停止時)のスピーカーボン回避がちゃんと動くか
  • MPDのインストールとpulseの利用
  • Aplayからpulseの利用
  • ソースとシンクの音量調節をコマンドで実施

まだまだ続くぞー!

#2016/04/01追記

続きから。上2つはすぐに確認とれたので良しとする。

  • マイク入力テスト(Juliusと録音)
  • 再起動時(実際は停止時)のスピーカーボン回避がちゃんと動くか
  • MPDのインストールとpulseの利用
  • Aplayからpulseの利用
  • ソースとシンクの音量調節をコマンドで実施

先にAplayで鳴らしているWAVをPulse経由で利用するぞ。

。。。CUIで頑張ろうと思っていたが、GUI使います。paprefsとpavucontrolを入れます。

。。。なんか色々やったら壊れました。Shairportから音ならない。。くそう。
リストアして再チャレンジするか。。。

続く。。。

#参考リンク

関数 - php プログラマのための Python チュートリアル
How can I get line-in microphone working with Skype?
pulseaudio を使って Raspberry Pi [から|へ] 音を飛ばす
iPodとPCの音声出力をRaspberry Piに丸投げするまでの作業ログ

26
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?