Raspberry Piで超簡易オーディオプレイヤー 「松」2020
Raspberry Piで超簡易オーディオプレイヤー 「松」2020
「ボタンを押すと、楽曲ファイルが演奏され、もう一度ボタンを押すと演奏が止まる」という、誠に単純な機能を持つ装置
Raspberry pi 公式タッチパネルディスプレイを接続したGUIを備えたデバイスに搭載する超簡易オーディオプレイヤー
I2Cインタフェースによる温湿度気圧センサを搭載することにも配慮している。
2020年版では、iPhoneやiPad, Macの「ミュージック」アプリの音声を出力できるAirPlayにも対応
インストールしたRaspbian OSのイメージ
2020-08-20-raspios-buster-armhf.img
ソフトウェアの設定は、共通のものを参照。
(追加内容)
lcd_rotate=2 $ pip3 install python-vlc $ pip3 install smbusRaspberry Pi 3 model B/B+
Raspberry Pi 7インチ公式タッチディスプレイ(800x480px)
USBオーディオDAコンバーターキット REV.C [AKI.DAC-U2704 REV.C]
USBメモリ(またはUSBメモリアダプタとmicro SDHC card)
赤外線焦電センサ
押しボタンスイッチ
ピンソケット
$ aplay -l
これでUSB-DACのcard番号を調べる
設定ファイル: /usr/share/alsa/alsa.conf
(修正前)
defaults.ctl.card 0 defaults.pcm.card 0(修正後)
defaults.ctl.card 1 defaults.pcm.card 1Python3を使って、実装する。
(ソースコード:ファイル名 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 VLCimport tkinter as tkimport RPi.GPIO as GPIOimport subprocessimport vlcimport pathlibimport randomimport reimport urllib.parseimport timeimport sysimport osclass 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 +350app.mainloop()ソースコードを(Python3)をtksimpleplayer.pyという名前で保存
場所は/home/pi/tksimpleplayer/
$ chmod +x /home/pi/tksimpleplayer/tksimpleplayer.py編集するファイル: /home/pi/.config/lxsession/LXDE-pi/autostart
最後に下記のエントリを追加
@/home/pi/tksimpleplayer/tksimpleplayer.pyファイル名 /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 GPIOimport timeimport 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()編集するファイル: /home/pi/.config/lxsession/LXDE-pi/autostart
最後に下記のエントリを追加
@/home/pi/ctrl_backlight/sensor_backlight_on_off.pyiPhoneやiPad, Macの「ミュージック」アプリの音声を出力できるAirPlayに対応させる設定
必要なライブラリおよびgitのインストール
$ sudo apt install git autoconf libdaemon-dev libpopt-dev libconfig-dev libasound2-dev libpulse-dev \ libavahi-client-dev libssl-dev libsoxr-dev $ 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コマンド 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”; $ sudo systemctl start shairport-sync $ sudo systemctl enable shairport-sync