Raspberry Pi音声認識・合成 クリスマスソング自動再生やってみた DIY 後半
クリスマスソングという事でクリスマス期間中に記事を書きたかったのですが、時間が間に合いませんでしたTT。少し遅れてしまいましたが書きます。
前回の記事はこちら
前回は音声の自動認識までは出来ましたので、残りは次のとおりです。
- MP3を再生できるようにする(mpg321)
- AquesTalkインストール
- 音声自動認識、音楽再生プログラムの作成 (Julius + AquesTalk + mpg321)
目次
MP3を再生できるようにする(mpg321)
mpg321というソフトを使いました。名前からするとmpegの1~3までは再生できるという事なのでしょうか。。。
sudo apt-get install mpg321
再生はこんな感じでコマンド打つと再生されます。停止はctrl + cで停止します。
mpg321 /home/pi/work/music/Wham.mp mpg321 /home/pi/work/music/PaulMcCartney.mp3
これで曲を再生する準備が整いました。
AquesTalkインストール
今回は対話型のものにしたいので、AquesTalkをインストールします。これもインストールするだけで即時使用できます。
こちらの記事を参考にインストールをしました。特別な作業はなくダウンロードしたtarファイルを展開するだけです。
tar -zxvf aquestalkpi-20130827.tgz cd aquestalkpi/ chmod a+x AquesTalkPi
これで再生できますので、試してみます。
./AquesTalkPi "嘘ではありませーん" | aplay ./AquesTalkPi "まじかよーやる気なくした" | aplay
おおおお。声が出たーーー。これまた感動です。いかにもロボットという感じの声ですが聞き取りやすいです。
音声自動認識、音楽再生プログラムの作成 (Julius + AquesTalk + mpg321)
これでツール類の準備が終わったのでプログラムの作成です。
動作フローとしては、こんなイメージです。
- juliusをモジュールモード(サーバモード)で起動
- juliusサーバに対してクライアントプログラムからSOCKET通信
- 使用者が曲名をラズベリーパイに対して発する。
- 曲名が音声入力されたらjuliusサーバが解釈
- クライアントプログラムはjuliusサーバの応答を解釈して音声入力に応じた曲を再生させます。
今回の例だと使用者が「マライヤ」と発した場合にはマライヤの曲がかかるイメージです。 - 同様にユーザが「止まれ!」と発言したら曲を停止させます。
juliusをモジュールモード(サーバモード)で起動
julius -C コマンドに-module引数を付けて実行します。下記のようにコンソールに出力され、クライアントからの接続を待機します。
julius -C ~/julius-kits/dictation-kit-v4.3.1-linux/word.jconf -module STAT: include config: /home/pi/julius-kits/dictation-kit-v4.3.1-linux/word.jconf STAT: jconf successfully finalized STAT: *** loading AM00 _default (中略) Stat: server-client: socket ready as server /////////////////////////////// /// Module mode ready /// waiting client at 10500 ///////////////////////////////
juliusサーバに対してクライアントプログラムからSOCKET通信
ここまで来てやっとプログラミングができます。長かった。とは言っても最初からサンプルがあるので特にすることはないです。。これがサンプルプログラムです。
~/julius-4.3.1/jclient-perl/jclient.pl
音声入力の結果が返ってくるところまで書いてるので、あとは少し改造します。
cp -p ~/julius-4.3.1/jclient-perl/jclient_test2.pl ~/julius-4.3.1/jclient-perl/jclient_music.pl
内容はざっくりですがこんな感じです。汚いプログラムですみません。jclient_music.pl
#! /usr/bin/perl use strict; use IO::Socket; use IO::Select; my $host = "localhost"; my $port = 10500; print STDERR "$host($port) に接続します\n"; # Socketを生成して接続 my $socket; while(!$socket){ $socket = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port, Proto => 'tcp', ); if (!$socket){ printf STDERR "$host($port) の接続に失敗しました\n"; printf STDERR "再接続を試みます\n"; sleep 10; } } print STDERR "$host($port) に接続しました\n"; # バッファリングをしない $| = 1; my($old) = select($socket); $| = 1; select($old); # Selecterを生成 my $selecter = IO::Select->new; $selecter->add($socket); $selecter->add(\*STDIN); # 各種コマンド my $command_start = '~/aquestalkpi/AquesTalkPi "みゅうじっく 、スタート!" | aplay'; my $command_stop = '~/aquestalkpi/AquesTalkPi "みゅうじっく 、ストップ!" | aplay'; my $command_speak = '~/aquestalkpi/AquesTalkPi "、、えー、、、、hogehoge、を再生します。" | aplay'; my $command_retry = '~/aquestalkpi/AquesTalkPi "えっ? 、なんだって?" | aplay'; my $command_running = '~/aquestalkpi/AquesTalkPi "音楽再生中だよ。静かにしてね" | aplay'; my $command_play_mp3 = 'mpg321 /home/pi/work/music/musicname.mp3 &'; my $command_kill_mp3 = 'perl /home/pi/julius-4.3.1/jclient-perl/killmp3.pl'; my $musicPlay = 0;#音楽再生フラグ。2重起動防止 while(1){ my ($active_socks) = IO::Select->select($selecter, undef, undef, undef); foreach my $sock (@{$active_socks}){ # Juliusからの出力を表示 if ($sock == $socket){ while(<$socket>){ print $_; if ($_ =~/.*<WHYPO WORD=\"([^\"]{1,})\"/) { print "$1\n"; my $callName = $1; print "$callName\n"; my $result = ""; my $exeCom; my $exeMusic; if ( ($callName eq "マライヤ" || $callName eq "ポールマッカートニー" || $callName eq "ワム" || $callName eq "アクロスザスカイ" || $callName eq "ハチ" || $callName eq "パパパパ" ) && $musicPlay == 0) { $exeCom = $command_speak; $exeMusic = $command_play_mp3; $exeCom =~ s/hogehoge/$callName/; $exeMusic =~ s/musicname/$callName/; $result = system ($exeCom); $result = system ($command_start); $result = system ($exeMusic); $musicPlay = 1; } elsif ($callName eq "とまれ") { $result = system ($command_stop); $result = system ($command_kill_mp3); $musicPlay = 0; } elsif ($musicPlay == 1 ) { $result = system ($command_running); } else { $exeCom = $command_retry; $result = system ($exeCom); } } last if(/^\./); } # 標準入力をJuliusに送信 }else{ my $input = <STDIN>; # 小文字を大文字に変換 $input =~ tr/a-z/A-Z/d; print $socket $input; } } }
このプログラムをjuliusサーバが起動した状態で実行します。すると接続できます。
perl ~/julius-4.3.1/jclient-perl/jclient_music.pl localhost(10500) に接続します localhost(10500) に接続しました <STARTPROC/>
この状態ですでに音声入力ができる状態になっているのでアーティスト名を発します。 例として「マライヤ」と発します。すると下記のようにログが出て曲が再生されます。
<RECOGOUT> <SHYPO RANK="1" SCORE="-2464.720703" GRAM="0"> <WHYPO WORD="マライヤ" CLASSID="マライヤ" PHONE="silB m a r a i y a silE" CM="0.967"/> マライヤ 省略 Playing MPEG stream from マライヤ.mp3 ... MPEG 1.0 layer III, 128 kbit/s, 44100 Hz joint-stereo <INPUT STATUS="STARTREC" TIME="1451204574"/>
曲は/home/pi/work/music/配下にある「名前.mp3」の曲をmpg321が再生します。 実行する時にいきなり音楽がかかるとびっくりするのでAquesTalkPiを使いロボットに返事をさせるようにしています。
使用例)
使用者「マライヤ!」
プログラム「マライヤを再生します。」
プログラム「ミュージックスタート」
~mpg321が曲を再生 ♪♪♪♪♪♪
使用者「とまれ!」
プログラム「ミュージックストップ」
~mpg321の強制停止プログラムを動作
みたいな感じです。アーティストではない単語が出てきた時には「え?なんだって?」っと返却します。。。曲の再生中は「静かにしてね。。」みたいな感じです。Bluetoothのスピーカーを使って曲を再生させましたが曲が鳴った時は嬉しかったです。スピーカーの使い方はこちら。
曲の停止方法がよくわからなかったので、こんな感じでプロセスをkillするプログラムを用意しておきました。
perl /home/pi/julius-4.3.1/jclient-perl/killmp3.pl
#! /usr/bin/perl use strict; use POSIX qw( SIGKILL ); my $procname= "mpg321"; my $proxmax = "10"; my $proccnt = 0; my @process; eval { @process = `ps -ef`; @process = grep { /$procname/ } @process; }; foreach my $process (@process){ if ($process =~/[a-z]{1,}\s{1,}([0-9]{1,})\s{1,}/) { kill(SIGKILL, $1); } }
以上です。
実際に使ってみた感想としては、juliusの音声認識の精度は素晴らしいです。マイクの近くからしか声が拾えないという難点はありますが、精度としては8割以上は認識してくれる感じでした。女性より男性の声の方が認識率は若干高いと感じました。juliusの戻り値にヒット率みたいな値を持ってそうなのでチューニングすれば精もっと度ものが作れそうです。
近年はIOTが大きなワードして取り上げられておりますが、最近は各種認識装置やソフトウェアが優れているので手軽にIOTを体験、DIYできる時代が来たんだなーっと実感しました。
これで寂しいクリスマスを迎えても話が相手がいるので大丈夫ですねw
メリークリスマスー!