ASIO音频驱动开发指南 2.0

 

  1.0版和2.0版的PDF下载地址:http://bbs.driverdevelop.com/read.php?tid-111697-keyword-asio.html

 

   2.0版本在原文档基础上,增加实现了一款ASIO音频软件,讲述其实现代码和内部逻辑。原文在本博客中搜索,本文只包含新增部分。


    注释(2011/2/22):

    我新出版的《竹林蹊径——深入浅出Windows驱动开发》一书中,有一章专门阐述了ASIO驱动开发,包括完整的用户层ASIO接口驱动和内核ASIO驱动实现,它不需要实际硬件,即能完整实现ASIO功能并工作,这种形式的ASIO被我定义为V-ASIO。由于书刚出版,我还不能把文档免费发布,但今年晚些时候,我会争取发出来。请大家关注。下图是V-ASIO原理图:

图1 V-ASIO原理图


附录.  简易ASIO音频播录软件

 

ASIO TOOL

2  ASIO软件界面


 

上面章节讲了IASIO接口类中的接口函数。和读SDK文档中的介绍文字一样,看过之后可能会觉得有一种看得见、但摸不着的感觉。这个一个小节,我用一个音频软件的简例,力争让大家有机会能摸得着这些接口。

简单介绍一下这个例子程序。这是一个用Win32控制台编写的简易ASIO音频软件,能够实现ASIO的播录功能。它首先根据指定的ASIO驱动名称查找并获取ASIO驱动类的一个实例指针。然后初始化这个实例,并在用户控制下进行播放或录音操作。

代码大体改编自SDK中的sample,但根据我收到的读者反馈,SDK中的sample有几个很不好的地方。第一是写得太标准了(一笑),以至于好多读者都不耐心去分析那一个子函数套一个子函数的结构;第二是竟然用把数字打印到屏幕上的方式来模拟音频输出,要知道大部分读者是没有耐心的,可能还急眼啦,他们需要的,是能够直接听到声音!

我在重写的过程中,针对第一点,尽量把代码写得简单,在主函数中完成了大部分的初始化工作;针对第二点(读者会微笑的),我使得它既能播放,又能录音。

这个例子程序可运用于任何ASIO驱动,本人在XP系统上用AudioPhile、麦盒(Mi-Box I)测试过,也试过ASIO4ALL,完美通过。如果读者的系统中已经安装有某个ASIO驱动,也可试试。希望这个例子不是美人图,而是美人胚,让你看到且摸到。

 

 

 

5.1 主函数

主函数包含了最主要的内容,全部ASIO驱动初始化都在主函数中完成。我们需要获取IASIO接口类的一个实例,初始化实例,然后获取ASIO驱动的有用信息。我发现代码并不多,所以就全部粘贴过来,代码中详细的注释足以让读者读懂它们。

 

int _tmain(int argc, _TCHAR* argv[])

