使用 add + shift 将 pcm16 转换为 pcm14

Posted

技术标签:

【中文标题】使用 add + shift 将 pcm16 转换为 pcm14【英文标题】:pcm16 to pcm14 conversion using add + shift 【发布时间】:2010-07-06 17:01:03 【问题描述】:

我正在研究一种声音转换算法,其中接收到一组签名短裤。 在算法的给定点,它将样本从 16 位转换为 14 位,它是这样进行的:

int16_t sample = (old_sample + 2) >> 2;

对我来说,很明显需要进行移位,因为我们想去掉最低 2 个有效位,但是那里的 +2 呢?

【问题讨论】:

【参考方案1】:

向下移动会丢失最低有效两位。如果您只是移位,那么即使底部两位都已设置,它也将始终向下舍入。如果设置了较大的丢失位,则将 2 轮向上添加。

(另外值得注意的是,减少比特数的更好方法是使用dithering,即在减少样本大小之前添加随机(并且非常小)量的噪声;这避免了以下问题,因为声音是周期性的,对于特定频率,四舍五入通常最终会持续上升或持续下降,从而导致声音出现明显的失真。链接的***文章比我能更好地解释它!)

【讨论】:

【参考方案2】:

我猜它打算有四舍五入的效果?我只是希望他们考虑old_sampleMAX_INT16 - 2 更多的情况。否则溢出时可能会出现问题。

【讨论】:

如果是 MAX_INT16 - 2,还是可以的……我认为 old_sample 应该在加 2 之前转换为 32bit int,然后 shift 将确保下一次转换为 16bit 不会'不会造成伤害...不用等待,它可以...它应该是算术移位,而不是逻辑移位(如果样本被认为是有符号的,看起来); >> 是否按标准执行算术移位? 是的,应该阅读超过 MAX_INT16,将编辑修复。我很确定它是有符号数的算术移位和无符号数的逻辑移位,尽管我可能再次错了。如果我是的话,希望有人能指出来。 @ShinTakezou:移位负整数的行为是实现定义的,这在实践中意味着算术或逻辑移位,具体取决于相关架构上最简单的方法。 int 不能保证大于 16 位,所以你是对的 - 它可能会溢出。使用 2L 作为常量可以解决这个问题,因为 long 至少是 32 位。 @caf 谢谢。 @torak @fail 此外,代码不能保证符号被班次保留! (如果签名的 int 旨在作为未签名的样本,则无害......)【参考方案3】:

正如其他人所指出的,+2 是尝试使右移执行四舍五入到最近的除法。但是,有两个问题:

32766 或 32767 的输入样本在加 2 时可能会溢出intint 仅保证能够表示最大为 32767 的数字);

负数右移的行为是实现定义的。

为了避免这些问题,应该是:

int16_t sample = (old_sample > 0 ? old_sample + 2L : old_sample - 2L) / 4;

(与移位运算符不同,C99 中的除法运算符被定义为向零舍入)。

【讨论】:

【参考方案4】:

该代码的意图可能是四舍五入,如其他回复中所示。但这肯定是一个非常糟糕的例子。这里发生了两件事,最初的程序员可能并不打算这样做:

晋升至int 并重新分配至int16_t signed 值的右移

升级到int(因为+2 只是一个int)在这里不好,因为你不知道int 在任何随机平台上的精确度是你碰巧登陆.

如果值为负数,signed 值的右移取决于编译器,因此结果也可能因平台而异。

【讨论】:

升级到int 还不错,因为int 必须至少 16 位。但是,如果输入样本为 32766 或 32767,最好升级为long,以避免溢出问题。 @caf:我不同意这种观点,相反这是一种非常讨厌的错误。它使您认为代码中的所有内容都可以正常工作,直到...有人在 16 位平台上编译它的那一天(是的,我知道这些天很少见)并且遇到溢出的极端情况在+2 中,您遇到了严重的崩溃。非常讨厌,无法调试。所以这里肯定需要演员表,我会这样做 int32_t 保持一致。 (就像我说的那样,将类型更改为unit.. 当结果被截断为 14 位时,提升和右移问题都不重要。无论如何,将通过使用更长的数据类型保留的位被丢弃,未定义的位也是如此。我没有看到“严重崩溃”的可能性;问题将是样本将有效地改变符号。这可能在输出中听得见,但如果样本那么大,那么它可能无论如何都会削波。当然,抖动是正确的答案。

以上是关于使用 add + shift 将 pcm16 转换为 pcm14的主要内容,如果未能解决你的问题,请参考以下文章

将 32 位浮点数转换为 16 位 PCM 范围

将 PCM 16 位 LE 转换为 WAV

使用 NAudio 将 PCM S16 LE (s16l) 转换为 GSM610

是否可以使用 opus API 将 opus-fltp 转换为 opus-s16(或)将 opus-fltp 解码为 pcm

将原始PCM数据转换为RIFF WAV

Java - 将 16 位有符号 pcm 音频数据数组转换为双精度数组