Ape
数字音频
声音仅仅是一个波形,而数字音频则是该波形的数字表示。 通过每秒多次对模拟信号的大小进行“采样”来实现数字表示。 从概念上讲,这可以看作是每秒多次记录声波的“高度”。 如今的音频 CD 每秒可存储 44,100 个样本。 由于 CD 是立体声的,因此它们每秒存储左右声道各 44,100 次。 这些值由 16 位整数表示。 基本上,WAV文件是一个标头,其后是R,L,R,L…的数组。 由于每个样本占用 32 位(左侧为16,右侧为16),并且每秒有 44,100 个样本,因此一秒钟的音频占用 1,411,200 位,即 176,400 字节。
无损压缩
无损压缩可以分解为几个基本步骤。 它们在下面详细说明。
1.转换为 X,Y
无损压缩的第一步是更有效地将通道 L 和 R 建模为某些 X 和 Y 值。 L 和 R 通道之间通常存在大量的相关性,可以通过多种方式加以利用,其中一种流行的方式是通过使用中间/侧面编码。 在这种情况下,对中间(X)和侧面(Y)值进行编码,而不是对 L 和 R 值进行编码。 中点(X)是左声道和右声道之间的中点,而侧面(Y)是声道之间的差异。 这可以实现: X =(L + R)/ 2 Y =(L - R)
R = Y / 2 - X L = R + Y
??由于采用整数的时候进行除法减法运算回导致越界以及精度损失,怎么处理呢?
2.预测器
接下来,X 和 Y 数据将通过预测器传递,以尝试删除任何冗余。 基本上,此阶段的目标是使 X 和 Y 数组包含尽可能小的值,同时仍保持可解压缩的状态。 该阶段将一种压缩方案与另一种压缩方案分开。 几乎有无数种方法可以做到这一点。 这是使用简单线性代数的示例: PX和PY是预测的X和Y; X[-1]是上一个X值; X[-2]是往前第二个X值 PX =(2 * X[-1])-X[-2] PY =(2 * Y[-1])-Y[-2]
例如,如果 X =(2,8,24,? ); PX =(2 * X[-1])-X[-2] =(2 * 24)-8 = 40
然后,将这些预测值与实际值进行比较,然后将差(错误)发送到下一级进行编码 。
大多数好的预测变量都是自适应的,因此它们可以调整以适应当前数据的“可预测性”。 例如,让我们使用范围为0到1024的因子“ m”(0为无预测,而1024为完全预测)。 每次预测后,根据预测是否有用,将m调高或调低。 因此,在前面的示例中,剩下的预测变量是: X =(2,8,24,?) PX =(2 * X[-1])-X[-2] =(2 * 24)-8 = 40
如果 ? = 45且m = 512 [Final Value] =? -(PX * m / 1024)= 45-(40 * m / 1024)= 45-(40 * 512/1024)= 45-20 = 25 ??调整预测因子的话,预测因子是否需要保存呢?保存的话占用的空间怎么算呢?? 此后将向上调整m,因为较高的m本来会更有效 。
使用不同的预测方程式并使用多次遍历预测器可以在压缩级别上产生相当大的差异。 这是一些预测方程的快速列表,如Shorten技术文档中所示(针对不同的订单): P0 = 0 P1 = X[-1] P2 =(2 * X[-1])-X[-2] P3 =(3 * X[-1])-(3 * X[-2])+ X[-3]
3.数据编码
音频压缩的目的是通过消除数字之间的任何相关性,使所有数字尽可能小。 一旦实现,必须将结果编号写入磁盘。 为什么较小的数字更好? 它们更好,因为它们需要更少的位来表示。 例如,假设我们要对数字数组(32位长)进行编码: Base 10:10、14、15、46 或二进制 Base 2:1010、1110、1111、101110 现在,如果要表示这些数字,显然 在尽可能少的位中,将它们分别表示为每个32位的独立long的效率非常低。 这将需要128位,并且仅通过查看以底数二表示的相同数字,就可以明显看出必须有一种更好的方法。 理想的情况是使用最少的必要位数将四个数字相加,因此没有逗号的1010、1110、1111、101110将是101011101111101110。这里的问题是我们不知道一个数字从哪里开始,下一个数字从哪里开始 开始。 为了存储较小的数字,使它们占用较少的比特,但仍可以进行解压缩,我们使用“熵编码”。 以下是有关称为莱斯编码的常见熵编码系统的详细信息。 Monkey's Audio 使用稍微更高级的熵编码器,但是莱斯编码的基本原理仍然有用。
熵编码 Rice编码 https://en.wikipedia.org/wiki/Golomb_coding Rice编码是一种使用较少的比特表示较小数字的方法,同时仍保持了从下一个数字中分辨出一个数字的能力。 基本上它是这样的: 您可以对一个数字进行多少猜测,然后将其称为k。 取数字中最右边的k位,并记住它们是什么。 想象一下没有那些最右边的k位的二进制数,然后看一下它的新值(这是不适合k位的溢出),请使用这些值对数字进行编码。 该编码值表示为与步骤3对应的零,然后是一个终止符1,表示您已经完成“溢出”的发送,然后是步骤2中的k位。
示例 让我们来看一下示例,并尝试对10、14、15、46系列中的第四个数字进行编码。 您最好猜测一个数字将要占用多少位,然后将其称为k:由于前三个数字取了4位,这似乎是一个合理的猜测,因此我们将 k = 4 设为该数字的最右边的k位。 并记住它们是什么:46的右边4位(101110)是1110 想象没有最右边的k位的二进制数,并查看它的新值(这是不适合k位的溢出):在距101110右边1110处,您剩下10或2(以10为底)。 使用这些值对数字进行编码。 因此,我们放两个0,然后是终止1,再是k位1110。我们总共有:0011110 现在不当执行此操作,我们只取0011110和k = 4,然后倒退。 我们首先看到溢出为2(在终止1之前有两个零)。 我们还看到最后四个位=1110。因此,我们取值10(溢出)和值1110(k),然后进行一些移位和波动! (溢出移位<< << k个位)
同一示例的更多技术版本 这里是同一过程的更多技术和数学描述: 假设某个整数n是要编码的数字,而k是直接要编码的比特数 。 符号(1为正,0为负) n /(2k)0 终止n的1 k个最低有效位
例如,如果n = 578且k = 8:100101000010 符号(1为正,0为负)= [ 1] n /(2k)0:n / 2k = 578/256 = 2 = [00] 终止1:[1] k个n的最低有效位:578 = [01000010] 将1-4放在一起:[1] [00] [1] [01000010] = 100101000010 在编码过程中,通过查看过去的平均值来确定最佳k,但是平均值很多(16-128效果很好),然后为该平均值选择最佳k( 基本上是在猜测下一个值是什么,然后根据该值选择最有效的k)。 最佳k可以计算为[log(n)/ log(2)]。
https://monkeysaudio.com/theory.html