{

 ASIOError result;

 char strErr[128];

 ASIOCallbacks asioCallbacks;

 

 __try

 {

  //

  // 获取ASIO驱动接口类的实例;这是第一步工作,如果成功才能进行下面的初始化工作

  //

 

  gpASIO = loadDriver(argv[1]);

  if(gpASIO == NULL){

   printf("找不到ASIO设备/n");

   __leave;

  }

 

  // 

  // 初始化

  //

 

  result = gpASIO->init(NULL);

  if(ASIOTrue != result)

  {

   gpASIO->getErrorMessage(strErr); // 获取错误信息

   printf("调用init方法失败:%d %s", result, strErr);

   __leave;

  }

 

  // 获取ASIO驱动名称

  gpASIO->getDriverName(gASIODrvInfo.driverInfo.name);

  gASIODrvInfo.driverInfo.driverVersion = gpASIO->getDriverVersion();

  printf ("/n名称: %s/n版本: %d/n", 

   gASIODrvInfo.driverInfo.name,

   gASIODrvInfo.driverInfo.driverVersion

   );

 

  // 获取缓冲信息

  result = gpASIO->getBufferSize(&gASIODrvInfo.minSize,

 &gASIODrvInfo.maxSize, 

 &gASIODrvInfo.preferredSize,

 &gASIODrvInfo.granularity

);

  if(result != ASE_OK)

  {

   gpASIO->getErrorMessage(strErr); // 获取错误信息

   printf("调用getBufferSize方法失败:%d %s", result, strErr);

   __leave;

  }

 

  // 获取当前采样频率

  result = gpASIO->getSampleRate(&gASIODrvInfo.sampleRate);

  if(result != ASE_OK)

  {

   gpASIO->getErrorMessage(strErr); // 获取错误信息

   printf("调用getSampleRate方法失败:%d %s", result, strErr);

   __leave;

  }

  

  // 判断获取的当前采样频率值是否在有效区间

  if (gASIODrvInfo.sampleRate <= 0.0 || gASIODrvInfo.sampleRate > 96000.0)

  {

   result = gpASIO->setSampleRate(44100.0);

   if(ASE_OK != result)

   {

    printf("不支持默认采样频率:%d", result);

    __leave;

   }

 

   result = gpASIO->getSampleRate(&gASIODrvInfo.sampleRate);

   if(result != ASE_OK)

   {

    gpASIO->getErrorMessage(strErr); // 获取错误信息

    printf("调用getSampleRate方法失败:%d %s", result, strErr);

    __leave;

   }

  }

  

  // 获取输入/输出声道数

  result = gpASIO->getChannels(

&gASIODrvInfo.inputChannels, 

&gASIODrvInfo.outputChannels

);

  if(result != ASE_OK)

  {

   gpASIO->getErrorMessage(strErr); // 获取错误信息

   printf("调用getChannels方法失败:%d %s", result, strErr);

   __leave;

  }

  

  // 针对每个声道,初始化ASIO缓冲

  //

  ASIOBufferInfo *info = gASIODrvInfo.bufferInfos;

 

  // input

  if (gASIODrvInfo.inputChannels > kMaxInputChannels)

   gASIODrvInfo.inputBuffers = kMaxInputChannels;

  else

   gASIODrvInfo.inputBuffers = gASIODrvInfo.inputChannels;

 

  for(int i = 0; i < gASIODrvInfo.inputBuffers; i++, info++)

  {

   info->isInput = ASIOTrue;

   info->channelNum = i;

   info->buffers[0] = info->buffers[1] = 0;

  }

 

  // outputs

  if (gASIODrvInfo.outputChannels > kMaxOutputChannels)

   gASIODrvInfo.outputBuffers = kMaxOutputChannels;

  else

   gASIODrvInfo.outputBuffers = gASIODrvInfo.outputChannels;

 

  for(int i = 0; i < gASIODrvInfo.outputBuffers; i++, info++)

  {

   info->isInput = ASIOFalse;

   info->channelNum = i;

   info->buffers[0] = info->buffers[1] = 0;

  }

 

  // 初始化回调函数数组。这些函数必须由音频程序自己提供,被ASIO驱动调用。

  // bufferSwitch是实现音乐播放的关键,此处传入音乐文件数据,就能播放出相应的音乐了。

  // sampleRateChanged在设备采样率改变时被调用,以提醒播放软件更改显示信息。

  // bufferSwitchbufferSwitchTimeInfo两个回调函数,ASIO驱动在需要更新音频数据的时候调用它们。

  // 后者是前者的升级版,即ASIO驱动在调用的时候会传入一个时间信息结构指针,便于音频软件做同步、定位等操作。

  // asioMessages回调可以让ASIO驱动更好地了解音频软件的能力,比如它所能支持的ASIO版本,是1.0还是2.0等。

  asioCallbacks.bufferSwitch = &bufferSwitch;

  asioCallbacks.sampleRateDidChange = &sampleRateChanged;

  asioCallbacks.asioMessage = &asioMessages;

  asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;

 

  // 创建缓冲区,注册回调函数

  result = gpASIO->createBuffers(gASIODrvInfo.bufferInfos,

   gASIODrvInfo.inputBuffers + gASIODrvInfo.outputBuffers,

   gASIODrvInfo.preferredSize, 

   &asioCallbacks

   );

 

  if(result != ASE_OK)

  {

   gpASIO->getErrorMessage(strErr); 

   printf("调用createBuffers方法失败:%d %s", result, strErr);

   __leave;

  }

 

  // 调用createBuffers后,可获取设备的输入输出延迟。

  // 延迟是一定存在的,除非操作系统能够快到收到一个字节,立刻播放一个字节的速度。

  // 既然有缓冲区,就有先后、等待。等待的时间就是延迟。

  // 输入延迟:一个音频采样从被设备获取到被传入音频软件,所经历的时间

  // 输出延迟:一个采样数据从音频软件输出去,直到它被声卡播放出来,期间所经历的时间

  gpASIO->getLatencies(&gASIODrvInfo.inputLatency, &gASIODrvInfo.outputLatency);

 

  // 获取声道的详细信息

  printf("声道信息.../n/n输入声道:%d/t输出声道:%d", gASIODrvInfo.inputBuffers, gASIODrvInfo.outputBuffers);

  for (int i = 0; i < gASIODrvInfo.inputBuffers + gASIODrvInfo.outputBuffers; i++)

  {

   gASIODrvInfo.channelInfos[i].channel = 

gASIODrvInfo.bufferInfos[i].channelNum;

   gASIODrvInfo.channelInfos[i].isInput = 

gASIODrvInfo.bufferInfos[i].isInput;

   result = gpASIO->getChannelInfo(&gASIODrvInfo.channelInfos[i]);

 

   if (result == ASE_OK)

    printf("%d %s声道%d:%s group: %d type: %d/n", 

     i, gASIODrvInfo.channelInfos[i].isInput ? "输入":"输出",

     gASIODrvInfo.channelInfos[i].channel,

     gASIODrvInfo.channelInfos[i].name,

     gASIODrvInfo.channelInfos[i].channelGroup,

     gASIODrvInfo.channelInfos[i].type);

  }

 

  printf("/n音频信息.../n/n");

  printf( "采样率:%d/n", gASIODrvInfo.sampleRate);

  printf( "输入延迟:%d, 输出延迟:%d/n", 

   gASIODrvInfo.inputLatency, gASIODrvInfo.outputLatency);

  printf( "最大缓冲:%d, 最小缓冲:%d, 当前缓冲:%d, 缓冲粒度:%d/n",

   gASIODrvInfo.maxSize, gASIODrvInfo.minSize, 

   gASIODrvInfo.preferredSize, gASIODrvInfo.granularity);

 

  // 测试ASIO驱动是否支持outputReady接口。

  // 如果ASIO驱动不支持,按照SDK中所做说明,返回值当为ASE_NotPresent

  // 这种情况下,我们程序下面就不应当调用outputReadyASIO驱动进行数据同步。

  if(ASE_OK == gpASIO->outputReady())

   gASIODrvInfo.postOutput = TRUE;

  else

   gASIODrvInfo.postOutput = FALSE;

 

  InitializeCommandVaribles();

  

  // 提高线程优先级

  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);

 

  // 等待并处理用户命令,直到退出。

  while (!gASIODrvInfo.bMainQuit)

  {

   CommandProcess();

  }  

 }

 __finally

 {

  if(gpASIO)

  {

   unloadASIO(gpASIO);

   delete gpASIO;

  }

 

  if(data_buff)

   delete data_buff;

 }

 

 

 return 0;

}

 

 

 

 

