Xp下麦克风设备及音量检测
从Vista开始,windows底层的音频架构发生了改变:原本是底层API的waveXXX、mixerXXX等都在Core Audio APIs的基础上进行了重构,上升为了高层API;底层API变为Core Audio API。 由于这个原因,在利用遗留音频技术(waveXXX、mixerXXX等)进行开发的时候,在WinXp和其他系统上的表现会不太一致。
但是如果要在Xp上进行开发的话,就必须要使用这些老旧的技术,没得选。
Xp下音频开发选择
在Xp下进行开发,大概只有DirectX、waveXXX和mixerXXX可选了。 这里我们简单描述它们的优缺点:
优点:
- DirectX: 功能强大、灵活。
- waveXXX: 使用简单,对于输入音频设备,应用中的大部分功能需求都支持。
- mixerXXX: 完全底层的音频控制。
缺点:
- DirectX: 概念多、不容易上手(灵活的代价)。
- waveXXX: 对输入音频的控制处于应用层,无法控制系统层的音频输入(输出设备未测试)。
- mixerXXX: 概念多并且比较抽象,API的使用很晦涩。
Xp下输入音频开发实例
我们选择waveXXX api来实现这个开发实例,因为waveXXX相对来说比较好用,这样我们不用花费过多的时间去了解其他概念上的细节。
1. 设备枚举及打开
先调用waveInGetNumDevs()获取设备总数,然后传入设备序号(0 ~ 总数-1),并选择设备支持的PCM数据格式中的一种打开设备,获取到设备句柄:
auto inputAudioDeviceNum = waveInGetNumDevs();
for (int i = 0; i < inputAudioDeviceNum; ++i) {
WAVEINCAPS waveInCaps;
auto returnValue = waveInGetDevCaps(i, &waveInCaps, sizeof(waveInCaps)) ;
......
WAVEFORMATEX waveFormatEx = chooseAppropriateFormat();
auto returnValue = waveInOpen((LPHWAVEIN)&deviceInfo.handle, index, &waveFormatEx,
(DWORD_PTR)CoreAudioHelper::waveInProc,
(DWORD_PTR)this,
CALLBACK_FUNCTION);
......
}
2. 获取输入音频数据
为了获取音频数据,我们需要准备一个Buffer,并将这个Buffer添加到你想要获取数据的音频设备上,然后开始这个设备的音频捕获:
bool CoreAudioHelper::startPeakGetter()
{
Q_ASSERT(m_currentDeviceIndex >= 0 && m_currentDeviceIndex < m_infos.size());
auto& deviceInfo = m_infos[m_currentDeviceIndex];
ZeroMemory(m_buffer, sizeof(m_buffer));
m_waveHdr.dwFlags = 0;
m_waveHdr.lpData = (LPSTR)m_buffer;
m_waveHdr.dwBufferLength = sizeof(m_buffer);
auto returnValue = waveInPrepareHeader(deviceInfo.handle, &m_waveHdr, sizeof(m_waveHdr));
CHECK_RETURN(returnValue);
returnValue = waveInAddBuffer(deviceInfo.handle, &m_waveHdr, sizeof(m_waveHdr));
CHECK_RETURN(returnValue);
returnValue = waveInStart(deviceInfo.handle);
CHECK_RETURN(returnValue);
deviceInfo.started = true;
return true;
}
当这个Buffer被数据填满的时候,系统就会通知你,这时候我们需要先调用waveInUnprepareHeader()来取消先前准备的Buffer,然后就可以对数据进行操作了(这里我们计算了音频的音量大小)。在之前打开设备的时候,你可以选择多种通知方式:回调、窗口消息、事件或者线程,这里我选择使用回调方法。如果要连续的获取捕获到的数据,我们就要在Buffer被填满的时候不断添加新的Buffer。注意因为在回调中基本上不可以调用任何系统api,所以我们需要另一个线程来添加新Buffer,并利用信号量来进行同步:
void CoreAudioHelper::waveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
switch (uMsg) {
case WIM_OPEN:
break;
case WIM_CLOSE:
{
......
}
case WIM_DATA:
{
......
break;
}
default:
Q_ASSERT(false && "never receive other msg!");
}
}
// non-qt thread have no qt event loop which causing signal/slot not working,
// we use a queue to keep the value and a semaphore to notify the internal thread
// to emit the signal.
void CoreAudioHelper::appendPeakValue(qint16 value)
{
m_peakValueQueue.push(value);
// cannot call Win32 api inside a callback, so we notify the buffer waiter thread
m_bufferFilled.release(1);
}
void CoreAudioHelper::BufferWaiterThread::run()
{
while (true) {
m_helper->m_bufferFilled.acquire(1);
m_helper->unprepareBuffer();
if (m_helper->m_stopThread)
break;
if (m_helper->m_emitUnplugged) {
emit m_helper->currentDeviceUnplugged();
m_helper->m_emitUnplugged = false;
break;
}
m_helper->emitPeakLevelAndContinue();
}
}
bool CoreAudioHelper::unprepareBuffer()
{
auto deviceInfo = m_infos.at(m_currentDeviceIndex);
auto returnValue = waveInUnprepareHeader(deviceInfo.handle, &m_waveHdr, sizeof(m_waveHdr));
CHECK_RETURN(returnValue);
return true;
}
3. 音量大小计算
根据PCM数据是8位还是16位,我们把Buffer中的比特数据转换成合适的变量并计算保存最小值和最大值。因为实际音频波形是以0点为水平上下波动的,

