6.2 音效与混音


6.2 音效与混音

本节摘要:一条合格的成片音轨至少由三层叠成:台词层负责信息,音效层负责质感,音乐层负责情绪。本节先建立三轨模型,再讲音效选配的克制原则——四类音效各司其职,宁少勿多;然后用 ffmpeg 滤镜链实现"台词加时间点音效"的程序化混音,讲透 BGM 自动闪避的 sidechaincompress 思路:把人声当侧链信号去压音乐,台词一来音乐自动退后半步;最后做响度归一化,用 loudnorm 两遍法把整片响度压进平台口径的目标带。所有命令为写法示意,参数以 ffmpeg 官方文档与各平台现行规范为准。

学习目标

  • 说出台词、音效、BGM 三轨各自的职责与混音优先级。
  • 按四类音效选配原则为分镜表标注音效点,且不越界。
  • 用 filter_complex 把带时间点的音效混进台词轨。
  • 解释 sidechaincompress 的信号流并实现 BGM 自动闪避。
  • 用两遍法完成响度归一化并核对目标带。

一、三轨模型:混音前先想清楚谁让谁

把成片音频拆开,无非三根轨。它们的地位不平等:台词是信息轨,任何时刻优先保障可懂度;音效是质感轨,服务真实感与节奏感;BGM 是情绪轨,铺底时可以一直在,但永远不能压过台词。

电平示意(从上到下优先级递减) 台词轨 ── ▂▄▆█▆▄ ▂▄▆█▆▄ ▂▄▆█▆▄ ── 时时刻刻最响 音效轨 ── ▄█▄ ▄█▄ ── 只在事件点出现 BGM 轨 ── ▄▄▄▄▄▄▄▂▂▂▂▄▄▄▄▄▄▄▂▂▂▂▄▄▄▄▄ ── 台词出现时被压低 镜头01 镜头02 镜头03 (▂ 为被闪避压低后的 BGM) ​

混音时按"先台词、后音效、再 BGM"的顺序叠加,冲突时永远砍下层保上层。

二、音效选配:克制是第一原则

音效分四类,各有明确职责:

类别 作用 例子 用量原则
环境底 建立空间真实感 雨声、街噪、店内嗡鸣 全场垫底,音量最低
动作音 对齐画面事件 开门、脚步、手机震动 与画面帧级对齐
转场音 缝合镜头切换 嗖声、低频顿挫 少用,快剪慎用
情绪点 强调叙事节拍 心跳、钟表滴答 只在关键情绪点

选配的克制原则:每镜头音效不超过两类;环境底一镜一个,不叠多个环境;转场音能不用就不用——观众对"嗖"的容忍度远低于制作者的想象。选配结果落在分镜表上,形成一张音效计划:

# sfx_plan.json —— 音效计划:哪个文件、在第几秒、多大增益(写法示意) [ {"file": "assets/sfx/rain_loop.wav", "at": 0.0, "gain": 0.5}, {"file": "assets/sfx/door_knock.wav", "at": 6.2, "gain": 0.9}, {"file": "assets/sfx/phone_buzz.wav", "at": 14.8, "gain": 0.7} ] ​

来源与授权在这一步就要把关:素材站按许可筛选(可商用、是否署名),自录是最安全的来源,许可不明的音频一律不进 assets 目录。这一纪律与第 8.2 节素材采集、第 11.1 节合规交付一脉相承。

三、程序化混音:把音效按时间点叠进台词

手工拖音轨无法批量复用,把混音写成 ffmpeg 滤镜链才是流水线的做法。核心思路:每个音效用 adelay 移到计划时间点、用 volume 定增益,再与台词轨一起送进 amix。

# mix_audio.py —— 台词加带时间点的音效,程序化混音(ffmpeg 写法示意) import json, subprocess def mix(voice="audio/voice_full.wav", sfx_plan="build/sfx_plan.json", out="audio/mixed.wav"): plan = json.load(open(sfx_plan, encoding="utf-8")) inputs, filters, labels = ["-i", voice], [], [] for i, s in enumerate(plan): inputs += ["-i", s["file"]] delay_ms = int(s["at"] * 1000) filters.append(f"[{i + 1}:a]adelay={delay_ms}|{delay_ms}," f"volume={s.get('gain', 1.0)}[s{i}]") labels.append(f"[s{i}]") mix_inputs = "[0:a]" + "".join(labels) graph = ";".join(filters) + ";" + mix_inputs \ + f"amix=inputs={1 + len(plan)}:normalize=0[out]" subprocess.run(["ffmpeg", "-y", *inputs, "-filter_complex", graph, "-map", "[out]", "-ar", "48000", out], check=True) print(f"混音完成:{1 + len(plan)} 路输入 → {out}") if __name__ == "__main__": mix() ​

