65.9K
CodeProject 正在变化。 阅读更多。
Home

在 .NET 中使用 ASIO 驱动程序实现低延迟音频

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (29投票s)

2008年3月21日

CPOL

10分钟阅读

viewsIcon

595656

downloadIcon

10466

演示如何通过 ASIO 驱动程序访问您的声卡硬件

Asio

引言

本文演示了一种在 .NET 中与支持 Steinberg ASIO 驱动程序的音频设备进行通信的方法。这允许与声卡进行底层和低延迟通信,对于那些有兴趣开发音频应用程序——软件合成器、录音应用程序、效果器等的人来说,可能会很有用。请注意,提供的源代码不完整;您需要从 Steinberg 下载 ASIO SDK 才能构建和运行它。许可限制使我无法在此处分发它。

因为这个库本身有点无聊,所以我包含了一个小的控制台应用程序来演示它的用法——口齿不清的傻瓜应用程序。有了这个、一个麦克风和一副耳机,您应该能够以最小的努力将自己变成一个低延迟、口齿不清的傻瓜。有点像一桶比利时啤酒,但没有宿醉。

背景

作为一个书呆子,我目前正在重新审视一个我多年来零星进行的项目。它是一个音乐合成器。多年前,我购买了许多录音室级键盘和机架设备,希望能好好利用它们来制作音乐,但事实证明,我是一个糟糕的音乐家。尽管如此,我仍然喜欢这些机器以及它们能产生丰富音色的声音。

在那些日子里,这些设备都内置了专用的 DSP 芯片来实现其效果,但多年来,计算机遵循了摩尔定律,并且已经从硬件声音模块普遍转向了软件对应物。特别是,现在有许多商业 VST 插件可用,它们是功能齐全的合成器和采样器,可与音序器集成。

制作符合 VST 标准的产品不是我的任务;也许,如果我追求商业成功,我会走这条路。相反,我有兴趣制作一个独立的合成器,并使用 .NET 来完成,对我来说,.NET 是目前可用的最佳软件开发环境。

DirectX vs. ASIO

如果您想开发一个音频应用程序,您首先需要的是一种将声音输入和输出应用程序的方式——您的声卡。有各种方法可以做到这一点,在我的初版中,我使用了 DirectSound。这完美地运行——在一定程度上。

音频流只是一串数字或“样本”,它们代表声压。每秒使用的样本越多,“采样率”越高,音质越好,尤其是在声音的更高频率分量中。CD 以 44.1KHz 采样,DVD 以 48KHz 采样。因此,如果我们需要 CD 质量的音频,我们需要准备每秒对声卡进行 44100 次读写。对于立体声,我们需要进行两次。

实际上,不可能每秒中断处理器那么多次,因此使用了缓冲系统;这有助于将工作量降到最低,并有助于从磁盘或网络读取时的 IO 等事情。一个更合理的方法是每秒中断处理器 100 次,并让它每次吐出 441 个样本的缓冲区。缓冲区越大,效率越高,但延迟也越大。使用 441 个样本的缓冲区,您可以获得最小 1/100 秒的延迟,最大 1/50 秒的延迟。这是在键盘上敲击音符后可能听到音符的最短时间。

在决定使用多大的缓冲区时,总会有折衷——大缓冲区会带来出色的性能,但也会有延迟。小缓冲区会消耗性能,但会最大程度地减少延迟。给声音增加 1/100 秒的延迟也会明显改变它,就好像你在一个大房间里一样。

这就是 DirectX 的问题所在,它使用的缓冲区大小太大,无法制作出有效的实时音频程序,这就是为什么音频软件巨头 Steinberg 着手创建新的低延迟音频驱动程序规范的原因。他们创建了一个名为 ASIO 的标准,旨在通过使用最小化延迟的小缓冲区来提供对音频硬件的底层访问。如果您正在开发一个严肃的实时音频应用程序,您将需要 ASIO。

我的声卡不支持 ASIO!

如果你像我一样讲究,你可能有一张提供 ASIO 支持的讲究声卡。如果不是这种情况,请不要担心,Michael Tippach 已经前来救援。他开发了一个 ASIO 驱动程序,似乎可以与市面上几乎所有声卡一起工作。我不知道他是怎么做到的,但他显然是个聪明人。你可以从他的网站这里下载它。

ASIO 规范

ASIO 是免费的,差不多吧。Steinberg 很好心,发布了标准,任何人都可以自由使用。他们不喜欢你分发它,或者在没有承认他们的情况下将其用于商业用途,这就是为什么你需要直接从他们那里下载 SDK,而不是我分发我们需要的那些文件。更多信息请见下文。

ASIO 驱动程序不是您可能习惯的驱动程序,例如,一些位于操作系统底部的内核模式的糟糕二进制文件。ASIO 驱动程序是一个与您的声卡通信的 COM 对象。它是如何做到这一点的因卡而异,我不知道确切的机制。

理论上,要在 .NET 中开始产生一些酷炫的声音,COM Interop 应该能解决问题——实例化 COM 对象,并通过其接口调用方法。不幸的是,事实证明,事情并非那么简单。Steinberg 在其标准的 Windows 实现中做了一些**有趣的选择**,其中最大的一个就是对象的 CLSID 和其主接口的 IID 总是相同的。

这意味着在了解对象之前我们不知道 IID,所以期望我们提前知道 IID 的简单 COM 互操作将不起作用。我们需要一点混合托管/非托管 C++ 来完成此操作。

