Raspberry Piで超簡易オーディオプレイヤー 「竹」2020年版

小型液晶ディスプレイ搭載

AirPlay対応プレイヤー

「ボタンを押すと、楽曲ファイルが演奏され、もう一度ボタンを押すと演奏が止まる」という、誠に単純な機能を持つ装置

Raspberry Piに液晶キャラクタディスプレイ(LCD)とDACデバイスを接続した超簡易オーディオプレイヤー

2020年版では、iPhoneやiPad, Macの「ミュージック」アプリの音声を出力できるAirPlayにも対応

Raspberry Pi OSのインストール

インストールしたOSイメージ

2020-08-20-raspios-buster-armhf.img

基本的な設定は、(すべてのデバイスで共通)Raspberry Pi OS の基本設定を参照。

回路図

DACデバイスを使う設定

本機では、MM5102 というDACデバイスを使っているため、OSでDAC関連の設定が必要

ファイル:/etc/modules

(次の行を追加する)

snd_soc_bcm2708
bcm2708_dmaengine
snd_soc_pcm5102a
snd_soc_hifiberry_dac

ファイル:/boot/config.txt

(次の行を追加する)

dtoverlay=hifiberry-dac

Sound Cardとdevice numberの確認

$ aplay -l

これでUSB-DACのcard番号を調べる

ファイル:/home/pi/.asoudrc

HDMIディスプレイを接続したときと外したときで、DACサウンドカードの番号が変化するので、設定が異なる。

(変更箇所)HDMIディスプレイを使用する場合

 pcm.output {
         type hw
         card 2
 }

 ctl.!default {
         type hw
         card 2
 }

(変更箇所)HDMIディスプレイを接続しない場合

 pcm.output {
         type hw
         card 1
 }

 ctl.!default {
         type hw
         card 1
 }


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


音楽再生アプリのための設定

PythonでVLCを制御するためのパッケージのインストール

 $ pip3 install python-vlc

I2C用Python moduleのインストール

 $ pip3 install smbus


オーディオプレイヤーのソフトウェア

Python3を使って、実装する。

(ソースコード:ファイル名 lcdsimpleplayer.py)

#!/usr/bin/env python3
# coding:utf-8

# RapsberryPi Simple Music Player with LCD (AQM0802)

import RPi.GPIO as GPIO
import subprocess
import vlc
import pathlib
import random

import smbus
import time

import sys
import os

class LcdAqm0802:
    #LCD AE-AQM0802 controller class
    #参考 https://www.denshi.club/pc/raspi/i2caqmlcdarduinode1-aqm0802.html
    # class valiables
    i2c = smbus.SMBus(1) # 1 is bus number
    addr02=0x3e #LCD のアドレス
    _command=0x00
    _data=0x40
    _clear=0x01
    _home=0x02
    display_On=0x0f
    display_Off=0x08
    cursor_Off=0x0c
    LCD_2ndline=0x40+0x80
    #LCD AQM0802/1602
    def __init__ (self):
            self.command(0x38) # Function set
            self.command(0x39) # Function set
            self.command(0x14) # Internal OSC frequency
            self.command(0x70) # Contrast set
            self.command(0x56) # Power/ICON/Contrast control
            self.command(0x6c) # Follower control
            self.command(0x38) # Function set
            self.command(LcdAqm0802.cursor_Off) # dislay On , cursor off
            self.command(LcdAqm0802._clear) # Clear Display


    def command( self, code ):
        LcdAqm0802.i2c.write_byte_data(LcdAqm0802.addr02, LcdAqm0802._command, code)
        time.sleep(0.1)

    def write( self, message ):
            mojilist=[]
            for moji in message:
                    mojilist.append(ord(moji))
            LcdAqm0802.i2c.write_i2c_block_data(LcdAqm0802.addr02, LcdAqm0802._data, mojilist)
            time.sleep(0.1)

    def moveHome(self):
        self.command(LcdAqm0802._home)

    def move2ndLine(self):
        self.command(LcdAqm0802.LCD_2ndline)

    def clear(self):
        self.command(LcdAqm0802._clear)

    def displayOff(self):
        self.command(LcdAqm0802.display_Off)

