[WASAPI]音频API:从Qt MultipleMedia走到WASAPI,相似与不同
[WASAPI] 从Qt MultipleMedia 来看WASAPI
最近在学习有关Windows上的音频驱动相关的知识,在正式开始说WASAPI之前,我想先说一说Qt的Multiple Media,为什么呢?因为Qt的MultipleMedia实际上是WASAPI的一层封装,它在是线上替我做了很多事,就好像在Microsoft的文档上会推荐你先学习Windows.Media.Capture,然后再看low level的WASAPI。
我这篇文章中,一方面是我Qt MultipleMedia用的比较多,另一方面,Qt MultiMedia也比较简单,为音频相关的API做了很多封装,这样就不需要你自己一个个HRESULT的去调试和测试了。
Qt MultiMedia Audio Recorder
由于Qt在5进6之后对Qt MultiMedia进行了大范围重构,所以这里Qt的项目我做了两个版本,分别为
audio-record-qt
audio-record-qt6
在调用上,Qt6和Qt5没有本质区别,所以这里我将着重聊一聊qt5上的录音机
在Qt5中,录音机的数据流如图所示:

流程大概如下:
- 获取所有设备的信息
- 根据名称匹配,获取我们需要的那个设备的QAudioDeviceInfo
- 使用QAudioDeviceInfo,获取到QAudioInput(输入)和QAudioOutput(输出)设备
- 重写一个QIODevice类,修改其writeData方法,并在其中完成你想要做的事情,包括但不限于:保存为文件,获得耳返数据,进行算法的处理等等。
- 将你继承了QIODevice的类的成员变量,放进QAudioInput和QAudioOutput的start中,这样一个完整的流就完成了。
其实WASAPI实际上也就是沿着这个Qt的MultiMedia的思路进行开发就可以了,但是在WASAPI中,没有Qt的封装,接口上会更加复杂一点而已。但是总的流程并没有本质区别。
还有需要注意的一点,就是QIODevice和QByteArray对数据流的封装做的很好,在纯C++中只能自己手动管理,所以这个地方可能会出现内存泄漏的风险,在开发的时候需要多多注意内存泄漏的问题。
WASAPI Audio Recorder
工程地址:
LeventureQys/Windows_Audio_Driver/WASAPI_Testbench
在WASAPI中,和Qt的MultiMedia中大的流程是一样的,但是在接口上来说往往更加复杂,简单的来说,流程大致如下:

其中和QtMultiMedia中最重要的区别就是没有一个专门的QIODevice去帮我处理线程和数据的关系,而是需要自己单开一个线程,然后从Capture/Render实例中去GetBuffer,然后从中获取数据或者往里面写入数据,再手动释放。
这个过程非常自由,同样也非常容易出现意外,所以在操作WASAPI的过程中需要谨慎谨慎再谨慎。
具体的代码详情见Github链接 LeventureQys/Windows_Audio_Driver/WASAPI_Testbench 我这里只简单说说我在工程中遇到的几个小问题。
- 输入设备的IAudioClient Initialize方法失败
我的调用函数如下:
hr = this->ptr_audio_client->Initialize(AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,AUDCLNT_STREAMFLAGS_EVENTCALLBACK,hnsDefaultDevicePeriod,hnsDefaultDevicePeriod,format_wav,NULL);
在这个函数中,第二个参数我设置的是AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK 这个地方具体要取决于设备是否允许进行回环录制和是否允许回调,并不是所有麦克风都支持这俩。
- 录制后的声音播放出来有很强的噪音,但我能确定声音是从麦克风传来的。
这种情况大概率是两边的声音没有对齐,这个根据wav的编码方式来的。简单地说,就是两边的channel和bitrate不匹配,导致声音无法对齐。具体你需要比对这两个format,然后再根据实际情况在音频处理处做应对和调整
WAVEFORMATEX* format_wav = NULL;
hr = ptr_audio_client->GetMixFormat(&format_wav);
if (FAILED(hr)) throw std::exception("Cant Get Mix Format!");WAVEFORMATEX* format_wav_output = NULL;
hr = ptr_output_audio_client->GetMixFormat(&format_wav_output);
if (FAILED(hr)) throw std::exception("Cant Get Mix Format Output!");
具体怎么调整详情可以看
[音视频学习笔记]二、什么是PCM音频?一些常见的PCM处理
比如我这里,我的麦克风的channels是1,但是耳机的channels是2,所以这里在播放的时候需要调整一下,将每一个bit都复制一份,放到输出的音频流中,如代码所示:
BYTE* pRenderData;
hr = ptr_output_audio_client_render->GetBuffer(numFramesAvailable, &pRenderData);
if (FAILED(hr)) {std::cerr << "GetBuffer (render) failed: " << hr << std::endl;return hr;
}
float* inputData = reinterpret_cast<float*>(pData);
float* outputData = reinterpret_cast<float*>(pRenderData);for (UINT32 i = 0; i < numFramesAvailable; i++) {// 将单声道复制到立体声的两个通道outputData[i * 2] = inputData[i];outputData[i * 2 + 1] = inputData[i];
}
到立体声的两个通道outputData[i * 2] = inputData[i];outputData[i * 2 + 1] = inputData[i];
}
相关文章:
[WASAPI]音频API:从Qt MultipleMedia走到WASAPI,相似与不同
[WASAPI] 从Qt MultipleMedia 来看WASAPI 最近在学习有关Windows上的音频驱动相关的知识,在正式开始说WASAPI之前,我想先说一说Qt的Multiple Media,为什么呢?因为Qt的MultipleMedia实际上是WASAPI的一层封装,它在是线…...
【畅购商城】微信支付之支付模块
目录 支付页面 接口 后端实现 前端实现 支付页面 步骤一:创建 flow3.vue组件 步骤二:引入第三方资源(js、css) <script> import TopNav from ../components/TopNav import Footer from …...
网络安全专有名词详解_2
57.Webshell 就是以ASP、php、jsp、cgi等网页文件形式存在的一种命令执行环境,也叫做网页的后门,可以上传下载文件,查看数据库和执行任意的程序命令等。 58.跨站攻击 XSS,是指攻击者利用网站程序对用户输入过滤不足,输…...
【传感器技术与应用】第2章 基本电量传感器,电位器式传感器,电感式传感器,电容式传感器
注作者了解更多 我的其他CSDN专栏 毕业设计 求职面试 大学英语 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数…...
【day20】集合深入探讨
模块19回顾 在深入探讨模块20之前,让我们回顾一下day19中的关键内容: Collection集合:单列集合的顶级接口,提供了add、addAll、clear、size、isEmpty、remove、toArray、contains等方法。迭代器(Iterator)…...
【英语语法】用must表对过去推测时,要用完成时must have been / must have done(不能直接用过去时)
文章目录 疑问解释1. 表达过去的推测2. 与时态一致3. 语法结构的限制4. 例子对比总结 疑问 This must have been a year-round activity as no structures have been found which would have been used to shelter animals in the winter. 为什么must表示对过去推测要用完成时&…...
数值计算期末考试重点(一)(黄云清版教材)
1.误差的分类 2.绝对误差和绝对误差限 3.绝对误差和绝对误差限 例题(课后习题1.2) 4.有效数字 例题(课后习题1.6) 5.算法的数值稳定性 例题(课后习题1.9) 这个手算比较艰难,还是给计算机算吧&am…...
使用 pushy 热更新后 sentry 不能正常显示源码
问题 使用 Android Studio 打包后,上传使用 sentry 官网命令打包的 sourcemap 文件,sentry能正常显示异常位置源码。 使用 pushy 热更新之后,sentry 不能正常显示异常位置的源代码。 如下图: 问题原因: 使用 pushy …...
IntelliJ IDEA 远程调试
IntelliJ IDEA 远程调试 在平时开发 JAVA 程序时,在遇到比较棘手的 Bug 或者是线上线下结果不一致的情况下,我们会通过打 Log 或者 Debug 的方式去定位并解决问题,两种方式各有利弊,今天就简要介绍下如何通过远程 Debug 的情况下…...
Java实现简单爬虫——爬取疫情数据
1.项目准备 在项目中使用到了jsoup和fastjson jsoup用于创建一个连接(绘画) 用于获取和解析HTML页面 而fastjson对数据进行一个格式化 在pom.xml导入坐标 <dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</a…...
大数据技术-Hadoop(一)Hadoop集群的安装与配置
目录 一、准备工作 1、安装jdk(每个节点都执行) 2、修改主机配置 (每个节点都执行) 3、配置ssh无密登录 (每个节点都执行) 二、安装Hadoop(每个节点都执行) 三、集群启动配置&a…...
04.HTTPS的实现原理-HTTPS的混合加密流程
04.HTTPS的实现原理-HTTPS的混合加密流程 简介1. 非对称加密与对称加密2. 非对称加密的工作流程3. 对称加密的工作流程4. HTTPS的加密流程总结 简介 主要讲述了HTTPS的加密流程,包括非对称加密和对称加密两个阶段。首先,客户端向服务器发送请求…...
flutter插件开发-ios
flutter插件开发是一个重要的技能,拓展flutter与原生的通信,将一些公用的东西封装,给不同的项目使用。 阅读前置: flutter基本通道调用 objective-c基础语法 ios项目基础知识 目录 1、创建一个插件项目2、项目结构3、编写原生代码…...
【AI日记】24.12.29 kaggle 比赛 2-17
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加:kaggle 比赛 Regression with an Insurance Dataset时间:6 小时 读书 书名:教育的本质时间:1 小时 律己 工作时间:优作息:…...
设计模式-创建型-工厂方法模式
什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是 创建型设计模式之一,目的是通过定义一个用于创建对象的接口,让子类决定实例化哪个类。简而言之,工厂方法模式通过延迟对象的创建过程到子类来…...
解决opencv在windows环境下读取中文图片名问题
在Windows系统下,cv2读取中文图片名时可能会报错,主要是因为OpenCV的imread函数在处理文件路径时,默认使用的是系统的编码格式,而Windows的默认编码可能与文件名的编码不匹配。具体原因包括: 编码不匹配:Wi…...
Apache Commons Pool :介绍与使用
Apache Commons Pool :介绍与使用 什么是 commons-pool2? commons-pool2 是 Apache Commons 提供的一个开源对象池实现框架。它旨在为应用程序提供通用的对象池支持,方便开发者管理资源(如数据库连接、网络连接等)复…...
sentinel-请求限流、线程隔离、本地回调、熔断
请求限流:控制QPS来达到限流的目的 线程隔离:控制线程数量来达到限流的目录 本地回调:当线程被限流、隔离、熔断之后、就不会发起远程调用、而是使用本地已经准备好的回调去提醒用户 熔断:熔断也叫断路器,当失败、或者…...
微信小程序 app.json 配置文件解析与应用
目录 一、什么是 app.json? 二、app.json 文件的基本结构 三、详细解析 app.json 配置项 1. pages:小程序页面路径配置 2. window:窗口样式配置 3. tabBar:底部标签栏配置 4. networkTimeout:网络请求超时配置 …...
C语言-共用体(联合体)
1.共用体(联合体) 1.共用体union是一个能在同一个存储空间存储不同类型数据的类型 2.共用体所占的内存长度等于其最长成员的长度。 3.同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用 4.共用体变量中起作用的成员是最后一次存放的成员ÿ…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
