HyperFrames 音频
混音是一组关系,而非处理器的堆叠。两轨各自听起来不错,但放在一起可能无法入耳,解决方法几乎从来不是“调低其中一轨”——而是找出它们冲突的点,并将其分配给需要的那一轨。这里的每个工具都是为了表达其中一种关系。
效果位于元素上的 data-fx-chain 中,预览和渲染使用相同的 Web Audio 图形——在实时上下文中是工作室,在离线上下文中是引擎,而引擎运行在浏览器内。每个效果只有一个实现,因此你在擦洗时听到的就是将被写入的内容。你永远不需要调整两次。
所有内容由三个属性承载,它们都位于音频/视频元素本身上:
| 属性 | 承载的内容 |
|---|---|
data-fx-chain | 效果链(按信号顺序) |
data-automation | 此轨道音量或其效果参数的包络线 |
data-fx-carve | 雕刻本身的设置(以便后续重新推导) |
每个属性及其JSON格式,以及一个轨道必须满足的规则:references/attributes.md。
每个效果及其参数、范围和单位:references/fx-registry.md。
如何处理你无法听到的文件的故障诊断:references/diagnosis.md。
预设、命名任务和单旋钮配置,以及症状对应修复表格:references/presets.md — 在手动构建效果链之前,请先阅读该文档,因为其中某个预设或命名任务通常已经指出了问题。
如何配合工作
两个创作界面写入这些属性;两个运行时通过相同的构建器读取它们。正是这种共享的中间层使得预览能够预测渲染结果。
flowchart TB
voice["语音轨道<br/>媒体文件"]
bed["音乐背景<br/>媒体文件"]
subgraph AUTHOR["创作——唯一写入属性的部分"]
panel["工作室<br/>旁白雕刻控制"]
script["scripts/carve.mjs<br/>检测配对,默认动态"]
analysis["core/audioCarve.ts<br/>carveProfile · analyseCarveBands<br/>analyseCarveDuck · analyseCarveDynamics"]
panel --> analysis
script --> analysis
end
voice --> analysis
bed --> analysis
subgraph ATTRS["写入到背景元素上"]
carveAttr["data-fx-carve<br/>源 · 强度 · 动态"]
chainAttr["data-fx-chain<br/>峰值滤波器 xN + 增益,标记为 fromCarve"]
autoAttr["data-automation<br/>每个雕刻参数一条轨道"]
end
analysis --> carveAttr
analysis --> chainAttr
analysis --> autoAttr
subgraph SHARED["一个实现,两者读取"]
build["audioFxGraph.ts · buildFxChain"]
sched["audioFxAutomation.ts · scheduleChainAutomation"]
end
chainAttr --> build
autoAttr --> sched
build --> preview["预览<br/>实时 AudioContext<br/>attachElementFxChain"]
sched --> preview
build --> render["渲染<br/>无头浏览器中的 OfflineAudioContext<br/>applyAudioFxChain"]
sched --> render
preview --> heard["擦洗时听到的内容"]
render --> wav["处理后的 WAV<br/>+ chainTailSeconds 让混音允许尾音通过"]
wav --> mix["引擎 · audioMixer<br/>音量轨道在此处烘焙到 PCM 中,而非在图形中"]
mix --> out["渲染后的混音"]
edit["在播放中编辑属性"] -.->|MutationObserver| preview
雕刻本身的设置永远不会在播放时读取——它生成的效果链和轨道才是播放的内容。data-fx-carve 的存在是为了可以更改现有雕刻的强度,而无需从滤波器中反向推导。
在已雕刻的背景内部,信号首先经过凹陷,然后进行电平匹配,最后经过你自己添加的效果——这就是为什么你添加的限制器仍然充当最后的限制器:
flowchart LR
src["解码后的背景"] --> p1["峰值滤波器<br/>400 Hz"]
p1 --> p2["峰值滤波器<br/>1 kHz"]
p2 --> p3["峰值滤波器<br/>1.6 kHz"]
p3 --> g["增益<br/>电平匹配"]
g --> hand["你自己的效果<br/>例如限制器"]
hand --> dest["轨道增益,然后输出"]
l1["轨道 fx.n1.gain"] -.->|"该频段中语音<br/>电平的包络"| p1
l4["轨道 fx.n4.gain"] -.->|"背景整体<br/>闪避的程度"| g
静态雕刻是同样的图形,但具有固定值且完全没有轨道。
首先,找出问题所在
下表从“听起来很沉闷”开始——这假设有人已经听过并这么说了。当你拿到一个文件并被告知“修复它”时,你既没有这样的描述,也无法亲自监听,因此你必须进行测量。一条规则支配着所有情况:
单个未知人声的绝对频谱无法诊断。 共振峰为 ±10 dB,基频范围为 85–255 Hz,语句结尾会衰减 5–6 dB。 这些每项单独看起来都是缺陷,但每一项都是说话者的特征。
因此,进行比较,并且要与同一文件内的内容进行比较:如果存在干净的原始版本,则与之比较;否则与静音段落比较——在间隙中能听到的声音是叠加的,而间隙的频谱代表的是通道而非人声。与已发布的平均频谱或合成参考人声进行比较是行不通的:两个说话者之间的差异比大多数缺陷都要大,而在本指南背后的评估中,两个错误答案正是源自这种方法。
当没有原始版本也没有可用的静音时,静态音调缺陷确实是欠定的。说明这一点并提供符合的测量结果,而不是挑选一个并据此构建效果链。
命令、陷阱和实操配方:references/diagnosis.md。在诊断一个没有人描述过的文件之前,请阅读它。
从症状开始
一旦你知道了频段和类型,就说明音频的问题在哪。大多数有问题的音频都属于以下一两种,并且每种都有现成的解决方案:
| 听起来像 | 使用方式 |
|---|---|
| 底噪或低沉的嗡嗡声 | rumble-cut,或在 80 Hz 处使用 highpass |
| 沉闷、胸腔感 | 抑制沉闷 任务 (200 Hz) |
| 发闷、像隔着一层纸板 | 减少浑浊 任务 (250 Hz) |
| 单词难以辨认 | 增加清晰度 任务 (3 kHz),或雕刻背景 |
| 刺耳且令人疲劳 | 柔化刺耳 任务 (3.2 kHz) |
| 有些单词比其他单词响得多 | 压缩器上的 均衡度,或 均衡电平 |
| 句子之间的环境噪音 | room-gate |
| 语音和音乐冲突 | 旁白雕刻 — 不是对其中任何一个使用均衡器 |
| 干涩、听起来不像在房间里录制 | room-tight 或 room-natural |
| 只是“业余” | voice-clean,它按顺序包含以上四种 |
完整目录、每个预设包含的内容、频段词汇表,以及故意不涵盖的内容(去咝声、降噪、音色匹配):references/presets.md。
先减后加,滤波后调电平,电平后调关系,最后是特性和限制。每个步骤都会改变下一步听到的内容——在高速滤波器之前设置的压缩器将浪费资源去处理低频隆隆声。
根据问题选择效果家族,而非名称
滤波器(highpass、lowpass、peaking、lowshelf、highshelf)决定了轨道允许占据哪些频率。这是解决两个音源冲突的首要工具,因为冲突发生在频段中:背景和语音都想要 1–3 kHz,从背景中移除这些频段对背景的损害远小于整体调低对混音的损害。对语音使用高速滤波器是解决低频嗡嗡声的标准方案;低通滤波器则用于有意地使声音变暗或发闷。
动态处理(gain、compressor、limiter、gate)决定了轨道的电平随时间变化的方式。压缩缩小了响亮和安静之间的差距,使得安静的部分得以提升。限制器是一个上限——它不塑造任何东西,只保证没有信号超过它。门控移除低于阈值的声音,这就是你如何在短语之间静音环境噪音的方法。gain 是一个简单的电平级,当轨道需要让出空间时,自动化轨道将作用于它。
非线性处理(saturate、bitcrush)改变波形的形状,从而添加原本不存在的谐波。当轨道需要特性或质感而不是修正时使用它——记住,它是生成性的:它使薄弱的音源更密集,而不是更干净。
时间效果(delay、reverb、chorus、phaser)将轨道放置在一个空间或赋予其宽度。这些效果最容易破坏混音,因为一个尾音或一个失真的副本会占据语音所需的同一空间。将它们用于应该位于其他内容之后的元素,并保持湿声量低于单独听起来合理的水平。
效果链是串行的:每个效果处理前一个效果产生的信号。因此,校正性滤波应放在前面,特性处理在中间,限制器放在最后,这样它才能实际起到上限的作用。
旁白雕刻
解决的问题。 音乐背景下的旁白使得旁白难以被听清。本能的反应是闪避整个背景,这确实有效,但代价是背景失去了所有的临场感——音乐在整段旁白期间变得软弱无力。但语音并不需要整个频谱,它只需要它实际占据的那几个频段。雕刻仅处理这些频段,背景因此能保留其低频和高频,这样在语音仍然清晰可辨的同时,音乐性也得以保持。
它是一种关系,而非一种效果。 设置位于被处理的背景轨道上——它指定要“监听”的语音源,就像侧链压缩器一样:你选择哪个轨道会变轻,并指定什么使它变轻。永远不要对语音轨道进行雕刻。 语音对自身进行雕刻是一个错误,而不是一个微妙的混音选择。
针对所有语音,而非其中某一个。 sources 是一个列表,因为一个背景通常运行在一整个序列下——一个叙述者、一个采访回答、第二个主持人。在测量任何内容之前,它们会在背景自己的时钟上求和(mixCarveSources),因此一次分析涵盖了所有语音:频段来自所有存在的语音,包络线在任何语音出现的地方上升。在背景播放时从未出现的语音会被忽略;它们无法掩蔽背景。
一个旋钮控制一切。 strength 是 0..1 之间的值,并推导出所有其他参数:切割深度、频段数量、宽度、倾向于清晰度而非原始语音能量的程度、电平允许下降的幅度、目标电平低于语音的程度。这六个参数在任何真实混音中都是一起变化的——轻柔的雕刻意味着在少量频段上进行浅切割且闪避较少,而强烈的雕刻则是在更多频段上深度切割且闪避更多——因此它们作为一个关系被一次写入 carveProfile 中。默认值为 0.25——三个频段衰减 6 dB,具有 6 dB 的电平余量,可闻但不会听起来像有个洞。在 0.5 时,衰减达到 10 dB,此时雕刻开始被明显感觉为一种效果,而不仅仅是给语音腾出空间;再往上就是有意的领域了,用于响亮的背景下的轻柔语音。0 仅进行频谱雕刻——一个频段,完全没有电平匹配。
默认进行雕刻。 在叙述下播放的背景需要进行雕刻;这不是一个有时间才做的修饰步骤。放置好两个轨道,运行下面的命令,监听。只有当音乐下方没有叙述时——如音乐视频、标题卡、根据音乐剪辑的蒙太奇——才跳过它。
它始终跟随语音。 没有静态模式:固定的深度会在每个停顿期间削弱背景,一旦你听过两者,就没有理由再想要静态模式。每个值都会变成语音自身电平的包络——静音时背景不受影响,响亮段落时雕刻达到最大深度——这些被写为普通的自动化轨道,这就是为何这些轨道会显示在时间线上并可在事后编辑。
电平匹配是其中的一部分。 频谱雕刻无法解决背景比语音只是更响的问题。因此,雕刻还会测量背景高于语音的程度,并写入一个 gain 级:静态雕刻时保持为一个值,动态雕刻时由包络驱动。该包络特意缓慢释放——在单词结束瞬间弹回满音量的音乐听起来像是机器干的。
运行它。 在工作室中,雕刻是轨道效果架顶部的一个模块——语音、强度、动态以及它产生的分析,都在一个卡片内。每当另一个轨道可能是语音时,它都会出现,而一个具有恰好一个候选语音的背景会默认以默认强度进行动态雕刻:这正是叙述下的背景所需的效果,而该模块正是你更改或关闭它的地方。如果存在多个候选,则选择器会等待,而非猜测。无头模式——这是在你创作一个合成而不是编辑它时的路径:
node <SKILL_DIR>/scripts/carve.mjs --comp index.html
这就是整个命令。它会自行找到语音和背景,以默认强度进行动态雕刻,并打印其决策:
bed music-bed (名字看起来像音乐)
voice narration (唯一的剩余轨道)
carve strength 0.25 dynamic
bands 400Hz -6dB q1.4, 1000Hz -3dB q1.4, 1600Hz -3.17dB q1.4
level 216-point envelope, floor -6 dB
当自动选择错误时,使用 --bed / --voice(可重复)指定轨道,使用 --strength 调整强度,使用 --dry-run 查看报告而不写入任何内容。
如何选择轨道。 首先使用名称,因为这是你已经告诉它的信息,并且答案是可解释的——core 中的 classifyAudioName,与工作室自己的选择器使用相同的分类器,因此两者不会不一致。一个 ID 或文件名看起来像音乐(music、bgm、bed、score……)的轨道被视为背景;其他所有在其上播放且不是 SFX 形状的轨道都是语音。音频元素优先:只有当没有音频轨道可作语音,或组合中的每个 B-roll 片段都会被解读为有人在说话时,视频才会计入。**当无法……
…(原文已被截断)