工作原理

我不会深入讨论太多细节,因为这很无聊。如果你想知道它到底是如何工作的,可以看看代码,但总的来说……

首先要做的是决定使用哪个 ASIO 驱动程序,如果您的系统上安装了多个驱动程序。驱动程序通过向注册表 (HKEY_LOCAL_MACHINE\SOFTWARE\ASIO) 添加条目来使其自身为人所知。每个驱动程序在此处注册其名称和 COM 对象的 CLSID。我们需要遍历此键以获取每个驱动程序。

一旦我们决定使用哪个驱动程序,我们就会实例化它 (CoCreateInstance) 并获取其接口的指针;然后我们可以开始调用方法,并注册一堆回调以响应事件。我们询问它有多少输入和输出通道。

然后,我们为每个通道创建一个托管的 `Channel` 对象,每个对象都包含一个包装非托管缓冲区的索引器。使用了双缓冲系统,因此在一个缓冲区播放或捕获时,我们更新另一个缓冲区。

ASIO 支持各种不同的采样格式,但此实现只使用一种,即 32 位带符号整数。我不太愿意将其暴露给我们的调用应用程序,所以我们改为暴露允许范围为 -1.0 到 1.0 的浮点数。当我们读取或写入 ASIO 缓冲区时,我们进行来回转换。

当我们要求驱动程序启动时,它将开始播放输出通道并捕获输入通道,每次需要更新缓冲区时都会触发回调。在我们的程序集中,我们通过切换更新缓冲区(双缓冲)并触发一个托管事件来处理此回调,调用应用程序应处理该事件以更新缓冲区。

继续

可能比它如何工作更有趣的是如何使用它,所以我将在这里介绍。

要构建此项目,您需要下载 ASIO SDK。您可以在这里获取它。(我希望如此。该 URL 看起来有点动态,因此如果它不起作用,请进行 Google 搜索。)正如我已经说过的那样,由于许可原因,我无法分发它。您从 SDK 中唯一需要的是包含大量定义和其他内容的 Asio.h 头文件。将其复制到项目中,您应该能够进行构建。

演示应用程序展示了如何遍历可用驱动程序并选择一个驱动程序。首先,选择一个驱动程序

AsioDriver driver = AsioDriver.SelectDriver(
           AsioDriver.InstalledDrivers[driverNumber - 1]);

这可能看起来很奇怪,因为您只需从 `InstalledDrivers` 数组中选择一个驱动程序即可。但是,一次只能激活一个 ASIO 驱动程序,您需要选择它才能使其生效。

接下来,创建托管缓冲区

driver.CreateBuffers(true);

添加一个事件处理程序来更新缓冲区

driver.BufferUpdate += new EventHandler(AsioDriver_BufferUpdate);

将您的代码放入事件处理程序以操作缓冲区,然后调用

driver.Start();

我们开始工作了。

演示应用程序

现在,讲讲愚蠢的部分。测试我们组装件的简单方法是将输入反馈回输出,并稍作延迟,以便我们能够区分播放的内容和输入的内容。前段时间,我正在和我的女朋友 Skype 通话,她当时在澳大利亚工作,线路上有延迟。我听到了自己的回声。奇怪的是,我几乎说不出话来——听到自己带有延迟的声音让我的大脑困惑。演示应用程序具有完全相同的效果。我找了一些志愿者尝试在佩戴运行该应用程序的 USB 耳机时阅读一段文本,令我非常开心的是,他们都变成了语无伦次的结巴傻瓜。我很高兴地报告,当你摘下耳机时,这种情况通常就会停止。

下一阶段

如果能写一篇关于软件合成器的完整文章会很酷,但不幸的是,这个主题太大了,无法容纳在一篇文章中。希望我能很快完成并在此处更好地利用该组件。

还有三件事……

此应用程序仅针对驱动程序 ASIO4ALL 进行了测试。如果您正在使用不同的驱动程序并且它不起作用,可能是驱动程序正在使用不同的底层采样格式。如果您遇到问题,请给我留言。

该代码是使用 Visual Studio 2008 构建的。这里使用了几个通用列表,它们将代码基准定位在 .NET 2.0,但如果您仍在使用 .NET 1.1,您可以轻松修改这些。如果您没有使用 Visual Studio 2008,您可能需要创建一个新的解决方案并手动添加文件 - 我怀疑解决方案文件会向后兼容。

ASIO 是 Steinberg Media Technologies GmbH 的商标和软件。

历史

  • 2008 年 3 月 21 日 - 初始版本
  • 2008 年 4 月 17 日 - 各种错误修复和改进
  • 我已更新源代码,其中包含各种改进和修复。有些人发现该组件在某些卡上无法正常工作,并且发现了一些普遍的疏忽,并帮助追踪和修复了这些问题。此次更新几乎完全是 Sieds Tilstra 的工作,因此非常感谢他。增强功能包括

    • 使用 `CoInitialize` 初始化 COM,我忘记了这一点,将永远为此感到羞耻
    • 移除托管缓冲区,索引器现在直接从非托管缓冲区写入和读取
    • 将采样范围调整为应有的值:-1.0 到 1.0。以前它总是正值
    • 对样本进行范围检查以避免不愉快的咔哒声
    • 取消选择驱动程序的功能。以前无法做到这一点
© . All rights reserved.