0

我目前正在做一个项目,其中一个主要元素是一个可以实时进行信号处理的软件。

基本思想/概念如下。音频数据从 (USB) 音频接口(以 96 kHz,24 位运行)连续捕获。然后通过“处理链”(如“虚拟效果链”)运行,其中每个元素都可以对数据执行任意处理。数据从一个元素传递到下一个元素,直到它到达接收器。流入接收器的数据被发送回音频接口进行输出。

信号链由实现某种“处理”方法的“对象”组成,该方法适用于 1920 个样本(20 毫秒)大小的数据块(尽管可能会更改此数字)。

“伪代码”中的基本思想。

while (true) {
   float[] samples = audio.read(BLOCK_SIZE);

   foreach (Effect e in signal_flow)
      e.process(samples);

   audio.write(samples);
}

第一个原型已经在 Python 中实现,它使用 ALSA API 进行音频 I/O。三个单独的线程被启动。

第一个是不断从 ALSA 读取数据(这是一个阻塞操作)并将数据放入队列中,然后继续读取下一个块。(ALSA 要求我至少每 20 毫秒“读取(...)”一次,所以我需要一个非常轻量级的线程,它只做读取和存储数据,所以我可以满足我的时间限制。)

第二个从这个队列的输入中读取样本,将它们通过信号链,最后进入另一个队列进行输出。

第三个线程从这个输出队列中读取(这是一个阻塞操作)并将这些块写到 ALSA。

我在这里基本上有两个“数字”变量,一个是块大小,另一个是我什至开始处理之前“预先发布”到输出队列的块数,这给了我一些时间来“提供更多样本” " 没有输出缓冲区欠载。此外,我可以决定在单独的线程或单独的进程中执行这些操作。

经过一些认真的调试(包括使用信号发生器和示波器!),我终于让这种方法运行起来了。最大的问题是 ALSA API 中的一个错误,即“period_size”,它被指定为“读/写的帧数”,看起来实际上是读取的帧数,但字节数书面。当我为输入和输出都指定 1920(96 kHz 采样率下 20 毫秒的音频)时,我只得到 1/4 的输出,然后是 3/4 的静音,然后是 1/4 的输出。当我为输入和输出都指定 1920 * 4 = 7680 时,我收到一个错误,即设备的缓冲区不够大,无法读取这么多帧。当我为输入指定 1920 并为输出指定 7680 时,它工作得很好(而且我的队列也不会“溢出”)。

解决了这个问题后,我还有一个严重的问题,那就是:延迟!

我基本上开始整个项目是为了创建自己的效果,所以我希望这个软件可以作为舞台/现场使用的效果单元。我已经读过,当两个事件相隔不到 30 毫秒时,例如,两个频闪灯相隔 30 毫秒发射,人脑不再能分辨出哪个是第一个,哪个是第二个。因此,30 毫秒或更短的延迟基本上会被视为“即时”。这是事件“合并”并被视为“一个”的阈值。如果延迟那么低,您将无法分辨哪个事件是第一个,例如,吉他手无法分辨是他敲击琴弦然后声音来自 PA,还是声音来自 PA 然后他敲击字符串,所以它基本上感觉就像“零延迟”,因此就像一个真正的放大器。

现在我目前得出的延迟是……嗯……我还没有机会精确测量它们,但它大约是十分之一秒,比如 200 或 300 毫秒。太过分了!我为 I/O 尝试了单独的线程和进程(因为在 Python 中,由于“全局解释器锁”,你永远不知道线程是否真的会更快)并且进程明显变慢,这是可以预料的,因为与进程间通信相关的开销以及两个 I/O 进程不执行任何“密集”操作的事实。他们只是等待阻塞操作并执行快速 I/O。

作为另一句话,我想说我希望我的应用程序托管一个 Web 服务器(当然在一个单独的线程甚至进程中),以允许用户配置信号路径并调整效果“设备”中的参数一种直观的方式。

