记录一个音频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; //左右声道求均值
}

问题

实际测试发现,上述代码处理后的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的补码

例如:左声道样点值 = -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的补码

其实,类似的左右样点组合还有很多,都是在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; //左右声道求均值
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值