Referencia: use Raspberry Pi para construir un reproductor de música
(1) Una Raspberry Pi 4B
(2) Un altavoz
1 instalación de mtool del reproductor de música
1.1 Instalar mtool
mtool es un reproductor de música escrito en python, de código abierto en gitee y github, controlado principalmente por línea de comandos.
Enlace de descarga: https://gitee.com/shadowlesswalker/mtool.git.
$ cd / usr / Este directorio debe ser
$ sudo git clone https://gitee.com/shadowlesswalker/mtool.git mtool
$ cd mtool
$ sudo chmod 777 / usr / mtool /
$ sudo chmod 777 ./*Otorgar permiso
$ sudo ln -s / usr / mtool / mtoolctl / usr / bin / mtoolctl #Crear
enlace suave
$ sudo apt install python3-pygame dependencia de instalación
$ pip3 install configparser dependencia de instalación
mtool es un programa en modo C / S (servidor / cliente) .Después de encender el servidor, escuchará el puerto UDP (el valor predeterminado es 127.0.0.1:6666) y luego aceptará los comandos del cliente para controlar la reproducción.
1.2 Usar mtool
(1) El archivo de configuración
mtool usa el archivo de configuración ./mtool.conf para establecer el volumen predeterminado, la lista de reproducción, el modo de reproducción, el puerto, etc.
[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) Inicie y cierre el servidor
python3 /usr/mtool/mtool.py --server start
如果要在后台运行服务,执行命令:
$ nohup python3 /usr/mtool/mtool.py --server start > /usr/mtool/log 2>&1 &
0 y 1 y 2 representan la entrada estándar, la salida estándar y la salida de mensaje de error estándar, respectivamente, y se pueden usar para especificar la entrada o salida estándar que necesita ser redirigida.
En el uso general, el valor predeterminado es la salida estándar 1, que redirige la salida estándar a / usr / mtool / log.
2> & 1 Redirigir el mensaje de error a la salida estándar.
(3) Ver el estado del servidor
(4) Cerrar el servicio
$ kill -9 4033 Cerrar el servicio
$ mtool -c salir Cerrar el servicio
Tenga en cuenta que es necesario reiniciar el servicio después de agregar nuevos archivos de música.
1.3 Comandos comunes
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 Inicio automático en el arranque
$ vim /home/pi/.bashrc
nohup python3 /usr/mtool/mtool.py --server start > /usr/mtool/log 2>&1 &
Primero inicie el servidor manualmente. Aunque el inicio automático se configura antes del inicio, solo puede tener efecto el próximo inicio.
$ fuente /home/pi/.bashrc
1.5 Conexión de teléfono móvil
Puede instalar herramientas ssh en su teléfono, como JuiceSSH (recomendado).
1.6 Resolución de excepción
Los archivos en ejecución de Python siempre tendrán problemas confusos. Para resolver este problema, agregue al principio del archivo: Codificación de caracteres Python de dos maneras:
# coding=utf-8
或
# -*- coding:utf-8 -*-
2 Configure la Raspberry Pi para que se reproduzca con regularidad
2.1 sintaxis de crontab
Sintaxis crontab [-u usuario] archivo
Sintaxis crontab [-u usuario] {-l | -r | -e}
Donde
-e: ejecuta un editor de texto para establecer la programación.
-r: Elimina la programación actual.
-l: muestra la programación actual.
El formato de hora es el siguiente: f1 f2 f3 f4 f5 programa
donde
f1 es el minuto,
f2 es la hora,
f3 es el día del
mes, f4 es el mes,
f5 es el día de la semana y el
programa es el programa para ser ejecutado.
2.2 Escribir script de reproducción
$ 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 Agregar una tarea programada
$ crontab -e
10 17 * * * /usr/mtool/start-play-music.sh >> /usr/mtool/log #每天下午17:10开始播放
15 17 * * * mtool -c stop #每天早上17:15停止播放
3 código fuente
# -*- 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
4 control por voz
Python implementa el reconocimiento de voz llamando a la API de Baidu
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)