参考:使用树莓派打造一个音乐播放器
(1)树莓派4B一个
(2)音箱一个
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),然后接受来自客户端的命令去控制播放。
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
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 将错误信息重定向到标准输出。
(1)查看服务端状态
(2)关闭服务
$ kill -9 4033关闭服务
$ mtool -c exit关闭服务
注意添加完新的音乐文件后需要重启服务。
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)U盘开机自动被挂载
目录/media/pi/Linux Mint
当然也可以自己挂载后取消挂载
sudo mount /dev/sda4 /home/pi/mnt/windows挂载
sudo unmount /home/pi/mnt/windows取消挂载
(2)修改配置文件mtool.conf
根据自动挂载配置。
[player]
list = music3
volume = 0.4
port = 6666
index = 1011
next = random
[playlists]
music = music
en-listen = /var/share/en-listen
music2 = /media/pi/Linux Mint/music
music3 = /media/pi/Linux Mint/mymusic
$ vim /home/pi/.bashrc
nohup python3 /usr/mtool/mtool.py --server start > /usr/mtool/log 2>&1 &
先手动启动服务端,虽然前面配置了开机自启动,但也只能下次开机才能生效。
$ source /home/pi/.bashrc
mtool -c play播放
mtool -c stop停止
mtool -c exit退出
mtool -c vol=0.5 #设置音量
source /home/pi/.bashrc
mtool -c exit
可以在手机上安装ssh工具,比如JuiceSSH(推荐)。
python运行文件是总会出现乱码问题,为了解决这个问题,在文件开头加上:python 字符编码的两种方式写法:
# coding=utf-8
或
# -*- coding:utf-8 -*-
语法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 表示要执行的程序。
$ 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 #开始播放
$crontab -e
10 17 * * * /usr/mtool/start-play-music.sh >> /usr/mtool/log #每天下午17:10开始播放
15 17 * * * mtool -c stop #每天早上17:15停止播放
# -*- 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
python通过调用百度api实现语音识别
import os
# 播放
os.popen("/usr/bin/mtool -c play")
# 播放文件
os.popen("/usr/bin/aplay speech.wav")
# -*- coding:utf-8 -*-
import wave
import requests
import time
import base64
from pyaudio import PyAudio, paInt16
import os
''' 你的APPID AK SK 参数在申请的百度云语音服务的控制台查看'''
APP_ID = '237***29'
API_KEY = 'DRGC6L***hG5RsWOQZbC'
SECRET_KEY = 'OnCmaG9S***1GFq5nTnKEfukXQ'
framerate = 16000 # 采样率
num_samples = 2000 # 采样点
channels = 1 # 声道
sampwidth = 2 # 采样宽度2bytes
filepath = 'speech.wav'
def getToken(API_KEY, SECRET_KEY):
base_url = "https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s"
host = base_url % (API_KEY, SECRET_KEY)
res = requests.post(host)
return res.json()['access_token']
def save_wave_file(filepath, data):
wf = wave.open(filepath, 'wb')
wf.setnchannels(channels)
wf.setsampwidth(sampwidth)
wf.setframerate(framerate)
wf.writeframes(b''.join(data))
wf.close()
def my_record(filepath):
pa = PyAudio()
stream = pa.open(format=paInt16, channels=channels,
rate=framerate, input=True,
frames_per_buffer=num_samples)
my_buf = []
t = time.time()
print('正在录音...')
while time.time() < t + 4: # 秒
string_audio_data = stream.read(num_samples)
my_buf.append(string_audio_data)
print('录音结束...')
# 写入文件
save_wave_file(filepath, my_buf)
stream.close()
def speech2text(file, token, dev_pid=1537):
# 读取音频文件
with open(file, 'rb') as f:
speech_data = f.read()
FORMAT = 'wav'
RATE = '16000'
CHANNEL = 1
CUID = APP_ID
SPEECH = base64.b64encode(speech_data).decode('utf-8')
data = {
'format': FORMAT,
'rate': RATE,
'channel': CHANNEL,
'cuid': CUID,
'len': len(speech_data),
'speech': SPEECH,
'token': token,
'dev_pid': dev_pid
}
url = 'https://vop.baidu.com/server_api'
headers = {'Content-Type': 'application/json'}
print('正在识别...')
r = requests.post(url, json=data, headers=headers)
Result = r.json()
print('识别完成')
if 'result' in Result:
return Result['result'][0]
else:
return Result
if __name__ == '__main__':
while True:
# 听到ding后一秒开始录音并识别
os.popen("/usr/bin/aplay ding.wav")
time.sleep(1)
my_record(filepath)
TOKEN = getToken(API_KEY,SECRET_KEY)
result = speech2text(filepath, TOKEN)
print(result)
if "同学" in result:
os.popen("/usr/bin/aplay dong.wav")
time.sleep(1)
if "打开音乐" in result:
os.popen("/usr/bin/mtool -c play")
if "关闭音乐" in result:
os.popen("/usr/bin/mtool -c stop")
time.sleep(5)