两个细节:adelay 的两个数值对应左右声道,单声道也要写两份;amix 的 normalize 关掉,让各轨增益由 volume 显式控制,避免混音器自作主张拉低总电平。

四、BGM 自动闪避:sidechaincompress 思路

手动给 BGM 画音量包络既费时又难以复用。成熟做法是侧链压缩:压缩器通常拿自己的输入信号控制增益,sidechaincompress 允许"用另一路信号控制这一路的增益"——用台词当控制信号去压 BGM,台词一出现,BGM 自动变小;台词结束按释放时间缓缓恢复。

信号流(duck 即"闪避") 台词 voice.wav ──────────────┬──────────────▶ amix ─▶ 成片音轨 │(作为侧链控制信号) BGM bgm.wav ─▶ volume ─▶ sidechaincompress ─▶ ducked ─┘ 压缩量由台词电平决定 ​
# duck_bgm.sh —— 人声侧链压低 BGM(写法示意,参数听感为准) ffmpeg -y -i audio/voice_full.wav -i audio/bgm.wav -filter_complex \ "[1:a]volume=0.7[m];[m][0:a]sidechaincompress=threshold=0.03:ratio=12:attack=25:release=400[duck];[0:a][duck]amix=inputs=2:normalize=0[aout]" \ -map "[aout]" -ar 48000 audio/ducked_mix.wav ​

参数听感含义:threshold 是触发压缩的台词电平门限;ratio 决定压多狠(数值越大压得越深);attack 是台词出现后压下去的速度(几十毫秒内较自然);release 是台词结束后音乐恢复的速度(几百毫秒不突兀)。闪避不是一刀切静音,"退后半步"比"消失"更专业。

五、响度归一化:两遍法与目标带

混好的音轨响度是随缘的,平台会整体拉高或压低,导致听感变形。正确做法是把整片综合响度归一到目标值。常见的短视频平台口径在负十四 LUFS 上下(口径值,以各平台现行规范为准),真峰值留约负一 dB 余量。loudnorm 滤镜推荐两遍法:第一遍只测量不修改,拿到整片的响度统计;第二遍把测量值作为参数回填,做线性归一。

# normalize_loudness.py —— loudnorm 两遍法响度归一化(目标值以平台规范为准) import json, re, subprocess TARGET = "I=-14:TP=-1.5:LRA=11" def measure(path): cmd = ["ffmpeg", "-i", path, "-af", f"loudnorm={TARGET}:print_format=json", "-f", "null", "-"] proc = subprocess.run(cmd, capture_output=True, text=True) m = re.search(r'\{[^{}]*"input_i"[^{}]*\}', proc.stderr, re.S) return json.loads(m.group(0)) if m else {} def apply_norm(src, dst, stat): af = (f"loudnorm={TARGET}:" f"measured_I={stat['input_i']}:measured_TP={stat['input_tp']}:" f"measured_LRA={stat['input_lra']}:measured_thresh={stat['input_thresh']}:" f"linear=true:print_format=summary") subprocess.run(["ffmpeg", "-y", "-i", src, "-af", af, "-ar", "48000", dst], check=True) if __name__ == "__main__": stat = measure("audio/ducked_mix.wav") print("测量值:", {k: stat.get(k) for k in ("input_i", "input_tp", "input_lra")}) apply_norm("audio/ducked_mix.wav", "audio/final_audio.wav", stat) print("已归一化 → audio/final_audio.wav") ​

为什么必须两遍:单遍模式下 loudnorm 处于动态压缩状态,会实时改变听感;带测量值的线性模式只做整体增益,不引入压缩染色。

六、混音验收

交棒给合成之前,最后核对四件事:响度落在目标带内(重跑 measure 核对);真峰值不超限(削波一旦混进母轨无法事后修复);闪避回落正常(台词结束后 BGM 是否按 release 平滑回来);单声道兼容(手机外放是单声道,混音后用单声道再听一遍,确认没有相位抵消导致的乐器消失)。

本节要点回顾

  • 三轨模型:台词是信息轨、音效是质感轨、BGM 是情绪轨,冲突时砍下层保上层。
  • 音效四类各有职责,每镜头不超过两类,许可不明的音频不进资产库。
  • 混音写成 ffmpeg 滤镜链:adelay 定时间点、volume 定增益、amix 关自动归一。
  • 闪避用 sidechaincompress:台词做侧链控制信号,attack 快压、release 慢放。
  • 响度归一化用两遍法,线性模式不染色,目标带以各平台现行规范为准。

音轨就绪,接下来要让它与画面咬合。下一节讲"先配音后剪辑"的落地方案:用台词时长反推每个镜头的画面时长,并用三层校验确认音画真的对上了。

作者与出处
原作者:灏天文库
整理人: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U