libdca是DTS的浮点开源解码器,在不影响音质的条件下,我对其进行浮点转定点并进行优化,使其能在32位定点DSP上高效的运行。
首先是定点化,首先我们可以先使用64位运算来替代浮点运算,一般来说这种定点化不会影响音质,但在32位定点DSP上64位运算的效率也是不高的,所以最终的目标是32位定点运算。(听说还有16位的,这我就没有达到过了,不知道哪位仁兄能够达到,分享一下)
libdca解码器代码中的主要的浮点运算主要在两个函数中,一个是dca_subsubframe(),另一个是qmf_32_subbands(),集中在反量化,ADPCM,IMDCT,加窗操作中。
我的策略是各个击破,在其他部分不变的情况下,定点化其中之一,这样可以隔离它们之间的互相干扰。当四个部分都定点完成,再集成到一起,进行微调。大体上这样就可以完成定点工作。
具体这四个部分怎么操作,我以反量化部分举例说明。
首先,介绍一下DTS的量化,DTS的量化比较特别,她有两个量化步长矩阵,对每个采样在每个量化矩阵中各对应一个量化步长,逆量化的步骤就是熵解码输出×量化步长矩阵1×量化步长矩阵2得到逆量化输出。(能不能把两个量化矩阵合二为一呢?我估计不能,因为每个采样对应的两个矩阵中的元素是动态的,对应关系我不是很清楚,有清楚的强人希望分享一下,毕竟能够节省很多乘法)
因为不能合并两个矩阵,我只能估计一下两个矩阵相乘的结果动态范围,应该是2.384e-7~1.33e7,当然这个是极限值,说明DTS标准量化步长的范围很大,实际上用到的不可能有这么大。
熵解码的输出大概是0~3e4(绝对值,解码输出为整数),可以看出,如果直接相乘,得到逆量化的动态范围2.382e-7~4e11,实际上像上面说的一样,不可能会有这么大的动态范围,至少上限根据我的经验不会溢出32位,应该比32位还小不少,我曾经测试过一些码流,可惜数据量太大没有记录下来,我也忘记了。
我们需要定点化哪个部分呢?
浮点运算出现在量化步长矩阵1和量化步长矩阵2相乘得最终量化步长的运算上,其中一个矩阵是定点的,范围为0~8317638;另一个矩阵是浮点的,动态范围是2.384e-7~1。
使用浮点运算可以保证精确度和动态范围,先使用64位定点化。
首先就是先把矩阵2左移一定的位数,比如24位,将矩阵2转为定点的,这样矩阵2的动态范围为4~1.67e7,这样再和矩阵2相乘,乘完之后再右移24。这样的做的话虽然比浮点运算损失一定的精度,但一般人耳是听不出来的。
64位定点完成,考虑如何转成32位定点,矩阵2的动态范围太大了,达到了24位,如果直接笼统左移就会遇到一个矛盾——左移位数大了,32位乘法会溢出,左移位数小了又会损失精度太多,出现明显的噪声。于是我考虑缩小矩阵2的动态范围(有人说,为什么不考虑改动矩阵1?事实上我试过改动矩阵1,结果在一些码流出现明显的噪声,所以还是推荐改动矩阵2),缩小矩阵2的动态范围的方法就是对不同大小的元素左移不同的位数,使其达到一定的范围,比如128~256,于是对0.5我就左移8位,对0.0008我就左移17位,创建一个新矩阵记录我们的移位值,乘法结束后还要右移回来。于是矩阵2的动态范围为128~256,和矩阵1的乘法也不会溢出,而且精度不错。这样32位的定点也就完成了。其实这就是一个矩阵定标的过程。
其他几个部分的32位定点类似,只不过ADPCM定标的是ADPCM的表,IMDCT定标的是cos表,加窗定标的是加窗的表。
当然,上面的过程说起来简单,做起来还是比较麻烦的,主要是左移位数的把握,也就是矩阵2元素大小的把握,小了精度不够,大了又容易溢出,(要知道溢出造成的噪声是相当明显的,有时宁愿损失一些精度也要保证不溢出)。我常常要测码率的采样的动态范围,像走钢丝一样,在尽量提高精度的同时不溢出。
写到现在,觉得其实我的定点化的思想早就有过了,就是Q格式,思想上差不多,只不过我用Q格式还不习惯。
技术水平不行,语言组织能力更差,可能会有不对的地方,希望您的指正。