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

ラズパイスマートスピーカーにUPnP/DLNAメディア再生機能を追加

ホーム前へ次へ
Raspberry Piって?

ラズパイスマートスピーカーにUPnP/DLNAメディア再生機能を追加

ラズパイスマートスピーカーにUPnP/DLNAメディア再生機能を追加

2019/01/24

 大坂なおみちゃん全豪オープン決勝進出を祝して!?Raspberry Pi 3 Model B+とJuliusOpen JTalkベースの自作スマートスピーカーにUPnP/DLNAメディア再生機能を追加してみた話。

 既存の主な機能は、

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

 音声認識にJuliusを使った自作スマートスピーカーに伝言とメモの機能を実装するにあたり、マイクとスピーカーを専有してしまうOSSやALSAからPulseAudioに移行しました。

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

 自ずとモニタ付きとなるPC版スマートスピーカーには、PC及びラズパイ双方のスマートスピーカー機能のデスクトップアプリとしてPyQt5/Qt Designerによる操作パネルも作成しました。

UPnP/DLNAメディア再生機能を追加

 今回は、これにMediaTombMiniDLNA/ReadyMediaといったUPnP/DLNAメディア再生機能を追加します。

 約3年運用しているRaspberry Pi 2 Model B/Raspbianサーバには、最初から、これらUPnP、DLNAメディアサーバも入れてありました。

 今回は、これを自作スマートスピーカーから呼び出して再生するだけ。

 とりあえず、ネット検索してみたところ、mplayerでUPnP/DLNAサーバ上のメディアってどうやって再生するんだ?など、直接再生しようとしてか、妙に難しく考えているっぽく、解決に至っていないケースが多いように見受けられました。

 簡単にできるでしょと考えていた自身も一瞬、その渦に巻き込まれそうになり、以前、PC上のDebian 8 JessieFedora 23NetBSD 7.0SHARP AQUOS TVで検証した記事を見直したりしてUPnP/DLNAクライアントとして使えたRythmboxでも使わないとダメか?なんてハマりかけましたが、よく考えたら簡単でmplayerで実現できました。