最后我拎出几个要点讲一下:

1. 音频软件由于数据吞吐量大,并且ASIO又要求低延迟和高性能保证,所以需要提高数据处理线程的优先级。这样处理的效果是相当明显的;读者可尝试把提高和不提高优先级两种情况进行比较:把ASIO驱动的延迟降到最低,然后修改代码分别在提高优先级和不提高优先级的情况下运行软件。经过我多次测试后发现,不提高优先级的时候爆音情况普遍严重。

2. ASIO驱动初始化函数的调用需要注意先后顺序。比如一般只在createBuffers被调用后才能调用getLatencies获取延迟读者应阅读SDK中的说明。

3. 四个ASIO回调函数非常重要。音频软件向ASIO驱动注册这些函数,自己是不会调用的,而是由ASIO驱动在适当的时候调用。初始化结束后,剩余的工作大部分都是由这几个回调在操作着。另外一个要点是,回调函数的调用是不被音频软件控制的,它们的运行环境不可知,可能在主线程中,也可能在辅助线程中。所以要注意线程同步。

 

5.2 数据同步

数据同步的重任落在bufferSwitchbufferSwitchTimeInfo两个回调函数的肩膀上。bufferSwitchTimeInfo函数比bufferSwitch多接受一个时间戳参数。软件可以根据时间戳,更新界面上显示的进度。

数据同步是以声道为单位进行的:先处理声道0数据,再处理声道1数据......但音频文件一般都是双声道的,所以可以变通一下将声道0和声道1组成立体声同时处理,这样更方便。可参看代码中的实现。

数据同步需要判断采样类型,采样类型其实就是采样深度,有16位、24位、32位,以及大序(Big Edian),小序(Little Edian)等。如声道0的采样类型为32位,当音频文件格式为16位采样深度时,需将文件中的每个采样转换为32位后再拷贝到声道0的缓冲中。转换算法算不上复杂,但如果弄错了,就会听到噪音。

播放的时候,将音频文件或内存中的数据拷贝到ASIO驱动的声道缓冲中;录音的时候,将声道缓冲中的数据保存到文件或内存中。这样,就实现了音频播录。

 

5.3 其他注意点 

1. 流程控制。ASIO数据流总是输入/输出操作并行的,所以不管是启动播放操作,还是启动录音操作,都将把两个方向的数据流同时开启。而如果要彻底关闭ASIO数据流,需要同时关闭播放与录音。

2. 音频格式。播放音频文件就会涉及到文件读写,正确解析WAV音频格式很重要。重要的信息包括:采样深度、采样频率、声道数等。

