Raspberry Pi -3-reproductor de música + control de voz

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
Inserte la descripción de la imagen aquí
$ 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.
Inserte la descripción de la imagen aquí
(3) Ver el estado del servidor
Inserte la descripción de la imagen aquí
(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

Inserte la descripción de la imagen aquí
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.
Inserte la descripción de la imagen aquí

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)


Supongo que te gusta

Origin blog.csdn.net/qq_20466211/article/details/114174332
Recomendado
Clasificación