気の向くままに辿るIT/ICT/IoT
webzoit.net
IoT・電子工作

Raspberry Pi 3 Model B+自作スマートスピーカーにラジオを追加

ウェブ造ホーム前へ次へ
サイト内検索
カスタム検索
Raspberry Piって?

Raspberry Pi 3 Model B+自作スマートスピーカーにラジオを追加

Raspberry Pi 3 Model B+自作スマートスピーカーにラジオを追加

2018/12/20

 以前、作って運用しつつもブラッシュアップ中のRaspberry Pi 3 Model B+とJuliusOpen JTalkベースの自作スマートスピーカーがあります。

 主な機能は、

 尚、ラズパイ用ACアダプタを挿したスイッチ付きコンセントでのON/OFFとは別にラズパイ用boot/reboot/shutdown物理ボタン付き。

 ちなみに便利なのでラズパイだけでなく、PC/Debianにも自作スマートスピーカー機能を搭載しています。

radiko/らじるらじる/サイマルラジオ/ICECAST等ラジオ機能の追加

 今回は、更にradiko、らじるらじる、サイマルラジオ、ICECAST、オーストラリア放送ABC Newsと英国放送BBC World News等の無料ラジオ局を声で再生、停止させる機能を加えることにしました。

 BGMとしてなど音楽をかけ流ししたい場合にもラジコでラジオNIKKEI第2(RN2)を選局すれば、平日8時〜21時30分の間、ほぼトークなしの邦楽・洋楽を聴けますし、ICECASTでジャズやクラシック、ロック等々任意の放送局を複数選んでおけば、基本、365日終日、いつでも音楽を聴けるようになるでしょう。

 後述の通り、他にも事前にいくつかやることはあるにせよ、メインの作業となる部分は、こんな感じであり、先人のおかげもあって、それほど難解というわけではありません。

 それぞれ前回作った自作スマートスピーカ用のスクリプトvoicerecive.plへの追記。

追加インストール

$ sudo apt install mplayer rtmpdump swftools libxml2-utils
$

 今回の実装に伴い、Debianを使っている自身の環境では、mplayer、rtmpdump、swfextractを得るべく、swftools、xmllintを得るべく、libxml2-utilsパッケージを追加インストールしておく必要がありました。

事前準備

$ curl -O http://radiko.jp/apps/js/flash/myplayer-release.swf
$ swfextract myplayer-release.swf -b 12 -o authkey.png
$ vi sound/radio/radiko.sh
...
#wkdir='/var/tmp'
wkdir="$HOME/var/tmp"
...
$ mkdir -p ~/var/tmp/
$ cp authkey.png ~/var/tmp/
$

 また、radikoタイムフリー保存方法にあるようにradiko.sh用に、予め、myplayer-release.swfをダウンロード、swfextractコマンドからauthkey.pngと命名した認証ファイルを抽出、radiko.shwkdirに置いておく必要がありました。

 このwkdir、デフォルトの/var/tmpにするならchownなりしておく必要があるでしょうが、/home/xxx/以下にしておく方が、無難と判断、radiko.shを編集し、今回はそうしました。

 尚、radiko.sh再生時のオプション[-p]の引数となる値は、radiko番組表xml取得APIにあるように全放送局一覧から得られる内の[id]タグの値。

Juliusオリジナル辞書の編集

$ awk '{print $1}' mysmartspeaker.list > dummy.list
$ vi dummy.list
TBSラジオ    てぃーびーえすらじお
ニッポン放送        にっぽんほうそう
ラジオ日本    らじおにっぽん
ラジオNIKKEI第1    にっけいだいいち
ラジオNIKKEI第2      にっけいだいに
NACK5        なっくふぁいぶ
$ iconv -f utf8 -t eucjp dummy.list | ../../../julius/julius-kits/grammar-kit/bin/linux/yomi2voca.pl > iconv -f eucjp -t utf8 dummy.list.out.utf8
$ cat dummy.list.out.utf8 >> mysmartspeaker.list
$ vi mysmartspeaker.list
$ ...
$ iconv -f utf8 -t eucjp mysmartspeaker.list > mysmartspeaker.eucjp
$

 独自辞書に追記した際、[TBS]ラジオ、[ニッポン]放送、ラジオ[日本]、[日経]、[NACK]5のローマ字表記が思い当たらず、適当に書いたら、やはり、エラーになりました。

 そこで、これらのみ抜き出してタブ区切りで平仮名をあてたファイルを作り、実行権限を与えた(sudo chmod u+xした)yomi2voca.plで変換、出力された結果を反映させたところ、うまく機能しました。(そのためのスクリプトなので当然ですが。)

