AI语音识别-我给浏览器加了个语音搜索功能

415 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

1. 前言

随着物联网的发展,语音识别技术受到越来越多的关注,语音识别技术正积极推动信息通信领域的革命,语音拨号,语音邮件,语音输入乃至语音操控等以语音识别为基础的人机交互日益普及.尽管生物识别方式不断增多,语音识别方式仍是主流方式.与其他生物识别技术相比,语音识别技术不仅具有非接触,非侵入性,使用方便,不会遗失和忘记,不需记忆等特点。

本篇文章就采用华为云提供的在线语音识别服务给浏览器设计一个语音自动搜索的功能,编程语言采用C++,软件框架采用QT设计,浏览器内核采用QWebEngineView,在QT5.7以后,QT里就不支持webkit了,目前自带的浏览器内核是QWebEngineView,只能使用MSVC编译编译,mingw要使用浏览器可以单独下载webkit的库,或者使用COM组件调用IE浏览器,当前文章里使用的浏览器是QWebEngineView,编译器采用VS2017,32bit。

语音采集功能使用QT的QAudioInput类来实现,采集声卡的PCM数据,保存起来,通过华为云的语音识别HTTP接口完成文字识别,得到文字后再通过浏览器进行搜索文字相关内容。

实现效果如下:

image-20220106190739135

点击界面上的 ”开始语音采集“按钮,就可以说话,说完点击停止采集,然后调用华为云的语音识别接口进行语音识别,在下面的显示框上显示识别到的文字,然后再完成浏览器自动搜索。

image-20220106190855680

image-20220106193216974

2. 创建语音服务器

2.1 使用语音服务

登录华为云官网: www.huaweicloud.com/

选择产品-人工智能-语音交互服务-一句话识别。

image-20220106164835826

短语音识别地址: www.huaweicloud.com/product/asr…

短语音识别是将口述音频转换为文本,通过API调用识别不超过一分钟的不同音频源发来的音频流或音频文件。适用于语音搜索、人机交互等语音交互识别场景。 支持免费试用。

image-20220106165023581

免费试用每日500次。

image-20220106165205406

image-20220106165217533

image-20220106165234937

image-20220106165403583

image-20220106170045851

2.2 HTTP接口使用介绍

文档地址: support.huaweicloud.com/api-sis/sis…

image-20220106170707976

在线调试接口地址: apiexplorer.developer.huaweicloud.com/apiexplorer…

几个重要的参数:

本地音频采集的频率、通道数都得与参数匹配。

image-20220106191232190

image-20220106191250256

2.3 接口地址总结

 请求地址: "https://{endpoint}/v1/{project_id}/asr/short-audio"
 ​
 请求数据:
 {
     "config": {
         "audio_format": "ulaw8k8bit",
         "property": "chinese_8k_common",
         "add_punc": "yes",
         "digit_norm": "yes",
         "need_word_info": "yes"
     },
     "data": "/+MgxAAUeHpMAUkQAANhuRAC..."
 }
 ​
 请求头里要带: X-Auth-Token 参数

请求数据里的参数在前面截图里介绍了,data就是音频文件的base64编码数据。

请求地址里的endpoint字段、project_id字段、还有X-Auth-Token字段只要是访问华为云的任何API接口都需要填,获取方法看这里: bbs.huaweicloud.com/blogs/31775… 翻到2.3小节。

image-20220106192419443

识别成功返回的数据:

 {
   "trace_id": "567e8537-a89c-13c3-a882-826321939651",
   "result": {
     "text": "欢迎使用语音云服务。",
     "score": 0.9,
     "word_info": [
       {
         "start_time": 150,
         "end_time": 570,
         "word": "欢迎"
       },
       {
         "start_time": 570,
         "end_time": 990,
         "word": "使用"
       },
       {
         "start_time": 990,
         "end_time": 1380,
         "word": "语音"
       },
       {
         "start_time": 1380,
         "end_time": 1590,
         "word": "云"
       },
       {
         "start_time": 1590,
         "end_time": 2070,
         "word": "服务"
       }
     ]
   }
 }

其中的text字段就是识别的文本数据。

3. 项目代码示例

下面列出核心的代码,主要是就是字符串拼接格式,拼接完发送http请求即可。

