百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Windows下Core_Audio_APIs的使用简介

cac55 2024-10-27 08:18 17 浏览 0 评论


Windows Vista 之后系统,音频系统相比之前的系统有很大的变化,产生了一套新的底层 API 即 Core Audio APIs 。该低层 API 为高层 API( 如 Media Foundation( 将要取代DirectShow 等高层 API) 等 ) 提供服务。该系统API具有低延迟、高可靠性、安全性等特点。

本文主要从实时音视频场景中,简单介绍该API的使用。

Core Audio APIs 的组成:MMDeviceEndpointVolumeWASAPI等。对于实时音视频系统,主要用到的是MMDeviceEndpointVolume这两套API。其在系统中的位置如下图:


我对实时音视频中音频设备的使用简单的分为:

1、设备列表管理

2、设备初始化

3、设备功能管理

4、数据交互

5、音量管理

6、设备终端监听

接下来为大家介绍相关功能的实现:

1、设备列表管理

音频设备的管理,由MMDevice API来实现。

首先我们要创建一个IMMDeviceEnumerator对象来开始相关功能的调用。

IMMDeviceEnumerator* ptrEnumerator;

CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&ptrEnumerator));

并通过IMMDeviceEnumerator可以实现:获取系统默认设备GetDefaultAudioEndpoint、获取设备集合IMMDeviceCollection、获取指定设备GetDevice、注册设备监听IMMNotificationClient(监听设备插拔及状态变更)。

通过这些方法,我们能得到系统默认设备、遍历设备列表、打开指定设备并监听设备变更。这样就实现了实时音视频中的设备管理相关的功能。

2、设备初始化

音频设备的启动是整个音频模块的可靠性的重要节点。根据设备类型和设备数据捕获方式,我们可分为3类设备:麦克风采集、扬声器播放、扬声器采集。

首先我们需要一个IMMDevice对象,可以在设备管理的相关功能中获取。

IMMDevice* pDevice;

//GetDefault

ptrEnumerator->GetDefaultAudioEndpoint((EDataFlow)dir, (ERole)role/* eCommunications */, &pDevice);

//Get by path

ptrEnumerator->GetDevice(device_path, &pDevice);

//GetIndex

pCollection->Item(index, &pDevice);

再通过IMMDevice得到IAudioClient,设备的格式设置及初始化通过IAudioClient对象实现。一般都以共享模式打开,其中麦克风采集及扬声器播使用事件驱动方式处理数据,而扬声器采集以回环的方式驱动处理数据。简单示例如下:

//mic capturer

ptrClient->Initialize(

AUDCLNT_SHAREMODE_SHARED,

AUDCLNT_STREAMFLAGS_EVENTCALLBACK |

AUDCLNT_STREAMFLAGS_NOPERSIST,

0,

0,

(WAVEFORMATEX*)&Wfx,

NULL);

//playout render

ptrClient->Initialize(

AUDCLNT_SHAREMODE_SHARED,

AUDCLNT_STREAMFLAGS_EVENTCALLBACK,

0,

0,

(WAVEFORMATEX*)&Wfx,

NULL);

//playout capturer

ptrClient->Initialize(

AUDCLNT_SHAREMODE_SHARED,

AUDCLNT_STREAMFLAGS_LOOPBACK,

0,

0,

(WAVEFORMATEX*)&Wfx,

NULL);

其中Wfx是设备格式参数,一般为了保证设备的可用性,使用默认格式(通过IAudioClient::GetMixFormat获取),如果需要使用自定义格式,可以通过IAudioClient::IsFormatSupported方法去遍历尝试设备支持格式。

3、设备功能管理

针对麦克风设备,我们通常需要对其进行数据处理。部分硬件设备和系统支持自带的降噪、增益、消回音等功能。但是一般windows系统下设备比较繁杂不可控,大都使用软件算法处理。如果我们需要检测设备是否使用了自带的处理功能及相关参数,需要使用Topology模块的功能。

IDeviceTopology* pTopo;

pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_INPROC_SERVER, 0,&pTopo);

通过IDeviceTopology,我们能够遍历IConnector对象,获得IAudioAutoGainControlIAudioVolumeLevel等能力对象,并处理相关能力。

注意:IConnector可能是循环嵌套,在遍历IConnectorIPart时需要判别成员对象IPart的类型。


4、数据交互

在设备初始化的时候,我们就根据不同的设备选择了不同的模式进行了启动。不同的设备在各自的模式下,数据驱动也各有不同:




在和设备进行数据交互时,我们需要根据数据获取模式,获取对应的服务对象来获取设备数据。其中采集部分使用IAudioCaptureClient服务用于获取设备数据,播放使用IAudioRenderClient服务获取设备数据传入指针。示例如下:

//capturer

IAudioCaptureClient* ptrCaptureClient;//audioin or audioout

ptrClient->GetService(__uuidof(IAudioCaptureClient), (void**)&ptrCaptureClient);

