(1)树莓派4B一个
(2)音箱一个
1 音乐播放器mtool安装
1.1 安装mtool
mtool是一个用python写的音乐播放器,在gitee和github上开源,主要用命令行进行控制。
下载地址:https://gitee.com/shadowlesswalker/mtool.git。
$ cd /usr/必须此目录
$ sudo git clone https://gitee.com/shadowlesswalker/mtool.git mtool
$ cd mtool
$ sudo chmod 777 /usr/mtool/
$ sudo chmod 777 ./*赋予权限
$ sudo ln -s /usr/mtool/mtoolctl /usr/bin/mtoolctl
#创建软链接
$ sudo apt install python3-pygame安装依赖
$ pip3 install configparser安装依赖
mtool是一个C/S(服务器/客户端)模式的程序,服务器开启后会监听UDP端口(默认为127.0.0.1:6666),然后接受来自客户端的命令去控制播放。
1.2 使用mtool
(1)配置文件
mtool使用配置文件./mtool.conf来设置默认的音量、播放列表、播放模式、端口等。
[player]
list = music #默认播放列表,列表名必须出现在playlists节中
volume = 0.8 #默认音量
port = 6666 #服务器端口
index = 2 #当前播放文件索引(相对于当前播放列表)
next = next #播放模式next|loop|random 分别对应顺序播放|单曲循环|随机播放
[playlists] #定义播放列表节,可设置多个列表,其名称必须唯一
music = music #路径为./music/
en-listen = /var/share/en-listen
(2)启动和关闭服务端
python3 /usr/mtool/mtool.py --server start
如果要在后台运行服务,执行命令:
$ nohup python3 /usr/mtool/mtool.py --server start > /usr/mtool/log 2>&1 &
0和1和2分别表示标准输入、标准输出和标准错误信息输出,可以用来指定需要重定向的标准输入或输出。
在一般使用时,默认的是标准输出1,将标准输出重定向到/usr/mtool/log。
2>&1 将错误信息重定向到标准输出。
(3)查看服务端状态
(4)关闭服务
$ kill -9 4033关闭服务
$ mtool -c exit关闭服务
注意添加完新的音乐文件后需要重启服务。
1.3 常用命令
mtool -c play #播放
mtool -c play|pause|resume开始|暂停|继续播放
mtool -c stop停止播放
mtool -c vol=0.5 #设置音量
mtool -c lists #查看可用的播放列表
mtool -c list #列出当前播放列表中的音乐文件
mtool -c next=random|next|loop #设置播放顺序
mtool -c playf=zui #切换为单曲循环(next=loop),并且播放文件名中包含zui的文件
1.4 开机自启动
$ vim /home/pi/.bashrc
nohup python3 /usr/mtool/mtool.py --server start > /usr/mtool/log 2>&1 &
先手动启动服务端,虽然前面配置了开机自启动,但也只能下次开机才能生效。
$ source /home/pi/.bashrc
1.5 手机连接
可以在手机上安装ssh工具,比如JuiceSSH(推荐)。
1.6 异常解决
python运行文件是总会出现乱码问题,为了解决这个问题,在文件开头加上:python 字符编码的两种方式写法:
# coding=utf-8
或
# -*- coding:utf-8 -*-
2 设置树莓派定时播放
2.1 crontab语法
语法crontab [ -u user ] file
语法crontab [ -u user ] { -l | -r | -e }
其中
-e : 执行文字编辑器来设定时程表。
-r : 删除目前的时程表。
-l : 列出目前的时程表。
时间格式如下:f1 f2 f3 f4 f5 program
其中
f1 是表示分钟,
f2 表示小时,
f3 表示一个月份中的第几日,
f4 表示月份,
f5 表示一个星期中的第几天,
program 表示要执行的程序。
2.2 编写播放脚本
$ vim /usr/mtool/start-play-music.sh
$ sudo chmod 777 /usr/mtool/start-play-music.sh
#!/bin/bash
_dir="/usr/mtool"
mtool -c playlist=music #切换到music播放列表
mtool -c vol=0.5 #音量放到一半
mtool -c next=random #设置随机播放
mtool -c play #开始播放
2.3 添加定时任务
$crontab -e
10 17 * * * /usr/mtool/start-play-music.sh >> /usr/mtool/log #每天下午17:10开始播放
15 17 * * * mtool -c stop #每天早上17:15停止播放
3 源码
# -*- coding:utf-8 -*-
'''
code by Shadow(山斗) from China.
mtool(music tool) is a simple audio player mainly running on linux.
mtool is used with command line interface.you can control the player in your own code to play,stop musics.
mtool is C/S architecture program. it runs a UDP server at the default port:127.0.0.1:6666 and receives commands from clients to control the player.
mtool use the config file ./mtool.conf .
In mtool.conf , you can set the default values like volume,playing order,default playlist in the player section,
and set some playlists in the playlists section.Each playlist is a path in which should include music files.
'''
import pygame
import time
import sys, getopt
from socket import *
import threading
import re
import configparser
import os
import random
_dir=os.path.dirname(os.path.abspath(__file__))
_conf_file=os.path.join(_dir,'./mtool.conf')
print('hello from mtool by shadow')
mfile='./qiansixi.mp3'
isactive=True #True to run server normally, False to quit Server
server_status='stopped' #stopped|playing|paused
mnext='stop' #what to do for the next playing:stop|next|loop|random
vol=0.5 #volume
mlist=[] #audio paths of current playlist
mindex=0 #index of playlist for next play
mport=6666 #Server UDP port
playlist_name='default' #current playlist name
playlist_path=os.path.join(_dir,'music')
def loadlist(listname):
'''
load audios from a playlist,the palylist name must be specified in mtool.conf
'''
global mlist,mfile,playlist_name,playlist_path
listpath=''
conf=configparser.ConfigParser()
conf.read(_conf_file)
if listname in conf['playlists'].keys():
playlist_name=listname
playlist_path=conf['playlists'][playlist_name]
if os.path.isabs(playlist_path)==False:
playlist_path=os.path.join(_dir,playlist_path)
else:
print("invalid playlist name:%s"%(listname))
return
if os.path.exists(playlist_path):
mlist.clear()
for iroot,idir,flist in os.walk(playlist_path):
for f in flist:
mlist.append(os.path.join(iroot,f))
else:
print("playlist %s(%s) doesn't exist"%(listname,playlist_path))
mindex=0
if len(mlist)>0:
mfile=mlist[0]
print("load playlist %s(%s) from %s"%(listname,len(mlist),playlist_path))
def _init():
global mlist, vol, mport,mnext
conf=configparser.ConfigParser()
conf.read(_conf_file)
vol=float(conf['player']['volume'])
print("volume:%s"%(vol))
mport=int(conf['player']['port'])
mindex=int(conf['player']['index'])
mnext=conf['player']['next']
loadlist( conf['player']['list'])
pygame.mixer.init()
def play():
global isactive,mfile,mlist,vol,mnext,mindex,server_status
if mnext=='loop':
pass
elif mnext=='next':
mindex=mindex+1
if mindex >= len(mlist):
mindex=0
mfile=mlist[mindex]
elif mnext=='random':
mindex=random.randint(0,len(mlist)-1)
mfile=mlist[mindex]
try:
print("vol:%s,next:%s,playing file %s"%(vol,mnext,mfile))
track=pygame.mixer.music.load(mfile)
pygame.mixer.music.set_volume(vol)
pygame.mixer.music.play()
server_status='playing'
except Exception as e:
print(e)
def stop():
global server_status
server_status='stopped'
pygame.mixer.music.stop()
print('music stopped')
def pause():
global server_status
server_status='paused'
pygame.mixer.music.pause()
print('music paused')
def unpause():
global server_status
server_status='playing'
pygame.mixer.music.unpause()
print('music resume')
def _saveconfig():
conf=configparser.ConfigParser()
conf.read(_conf_file)
conf['player']['volume']=str(vol)
conf['player']['list']=playlist_name
conf['player']['next']=mnext
conf['player']['index']=str(mindex)
with open(_conf_file,'w') as f:
conf.write(f)
def command(opts,udpsvr=None,cltaddr=None):
'''
deal commands received from clients to control playing status
'''
global isactive, mnext, vol,mfile
cmd=opts[0]
response=None
if cmd=='play':
play()
response="playing "+mfile
elif cmd=='playi':
try:
i=init(opt[1])
if i>=0 and i<len(mlist):
mindex=i
play()
except Exception as e:
print(e)
elif cmd=='playf':
try:
mfile=opts[1]
if os.path.isabs(mfile)==False:
for x in mlist:
if mfile in os.path.basename(x):
mfile=x
mnext='loop'
play()
response="playing "+mfile
except Exception as e:
print(e)
elif cmd=='stop':
stop()
elif cmd=='pause':
pause()
elif cmd=='resume':
unpause()
elif cmd=='vol':
try:
vol=float(opts[1])
pygame.mixer.music.set_volume(vol)
response="volume="+str(vol)
_saveconfig()
except Exception as e:
print(e)
elif cmd=='next':
try:
if opts[1] in ('loop','next','random','stop'):
mnext=opts[1]
response="next="+mnext
_saveconfig()
except Exception as e:
print(e)
elif cmd=='playlist':
try:
loadlist(opts[1])
response="playlist=%s(%s,%s)"%(playlist_name,len(mlist),playlist_path)
_saveconfig()
except Exception as e:
print(e)
elif cmd=='info':
if udpsvr and cltaddr:
response="Server:Running\nnext=%s,vol=%s,status=%s\nplaylist=%s(%s,%s)\nfile=%s"%(mnext,vol,server_status,playlist_name,len(mlist),playlist_path,mfile)
elif cmd=='lists':
conf=configparser.ConfigParser()
conf.read(_conf_file)
response=''
for x in conf['playlists']:
response=response+'\n'+x
elif cmd=='list':
response=''
for x in mlist:
response=response+'\n'+x
elif cmd=='saveconf':
_saveconfig()
elif cmd=='exit':
isactive=False
response="server exitted"
if response:
udpsvr.sendto(response.encode('utf-8'),cltaddr)
def thcontrol():
'''
this function starts a UDP socket which binds at the port 127.0.0.1:6666
and receives commands to control the music playback.
'''
global isactive,vol,mport
udpsvr=socket(AF_INET,SOCK_DGRAM)
udpsvr.bind(('127.0.0.1',mport))
print("server started and bind to 127.0.0.1:6666")
while isactive:
data,addr=udpsvr.recvfrom(1024)
cmd=data.decode('utf-8')
print("msg from %s :%s"%(addr,cmd))
opts=re.split("=",cmd)
try:
command(opts,udpsvr,addr)
except Exception as e:
print(e)
def sendcmd(cmd):
global mport
udpclt=socket(AF_INET,SOCK_DGRAM)
udpclt.settimeout(1)
udpclt.sendto(cmd.encode('utf-8'),('127.0.0.1',mport))
try:
data,addr=udpclt.recvfrom(1024)
if data:
msg=data.decode('utf-8')
print('Server Response:\n'+msg)
except Exception as e:
pass
def _notify():
'''
loop to check the status of playing
'''
try:
if pygame.mixer.music.get_busy()==0:
if mnext!='stop' and server_status=='playing':
play()
except Exception as e:
print(e)
if isactive:
t=threading.Timer(2,_notify)
t.start()
def main(argv):
global isactive
try:
opts, args = getopt.getopt(argv,"hc:",["server=","test"])
except getopt.GetoptError:
print('-h //help information')
exit(1)
if len(opts)==0:
print('hello from mtool by shadow')
exit(0)
for opt,arg in opts:
if opt=='--server' and arg=='start':
print('starting server')
_init()
threading._start_new_thread(thcontrol,())
elif opt=="-c":
sendcmd(arg)
exit(0)
elif opt=='-h':
print('--server start //start server')
print('-c info|play|pause|resume|stop|list|lists|exit|vol=0.5')
print('-c playf=filename //loop to play a music file')
print('-c next=stop|loop|next|random')
print('-c playlist=playlistname //note that the playlistname must be specified in mtool.conf')
exit(0)
else:
print('-h //help information')
exit(0)
threading.Timer(2,_notify).start()
while isactive:
time.sleep(1)
print('mtool exit')
exit(0)
if __name__=='__main__':
main(sys.argv[1:])
else:
pass