mcpField
V
vox-skill

vox-skill

github.com/jaredcassoutt创意与媒体1257 次安装4 天前更新
代码仓库

关于 vox-skill

vox-skill 是使用 React 和 Remotion 制作纪实风格旁白影片的生产技能,覆盖脚本、配音计时、分层场景、音频处理、质检与渲染,并支持竖屏影片。

主要功能

  1. 01按节拍规划旁白
  2. 02生成配音与计时数据
  3. 03用 React 构建分层动画场景
  4. 04制作代码绘制的视觉素材
  5. 05混合并处理旁白与音乐
  6. 06渲染横屏或竖屏影片

适用场景

  1. 01根据主题制作旁白纪录片
  2. 02让场景切换与口播词同步
  3. 03制作动画图表和文档画面
  4. 04完整渲染前检查场景布局

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaredcassoutt/vox-editorial --skill vox-skill

安装后在 Claude Code 中输入/vox-skill确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE
README.md
example/
.prettierrc.json
cues.json
package.json
remotion.config.ts
script.json
timing.json
tsconfig.json
example/src/
index.ts
Root.tsx
example/src/scenes/
AnnexationScene.tsx
BaylorFoundingSequence.tsx
CloserScene.tsx
FoundersScene.tsx
NamingScene.tsx
OlderThanStateScene.tsx
PetitionScene.tsx
RepublicScene.tsx
SigningScene.tsx
example/src/shared/
fonts.ts
pieces.tsx
system.tsx
texas.ts
timing.ts
references/
scene-patterns.md
vertical.md
visual-system.md
scripts/
captions.py
cutout.swift

详细介绍

Remotion 纸张 B-roll

输入一个主题,输出一部 1920×1080 的配音影片,以 React 构建。整体风格是编辑式纪录片:靠内容本身引人入胜来抓住观众,而不是靠大声喧哗。

所有动画都由代码驱动。无需经过 After Effects,也无需拖动时间轴。你只需编写组件并调整属性,就能导演这部影片。

先选择合适的技能

你的需求使用
横屏 1920×1080、React/Remotion、以代码绘制图表和报纸此技能
适用于 TikTok、Reels 或 Shorts 的竖屏 1080×1920此技能、--vertical — 参见 references/vertical.md
任意配音解说视频,制作流程尚未确定explainer-studio
配有声音设计、快节奏剪辑的产品广告fastcut-ad

决定质量的因素,按重要性排序

  1. 作为基础的事实。 一个真正出人意料、可核查的反转。没有它,影片便没有存在的理由,再精湛的工艺也无法补救。
  2. 配音稿。 它是节奏和主线。每次切镜都与一个说出的词同步。
  3. 层次。 画面中是否有三个相互独立运动的层次。
  4. 其他一切。

按这个顺序分配精力。

制作流程

共十个阶段。第 N 阶段的产物完成之前,不要开始第 N+1 阶段。

1. 找到反转,写出节拍

找到那个结果与观众预想恰恰相反的事实。不是一个统计数字,而是一个矛盾。

写出 8 到 12 个节拍,每行一个想法。 脚本就是时间轴。一个节拍是一句话或一个分句,足以承载一幅画面。

钩子是第 1 个节拍,并且要隐去主题。 不要出现专有名词:不能提国家、人物、机构或年份。它要陈述一件不可能成立的事。最后一个节拍要回答这个确切的问题。用两个问题检验草稿:观众能从钩子中说出主题吗?如果能,就重写。最后一句是否偿还了钩子留下的悬念?如果没有,其中一句就有问题。

将节拍写入 script.json:

{"topic": "...", "beats": [{"id": 1, "narration": "This university is older than the state it stands in."}]}
2. 先生成语音,再确定时间

必须在任何动画之前生成配音,因为它的时长决定每一帧的数量。

python3 -m venv .venv && ./.venv/bin/pip install -q edge-tts
./.venv/bin/python ~/.claude/skills/vox-skill/scripts/gen_vo.py

这会生成 vo/beat_NN.mp3、合并后的 public/audio/vo.mp3,以及 vo/timing.json,其中包含每个节拍的开始时间、结束时间、时长、帧区间和逐词边界。下游所有时长都从该文件读取。绝不要猜测帧数。