$ cat config_file
-w mysmartspeaker.eucjp
-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
-input alsa
-rejectshort 600
-charconv euc-jp utf8
-lv 1500
$

 後にjuliusをモジュールモード(-module)で起動する際に-Cオプションで指定するdictation kit(ver 4.4)の構成ファイルconfig_fileにおいて独自に作った辞書は、-wオプション付きで指定できます。

 この中でmysmartspeaker.eucjp以外の言語モデルや音響モデルは、Julius標準のものを使っているだけ。

 尚、input値については、ラズパイで使えるよう前回追記の通り、OSSではなく、ALSAを使うべく、一連の作業を行ないつつ、明示的に./configure --with-mictype=alsaしたので-input alsaとしてあります。

スマートスピーカー応答スクリプトの編集

$ pwd
/home/xxx/sound/
$ vi ./voicerecieve.pl
#!/usr/bin/env perl
...
      # 【radiko】
      when("ラジオ日本"){
       system("/home/xxx/sound/radio/radiko.sh -p JORF &");
      }
      when("JWAVE"){
       system("/home/xxx/sound/radio/radiko.sh -p FMJ &");
      }
      when("放送大学"){
       system("/home/xxx/sound/radio/radiko.sh -p HOUSOU-DAIGAKU &");
      }
 ...
      # 【らじるらじる by radiko】
      when("東京NHK第1"){
      # system("mplayer -playlist https://nhkradioakr1-i.akamaihd.net/hls/live/511633/1-r1/1-r1-01.m3u8 &");
       system("/home/xxx/sound/radio/radiko.sh -p JOAK &");
      }
      when("東京NHK第2"){
      # system("mplayer -playlist https://nhkradioakr1-i.akamaihd.net/hls/live/511633/1-r1/1-r1-01.m3u8 &");
       system("/home/xxx/sound/radio/radiko.sh -p JOAB &");
      }
      when("東京NHK第 FM"){
      # system("mplayer -playlist ://nhkradioakfm-i.akamaihd.net/hls/live/512290/1-fm/1-fm-01.m3u8 &");
       system("/home/xxx/sound/radio/radiko.sh -p JOAK-FM &");
      }
 ...
      # 【ICECAST】
      when("JAZZ"){
       system("mplayer -playlist http://dir.xiph.org/listen/212037/listen.m3u &");
      }
 ...
      # 【ラジオ停止】
      when("ラジオ停止"){
       system("/home/xxx/sound/jsay ラジオを停止します");
       system("pkill -f rtmpdump");
       system("pkill -f mplayer");
      }
 ...
 

 これらを踏まえ、自作スマートスピーカー用スクリプトvoicerecieve.plに適宜追記。

 ラジオを止める場合、rtmpdumpしているradiko.sh、mplayer、何れで再生していようがいまいが、両方とも強制的にpkillすることにしました。

 コマンドラインから実行した限りにおいては、rtmpdumpを止めても即終了せず、タイムラグがありましたが、スマートスピーカー実装後は、なぜか、このタイムラグがなくなりました。

 尚、らじる★らじるは、mplayerで再生すると10秒以内程度の周期で更新がかかり、その都度、途切れるが、幸い、試験的で年度をまたぐと若干聴取できない期間が生じがちな模様もradiko対応している為、これを利用するとよいでしょう。

 なぜか全国版にはNHK第2がなかったので先のリンクから、地域別、例えば東京ならJP13.xmlにある通り、NHK第1なら[JOAK]、NHK第2なら[JOAB]、NHK FMなら[JOAK-FM]というidで先の例のように[radiko.sh -p]の引数としてこれらidを渡してradikoで再生すれば、途切れることなく、良好に聴取できます。

