QT实战百度语音识别

前言

随着学习的深入,感觉愈发缺乏满足感。刚好看到微信语音转文字的功能,经网上查询,发现可以使用 QT + 百度语音识别技术 实现这一功能。当然,由于使用的 QT 和 百度语音识别,那么看不到一些具体的底层实现,但操作起来相对比较简单。俗话说:“没吃过猪肉,还没见过猪跑?”,我打算先看看别人已有的技术,搬过来跑一下,然后再进行深入学习,同时也可以复习一下 QT 相关知识。文章如有写错或者代码可优化,欢迎大家指正!

QT 采集麦克风 pcm 音频裸数据

基础知识

PCM(Pulse Code Modulation,脉冲编码调制)⾳频数据是未经压缩的⾳频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字⾳频数据。
描述PCM数据的6个参数:

  1. Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。
  2. Sample Size : 量化位数。通常该值为16-bit。
  3. Number of Channels : 通道个数。常⻅的⾳频有⽴体声(stereo)和单声道(mono)两种类型,⽴体声包含左声道和右声道。另外还有环绕⽴体声等其它不太常⽤的类型。
  4. Sign : 表示样本数据是否是有符号位,⽐如⽤⼀字节表示的样本数据,有符号的话表示范围为-128 ~127,⽆符号是0 ~ 255。有符号位16bits数据取值范围为-32768~32767。
  5. Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。
  6. Integer Or Floating Point : 整形或浮点型。⼤多数格式的PCM样本数据使⽤整形表示,⽽在⼀些对精度要求⾼的应⽤⽅⾯,使⽤浮点类型表示PCM样本数据(浮点数 float值域为 [-1.0, 1.0])。
环境配置

第一步: 新建一个QWidget项目
在这里插入图片描述

第二步: 项目名与存放路径自选(然后一直下一步)
第三步: 在.pro文件中添加模块

QT += multimedia

第四步: 新建一个C++ Class(因为采集麦克风只是一个小功能,我们还有其他的功能),名字可自取。这里类名我起的是 AudioCapture

代码

