以前、作って運用しつつもブラッシュアップ中のRaspberry Pi 3 Model B+とJulius、Open JTalkベースの自作スマートスピーカーがあります。
主な機能は、
尚、ラズパイ用ACアダプタを挿したスイッチ付きコンセントでのON/OFFとは別にラズパイ用boot/reboot/shutdown物理ボタン付き。
音声認識にJuliusを使った自作スマートスピーカーに伝言とメモの機能を実装するにあたり、マイクとスピーカーを専有してしまうOSSやALSAからPulseAudioに移行しました。
ちなみに便利なのでラズパイだけでなく、PC/Debianにも自作スマートスピーカー機能を搭載しています。
自ずとモニタ付きとなるPC版スマートスピーカーには、PC及びラズパイ双方のスマートスピーカー機能のデスクトップアプリとしてPyQt5/Qt Designerによる操作パネルも作成しました。
今回は、更にradiko、らじるらじる、サイマルラジオ、ICECAST、オーストラリア放送ABC Newsと英国放送BBC World News等の無料ラジオ局を声で再生、停止させる機能を加えることにしました。
BGMとしてなど音楽をかけ流ししたい場合にもラジコでラジオNIKKEI第2(RN2)を選局すれば、平日8時〜21時30分の間、ほぼトークなしの邦楽・洋楽を聴けますし、ICECASTでジャズやクラシック、ロック等々任意の放送局を複数選んでおけば、基本、365日終日、いつでも音楽を聴けるようになるでしょう。
後述の通り、他にも事前にいくつかやることはあるにせよ、メインの作業となる部分は、こんな感じであり、先人のおかげもあって、それほど難解というわけではありません。
今回の実装に伴い、Debianを使っている自身の環境では、mplayer、rtmpdump、swfextractを得るべく、swftools、xmllintを得るべく、libxml2-utilsパッケージを追加インストールしておく必要がありました。
また、radikoタイムフリー保存方法にあるようにradiko.sh用に、予め、myplayer-release.swfをダウンロード、swfextractコマンドからauthkey.pngと命名した認証ファイルを抽出、radiko.shのwkdirに置いておく必要がありました。
このwkdir、デフォルトの/var/tmpにするならchownなりしておく必要があるでしょうが、/home/xxx/以下にしておく方が、無難と判断、radiko.shを編集し、今回はそうしました。
尚、radiko.sh再生時のオプション[-p]の引数となる値は、radiko番組表xml取得APIにあるように全放送局一覧から得られる内の[id]タグの値。
radikoについては、Raspberry Piインターネットラジオの通り、radiko.shにパッチを当てます。
ICECASTについては、そのリンク先にあるmplayer -playlistを使う方法でサイマルラジオの.asxファイル、ICECASTの.m3uファイルを指定します。
らじるらじるについては、「らじる☆らじる」をHLS経由ででプロトコルの変遷を眺めつつ、らじるらじる配信の最新プロトコルであるHLS対応のURIのあるconfig_web.xmlで必要なm3u8(M3U UTF-8)のURLを指定します。
それぞれ、後述のようにJulius辞書の編集と前回作った自作スマートスピーカ用のスクリプトvoicerecive.plへの追記。
独自辞書に追記した際、[TBS]ラジオ、[ニッポン]放送、ラジオ[日本]、[日経]、[NACK]5のローマ字表記が思い当たらず、適当に書いたら、やはり、エラーになりました。
そこで、これらのみ抜き出してタブ区切りで平仮名をあてたファイルを作り、実行権限を与えた(sudo chmod u+xした)yomi2voca.plで変換、出力された結果を反映させたところ、うまく機能しました。(そのためのスクリプトなので当然ですが。)
後にjuliusをモジュールモード(-module)で起動する際に-Cオプションで指定するdictation kit(ver 4.4)の構成ファイルconfig_fileにおいて独自に作った辞書は、-wオプション付きで指定できます。
この中でmysmartspeaker.eucjp以外の言語モデルや音響モデルは、Julius標準のものを使っているだけ。
尚、input値については、ラズパイで使えるよう前回追記の通り、OSSではなく、ALSAを使うべく、一連の作業を行ないつつ、明示的に./configure --with-mictype=alsaしたので-input alsaとしてあります。
これらを踏まえ、自作スマートスピーカー用スクリプト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で再生すれば、途切れることなく、良好に聴取できます。
あとは、検証中なら、例えば、端末からこのようにJuliusをモジュールモードで実行しておき...
他の端末でスマートスピーカ用スクリプトを実行...
スクリプトの手順に沿って独自辞書に登録したワードを発すれば、今回新たに追加したラジオを再生できるはず。
辞書に影響がある(辞書を編集した)場合は、少なくともJulius 4.4においては、iconvでeucjpに変換が必要です。
構成ファイル変更の反映については、systemd/systemctlならsystemd自動起動設定参照。
自動起動する際などroot起動の可能性を考えると$HOMEなどをも使わず、[/home/xxx/sound/jsay]などは、省略せずに[/home/xxx/sound/jsay]などとした方が賢明かと。
とても良好に機能しています。
ただ、課題がなくもない。
1つは、ラジオ局を複数再生できてしまい、その場合、混線状態となること。
ただ、日付時刻や天気などラジオ局再生中でも確認できるのは、特に音楽鑑賞中は、意外と重宝する為、ラジオ局再生が複数重ならないように何か工夫が必要かとも思いますが、停止をかければ全て止まるし、誤動作でない限りは、そもそも再生中に重複指示しなければよく、運用でなんとかなるとも言える。
これは課題というのか、現状、スピーカースクリプトにラジオの音量調整機能はなく、必要ならスピーカーZ120のボリューム調整つまみでなら調整できる状態ですが、ソフト的な音量調整を実装するか、スピーカーのつまみでの運用を前提にするか...。
WiFi経由で赤外線リモコン操作するテレビなどについては、音量調整も搭載しますが、スマートスピーカ自体の音量も声で操作できた方がいいのかな...。
Mplayer, Radio Station, TUNE-IN Plugin available?を参考に英語系ニュースラジオ・ライブとしてオーストラリア放送ABC Newsと英国放送BBC World Newsを追加。
何れもmplayerでいけますが、BBCの方は、-playlistオプションは不要な点に注意。
UPnP/DLNAメディア再生機能を追加した際、ラジオや音楽の再生が複数被らないようラジオ停止操作をstop_radio.shにまとめました。
他のラジオでは、このスクリプトをバックグラウンド起動させてから再生スクリプトを実行しても何ら問題ありませんが、BBCニュース、ABCニュース再生前に置く時は、なぜか、フォアグラウンド起動しないと[can not connect socket]、LIRCがどうのといった予期せぬエラーになるので注意。
stop_radio.shを実行しない手もあるにはありますが、重複再生を回避できない為、本末転倒。
ちなみにLIRCに関するエラー自体は、mplayer.conf内で設定したり、-nolircオプション付きでmplayerを実行すれば回避できるのですが、それだけだと肝心のニュースが再生されない。
Juliusのdictation kit ver 4.4の独自辞書に追記、ファイルエンコーディングをUTF-8からEUC-JPに変換。
ICECAST STREAMからJAZZ、CLASSICに加え、BLUESを追加。
ICECAST STREAMからURLを直接指定していたJAZZ、CLASSIC、BLUESですが、スクリプトを作成、実行する方式にすることにしました。
というのも当初から、URL指定だとURLが度々変更になるようで再生できなくなり、その度にスクリプト内のURLを変更するというアナログな方法をとる必要がありながら、対策を講じることなく放置、しまいには使わなくなっていたのですが、ちょっと時間ができたため、考えてみることに。
何らかの方法でURLを特定できるのであれば、HTMLやXMLを解析して持ってくればよいんだよね?と思ったら、巷では、そういうのをWebスクレイピングと呼ぶらしいことを知りました。
どれかと言われればPerlが慣れているのですが、今やスクリプトと言えば、RubyかPythonらしい、Pythonのほうが馴染みやすかったためPythonで作ってみることにしました。
PythonでWebスクレイピングと言えば、BeautifulSoup一択の雰囲気。
そこでpython3でwebスクレイピング(Beautiful Soup)をとっかかりにBeautiful Soup 4.4.0 documentation(Beautiful Soup 4.2.0 Doc. 日本語訳 (2013-11-19最終更新)もあったらしい)を見ながら書いてみました。
aタグのtitleで*.m3uと*.xspfの2つに、breakで抜けることで強引に一意に絞れそうだったため、そうすることにしました。
import sysしてsys.argvを使ってスクリプト1つで済まそうと思ったら、うまくいかなかったので引数を取る方法は早々に諦め、ジャンル(ラジオ局)ごとに1つスクリプトを作ることにしました。
また、常用しているmplayerをpythonから直接起動する方法としてimport subprocessしてsubprocess.call()しようと思ったら、値を直接渡せばいけるものの、変数だとダメ...、これまたあっさり手を引き、URLを吐くだけにしてshellスクリプトからpythonスクリプトを呼ぶ恰好にしました。
ある日、Jazzを再生しても音が出ない、別の日、クラシックもブルースも音が出ない...音がでなーい、どーしよっ、どーしよっ、パオッって感じですが、ストリームがなくなったのかなと思って、しばらく放置していました。
が、今日、確認してみたところ、ICECASTのSTREAMページがリニューアルしており、HTMLも内容が変わっていることに気づきました。
そう、これに伴い、マッチングも無効ですが、それ以前にCGI引数の渡し方が微妙に変更されている時点で再生されなかったのでした。
というわけで書き直したのがこれです。
正規表現ライブラリreをインポートしてURLをマッチさせる方法で特定、複数ある場合に備え、相変わらず、breakで抜けて取得は1件だけということで。
ただ、以前のように全結果にアクセスできておらず、1ページめだけっぽい気がしなくもない感じ...そうだとすると2ページめ含む以降に存在しても再生されないですが、とりあえず、いっか。
そのshellスクリプトは、mplayerの引数にURLを返すpythonスクリプトを指定するだけ。
結果、スマートスピーカー用の応答スクリプトvoicerecieve.plからは、このshellスクリプトを呼ぶことにしました。
こんな感じでClassicalとBlues、今回新たにPOPS(洋楽)も加え、それぞれスクリプトを作りました。
当初、soup.select('a[href^="/listen/"]')して最初の局を再生しようと思ったのですが、仮にブラウザ上で聴取できたとしてもCLIで実行するとなぜか[This station is not available in your country.]と言われてしまうことがあった為、聴取できることを確認できた特定の局を再生することにしました。
ラズパイ版スマートスピーカーについては、mplayerのバグっぽく、mplayerからmpvに変更することになりました。
PC版スマートスピーカー機能においても、mpvに変更することになりました。
が、PCの方のmplayerは、ラズパイでの変更時に確認したバージョンから変わっていないのでmplayerのバグというわけではなさそうです。
これに伴い、ラジオ停止操作スクリプトstop_radio.shにpkill -f mpvを追記。
尚、スクリプト修正後、systemctl restart ...が必要です。
昨夜、自作スマートスピーカーでふとRadikoを再生しようと思ったら、なぜか再生できないことに気づきました。
調べてみると、どうも2020年のAdobe Flashのサポート終了に伴い、昨年11月でRadikoがFlash対応を終了したことによる模様。
4ヶ月も5ヶ月もラジコ再生してなかったってことですよね!?まぁ、既存スクリプトradiko.shは、確かFlash依存なはず、ということで今日になってFlashレスで再生できるスクリプトを検索した結果、【Python】ラジコを再生するを発見しました。
タイムスタンプは[2019-06-24]となっており、そもそもFlash終了以前からFlashに依存しないスクリプトを作っていらっしゃいました。
そこにあるとおり、スクリプトのあるパス上で./pyradiko.py QRR -p 'ffplay'(または、./pyradiko.py -p 'ffplay' QRR)のようにすれば、再生時間を指定せずともライブ配信されました、が、そのままでは、すぐ停止してしまうので後述の通り、スクリプト内の時間設定を修正しました。
自身はライブ配信視聴しかしませんが、このスクリプト多機能でいろいろできる模様、プレイヤーもffmpegパッケージに同梱されるらしきffplayの他、mplayerも指定できるようですが、詳しく見ていないものの、mplayerだとエラー([do_connect: could not connect to socket]/[connect: No such file or directory])になりました。
が、python2対応のようでpython3だとbase64部分でエラーが、そこをクリアしてもreturn response.content.splitlines()[-1]のエラーが。
そこでめぼしいところにprint文を入れてみたところ、ffplayで再生するにあたり、なぜか、URL全体が、b''でくくられ、byte扱いになっていることが判明、これをdecodeしてstr型にすることでpython3でもいけました。
たぶん、時間指定しなければ、再生継続なはずと信じつつ、と思ったら甘かったので[parser.add_argument('-d',..., const=1800, default=60]の[const=1800]が後述のように上限っぽいのででないと思い込んで[const=1800, default=60]のdefaultをとりあえず、[const=1800, default=36000]に...
あ、1800秒って、このスクリプトでも再生できるっぽいタイムフリー機能の制限の3時間か?と思ったら、1800秒って30分か...。
ということは、const=1800ってなんだろ...、それにしても、やはり、この上限かな...。
何れにせよ、ライブ視聴には上限ってないですよね?radiko.shでも30分や3時間どころかもっとかけ流していたこともあったような...、どっかいじらなくちゃダメか...const値を変えれば良いだけかな...?そんなにラジオ聴くことはないけど、かけ流しにはしたいので変えておこう...。
ん!?ラジオNIKKEI第1、ラジオNIKKEI第2、NHK第一、NHK FM、放送大学については、リターンコード404エラーで視聴できない...。
404はサーバにアクセスはできてるけど要求されたもの返せないよエラーだよね?ID変わったのかと思いつつ、http://radiko.jp/v3/station/region/full.xmlを見る限り、ブラウザからRadiko視聴してみても、変わってなさ気...Python2の元のスクリプトも試してみても同様、なぜ...。
わかった!いろいろ検索しているとストリーミングサーバのサブドメイン(≒サブディレクトリ)がc-radikoとf-radikoの2種類あるなと思っていたのですが、[c-radiko.smartstream.ne.jp]を[f-radiko.smartstream.ne.jp]にしてみたところ、全局、再生できるようになりました。
これは、ブラウザ(Firefox)のヨコ三本線の[設定]、[ウェブ開発]、[ネットワーク]から[メディア]に絞って[ドメイン]で、また、行をマウスオーバーして選んで右クリック、[コピー]、[URLをコピー]した中身を眺めるとわかります。
今日確認した限りでは、[c-radiko]は見たような見てないような、[f-radiko]はあっても稀、たいていは、[rpaa]というサブドメインで、このスクリプト上で、単にこれにするとエラーになる、コピーして確認するとパラメータも違うっぽいものの、もしや、以後、このサブドメインに統一していく過渡期なのか...、とは言え、とりあえず今は、[f-radiko]でいけたということで。
結果、太字・フォント大きめにした
でpython3対応としての3箇所含め、実用上は5箇所の修正だけで済み、あと実用上、問題ないものの、
しておきました。
あ、[parser.add_argument('-p', '--player', help='再生するプレイヤー', type=str, nargs='?', default='mplayer')]をdefault='ffplay'にすれば、プレイヤーを指定することなく、./radiko.py TBSなどとしても再生できますね(mplayerだとダメですが)。
というわけでサイトへのコメントは割愛させて頂きますが、作者Turtlechan氏に感謝して有り難く使わせて頂きます。
尚、[NHKラジオ第2: JOAB]はRadiko経由については既にサービスが終了しています(らじる☆らじるでは、視聴可)。
ちなみにtakuya / play-radikoでは、そのままで全局、見事に再生できたのですが、なぜか、再生されるまでにちょっとしたタイムラグがあった、端末で実行、終了(Ctrl+C)するとフリーズして端末を閉じるしかなくなったりしたので先出のものを選ばせて頂きました。
というわけで自身の場合、自作のラズパイスマートスピーカー、Linuxパソコン用の自作スマートスピーカー機能に反映させるべく、スマートスピーカー応答スクリプトとPCから操作できるスマートスピーカー操作パネル用のスクリプトをradiko.shからradiko.py(radiko.sh -p 放送局IDからradiko.py -p ffplay 放送局ID)に変更、音楽・ラジオ等停止スクリプトにpkill -f ffplayを追記、応答ファイルの反映は、systemctl stop サービス/systemctl daemon-reload/systemctl start サービス、操作パネルは、編集後、パネルを開くかパネルを再起動しておきました。
加えて、音声操作応答ファイルにおいて、音楽やラジオ再生中に他の再生を指示した際、2重3重に音声が被らないよう停止スクリプトで止めてから再生するようにしているのですが、停止スクリプトと各種再生スクリプトやチャンネルとの間に(バックグラウンドじゃなくてフォアグラウンドで)スリープ[system("sleep 1");]を入れる必要がありました。
radiko.shの際には不要だったのですが、radiko.pyと停止スクリプトの間には、タイムラグがあるようで、そもそも停止スクリプトを入れなければ再生されるものの、停止スクリプトを介してスリープさせないと再生されなかったので。
もう少し長い方が良いのかと最初は10秒で試しましたが、1秒でOKでした。
操作パネルからでもRadikoの放送局の再生を停止するまでに微妙なタイムラグがあったことから、すぐに目星はつき、当初、停止スクリプトのpkill -f ffplay分だけ停止に時間がかかるのか繊細なものだなとも思いましたが、停止スクリプトを共有していてmplayerで再生させているICECASTやYouTube、ABCラジオやBBCラジオでは影響がないので、もしかすると、それにプラスしてか、ffplayでの再生開始が速い可能性はあるのかも。
ICECASTにおいてJazzやBluesは安定しているものの、Classicalは、局に関わらず、なぜか再生できなくなることが多く、放置していました。
が、ICECAST以外の選択肢をと思い、クラシックについては、internet-radio.comからチョイスさせて頂くことにしました。
と思ったら、ICECASTのジャズやブルースも時折、局が見当たらなくなるのでスクレイピングはやめて同じくinternet-radio.comから探した特定の局を指定、再生することにしました。