Raspberry Piで超簡易オーディオプレイヤー 「松」2020
ディスプレイ搭載
RasPi Audio Player
「ボタンを押すと、楽曲ファイルが演奏され、もう一度ボタンを押すと演奏が止まる」という、誠に単純な機能を持つ装置
Raspberry pi 公式タッチパネルディスプレイを接続したGUIを備えたデバイスに搭載する超簡易オーディオプレイヤー
I2Cインタフェースによる温湿度気圧センサを搭載することにも配慮している。
2020年版では、iPhoneやiPad, Macの「ミュージック」アプリの音声を出力できるAirPlayにも対応
OSのインストール
インストールしたRaspbian OSのイメージ
2020-08-20-raspios-buster-armhf.img
ソフトウェアの設定は、共通のものを参照。
公式ディスプレイの方向を変更
変更するファイル /boot/config.txt
(追加内容)
lcd_rotate=2
音楽再生アプリのための設定
PythonでVLCを制御するためのパッケージのインストール
$ pip3 install python-vlc
I2C用Python moduleのインストール
$ pip3 install smbus
必要なハードウェア
Raspberry Pi 3 model B/B+
Raspberry Pi 7インチ公式タッチディスプレイ(800x480px)
USBオーディオDAコンバーターキット REV.C [AKI.DAC-U2704 REV.C]
USBメモリ(またはUSBメモリアダプタとmicro SDHC card)
赤外線焦電センサ
押しボタンスイッチ
ピンソケット
回路図
Sound Cardとdevice numberの確認
$ aplay -l
これでUSB-DACのcard番号を調べる
USB-DACの設定
設定ファイル: /usr/share/alsa/alsa.conf
(修正前)
defaults.ctl.card 0
defaults.pcm.card 0
(修正後)
defaults.ctl.card 1
defaults.pcm.card 1
オーディオプレイヤーのソフトウェア
Python3を使って、実装する。
(ソースコード:ファイル名 tksimpleplayer.py)
#! /usr/bin/env python3
# coding:utf-8
# RapsberryPi Simple Music Player
#(準備)
# VLCのインストール install VLC MediaPlayer
# python VLCのインストール pip3 install python-vlc
# Tact Switch on GPIO 22
# LED on GPIO 27 Active Low
# Tact Switch ON -> PLAY (LED ON) -> Tact Switch ON -> STOP (LED OFF)
# Musicvplayer application software is VLC
import tkinter as tk
import RPi.GPIO as GPIO
import subprocess
import vlc
import pathlib
import random
import re
import urllib.parse
import time
import sys
import os
class SimplePlayer(tk.Frame):
def __init__(self, master=None):
self.background_color="#000020"
self.musicdatapath = "/media/pi/0123-4567" # Path of Music DATA Folder, (with USB-ID)
super().__init__(master)
self.master.title("tkSimplePlayer")
self.set_gpio()
self.pack()
self.create_widgets()
self.status = "STOP"
self.vlcmlp = vlc.MediaListPlayer() #VLC Media List Player
self.vlcmlp.set_playback_mode(vlc.PlaybackMode.loop) #vlc playback mode をloop(play listの繰り返しに)
# VLC Volume 100%
self.VLC_volume = 100
self.vlc_media_player=self.vlcmlp.get_media_player() # get media player instance from media list palyer
self.vlc_media_player.audio_set_volume(self.VLC_volume) # volume set to media player
# Play Listの生成
self.vlcmlp.set_media_list( self.get_media_list() ) #set media list
def stop_and_clean(self):
#停止時の後始末
self.led_off()
if self.status == "PLAY" :
self.vlcmlp.stop() #曲の停止
print("--STOP--")
GPIO.cleanup(22) # Cleanup GPIO 22
GPIO.cleanup(27) # Cleanup GPIO 27
def on_closing(self):
self.stop_and_clean()
root.destroy()
def create_widgets(self):
self.message_button = tk.Button(self, bg=self.background_color, fg="white", text="STOP", font=("",20), command=self.play_sound_tk_button)
#self.message_button = tk.Button(self, text="STOP", font=("",20), command=self.play_sound_tk_button)
self.message_button.pack(side=tk.LEFT, anchor=tk.W)
# 曲のパス名ラベル
self.mrl_label = tk.Label(self, text=" MRL is here. ", font=("",10), anchor='center',
foreground='#ffffff', background=self.background_color,
width=700)
#self.mrl_label = tk.Label(self, text="MRL", font=("",8))
self.mrl_label.pack(side=tk.LEFT, anchor=tk.E, fill=tk.X)
def get_media_list(self):
#メディアリストを更新し、返す。
print("--LIST--")
p_temp_media_list = vlc.MediaList() # MediaListを生成
#メディアファイルのpathのリストを取得
p_temp = pathlib.Path(self.musicdatapath) #USBメモリのパスを設定
mrl_list = [] #mrl(楽曲ファイルのPATH)のリストの初期化
extension_mrl_list = list(p_temp.glob('**/**/*.m[4p][3a]')) # 楽曲拡張子mp3,m4aのファイル名を取得
mrl_list.extend(extension_mrl_list)
length_mrl_list = len(mrl_list) #mrl_listの要素数を取得
for i in range(length_mrl_list):
mrl = random.choice(mrl_list)
p_temp_media_list.add_media(str(mrl))#リストからランダムに取り出す
#print(mrl) #DEBUG
return p_temp_media_list
def vlc_play(self):
#DEBUG:print("DEBUG:status="+self.status)
if self.status == "STOP" :
self.led_on()
self.status = "PLAY"
print("--PLAY--")
# Music media files in /media/pi directory
self.vlcmlp.play() # 曲の再生
self.message_button["text"] = "PLAY"
#再生中にメディアリストを再構築する
self.vlcmlp.set_media_list( self.get_media_list() ) #set media list
print("--PLAYING--")
# MRLを表示
self.show_mrl()
else:
self.led_off()
self.status = "STOP"
self.vlcmlp.stop() #曲の停止
self.message_button["text"] = "STOP"
self.mrl_label['text'] = " "
print("--STOP--")
def show_mrl(self):
# MRL (曲のファイルのパス名)を出力
try :
#MRLを取得
mp = self.vlcmlp.get_media_player()
m = mp.get_media()
mrl_raw = m.get_mrl()
#print(mrl_raw) # DEBUG:
mrl_quote = urllib.parse.unquote(mrl_raw)# MRLのURLデコード (%20 -> spaceにするなど)
mrl_with_ext = mrl_quote.lstrip("file://" + self.musicdatapath) # MRLの最初の共通部分削除
# mrl = mrl_with_ext.replace(".mp3", '') # MRLの拡張子を削除(末尾でなくても削除する)
# mrl = mrl.replace(".m4a", '') # MRLの拡張子を削除(末尾でなくても削除する)
mrl = re.sub("\.m[p4][3a]$", '', mrl_with_ext) # MRLの拡張子.mp3, .m4aを削除
#print(mrl) # DEBUG:
except:
mrl = "MRL ERROR !"
if self.vlcmlp.is_playing() :
#曲の再生中だけ MRL更新
len_mrl = len(mrl)
if len_mrl < 32:
#MRLの文字数が少ないときは、フォントを大きくする
mrl_splitted = mrl.split('/') #/で分割
mrl1stline = "" #1行目に全て表示
for i in range(len(mrl_splitted)):
mrl1stline += mrl_splitted[i] + ' '
self.mrl_label["font"] = ("",18) # labelのフォントサイズを大きめに
mrl = mrl1stline
else:
#文字数が多いとき
mrl_splitted = mrl.split('/') #/で分割
if len(mrl_splitted) <= 2:
mrl1stline = mrl_splitted[0]
mrl2ndline = mrl_splitted[1]
else:
mrl1stline = "" #1行目は、第1、第2要素
for i in range(2):
mrl1stline += mrl_splitted[i] + ' '
mrl2ndline = "" #2行目は第3要素以降
for i in range(len(mrl_splitted) - 2):
mrl2ndline += mrl_splitted[i + 2]
if len(mrl1stline) < 75 and len(mrl2ndline) < 75:
self.mrl_label["font"] = ("",12) # labelのフォントサイズを中程度に
else:
self.mrl_label["font"] = ("",9) # labelのフォントサイズを最小に
mrl = mrl1stline + '\n' + mrl2ndline
#Labelの内容をMRLで更新
#print(mrl) #DEBUG:
self.mrl_label["text"] = mrl
self.after(1000, self.show_mrl) # 1sec. ごとにMRL表示
def play_sound_gpio(self, gpiolist):
## DEBUG:print("DEBUG:" + self.status)
print("SW: PRESSED")
# ボタン長押しでreboot
counter = 50; # 0.1秒間隔で、50回押され続けたら、長押しと判断
while GPIO.input(22) == GPIO.LOW :
time.sleep(0.1)
counter -= 1
if counter <= 0:
os.system("sudo reboot")
# ボタンが離されたら、音楽再生
# VLC palyer control by GPIO
self.vlc_play()
def play_sound_tk_button(self):
# clv palyer control by TKinter GUI
print("DEBUG:play sound tk button")
self.vlc_play()
def set_gpio(self):
GPIO.setmode(GPIO.BCM) #GPIO Access by BCM
GPIO.setup(27,GPIO.OUT) #BCM PIN-27 as Output
GPIO.setup(22,GPIO.IN, pull_up_down=GPIO.PUD_UP)
#GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_OFF) #BCM PIN-22 as Input
GPIO.add_event_detect(22, GPIO.FALLING,bouncetime=500, callback=self.play_sound_gpio)
#GPIO.add_event_detect(22, GPIO.RISING,bouncetime=200) #なぜか勝手に割り込みが入るのでダメ
#GPIO.add_event_callback(22, self.play_sound_gpio) # Call back Trigger GPIO PIN-22
self.led_off(); # LED OFF
def led_on(self):
GPIO.output(27, GPIO.HIGH)
def led_off(self):
GPIO.output(27, GPIO.LOW)
####################################################
root = tk.Tk()
app = SimplePlayer(master=root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.configure(background=app.background_color)
app.configure(background=app.background_color)
root.geometry("800x50+0+275") # Window geomety : X-axis offset 0, Y-axis offset +350
app.mainloop()
オーディオプレイヤーのソースコードの保存
ソースコードを(Python3)をtksimpleplayer.pyという名前で保存
場所は/home/pi/tksimpleplayer/
ソースコードを実行可能に設定する。
$ chmod +x /home/pi/tksimpleplayer/tksimpleplayer.py
OSの起動時に自動的に起動するように設定する。
編集するファイル: /home/pi/.config/lxsession/LXDE-pi/autostart
最後に下記のエントリを追加
@/home/pi/tksimpleplayer/tksimpleplayer.py
人感センサでディスプレイのバックライトをON/OFFするスクリプト
ファイル名 /home/pi/ctrl_backlight/sensor_backlight_on_off.py
#!/usr/bin/python
# coding: utf-8
#
# GPIOピン18番に焦電センサ(TOP3224)を接続。動きを検出すると1が数秒間出力される
# バックライトが60秒後に消灯する。
# 焦電センサが動きを検出すると、バックライトは点灯する。
#
# GPIOモジュール, timeモジュール, osモジュールをインポートする
import RPi.GPIO as GPIO
import time
import os
# GPIO指定をGPIO番号で行う
GPIO.setmode(GPIO.BCM)
# GPIO18ピンを入力モードに設定
GPIO.setup(18, GPIO.IN)
#Display Power Management System を OFF にする
#print "xset dpms force off"
#os.system('xset dpms force off')
while True:
# GPIO18ピンの入力状態を表示する
gpio18 = GPIO.input(18)
#debug:
#print (gpio18)
if gpio18 == 1 :
#焦電センサが動きを検出したら、バックライト一定時間点灯するようにDPMSをONにする
print( "xset dpms force on")
os.system('xset dpms force on')
#600sec.でsleep, standby, off
#os.system('xset dpms 600 600 600')
#60sec.でsleep, standby, off
print( "xset dpms 60 60 60 ")
os.system('xset dpms 60 60 60')
#59sec待つ
time.sleep(59)
# 1sec. sleep状態になり、他の仕事にお任せする
else :
time.sleep(1)
# GPIOピンをリセット
GPIO.cleanup()
OSの起動時に自動的に起動するように設定する。
編集するファイル: /home/pi/.config/lxsession/LXDE-pi/autostart
最後に下記のエントリを追加
@/home/pi/ctrl_backlight/sensor_backlight_on_off.py
AirPlay対応の設定
iPhoneやiPad, Macの「ミュージック」アプリの音声を出力できるAirPlayに対応させる設定
Shairport-syncのインストール
必要なライブラリおよびgitのインストール
$ sudo apt install git autoconf libdaemon-dev libpopt-dev libconfig-dev libasound2-dev libpulse-dev \ libavahi-client-dev libssl-dev libsoxr-dev
shairport-syn のファイル取得とビルド、インストール
$ git clone https://github.com/mikebrady/shairport-sync.git
$ cd shairport-sync
$ autoreconf -i -f
$ ./configure --sysconfdir=/etc --with-alsa --with-pa --with-avahi --with-ssl=openssl \
--with-metadata --with-soxr --with-systemd
$ make && sudo make install
shairport-sync設定ファイルの編集
コマンド aplay -l
で 確認したカード番号をもとにファイル/etc/shairport-sync.confを編集する
$ sudo vi /etc/shairport-sync.conf
(編集する部分)
general =
{
name = "AirPlay%H"; //AirPlayサーバの名前(任意)
}
(USB-DACを出力として使うための変更部分)
(変更前)
alsa = {
output_device = “default”;
( HDMI使用時にUSB-DACを出力として使うための変更部分(DACのcardが 2 の場合))
alsa = {
output_device = “hw:2,0”;
(HDMI使用しないときにUSB-DACを出力として使うための変更部分(DACのcardが 1の場合))
alsa = {
output_device = “hw:1,0”;
Shairport起動
$ sudo systemctl start shairport-sync
OS 起動時にShairportが起動するよう設定する。
$ sudo systemctl enable shairport-sync