$ pwd
/home/xxx/julius/julius-kits/dictation-kit-v4.4
$ julius -C config_file -C am-gmm.jconf -module
...

 あとは、検証中なら、例えば、端末からこのようにJuliusをモジュールモードで実行しておき...

$ pwd
/home/xxx/sound/
$ ./voicerecieve.pl
...

 他の端末でスマートスピーカ用スクリプトを実行...

 スクリプトの手順に沿って独自辞書に登録したワードを発すれば、今回新たに追加したラジオを再生できるはず。

注意

 自動起動する際などroot起動の可能性を考えると$HOMEなどをも使わず、[/home/xxx/sound/jsay]などは、省略せずに[/home/xxx/sound/jsay]などとした方が賢明かと。

備考

 とても良好に機能しています。

 ただ、課題がなくもない。

 1つは、ラジオ局を複数再生できてしまい、その場合、混線状態となること。

 ただ、日付時刻や天気などラジオ局再生中でも確認できるのは、特に音楽鑑賞中は、意外と重宝する為、ラジオ局再生が複数重ならないように何か工夫が必要かとも思いますが、停止をかければ全て止まるし、誤動作でない限りは、そもそも再生中に重複指示しなければよく、運用でなんとかなるとも言える。

 これは課題というのか、現状、スピーカースクリプトにラジオの音量調整機能はなく、必要ならスピーカーZ120のボリューム調整つまみでなら調整できる状態ですが、ソフト的な音量調整を実装するか、スピーカーのつまみでの運用を前提にするか...。

 WiFi経由で赤外線リモコン操作するテレビなどについては、音量調整も搭載しますが、スマートスピーカ自体の音量も声で操作できた方がいいのかな...。

追記

$ pwd
/home/xxx/sound/
$ vi ./voicerecieve.pl
#!/usr/bin/env perl
...
      when("ABCAustraliaNews"){
       system("/home/xxx/sound/jsay ABCニュース");
       system("mplayer -playlist http://abc.net.au/res/streaming/audio/aac/news_radio.pls &");
      }
      when("BBCNews"){
       system("/home/xxx/sound/jsay BBCワールドニュース");
       system("mplayer http://bbcwssc.ic.llnwd.net/stream/bbcwssc_mp1_ws-eieuk &");
      }
 ...
 
2019/01/15

 Mplayer, Radio Station, TUNE-IN Plugin available?を参考に英語系ニュースラジオ・ライブとしてオーストラリア放送ABC Newsと英国放送BBC World Newsを追加。

 何れもmplayerでいけますが、BBCの方は、-playlistオプションは不要な点に注意。

2019/01/24

 UPnP/DLNAメディア再生機能を追加した際、ラジオや音楽の再生が複数被らないようラジオ停止操作をstop_radio.shにまとめました。

 他のラジオでは、このスクリプトをバックグラウンド起動させてから再生スクリプトを実行しても何ら問題ありませんが、BBCニュース、ABCニュース再生前に置く時は、なぜか、フォアグラウンド起動しないと[can not connect socket]、LIRCがどうのといった予期せぬエラーになるので注意。

 stop_radio.shを実行しない手もあるにはありますが、重複再生を回避できない為、本末転倒。

 ちなみにLIRCに関するエラー自体は、mplayer.conf内で設定したり、-nolircオプション付きでmplayerを実行すれば回避できるのですが、それだけだと肝心のニュースが再生されない。

$ cd path/to/dictation-kit-v4.4
$ vi mysmartspeaker.list
...
ABCニュース    [ABCAustraliaNews]    e: b i: sh i: ny u: s u
BBCニュース    [BBCWorldNews]    b i: b i: sh i: ny u: s u
...
$ iconv -f utf8 -t eucjp mysmartspeaker.list > mysmartspeaker.eucjp
$

 Juliusのdictation kit ver 4.4の独自辞書に追記、ファイルエンコーディングをUTF-8からEUC-JPに変換。

