• 记录一个音频PCM数据由双声道转单声道出错问题


    引言

    工作需要将一份 双声道的PCM数据转换成单声道数据,我采用的是将左右声道样点值对应相加求平均样点值的办法。
    计算式如下:

    typedef          int s32;
    typedef unsigned int u32;
    
    typedef          short s16;
    typedef unsigned short u16;
    
    /*
    *	双声道转换成单声道:48khz-16bit-stereo  --->  8khz-16bit-mono
    *	源数据缓冲:pSrcBuf 
    *	源数据长度:dwSrcLen 
    *	目标数据缓冲:pDstBuf
    *	下面的样点必须使用 s16 表示,禁止使用 u16表示,因为u16会导致计算出错,从而产生大噪声问题!
    */
    
    u32 dwSampleNum = dwSrcLen >> 2; //计算单声道样点数
    u16* pwSrc = (u16*)pSrcBuf; //双声道数据缓冲
    u16* pwDst = (u16*)pDstBuf; //单声道数据缓冲
    
    for (u32 dwIdx = 0 ; dwIdx < dwSampleNum; dwIdx++)
    {
    	pwDst[dwIdx]     = (pwSrc[2*dwIdx] + pwSrc[2*dwIdx + 1]) / 2; //左右声道求均值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    问题

    实际测试发现,上述代码处理后的PCM数据含有大量的大噪声!
    用 Adobe Audition 打开处理后的单声道数据:
    请添加图片描述
    由图可知,必是大噪声。
    将上图局部放大
    请添加图片描述
    继续放大
    请添加图片描述
    由图分析:正常的声音波形是在样点值接近0的时候发生突变的,变成极大值或极小值。
    查看这些突变样点值大小
    请添加图片描述
    综上分析,正常的声音波形是在样点值接近0的时候发生突变的,变成极大值或极小值(即:绝对值接近 32768 = 2^15)

    分析

    我们在上面计算左右声道样点值的均值时,是用 u16 来表示样点值的,这种表示方法在计算均值时出问题了!
    一般左右声道的对应样点值是比较接近的,可能出现下面的两种情况:
    例如:左声道样点值 = -1 ,右声道样点值 = 1 时

    (-1 + 1)/2
    = (0000 0000 0000 0000 1111 1111 1111 1111 + 0000 0000 0000 0000 0000 0000 0000 0001) /2  //u16 整型提升为 s32
    = (0000 0000 0000 0001 0000 0000 0000 0000) /2                                            //加法进位
    = (0000 0000 0000 0000 1000 0000 0000 0000)                                               //除2相当于右移1位
    = (1000 0000 0000 0000)                                                                   //截断操作:将计算结果赋值给 目标样点值s16
    = -32768                                                                                  //(1000 0000 0000 0000)就是-32768的补码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    例如:左声道样点值 = -2 ,右声道样点值 = 1 时

    (-2 + 1)/2
    = (0000 0000 0000 0000 1111 1111 1111 1110 + 0000 0000 0000 0000 0000 0000 0000 0001) /2  //u16 整型提升为 s32
    = (0000 0000 0000 0000 1111 1111 1111 1111) /2                                            //加法进位
    = (0000 0000 0000 0000 0111 1111 1111 1111)                                               //除2相当于右移1位
    = (0111 1111 1111 1111)                                                                   //截断操作:将计算结果赋值给 目标样点值s16
    = 32767                                                                                   //(0111 1111 1111 1111)就是32767的补码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其实,类似的左右样点组合还有很多,都是在0值附近,表现在波形中就是:
    正常的声音波形在样点值接近0的时候发生突变的,变成极大值或极小值。

    上述分析与问题现象一致,所以可以确定问题原因就是:
    因为用 u16 来表示样点值的,导致在计算均值时出错!

    解决方案

    在计算左右声道的对应样点均值时,样点必须使用 s16 表示,禁止使用 u16表示,因为u16会导致计算出错,从而产生大噪声问题!

    /*
    *	双声道转换成单声道:48khz-16bit-stereo  --->  8khz-16bit-mono
    *	源数据缓冲:pSrcBuf 
    *	源数据长度:dwSrcLen 
    *	目标数据缓冲:pDstBuf
    *	下面的样点必须使用 s16 表示,禁止使用 u16表示,因为u16会导致计算出错,从而产生大噪声问题!
    */
    
    u32 dwSampleNum = dwSrcLen >> 2; //计算单声道样点数
    s16* pwSrc = (s16*)pSrcBuf; //双声道数据缓冲
    s16* pwDst = (s16*)pDstBuf; //单声道数据缓冲
    
    for (u32 dwIdx = 0 ; dwIdx < dwSampleNum; dwIdx++)
    {
    	pwDst[dwIdx]     = (pwSrc[2*dwIdx] + pwSrc[2*dwIdx + 1]) / 2; //左右声道求均值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    提取PDF数据:Documents for PDF ( GcPdf )
    day5-selenium的高级和实战
    从零学习Linux操作系统 第三十五部分 Ansible中的角色
    阿里云日志上报乱码问题记录
    GoLand 2023:为Go开发者打造的智能IDE mac/win激活版
    Python学习笔记合集(总结)
    金属压块液压打包机比例阀放大器
    客服常用100句用语帮您全面搞定客服回复
    GO语言篇之反射
    Cyanine 5 monosuccinimidyl ester,氰胺5-单琥珀酰亚胺酯,花菁染料CY5标记单琥珀酰亚胺酯
  • 原文地址:https://blog.csdn.net/lijian2017/article/details/126884278