概要

 簡潔に言うとマウントしてから、MPlayerにファイルパスを渡すだけ。

 つまり、先のラズパイ上のNAS・ファイルサーバはSambaで実装してあり、アクセスするマシンからは、当該共有パスをマウント、mplayerにそこから辿った音源ファイルのあるパスを渡すだけでOK。

 音源パスに[*](階層に合わせてpath/to/*/*など複数も可)を使えば、ディレクトリをプレイリストのように扱うことができるため、音源ファイルを直接指定する必要はなく、スマートスピーカーには好都合。

 よってコマンドライン(CLI/CUI)操作もできるし、スクリプトに書くこともできます。

 尚、今回は、映像は考慮せず、音源再生のみにフォーカスしました。

何が嬉しい?

 UPnP/DLNAサーバは自前のサーバであり、例えば、手持ちの音楽CDをリッピングしてできた音源ファイルをアップしておけば、クライアントから再生することができます。

 ということは、有線はもちろん、無線LANや無線アクセスポイントなど同じネットワーク内にいるパソコン、タブレット、スマホなどからもオリジナルの音源ライブラリを再生できることを意味します。

 もちろん、メディアサーバには、後から追加することもできます。

 市販の一部のスマートスピーカーにもこうした機能はある(全てのスマートスピーカーが対応しているわけではない)らしいし、今回は、そんなオリジナル音楽ファイル再生機能を自作スマートスピーカーに搭載、音声で再生、停止できるようにしてみたということです。

Samba共有ディレクトリのマウント

pi@raspberrypi~~$ sudo mount -t cifs -o username=samba,password=samba //serverHOSTorIPormDNS/shared /mnt/nas
pi@raspberrypi~~$

 NetBSD上のSamba共有パスにWindows/Fedora/NetBSDからアクセスにも書きましたが、Linuxからなら、Samba共有のマウントは、このようにすればよいでしょう。

 リンク先にある通り、予め必要なパッケージもありますが、既にSambaサーバが稼働しているのであれば、それらはあるはず。

 ただ、リンク先では、NetBSDでサーバを立てたこともあり、smbpasswdを使いましたが、ある時点からpdbeditに切り替わったようです。

 自身は、この仕様変更に伴うものではないはずですが、ここで少しハマりました。

 なぜか、/etc/samba/smb.confに追記したユーザーとpdbedit -Lで出力されたユーザー一覧の結果があっていません。

 実際には、sambaがユーザーとして存在すると思っていたら、存在せず、mountオプションで[-o username=samba]としていたことで権限がないだの、そんなパスはないだのと無駄にアクセスできずに時間をとられました。

MPlayerでNAS越しの音源ファイルを再生

pi@raspberrypi~~$ mplayer /mnt/nas/path/to/music/*/*
pi@raspberrypi~~$

 マウントさえしてしまえば、あとは、MPlayerにファイルパスを渡すだけです。

 前述の通り、アスタリスクで抽象化でき、プレイリストのように振る舞ってくれるのは好都合。

 尚、ファイルパスにスペースがあるとエラーになるシーンもありますが、これは大丈夫でした。

pi@raspberrypi~~$ mpv /mnt/nas/path/to/music
pi@raspberrypi~~$
2021/10/22 追記

 以前、CD/MDは終わらせてあったものの、ここ数日、放置していたカセットテープ音源をリッピングしており、サーバに上げた|上げる予定です。

 そこでminiDLNAサーバのディレクトリ構成を変更したことからmplayerではワイルドカードを使ってもディレクトリ階層を再帰的に再生できなかったこと、Audacityで変換出力したものの内、スペースがある曲名などでシングルクォートのないファイルでエラーが起きたことからmpvに変更しました。

 これらについてshellコマンド含め、mplayerで実装すべく結構悩みましたが、mpvなら、ワイルドカードすら使わずに上位のファイルパスを渡すだけでファイル名のスペースの有無含め、何も考えることなく、拍子抜けするほど、あっさりと再帰的に再生してくれたのでした。

 ついては、mpvに--shuffleでシャッフル、--loop-playlist=infで無限ループできるようにオプションを追加しておきました

 ちなみに、これらについては、mplayerでも-shuffleでシャッフル、-loop 0で無限ループできます(後者はたぶん)。

Juliusオリジナル辞書の編集

pi@raspberrypi~$ vi mysmartspeaker.list
pi@raspberrypi~$ ...
音楽かけて    [音楽再生]    o N g a k u k a k e t e
pi@raspberrypi~$ ...
pi@raspberrypi~$ iconv -f utf8 -t eucjp mysmartspeaker.list > mysmartspeaker.eucjp
pi@raspberrypi~$

 自作スマートスピーカーの音声認識ソフトウェアJuliusの独自辞書に今回のオリジナル音源再生時の呼びかけ情報を追加し、eucjpに変換しておきます。

 今回は、「音楽かけて」と呼びかけた時、Juliusが「音楽再生」と認識、テキスト出力したものを自作スクリプト./voicerecieve.plの条件分岐でこれに該当した場合、音声メッセージ「CDライブラリを再生します」に続き、手持ちの複数のCDからリッピング、ラズパイ/DLNAサーバ上にアップしてあるオリジナルミュージックライブラリが再生されるようにしました。

2021/10/22 追記

 カセットテープ音源のリッピングに伴いジャンルが増えたのでYouTubeのJPOPやPOPSとは区別したもの含め、コールワードも適宜追加しました。

自作スマートスピーカー用スクリプトの編集

pi@raspberrypi~$ pwd
/home/xxx/sound/
pi@raspberrypi~$ vi voicerecieve.pl
#!/usr/bin/env perl
...
      when("音楽再生"){
       system("/path/to/my_music.sh &");
      }
 ...
      # 【ラジオ停止】
      when("ラジオ停止"){
       system("/home/xxx/sound/jsay ラジオを停止します");
       system("/path/to/stop_radio.sh &");
      }
 ...
 
pi@raspberrypi~$

 Juliusの音声テキスト変換出力結果を受ける自作スマートスピーカー用スクリプト./voicerecieve.plは、このように編集しました。

 メディアサーバ上の音源を再生する際には、my_music.shを呼び、これまで個別にpkillしていたコマンドをstop_radio.shにまとめることにしました。

 stop_radio.shにまとめたのは、音源混線回避にあたり、スクリプトをよりスマートにするため。

 というのもラジオと天気予報や日付時刻などは再生が被っても、むしろ都合がよいこともありますが、複数のラジオが再生され、混線する可能性があり、そうなると何とも気色悪い響きになることがあったのですが、わかっていつつも面倒で対策は先延ばしにしていました。

 今回、mplayerによる再生のみならず、メディアサーバパスをマウントしており、再生を停止する際には、DLNAメディア(ラズパイ)サーバを常時起動していない関係でアンマウントもしておきたい、となるとラジオ停止時の処理に追加することになるので、この機会にスクリプト化することにした次第。

 ラジオや音源再生の条件分岐全て最初にこのスクリプト行を追加しておけば、pkillしてから再生するため、誤動作による混線を回避できます。

2021/10/22 追記

 カセット音源リッピングにより音楽のジャンルが増えたのでスマートスピーカー応答スクリプト上の分岐もその分増やしました。

 従来のジャンルは、1周回ってYouTubeのJPOP/POPSinternetradio.comのJAZZ/CLASSIC/BLUES

 増えたジャンルは、カセット(+CD)のJPOP/フォーク/演歌/日本民謡、ロシア民謡、オペラ、POPS、JAZZ、CLASSIC、洋画の映画音楽、ピアノ・ギター・ハーモニカ等による各楽器演奏。

 それぞれ曲順ランダム・エンドレス再生に加え、リッピング音源全ジャンルのランダム・エンドレス再生も追加...って少なくとも全ジャンル再生ではエンドレスにしたところで一巡すら聴ききれるはずもないですが。

音源再生スクリプトの作成

pi@raspberrypi~$ vi my_music.sh
#!/bin/sh
 
mount -t cifs -o username=XXX,password=YYY //HOST.local/nas /mnt/nas
a=`ls /mnt/nas/path/to/minidlna/music`
 
if [ $? -ge 1 ]; then
  /home/xxx/path/to/jsay "サーバーがマウントされていません"
  echo "NAS Mount Error"
else
  /home/xxx/path/to/jsay "CDコレクションを再生します"
  mplayer -shuffle /mnt/nas/path/to/minidlna/music/*/*
fi
pi@raspberrypi~$

 音源再生スクリプトmy_music.shは、こんな感じにしてみました。

 mountは、通常、root権限が要りますが、systemd(デーモン)のサービスファイル内でsuしない限り、Juliusも自作スクリプトもroot起動するため、スクリプト上でsudoを付ける必要はありません。

 [man mount.cifs]にあるようにcifsの場合、mountオプション-oのusername/passwordを直接書く代わりにファイルに書いておくこともできます。

 その場合には、[credentials=/home/xxx/.smb_credencials]などとして置き換え、[/home/xxx/.smb_credencials]ファイルに[username=XXX]行、[password=YYY]行、必要なら加えて[domain=WORKGROUP_NAME]を1行ずつ書いて[chown 0600 /home/xxx/.smb_credencials]しておけばよいでしょう。

 Linuxでは、Avahi、macOS(やWindows)では、Bonjourをインストール済みであれば、mDNSでホスト名.localを使え、ここでもこれを指定することができます。

 Samba共有のマウントポイント配下のパスが通れば(0/true)、「CDコレクションを再生します」、パスが通らなければ(1以上/false)、mount失敗ということで「サーバーがマウントされていません」とメッセージが再生されつつ、端末から実行した場合「NAS Mount Error」とエラーメッセージがテキスト出力されます。

 一瞬、毎回同じ再生順じゃな...シャッフル再生か、どう実装するか...と思いましたが、なんのことはないman mplayerを見たら、あるじゃんshuffleオプション、mplayerすごし。

 尚、自身は、今のところ、ラズパイサーバを常時起動しておらず、必要都度起動(要らない時はシャットダウン)している為、mountチェックを入れています。

...
 
mount /mnt/nas
a=`ls /mnt/nas/path/to/minidlna/music`
 
if [ $? -ge 1 ]; then
...
2019/06/09 追記・修正

 先日、JuliusでPulseAudioを使うにあたり、systemdサービスファイルをユーザー起動に変更したのに伴い、変更が必要になりました。

 ここでのマウントを単にmount /mnt/nasとします、そもそも、後述のように/etc/fstabにこれ用の記述があれば、こうできるから利便性が高いわけで...。

 というか、systemdサービスファイルを結果root起動しつつ、suしており、このスクリプトもroot起動だったから先の記述で事なきを得て気づかなかったという大ボケっぷりでした。

 PC版、ラズパイ版ともにスマートスピーカーで「音楽かけて」と言っても常に「サーバがマウントされていません」となり、あれ?と思って調べてみて気づくに至りました。

2021/10/22 追記

 前述追記の通り、mpv --shuffle --loop-playlist=infに変更しました。

 また、カセット音源によりジャンルが増えたことでスクリプトもその分増やしました。

ラジオ停止スクリプトの作成

pi@raspberrypi~$ vi my_music.sh
#!/bin/sh
 
pkill -f rtmpdump
pkill -f mplayer
# =======
# YouTubeプレイリスト再生に伴う追記
ps aux | grep youtubejpop.*.sh | awk '{ print "kill -9", $2 }' | sh
pkill -f youtube-dl
# =======
umount -f /mnt/nas
 
pi@raspberrypi~$

 音源再生スクリプトstop_radio.shは、こんな感じにしてみました。

 もはや、rtmpdumpについては不要ですが、一応、rtmpdumpとmplayerをpkill、マウントポイント/mnt/nasをumount。

 umount -f /mnt/nasをバッククォートで括ってダミーの変数に入れたあと、$?で評価する手もありますが、今のところ、これで支障はありません。

2021/10/22 追記

 前述追記の通り、mpvを追加したので pkill mpvも追記しました。

 というか、これはなにかの折にどっかで追記したはずですが念の為。

構成ファイル編集後の反映

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

/etc/fstabでCIFSをマウント

pi@raspberrypi~$ sudo vi /etc/fstab
...
//HOST.local/nas  /mnt/nas  cifs  username=XXX,password=YYY,uid=1000,gid=1000,file_mode=0755,dir_mode=0755,user,users,noauto  0  0
...
pi@raspberrypi~$ sudo mount -a
pi@raspberrypi~$ sudo reboot

 ユーザー権限でmount/umountさせないとと思い/etc/fstabにNASをnoautoでマウント、再起動。

 mount -aしておけば、再起動する必要はないと思いましたが、一応。

 /etc/fstabでも前述のようにusername/passwordに代えて、これらを書いたファイルを指定すべく、[credentials=/home/xxx/.smb_credencials]を使うこともできます。

 前述の通り、Linuxでは、Avahi、macOS(Windows)では、Bonjourをインストール済みであれば、mDNSでホスト名.localを使えるわけですが、/etc/fstabでもこれを指定することができました。

 が、よく考えたら、systemdでデーモン化しており、.serviceファイルでは、現状、Juliusも自作スクリプトもsuしておらず、root起動となるため、一般アカウントでマウント・アンマウントさせる必要なかったか...と思いきや、なぜか、必要っぽい...。

 また、ちゃんと精査していませんが、この場合、mountオプションには、どうもuserとusers両方とも必要な模様。

 man mountによれば、userは、一般ユーザーでもmountできる指定ですが、当該マウントポイントはmountしたユーザーしかumountできない一方、usersは、他のユーザーでもumountできるとあり、/etc/fstabへの追記が必要だとしてもusersだけで事足りそうなのですが...。

 ラズパイとPCのスマートスピーカー両方やってたら、ちょっと混乱してきた...、systemdの.serviceファイルでsuでもしない限り、やはり、/etc/fstabの設定は要らないかも...。

2019/06/09

 uid/gid/file_mode/dir_modeはなくても可、そもそもrootはさておき、他ユーザーがumountすることはないのでusersも不要でした。

 逆にこれに読み取り専用のroを追加しておくのは賢明かもしれません。

備考

 何れにしてもラズパイスマートスピーカーもPC/Debianに搭載したスマートスピーカーもうまいこと機能しています。

ホーム前へ次へ