嵌入式音频处理基础(3)
把数据送入处理器内核有若干种方法。例如,一个前台程序能对一个串行端口中的新数据来进行查询,但这种传输方式在嵌入式媒体处理器中是不常用的,因为这样会降低内核的使用效率。
取而代之的是,与音频编解码器相连的处理器一般用DMA引擎把数据从编解码器的数据口(就像一个串行口)传输到处理器可用的某个存储空间内。这种数据传输是以后台操作的形式完成的,无需处理器内核的干预。这里的唯一开销是对DMA序列的设定以及一旦数据缓冲区的接收或发送完成之后对中断的处理。
样点处理和块处理是处理数字音频数据的两种方法。在样点处理的方法中,只要样点一出现,处理器就处理这个样点。这里,在每个采样周期中的处理操作都会有开销。许多滤波器(例如FIR和IIR,将在下面叙述)是以这样的方式实现的,因为这样的形式的有效延迟会很低。
另一方面,块处理是基于把数据传送到处理函数之前对特定长度缓冲区的填充。有些滤波器是用块处理的方式实现的,因为这样比样点解决方法更有效。其中要说明的一点是,块处理方法大幅度的降低了针对每个样点而调用处理函数的开销。而且,许多嵌入式处理器包含有多个ALU, 可以对数据块进行并行操作。另外,有些算法从本质上就是以块解决方法操作的。其中一个大家都知道的是傅里叶变换(以及它的实际使用的形式,快速傅里叶变换,或称FFT),这种算法接受时域数据块或空间域(spatial)数据块,然后把这一些数据块转换成频域表示。
在基于块处理的、使用DMA与处理器内核进行数据传递的系统中,一定要使用双缓冲,以便在DMA传输和内核之间进行仲裁。这会使处理器内核和独立于内核的DMA引擎不会在同一时间对同一数据来进行访问,避免了数据一致性问题。为了对长度为N的缓冲区的处理进行改进,我们简单地产生一个长度为2×N的缓冲区。对于一个双向系统,必须生成两个长度为2×N的缓冲区。如图1a中所示,处理器内核正在对in1缓冲区做处理,并将结果存储在out1缓冲区中,而DMA引擎此时正在对in0进行填充,并对out0中的数据来进行传输。图1b指出,一旦DMA引擎完成对双缓冲区左边半个的操作之后,它就开始把数据传送到in1,并从out1取出数据,而此时的处理器内核正在处理来自in0的数据,并填入out0。这个结构有时被称为“乒乓式缓冲”,因为处理器内核来回地对双缓冲区的左右两半进行处理。
应该注意到,在实时系统中,串行端口的DMA(或者另一个与音频采样率关联的外围设备的DMA)规定了时序预算。基于这个原因,块处理算法必须以这样的方式来进行优化,即它的执行时间要小于或等于DMA对双缓冲区的一半进行数据传输所需的时间。
当数据通过像I2S这样的数据链路传输时,它可能会包含多个声道。这些声道可以全是从一条数据线上通过复用而输入到同一个串行端口的。在这种情况下,2D DMA可拿来对数据来进行解交织,从而使每个声道在存储器中是线性分配的。可以看一下图2中对这一安排的图示,其中从左右声道来的样点被解复用到两个分离的数据块。这个自动数据安排对那些使用块处理的系统是极其有用的。
图2 2D DMA引擎用于解交织 (a) I2S立体声数据输入 (b)分离的左右缓冲区
在音频处理中有三个基本的构建模块。它们是加法操作、乘法操作和时间延迟。许多更复杂的效果和算法可以用这三个基本操作来实现。加法器显而易见的任务是把两个信号加在一起。乘法能够适用于提升或衰减音频信号。在大多数媒体处理器中,可以在一个周期内完成多次加法和乘法操作。
时间延迟有点复杂。在许多音频算法中,当前的输出取决于过去的输入和输出之间的组合。这种延迟效果是用延迟线实现的,而延迟线仅仅是存储器中用来保持过去数据的一个数组。例如,一个回声算法可以对每个声道保持500 mS的输入样点。当前输出值可以用当前输入值与稍微衰减的过去样点进行相加后得到。如果音频系统是基于样点的解决方法,那么程序设计人能简单地跟踪一个输入指针和一个输出指针(两者之间保持500 mS样点数的间隔),并且在每个采样周期之后增加这两个指针。
由于延迟线要被随后的各组数据重复使用,因此,输入与输出指针将需要从延迟线缓冲区的末尾回绕到起始端。在C/C++中,这通常是在指针增加操作时再附带一次求模操作(%)完成的。
对于那些支持循环缓冲(见图3)的处理器来说,这个回绕操作不会增加额外的处理周期。在这种情况下,一个循环缓冲区的起始位置和长度必须只提供一次。在处理过程中,软件增加或减少缓冲区内的当前指针,如果当前的指针位置落在缓冲区的两个端点之外,则由硬件使指针回绕到缓冲区的起始位置。假如没有这个自动地址生成功能,程序设计人员就必须手动地保持对缓冲区的跟踪,因而会浪费有用的处理周期。
图3 (a) 使用循环缓冲区的延迟线的图示 (b) 循环缓冲区在存储器中的布局
由延迟线结构可以引出一个叫做梳状滤波器的重要的音频构建模块,它本质上是一个带有反馈的延迟线。当多个梳状滤波器同时使用的时候,可以产生混响的效果。
在有些音频系统中,也许需要合成一个信号(例如一个正弦波)。泰勒级数的函数近似法可拿来对三角函数进行仿真。而且,用均匀随机数发生器来产生白噪声是很容易的。
但是,合成的方法也许并不适用于某些给定系统的处理预算。在具有充足存储器的定点系统中,您可以取而代之地使用查表的方法来产生信号。这样做的负面效应是占用了宝贵的存储器资源,所以,作为一种折衷考虑,能够正常的使用混合的方法。例如,您可以存储一个不太精细的函数表,以节省存储器。在运行时,准确的值可以用插值的方法从函数表中提取出来,而插值操作比使用泰勒级数近似法的时间大为缩短。这个混合法提供了在计算时间和存储器资源之间的很好的平衡。
音频系统中的数字滤波器被用来对指定频带内的声波能量进行衰减或提升。最常用的滤波器形式是高通、低通、带通和点阻。这些滤波器中的任何一种都有两种实现方法。这就是有限冲击响应(FIR)滤波器和无限冲击响应(IIR)滤波器,而且它们组成了搭建像参数均衡器和图示均衡器那样更复杂的滤波算法的构建模块。
FIR滤波器的输出是由当前和过去输入之和确定的,而其中的每个输入样点首先要乘以一个滤波器系数。示于图4a中的FIR求和公式,也叫做“卷积”,是信号处理中最重要的操作之一。在这个公式的句法中,x为输入向量,y为输出向量,而h为滤波器系数。图4a表示了FIR的实现结构图。
卷积是在媒体处理中非常常用的操作,因而许多处理器都可以在一个周期内完成一条乘累加(MAC)指令,同时还能够实现多个数据的访问操作(读或写)。
与输出仅仅取决于输入的FIR滤波器不同,IIR滤波器则依靠输入和过去的输出。IIR滤波器的基本公式是一个差分方程,如图4b所示。由于当前输出对于过去输出的依从关系,IIR滤波器经常被称为“递归式滤波器”。图4b也给出了IIR滤波器结构的图示。
我们往往可以更好地描述音频信号的特性,那就是用频率组成。傅里叶变换以时域信号作为输入,并把信号重新安排到频域里,而傅里叶反变换则完成逆向的工作,把频域表示变换回时域。从数学上看,时域中的操作与频域中的操作之间有一些很妙的特性关系。特别是,时域卷积(或者FIR滤波器)等效于频域的相乘。假如没有傅里叶变换这个特别的优化方法,即快速傅里叶变换(FFT),那么这个信号处理中的珍品就不可能变为实用。事实上,FIR滤波器往往有更高效的实现方法,那就是把输入信号和滤波器系数用FFT变换到频域,然后将两个变换式相乘,最后再用傅里叶反变换把乘积变换回时域。
在音频处理中还常常使用其他一些变换。这中间还包括最常用的修正离散余弦变换(MDCT),这是许多音频压缩算法的基础。
有时您会需要把信号的采样率转换到另一个不同的采样率。用到变采样率的一个情况是,您需要对一个以8 kHz采样的音频信号进行解码,但是您使用的DAC并不支持这个采样率。另一个情况是,当一个信号被过采样之后,需要转换成一个较低的频率,因而可以缩短计算时间。把信号从一个采样率变换到另一个采样率的过程叫做变采样率(或SRC)。
提高采样率的方法叫做插值,而降低采样率的方法叫做抽取。对信号进行M倍抽取是通过只保留每个M样点而抛弃所有其余的样点来完成的。对信号进行L倍插值是通过对原来信号的每两个样点之间补充L-1个零来完成的。
虽然插值和抽取的因子都是整数,但您可以对输入信号接连使用插值和抽取而得到一个有理数的变换因子。当您用5倍进行上采样,然后用3倍作下采样,那么最后的因子为5/3 = 1.67。
老实说,我们有点过分地简化了SRC的过程。为了尽最大可能避免对信号补零(补零会引起频域的镜像成分)而引起的人工痕迹,值的信号在当作输出或当作抽取器的输入而被使用之前,一定要经过低通滤波。利用一个知晓与L-1个插值样点相对应的输入都是零值的特定FIR滤波器结构,这个抗镜像低通滤波器可以在输入采样率下进行,而不必在较快速率的输出采样率下进行。
同样,在被抽取之前,不符合Nyquest采样定律的输入信号必须被低通滤波,以避免混叠。使用知晓输出样点中那些被抛弃的样点是不有必要进行滤波计算的FIR滤波器结构,这个抗混叠低通滤波器可以设计成以抽取后采样率做相关操作,而不必在较快速的输入采样率下操作。图5表示了变采样率的流程图。需要注意到,抗镜像滤波器和抗混叠滤波器是可以合二为一的,以便节省计算量。
显然,对这些嵌入式音频的论题,我们仅能够给出一个很表层的讨论,但对于嵌入式音频处理应用的开发,我们仍旧是希望已经提供了一个有用的框架,并提到了其中各种必需考虑的问题。