如何正确实现数字音频音量增益 避免量化失真
问题根源
你观察到的采样值0、100、200…离散分布、阶梯状听感,既不是16bit音频精度不足,也不是小型扬声器的物理限制,完全是现有实现的逻辑缺陷导致的:
- 你读取的原始16bit采样是整数类型,当原始信号是极微弱的野生动物录音时,采样值本身分布在非常窄的整数区间(比如-1010),乘以0100的增益后,结果会按增益值为步长跳变,直接强转
short时的向零取整操作会直接丢弃所有小数部分,相当于人为把有效量化级从16bit的65536级砍到了只有几百级,这种和信号强相关的量化谐波失真听感就是颗粒感、阶梯感的来源,和削波失真、扬声器失真有非常明显的区别,你没有过度敏感。
行业通用成熟解决方案
这些都是专业音频领域用了几十年的标准处理流程,全链路遵循即可彻底解决你遇到的量化阶梯问题:
- 全处理链路使用高位深格式,不要在16bit整数域做增益运算。正确流程是先把读取到的16bit整数采样归一化转成32位浮点,所有增益、限幅、效果处理全部在浮点域完成,最后输出阶段再做量化。Android系统从5.0开始就支持
ENCODING_PCM_FLOAT格式的AudioTrack输出,直接把处理完的32位浮点采样传给系统即可,现代移动设备的音频DAC普遍支持24bit以上输入,系统层会处理后续的数模转换,完全不需要你在应用层提前量化回16bit整数,从根源上避免应用层量化误差。 - 如果必须输出16bit整数格式,量化前必须加抖动(Dither)。这是解决低比特量化失真的标准方案:在量化前给信号叠加幅度为1个最低有效位的三角概率分布(TPDF)白噪声,把原本和信号相关的刺耳量化谐波失真,转换成和信号无关的均匀低电平白噪声。人耳对这种低电平白噪声的感知度极低,叠加后你听到的阶梯感会完全消失,效果远好于事后用滤波器平滑——滤波器是在失真产生后抹除痕迹,抖动是从根源上避免可感知的失真产生。
- 替换硬截断削波逻辑。你现在用的
Math.max(Math.min(...))属于硬削波,信号超过满幅时会被直接砍平,产生大量刺耳的奇次谐波失真。高增益场景下应该用软削波(Soft Clipping)或者真峰值限幅器,把超幅信号平滑压缩到合法范围,这部分解决的是削波带来的失真,和量化阶梯是两个独立的失真来源,需要分开处理。
现有代码的快速修正参考
核心修改点就是把增益计算移到浮点域,输出时根据目标格式选择直接传浮点,或者加抖动后再量化回16bit:
// 建议优先把AudioTrack配置为ENCODING_PCM_FLOAT格式,直接输出浮点采样,省略手动量化步骤 Random random = new Random(); while (isPlaying() && (i = is.read(music)) != -1) { int sampleCount = i / 2; for (int j = 0; j < sampleCount; j++) { // 解析原始16bit小端采样 short rawSample = (short) ((music[j * 2] & 0xff) | (music[j * 2 + 1] << 8)); // 归一化到[-1, 1]浮点范围做增益,所有处理都在浮点域完成 float processedSample = rawSample / 32768.0f * gain; // 此处可替换为软削波/限幅逻辑,替代硬截断 // 简单软削波示例:processedSample = (float) Math.tanh(processedSample); processedSample = Math.max(-1.0f, Math.min(1.0f, processedSample)); // 如果输出浮点格式,直接写入浮点数组即可,以下为输出16bit short时的量化逻辑 // 叠加TPDF抖动,幅度为1个16bit最低有效位 float dither = (random.nextFloat() - random.nextFloat()) / 32768.0f; processedSample += dither; // 量化回16bit整数 musics[j] = (short) Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, (int)(processedSample * 32768.0f))); } if (isPlaying()) mplayer.write(musics, 0, sampleCount); }
补充说明:16bit PCM本身拥有约96dB的动态范围,完全满足消费级回放的精度要求,你遇到的问题和16bit精度无关,纯粹是处理流程中整数域运算、无抖动截断导致的有效位损失,修正后40dB(100倍)增益下的量化噪声完全在可感知阈值以下,不会出现明显的阶梯感。
内容的提问来源于stack exchange,提问作者Luis A. Florit
相关产品推荐
相关产品推荐

