870920 Menu

JUCE音频之基—AudioSampleBuffer类

音频功能是JUCE的立库之本,也是其最核心的部分。其中,核心中的核心,非AudioSampleBuffer类莫属。该类是一个临时存放音频采样的缓冲区,音频数据的进出读写、操纵处理皆在此区中完成。它的本质是一个二维数组,float**类型。换个角度理解,缓冲区作为一种临时性的数据容器,相当于一个不断刷新、先进先出的“队列”。

AudioSampleBuffer是一块固定大小的内存区,其容量由创建时所指定:保存几个通道的数据,每个通道有多少个采样。根据这两者,本类内部计算出所需的内存大小。实际上,每个通道的数据保存在一个数组中,该数组作为总数组的一个元素。即:总数组相当于一个二维数组(数组的数组),其类型为float**(JUCE音频以32位float为最基本的数据类型和计算单位)。总数组的元素个数(第一维)即本缓冲所保存的音频通道数,比如:单声道、立体声、5.1通道等等。总数组中每个子数组的元素(第二维)即为音频数据的采样值,子数组的元素个数则是音频数据中某个通道的采样数。每个采样均为float数值。创建本类对象后,可通过setSize()扩容或缩减,该函数在扩容的同时还可增减音频通道数。

由上可确认:AudioSampleBuffer就是一个float二维数组,该数组所占用的内存空间,用来接纳一批音频采样(float数据)。而这些数据位于总数组的各个子数组中。每个子数组则是一条音频通道。

采用原始数组,一是最大限度发挥C++语言在性能方面的优势,保证执行的效率;二是保证内存空间的连续性(便于寻址操作和指针的算术运算)。每个float音频采样的标准范围为-1.0到1.0(其上下限有可能超出。保险起见或为了省事,也可在音频处理“链”的某个环节做“截断”处理)。

AudioSampleBuffer拥有以下属性:
 通道数量(本缓冲区包含了几个通道的数据),int类型,构造时指定。
 每个通道的采样数量(每个通道有多少个采样),int类型,构造时指定。
 每个通道的采样数据(这些数据保存在总数组中,使用时,该总数组定义为指向指针的指针,亦即指向数组的指针,也可以说是数组之数组的首地址。其数据类型为float**)
 该缓冲区的总容量(所分配的内存空间的总字节数),size_t类型(即unsigned int)。

为防止float在不同平台下的字节数不同,本类内部所有的内存分配,只要涉及到float的字节数,一律用sizeof()进行计算,而不是假设为4个字节。这一点,在进行跨平台音视频软件开发时,务必要谨记。永远不要做想当然的假定。与其个人费劲假定,不如交给编译器去“定”。

对缓冲中每个音频采样进行算术运算,是计算机处理数字音频的最核心、最基础的处理方式。所有简单或复杂的数字音频处理,全部建立在对缓冲中的音频采样进行算术运算的基础之上。下面的代码演示了增益处理(调整音量)。其算法思路为:

channels的数据类型为float**,即指向指针的指针。或者干脆将channels理解为:这就是一个数组,该数组中的每个元素又是一个数组,“二级”数组中保存的元素,则是一个个的float值,每一个float值代表一个音频采样。channels[0]代表该数组中的第一个元素,即第一个数组,亦即保存了第一条音频通道中所有音频采样的数组。将该数组的首地址,即该数组的第一个元素的内存地址赋值给指针d。对d进行算术运算,指针移动,指向数组中的下一个float元素(音频采样)。以该数组的元素个数为上限,遍历,使指针d不停移动,不断指向下一个元素。每次循环,指针d解引用就得到当前所指向的元素的float值。对该值进行计算和处理,即可完成各种各样的音频处理。比如:增益、延时、叠加、相减等等。本例为渐变式增益,即:对每个采样进行不同数值的增益,使“整体音量”呈线性变化,而非统一调整。比如:淡入效果。稍加变通,即可改写为淡出效果,或者由线性渐变改为对数式渐变。

另一例常用的音频缓冲操作,将内存中的缓冲数据复制到当前缓冲:

AudioSampleBuffer的构造函数:
 AudioSampleBuffer (通道数, 每个通道的采样数)
1参为内存数据,即数组的数组,该数组的每个元素为一个子数组,一个子数组保存一个通道的采样数据
 AudioSampleBuffer (1参, 通道数, 每个通道的采样数)
 AudioSampleBuffer (1参, 通道数, 从1参的此处开始读取, 每个通道的采样数)
 拷构和=运算符(两者均指向同一块内存数据,并非深度复制。赋值后,改变当前缓冲的大小)

AudioSampleBuffer类的成员函数及使用详解:
(网站发布,略)