语音默认使用 en-GB-RyanNeural,速率为 +4%。使用 VOICE= 和 RATE= 覆盖。提交之前值得先试听,因为影片完成后重新配音,只有在你已完成第 3 步的情况下才算轻松:

语音风格
en-US-AndrewMultilingualNeural较新的 HD 模型,温暖而自信。最佳默认选择。
en-US-ChristopherNeural标注为“可靠、有权威感”。经典旁白风格。
en-GB-RyanNeural / en-GB-ThomasNeural英式口音,风格更冷静
en-US-SteffanNeural最接近直白的新闻播报

语音模型没有混音处理重要。 参见第 6 阶段。

3. 从时间数据推导时间点,不要手动填写

根据波形手动微调帧数,只在语音不变时才有效。只要更换语音、速率或一个词,所有数字都会出错。

编写 cues.json,标明每个元素出现时对应的单词,然后据此生成时间模块:

python3 ~/.claude/skills/vox-skill/scripts/sync_timing.py --check   # inspect
python3 ~/.claude/skills/vox-skill/scripts/sync_timing.py           # write src/shared/timing.ts

场景通过 D 获取时长,通过 CUE 获取每个起始帧。此后重新配音只需运行三条命令并重新渲染,而不必花上一个下午。--check 还会检查每个 cue 是否超出场景时长。

4. 将节拍映射到场景

场景数量少于节拍数量。 两三个节拍通常可以共用同一幅持续呈现的画面;旁白继续时,场景保持不变会显得更从容。十个节拍对应七个场景,是不错的比例。

场景时长是其对应节拍的帧区间总和,取自 vo/timing.json。编写任何组件之前,先写下映射关系。

5. 制作素材

素材分为三类,按优先顺序排列。

代码绘制(最佳)。 报纸、图表、文档、地图、账本、打字机文字、划掉的列表。无需文件、无需抠图,可无限调整,还能原生动画。优先选择这种方式。参见 references/scene-patterns.md。

透明 PNG 抠图。 通过 Vision 在本地抠出主体,免费且离线:

swiftc -O ~/.claude/skills/vox-skill/scripts/cutout.swift -o /tmp/cutout
/tmp/cutout raw/portrait.jpg public/scene-04/portrait.png
python3 ~/.claude/skills/vox-skill/scripts/halftone.py public/scene-04/portrait.png

halftone.py 会应用纸张效果:降低饱和度、提高对比度、添加网点、让墨色偏暖。它能让现代照片呈现出 19 世纪纸张上的效果。

如果 Vision 找不到主体,则使用 PIL 作为备用方案:以约 55 的容差从四个角进行泛洪填充,然后仅保留最大的连通不透明区域,以保留衣领等内部填充区域。切勿从边缘中段开始泛洪填充,否则会渗入较宽的主体。

透明视频。 转码为带 alpha 通道的 VP9,然后使用 <OffthreadVideo transparent />:

ffmpeg -i in.mov -c:v libvpx-vp9 -pix_fmt yuva420p -vf scale=1920:-2 -b:v 0 -crf 32 out.webm

纸张背景应控制在 5 MB 以内,分辨率严格为 1920×1080。28 MB 的背景会让 Studio 丢帧,而丢帧会导致音频与画面失去同步,影响调试。

6. 基于固定的视觉系统构建场景

只需确定一次视觉系统,此后每个场景都是它的变体。完整的配色方案、字体比例和动画规则见 references/visual-system.md。以下三点不可妥协:

每个场景都由三层组成。

  1. 背景 — 每个场景都使用相同的纸张纹理,覆盖整个画面,zIndex 0,上方叠加柔光效果。背景绝不改变。正是这种连贯性,让七个场景看起来像一部完整的影片。
  2. 中景 — 主体以抠图形式加入,每个主体都带有偏移的红色标记笔描边。
  3. 前景 — 遮挡主体的下半身,为镜头构图提供锚点。

缺少前景的场景看起来就像幻灯片。遮挡才能营造出景深。

红色标记笔描边是标志性元素。在图像后方渲染抠图的纯色剪影,并偏移几个像素,主要水平偏移,让描边从主体一侧显现:

<div aria-hidden style={{
  ...sameBoxAsImg,
  backgroundColor: "#E04329",
  maskImage: `url(${assetUrl})`,      WebkitMaskImage: `url(${assetUrl})`,
  maskSize: "100% 100%",              WebkitMaskSize: "100% 100%",
  maskRepeat: "no-repeat",            WebkitMaskRepeat: "no-repeat",
  transform: `translate(calc(-50% + ${strokeX}px), ${rise + strokeY}px)`,
}}/>
<Img src={assetUrl} style={{ ...sameBoxAsImg, transform: `translate(-50%, ${rise}px)` }} />

strokeX 约为 ±26,strokeY 约为 8。根据主体所在位置调整方向:画面左侧的主体向左描边,右侧的主体向右描边。

元素应当登场,而不是淡入。 使用 spring() 从约 390px 处上升,参数为 {damping: 18, mass: 0.75, stiffness: 110},并叠加持续 16 帧的透明度渐变。图表和签名通过 interpolate() 对 strokeDashoffset 进行绘制。错开元素的入场时间,确保没有两个元素在同一帧出现。

每个场景都使用带有精调属性的 zod schema。 为每个元素设置位置、缩放比例以及起始帧和结束帧。这样才能在 Studio 中调整影片,而不必每次微调都重新编译。

7. 评分并完善音频

判断背景音乐时,要听笔记本扬声器实际播放出来的效果,而不是看频谱图。

一段真实的纪录片配乐有 93% 的能量低于 250 Hz。照搬这个目标,会让背景音乐在所有没有低音炮的扬声器上都听不见,因为手机和笔记本会大幅衰减 ~200 Hz 以下的声音。要比较音乐在 250 Hz 以上的音量与语音在 250 Hz 以上的音量。这个数字就能决定听众是否听得到音乐。

# 语音片段与两句台词之间的间隙,均应用高通滤波
ffmpeg -ss <speech> -t 1.5 -i mix.wav -af "highpass=f=250,volumedetect" -f null /dev/null
ffmpeg -ss <gap>   -t 0.3 -i mix.wav -af "highpass=f=250,volumedetect" -f null /dev/null
250 Hz 以上的音量差评估
30 dB 或更高听不见;听众会说“没有音乐”
18-22 dB听得到且不突兀。目标值。
低于 8 dB挤压旁白空间

score.py 会将此项显示为小扬声器扣分,并列出整段内容中四个位置的可听音量,因此在渲染前就能发现一段音乐静音的情况。

python3 ~/.claude/skills/vox-skill/scripts/score.py music/bed.wav --seconds <SEQ_SECONDS>
bash   ~/.claude/skills/vox-skill/scripts/mix_audio.sh \
       public/audio/vo.mp3 music/bed.wav /tmp/mix.wav -20

score.py 还会打印自身的频谱平衡与这些目标值的对比。背景音乐应裁剪到序列长度,而不是语音长度,这样才能覆盖结尾停留画面。

背景音乐通过 music_db 偏移量设置音量,并在语音播放时通过侧链再降低约 6 dB,这样它就会在间隙中逐渐回升,而不是始终保持平直。**-18 到 -22 dB 是教科书式的数值,前提是混音覆盖全频段。**如果背景音乐的低频占比过高,那么在关键频段,它的音量会低 30 dB 以上。应根据测得的音量差设置偏移量,而不是照搬惯例。如果背景音乐的中频足够突出,通常设为 -12 dB 左右。

**短片要留意编排曲线。**曲线表示各部分占整段内容的比例,因此某部分在 0.20 处才达到完整状态,意味着在一段 65 秒的成片中,前十三秒都没有这部分内容。开场只有次低音无人机音效,会让人觉得“没有音乐”;而所有人都会评判开场。要从一开始就让各个音乐部分保持一定音量。

mix_audio.sh 还会对旁白进行母带处理,这也是“文本转语音”和“纪录片旁白”之间大部分差距的来源:

处理原因
85 Hz 高通滤波去除语音中不会用到的隆隆声
350 Hz 处降低 2 dB消除所有神经 TTS 都有的箱音
3 kHz 处提高 2.5 dB增强临场感,让辅音清晰落地
6.8 kHz 处降低 3 dB去齿音;TTS 的嘶嘶声比真人发声更刺耳
3:1 压缩让较轻的分句与较响的分句音量一致
loudnorm -16 LUFS适合手机和笔记本播放

让合成内容使用混音后的文件,而不是原始 VO 文件。

8. 渲染前检查静帧