- 8位PCM: 无符号数据,范围0~255, 水平值127。
- 16位PCM: 有符号数据,范围-32767~32767,水平值0。
我们只需要把最大波动值除以上限值就可以获得音量大小了(具体见下一小节)。
// buffer already filled with input audio data
CoreAudioHelper* helper = reinterpret_cast<CoreAudioHelper*>(dwInstance);
Q_ASSERT(helper->m_waveHdr.dwFlags & WHDR_DONE);
qint32 peakMin = 255;
qint32 peakMax = 0;
for (char* ptr = helper->m_buffer; ptr < &helper->m_buffer[16]; ) {
qint32 dataValue;
if (helper->m_is8BitsSample) {
dataValue = *(unsigned char*)ptr;
ptr++;
} else {
dataValue = *(qint16*)ptr;
ptr += 2;
}
if (dataValue < peakMin) peakMin = dataValue;
if (dataValue > peakMax) peakMax = dataValue;
}
helper->appendPeakValue(max(-peakMin, peakMax));
4. 音量设置和静音
waveXXX API只提供了音频数据捕获,因此我们需要自己来模拟音量和静音的控制,这里我们把这些控制应用在获取到的音量大小上:
void CoreAudioHelper::emitPeakLevelAndContinue()
{
if (!m_peakValueQueue.empty()) {
qint32 peakValue = m_peakValueQueue.front();
m_peakValueQueue.pop();
if (!m_infos.at(m_currentDeviceIndex).muted) {
if (m_is8BitsSample) {
// when 8-bit sample, the range is 0--255, the silence data value is 127
emit peakChanged(qint32(abs(peakValue - 127) / 1.27) *
m_infos.at(m_currentDeviceIndex).volumeFilterPercent);
}
else {
// when 16-bit sample, the range is -32767--32767, the silence data value is 0
emit peakChanged(qint32(abs(peakValue) / 327.67) *
m_infos.at(m_currentDeviceIndex).volumeFilterPercent);
}
startPeakGetter();
}
}
}
5. 运行结果
结果就是这样啦,完整代码见此处。