2019/01/30

 ICECAST STREAMからJAZZ、CLASSICに加え、BLUESを追加。

2019/07/18

 ICECAST STREAMからURLを直接指定していたJAZZ、CLASSIC、BLUESですが、スクリプトを作成、実行する方式にすることにしました。

 というのも当初から、URL指定だとURLが度々変更になるようで再生できなくなり、その度にスクリプト内のURLを変更するというアナログな方法をとる必要がありながら、対策を講じることなく放置、しまいには使わなくなっていたのですが、ちょっと時間ができたため、考えてみることに。

 何らかの方法でURLを特定できるのであれば、HTMLやXMLを解析して持ってくればよいんだよね?と思ったら、巷では、そういうのをWebスクレイピングと呼ぶらしいことを知りました。

 どれかと言われればPerlが慣れているのですが、今やスクリプトと言えば、RubyかPythonらしい、Pythonのほうが馴染みやすかったためPythonで作ってみることにしました。

$ pip3 install beautifulsoup4
$ pip3 install lxml
$

 PythonでWebスクレイピングと言えば、BeautifulSoup一択の雰囲気。

 そこでpython3でwebスクレイピング(Beautiful Soup)をとっかかりにBeautiful Soup 4.4.0 documentation(Beautiful Soup 4.2.0 Doc. 日本語訳 (2013-11-19最終更新)もあったらしい)を見ながら書いてみました。

$ cd path/to/smartspeaker/script
$ cat GetIcecastJazz.py
#!/usr/bin/env python3
#coding:UTF-8
import requests
from bs4 import BeautifulSoup
 
host = "http://dir.xiph.org"
url = "http://dir.xiph.org/search?search=jazz"
 
html = requests.get(url)
 
# lxmlの方が高速らしい
#soup = BeautifulSoup(html.text, "html.parser")
soup = BeautifulSoup(html.text, 'lxml')
 
for musicline in soup.find_all(title="Listen to 'jazzheart'"):
  print(host + musicline.get('href'))
  break
 
$

 aタグのtitleで*.m3uと*.xspfの2つに、breakで抜けることで強引に一意に絞れそうだったため、そうすることにしました。

 import sysしてsys.argvを使ってスクリプト1つで済まそうと思ったら、うまくいかなかったので引数を取る方法は早々に諦め、ジャンル(ラジオ局)ごとに1つスクリプトを作ることにしました。

 また、常用しているmplayerをpythonから直接起動する方法としてimport subprocessしてsubprocess.call()しようと思ったら、値を直接渡せばいけるものの、変数だとダメ...、これまたあっさり手を引き、URLを吐くだけにしてshellスクリプトからpythonスクリプトを呼ぶ恰好にしました。

$ pwd
path/to/smartspeaker/script
$ cat IcecastJazz.sh
#!/bin/sh
 
mplayer -playlist `/home/xxx/path/to/smartspeaker/script/GetIcecastJazz.py`
 
$

 そのshellスクリプトは、mplayerの引数にURLを返すpythonスクリプトを指定するだけ。

 結果、スマートスピーカー用の応答スクリプトvoicerecieve.plからは、このshellスクリプトを呼ぶことにしました。

 こんな感じでClassicalとBlues、今回新たにPOPS(洋楽)も加え、それぞれスクリプトを作りました。

 当初、soup.select('a[href^="/listen/"]')して最初の局を再生しようと思ったのですが、仮にブラウザ上で聴取できたとしてもCLIで実行するとなぜか[This station is not available in your country.]と言われてしまうことがあった為、聴取できることを確認できた特定の局を再生することにしました。

変更の反映

 辞書に影響がある(辞書を編集した)場合は、少なくともJulius 4.4においては、iconvでeucjpに変換が必要です。

 構成ファイル変更の反映については、systemd/systemctlならsystemd自動起動設定参照。

ウェブ造ホーム前へ次へ