自定义SpeechService开发指导

2019-07-20  本文已影响0人  徐海波

概述

悟空机器人在语音识别这块官方只支持中文、英语和韩文,开发者们可能希望让悟空支持更多的语言,为此他们需要接入新的语音服务商(如Alexa, Google Assistant等),如果想让语音交互的体验达到悟空官方版本的效果,开发者需要做大量的开发工作。为此我们开发了一套开发框架SpeechFramework,方便快速接入。 该框架把麦克风阵列模块、唤醒模块、识别模块、TTS模块、交互模型整合在一起,并实现各阶段的自动切换。它实现了麦克风阵列的开关,录音数据的读取,声源定位,并实现与机器人中其他模块的通信,实现开机自动启动, 休眠时自动关闭麦克风阵列,激活时重新开启的功能。开发者只需要按照我们定义的接口,来实现唤醒、识别、TTS模块(可选),就可以实现完整的与悟空机器人内置语音模块一样的交互表现。

基本框架如下:

speechFramework (1).png

核心模块说明

MicArrayModule:从4-Mic中读取原始的音频数据,经过麦克风阵列前端算法处理后,输出16K HZ、 16bit、 单通道的音频数据。

WakeupModule : 实现唤醒功能,通过不断从MicArrayModule读取麦克风数据输入到唤醒引擎中监测是否发生了语音唤醒,当发生了语音唤醒时通过发布唤醒事件来触发RecognizeModule进入到识别阶段。

RecognizeModule:包含ASR、NLP、TTS过程,开发者即可以集成Amazon AVS和腾讯叮当这类端到端的完整语音服务,也可以通过分别接入ASR Api,NLP Api,TTS Api来实现语音交互功能。

Interactive Model: 当机器人被语音唤醒时,当机器人处于识别状态时,当机器人处于等待识别结果阶段时,机器人都有不同的表现,为保证交互的一致性,我们已经实现了标准的交互模型,开发者不需额外开发。
唤醒时:播报唤醒应答音频,眼睛播放微笑动画,转头声源定位;
语音识别阶段:眼睛播放眨眼动画;
NLP识别阶段:眼睛播放左右转动动画;

Note:
开发者可以通过传入音频资源的id来自定义唤醒应答,并允许传入多个;
转头声源定位的时机可以自定义,支持在唤醒时或者语音识别结束时。

编码实现

如何获取访问麦克风阵列访问权限

1.增加标签

AndroidManifest.xml文件application标签里,加入如下代码,申请访问麦克风阵列

<meta-data android:name="ubt-master-app" android:value="third_part_speechservice"/>

2. 首次安装该apk后,需要重启机器人

机器人在每次开机时会检查安装的apk里有没有third_part_speechservice标记,如果有的话,
则disable系统内置的访问麦克风阵列的应用SpeechService,把麦克风阵列的访问权让给该apk。
系统内置的SpeechService被disable后,用户将无法访问腾讯叮当语音服务。

让出麦克风阵列访问权

1.卸载app或去掉meta-data的标签

2.重启机器人
重启机器人后,系统内置的SpeechService将重新获取麦克风阵列访问权限, 用户可以继续使用叮当语音服务。

SDK引入

micrecord-sdk-release-versionCode.aar
mini-outer-sdk-versionCode.jar
protobuf-java-versionCode.jar
speechFramework-release-versionCode.jar
android {
    repositories {
        flatDir {
            dirs 'libs'
        }
    }

}


dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

   implementation(name: 'speechFramework-release', ext: 'aar')
    implementation(name: 'micrecord-sdk-release', ext: 'aar')
    implementation 'org.greenrobot:eventbus:3.0.0'
}
        //setting turn head timing and wakeup reply media file
        Config config = new Config.Builder().timing(TurnHeadApi.Timing.AfterRecord).
                wakeupResList(new int[] {R.raw.wakeup_001,R.raw.wakeup_002}).build();

        IModuleManager.Builder builder = new IModuleManager.Builder().recognizeModuleFactory(new DemoRecognizeModuleFactory())
                .ttsModuleFactory(new DemoTtsModuleFactory())
                .wakeupModuleFactory(new DemoWakeupModuleFactory())
                .config(config);

        //inject
        SpeechManager.get().setImpl(new DefaultSpeechServiceImpl(builder));