Xp下麦克风设备及音量检测的更多相关文章
- Linux下usb设备驱动详解
USB驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,我们只需要了解它的功能.形象的说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB ...
- 在XP下基于VHD版XP 2003 win7制作的RAMOS心得
在XP下基于VHD版win7制作的RAMOS心得1.用DiskGenius创建1.85G的VHD固定磁盘文件,以win7prosen.vhd为例,然后进行分区格式化,格式化时启用NTFS压缩.2.为了 ...
- ubuntu 在XP下硬盘安装
以下选择在XP下用 grub4dos 安装 ubuntu 12.04版本 需要下载两个文件:一个是grub4dos,另一个是 ubutuntu 镜像文件 grub4dos下载地址:http://dow ...
- Windows XP下安装WinCE6.0开发环境
Windows下怎样编译WinCE6.0及开发应用程序.以下介绍(安装之前必须保证C盘有足够的空间!20g左右!主要是由于在安装程序在安装过程中要解压): 在Visual Studio 2005之前, ...
- Node.js 操作 OSX 系统麦克风、扬声器音量
最近几年 Electron 很火,公司也正好有个项目想做跨平台客户端,大家研究了一下就选择了 Electron,第一次做 js 的项目遇到了不少坑,不过也都一点点解决了. 因为项目中需要对用户录音,H ...
- 【DSP开发】【Linux开发】Linux下PCI设备驱动程序开发
PCI是一种广泛采用的总线标准,它提供了许多优于其它总线标准(如EISA)的新特性,目前已经成为计算机系统中应用最为广泛,并且最为通用的总线标准.Linux的内核能较好地支持PCI总线,本文以Inte ...
- xp 下查看进程指令
xp 下快速查看进程及关联 exe 的指令,刚发现,还没有测试 win7 和 win10 支持不支持. wmic process where creationclassname="win32 ...
- 【求助】WPF 在XP下 有的Textbox光标会消失
最近做个项目,一直有一个问题没有解决,就是在XP下,有的Textbox里在文本框里没有东西的时候,会没有光标.不同的XP机器,失去光标的Textbox也不一样. 各位大师看下面的三张图,当Textbo ...
- 使用Windbg在XP下Heap追踪失败的原因
1.故事背景 最近同事的代码中碰到一个bug会导致奔溃的bug,从dump上看是由于某个对象的堆内存指针被释放了,但代码仍调用了该对象指针的虚函数,从而引起内存访问违法崩溃,由于该类被大量使 ...
随机推荐
- ES6块级作用域
块级作用域的优点 避免变量冲突,比如程序中加载了多个第三方库的时候,如果没有妥善地将内部私有函数或变量隐藏起来,就很容易引发变量冲突: 可以方便的进行模块管理: 利于内存回收:(块级作用域里声明的变量 ...
- 自制STP配置实验
本图使用Gns模拟器 实验需求: 1.要求利用vtp实现vlan同步设置 2.要求vtp server实现备份冗余 3.创建vlan 1~10要求MLSW1 是奇数vlan主根 MLSW2 是偶数vl ...
- 九度OJ 1013 开门人和关门人
#include <iostream> #include <string.h> #include <sstream> #include <math.h> ...
- 使用 LVS 实现负载均衡原理及安装配置详解
负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均衡设备F5.Netscale.这里主要是学 ...
- 【1414软工助教】团队作业2——需求分析&原型设计 得分榜
题目 团队作业2--需求分析&原型设计 作业提交情况情况 本次作业所有团队都按时提交作业. 往期成绩 个人作业1:四则运算控制台 结对项目1:GUI 个人作业2:案例分析 结对项目2:单元测试 ...
- 团队作业八——第二次团队冲刺(Beta版本)第4天
团队作业八--第二次团队冲刺(Beta版本)第4天 一.每个人的工作 (1) 昨天已完成的工作 做一下用户注册的功能和登录功能. (2) 今天计划完成的工作 完成界面跳转 (3) 工作中遇到的困难 界 ...
- Eclipse rap 富客户端开发总结(6) : 如何发布rap到tomcat
1.先下载以来的打包插件 war products 输入下面的地址,选择相应的插件 新建一个 war product Configutation向导 下面的war product Configut ...
- 巧用 BootStrap --- 栅格系统(布局)轻松搞定网页响应式布局!
摘要:Bootstrap 为我们提供了一套响应式.移动设备优先的流式栅格系统,合理的使用栅格系统将会使得网站页面布局变得更加简单,在设置了媒体查询之后,响应式网站也无需再单独写了.接下来我以Boots ...
- APUE 3 -- 信号 (signal)<II>: 可靠信号
一个事件可以事一个信号发送给一个进程,这个事件可以是硬件异常,可以是软件条件触发,可以是终端产生信号,也可以是一个kill函数调用.当信号产生后,内核通常会在进程表中设置某种形式的标志(flag).我 ...
- jz2440重新分区
在购买开发板的时候,板子上已经烧写好了bootloader.内核和文件系统.但是在具体使用时,发现板子上划分的内核分区只有2M,但是我编译出来的内核大于2M,于是将内核烧写到nandflash上面时会 ...