• 记录一个音频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
  • 相关阅读:
    拒绝一次性芯片,新技术:无线升级芯片
    网络精通-端口镜像、端口安全
    杰理之内置短按再长按,不管长按多长时间都是短按【篇】
    ip地址范围不是整体范围的子集
    Soft Actor-Critic Algorithms and Applications
    IPSec NAT穿越原理
    NtyCo纯C协程的原理分析
    ASP.NET教学科研成果管理系统VS开发sqlserver数据库web结构c#编程计算机网页项目
    基于JAVA校园点餐系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    【Elasticsearch】es脚本编程使用详解
  • 原文地址:https://blog.csdn.net/lijian2017/article/details/126884278