{//work thread

//Wait Event

ptrCaptureClient->GetBuffer(

&pData, // packet which is ready to be read by used

&framesAvailable, // #frames in the captured packet (can be zero)

&flags, // support flags (check)

&recPos, // device position of first audio frame in data packet

&recTime); // value of performance counter at the time of recording

//pData processing

ptrCaptureClient->ReleaseBuffer(framesAvailable);

}

//render

IAudioRenderClient* ptrRenderClient;//audioout

ptrClient->GetService(__uuidof(IAudioRenderClient), (void**)&ptrRenderClient);

{//work thread

BYTE* pData;//form buffer

UINT32 bufferLength = 0;

ptrClient->GetBufferSize(&bufferLength);

UINT32 playBlockSize = nSamplesPerSec / 100;

//Wait Event

UINT32 padding = 0;

ptrClient->GetCurrentPadding(&padding);

if (bufferLength - padding > playBlockSize)

{

ptrRenderClient->GetBuffer(playBlockSize, &pData);

//request and getdata

ptrCaptureClient->ReleaseBuffer(playBlockSize, 0);

}

}

在实际的数据交互中,需要另开单独线程处理GetBufferReleaseBuffer。其中麦克风采集及扬声器播放时,都是通过设备事件驱动,可以在设备初始化完成后设置响应的事件句柄(IAudioClient::SetEventHandle)。

在整个音视频系统中,设备数据线程还需要统计数据处理时长、采集播放缓存大小等,用户监听检查设备状态及aec延迟计算。

5、音量管理

一般音量管理只在设备选定后处理当前设备的音量,所以一般使用IAudioEndpointVolume,该对象通过设备对象IMMDevice获取:

IAudioEndpointVolume* pVolume;

pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, reinterpret_cast<void**>(&pVolume));

得到IAudioEndpointVolume对象后,我们能处理当前设备的音量控制:

pVolume->GetMasterVolumeLevelScalar(&fLevel);

pVolume->SetMasterVolumeLevelScalar(fLevel, NULL);

静音控制:

BOOL mute;

pVolume->GetMute(&mute);

pVolume->SetMute(mute, NULL);

以及注册IAudioEndpointVolumeCallback监听音量状态:

IAudioEndpointVolumeCallback* cbSessionVolume;//need to do

pVolume->RegisterControlChangeNotify(cbSessionVolume);


6、设备终端监听

在运行过程中除了设备的插拔等操作,还可能有一些属性变更等,一般用IAudioSessionEvents监听:

IAudioSessionControl* ptrSessionControl;

ptrClient->GetService(__uuidof(IAudioSessionControl), (void**)&ptrSessionControl);

IAudioSessionEvents* notify;

ptrSessionControl->RegisterAudioSessionNotification(notify);

该回调监听,能监听该设备的连接工作状态,名称变更等。

一些注意事项:

1、线程优先级

在实际的工程开发过程中,我们需要对音频线程的工作线程进行处理。通常通过调用系统模块Avrt.dll,动态调用其下的函数,将调用线程与指定任务(Pro Audio)相关联。上代码:

函数绑定:

avrt_module_ = LoadLibrary(TEXT("Avrt.dll"));

if (avrt_module_)

{

_PAvRevertMmThreadCharacteristics = (PAvRevertMmThreadCharacteristics)GetProcAddress(avrt_module_, "AvRevertMmThreadCharacteristics");

_PAvSetMmThreadCharacteristicsA = (PAvSetMmThreadCharacteristicsA)GetProcAddress(avrt_module_, "AvSetMmThreadCharacteristicsA");

_PAvSetMmThreadPriority = (PAvSetMmThreadPriority)GetProcAddress(avrt_module_, "AvSetMmThreadPriority");

}

在实际的数据处理线程关联:

hMmTask_ = _PAvSetMmThreadCharacteristicsA("Pro Audio", &taskIndex);

if (hMmTask_)

{

_PAvSetMmThreadPriority(hMmTask_, AVRT_PRIORITY_CRITICAL);

}

通过任务绑定,能有效的提升音频数据处理线程的可靠性。

2、工作线程

设备的相关初始化和释放操作,需要在统一的线程处理,部分系统com对象在释放时需要在创建线程释放,不然可能导致释放崩溃。而一些音量选择、监听等的处理可以在用户线程处理,但需要做好多线程安全。

3、设备格式选择

在设备的采样率、声道等格式选择时,如果需要使用自定义的格式,可能出现格式匹配失败或者选择匹配的格式后设备初始化失败的场景。通常此类场景下直接使用默认格式启动。

4、数据处理异常

在数据处理线程处理音频数据时,通常会出现事件响应超时、设备对象异常等情况。通常的处理方法是,先退出数据线程并结束设备,然后检查当前设备是否正常功能,然后重新启动当前设备或选用默认设备。