3.1 语音转文字请求代码

 //语音转文本
 void Widget::audio_to_text(QByteArray data)
 {
     function_select=0;
 ​
     QString requestUrl;
     QNetworkRequest request;
 ​
     //存放文件的BASE64编码
     QString base64_Data;
 ​
     //设置请求地址
     QUrl url;
 ​
     //一句话识别的请求地址
     requestUrl = QString("https://sis-ext.%1.myhuaweicloud.com/v1/%2/asr/short-audio")
             .arg(SERVER_ID)
             .arg(PROJECT_ID);
     qDebug()<<"requestUrl:"<<requestUrl;
     
     //设置数据提交格式
     request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
 ​
     //将图片进行Base64编码
     base64_Data = QString(data.toBase64());
 ​
     //设置token
     request.setRawHeader("X-Auth-Token",Token);
 ​
     //构造请求
     url.setUrl(requestUrl);
     request.setUrl(url);
 ​
    //设置采样率
    QString post_param=QString
               ("{"
                    ""config": {"
                        ""audio_format": "%1","
                        ""property": "%2","
                        ""add_punc": "yes","
                        ""digit_norm": "yes","
                        ""need_word_info": "yes""
                    "},"
                    ""data": "%3""
                "}").arg("pcm16k16bit").arg("chinese_16k_common").arg(base64_Data);
 ​
 /*
 chinese_16k_common  支持采样率为16k的中文普通话语音识别。
 pcm16k16bit  16k16bit单通道录音数据。
 */
     //发送请求
     manager->post(request, post_param.toUtf8());
 }

3.2 更新token代码

 /*
 功能: 获取token
 */
 void Widget::GetToken()
 {
     //表示获取token
     function_select=3;
 ​
     QString requestUrl;
     QNetworkRequest request;
 ​
     //设置请求地址
     QUrl url;
 ​
     //获取token请求地址
     requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")
                  .arg(SERVER_ID);
 ​
     //自己创建的TCP服务器,测试用
     //requestUrl="http://10.0.0.6:8080";
 ​
     //设置数据提交格式
     request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));
 ​
     //构造请求
     url.setUrl(requestUrl);
 ​
     request.setUrl(url);
 ​
     QString text =QString("{"auth":{"identity":{"methods":["password"],"password":"
     "{"user":{"domain": {"
     ""name":"%1"},"name": "%2","password": "%3"}}},"
     ""scope":{"project":{"name":"%4"}}}}")
             .arg(MAIN_USER)
             .arg(IAM_USER)
             .arg(IAM_PASSWORD)
             .arg(SERVER_ID);
 ​
     //发送请求
     manager->post(request, text.toUtf8());
 }

3.3 华为云返回的结果处理

 //解析反馈结果
 void Widget::replyFinished(QNetworkReply *reply)
 {
     QString displayInfo;
     int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
 ​
     //读取所有数据
     QByteArray replyData = reply->readAll();
 ​
     qDebug()<<"状态码:"<<statusCode;
     qDebug()<<"反馈的数据:"<<QString(replyData);
 ​
     //更新token
     if(function_select==3)
     {
         displayInfo="token 更新失败.";
         //读取HTTP响应头的数据
         QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs();
         qDebug()<<"HTTP响应头数量:"<<RawHeader.size();
         for(int i=0;i<RawHeader.size();i++)
         {
             QString first=RawHeader.at(i).first;
             QString second=RawHeader.at(i).second;
             if(first=="X-Subject-Token")
             {
                 Token=second.toUtf8();
                 displayInfo="token 更新成功.";
 ​
                 //保存到文件
                 SaveDataToFile(Token);
                 break;
             }
         }
         qDebug()<<displayInfo;
         return;
     }
 ​
     //判断状态码
     if(200 != statusCode)
     {
         //解析数据
         QJsonParseError json_error;
         QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
         if(json_error.error == QJsonParseError::NoError)
         {
             //判断是否是对象,然后开始解析数据
             if(document.isObject())
             {
                 QString error_str="";
                 QJsonObject obj = document.object();
                 QString error_code;
                 //解析错误代码
                 if(obj.contains("error_code"))
                 {
                     error_code=obj.take("error_code").toString();
                     error_str+="错误代码:";
                     error_str+=error_code;
                     error_str+="\n";
                 }
                 if(obj.contains("error_msg"))
                 {
                     error_str+="错误消息:";
                     error_str+=obj.take("error_msg").toString();
                     error_str+="\n";
                 }
                 //显示错误代码
                 qDebug()<<error_str;
             }
          }
         return;
     }
 ​
     //语音识别
     if(function_select==0)
     {
         //解析数据
         QJsonParseError json_error;
         QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
         if(json_error.error == QJsonParseError::NoError)
         {
             //判断是否是对象,然后开始解析数据
             if(document.isObject())
             {
                 QString error_str="";
                 QJsonObject obj = document.object();
                 QString error_code;
 ​
                 if(obj.contains("result"))
                 {
                     QJsonObject obj2=obj.take("result").toObject();
                     if(obj2.contains("text"))
                     {
                         QString text=obj2.take("text").toString();
                         qDebug()<<"识别的文本:"<<text;
                         ui->lineEdit_text_display->setText(text);
 ​
                         //浏览器搜索
                         QString url="https://www.baidu.com/s?ie=UTF-8&wd="+text;
 ​
                         m_webView->load(QUrl(url));
 ​
                     }
                 }
                 //显示错误代码
                 qDebug()<<error_str;
             }
          }
     }
 }

\