代码参考:小程序Basic(aplt.leagor.basic)有一个语音驱动实例tleagor_speech。
视频1演示了新建小程序、让小程序符合语音驱动规范、使用poedit、小程序上传到商店、从商店下载内测版,这整个过程。
- 机器人采集到用户说的声音后,判断出来可能是段有效语音,就进行语音识别,得到一段文字。
- 解析文字,这段文字至少可能有两种功能,一是用于控制机器人,二是聊天。
- 用于控制机器人的文字。举个例子,命令机器人去启动豆浆机。机器人收到命令后,将移动到豆浆机,同时为让用户知道收到这命令了,会发出声音:“好的,这就去启动豆浆机”。
- 用于聊天的文字。举个例子:叫机器人背《静夜思》。这时是作为一个问题提交给NLP模型,NLP模型返回以文字表示的回答。接下把文字表示的回答转为声音,再叫机器人播放出来。于是现实人和机器人聊天。
正如上图所述,语音识别,识别出文字后怎么解析,怎么由文字表示的问题得到答案,这些都是语音驱动小程序实现。语音识别用的是科大讯飞还是百度,NLP模型用的是哪家,全看你此刻使用的是哪个语音驱动小程序。
一、输出规范
1.1 settings.cfg
speech_driver = yes
根下需存在键“speech_driver”,值是bool类型的“yes”。
1.2 libroseaplt.so需输出函数:aplt_create_speech_slot
void* aplt_create_speech_slot();
返回值是个指向aplt::tspeech_slot对象的指针。
二、识别、解析语音
语音驱动涉及三个线程:主线程,声音采集线程和语音识别线程。让根据图2,看一次语音处理过程。
- [采集线程]提取出一段认为是语音的声音片断,记为(data_, voice_len_)。
- [采集线程]将voice_event_设为有信号,唤醒识别线程。
- [识别线程]调用虚函数recognize()识别声音片断(data_, voice_len_),tleagor_speech用的是科大讯飞语音识别算法。识别出的文字放在函数内变量result。
- [识别线程]把result值安全地放在类内变量vocie_result_,让主线程可以访问。
- [主线程]在新近一次tleagor_speech::slice,发现voice_result_不是空,先安全地转放到函数内变量result。至此result存储着一段新近识别出的语音。根据文字内容可执行不同操作。
tspeech_slot::DoWork是声音识别线程默认执行的操作。如果不重载它,语音驱动须实现方法:has_voice、recognize。
在slice,安全得到一个非空的result后,便可根据不同内容执行不同操作。
2.1 控制机器人
执行一个aplt::treq_task封装的任务
type | 描述 | 生成该任务的方法 |
type_moveto | 移动到某个位置 | set_moveto |
type_moveto_recognition | 移动到某个位置,移动成功后开始识别物体 | set_moveto_recognition |
type_recognition | 原地开启摄像头,识别物体 | set_recognition |
得到任务treq_task后,以它为参数调用tros::request_task,便可让launcher去执行该任务。
要求机器人播放一段声音
aplt::tpinyin::speak,参数是utf-8格式的一个字符串。
三、接收临时任务执行过程中消息
语音驱动发现语音是要控制机器人,会调用aplt::tros::request_task向launcher发起控制请求。launcher在执行这个临时任务时,为让用户有个好的体验,需让知道现在执行到哪阶段了。于是到某个事件点,launcher会以消息码为参数,调用aplt::tmsg_subscriber的对应方法。
每个方法都有一个id参数。这个id来自request_task时的参数aplt::treq_task中的id字段。系统中可能同时存在多个treq_task,语音驱动在处理时首先要判断这id是不是自已的,是的才处理,否则要忽略。
virtual void did_task_pre_navigation(int id);
机器人要开始导航
virtual void did_task_navigation_stopped(int id, bool result);
机器人导航结束。result表示导航是成功还是失败。
virtual void did_task_object(int id, const std::string& object);
图像识别时识别出新物体。参数object指示物体名称(utf-8格式)。
virtual void did_task_msg(int id, msg_t msg);
这是一个可处理多种消息的函数,这些消息都没有关联参数。
- msg_disallowed。现在不允许执行新任务。举个例子,正显示着“设置”、“地图”等这些窗口时,不允许执行临时任务。
- msg_unrecognized。物体识别时,如果此次一直没有识别到物体,那识别15秒后就结束。结束时会发出这消息。如果此次有识别到物体,那结束时刻是在识别出那一刻再延时15秒,而且结束后不会发这个消息。
- msg_more_precise。物体识别时,4秒内没识别到这物体,会发出这消息。一次识别过程只会发一次。
四、科大讯飞语音识别
tleagor_speech在实现语音识别时,调用科大讯飞语音识别SDK。SDK是动态库方式,即windows上的msc.dll,android上的libmsc.so。
launcher是以动态方式加载msc.dll/libmsc.so。在获取库中某个地址时,该函数必须有“MSPAPI/__stdcall”修饰符。否则变成__cdecl,那会导致程序崩溃。
为安全,android对允许动态方式加载的动态库文件名做了限制,只允许:libroseaplt.x、libroseaplt2.x、libroseaplt3.x、libroseaplt4.x。libroseaplt.x有专门用途,那只能在2/3/4中选一个。
tleagor_speech集成的科大讯飞语音识别算法是免费的,有每天识别次数限制。自写、还是用讯飞的话,去讯飞官网申请个账号。