一、动态处理:从响度战争到动态保留
动态处理,说白了就是跟声音的「大小变化」较劲。你想想看,一段音乐里最响的部分和最轻的部分之间的差距,就是动态范围。而压缩器、限制器、扩展器这些工具,就是用来调整这个范围的。
我在项目中遇到过不少音频算法工程师,一上来就堆压缩器,结果声音是响了,但听着特别累。嗯,这里要注意——动态处理不是越狠越好。
1.1 压缩器:把大的变小
压缩器的工作原理其实很简单:当信号超过某个阈值(Threshold),就按比例(Ratio)把超出的部分压下去。比如阈值设为 -20dB,Ratio 设为 4:1,那么输入 -12dB(超出 8dB)的信号,输出就变成 -20dB + (8dB / 4) = -18dB。
我个人习惯把压缩器分成几个核心参数:
- 阈值(Threshold):决定什么时候开始干活
- 压缩比(Ratio):决定干活的力度
- 启动时间(Attack):信号超过阈值后,多久开始压
- 释放时间(Release):信号低于阈值后,多久停止压
- 增益补偿(Makeup Gain):压完了再把整体音量提回来
核心公式:
输出电平 = 阈值 + (输入电平 - 阈值) / Ratio
当输入电平 ≤ 阈值时,输出 = 输入
我曾经踩过一个坑:在 Android 的 AudioEffect 框架里实现压缩器时,忘了处理 Attack 和 Release 的平滑过渡。结果压缩器一启动,声音像被「咔嚓」切了一刀,听着特别突兀。后来加了个一阶低通滤波器做平滑,才解决了这个问题。
1.2 限制器:压缩器的极端版本
限制器其实就是压缩比特别大的压缩器。当 Ratio 达到 10:1 甚至 ∞:1 时,压缩器就变成了限制器。它的作用很简单:绝对不让信号超过某个电平。
你想想看,为什么需要限制器?因为数字音频系统有个硬上限——0dBFS。一旦超过,就会产生削波失真。限制器就是最后一道防线。
避坑指南:
我曾经在某个项目里把限制器的阈值设得太低,结果整个音频听起来像被「捏住」了一样,动态全没了。限制器是用来防止过载的,不是用来做动态压缩的。别把它当压缩器用。
1.3 扩展器:跟压缩器反着来
扩展器的工作方向跟压缩器相反。当信号低于阈值时,它会把信号进一步压低。说白了,就是让安静的部分更安静,从而扩大动态范围。
扩展器有个特殊参数叫扩展比(Expansion Ratio)。比如 Ratio 设为 2:1,阈值设为 -40dB,那么输入 -50dB(低于阈值 10dB)的信号,输出就变成 -40dB - (10dB × 2) = -60dB。
我记得有一次做语音降噪,背景噪声一直去不干净。后来用了个扩展器,把低于 -50dB 的信号再压下去 6dB,噪声瞬间就消失了。嗯,这招挺管用。
1.4 多频段动态处理
单频段压缩有个问题:它不分青红皂白地压整个频谱。比如低频的鼓声一响,可能把高频的镲片也给压下去了。多频段动态处理就是为了解决这个问题。
多频段处理器的核心思路:
- 用分频滤波器把信号分成几个频段(比如低频、中频、高频)
- 每个频段独立进行动态处理
- 最后再把处理后的信号合起来
在 Android 的 DynamicProcessingEffect 框架里,多频段配置是通过 ChannelConfig 和 BandConfig 来实现的。每个频段可以独立设置阈值、压缩比、Attack/Release 时间。
// Android DynamicProcessingEffect 多频段配置示例
DynamicProcessingEffect.Config config = new DynamicProcessingEffect.Config();
config.setChannelCount(2);
config.setBandCount(3); // 三个频段
// 低频段:0-200Hz
BandConfig lowBand = new BandConfig();
lowBand.setFrequencyRange(0, 200);
lowBand.setThreshold(-20.0f);
lowBand.setRatio(4.0f);
lowBand.setAttackTime(5.0f); // 5ms
lowBand.setReleaseTime(50.0f); // 50ms
// 中频段:200-4000Hz
BandConfig midBand = new BandConfig();
midBand.setFrequencyRange(200, 4000);
midBand.setThreshold(-24.0f);
midBand.setRatio(3.0f);
midBand.setAttackTime(3.0f);
midBand.setReleaseTime(30.0f);
// 高频段:4000-20000Hz
BandConfig highBand = new BandConfig();
highBand.setFrequencyRange(4000, 20000);
highBand.setThreshold(-18.0f);
highBand.setRatio(2.0f);
highBand.setAttackTime(2.0f);
highBand.setReleaseTime(20.0f);
config.setBandConfig(0, lowBand);
config.setBandConfig(1, midBand);
config.setBandConfig(2, highBand);
个人经验:
多频段处理时,分频点的选择很关键。我一般建议分频点选在 200Hz 和 4kHz,这样低频的冲击感、中频的人声、高频的空气感都能独立控制。当然,具体数值还得看你的音频内容。
1.5 音频响度战争与动态保留
说到响度战争,这其实是个历史问题。从上世纪 90 年代开始,唱片公司为了让自己的歌在电台里听起来更响,拼命提高录音的电平。结果呢?动态范围被压缩得越来越小,音乐听起来又平又累。
我见过一些极端案例:一首歌的平均响度能做到 -6dBFS,但动态范围只有可怜的 3dB。说白了,整首歌从头到尾都是一个音量,听着像「一堵墙」砸过来。
动态保留的核心思想:在提升响度的同时,尽量保留原始音频的动态变化。具体做法包括:
- 使用较温和的压缩比:2:1 到 3:1 就够用了,别动不动就 10:1
- 较长的 Attack 时间:让瞬态信号先通过,再慢慢压缩
- 多频段处理:避免低频影响高频的动态
- 限制器只做峰值保护:别让它参与日常压缩
我的建议:
做动态处理时,先问自己三个问题:
- 这个音频的原始动态范围是多少?
- 目标播放场景是什么?(手机外放?耳机?车载?)
- 听众需要什么样的听感?(自然?冲击力?)
想清楚这些,再动手调参数。
1.6 动态处理知识体系
下面这张图展示了动态处理的核心知识结构,我把它画成了流程图,方便你理解各个模块之间的关系。
这张图把动态处理的四个核心模块串起来了。从压缩器、限制器、扩展器到多频段处理,每个模块都有自己独特的参数和应用场景。而最底层的三个应用场景——响度提升、动态保留、防削波保护——正是我们在实际项目中需要权衡的目标。
我个人觉得,动态处理最难的不是学会怎么调参数,而是知道什么时候该停手。响度战争打了这么多年,听众的耳朵已经开始疲劳了。保留动态,让声音有呼吸感,这才是好音频该有的样子。