一个数字音频系统很容易拆分为多个线程,包括一个网络协议栈线程、一个时钟恢复线程、一个音频传输线程,以及可选的用于 DSP、设备升级和驱动程序认证的线程。网络协议栈可以像以太网/IP 栈一样复杂并包含多个并发任务,或者像 S/PDIF 接收器一样简单。
我们假设系统中的线程通过通道发送数据样本进行通信。在这种设计方法中,线程是在单核还是多核系统上执行并不重要,因为多核只是为设计增加了可扩展性。我们假设每个线程的计算要求可以静态建立并且不依赖于数据,这通常是未压缩音频的情况。
我们将把注意力集中在设计的两个部分:线程之间的缓冲(以及它们对性能的影响)和时钟恢复。一旦做出了这些设计决策,实现每个线程的内部就遵循正常的软件工程原则,并且与人们预期的一样难或容易。缓冲和时钟恢复很有趣,因为它们都对用户体验有定性影响(促进稳定的低延迟音频),并且在多线程编程环境中很容易理解。
缓冲
在数字解决方案中,数据样本不一定在交付时进行传输。这需要缓冲数字音频。例如,考虑一个采样率为 48 kHz 的 USB 2.0 扬声器。USB 层将在每 125 µs 窗口中传输六个样本的突发。无法保证在 125 µs 的窗口中将传送六个样本,因此需要至少 12 个样本的缓冲区,以保证样本可以实时流式传输到扬声器。
设计挑战是建立适量的缓冲。在模拟系统中,缓冲不是问题。信号按时传递。在基于非实时操作系统设计的数字系统中,程序员通常坚持使用相当大的缓冲区(250 或 1,000 个样本)以应对调度策略中的不确定性。然而,大缓冲区在内存方面、增加延迟方面以及证明它们足够大以保证无点击交付方面都是昂贵的。
多线程设计提供了一个很好的框架来非正式地和正式地推理缓冲并避免不必要的大缓冲区。例如,考虑上述 USB 扬声器增加了环境噪声校正系统。该系统将包括以下线程:
通过网络接收 USB 样本的线程。
过滤样本流的一系列 10 个或更多线程,每个线程都有一组不同的系数。
使用 I 2 S将过滤后的输出样本传送到立体声编解码器的线程。
从连接到麦克风采样环境噪声的编解码器中读取样本的线程。
将环境噪声二次采样到 8 kHz 采样率的线程。
建立环境噪声频谱特性的线程。
根据计算的光谱特性更改滤波器系数的线程。
所有线程都将在 48 kHz 基本周期的某个倍数上运行。例如,每个过滤线程将每 48 kHz 周期过滤一个样本;交付线程将在每个周期交付一个样本。每个线程也有一个定义的窗口,它在上面操作,以及一个定义的方法,通过这个方法推进这个窗口。例如,如果我们的过滤器线程是使用双二阶实现的,它将在一个包含三个样本的窗口上运行,每个周期提前一个样本。频谱线程可以在每 64 个样本推进 64 个样本的 256 个样本窗口(以执行 FFT(Fest Fourier Transform))上运行。
现在可以建立在同一周期运行的系统的所有部分,并将它们以同步部分的形式连接在一起。在这些同步部分内不需要缓冲区,尽管如果线程要在管道中运行,则需要单个缓冲区。在各个同步部分之间需要缓冲区。在我们的示例中,我们最终得到三个部分:
从 USB 接收样本、过滤并以 48 kHz 传输的部分。
以 48 kHz 采样环境噪声并以 8 kHz 传输的部分。
建立频谱特性并在 125 Hz 时更改滤波器设置的部分。
这三个部分如图 3 所示。从 USB 缓冲区接收样本的第一部分需要缓冲 12 个立体声样本。
图 3:根据频率分组在一起的线程。
传递的部分需要缓冲一个立体声样本。将 10 个过滤器线程作为管道运行需要 11 个缓冲区。这意味着从接收器到编解码器的总延迟包括 24 个采样时间,即 500 µs,并且可以添加一个额外的采样以应对时钟恢复算法中的中期抖动。这部分以 48 kHz 运行。
对环境噪声进行采样的第二部分需要在输入端存储一个样本,并在二次采样中存储六个样本。因此,在 48 kHz 或 145 µs 处有 7 个样本延迟。
建立频谱特性的第三部分需要以 8 kHz 的采样率存储 256 个样本。不需要其他缓冲区。因此,环境噪声和滤波器校正之间的延迟为 8 kHz 下的 256 个样本,二次采样时间为 145 µs,或刚好超过 32 ms。请注意,这些是我们选择使用的算法的最小缓冲区大小;如果此延迟不可接受,则必须选择不同的算法。
设计线程以对数据块而不是单个样本进行操作通常很容易,但这会增加所经历的整体延迟,增加内存需求并增加复杂性。仅当有明显的好处时才应考虑这一点,例如增加吞吐量。
计时数字音频
数字音频和模拟音频之间的一个很大区别在于,模拟音频基于此基础采样率,而数字音频需要将时钟信号分配给系统的所有部分。尽管组件都可以使用不同的采样率(例如,系统的某些部分可能使用 48 kHz,而其他一些部分可能使用 96 kHz,中间有一个采样率转换器),所有组件都应就一秒的长度达成一致,并且因此在测量频率的基础上达成一致。
数字音频的一个有趣特性是系统内的所有线程都与这个时钟频率的基数无关,假设有一个黄金标准的基频。系统中的多个核心是否使用不同的晶体并不重要,只要它们对样本进行操作即可。然而,在系统的边缘,真正的时钟频率很重要,采样在途中产生的延迟也很重要。
在多线程环境中,将留出一个线程来明确测量真实时钟频率,实施时钟恢复算法,测量本地时钟与全局时钟,并在时钟偏移上与主时钟达成一致。
可以使用互连的底层比特率隐含地测量时钟,例如 S/PDIF 或 ADAT。测量其中任何一个网络上的每秒比特数将给出主时钟的测量值。时钟可以通过使用为此目的而设计的协议来明确测量,例如以太网上的 PTP。
在时钟恢复线程中,可以实现一个控制循环,它估计时钟频率,并根据观察到的误差进行调整。在最简单的形式中,误差用作调整频率的指标,但滤波器可用于减少抖动。该软件线程实现了传统上由 PLL 但在软件中执行的功能,因此它可以廉价地适应环境。
结论
多线程开发方法使数字音频系统能够使用分而治之的方法进行开发,其中一个问题被分成一组并发任务,每个任务在多线程内核上的单独线程中执行。
像许多实时系统一样,数字音频适合多线程设计方法,因为数字音频系统显然由一组处理数据的任务组成,并且还需要这些任务同时执