@dsh-gis/dsh-video-studio-skill 0.9.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +473 -0
- package/README.md +263 -327
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/package.json +9 -10
- package/schemas/design.schema.json +722 -0
- package/schemas/film.schema.json +31 -42
- package/schemas/proposal.schema.json +133 -0
- package/skills/video-studio/SKILL.md +1216 -1123
- package/skills/video-studio/assets/design_fixture.json +141 -0
- package/skills/video-studio/assets/film.json +2 -4
- package/skills/video-studio/assets/golden_frames.json +13 -10
- package/skills/video-studio/assets/scenes.py +153 -65
- package/skills/video-studio/assets/voice_loudness.json +16 -0
- package/skills/video-studio/references/appearance.md +123 -0
- package/skills/video-studio/references/audio-variation.md +74 -93
- package/skills/video-studio/references/audio.md +64 -66
- package/skills/video-studio/references/change-impact.md +5 -5
- package/skills/video-studio/references/{v08-quality.md → delivery-gates.md} +16 -18
- package/skills/video-studio/references/frame-qa.md +48 -16
- package/skills/video-studio/references/pipeline.md +4 -4
- package/skills/video-studio/references/pitfalls.md +22 -3
- package/skills/video-studio/references/proposal.md +160 -0
- package/skills/video-studio/references/quality-gates.md +11 -11
- package/skills/video-studio/references/scene-authoring.md +2 -2
- package/skills/video-studio/references/scene-grammar.md +40 -42
- package/skills/video-studio/references/sfx-design.md +8 -6
- package/skills/video-studio/references/web-engine.md +8 -8
- package/skills/video-studio/references/word-timeline.md +9 -9
- package/skills/video-studio/requirements.txt +9 -13
- package/skills/video-studio/scripts/designkit.py +2115 -0
- package/skills/video-studio/scripts/filmcheck.py +22 -48
- package/skills/video-studio/scripts/filmkit.py +2980 -2904
- package/skills/video-studio/scripts/gates.py +4 -4
- package/skills/video-studio/scripts/grammar.py +73 -13
- package/skills/video-studio/scripts/musicgen.py +2042 -0
- package/skills/video-studio/scripts/preflight.py +14 -7
- package/skills/video-studio/scripts/previewkit.py +848 -0
- package/skills/video-studio/scripts/propose.py +825 -0
- package/skills/video-studio/scripts/qa_frames.py +73 -39
- package/skills/video-studio/scripts/qaasr.py +26 -7
- package/skills/video-studio/scripts/qavmaf.py +89 -23
- package/skills/video-studio/scripts/render.py +545 -231
- package/skills/video-studio/scripts/scorekit.py +55 -1755
- package/skills/video-studio/scripts/selfcheck.py +815 -215
- package/skills/video-studio/scripts/sfxkit.py +27 -26
- package/skills/video-studio/scripts/stagewatch.py +24 -8
- package/skills/video-studio/scripts/stylekit.py +207 -888
- package/skills/video-studio/scripts/voicekit.py +508 -387
- package/skills/video-studio/scripts/webengine.py +71 -72
- package/tools/audio_similarity.py +64 -37
- package/tools/ci_check.py +20 -14
- package/tools/estimate.py +34 -29
- package/tools/golden_frames.py +72 -28
- package/tools/silhouette_qa.py +520 -168
- package/docs/demo-pil-sheet.png +0 -0
- package/docs/styles/demo-results.json +0 -76
- package/docs/styles/sheet-8-styles.png +0 -0
- package/docs/styles/sheet-ai.png +0 -0
- package/docs/styles/sheet-before-after.png +0 -0
- package/docs/styles/sheet-craft.png +0 -0
- package/docs/styles/sheet-night.png +0 -0
- package/skills/video-studio/assets/score_demo.json +0 -307
- package/skills/video-studio/references/style-packs.md +0 -283
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"_comment": "音色响度补偿(数据,不是身份):gain_db = 目标响度 - 该音色实测 rms_db,夹在 ±3 dB。换机/换 voice 必须重测(python voicekit.py --calibrate 会对表里的音色重新合成校准句)。designkit 与 voicekit 共用这一份:两处各存一份必然漂移——实测采样器会产出 4 把表里没有的音色。",
|
|
4
|
+
"target_dbfs": -20.97,
|
|
5
|
+
"voices": {
|
|
6
|
+
"zh-CN-YunyangNeural": 3.0,
|
|
7
|
+
"zh-CN-XiaoxiaoNeural": -2.7,
|
|
8
|
+
"zh-TW-HsiaoChenNeural": -3.0,
|
|
9
|
+
"zh-CN-XiaoyiNeural": -0.6,
|
|
10
|
+
"zh-CN-YunjianNeural": 1.0,
|
|
11
|
+
"zh-CN-YunxiNeural": 1.9,
|
|
12
|
+
"zh-CN-YunxiaNeural": 0.3,
|
|
13
|
+
"zh-CN-liaoning-XiaobeiNeural": 0.0,
|
|
14
|
+
"zh-CN-shaanxi-XiaoniNeural": -0.3
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# 外观定制:设计 DSL 实操手册
|
|
2
|
+
|
|
3
|
+
> **外观由 `work/design.json` 决定:`propose.py` 现场采样,你确认之后冻结。**
|
|
4
|
+
> 没有现成外观目录可挑,也没有 `style` 字段——剧本不决定长相。
|
|
5
|
+
> 同一份稿子换一份 design.json 就是另一种画面,反之亦然。
|
|
6
|
+
> 这篇讲的是:**想控制画面的哪一块,就改 design.json 的哪个字段,怎么验证它真的生效。**
|
|
7
|
+
|
|
8
|
+
## 0. 三条正确姿势
|
|
9
|
+
|
|
10
|
+
| 你想要 | 怎么做 | 不要这样做 |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| 换一种气质 | `propose.py --revise "更冷一点,少装饰,换女声"` 重出方案 | 去找"第 9 套风格"——没有这种目录 |
|
|
13
|
+
| 微调某个细节 | 手改 `work/design.json` 的字段 → `designkit.py --lint` 校验 → `render.py --stage stills` 看 | 直接改 `filmkit.py` 里的常量 |
|
|
14
|
+
| 换整个画面语言 | 写自己的 `scenes.py`(放剧本同目录),实现 `draw_frame(scene, t_local, dur, t_global, script, frame)`(完整签名见 SKILL.md §1) | 期望 `--engine web` 跟随 design(它不会) |
|
|
15
|
+
|
|
16
|
+
## 1. design.json 的六块
|
|
17
|
+
|
|
18
|
+
| 块 | 决定什么 | 字段 |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `structure` | 几段、目标时长 | `segments` / `beats` / `hold_policy` / `target_sec` |
|
|
21
|
+
| `visual` | 画面全部外观 | 见 §2 |
|
|
22
|
+
| `audio` | 配乐 / 声线 / 音效 | `music`(scale/bpm/key/mood/seed)/ `voice`(id/map/jitter/max_distinct)/ `sfx`(enabled/density/gain_db) |
|
|
23
|
+
| `pacing` | 节奏 | `lead` / `tail` / `min_dur` / `hold` / `item_stagger` |
|
|
24
|
+
| `id` | 指纹 | `design:<hash>`,由 `designkit.design_fingerprint()` 算出 |
|
|
25
|
+
| `label` / `pitch` | 中文标签 / 立意 | 提案卡上显示的那一句话 |
|
|
26
|
+
|
|
27
|
+
校验:`python <SKILL_DIR>/scripts/designkit.py --lint work/design.json`;
|
|
28
|
+
契约:`<REPO>/schemas/design.schema.json`。**fail-closed**:缺字段/越界/对比度不达标一律报错,
|
|
29
|
+
不会静默回落——设计层不允许「改了不生效也不报错」这种状态存在。
|
|
30
|
+
(实测过 11 个静默失效的旋钮:`voice` 写字符串时 map/jitter 全失效、`pan` 对立体声配方无效……
|
|
31
|
+
每个都"配置生效、画面不动",且不报错。设计层改成一律报错,这类问题在 `--lint` 就暴露。)
|
|
32
|
+
|
|
33
|
+
## 2. visual 字段表(能控制什么)
|
|
34
|
+
|
|
35
|
+
| 字段 | 取值 | 改它会发生什么 |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `palette` | 11 个颜色角色(bg/bg2/surface/ink/muted/dim/accent/accent2/good/bad/rule),每个 `[r,g,b]` | 整片色调。它不是"从 8 套配色里挑",而是**连续色相空间**里的一点:改 `bg` 的色相,其余角色会按对比度网格重新求解 |
|
|
38
|
+
| `family` | `sans` / `serif` / `kai` / `round` | 全片字族(标题优先,字幕默认跟随) |
|
|
39
|
+
| `typography` | `tracking`(字距)/ `line_gap`(行距)/ `number`(编号写法,8 种)/ `ratio`(字号比例)/ `align` | 文字气质;`number` 决定"01 / 壹 / I / 1.0"这类编号外观 |
|
|
40
|
+
| `motif` | `kind`(8 种:brackets / hairlines / handdrawn / slabs / minimal / letterbox / signal / rays)× `density`(0.55–1.00) | 装饰语言:四角括线 / 细线页眉 / 手绘框 / 色块 / 极简 / 遮幅 / 信号条 / 放射线 |
|
|
41
|
+
| `transition` | `kind`(dip / wash / paper / wipe / cut / iris)+ `dur`(秒) | 段与段之间的转场 |
|
|
42
|
+
| `motion` | `ease`(out3/out5/inOut3/outBack)/ `enter`(dx,dy) / `push`(镜头缓推)/ `breathe_hz` | 入场与镜头运动 |
|
|
43
|
+
| `bias` | `x` / `y` / `inset` | 整屏重心偏移与安全区收缩 |
|
|
44
|
+
| `layout` | `left-stack` / `center-stack` / `split-right` | 默认版式 |
|
|
45
|
+
| `layout_cycle` | 版式数组(≥2 种不同) | 按章节轮换版式;不写则自动派生 2–3 种 |
|
|
46
|
+
| `hud` | `chip-left` / `corner-line` / `none` | 页眉/角落装饰 |
|
|
47
|
+
| `size` / `fps` | `1920x1080` / `30` | 只支持 16:9 横屏(编码时可下采样到 720p) |
|
|
48
|
+
| `subtitle` | 见 §3 | 字幕字号/位置/对齐/背板/圆角/字族/颜色/行数 |
|
|
49
|
+
|
|
50
|
+
> **能组合出多少种画面?** 离散维度是 8 母题 × 6 转场 × 4 缓动 × 8 编号 × 3 版式 × 3 HUD × 4 字族
|
|
51
|
+
> × 7 情绪 × 2 调式(= 774,144 种),再乘配色这个连续量(色相 0–360° 任意取值)。
|
|
52
|
+
> 这个数字只说明"字段可组合空间足够大",**不是推荐你从中挑**——好看与否仍由对比度体检、
|
|
53
|
+
> 分镜语法和你的目检决定。
|
|
54
|
+
|
|
55
|
+
## 3. 字幕样式(visual.subtitle)
|
|
56
|
+
|
|
57
|
+
字幕的字号 / 位置 / 背板 / 圆角由设计 DSL 决定;写死的常量会让**任何两条片子**的字幕带一模一样。
|
|
58
|
+
**不写 = 用默认值**,老 `design.json` 不受影响、字幕与默认常量逐像素一致。
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
"subtitle": {"size": 46, "y": 560, "align": "left", "plate_alpha": 170,
|
|
62
|
+
"radius": 10, "family": "sans", "color": [245, 245, 245], "max_lines": 2}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| 字段 | 默认 | 取值 | 备注 |
|
|
66
|
+
|---|---|---|---|
|
|
67
|
+
| `size` | 46 | 20–96 | 采样器默认跑 40–56 |
|
|
68
|
+
| `y` | null | 0–1030 | null = 贴底安全区;采样器 1/3 概率抬高到 560–880 |
|
|
69
|
+
| `align` | center | center / left | |
|
|
70
|
+
| `plate_alpha` | 170 | 0–255 | 采样器默认 96–194 |
|
|
71
|
+
| `radius` | 10 | 0–48 | 采样器默认 2–20 |
|
|
72
|
+
| `family` | null | sans / serif / kai / round | null = 跟随大标题字族 |
|
|
73
|
+
| `color` | null | [r,g,b] 或 #rrggbb | null = 默认墨色 |
|
|
74
|
+
| `max_lines` | 2 | 1–3 | 决定了折行上限 |
|
|
75
|
+
|
|
76
|
+
验证:`render.py --stage style` 会打印一行「字幕: ... 对齐=... 背板α=... 圆角=... 最多N行」,
|
|
77
|
+
改 design 后重跑这一行就能看重算结果,不用等渲完。
|
|
78
|
+
|
|
79
|
+
## 4. 版式轮换(visual.layout_cycle)
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
"layout_cycle": ["split-right", "left-stack", "center-stack"]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- 元素取 `left-stack` / `center-stack` / `split-right`;**至少 2 种不同版式**,只写一种会被校验拒绝。
|
|
86
|
+
- 由 `grammar.plan_scenes` 按**章节**轮换:同一章内固定,换章才换(观众视角一章是一段整体)。
|
|
87
|
+
- 不写时 `design_to_plan` 按设计指纹自动派生 2–3 种,**默认就不是全片一张版式**。
|
|
88
|
+
- 审计:`--stage layout` 打印每段构图;`work/storyboard.json` 每段都有 `layout` 字段。
|
|
89
|
+
|
|
90
|
+
## 5. 配色怎么改才不出事
|
|
91
|
+
|
|
92
|
+
配色不是"挑一个好看的颜色",而是 11 个角色要同时满足对比度:
|
|
93
|
+
`theme_lint` 的门槛是 ink≥7 / muted≥4.5 / dim≥3 / accent≥4.5 / accent2≥4.5(good/bad≥3),
|
|
94
|
+
另外要求 bg 与 bg2 有可感知的亮度差(防纯色底)。
|
|
95
|
+
|
|
96
|
+
推荐两条路:
|
|
97
|
+
|
|
98
|
+
1. **让采样器算**(默认):采样器在 (hue, s, v) 网格里找满足全部门槛的点;
|
|
99
|
+
实测纯随机取色通过率 35%,网格求解 100%。
|
|
100
|
+
2. **手改色相**:只改 `palette.bg` 的色相/饱和度,然后跑 `designkit.py --lint`;
|
|
101
|
+
过不了就调一档明度——报错会直接点名哪个角色不达标。
|
|
102
|
+
|
|
103
|
+
> 片内**不要跨明暗族**:同一部片锁一个明暗族,段界跨族会整屏换底、触发闪烁门禁,观众也看着像换片。
|
|
104
|
+
> 这条在采样器里是硬约束,手改 design 时请自己守住。
|
|
105
|
+
|
|
106
|
+
## 6. 片内不重复:三层一起用
|
|
107
|
+
|
|
108
|
+
| 层 | 机制 | 防的是什么 |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| 段内 | `lines[].type`(10 类构图,grammar 自动选) | 每段都长一个样 |
|
|
111
|
+
| 章内 | `layout_cycle`(按章节轮换) | 全片一张版式 |
|
|
112
|
+
| 片间 | 采样 nonce + 候选 ≥2 维 + 底色色相分叉 + **片间去重台账** | 不同片撞衫 |
|
|
113
|
+
|
|
114
|
+
## 7. 常见误用
|
|
115
|
+
|
|
116
|
+
| 现象 | 原因 | 修法 |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| 改了 `design.json` 画面没变 | 没重跑阶段 / 没重签核 | `render.py --stage explain` 看哪些阶段过时 |
|
|
119
|
+
| `--stage style` 报「找不到设计」 | 工程里没有 `work/design.json` | 先 `propose.py --brief ... --script film.json --work work` 再 `--confirm` |
|
|
120
|
+
| 手改 design 后 `--confirm` 报校验失败 | 字段越界/对比度不达标 | 按报错逐条修;或干脆 `--revise` 重出 |
|
|
121
|
+
| 想"用某套预设当起点" | 没有预设目录,外观只由 design.json 决定 | 现场采样 + `--revise` 表达要求,要复现用 `--seed` |
|
|
122
|
+
| `render.py --preset x` 报错 | render.py 没有 `--preset` 这个参数 | 用 `--brief` 或直接写 design.json |
|
|
123
|
+
| `stylekit.py --list` 只有一段说明、exit 2 | 没有可选外观清单(预期行为) | 看 `designkit.py --list-palettes` 的配色采样示例(那不是可选清单) |
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 音频差异机制 —— 接口契约
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
>
|
|
3
|
+
> 当前机制一句话:配乐由 `musicgen.py` 参数化编排(bpm/key/scale/mood/seed + 编排参数,
|
|
4
|
+
> 内容指纹与编排参数共同决定和声/动机/鼓型);音效 seed 由 `content_seed(base, style, content_fp, recipe, t)`
|
|
5
|
+
> 按内容派生;提示音调性跟随配乐(`in_key`);段落边界前 2.2 s 内的转场 cue 自动 -3 dB
|
|
6
|
+
> (`sfx.duck_transitions`);混音旁白优先(配乐 -12 dB 基线 + 语音侧链 6/3 dB + SNR 兜底 12 dB)。
|
|
7
|
+
>
|
|
8
|
+
> 阈值由 `tools/audio_similarity.py` 按 §5 判定,诊断证据就是它的输出。
|
|
6
9
|
|
|
7
10
|
## 1. 要解决的两个问题(都有实测数据)
|
|
8
11
|
|
|
9
12
|
| 问题 | 实测 | 根因 |
|
|
10
13
|
|---|---|---|
|
|
11
|
-
| A. 跨视频配乐雷同 |
|
|
14
|
+
| A. 跨视频配乐雷同 | 同 bpm/同调/同编排参数、只换内容指纹:corr **0.966** | 指纹只改 seed(音色细节),和弦进行/动机/鼓型/段落骨架全固定 |
|
|
12
15
|
| B. 跨视频音效雷同 | 同 cue 序列两次渲染逐样本相同 | `CueBus.build_audio` seed = `seed_base + i`,只跟 cue 顺序有关 |
|
|
13
16
|
| C. 片内配乐与音效难分 | impact 质心 1001 Hz / whoosh 1589 Hz / music 5451 Hz;提示音硬编码 Dm 五声 | 提示音调性硬编码 + 配乐过门与 sfx 轨同边界同频段 + duck 仅 2.5–4.5 dB |
|
|
14
17
|
|
|
15
|
-
## 2.
|
|
18
|
+
## 2. 各层由谁负责
|
|
16
19
|
|
|
17
|
-
|
|
|
20
|
+
| 层 | 负责文件 | 做什么 |
|
|
18
21
|
|---|---|---|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
**写域规则**:A 路只改 scorekit.py;B 路只改 sfxkit.py / assets/scenes.py / assets/film.json;
|
|
24
|
-
C 路由 lead 改 filmkit.py / render.py / selfcheck.py。**任何一路不得改别人的文件**(包括顺手"优化")。
|
|
22
|
+
| 配乐编排 | `skills/video-studio/scripts/musicgen.py` | 按 bpm / key / scale / mood / seed 编排;`sections` / `progressions` / `motif` / `drums` / `voices` / `energy_curve` 真的参与事件展开;内容指纹进 seed 与变奏参数 |
|
|
23
|
+
| 音效差异化 | `skills/video-studio/scripts/sfxkit.py`、`assets/scenes.py`、`assets/film.json` | `in_key` 跟随配乐调性;`content_seed` 内容派生 seed;`cues()` 按画面内容生成;夹具 cue 不固定三条 |
|
|
24
|
+
| 接线与混音 | `skills/video-studio/scripts/filmkit.py`、`render.py`、`selfcheck.py` | CueBus 内容派生 seed、调性透传、duck 分级、过门避让、旁白优先 |
|
|
25
25
|
|
|
26
26
|
## 3. 接口契约(逐字实现,向后兼容)
|
|
27
27
|
|
|
28
|
-
### 3.1 sfxkit
|
|
28
|
+
### 3.1 sfxkit
|
|
29
29
|
|
|
30
30
|
```python
|
|
31
|
-
# 1)
|
|
31
|
+
# 1) 调性跟随:key 参数,默认 None = 固定 D 小调五声
|
|
32
32
|
def in_key(freq: float, key: str | None = None) -> float:
|
|
33
|
-
"""key=None ->
|
|
33
|
+
"""key=None -> 固定 D 小调五声(不随配乐调性变,逐样本可复现)。
|
|
34
34
|
key 给 "Dm"/"C"/"Am"/... -> 吸附到该调(小调用自然小调五声,大调用大调五声)。"""
|
|
35
35
|
|
|
36
36
|
# 2) 所有走 in_key 的配方接受 key(通过 **kw 透传,签名末位加 key=None 或直接从 kw 取):
|
|
@@ -39,121 +39,102 @@ def in_key(freq: float, key: str | None = None) -> float:
|
|
|
39
39
|
|
|
40
40
|
# 3) 内容派生 seed 的辅助函数(供 filmkit.CueBus 用)
|
|
41
41
|
def content_seed(base: int, style: str, content_fp: str, recipe: str, t: float) -> int:
|
|
42
|
-
"""稳定的 32 位非负 int:sha1(
|
|
42
|
+
"""稳定的 32 位非负 int:sha1("<固定盐>|<base>|<style>|<content_fp>|<recipe>|<t 三位小数>")
|
|
43
|
+
-> int(h[:8], 16) % 9000 + 1000。"""
|
|
43
44
|
```
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
`<固定盐>` 是代码里写死的字符串常量:`sfxkit.content_seed` 与 `filmkit.content_seed` 逐字相同。
|
|
47
|
+
要复刻就照抄实现里的拼接串,**不要自己另拼一份**——换个盐等于换一套 seed,
|
|
48
|
+
"A1 同参数换指纹可区分"的验收直接失效。
|
|
49
|
+
|
|
50
|
+
### 3.2 filmkit.CueBus.build_audio
|
|
46
51
|
|
|
47
52
|
```python
|
|
48
53
|
def build_audio(self, total=None, sr=SR, base_db=None, default_gain_db=0.0,
|
|
49
54
|
seed_base=1000, verbose=True,
|
|
50
|
-
style=None, #
|
|
51
|
-
content_fp=None, # 内容指纹 full;None =
|
|
52
|
-
key=None): # 配乐调性;None =
|
|
55
|
+
style=None, # 设计标识(design:<hash>);None = 位置派生
|
|
56
|
+
content_fp=None, # 内容指纹 full;None = 位置派生
|
|
57
|
+
key=None): # 配乐调性;None = 提示音按 D 小调五声
|
|
53
58
|
"""每条 cue 的 seed:
|
|
54
|
-
- style/content_fp 任一为 None -> seed = seed_base + i
|
|
59
|
+
- style / content_fp 任一为 None -> seed = seed_base + i(位置派生,逐样本可复现)
|
|
55
60
|
- 都给 -> seed = sfxkit.content_seed(seed_base, style, content_fp, kind, t)
|
|
56
61
|
并把 key 透传给配方(render(kind, seed=..., key=key, **params))。"""
|
|
57
62
|
```
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- `full` 指纹(已有 `fingerprint` 参数,当前只用于 seed)必须影响**和声与动机**:
|
|
64
|
-
- 和弦替换:按指纹从该风格的 `progressions` 里选 tier,再对**每两小节**做一次稳定替换
|
|
65
|
-
(从同大小的替代级数集里挑:i↔i7 / VI↔VI7 / III↔III7 / VII↔VII7;大调侧 I↔Imaj7 / IV↔IVmaj7)。
|
|
66
|
-
只在与乐句末/片尾规则不冲突时替换。
|
|
67
|
-
- 动机变形:`motif` 的每个 (拍, 级数, 时值) 按指纹做 **±1 级数的稳定偏移**,
|
|
68
|
-
只改级数不改节奏;问题句偏移与答句偏移必须独立派生(保证两部片子不会同步变形)。
|
|
69
|
-
- 鼓型:`drums[tier]` 的 kick 落点按指纹加 **最多一个 ghost 落点**(在 0.5 拍网格上,
|
|
70
|
-
不与现有落点重复);clap 位置按指纹 ±0.5 拍。
|
|
71
|
-
- **必须保证**:同 `fingerprint` 逐采样一致;不同 fingerprint 的相关性显著下降(§5 阈值)。
|
|
72
|
-
- 新增报告字段:`variation`(dict:`{"chord_swaps": n, "motif_shifts": n, "drum_ghosts": n}`),
|
|
73
|
-
写进 work/score.json,方便审计。
|
|
74
|
-
- legacy 路径(style=None)**逐字节不变**(`scorekit.py --selftest` 的第 20 条会验;
|
|
75
|
-
别和 `selfcheck.py` 的编号搞混——那边第 21 条只查 scorekit 同 seed 确定性)。
|
|
64
|
+
`render.py` 的 `_sfx_seed_context` 就是这条的调用方:它从 `resolve_style` 解出的设计计划里取
|
|
65
|
+
`style` / `fingerprint.full` / `music.key`;`sfx.content_seed=false`(或计划里没有指纹)时返回全 None,
|
|
66
|
+
`build_audio` 于是走位置派生。
|
|
76
67
|
|
|
77
|
-
### 3.
|
|
68
|
+
### 3.3 assets/scenes.py 的 cues()
|
|
78
69
|
|
|
79
|
-
|
|
70
|
+
按画面事件生成,**类型按场景 kind 与内容变化**:
|
|
80
71
|
|
|
81
72
|
- `hook`:段首 `impact`(size 按字数 0.9~1.3),落定 `riser`。
|
|
82
|
-
- `body
|
|
73
|
+
- `body`:条目落位用 `ui_blip`,但 **pitch 由条目文本 sha1 派生**(json 的 items 文本);
|
|
83
74
|
最后一个条目后加 `answer_chime`(若 kind=body 且 items≥3);段尾 `whoosh`(going 到下一段)。
|
|
84
75
|
- `quote`:`reverse_swell` + 落定 `impact`(轻)。
|
|
85
76
|
- `outro`:`answer_chime` + 片尾 `sub_drop`。
|
|
86
|
-
-
|
|
87
|
-
-
|
|
77
|
+
- 不许出现"每段都一样的三连击":同类型 cue 在一段内重复时,`gain` 必须递减(-2/-4/-6),pitch 必须不同。
|
|
78
|
+
- **对齐**:stagger 对齐逻辑(与 `_draw_items` 同节拍)保留,返回 schema 不变。
|
|
88
79
|
|
|
89
|
-
### 3.
|
|
80
|
+
### 3.4 assets/film.json 夹具
|
|
90
81
|
|
|
91
|
-
模板里的 `sfx.cues`
|
|
82
|
+
模板里的 `sfx.cues` 只保留 1 条转场 whoosh 作为示例,并注明"具体提示点交给 `scenes.py` 的 `cues()` 钩子"
|
|
83
|
+
(固定三条 whoosh/riser/impact 是"跨片雷同"的直接来源之一)。
|
|
92
84
|
|
|
93
|
-
-
|
|
94
|
-
- `sfx` 增加可选字段 `content_seed: true`(默认 true):走内容派生 seed;显式 false 回到旧行为。
|
|
85
|
+
- `sfx` 的 `content_seed: true`(默认):走内容派生 seed;显式 `false` 回到 `seed_base + i`。
|
|
95
86
|
|
|
96
|
-
## 4. render.py
|
|
87
|
+
## 4. render.py 接线
|
|
97
88
|
|
|
98
|
-
- `
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
89
|
+
- `_sfx_seed_context`:从设计计划取 `style` / `fingerprint.full` / `music.key`;
|
|
90
|
+
`sfx.content_seed=false` 或计划没有指纹时返回全 None -> `build_audio` 走位置派生。
|
|
91
|
+
- `st_sfx`:把上面三样传给 `bus.build_audio(...)`;`sfx.duck_transitions`(默认 true)
|
|
92
|
+
决定是否做过门避让。
|
|
93
|
+
- `st_audio`:duck 默认值按风格分级——`mono` 3.0 / `lo-fi`、`magazine` 3.5 / `cinema`、`epic` 5.5 /
|
|
94
|
+
其余 4.0;`music.duck_db` 显式值永远优先。
|
|
95
|
+
另外**过门避让**:配乐在 music.wav 里已经有段落过门,sfx 轨在
|
|
96
|
+
[段落边界 - 2.2s, 边界] 区间内的 whoosh/riser/reverse_swell 需要自动 -3 dB
|
|
97
|
+
(`_duck_transition_sfx` 实现,`sfx.duck_transitions=false` 可关)。
|
|
105
98
|
|
|
106
99
|
## 5. 验收阈值(改完必须重跑 `python <REPO>/tools/audio_similarity.py`)
|
|
107
100
|
|
|
108
|
-
| 指标 |
|
|
101
|
+
| 指标 | 目标 | 实测 | 不这么做时 |
|
|
109
102
|
|---|---|---|---|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
118
|
-
>
|
|
103
|
+
| A1 同参数换内容指纹 corr | **≤ 0.85**(仍要像同一部片子,但不能像同一首) | **0.645** | 0.966 |
|
|
104
|
+
| A2 换编排参数 corr | ≤ 0.52(不能变得更像) | **0.514**(连跑 3 次逐位相同) | 0.534 |
|
|
105
|
+
| B1 同 cue 序列跨片逐样本相同 | **False**(不同内容指纹必须不同) | False(同 fp 仍逐样本相同) | True |
|
|
106
|
+
| B2 位置派生(不传 style/fp) | **必须仍为 True**(向后兼容) | True | — |
|
|
107
|
+
| C1 duck 默认 | 3.5–5.5 dB | 4.0(mono 3.0 / cinema 5.5) | 2.5–4.5 dB |
|
|
108
|
+
| D1 人声段旁白/背景响度差 | **≥ 12 dB** | **15.0 dB** | 约 1.5 dB(配乐 -1.5、无侧链) |
|
|
109
|
+
| D2 旁白处背景额外让位 | ≥ 4 dB | 6.0 dB | 0 |
|
|
110
|
+
|
|
111
|
+
> A2 的余量只有 0.006,是有意的:抬到 0.53 就离 0.534 只剩 0.004,这条门禁就没意义了。
|
|
112
|
+
> D 组守的是"旁白优先":视频以旁白为主,BGM 与音效是可选层。`mix_film` 的
|
|
119
113
|
> `voice_duck_db=6` / `voice_duck_sfx_db=3` + `music.gain_db=-12` 把"人声比背景响 12 dB"
|
|
120
114
|
> 从文档口号变成可执行断言(selfcheck 35/36 条 + 本脚本 D 组)。
|
|
121
115
|
|
|
122
|
-
**已知边界(不是回归,是设计取舍)**:同风格不同内容指纹有约 1/3 概率落到同一个
|
|
123
|
-
和弦进行档位偏移上,此时两段配乐 corr 约 0.93(只剩七和弦色彩与鼓型 ghost 的差别)。
|
|
124
|
-
要再压下去需要"每段独立选档"或"同档时轮转",会削弱"片内是一条曲子"的连贯性,
|
|
125
|
-
本轮不做;`--strict` 用固定的 content-a / content-b 做验收,两者落在不同档。
|
|
126
|
-
|
|
127
116
|
另有硬性回归(任何一条红都不算完成):
|
|
128
117
|
|
|
129
|
-
> 下面 1–
|
|
118
|
+
> 下面 1–5 的路径都按**仓库根**写(`python skills/video-studio/scripts/…`);如果你已经 `cd` 进技能目录,
|
|
130
119
|
> 去掉 `skills/video-studio/` 前缀即可。`<REPO>` = 仓库根 / 装好后的包根。
|
|
131
120
|
|
|
132
|
-
1. `python skills/video-studio/scripts/selfcheck.py` 全 PASS
|
|
133
|
-
2. `python skills/video-studio/scripts/
|
|
134
|
-
3. `python skills/video-studio/scripts/sfxkit.py --selftest` 全 PASS
|
|
121
|
+
1. `python skills/video-studio/scripts/selfcheck.py` 全 PASS(**69 条**)。
|
|
122
|
+
2. `python skills/video-studio/scripts/musicgen.py --selftest` 全 PASS(26 项)。
|
|
123
|
+
3. `python skills/video-studio/scripts/sfxkit.py --selftest` 全 PASS(16 条)。
|
|
135
124
|
4. `python <REPO>/tools/ci_check.py` 全 PASS(在包根执行)。
|
|
136
|
-
5.
|
|
137
|
-
|
|
138
|
-
## 6. 协作规则
|
|
125
|
+
5. 音效位置派生的逐样本可复现:`selfcheck.py` 守着这条(`sfx.content_seed=false` 时与 `seed_base + i` 同值)。
|
|
139
126
|
|
|
140
|
-
|
|
141
|
-
- 改完在 `CHANGELOG.md` 追加的条目由 lead 统一写(teammate 不要改 CHANGELOG)。
|
|
142
|
-
- 任何接口拿不准的地方,按本文件逐字实现;本文件没写的自由发挥请控制在"不改变签名"的范围内。
|
|
143
|
-
- 完成后向 lead 报告:改了哪些函数、自测输出摘要、与契约的偏差(如果有)。
|
|
127
|
+
## 6. 实现备注
|
|
144
128
|
|
|
145
|
-
|
|
129
|
+
几处落地细节(不改契约本身):
|
|
146
130
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
|
150
|
-
|
|
151
|
-
|
|
|
152
|
-
| `roulette_tick` 调性 | §3.1 列进"走 in_key 的配方" | v0.6 本来没走 in_key;实现为 key=None 保持旧公式、key 非空才吸附 | 接受:不破坏 legacy 逐样本 |
|
|
153
|
-
| 同段重复 cue 的 gain | §3.4 写 -2/-4/-6 | 每次再降 2 dB 不封顶(items 很多时会到 -2n) | 待观察:模板 5 条时最末一条 -8 dB;若听感偏轻改 `_cue_gain_decay` 一行 |
|
|
154
|
-
| `content_seed` 实现位置 | §3.1 放 sfxkit | filmkit 与 sfxkit 各有一份逐字相同的实现(filmkit 走函数内延迟 import,模块级 from 会改导入时机) | 接受:两侧同参同值已核对;注释互相指认 |
|
|
131
|
+
| 项 | 现状 |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `content_seed` 实现位置 | `filmkit` 与 `sfxkit` 各有一份逐字相同的实现(`filmkit` 走函数内延迟 import,模块级 `from` 会改导入时机);两侧同参同值 |
|
|
134
|
+
| 同段重复 cue 的 gain | 每次再降 2 dB、不封顶(items 很多时会到 -2n);模板 5 条时最末一条 -8 dB;要改听感就改 `_cue_gain_decay` 一行 |
|
|
135
|
+
| `roulette_tick` 调性 | `key=None` 走原公式(D 小调五声);`key` 非空才吸附 |
|
|
155
136
|
|
|
156
|
-
|
|
137
|
+
**回归基线**:
|
|
157
138
|
|
|
158
139
|
- sfxkit 9 条渲染的 sha256(ui_blip ×3 / answer_chime / text_stream ×2 / data_chirp / roulette_tick ×2)逐条相同。
|
|
159
|
-
- scorekit
|
|
140
|
+
- scorekit 的 `render()` 只承担合成原语(成片配乐走 musicgen):同 seed 逐样本一致(`scorekit.py --selftest` 第 18 条)。
|