树莓派-3-制作音乐播放器+语音控制

参考:使用树莓派打造一个音乐播放器

(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.2.1 修改配置文件mtool.conf

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    

1.2.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-制作音乐播放器+语音控制_第1张图片
(1)查看服务端状态
树莓派-3-制作音乐播放器+语音控制_第2张图片
(2)关闭服务

$ kill -9 4033关闭服务
$ mtool -c exit关闭服务

注意添加完新的音乐文件后需要重启服务。

1.2.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.3 开机自动播放音乐

1.3.1 挂载U盘歌曲列表

(1)U盘开机自动被挂载
目录/media/pi/Linux Mint
树莓派-3-制作音乐播放器+语音控制_第3张图片
当然也可以自己挂载后取消挂载

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

1.3.2 配置开机自启动

$ vim /home/pi/.bashrc

nohup python3 /usr/mtool/mtool.py --server start > /usr/mtool/log 2>&1  &

先手动启动服务端,虽然前面配置了开机自启动,但也只能下次开机才能生效。
$ source /home/pi/.bashrc

1.3.3 播放音乐

mtool -c play播放
mtool -c stop停止
mtool -c exit退出
mtool -c vol=0.5   #设置音量
source /home/pi/.bashrc
mtool -c exit

1.4 手机连接

可以在手机上安装ssh工具,比如JuiceSSH(推荐)。

1.5 异常解决

在这里插入图片描述
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 表示要执行的程序。
树莓派-3-制作音乐播放器+语音控制_第4张图片

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

4 语音控制

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)


你可能感兴趣的:(树莓派,树莓派)