1实现原理 · 为什么它能做到
默认路线是「在页面里用代码合成」:把模板贴进动画页的 <script>,配乐与画面共享同一份 BPM/SECTIONS/EVENTS,因此每个动作的声音落在同一帧上——无 key、无音频文件。
Paste `templates/score.js` into the animation page's `<script>`, before the live-preview block at the bottom (the starter marks the spot), and set:
音乐跟随画面的机制是速度图而非手动对表:每个场景分配整数小节(约 70–110 bpm),于是每个剪切天然落在强拍上。
- **Tempo map.** Each scene gets a whole number of bars (tempo flexes per scene, roughly 70-110 bpm), so every cut lands on a downbeat.
剪切用和声而非音量标记(场景走向属和弦、剪切到达主和弦),并给出实测数值说明为什么不能加音量:+7~10 dB 的跳变会被用户指为惊吓,+3~5 dB 配和声变化才读作干净剪辑。
- **Mark cuts with harmony, not volume.** Each scene walks home to the dominant (G) and the cut arrives on the tonic (C). A loud accent on a cut (bass + bright chime at once) reads as a jump scare: users flagged a +7 to +10 dB jump as startling; +3 to +5 dB with a chord change reads as a clean edit.
自动校验把「对齐」与「不惊吓」变成两个可失败判据:逐事件报告到最近强起音的偏移与进入时的响度跳变,偏移超 ±100 ms 判 OFF-BEAT、跳变超 +6 dB 判 STARTLE;并跳过开头一秒与结尾淡出。
It fails on OFF-BEAT (more than ±100 ms, ~3 frames) or STARTLE (more than +6 dB versus the second before). The opening second and the final fade are skipped (no "before" to compare against; a fade-out is not a cut).
使用非自制音乐时方向反转:画面跟随音乐——渲染、混音、跑检查,再按报告偏移移动页面 SCENES 的场景边界并重渲;因为场景函数只接收本地时间,移动边界不动场景代码,所以这个循环很便宜。
With music you don't control, reverse the direction: **the picture follows the music.**
能力边界被主动声明:代码合成擅长音乐盒、类钢琴拨弦、pad、铃、轻打击,做不了写实人声/管弦/类型化制作——需要时改用 AI 音乐或真曲。
Synthesized in code: music box, piano-like plucks, pads, bells, soft percussion are fine. Realistic voices, orchestras and genre production are not; use AI music or a real track for those.
AI 音乐支路需要用户提供的 API key,由 agent 直接 curl 外部生成服务(OpenRouter 上的 Lyria),把音乐描述发出去——这是本 skill 唯一读凭证并外发素材的分支,且被定位为「只在被要求时」。
Use when the user wants something richer than a synthesized score. Needs an API key.
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| cli | ffmpeg / ffprobe(分析视频与音频:抽灰度帧、抽 PCM;AI 支路里还用于混音) |
| api | OpenRouter 上的 Lyria(可选 AI 音乐生成,stream:true 收 base64 音频) |
| api | 该端点的凭证:从环境变量读取并放入 Authorization 头(仅 AI 音乐支路) |
| package | 兄弟 skill javascript-animation 的 render.mjs(离线渲染 SCORE 为 WAV 并 mux 进 MP4) |
| network | Web Audio API(浏览器内置,用于合成与离线渲染;非外部服务) |
4风险提醒 风险提醒:橙色 · 评估后使用
- 可选支路会读取环境变量凭证并送素材出网 — references/ai-music.md 以 $OPENROUTER_API_KEY 调 openrouter.ai(Bearer 头),把音乐描述交给第三方生成服务并消耗账号额度;返回音频入库成片。仅用户要求时触发,但一旦触发即属凭证外发 + 素材出网。
- 第三方生成物的可用性/版权由使用者承担 — 文档未对生成的 bgm.mp3 做任何授权校验;商用发布前需自行确认音乐授权与平台条款。
- 跨 skill 依赖:SCORE 的渲染实际发生在 javascript-animation 的 render.mjs — 只装 soundtrack skill 时没有渲染入口(该脚本属另一 skill)。按范式不改档位,但使用者需知道成片渲染的执行面在那边。
- 检查脚本的自动切点检测有已知误报面 — SKILL.md 自述无 --cues-file 时靠画面检测,'fast camera moves can read as cuts';因此推荐始终提供 CUES 文件,否则可能得到无意义的 OFF-BEAT 报告。
- 同步阈值是统计判据,不等同于听感合格 — +6 dB/±100 ms 是可自动化的下限;最终仍要求「失败必须能指认出是刻意的 stinger」,即人耳判断不可被脚本完全替代。
5第二遍独立确认
- [ok] $OPENROUTER_API_KEY 是否真的被读取并外发(橙档判定关键) — references/ai-music.md 原文给出 `curl -s -N https://openrouter.ai/api/v1/chat/completions -H "Authorization: Bearer $OPENROUTER_API_KEY" …`,且开头写明 'Needs an API key.';端点与凭证同处一条可复制命令,调用点真实存在。
- [ok] 该支路是否真的是「可选、非默认」(避免过度判级) — SKILL.md 正文写 'Don't ask the user to choose a music source up front.',并把 AI 音乐放在 '## Other sources (only when asked)' 段下;默认段标题为 '## Default: synthesize it in the page (no key, no files)'。触发条件为用户显式要求。
- [ok] sync-check 的判据是否与文档一致(能力声明可信度) — 代码内 TOL = Number(opt('--tol', 100)) / 1000 对应 ±100ms;`const jump = 20 * Math.log10(rms(v.t, v.t + .3) / ref), startle = jump > 6;` 对应 +6 dB;无音轨时 console.error 并 process.exit(1);与 SKILL.md 逐条吻合,无夸大。
- [ok] 模板是否真的与画面共享数据(而非各自维护时间) — groove.js 头注释给出 schema(sections/events/kit/harmony/key)并写 'The picture reads the same BPM / SECTIONS / EVENTS, so sound and image can't drift apart.';score.js 同样由 cues(即 window.CUES)切段并做小节取整。声明与实现一致。
- [ok] 是否有比橙更高的行为(红档特征) — 全仓扫描 `sandbox|automationcontrolled|no-verify|stealth|bypass|ignoreHTTPSErrors|dangerously` 零命中;curl 未使用 -k/--insecure,无绕防护动作,无任意代码执行面(模板是页面内 Web Audio 代码)。
- [ok] 确定性声明(每次渲染一致)是否可在模板代码内定位 — score.js 的混响脉冲用固定种子 LCG 生成并注释 'deterministic noise tail (seeded, so every render is identical)';groove.js 同样用 `let s = 99 + c` 起手的 LCG 与固定种子噪声缓冲。声明属实。
6结论
7cd2fd4b7c5af0a2…deb46e0eff