2018-09-13 DuerOS + Raspberry3B+ + 麦克风阵列

DuerOS的个人开发者版本在官方的介绍中已经停止申请, 代码也大半年没人更新了, 不过好在这个版本需要的麦克风阵列可以自行在网上购买, 相关的代码也是托管在Github上, 于是在研究了网上的各位大神的文章后, 终于把DuerOS运行起来了...

选购硬件

树莓派3B+: 网上到处有卖, 需要买相关的套件, 单独的板子是无法玩的.
麦克风阵列: ReSpeaker树莓派4麦克风阵列扩展板

烧录系统

在树莓派上烧录raspbian-2018.4.18这个镜像. 千万不要烧录官网最新的镜像, 最新的镜像可能无法安装某些第三方库(比如pyaudio死活安装不了). 也不要安装DuerOS_For_Raspberry这个镜像, 因为这个镜像是针对Raspberry3B的, 3B+安装DuerOS这个镜像后会无法开机

安装声卡驱动

按照官方的文档去安装声卡驱动就行. 官方文档: http://wiki.seeedstudio.com/cn/ReSpeaker_4_Mic_Array_for_Raspberry_Pi/

运行装DuerOS

官方的文档: https://developer.baidu.com/forum/topic/show/244796?pageNo=1 安装完成后会有两个问题:

  1. 运行enter_trigger_start.sh 脚本, 服务器有回应但是没有声音
  2. 无法运行wakeup_trigger_start.sh 脚本

解决第一个问题
修改/home/pi/DUerOS-Python-Client/app/framework 目录下面的play.py文件

# -*- coding: utf-8 -*-

"""
基于GStreamer的播放模块
"""

import gi
import logging
import app.app_config as app_config

gi.require_version('Gst', '1.0')
from gi.repository import Gst

Gst.init(None)

logging.basicConfig(level=app_config.LOGGER_LEVEL)
logger = logging.getLogger(__file__)

class Player(object):
    '''
    播放器实现类
    '''

    def __init__(self):
        # create a new gstreamer pipeline
        self.pipeline = Gst.Pipeline.new("mypipeline")

        # add a file source to the pipeline
        self.urisrc = Gst.ElementFactory.make("uridecodebin","source")
        self.urisrc.connect("pad-added", self.decode_link)
        self.pipeline.add(self.urisrc)

        # add a convertor to the pipeline
        self.convert = Gst.ElementFactory.make("audioconvert","convert")
        self.pipeline.add(self.convert)

        # add an alsa sink to the pipeline and link it to theconvertor
        self.sink = Gst.ElementFactory.make("alsasink", "sink")
        self.pipeline.add(self.sink)
        self.convert.link(self.sink)

        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.enable_sync_message_emission()

    def decode_link(self, dbin, pad):
        pad.link(self.convert.get_static_pad("sink"))


    def play(self, uri):
        '''
        播放
        :param uri:播放资源地址
        :return:
        '''
        self.convert.unlink(self.sink)
        self.pipeline.set_state(Gst.State.NULL)
        
        # start playing
        logger.info('播放资源 uri: {}'.format(uri))
        self.urisrc.set_property("uri", uri)#'http://zhangmenshiting.qianqian.com/data2/music/067eaeefb2eb0498fe404edba700c6b1/599610520/599610520.mp3?xcode=d8bf0fee68258ce2350a5c038dd045b5')
        self.convert.link(self.sink)
        self.pipeline.set_state(Gst.State.PLAYING)

    def stop(self):
        '''
        停止
        :return:
        '''
        logger.info('停止')
        self.urisrc.unlink(self.convert)
        self.convert.unlink(self.sink)
        self.pipeline.set_state(Gst.State.NULL)

    def pause(self):
        '''
        暂停
        :return:
        '''
        logger.info('暂停')
        self.pipeline.set_state(Gst.State.PAUSED)

    def resume(self):
        '''
        回复播放
        :return:
        '''
        logger.info('回复播放')
        self.pipeline.set_state(Gst.State.PLAYING)

    def add_callback(self, name, callback):
        '''
        播放状态回调
        :param name: {eos, ...}
        :param callback: 回调函数
        :return:
        '''
        if not callable(callback):
            return

        def on_message(bus, message):
            callback()

        self.bus.connect('sync-message::{}'.format(name), on_message)

    @property
    def duration(self):
        '''
        播放时长
        :return:
        '''
        success, duration = self.pipeline.query_duration(Gst.Format.TIME)
        if success:
            return int(duration / Gst.MSECOND)

    @property
    def position(self):
        '''
        播放位置
        :return:
        '''
        success, position = self.pipeline.query_position(Gst.Format.TIME)
        if not success:
            position = 0

        return int(position / Gst.MSECOND)

    @property
    def state(self):
        '''
        播放状态
        :return:
        '''
        # GST_STATE_VOID_PENDING        no pending state.
        # GST_STATE_NULL                the NULL state or initial state of an element.
        # GST_STATE_READY               the element is ready to go to PAUSED.
        # GST_STATE_PAUSED              the element is PAUSED, it is ready to accept and process data.
        #                               Sink elements however only accept one buffer and then block.
        # GST_STATE_PLAYING             the element is PLAYING, the GstClock is running and the data is flowing.
        _, state, _ = self.pipeline.get_state(Gst.SECOND)
        return 'FINISHED' if state != Gst.State.PLAYING else 'PLAYING'

修改/home/pi/DUerOS-Python-Client/app 目录下面的enter_trigger_main.py文件

# -*- coding: utf-8 -*-
'''
通过输入[Enter]触发唤醒状态
'''
import logging
from sdk.dueros_core import DuerOS

from app.framework.mic import Audio
from app.framework.player import Player
from app.utils.prompt_tone import PromptTone

logging.basicConfig(level=logging.INFO)


def directive_listener(directive_content):
    '''
    云端下发directive监听器
    :param directive_content:云端下发directive内容
    :return:
    '''
    content = u'云端下发directive:%s' % (directive_content)
    logging.info(content)


def main():
    # 创建录音设备(平台相关)
    audio = Audio()
    # 创建播放器(平台相关)
    player = Player()

    dueros = DuerOS(player)
    dueros.set_directive_listener(directive_listener)

    audio.link(dueros)

    dueros.start()
    audio.start()

    #prompt_tone_player = PromptTone()

    while True:
        try:
            try:
                print '\n'
                input('单击[Enter]建,然后发起对话\n')
            except SyntaxError:
                pass
            # 唤醒态提示音
            #prompt_tone_player.play()
            dueros.listen()
        except KeyboardInterrupt:
            break

    dueros.stop()
    audio.stop()


if __name__ == '__main__':
    main()

解决第二个问题
还未解决...

你可能感兴趣的:(2018-09-13 DuerOS + Raspberry3B+ + 麦克风阵列)