3. 用户命令。软件接受用户输入的简单命令,输入h?命令,会打印出帮助信息。软件运行方法:asioTool [ASIOName]。如果参数ASIOName为空,软件将把系统中找到的第一个ASIO驱动作为当前驱动。

用户命令包括:P-播放;R-录音;T-停止;Q-退出;S-设置;h/?-帮助

 

 

到这里,文章就告一段落了。文介绍了ASIO驱动的一种简单实现,利用DirectKS技术对普通的WDM声卡提供ASIO支持,最后还让你成功听到了音乐。我又在附件中为读者展示了一款微型ASIO音频软件,讲解了其代码实现和内部逻辑。通过这些讲解,ASIO从里到外的知识都能串起来了。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 40
    评论
### 回答1: Asio音频采集SDK是一种功能强大的音频采集开发工具,它为开发者提供了一种高效、低延迟的音频采集解决方案。 首先,Asio音频采集SDK具有优秀的音频捕获性能。它能够实现高质量的音频采集,并提供了多种采样率和位深度的选择,以满足不同场景下的需求。同时,它还支持多通道的音频采集,可以同时处理多个声道的音频数据。 其次,Asio音频采集SDK具有低延迟的特点。采集过程中的延迟对于实时音频处理至关重要,Asio音频采集SDK通过优化算法和硬件支持,实现了极低的音频采集延迟。这使得它非常适合需要实时处理音频数据的应用场景,如音频输入设备、语音识别等。 此外,Asio音频采集SDK还提供了丰富的开发接口和示例代码,方便开发者进行二次开发开发者可以根据自己的需求,调用SDK提供的接口进行音频采集设置、数据处理和输出等操作。这样,开发者可以根据具体应用需求进行个性化的定制和优化,实现更加强大的功能。 综上所述,Asio音频采集SDK是一种功能强大、低延迟的音频采集开发工具,它提供了高质量的音频采集性能、低延迟的音频处理能力以及丰富的开发接口和示例代码。在音频采集和实时处理领域具有广泛的应用前景。 ### 回答2: ASIO音频采集SDK是一种专门用于音频设备驱动音频数据采集的开发工具。它基于ASIO(Audio Stream Input/Output)技术,可用于音频设备的驱动程序开发,同时提供了高性能和低延迟的音频数据采集功能。 ASIO音频采集SDK具有以下特点和优势: 1. 低延迟:ASIO技术通过使用直接内存访问(DMA)技术来减少音频数据传输的延迟,提供了极低的输入和输出延迟,保证音频信号的实时性。 2. 高性能:ASIO音频采集SDK针对音频设备驱动程序的开发进行了优化,提供了有效的数据流控制和音频处理算法,使音频采集性能更加出色。 3. 稳定可靠:ASIO音频采集SDK经过了多次测试和优化,可确保在不同的音频设备上具有稳定可靠的性能表现,保证音频采集的准确性和一致性。 4. 良好的兼容性:ASIO音频采集SDK支持主流的操作系统,如Windows和Mac OS,同时还支持广泛的音频设备,包括插件式音频接口和外部USB音频设备等。 5. 灵活扩展:ASIO音频采集SDK提供了丰富的开发工具和API,使开发者可以根据自己的需求进行二次开发和扩展,实现更多个性化的音频采集功能。 总而言之,ASIO音频采集SDK是一种强大的工具,可用于开发音频设备驱动程序和实现高性能、低延迟的音频数据采集应用。它的特点包括低延迟、高性能、稳定可靠、兼容性好和灵活扩展等。 ### 回答3: Asio音频采集SDK是一种用于音频数据采集的软件开发工具包。它可以帮助开发人员实现音频采集功能,并提供了一些接口和方法,使开发过程更加简单和高效。 Asio音频采集SDK具有以下特点和优势: 1. 低延迟:Asio音频采集SDK采用了先进的音频处理技术,能够实现低延迟的音频数据采集和处理,提供更顺畅的音频体验。 2. 高质量:Asio音频采集SDK支持高质量的音频采集,能够捕捉细微的音频细节,并提供清晰、真实的音频数据。 3. 多平台支持:Asio音频采集SDK可以在多种操作系统上使用,包括Windows、Mac和Linux等,提供了更广泛的应用场景和兼容性。 4. 简单易用:Asio音频采集SDK提供了简洁、易懂的接口和方法,使开发人员可以快速上手并使用SDK进行音频采集开发,缩短开发周期。 5. 强大的扩展性:Asio音频采集SDK支持用户自定义的音频采集设置,开发人员可以根据具体需求进行扩展和定制,实现更丰富的音频采集功能。 总的来说,Asio音频采集SDK是一种功能强大、易于使用的音频采集开发工具包,能够帮助开发人员实现低延迟、高质量的音频采集,并在多个平台上提供兼容性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值