本文介绍Buckhorn声卡的asio驱动开发,工作原理,和一些实现方法,供有兴趣的同仁起抛砖引玉之用。限于篇幅只涉及主要框架,如需体验Buckhorn声卡,可上天猫淘宝搜“Buckhorn”或“跳羚声卡”;如需合作,或更多技术资料,欢迎访问公司主页buckhorn.com.cn上的“技术支持->技术文档”。
1. ASIO注册DllRegisterServer/DllUnregisterServer
Windows下的ASIO分32位和64位,即使在64位操作系统下,有些软件例如千千静听,还是只会去找32位的ASIO,所以在32位OS下面我们只需安装32位ASIO,而64位OS下面我们要把32位ASIO和64位ASIO都安装上。对应于32位和64位OS,Buckhorn声卡的ASIO的文件名分别为bhuasio32.dll和bhuasio64.dll,下面我们的讲解都是基于这两个文件。
1.1 ASIO的安装
Windows下的ASIO驱动其实就是一个dll程序,提供DllRegisterServer,DllUnregisterServer这两个标准接口,供安装和卸载的时候使用。安装和卸载,可以使用Windows自带的dll注册程序regsvr32:
regsvr32 /s “C:\Program Files (x86)\Buckhorn_Studio\bhasio32.dll”
regsvr32 /s “C:\Program Files (x86)\Buckhorn_Studio\bhasio64.dll”
这时bhuauioxx.dll的DllRegisterServer接口函数会被调用。
卸载时:
regsvr32 /s /u “C:\Program Files (x86)\Buckhorn_Studio\bhasio32.dll”
regsvr32 /s /u “C:\Program Files (x86)\Buckhorn_Studio\bhasio64.dll”
这时bhuauioxx.dll的DllUnregisterServer接口函数会被调用。
1.2 ASIO 接口展现
为了让上层软件找到我们的ASIO,我们需要在DllRegisterServer函数里添加下图中的两个注册表,这是ASIO规范里的要求,上层软件都会按照这个注册表去寻找出所有的ASIO接口的---如果系统安装了不止一个ASIO声卡的话。
注意,这个CLSID的值,需要大家自己为自己的产品去生成一个的,可以使用visual studio里的Tools->Create GUID,产生全球唯一的一个GUID。
在的DllRegisterServer函数里创建这两个注册表,而在DllUnregisterServer函数里删除这两个注册表。在32位OS下这样做足够了,但在64位OS下,这两个注册表仅仅是注册了bhuasio64.dll,我么还要注册一个32位的ASIO,位于“Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ASIO”下。这时,上层软件就可以找到我们的ASIO了:
2. ASIO数据流
ASIO和上层软件之间的音频格式是PCM,上层软件从ASIO处看到的是有多少输入通道,有多少输出通道,没有立体声5.1声道等概念,由我们的ASIO了解到的下层物理声卡和虚拟声卡的通道数(立体声,5.1声道等),把上层软件的单声道输出组装成下层声卡可以认出的多声道输出格式。而在输入方向,ASIO会把来自声卡的多声道输入数据,分离成单声道的PCM数据流。下图以双声道声卡为例描述ASIO音频流:
具体的在输出方向组合成多声道,在输入方向分离成单声道,数据帧组装方式如下图:
3. ASIO与上下层的接口
ASIO与上层软件之间,是通过两个buffer交换数据的,这个buffer大小就是我们在ASIO设置界面里头看到的32/64/128/256/512 Sample,当ASIO把当前buffer的输出数据发送完成,并且输入数据也接收完成时,ASIO会去调用上层软件给我们的switchbuffer函数指针,上层软件这个回调函数就会把ASIO当前的buffer取走,把另一个新buffer递交给ASIO,这个新buffer里头已经由上层软件放入了需要输出的音频数据,并且输入的数据已经被上层软件copy走了。
而ASIO与下层驱动之间,可以通过DeviceIoCotrol(READ, OVERLAPPED)输入数据,通过DeviceIoCotrol(WRITE, OVERLAPPED)输出数据的。当然,采用异步操作,就是通过switchbuffer获取新buffer之后,立即在输出方向组合成下层声卡可以接受的多声道PCM音频流,调用DeviceIoCotrol(WRITE, OVERLAPPED)输出;而在输入方向调用DeviceIoCotrol(READ, OVERLAPPED)以读取输入音频流。由于是异步读写,这些函数调用会立即返回,然后ASIO调用WaitForMultiObject等待这些所有输入输出都完成,就把传动轮转一步,也就是调用switchbuffer进行下一轮操作。
如此,ASIO音频流就流动起来了。搞定ASIO注册和数据流之后,ASIO其它接口部分就容易了,可以下载免费的asio sdk,并参考里头的spec,详细地描述了接口要求。