**每个场景渲染一张静帧,并在开始完整渲染前放到联络表上检查。**一段 70 秒的影片需要渲染 10 分钟;静帧只需几秒。各种碰撞、换行和比例错误都能在这里显现。

for f in 200 541 695 1056; do
  npx remotion still src/index.ts <SequenceId> out/qc/f$f.png --frame=$f --log=error
done
ffmpeg -y -i out/qc/f200.png -i out/qc/f541.png -i out/qc/f695.png -i out/qc/f1056.png \
  -filter_complex "[0:v]scale=720:-1[a];[1:v]scale=720:-1[b];[2:v]scale=720:-1[c];[3:v]scale=720:-1[d];\
[a][b]hstack[t];[c][d]hstack[u];[t][u]vstack" out/qc/sheet.png

每一帧都要选场景内容最丰富的时刻,而不是第一帧。这一轮检查通常能发现:

现象原因
印章或标签叠在主体上,难以辨认红色叠在墨迹上。把它移到干净的纸面上,不要改样式。
两个元素互相贴着绝对位置发生了偏移。移动其中一个,不要把两个都缩小。
跟踪字幕在短语中间换行添加 whiteSpace: "nowrap"。
标题换行同样的处理,并将字号缩小 15–20%。
代码绘制的 SVG 图像显示得很小viewBox 范围比路径宽松。将其边界设为路径的实际范围。
形状看起来像另一种物体剪影需要有足够的宽度。肩膀太窄会像钥匙孔,而不是半身像。
9. 组装并渲染

使用 <Series> 将场景串联起来,每个场景对应一个 <Series.Sequence>,时长采用第 3 步确定的时长。

位置属性应与每个 <Composition> 的 defaultProps 保持一致。时序属性需在序列中重新调整,因为单独调试的场景几乎总是比它必须适配的节拍窗口更长。

通过标志控制音频,使合成默认静音,并单独交付各音轨以供混音:

const BAKE_AUDIO = false;   // true → VO at 1.0, music ducked to ~0.2, 24f fade in, 50f fade out
npx remotion studio                                              # preview and tune
npx remotion render src/index.ts <SequenceId> out/final.mp4      # render
ffmpeg -i out/final.mp4 -c:v copy -an out/video.mp4              # strip the silent track

只有在每个 <Composition> 的 defaultProps 都是内联对象字面量,而非导入的变量,并且项目根目录存在 .prettierrc.json 时,Studio 才能将属性编辑保存回磁盘。两项缺一不可。配置错误会导致每次重新加载时都丢失所有调试结果。

10. 选择海报帧

绝不要使用默认缩略图。 按这种方式制作的影片,第 0 帧只有空白纸张背景,还没有任何内容出现——这是文件中最糟糕的一帧,却是所有播放器都会选用的一帧。

python3 ~/.claude/skills/vox-skill/scripts/pick_thumbnail.py \
        out/film.mp4 out/thumbnail.png --embed --report

它会为每个候选帧评分,并选出元素最丰富、构图最完整的时刻。“有意思”指的是多个不同元素分布在整个画面中,而非单纯的墨迹覆盖率——深色前景色带填满画面底部三分之一的镜头虽然覆盖率高,却毫无看点,因此评分的主要项是元素在网格中的空间分布。其余评分项包括边缘密度、设有上限的覆盖率,以及是否出现红色点缀。

--embed 会将其作为封面附加到 mp4 中。--report 会打印候选列表,方便你在更喜欢第二名时选用它。制作竖屏剪辑时传入 --grid 5,否则评分会偏向选择内容位于平台界面后方的帧。

快速参考

项目值
输出1920×1080,30fps,MP4
纸张底色 / 墨色 / 红色点缀#DAD9D5 / #1A1A1A / #E04329
标题字体Arial Black,紧凑字距,全部大写
衬线字体PlayfairDisplay、PTSerif(报纸、文档)
打字机字体SpecialElite(结尾句)
入场弹簧参数{damping: 18, mass: 0.75, stiffness: 110}
上升距离360–390px,从下方上升
不透明度渐变16 帧,Easing.out(Easing.cubic)
描边偏移strokeX ±26,strokeY 8
投影drop-shadow(0 28px 26px rgba(55,45,31,0.28))
搭建项目scripts/scaffold.sh <dir> <SequenceName>
背景音乐scripts/score.py out.wav --seconds N
主音轨 + 闪避scripts/mix_audio.sh vo.mp3 bed.wav out.wav -20
人声下的音乐根据测得的 >250 Hz 频段分离度设置,约 18–22 dB。通常为 -12 dB。
音乐听起来平淡提高 --motif,而非音量
竖屏剪辑scaffold.sh <dir> <Name> --vertical,然后查看 references/vertical.md
字幕(竖屏必需)运行 scripts/captions.py,然后添加 <Captions />
海报帧scripts/pick_thumbnail.py out.mp4 --embed
“我听不到音乐”调整推子前,先测量 250 Hz 以上的频段
最终响度综合 -16 LUFS

