real-sfx:为动态视频添加真实录音音效 是做什么的?
目标:让影片听起来像是拥有专业音效库的剪辑师制作的效果。为此:
- 音效绝不使用合成。 音源来自:用户的音效库(
config.json)和 uisfx(一个 CC0 许可的界面音效包,本身是合成的,这是唯一的例外,仅用于界面时刻;可通过--ui library关闭合成)。若没有适合的音效,则该时刻保持静音并记入报告。这会覆盖 motion-standards 中的“无音轨:合成”规则。 - 你不能听到音效。 音效通过数字、文字和图片来读取(
references/listening.md)。不要将某个被选中的音效描述为“听起来如何”,而应说明它通过目视和测量检查了什么,以及用户应聆听什么。 - 保留音乐和人声,当它们承载影片时。最终输出始终是一个新的 MP4 文件 以及一个单独的 WAV 音轨。
- 避免加倍。 渲染中内嵌的合成音效将被真实音效替换,而不是在其下方叠加(第一步)。评论会将其量化(
doubling轴)。 - 不要修改项目或技能文件夹。 将所有工作文件放在自己的文件夹(
work/、result/)中。
在 Video Director 中使用
(此副本在 Video Director 的副本中新增;Clipwing 的原版中没有。UPSTREAM.md 列出了所有更改。) 此副本位于 Video Director 技能中,并由其 Sound 阶段执行:../references/sound-stage.md,“声音工程”。该阶段决定哪些时刻需要音效,将仅这些时刻提供给本技能(build/audio/sfx/events.json),固定用户选择的录音(sound_handoff.py pins),并使用 --ui library、--no-loudnorm 及无纹理层、无背景底垫运行,最后仅取回效果音轨和已解析的提示(sound_handoff.py collect)。请遵循这些步骤,并仅就它们未涵盖的选项阅读本文件。将 REAL_SFX_CONFIG、REAL_SFX_INDEX、REAL_SFX_TASTE、REAL_SFX_RATINGS、REAL_SFX_STEADY 和 REAL_SFX_STYLES 设置为 ~/.config/real-sfx/ 下的路径(插件会将每个版本安装到一个新文件夹,因此写入此处的任何内容都会在下一次更新时被遗弃),并且不要使用 --remember(它会写入此文件夹,learn_style.py 也是如此,学习到的样式应复制到 ~/.config/real-sfx/styles/)。
快速路径
首先执行 python scripts/setup.py check(检查 ffmpeg、主库/备用库、索引、uisfx)和 python scripts/look.py FILM.mp4 --out work/look(生成带有时间和原始音轨的帧)。然后,根据你拥有的内容:
# 1. 项目有事件列表(docs/cues.json, scripts/cues.json)或有合成其音效的脚本(audio.cjs)
python scripts/from_cues.py PROJECT_FOLDER|docs/cues.json --video RENDER.mp4 --out work/cues.json
# 2. 屏幕录制,无列表:ui_events.py ...(参见 references/screencast.md),然后 from_cues.py work/ui/events.json
# 3. 仅视频:find_moments.py FILM.mp4 --out work/moments; suggest_cues.py work/moments/moments.json --out work/cues.json
# 3b. 柔和彩色动画,无列表(UI 预览、插图、动态图形):自动化能检测到 5-6 次移动,总共 40 次。
# 步骤 2,“无列表的静默动画”:find_moments.py FILM --out work/moments --fine; look.py FILM --at T --span 2.4
# --step 0.1 --out work/at.png; 根据帧自行创建事件列表(参见 references/typed-cues.md)-> from_cues.py - --ui library
python scripts/mix.py work/cues.json --out result/film_sfx.mp4 --plan # 预览每个音效的位置;然后不带 --plan 运行
python scripts/critique.py result/film_sfx_resolved_cues.json result/film_sfx_sfx_track.wav
python scripts/levels.py result/film_sfx_resolved_cues.json result/film_sfx_sfx_track.wav --json work/edits.json
# 一部音效仅由 SFX 构成的影片(没有自己的音乐或人声):在空白处添加小音效,然后再次运行 mix.py(步骤 4,“小音效层”)
python scripts/texture.py work/cues.json --moments work/moments/moments.json
# 同一部影片:背景底垫(一个长的黑暗真实录音,铺满整个影片;步骤 4,“背景底垫”),然后再次运行 mix.py
python scripts/bed.py work/cues.json [--list]
# 与九个已完成 SFX 卷轴对比(步骤 6,“与基准对比”):不产生评分,不影响最终裁决
python scripts/critique.py RESOLVED.json WAV --style clean-sfx-refs
# 廉价的人类耳朵测试(步骤 6):生成一个包含评论问题和一个点击裁决的页面;以及遮罩模型
python scripts/listen_page.py build work/listen --moments result/film_sfx.mp4 --critique RESOLVED.json WAV
python scripts/listen_page.py serve work/listen [--lan] # 然后:listen_page.py results work/listen --log
python scripts/mix.py work/cues.json --out result/film_sfx.mp4 --masking # 被其余部分隐藏的音效(需要虚拟环境)
项目文件夹会自行查找其事件列表(docs/、scripts/、assets/);合成脚本仅在无列表时被读取。在编辑表格前选择所有 from_cues.py 标志:重新运行它会清除手动编辑(然后使用 sheet.py apply)。
影片中有语音:mix.py ... --voice-safe(深度 normal;当有许多轻柔纹理时使用 --voice-depth light,仅对罕见音效在暂停处使用 deep:它会将界面完全从混音中移除)。音乐承载影片:--duck。响度:默认重建的基底与原始匹配;自定义:--target-lufs NUMBER。
无索引,或库已移动:python scripts/build_index.py,然后 python scripts/listen.py tag-index。无 uisfx:setup.py init --uisfx。另一台机器:setup.py scan FOLDER,setup.py add-library NAME PATH [--tier main|extra] --index。从技能文件夹运行脚本;引号路径中包含空格或非拉丁字符;在 Windows 上使用你自己的 Python 及非拉丁路径:PYTHONUTF8=1。在 PowerShell 中,引号会破坏 JSON 参数:将它们放入一个文件中。
音效来源
| 需要什么 | 来源 |
|---|---|
| 基础:呼啸声、碰撞声、上升音效、咔嗒声、铃声、键盘声、纸张、拟音、环境音 | 主音效库(tier: main,路径在 config.json 中)。一个带有 catalog.json / catalog.csv 的分类包,为每个音效提供类型和源录音,避免仅凭文件名猜测 |
| 主库缺少的:低音下落、硬币声、短时故障音 | 备用音效库(tier: extra,结果中标记为 ~):仅当主库中少于三个匹配音效时使用 |
| 具有自身特定角色的设计包 | 在自动选择中不会请求角色 granular、atmosphere、power、music_transition、voice 的音效:显式使用它们(--role、"pick": {"role": ...}) |
| 界面时刻:点击、滴答声、窗口、通知 | uisfx(--ui auto)当列表本身命名了界面类型(点击、铃声等)时:from_cues 会说明原因和位置(routes: key -> library:click, pop -> uisfx:select) |
| 打字音 | 始终使用音效库的真实键盘,即使使用 --ui auto;from_cues 自身会将连续 8 个以上间隔 35–170 毫秒的 click 事件识别为打字(--no-typing-detect) |
| 物理接触:球、脚步、珠子、物体 | 始终使用音效库:例如球落在地板上是沉闷的撞击声,而不是 select(配方如下) |
尚无音效库:仅使用 setup.py init --uisfx 可提供 78 个 CC0 界面提示。值得检查的免费真实录音来源(阅读各许可):Freesound(CC0 过滤器)、Sonniss GDC bundles、Mixkit、Pixabay 音效。
“来自我的库”、“无合成”或“声音听起来很合成”:使用 --ui library。在带有 uisfx 界面音效的项目中强调标志或“cha-ching”声:使用 --from-library pop,bell(类型名称来自列表);对于单个音效:sheet.py set --at T --label bell id=65。在有调性音乐下:
- uisfx 重复音调以及音效库中有调性音效(铃声、砰砰声、玻璃声、硬币声)的音高步进会在项目有乐谱时自动关闭(
--ui-pitch off|cycle); - 真实音效的音高会与音乐音符对齐:
mix.py会获取表格中的music_ref(无效果的背景底垫、音乐主干)或--music-ref FILE,并将一个有调性音效移动最多 2 个完整半音加上使它与音符对齐的百分音(总共最多 2.5 个半音);噪音将被忽略(--no-tonal-fit;scripts/tonal.py SOUND --music FILE --at T显示音符); - 作者自己频率的间隔(
tick(t, 1800 * 2 ** (4 * i / 12)))会成为替换音效的音高步进(melody:)。
工作流程
1. 现有音轨中已有什么
look.py 显示原生音轨的频谱(语音是短语带、音乐是带节拍的地毯、静默是黑色);listen.py voice FILM.mp4 给出语音和音乐的占比。三种情况:
- 作者的音乐或语音:它们被保留,SFX 叠加在上面(
--duck/--voice-safe)。 例外(某人对真实影片的答复,2026-10-19):影片被送来“进行音效设计”,其原生音轨仅包含音乐(listen.py voice:语音 0%),并且此人希望 去掉 音乐,就像他们自己的干净 SFX 卷轴一样。制做一个无音乐版本(from_cues.py ... --ui library --drop-original,然后bed.py --id ... --level -46,使用一个喜爱的氛围音效),如果有用,再制做一个有音乐版本,并说明哪个是哪个。在三者的盲选(原始 / 音乐上叠加效果 / 仅效果)中,此人选择了最后一个,没有任何不满。尽管没有烘焙的合成音(语音 0%,音乐 84%,攻击点与事件不匹配:7% 对 9% 的随机概率),from_cues仍会在这样的影片上打印BAKED::这是误报。 - 烘焙在音轨中的合成音(项目是为合成配乐编写的):在合成音上叠加真实音效会造成加倍。
from_cues.py会自行检查这一点(BAKED:、unbake:),并在不触动语音和音乐的情况下从基底中移除合成音;具体方法以及当脚本失败或合成音位于音乐主干内时该怎么做(--extra-synth):请参阅references/project-audio.md(一旦输出中出现BAKED、WARN stems、unbake: not possible或项目使用脚本合成其音效,就阅读它)。构建后,critique.py会打印doubling轴:该层与原始音轨和新影片的相关性(应约为 0)。 - 屏幕录制窗口中播放视频片段的声音:原生音轨,保留;这些窗口内的事件将被丢弃。
2. 查找时刻(从最佳到最差来源)
- 项目的事件列表或合成脚本 →
from_cues.py(见下文)。详细信息:references/typed-cues.md。 - 屏幕录制 →
references/screencast.md(ui_events.py区域,将按键与字符匹配,片段声音)。 - 带有
data-start的 HyperFrames 且无列表:from_hyperframes.py PROJECT --out work/moments.json。如果输出“1 moments”但包含 18 个元素,则表示运动不遵循data-start:寻找合成脚本。 - 仅视频:
find_moments.py FILM --out work/moments [--roi x,y,w,h] [--from A --to B] [--fine];类型有cut、burst、onset、settle(settle也会在空白帧或静止循环上触发:查看帧);look.py --moments ...绘制时刻的帧。骨架:suggest_cues.py(references/recipes.md);草稿是起点,不是最终答案。--fine用于柔和彩色动画(渐变、发光、改变颜色而非亮度的柔和形状):它还会读取色度变化,时刻会获得src=chroma|luma;在三部这样的影片上,它找到了代理从帧中记录的事件的 33–69%(在 80 毫秒内),而单独使用亮度只能找到 13–25%;候选项数量多 3–4 倍(每 10 秒 15–25 个)。这些是候选项:帧决定最终选择。 - 音乐下:
beats.py FILM --out work/beats.json --plot work/beats.png;如果usable: true,则mix.py --snap-beats(±50 毫秒)。拍点网格 会与底鼓对比检查(低频 < 125 Hz 部分单独处理):它会打印在 40 毫秒内落在底鼓上的拍点数量。落在底鼓旁边的网格(轨道捕捉到镲片上,“偏离 0.22 秒”)会被移动到拍点上;落在空处的网格会被标记为usable: false,并且--snap-beats会忽略它。--plot绘制频带、底鼓(绿色标记)和网格(红色线条):标记应位于线条上,否则不要信任网格。如果音乐属于你并且可以匹配画面:beats.py FILM --suggest-offset work/moments.json指示应将音乐移动多少以使剪辑和碰撞点落在拍点上(仅供参考;音乐本身不会被触动)。
from_cues.py:python scripts/from_cues.py LIST|PROJECT_FOLDER|SCRIPT.cjs|- --video RENDER.mp4 --out work/cues.json(- 从 stdin 读取列表;PowerShell 的 BOM 能被理解)。它自行执行和说明的内容:
- 是否为正确的渲染:列表与画面不匹配(
VERDICT);一个设计时列表与一个根据语音时间调整的渲染:从项目的锚点构建时间映射(包含anchors或WARP的timing.json、src/timing.js),或从合成主干的开端点构建,每个事件落在自己的开点上(time map);--time-map FILE、--warp off|stems|audio; - 列表下的合成音:一个呼啸声落在合成音的开点上,其长度从该点测量(
retime:);如果该开点属于另一个声音(咔嗒声、铃声)或列表时间已经是呼啸声的中心,则不移动:以原因命名;没有对应列表事件的合成开点会被列出(--add-unlisted tick将它们转换为事件); - 音色:被替换的呼啸声的亮度(合成层的质心)会输入音效选择(
timbre:);--tone airy|dark手动设置; - 未知类型(
bead、soft):它会显示项目如何定义它。用一行说明,无需文件:--kind bead=click,--kind soft=pop,一个带有限制的角色--kind bead=role:click,max_dur=0.3,onsets_max=1,--kind bead=slices:740,763(来自这些录音的单个音效;最安静的那些,音量低 12 dB,不会被选用),--map '{"glass":{"ids":[65,54],"role":"chime"}}'(ids轮流使用;一个元素{"id":800,"slice":[0.78,0.12]});--remember保存它。摘要:declared N events: M voiced, K silent;评论会将静默类型计为...