• 记录一个音频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
  • 相关阅读:
    CentOS7使用yum安装MySQL8.0教程
    jQuery事件操作
    js语句简写大全
    友元,静态关键字,静态方法以及对象间的关系
    探讨服务端自定义生成PDF的几种方案
    Java异常处理
    [第十三篇]——Docker Compose
    5分钟搞懂MySQL - 行转列
    你安全吗?丨秦淮到底是哪种黑客?你猜对了吗?
    基于口罩佩戴公开赛数据实践口罩佩戴识别
  • 原文地址:https://blog.csdn.net/lijian2017/article/details/126884278