audiocapture.h
#ifndef AUDIOCAPTURE_H
#define AUDIOCAPTURE_H#include <QObject>
#include <QAudioInput>
#include <QFile>
#include <QMessageBox>class AudioCapture : public QObject
{Q_OBJECT
public:explicit AudioCapture(QObject *parent = nullptr);void startCapture(QString filename);    //开始录音,文件名由调用者传入void stopCapture();                     //结束录音~AudioCapture();                        //析构函数,释放相关资源
signals:
private:QAudioInput *pAudioInput;               //录音对象QFile       *pFile;                     //存取文件
};#endif // AUDIOCAPTURE_H
audiocapture.cpp
#include "audiocapture.h"AudioCapture::AudioCapture(QObject *parent) : QObject(parent)
{//初始化pAudioInput = nullptr;pFile = nullptr;
}//开始录音
void AudioCapture::startCapture(QString filename)
{//打开默认的音频输入设备QAudioDeviceInfo audioDeviceInfo = QAudioDeviceInfo::defaultInputDevice();//判断本地是否有录音设备if(audioDeviceInfo.isNull() == false){/* 创建文件并打开 */pFile = new QFile;pFile->setFileName(filename);pFile->open(QIODevice::WriteOnly | QIODevice::Truncate);// 设置音频文件格式QAudioFormat format;// 设置采样频率,常见的有16000、44100、48000format.setSampleRate(16000);// 设置通道数,单声道、双声道、5.1声道format.setChannelCount(1);// 设置每次采样得到的样本数据位值,8位、16位format.setSampleSize(16);// 设置编码方法format.setCodec("audio/pcm");// 判断当前设备设置是否支持该音频格式if(audioDeviceInfo.isFormatSupported(format) == NULL){format = audioDeviceInfo.nearestFormat(format);}// 创建录音对象pAudioInput = new QAudioInput(format, this);// 开始录音pAudioInput->start(pFile);}else{// 没有录音设备QMessageBox::information(NULL, tr("Record"), tr("Current No Record Device"));}
}void AudioCapture::stopCapture()
{if(pAudioInput != NULL){// 停止录音pAudioInput->stop();}if(pFile != NULL){// 关闭文件pFile->close();delete pFile;pFile = nullptr;}
}AudioCapture::~AudioCapture()
{//释放资源if(pAudioInput != nullptr){delete pAudioInput;pAudioInput = nullptr;}if(pFile != nullptr){delete pFile;pFile = nullptr;}
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include "audiocapture.h"QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_startPtn_clicked();     // 点击Start按钮后触发的槽函数void on_stopPtn_clicked();      // 点击Stop按钮后触发的槽函数private:Ui::Widget *ui;                 //操作界面上的相关控件AudioCapture myAudioCapture;    //录音功能封装对象
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->startPtn->setEnabled(true);     //Start按钮初始化可用ui->stopPtn->setEnabled(false);     //Stop按钮初始化不可用
}Widget::~Widget()
{delete ui;
}//点击Start按钮后触发的槽函数
void Widget::on_startPtn_clicked()
{QString filepath = ui->filepath->text();    //获取用户输入地址/* 判断用户是否输入地址 */if(filepath == ""){QMessageBox::information(NULL, "information", "Please input the filepath to save!");return;}/* 点击Start后禁用Start,开放Stop按钮 */ui->startPtn->setEnabled(false);ui->stopPtn->setEnabled(true);myAudioCapture.startCapture(filepath);  //开始录音
}//点击Stop按钮后触发的槽函数
void Widget::on_stopPtn_clicked()
{/* 点击Stop后禁用Stop,开放Start按钮 */ui->startPtn->setEnabled(true);ui->stopPtn->setEnabled(false);myAudioCapture.stopCapture();           //结束录音
}
运行时界面UI(仅供测试,不够美观)

在这里插入图片描述

播放 pcm 数据

由于vlc播放器无法直接播放pcm音频裸数据,这里使用ffplay来播放(也可使用代码播放)

ffplay -f s16le -ar 16000 -ac 1 -i D:\\1.pcm

这里的参数设置需与代码中的设置一样,否则音效不对。

使用 QAudioOutput 来播放 pcm 音频数据

主要代码

//设置音频输出格式
QAudioFormat fmt;//设置采样率
fmt.setSampleRate(44100);//设置采样位数
fmt.setSampleSize(16);//设置声道数
fmt.setChannelCount(1);//设置解码方式
fmt.setCodec("audio/pcm");// 设定字节序,以小端模式播放音频文件
fmt.setByteOrder(QAudioFormat::LittleEndian);// 设定采样类型。根据采样位数来设定。
fmt.setSampleType(QAudioFormat::UnSignedInt);// 创建QAudioOutput对象并初始化
QAudioOutput *out = new QAudioOutput(fmt);// 调用start函数后,返回QIODevice对象的地址
QIODevice *io = out->start();//获取设备播放一个周期所需要的字节数
int size = out->periodSize();//创建缓冲区
char *buf = new char[size];//以二进制只读方式打开pcm文件
FILE *fp = fopen("d:/1.pcm", "rb");//判断是否读到末尾
while(!feof(fp))
{//判断空闲空间是否小于一个周期的大小,如果是则说明CPU处理速度太快,得等一等。if(out->bytesFree() < size){QThread::msleep(1);continue;}int len = fread(buf, 1, size, fp);//判断是否成功读入if(len <= 0){break;}//这里相当于写入到电脑声卡的缓冲区,接下来的工作由声卡完成,与我们无关io->write(buf, len);
}fclose(fp);    //关闭文件//资源释放
if(NULL != buf)
{delete buf;buf = NULL;
}if(NULL != out)
{delete out;out = NULL;
}
语音识别

百度智能云网址: https://cloud.baidu.com/product/speech.html?track=cf3e1b9d08c41e54e7f0ace5828291cce549454e8c470208

第一步: 点击右上角控制台,并完成登录
在这里插入图片描述

第二步: 点击右上角三条杠,然后选中语音技术
在这里插入图片描述

第三步: 概览中点击免费尝鲜
在这里插入图片描述

第四步: 选中短语音识别-普通话,然后左下角点击0元领取(这里我已经领过了,所以没有这个选项了)
在这里插入图片描述

第五步: 点击应用列表,然后创建应用,应用名称随意,应用归属选择个人即可,然后添加一些描述,创建即可(这里我昨天实验时创建过一个了)
在这里插入图片描述

第六步: 复制 APIKey 和 SecretKey
在这里插入图片描述

整个语音识别的逻辑分析

第一步: QT中使用QAudioInput进行麦克风采集pcm音频数据
第二步: 通过http的post方式将音频数据提交给百度后台进行语音识别
第三步: 百度返回识别后的数据,将数据显示到文本框中。

语音识别

网络请求主要代码

bool httppost::postMsg(QString url, QMap<QString, QString> headerdata, QByteArray requestData, QByteArray &replyData)
{//发送请求的对象QNetworkAccessManager manager;//请求对象QNetworkRequest request;request.setUrl(url);//设置请求参数QMapIterator<QString, QString> it(headerdata);while(it.hasNext()){it.next();request.setRawHeader(it.key().toLatin1(),it.value().toLatin1());}QNetworkReply *reply = manager.post(request, requestData);QEventLoop loop;//一旦服务器返回,reply会发出信号connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);loop.exec();//死循环,reply发出信号,结束循环//判断是否响应成功if(reply != nullptr && reply->error() == QNetworkReply::NoError){replyData = reply->readAll();qDebug() << replyData;return true;}else{qDebug() << "请求失败";return false;}
}

百度的接口相关设置

//获取access_token相关
const QString baiduTokenUrl = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2&";
const QString client_id = 刚才复制的APIKey;
const QString client_secret = 刚才复制的SecretKey;//普通话测试
const QString baiduSpeechurl = "http://vop.baidu.com/server_api?dev_pid=1537&cuid=%1&token=%2";

语音识别主要代码

QString speechrecognition::speechIdentify(QString filename)
{//获取tokenQString tokenUrl = QString(baiduTokenUrl).arg(client_id).arg(client_secret);QMap<QString, QString> headers;headers.insert(QString("Content-Type"), QString("audio/pcm;rate=16000"));QByteArray requestdata;     //发送的内容QByteArray replydata;       //服务器返回的内容httppost httputil;          //封装的网络请求类bool success = httputil.postMsg(tokenUrl, headers, requestdata, replydata);//判断是否请求成功if(success){QString key = "access_token";//获取到access_token(通过json数据格式解析)accessToken = getJsonvalue(replydata, key);qDebug() << "----------------" << endl;qDebug() << accessToken << endl;}else    return "";//语言识别QString baiduSpeech = QString(baiduSpeechurl.arg("LAPTOP-71LN9B3Q").arg(accessToken));//把文件转化为QByteArrayQFile file;file.setFileName(filename);file.open(QIODevice::ReadOnly);requestdata = file.readAll();file.close();replydata.clear();//再次发起http请求bool result = httputil.postMsg(baiduSpeech, headers,requestdata,replydata);//判断是否请求成功if(result == true){QString key = "result";QString text = getJsonvalue(replydata,key);     //获取识别后的文字return text;}else{QMessageBox::warning(NULL, "识别提示", "识别失败");return "";}
}QString speechrecognition::getJsonvalue(QByteArray ba, QString key)
{QJsonParseError parseError;QJsonDocument jsonDocument = QJsonDocument::fromJson(ba, &parseError);if(parseError.error == QJsonParseError::NoError){if(jsonDocument.isObject()){//jsonDocument转化成json对象QJsonObject jsonObj = jsonDocument.object();//判断是否包含keyif(jsonObj.contains(key)){QJsonValue jsonVal = jsonObj.value(key);if(jsonVal.isString())  //字符串{return jsonVal.toString();}else if(jsonVal.isArray())  //数组{QJsonArray arr = jsonVal.toArray();QJsonValue jv = arr.at(0);return jv.toString();}}}}return "";
}
效果展示

当点击start按钮后,语音描述"很高兴和大家一起学习音视频"
点击stop后,文本框内显示文字"很高兴和大家一起学习音视频"
在这里插入图片描述

过程中的一些坑(个人遇到的主要的坑)
  1. 音频采样参数必须一致,否则百度只会识别出嗯嗯嗯等一系列奇怪的词语
  2. 注意采样频率目前百度语音识别支持16000,使用其他的如44100/48000等会报错
  3. 注意.pro文件中要添加network模块,否则根本发送不出去,也就是说百度根本收不到,奇怪的是QT没给我直接报错,虽然没有提示,但也还是一点一点写了,结果发现根本没发送出去。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/322125.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++初识多态(1)

1.多态要解决的问题&#xff08;引入&#xff09; 任何一种机制的存在&#xff0c;必然是有其存在的意义的&#xff0c;例如我们前面学过的函数重载&#xff0c;运算符重载&#xff0c;以及引用等等&#xff0c;都是解决一些特殊问题的&#xff1b; 下面通过一些具体的例子&a…

Echarts散点图的29个配置项,散点图可以随心所欲啦。

1-9 当使用 ECharts 绘制散点图时&#xff0c;可以配置以下一些常用的选项&#xff1a; 1. tooltip&#xff1a;配置提示框组件&#xff0c;用于显示鼠标悬停在散点上时的提示信息。 2. legend&#xff1a;配置图例组件&#xff0c;用于展示不同散点的标识和名称。 3. xAxis…

目录页码右对齐快速解决

选择目录–段落–制表符&#xff0c;按图中设置即可

merge函数占用内存过大

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

USB3.0接口——(1)基础知识

1.背景 USB 3.0是一种USB规范&#xff0c;该规范由英特尔等公司发起。 USB协议版本 命名约定 USB-IF组织引入命名约定&#xff0c;将端口列为 USB 5 Gbps、USB 10 Gbps、USB 20 Gbps 、USB 40 Gbps&#xff0c;而不使用版本号。获得 USB-IF 认证的 USB 产品的制造商会获得带…

WM Shell多动画场景处理

Shell导致的内存泄漏 基本上都是某个动画未正常结束&#xff0c;执行时间太久导致后续动画堆积或被merge到异常动画导致相关Surface得不到释放导致的。 某个Transition执行时间太久导致后续动画堆积 Visible layers 中有1558 个Transition Root相关layer Visible layers (c…

Java毕业设计 基于SpringBoot vue社区智慧养老监护管理平台

Java毕业设计 基于SpringBoot vue社区智慧养老监护管理平台 SpringBoot 社区智慧养老监护管理平台 功能介绍 登录注册 个人中心 修改密码 个人信息 房间信息管理 房间入住信息管理 反馈信息管理 留言管理 老人信息管理 公告管理 物资申请管理 管理员管理 护工管理 体检员管理…

C++多态(全)

多态 概念 调用函数的多种形态&#xff0c; 多态构成条件 1&#xff09;父子类完成虚函数的重写&#xff08;三同&#xff1a;函数名&#xff0c;参数&#xff0c;返回值相同&#xff09; 2&#xff09;父类的指针或者引用调用虚函数 虚函数 被virtual修饰的类成员函数 …

React:Router-1.BrowserRouter组件式

使用步骤 安装 react-router-dom 依赖 $ npm install react-router-dom6导入 BrowserRouter, Link, Routes, Route 对象 import {BrowserRouter, Link, Routes, Route} from react-router-dom;3.BrowserRouter&#xff1a;history模式路由&#xff1b; HashRouter&#xff1…

代码随想录算法训练营第二十二天|654.最大二叉树 、617.合并二叉树 、700.二叉搜索树中的搜索 、 98.验证二叉搜索树

654.最大二叉树 文档讲解&#xff1a;代码随想录 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 递归的三要素&#xff1a; 第一要素&#xff1a;明确这个函数想要干什么 传入一个数组&#xff0c;针对这个数组构造一个最大二叉树 第二要素&#xff1a;寻…

跟TED演讲学英文:What moral decisions should driverless cars make by Iyad Rahwan

What moral decisions should driverless cars make? Link: https://www.ted.com/talks/iyad_rahwan_what_moral_decisions_should_driverless_cars_make Speaker: Iyad Rahwan Date: September 2016 文章目录 What moral decisions should driverless cars make?Introduct…

kubeflow简单记录

kubeflow 13.7k star 1、Training Operator 包括PytorchJob和XGboostJob&#xff0c;支持部署pytorch的分布式训练 2、KFServing快捷的部署推理服务 3、Jupyter Notebook 基于Web的交互式工具 4、Katib做超参数优化 5、Pipeline 基于Argo Workflow提供机器学习流程的创建、编排…

漏洞挖掘之某厂商OAuth2.0认证缺陷

0x00 前言 文章中的项目地址统一修改为: a.test.com 保护厂商也保护自己 0x01 OAuth2.0 经常出现的地方 1&#xff1a;网站登录处 2&#xff1a;社交帐号绑定处 0x02 某厂商绑定微博请求包 0x02.1 请求包1&#xff1a; Request: GET https://www.a.test.com/users/auth/weibo?…

mysql从入门到起飞+面试基础题

mysql基础 MySQL基础 企业面试题1 代码 select m.id,m.num from ( select t.id as id,count(1) num from ( select ra.requester_id as id from RequestAccepted raunion all select ra.accepter_id as id from RequestAccepted ra ) t group by t.id ) m group by id ord…

生产制造中刀具管理系统,帮助工厂不再频繁换刀

一、刀具管理的定义与重要性 刀具管理是指对生产过程中使用的各种刀具进行计划、采购、存储、分配、使用、监控、维修和报废等全过程的管理。刀具作为制造过程中的直接工具&#xff0c;其性能、质量和使用效率直接影响产品的加工精度、表面质量和生产效率。因此&#xff0c;建…

ICode国际青少年编程竞赛- Python-1级训练场-基础训练2

ICode国际青少年编程竞赛- Python-1级训练场-基础训练2 1、 a 4 # 变量a存储的数字是4 Dev.step(a) # 因为变量a的值是4&#xff0c;所以Dev.step(a)就相当于Dev.step(4)2、 a 1 # 变量a的值为1 for i in range(4):Dev.step(a)Dev.turnLeft()a a 1 # 变量a的值变为…

ios苹果App上架到应用商店的操作流程

哈喽&#xff0c;大家好呀&#xff0c;淼淼又来和大家见面啦&#xff0c;发现最近有许多想要上架App的小伙伴&#xff0c;但是又不知道要怎么操作&#xff0c;对于开发者而言&#xff0c;将精心打造的iOS应用程序成功上架到苹果的 App Store 是向全球用户展示咱们的产品和服务的…

Windows+Linux的虚拟串口工具

文章目录 1.Windows虚拟串口工具1.1 安装教程1.2 使用方法 2.Linux系统虚拟串口工具2.1 socat安装2.2 开启虚拟串口2.3 测试2.3.1 命令测试2.3.2 Cutecom工具测试 2.4 关闭虚拟串口 3.参考资料 1.Windows虚拟串口工具 下载地址&#xff1a;https://www.downxia.com/downinfo/4…

9、String类型和基本数据类型转换(Java)

String类型和基本数据类型转换 1、基本数据类型转String类型2、String类型转基本数据类型⭐ 1、基本数据类型转String类型 Java中String类型是字符串类型&#xff0c;是用 “ ” 双引号括起来的内容&#xff0c;所以基本数据类型转String类型直接&#xff0b;“ ”即可。&…

三年软件测试经验遭遇求职困境?揭秘求职市场的隐藏陷阱

1.个人背景 小李&#xff0c;我的一位朋友&#xff0c;拥有三年多的软件测试工作经验。他本科毕业后便投身于测试行业&#xff0c;熟练掌握Python编程&#xff0c;能够编写自动化测试脚本&#xff0c;并且熟悉Selenium和性能测试。然而&#xff0c;尽管他具备这些技能和经验&am…