class SimplePlayer:
    def __init__(self, master=None):
        self.set_gpio()
        self.status = "STOP"

        # LCDの生成
        self.lcd = LcdAqm0802()
        self.lcd.clear()
        self.lcd.moveHome()
        self.lcd.write("--BOOT--")

        #create VLC instance
        self.vlcp = vlc.MediaListPlayer() # VLC Media List Player

        # VLC Volume 50%
        #self.VLC_volume = 50
        # VLC Volume 80%
        self.VLC_volume = 80
        self.vlcp_media_player=self.vlcp.get_media_player() # get media player instance from media list palyer

        #self.vlcp_media_player.audio_output_device_set(None, "hw:2,0")#set device DAC (HDMI connected)
        #self.vlcp_media_player.audio_output_device_set(None, "hw:1,0")#set device DAC
        self.vlcp_media_player.audio_set_volume(self.VLC_volume) # volume set to media player

        self.vlcp.set_playback_mode(vlc.PlaybackMode.loop) #vlc playback mode をloop(play listの繰り返しに)

        # Play Listの生成
        self.vlcp.set_media_list( self.get_media_list() ) #set media list

        # 起動メッセージ
        self.lcd.clear()
        self.lcd.moveHome()
        self.lcd.write("miniVLCp")

    def on_closing(self):
        if self.status == "PLAY" :
            self.vlcp.stop() #曲の停止
            print("--STOP--")

        self.lcd.clear() #LCD 表示を消去
        self.lcd.displayOff() #LCD 表示終了

        GPIO.cleanup(22) # Cleanup GPIO 22


    def get_media_list(self):
        #メディアリストを更新し、返す。
        print("--LIST--")
        p_temp_media_list = vlc.MediaList() # MediaListを生成
        #メディアファイルのpathのリストを取得
        p_temp = pathlib.Path('/media/pi/64GBSDXC') #USBメモリのパスを設定(Raspbian Desktop)
        #p_temp = pathlib.Path('/media/usb') #USBメモリのパスを設定(Raspbian LITE + usbmount)
        length_mrl_list = 0 #mrl_listの要素数を初期化
        while length_mrl_list <= 0 : #有効な要素数が得られなければ

            # デバッグ用メッセージを2行目に出力
            self.lcd.move2ndLine()
            self.lcd.write("--LIST--")
            print ("LISTING")

            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の要素数を取得

        # デバッグ用メッセージをクリア
        self.lcd.move2ndLine()
        self.lcd.write("STANDBY-")
        print ("LISTING DONE")

        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):
        if self.status == "STOP" :
            self.status = "PLAY"
            print("--PLAY Ready--")
            # Music media files in /media/pi directory
            self.vlcp.play() # 曲の再生
            #再生中にメディアリストを再構築する
            self.vlcp.set_media_list( self.get_media_list() ) #set media list

            self.lcd.move2ndLine() #LCD 2行目に移動
            self.lcd.write("--PLAY--") #LCDにPLAYと表示
            print("-- PLAYING --")

        else:
            self.status = "STOP"
            self.vlcp.stop() #曲の停止
            print("--STOP--")
            self.lcd.move2ndLine() #LCD 2行目に移動
            self.lcd.write("--STOP--")
            print("-- STOPPED --")


    def play_sound_gpio(self):
        print("EDGE DETECTED!")
        waittimercounter = 0
        while True:
            time.sleep(0.4) # wait 0.4 seconds
            charbar = "********" # シャットダウンまでにLCDに表示する文字列
            print("SW: PRESSED")
            if GPIO.input(22) == True:
                # ボタン(PORT22)が押されていないときは
                print("SW: RELEASED")
                break# Exit
            else:
                # ボタン(PORT22)が押されているときは
                self.lcd.move2ndLine()
                self.lcd.write(charbar[:waittimercounter])
                waittimercounter += 1 # ボタンの押された回数のカウンタを1増やす
                if waittimercounter >= 7: # ボタンのカウンタが規定数になったら終わり
                    print('SW: LONG PRESSED')
                    self.on_closing() #終了時処理
                    os.system("sudo shutdown -h now") # System Shut down
        # VLC palyer control by GPIO
        self.vlc_play()


    def set_gpio(self):
        GPIO.setmode(GPIO.BCM)  #GPIO Access by BCM
        GPIO.setup(22, GPIO.IN, pull_up_down = GPIO.PUD_UP)   #BCM PIN-22 as Input



####################################################
app = SimplePlayer()
while True: # Infinity LOOP
    print("Wait for EDGE")
    GPIO.wait_for_edge(22, GPIO.FALLING)#ボタンが押されるまで待つ
    app.play_sound_gpio() #再生処理

ソースコードの保存

ソースコードを(Python3)をlcdsimpleplayer.pyという名前で保存

場所は/home/pi/simpleplayer/

ソースコードを実行可能に設定する。

$ chmod +x /home/pi/simpleplayer/lcdsimpleplayer.py

OSの起動時に自動的に起動するように設定する。

編集するファイル: /home/pi/.config/lxsession/LXDE-pi/autostart

最後に下記のエントリを追加

@pcmanfm -d
@/home/pi/simpleplayer/lcdsimpleplayer.py