Config可以设置声源定位转头的时机以及自定义唤醒响应音频,当设置多个音频时则会随机播放一个。音频资源建议放在app工程的res/raw/目录下,支持mp3,wav等格式。
IModuleManager用于设置RecognizeModule的工厂类,WakeupModule的工厂类,TTSModule的工厂类以及设置Config.
SpeechManager.get().setImpl(new DefaultSpeechServiceImpl(builder));用于把`IModuleManager注入到SpeechFramework,由SpeechFramework来实现各个Module的调用。

public interface IWakeupModule {

    void init(IWakeuperListener listener);
    void destroy();
    void enable();
    void disable();
    boolean isEnable();
    /**
     * @param pcmData the record data after wakeup
     * @param dataLen the record data length
     */
    void writeAudio(byte[] pcmData, int dataLen);
}

所有的接口方法都由SpeechFramework调用,开发者必须要严格按照要求进行重写。
init():
在此方法中实现唤醒模块的初始化,当初始化成功时请调用IWakeuperListener.onInitSuccess()当初始化失败时请调用IWakeuperListener.onInitError(errMsg,errCode). 只有WakeupModule初始化成功,机器人开机时才会播报已经准备好了。

enable():使唤醒模块可用
disable():禁用唤醒模块
isEnable():判断唤醒模块是否可用,只有isEnable() ==true时麦克风数据才会输入到WakeupModule。
writeAudio():用于接收麦克风数据,麦克风数据为16K HZ, 16bit, 单通道。 开发者需要把麦克风数据持续写入到唤醒引擎。当唤醒引擎检测到唤醒时,需要调用IWakeuperListener.onWakeup(wakeword),当SpeechFramework收到onWakeup()回调时Interactive Model会播报唤醒响应音频,并播放唤醒眼睛表情,同时会启动RecognizeModule,进入到语音识别阶段。
destory():模块退出时调用,可以在此释放资源。

  1. 实现WakeupModuleFactory接口来实例化WakeupModule对象。
public interface WakeupModuleFactory {
    IWakeupModule factory();
}
public interface IRecognizeModule {
    void init(IRecognizerListener listener);
    void destroy();
    void startListening();
    /**
     * stop recognize
     */
    void stopListening();

    /**
     * @param data byte[]
     * @param start start >= 0
     * @param end end < byte.length
     */
    int writeAudio(byte[] data, int start, int end);

    /**
     * @return boolean Is Listening
     */
    boolean isListening();
}

在该Module里可以去接入Amazon AVS、腾讯叮当这样的完整的语音服务,也可以去分别接入ASR、NLP、TTS API.
init():进行初始化。
startListening():启动识别过程,当采用云端识别时,应开始于服务器建立连接,并开始把麦克风数据传输到云端识别服务。
stopListening():中断识别过程,当在识别过程中,用户再次唤醒会调用该方法来中断当前的识别,然后再调用startListening()来启动新一轮的识别。
writeAudio():用于接收麦克风数据,开发者需要把收到的数据持续地传输到识别服务。当isListening()== false时将无法收到麦克风数据。
isListening():判断当前是否在识别阶段。
destory():模块退出时调用,可以在此释放资源。
2.错误处理:
在RecognizeModule可能存在多种错误,在Speech.ErrorCode里我们有定义常见的错误,开发者在回调IRecognizerListener.onError()需要传入正确的错误码。SpeechFramework会把Error Event发布出去,机器人其他模块会进行相应的错误播报。

  1. 实现RecognizeModuleFactory接口来实例化RecognizeModule对象。
public interface RecognizeModuleFactory {
    IRecognizeModule factory();
}
  1. 实现ITtsModule接口
public interface ITtsModule {

    void init();

    boolean destroy();

    /**
     * 启动tts播报
     *
     * @param text 播报文本
     * @param listener 播报状态回调监听
     */
    void startSpeaking(String text, ITTsListener listener);

    /**
     * 停止tts播报
     */
    void stopSpeaking();
}

init():模块的初始化
destory():退出该模块,可以释放资源
startSpeaking():开发者需要在此实现tts合成功能,并把结果通过回调ITtsListener的方法传递给调用者。
``
stopSpeaking():停止播报。

  1. 实现TtsModuleFactory接口,实例化TtsModule.
public interface TtsModuleFactory {
    ITtsModule factory();
}
上一篇下一篇

猜你喜欢

热点阅读