常见错误

错误修正方法
猜测帧数,之后再修正同步从 vo/timing.json 读取每段时长。配音是时间基准。
每个节拍对应一个场景合并节拍。旁白继续时,让画面保持不变,能传递出自信。
场景之间背景发生变化背景绝不变化。固定不变的纹理能统一整部影片。
没有前景层添加遮挡元素。没有它,镜头就是幻灯片,而不是画面。
为代码可以绘制的内容寻找图片素材图表、报纸、文档、地图和列表始终更适合用代码制作。
所有元素都在同一帧进入错开 20–30 帧。同时进入会显得像套用模板。
元素淡入让它们弹出。淡入显得过于普通。
从常量导入 defaultProps将对象字面量直接写入代码,否则 Studio 会悄悄丢弃你的调整。
硬编码位置,而不是使用 props每个位置、缩放和帧数都要放进 zod schema。
使用 28 MB 的纸张背景裁剪并调整尺寸,精确到 1920×1080,且小于 5 MB。
在场景 props 中输入帧数在 cues.json 中为词语命名并生成帧数。重新配音不应意味着重新调整。
只因旋律好听就选择音乐根据频谱选择。如果音乐的频率范围在 200-4000 Hz,无论音量多低都会与人声冲突。
音乐音量始终保持不变让音乐自动降低音量。间隙中音量渐起的背景音乐听起来像经过混音;音量平直则像生硬地贴上去。
直接交付原始 TTS先进行母带处理。均衡和压缩链比选择哪种声音更重要。
交付默认缩略图第 0 帧是空白背景。运行 pick_thumbnail.py。
竖版剪辑没有字幕默认会静音观看。将字幕直接嵌入视频。
竖版画面中并排放三个主体每个只有 340px。将它们上下堆叠,或分散到不同节拍中。
渲染影片后才查看效果先为静帧制作联系表。渲染花十分钟,也看不出静帧无法呈现的内容。
主体没有公有领域形象参考在同一个方框中将其画成墨迹形态。绝不要借用别人的脸。

警示信号

  • vo/timing.json 还不存在,就准备编写场景组件
  • 准备为图表、文档或地图打开图像生成器
  • 钩子直接点出主体
  • 最后一个节拍没有回答钩子提出的问题
  • 场景只有两层
  • 因为弹簧效果“看起来太跳”而准备淡入(调整阻尼,不要淡入)
  • 还没看过每个场景的静帧,就准备运行完整渲染
  • 仅仅因为版式需要,就在画面中放入无法核实的数字或引文

参考资料

  • references/visual-system.md — 调色板、字体、动态效果,以及完整的分层模型
  • references/scene-patterns.md — 七种可复用的场景原型及可运行代码
  • scripts/gen_vo.py — 旁白、词语时间点和帧窗口
  • scripts/cutout.swift — 使用 Vision 提取主体并生成透明 PNG
  • scripts/halftone.py — 为抠图添加纸张/半色调效果
  • scripts/sync_timing.py — 将 cues.json 和 timing.json 同步到 TypeScript 计时模块
  • scripts/score.py — 为人声留出频谱空间的紧张纪录片风格背景音乐
  • scripts/mix_audio.sh — 旁白母带处理链和侧链闪避
  • scripts/pick_thumbnail.py — 选择并嵌入内容最丰富的帧作为封面
  • scripts/captions.py — 根据旁白的词语边界生成嵌入式字幕
  • scripts/scaffold.sh — 可运行的 Remotion 项目骨架(9:16 使用 --vertical)
  • references/vertical.md — TikTok、Reels 和 Shorts 的差异说明

评论

支持 Markdown