关注网易云信,听取最新前沿观察,看最有价值技术干货,学网易最新实践经验。陪你从思考者成长为技术专家。

相关推荐

Protel电路设计常用设计编辑器案例2——创建元件

#大有学问#今天介绍一下Protel常用设计编辑器的电气连接工具栏。单击主工具栏上的工具按钮或选择【查看】|【工具栏】|【配线工具栏】菜单命令可以关闭或打开【电气连接(WiringTools)】工具...

Protel调整元器件的位置(1)——移动和对齐元器件

今天介绍调整元器件位置的方法。首先介绍移动和对齐元器件的方法。在绘制电路原理图时,放置完了的电路图可能位置不太合适,需要进行移动。原理图中的所有对象都可以被移动,移动方法相似。对于元器件的移动来说又分...

电路仿真软件详谈(八),proteus电路仿真软件和protel的区别

电路仿真软件是常用工具类型之一,proteus更是电路仿真软件中的佼佼者。但是对于proteus电路仿真软件和protel,二者总是被弄混淆。例如,protel是电路仿真软件吗?proteus电路仿真...

PCB文件转换生产文件Protel 99SE_pcb格式转换

为何要将PCB文件转换为GERBER文件和钻孔数据?因为GERBER文件是一种国际标准的光绘格式文件,它包含RS-274-D和RS-274-X两种格式,其中RS-274-D称为基本GERBER格式,并...

PCB设计项目教程 -PDF_pcb设计作品

PCB设计项目教程》及相关PCB设计教材详细介绍:一、核心教材《PCB设计项目教程》该教材由徐凯、王威担任主编,于2017年由北京理工大学出版社出版。其采用“项目导向、任务驱动”的教学模式,...

最受欢迎的pcb设计软件Protel99se到底怎么样?

Protel99se是一款国内非常实用且流行的设计行业的pcb设计软件,其由pcb原理图设计和多层板电路设计两大功能组成,其最大的特点是好获取,在网上可以随便的找到,且Protel99se软件适用于w...

人人都是网络雇佣兵,一种基于路由器的ddos平台设计思路

本文灵感来自于三个方面优酷路由宝,迅雷宝这种路由器流量兑现方式Anonymous匿名者的ddos方式传统木马ddos方案先说路由宝迅雷宝,这种以用户网络为节点的CDN网络中,会传输大量的流量,我上月优...

接口性能测试工具Locust介绍_接口和性能的测试要点

接口性能测试工具其实挺多的,小型有apache的ab工具,大型的有Jmeter、Locust......这里要介绍的是Locust,相对于Jmeter进行了比较完善的封装,Locust可以就显的更自...

华硕笔记本电脑安装系统实战心得体会

故障:某某的电脑叫人安装系统至一半就蓝屏死机.拿来给我安装,发现光驱无效,不能用光盘安装.电脑启动蓝屏.解决方法:用了半天时间安装也出现类似问题.后来考虑用U盘来装.1.首先制作U盘系统,把U盘资...

dos命令systeminfo图文教程,显示操作系统配置信息msinfo32

大家好,我是老盖,首先感谢观看本文,本篇文章做的有视频,视频讲述的比较详细,也可以看我发布的视频。今天我们学习systeminfo命令,该工具显示本地或远程机器(包括服务包级别)的操作系统配置的信息,...

玩家展示现代硬件上运行的MS-DOS 拥有令人难以置信的向后兼容性

一位YouTuber展示了在现代计算机硬件上直接运行古老的MS-DOS操作系统和经典游戏的能力。这段视频由YouTuberInkbox发布,向观众展示了如何启动古老的、前Windows...

比微PE还干净还强大,带网络:USBOS V3.0超级PE装机工具20221031

期待已久的USBOSV3.0超级PE装机工具20221031又和大家见面了,用过的朋友都知道他的确很强大,对于新旧电脑的支持很好,目前为止还没有电脑不支持的,包括苹果PC。很多朋友还在用诸如大白菜、...

大童保险李晓婧:保险的本位是风险管理应在四方面进行建设

经济观察网记者姜鑫5月17日,大童保险服务宣布升级了风险管理模式,推出DOSM(DemandOriented,SolutionModel)需求导向型解决方案5.0版本。新解决方案从原有“六位...

DOS常用命令及简介_dos常用命令大全及用法

DOS是英文DiskOperatingSystem的缩写,意思是“磁盘操作系统”。我是在95年开始学的电脑,当时学校的机房里,还没有一台WINDOWS操作系统的电脑,当时都是用DOS、UC-DOS...

Windows 忘记开机密码?不用任何工具,1招轻松破解

出现忘记Windows密码的情况,概率有多大?对此,小电只能回答忘记开机密码的情况,说来就来,没有规律,也不会提前告诉你一声~而忘记Windows开机密码的时候,很多朋友都会想起可以使用u盘启动盘来破...

取消回复欢迎 发表评论: