希望大家收藏:
本文持续更新地址:https://haoqchen.site/2018/04/26/iflytek-awaken-asr/
int main(int argc, char **argv)//这只是主体程序
{
//init iflytek
int ret = 0 ;
ret = MSPLogin(NULL, NULL, lgi_param);
memset(&asr_data, 0, sizeof(UserData));
ret = build_grammar(&asr_data); //第一次使用某语法进行识别,需要先构建语法网络,获取语法ID,之后使用此语法进行识别,无需再次构建
while (1)
{
run_ivw(NULL, ssb_param);
if(g_is_awaken_succeed){
run_asr(&asr_data);
g_is_awaken_succeed = FALSE;
}
if(g_is_order_publiced == FALSE){
if(g_order==ORDER_BACK_TO_CHARGE){
play_wav((char*)concat(PACKAGE_PATH, "audios/back_to_charge.wav"));
}
if(g_order == ORDER_FACE_DETECTION){
play_wav((char*)concat(PACKAGE_PATH, "audios/operating_face_rec.wav"));
}
g_is_order_publiced = TRUE;
}
}
exit:
MSPLogout();
}
上面是main函数的主体部分
登录->构建语法->进入while(1)循环
在while(1)循环中,运行run_ivw(NULL, ssb_param); 开启录音,创建一个新的线程接收录音并上传至服务器等待唤醒结果。此时run_ivw处于阻塞状态,一直持续到收到唤醒结果,然后停止录音退出唤醒服务。
void run_ivw(const char *grammar_list , const char* session_begin_params)//这只是主题
{
//start QIVW
session_id=QIVWSessionBegin(grammar_list, session_begin_params, &err_code);
err_code = QIVWRegisterNotify(session_id, cb_ivw_msg_proc,NULL);
//start record
err_code = create_recorder(&recorder, iat_cb, (void*)session_id);
err_code = open_recorder(recorder, get_default_input_dev(), &wavfmt);
err_code = start_record(recorder);
record_state = MSP_AUDIO_SAMPLE_FIRST;
while(record_state != MSP_AUDIO_SAMPLE_LAST)
{
sleep_ms(200); //阻塞直到唤醒结果出现
printf("waiting for awaken%d\n", record_state);
}
exit:
if (recorder) {
if(!is_record_stopped(recorder))
stop_record(recorder);
close_recorder(recorder);
destroy_recorder(recorder);
recorder = NULL;
}
if (NULL != session_id)
{
QIVWSessionEnd(session_id, sse_hints);
}
}
只有当唤醒结果是成功的才运行run_asr(&asr_data),该函数构建离线命令词识别参数,调用demo_mic函数。该函数的作用与run_ivw函数基本相似,初始化语音识别、开始识别、等待15秒或者识别完成之后关闭录音
static void demo_mic(const char* session_begin_params)//这只是主体程序
{
struct speech_rec_notifier recnotifier = {
on_result,
on_speech_begin,
on_speech_end
};
errcode = sr_init(&iat, session_begin_params, SR_MIC, &recnotifier);
errcode = sr_start_listening(&iat);
/* demo 15 seconds recording */
while(i++ < 15 && iat.session_id != NULL)
sleep(1);
errcode = sr_stop_listening(&iat);
sr_uninit(&iat);
}
语音唤醒的回调函数为:
int cb_ivw_msg_proc( const char *sessionID, int msg, int param1, int param2, const void *info, void *userData )//这只是主体部分程序
{
if (MSP_IVW_MSG_ERROR == msg) //唤醒出错消息
{
g_is_awaken_succeed = FALSE;
record_state = MSP_AUDIO_SAMPLE_LAST;
}else if (MSP_IVW_MSG_WAKEUP == msg) //唤醒成功消息
{
g_is_awaken_succeed = TRUE;
record_state = MSP_AUDIO_SAMPLE_LAST;
}
int ret = stop_record(recorder);
return 0;
}
该函数通过服务器返回的消息判断唤醒结果,这里我们通过全局变量向主函数以及录音线程传递结果,以及时做出该有的反应。
离线命令词识别的回调函数:
void on_result(const char *result, char is_last)
{
if (result) {
size_t left = g_buffersize - 1 - strlen(g_result);
size_t size = strlen(result);
if (left < size) {
g_result = (char*)realloc(g_result, g_buffersize + BUFFER_SIZE);
if (g_result)
g_buffersize += BUFFER_SIZE;
else {
printf("mem alloc failed\n");
return;
}
}
strncat(g_result, result, size);
show_result(g_result, is_last);
g_order = get_order(g_result);
if(g_order > ORDER_NONE){
g_is_order_publiced = FALSE;
}
}
}
持续获取结果,然后调用get_order函数获取结果并返回到全局变量g_order中。这个get_order函数是我根据自己的语法特性编写的。各位看官可以根据自己的情况做更改。我的语法特性请看下图:
主要就是todo+order的形式,其中21300-21399是todo的id,21400-21499是order的id。这样就可以在获取结果时很明显地区分开todo和order,进而识别出语义。
这个语音唤醒与命令词识别一开始也主要想用在ROS系统进行机器人语音控制。这里给出了indigo版本的实现。
在这个包中,我定义了一个sr_order的msg,然后定义一个awaken_asr节点,当获取到识别结果时就发布消息。同时给出了一个listener接收消息的例子。熟悉ROS的朋友应该都知道我在说啥,就不细说了,具体看我github中ROS的分支程序。
科大讯飞开放平台
SDK文档
科大讯飞工作人员的态度真的很棒,之前调试发现了一个bug,向他们反馈,虽然还没解决,但是一直在积极跟进。
另外希望科大讯飞可以降低起购量。学生党真的想用,但是不需要这么多的装机量。最后在他们官方群哭诉了挺久,他们的工作人员建议我实名制学生身份,然后向他们服务申请了几个学术用途的装机量。他们做支持的小姐姐不辞劳苦地忙了很久,最后终于给我申请到了。在此感谢科大讯飞给了我这个机会学习到这些知识。