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

1. 前言

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

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

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

实现效果如下:


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



2. 创建语音服务器

2.1 使用语音服务

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

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


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

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


免费试用每日 500 次。






2.2 HTTP 接口使用介绍

文档地址: https://support.huaweicloud.com/api-sis/sis_03_0094.html


在线调试接口地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=SIS&api=RecognizeShortAudio

几个重要的参数:

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



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 接口都需要填,获取方法看这里: https://bbs.huaweicloud.com/blogs/317759 翻到 2.3 小节。


识别成功返回的数据:

{
  "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;
            }
         }
    }
}

本文文字及图片出自 InfoQ

余下全文(1/3)
分享这篇文章:

请关注我们:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注