所以很明显我需要降低延迟!我现在有几条路要走。

  1. 我的第一个想法是用JACK代替ALSA,据说是“低延迟”。缺点是我无法控制 ADC/DAC 的确切电平(JACK 总是使用浮点数,无论硬件做什么,浮点数都已经缩放到 [-1.0, 1.0])和过采样率(就我而言知道,JACK “规定”了一个采样率——我不能再选择了——使用 ALSA,我可以选择一个我想要使用的采样率,如果硬件偏离该采样率,它会透明地对我进行上采样/下采样),即阻塞我/O(我发现一个非常好的范例)被回调机制取代,而且时间限制可能更难满足。(我不能用 JACK 自己“缓冲”。它总是需要在一定时间内完成处理。)另外,一般来说,JACK API 似乎比 ALSA API 更复杂。我可能可以用 JACK 替换 ALSA,但我不确定我能“赢”多少,因为 ALSA 已经是一个非常“低级”的 API,而且 JACK 是在此基础上构建的,但可能以一种非常特定的方式使用来保持延迟低。老实说,我不确定到底有多少延迟是由于 ALSA 和“低于”以及有多少是由于“更高层”。

  2. 我目前使用一种解释的、垃圾收集的语言。这对实时要求不利。但是有什么替代方案?好吧,当考虑实时应用程序时,当然会想到 C,但它也有严重的缺点。

2.1 - 我希望软件具有基于 Web 的 UI。在 Python 中创建 Web 服务器很简单。Python 标准库中有一个类。用 C 语言创建 Web 服务器是一件痛苦的事情。C 中基本上没有任何东西可以做“web”。我必须从头开始,只基于套接字 API。祝你好运!:-(

2.2 - Python 支持数字。它具有 numpy 和 scipy,因此具有矢量数据类型、矩阵乘法、快速傅立叶变换、快速卷积、插值(样条等)。这对于手头的问题非常有用。在 C 中,我失去了所有这些,这意味着我要么必须在没有的情况下完成工作,这不太可能,要么我必须在 C 中自己重新创建所有这些,这既费时又出错-易于。

2.3 - Python 对多处理和多线程具有“高级”支持。我可以通过同步队列在进程和线程之间进行通信,以“线程池/复制的工作人员”风格的方式实现我的解决方案,这是解决手头问题的一个非常有用的范例。在 C 语言中,我将不得不求助于 POSIX 线程和低级同步原语,如 mutice。(“互斥体”的复数形式是正确的吗?就像在“Unix”->“Unices”中一样?)同样,这既费时又容易出错。

关于使用哪种语言有什么建议吗?我熟悉的语言包括 Python、C-Sharp、Java、C、Go、一点点C++(我从不需要那种语言——要么是低级的,然后我使用 C,要么是高级的,然后我使用垃圾收集、解释器或字节码编译的语言),以及一些 Web 技术(尤其是带有 AJAX 的 JavaScript 等,这在 UI 方面会很有用)。

所有这些都必须在 Linux 发行版下运行,最好使用尽可能少的依赖项/库。因此,“标准”库优于“外来”库。平台独立性,尤其是在 Windows 下运行的能力,是一个加分项,但不是严格的要求。

我同意 UI 以不同的语言实现(想到 Web 服务器)而不是实际处理(如果需要实时处理,可能“必须用 C 语言完成”?),不过这带来了很多额外的复杂性,因为两者都将在单独的进程中运行并通过例如 Unix 域套接字进行通信,这需要所有数据来回传递以表示为字节流(因为这都是套接字传输),因此需要设计一种进程间通信协议,并在这一层进行大量解析。有没有机会避免这种巨大的开销并用一种语言来完成?

还有其他想法吗?具体来说,从 ALSA 到 JACK 我会有所收获吗?还有其他我还没有考虑过的替代方案,可能更适合这项任务吗?

非常感谢您的时间!

4

0 回答 0