Files
SoundGame/docs/design/24-rhythm-drummer.md
T
xsl ae850a4ce0 feat(rhythm): 鼓手模式重构——屏幕六区鼓组 + 无鼓伴奏合拍(用户裁定重构)
- 屏幕3列×2行六区鼓组(列=声像左中右/行=音高上亮下沉),六件程序合成打击乐,三重冗余蒙眼可辨
- 无鼓伴奏BGM上跟随领奏 call&response + 加花句;3曲90/105/120 BPM解锁制,BPM实测89.99/105.00/120.01
- 引擎后向兼容扩展:onGesture补{x,y}坐标(单参回调零感知,15游戏+3引擎测试全量回归零失败)
- 判定简化:全部音符=分区点按零延迟,旧双击补偿/下滑折中退役;键盘Digit1-6+空格=家区
- 教学摸鼓认区(摸哪报哪,8s无触摸自动进阶)+重教≤2+降速DDA+归因真话律
- 保留旧作好评:跟随领奏核心/S~C评级/解锁制/判定口径/guide工程遗产
- 语音49id双性别+8key;R1+R2连续2轮4/4愿意玩P0=0裁定可交付;live直证33 Perfect dt≈0.00s
- 03号文档仅文末加取代指针;新设计24号;档案research/rhythm-drummer/;Mureka累计$1.35
2026-09-14 20:17:10 +08:00

417 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 24 · 《节拍行者》鼓手模式重构设计(rhythm / Drummer Mode)
> 用户裁定:现有模式「不错,保留」——跟随领奏的合拍玩法核心、S~C 评级、曲目解锁制、
> BPM 精确性延续;整体重构为**屏幕分区打击乐**:屏幕划分区域,每区一种打击乐音效,
> 玩家作为乐队鼓手与无鼓伴奏音乐合拍。本文取代 [03-rhythm-rebuild.md](03-rhythm-rebuild.md)
> 的全部施工依据(03 号文档仅作历史档案,文末有取代指针;其 rhythm 判定好评口径
> 「PERFECT ±0.15 / GOOD ±0.35」经 live 校准,本文沿用)。
>
> 语音台词全表在 [24-rhythm-voice-ids.md](24-rhythm-voice-ids.md)。断言清单在 §12。
---
## 0. 可行性勘察决策(阶段零结论)
### 0.1 引擎手势事件是否携带坐标?
**不携带。** `web/engine/gesture.js` 的 `onGesture(g)` 只传手势名字符串,
触摸坐标只在引擎内部用于滑动/点按判定,回调时丢弃。
### 0.2 两条路线的权衡
| | (a) 引擎扩展:第二参数透传坐标 | (b) 游戏层自建 touch 监听 |
|---|---|---|
| 改动面 | gesture.js 每个 onGesture 调用点补 `{x,y}` 第二参;docs/web-engine-api.md 同步 | 游戏层加 touchstart 记录坐标 |
| 既有游戏影响 | **零**——全部 15 款游戏的回调都是单参 `(g) =>`,多余实参被忽略,手势分类/震动/时序逐字节不变(同 opts.pan / immediateTap / tapcancel 先例) | 零(不碰引擎) |
| 键盘通道坐标 | **免费**:空格虚拟指针本就走 `handleDown(keyCenter)`,天然拿到元素中心坐标 | 无法区分键盘 tap 与残留触摸坐标,需自行加时效启发式(脏) |
| 保持的引擎福利 | haptics 自动震动、多指守卫、tapcancel、长按快进 window 事件、双击状态机、键盘三通道全部保留 | 全部丢失,需在游戏层重写(双击窗/tapcancel/快进事件) |
| 代价 | 全量回归:15 游戏 playtest + guide.test + engine-voice + engine-input + docs 同步(铁律) | 回归只需 rhythm 自身,但收获一个与引擎并行的第二输入状态机(长期维护债) |
### 0.3 决策:路线 (a),引擎后向兼容扩展
- `attachGestures` 的全部 `onGesture(...)` 调用补第二实参 `{x, y}`(视口像素
clientX/clientY 语义):tap 类事件(含 immediateTap 序曲 tap、双击改判、tapcancel)
与 longpress/longpressend 传**按下点**;四向滑动传**滑动起点**(出手瞄准位置);
键盘通道传虚拟指针坐标(`keyCenter()`,方向键滑动同样补)。
- 缺省行为逐字节不变:不加 config 开关、不改任何判定阈值与震动;回调签名扩为
`(g, info?: {x, y})`,单参回调零感知。
- 回归义务(已执行,见 research/rhythm-drummer/round0/engine-regression.md):
15 游戏 playtest + web/harness/guide.test.mjs + engine-voice.test.mjs +
engine-input.test.mjs 全绿,docs/web-engine-api.md §gesture 同步。
### 0.4 打击乐音色:程序合成
六区音色 + accent 全部 numpy 程序合成(`tools/gen_rhythm_syn.py`,
gen_cipher_syn.py 同款工艺):可精确控制起音/衰减(盲玩靠起音对拍)、
零授权问题、零网络延迟、与 BGM 调性关系无关(打击乐无调性)。
### 0.5 BGM:无鼓伴奏是硬需求
玩家就是鼓手——BGM 含鼓则叠打混乱。Mureka instrumental 生成时 prompt
显式 `no drums, no percussion`,`tools/bpm_precise.py` 梳状扫描实测 BPM
(节奏类硬性要求,误差 >0.5 则 atempo 拉伸,R3 track90 先例),
无缝循环后处理沿用批次 1-4 工艺(强 onset 起点 → 整数小节裁剪 →
首尾 3s acrossfade → loudnorm I=-20 → ogg q3 + m4a 128k)。
---
## 1. 概述与继承/舍弃清单
**一句话玩法**:屏幕是一面 3列×2行 的鼓组。系统(领奏)在无鼓伴奏上敲一小节
节奏型,玩家在下一小节的相同拍点敲相同的鼓。曲子从 90 BPM 打到 120 BPM,
拿 B 级以上解锁下一首。
### 1.1 继承自旧作(用户裁定「保留」的部分)
| 项 | 说明 |
|---|---|
| 跟随领奏 call & response 核心循环 | 领奏 1 小节 → 应答 1 小节,逐乐句推进 |
| 时值判定口径 | PERFECT ±0.15s / GOOD ±0.35s(live round1 §13 校准值,吸收蓝牙延迟) |
| 计分与评级 | Perfect=2/Good=1/Miss=0,rate = 得分/(2×总音符),S≥0.95 / A≥0.85 / B≥0.70 / C 其余;不掉血不中断,歌照唱完 |
| 曲目解锁制 | 曲目 n 拿到 B 级 → 解锁 n+1;localStorage + 已登录 PUT 同步 |
| BPM 精确性 | 谱面/判定全部锚定 BGM 实测 BPM 网格(bpm_precise 实测 + atempo 修正) |
| 工程质量遗产 | AudioContext 预排程泵(40ms + LOOKAHEAD 0.6s)、BGM 手动 `src.start(t0)` 对网格、模态菜单断点续播、`_speakGuarded` 看门狗语音链、结算后 menu pad 看门狗、旁白单击跳过/长按快进豁免、guide 层全量接入(状态签名/gd_answer/gd_miss 归因/教学 DDA 降速/count-in 恢复) |
### 1.2 舍弃/替换
| 旧 | 新 | 理由 |
|---|---|---|
| 手势→乐器映射(tap=kick、left=闭镲…6 手势 6 音色) | 屏幕分区→乐器(6 区 6 音色,tap 即敲击) | 用户裁定的新核心 |
| 滑动/双击作为音符类型(SWIPE_DELAY 0.15 / DOUBLE_DELAY 0.20 补偿、双击序曲撤销) | **全部音符 = 分区点按**,immediateTap 按下即判,零识别补偿 | 打击乐每次出手都是「敲一下」,滑动从音符类型降级为纯导航;双击=同区连击两下(两声都合法),**不再需要双击序曲撤销账簿** |
| BGM 含鼓组(track90/105/120 有鼓) | 无鼓伴奏 | 玩家是鼓手 |
| 下滑呼菜单的「down 音符临近」折中(live P0 加窗/复活窗一整套) | 下滑**无条件**呼菜单 | 音符全是点按,下滑永不与音符冲突;序曲 tap 仍出 ghost 音但判定时由 tapcancel 撤销 |
| 教学 T1-T3(手势记忆) | 分区心智模型教学(摸鼓认区 → 跟打) | 「摸到哪是哪」的建立是设计核心(§6) |
| 旧语音表 31 句 | 新表 41 句(+沿用向导 8 句 = 49 句双性别,配额见 §8) | 分区教学与区名播报需要新句 |
---
## 2. 屏幕分区与音色映射
### 2.1 分区布局(3 列 × 2 行,纯听觉可辨是硬约束)
```
┌─────────┬─────────┬─────────┐
│ TL 左上 │ TC 上中 │ TR 右上 │ 上排:清亮/高频
│ 踩镲 │ 高嗵 │ 吊镲 │ pan: -0.6 / 0 / +0.6
├─────────┼─────────┼─────────┤
│ BL 左下 │ BC 下中 │ BR 右下 │ 下排:低沉/低频
│ 军鼓 │ 底鼓 │ 低嗵 │ pan: -0.6 / 0 / +0.6
└─────────┴─────────┴─────────┘
```
**双轴心智模型**(教学的主话术):
- **列 = 声像**:左列在左耳、中列在中央、右列在右耳(opts.pan 引擎已支持)。
- **行 = 音高**:上排清亮、下排低沉。
- 在双轴之上,六件乐器音色本身两两不同(镲/嗵/鼓),盲玩家有
「声像 + 音高 + 音色」三重冗余线索。
### 2.2 分区 ↔ 音色 ↔ 声像总表
| 区码 | 位置 | 乐器 | sfx id | pan | 合成要点(起音硬、≤0.5s 残响) |
|---|---|---|---|---|---|
| TL | 左上 | 闭合踩镲 | `sfx/dm_hat` | -0.6 | 6-10kHz 高通噪声,0.06s 急衰减 |
| TC | 上中 | 高音嗵 | `sfx/dm_tomhi` | 0 | 200→140Hz 正弦扫频 + 轻皮噪,0.25s |
| TR | 右上 | 吊镲 | `sfx/dm_crash` | +0.6 | 4-12kHz 噪声 + 非谐波泛音列,1.2s 长尾 |
| BL | 左下 | 军鼓 | `sfx/dm_snare` | -0.6 | 200Hz 鼓皮 + 1-4kHz 带通噪声,0.2s |
| BC | 下中 | 底鼓 | `sfx/inst_kick`(沿用旧 id) | 0 | 130→45Hz 正弦扫频 + 触击瞬态,0.25s |
| BR | 右下 | 低音嗵 | `sfx/dm_tomlo` | +0.6 | 120→75Hz 正弦扫频 + 轻皮噪,0.3s |
- 附加层:`sfx/dm_accent`(Perfect 叠加亮音,程序合成木鱼 1.5kHz 短音)。
- 分区判定:`zoneOf(x, y)` 以 `#game-root` 的 `getBoundingClientRect()` 为界,
y 上半 = 上排、y 下半 = 下排;x 三等分 = 左/中/右。无布局环境(mock/harness)
由调用方显式传 payload;payload 缺失时落 BC(中央家区,兜底不炸)。
- **键盘平权**(游戏层 keydown,引擎键盘通道之外的补充,数字键引擎本就不拦截):
`Digit1/2/3/4/5/6`(及小键盘)= TL/TC/TR/BL/BC/BR,按下即发、零延迟;
引擎空格 tap 落在元素几何中心 → 判 BC(「家」)。选曲/菜单/结算态数字键不生效
(那些态沿用手势导航)。
### 2.3 领奏音色 = 玩家音色
领奏音符与玩家敲击走**同一批 sfx**(同音色同声像)——做对时听到自己在和
「另一个鼓手」合奏,这是旧作好评的「演奏感」来源,保留。
---
## 3. 玩法循环与状态机
### 3.1 循环
```
选曲 → 数拍导入(1 小节底鼓×4,含节拍轻震) → 逐乐句(领奏 1 小节 + 应答 1 小节 [+ 休止])
→ 过门/加花(领奏 1 小节密集加花 + 应答 1 小节) → 终句吊镲 → 结算评级
```
### 3.2 状态机
```
idle → intro(语音) → tut(分区教学) → select(选曲,二周目起) → countin → main(逐乐句/加花) → end(结算)
↑_________________|
```
- 数拍小节由游戏排程 4 记数拍(BGM 无鼓,数拍只能靠鼓手自己)+ 每拍 [8] 轻震:正片用底鼓 inst_kick×4(guide.test G3 的 _sched 锚点,13 号冻结文档不可改),教学步用木鱼 click(dm_accent,guide.test G2 要求教学 _sched 里 kick=领奏音)。
- 每曲第一个应答窗口前 1 小节落 `gd_answer` + turn earcon(旧作 G1 修正沿用)。
- 模态菜单(下滑呼出):单击循环 [继续演奏 / 回大厅]、双击确认、上滑帮助、
下滑关闭;暂停真冻结(泵停、BGM 断点记 offset),恢复时全网格平移 +
4 拍 count-in(guide full 档先播 `gd_count`,每局限 1 次,round3 G3 全套沿用)。
---
## 4. 谱面与难度曲线
### 4.1 记号
`拍:区码`,1 小节 4 拍,`x.5` = 八分反拍。乐句 = 领奏 1 小节 + 应答 1 小节;
加花 = 特殊乐句(fill 标记),领奏为八分音符密集型,最后一音恒为 TR 吊镲收束。
### 4.2 曲目一《启程》90 BPM(教学曲,8 句 + 终曲加花,≈65s)
| 句 | 领奏节奏型 | 音符数 |
|---|---|---|
| 1 | `1:BC, 3:BC` | 2 |
| 2 | `1:BL, 3:BR` | 2 |
| 3 | `1:BC, 2:BC, 3:BL` | 3(双击底鼓引入同区连击) |
| 4 | `1:BL, 2:BR, 3:BC` | 3 |
| 5 | `1:BC, 2:BL, 3:BC, 4:BR` | 4 |
| 6 | `1:TL, 3:BL, 4:BC` | 3(上排首次进正片) |
| 7 | `1:BC, 2:TL, 3:BC, 4:TR` | 4 |
| 8 | `1:BL, 2:BR, 3:TL, 4:BC` | 4 |
| 终曲加花 | `1:BC, 1.5:BC, 2:BC, 2.5:BL, 3:BL, 3.5:BC, 4:TR` | 7(全曲唯一反拍,仅限加花) |
前 4 句应答后各休 1 小节,其余不休。
### 4.3 曲目二《追风》105 BPM(10 句 + 中途加花 + 终曲加花,≈80s)
| 句 | 领奏节奏型 | 音符数 |
|---|---|---|
| 1 | `1:BC, 3:BR` | 2 |
| 2 | `1:BL, 2:BL, 3:BC` | 3 |
| 3 | `1:TL, 3:BL, 4:BR` | 3 |
| 4 | `1:BC, 2:BL, 3:BC, 4:TL` | 4 |
| 5 | `1:BC, 2.5:BR, 3:BC` | 3(首个反拍) |
| 中途加花 | `1:BL, 1.5:BL, 2:BC, 2.5:BC, 3:BR, 3.5:BR, 4:TR` | 7 |
| 6 | `1:BL, 2:TL, 3.5:BR, 4:BC` | 4 |
| 7 | `1:BC, 2:BL, 3:BR, 3.5:TL` | 4 |
| 8 | `1:BR, 1.5:BL, 3:BC, 4:TR` | 4 |
| 9 | `1:BC, 2.5:TL, 3:BC, 4:BL` | 4 |
| 10 | `1:BL, 2:BR, 3:TL, 4:BC` | 4 |
| 终曲加花 | `1:BC, 1.5:BL, 2:BC, 2.5:BL, 3:BC, 3.5:BR, 4:TR` | 7 |
前 2 句应答后各休 1 小节;第 5 句与中途加花之间不休。
### 4.4 曲目三《星爆》120 BPM(10 句 + 中途加花 + 终曲加花,≈75s)
| 句 | 领奏节奏型 | 音符数 |
|---|---|---|
| 1 | `1:BC, 2:BL, 3:BC, 4:BL` | 4 |
| 2 | `1:TL, 2:BL, 3:TL, 4:BC` | 4 |
| 3 | `1:BR, 2:TR, 3:BR, 4:BL` | 4 |
| 4 | `1:BC, 2.5:TL, 3:BC, 4:BR` | 4 |
| 5 | `1:BL, 1.5:BR, 2.5:BL, 3:BC` | 4 |
| 6 | `1:BC, 2:TL, 2.5:BL, 3:BC, 4:TR` | 5 |
| 中途加花 | `1:TL, 1.5:TL, 2:BL, 2.5:BL, 3:BC, 3.5:BC, 4:TR` | 7 |
| 7 | `1:BR, 2:BC, 3:BL, 3.5:BR, 4:TL` | 5 |
| 8 | `1:BC, 1.5:BC, 2.5:BL, 3:BC, 3.5:TL, 4:TR` | 6(高潮句) |
| 9 | `1:BL, 2:BC, 2.5:BR, 3:TL, 4:BL` | 5 |
| 10 | `1:BC, 2:BR, 3:BC, 3.5:TL, 4:BL` | 5 |
| 终曲加花 | `1:BC, 1.5:BL, 2:BC, 2.5:BL, 3:BC, 3.5:BC, 4:TR` | 7 |
全曲不休。
### 4.5 难度曲线规则(改谱遵守)
1. 乐句长度:曲目一 2→4 音、曲目二 2→4 音、曲目三 4→6 音;同曲单调不减
(终曲加花不计入)。
2. 反拍(x.5):曲目一**只允许出现在加花**;曲目二第 5 句起、普通乐句每句至多
1 个反拍音;曲目三不限。加花为八分音符基底,不受此限。
3. 同一分区连续出现 ≤2 次(同区双击是合法手法,三连击禁止——盲玩极限)。
4. 同一乐句内分区种类 ≥2(纯底鼓句仅允许曲目一句 1)。
5. 相邻反拍连续位禁止左列↔右列大跳(`BL↔BR` 相邻反拍只允许曲目三)——
声像跳变过快在耳机上易混(旧作 §3.5-6 同款)。
6. 加花尾音恒为 TR 吊镲(收束感 + 吊镲长尾盖住应答结束)。
---
## 5. 时值判定与计分评级
```js
const PERFECT = 0.15; // Perfect 窗口 ±150ms(live round1 §13 校准)
const GOOD = 0.35; // Good 窗口 ±350ms(吸收蓝牙延迟)
const LOOKAHEAD = 0.6; // 调度前瞻(秒)
const PUMP_MS = 40; // 调度泵周期
```
- **判定时刻 = 触摸/按键时刻**:immediateTap 按下即发 tap(零补偿);
键盘数字键 keydown 即发(零补偿)。旧作的 TAP_DELAY/SWIPE_DELAY/DOUBLE_DELAY
补偿与旧引擎闩锁、双击序曲撤销、超时复活窗**整体退役**(没有滑动/双击音符了)。
- 归属:同分区最近未判定音符 |dt|≤GOOD → 出手即播该区音色(pan 随行),
Perfect 叠 `sfx/dm_accent`;|dt|≤GOOD×0.5 的异分区音符被敲 = 判 Miss
(错鼓);窗口外 = 自由演奏(出声音不判定)。
- **序曲 tap 治理**(immediateTap 代价,旧作账簿简化版):每次按下先发 tap
(出声+可认领),转滑动/长按/触摸取消时引擎补 `tapcancel` → 撤销该 tap 的
认领(声音收不回当 ghost 音,判定/统计必须更正且不计 Miss)。双击不改判
账簿——两下敲击各自独立成立。
- Miss 超时:expected + GOOD + 0.10s 未应答 → 闷镲反馈音
(`sfx/dm_hat` 降调 0.5× 复用,零新资产)+ error 震动。
- 长按 = 死输入:Miss 反馈音 + error 震动,不判定不归属(演奏期长按快进
`setRateBoostEnabled(false)` 豁免照旧)。
- 计分/评级/连击:Perfect=2 / Good=1 / Miss=0;rate = 得分/(2×总音符);
S≥0.95 / A≥0.85 / B≥0.70 / C 其余;16 连击 success 震、乐句全 Perfect
[20,40,20]、结算 S/A success 震,全部沿用旧作。
---
## 6. 教学编排(分区心智模型,设计核心)
蒙眼玩家如何建立「摸到哪是哪」?分四步,全部零惩罚(教学不计分、不出
Miss 音),单步重教 ≤2 次(第 2 次失败 → 领奏降速 25% 重演一次 → 再败跳过,
旧作 G2 DDA 沿用):
| 步 | 内容 | 机制 |
|---|---|---|
| T0 摸鼓认区 | `tut_map` 讲双轴模型(横三列=左中右声像,竖两行=上亮下沉)→ `tut_probe` 进入**探索模式**:随便摸,敲到哪区就播哪区音色 + 区名(`zone_*` 六句) | 摸到 ≥3 个不同区自动进 T1(每区名每轮教学只播一次,重复摸只出声不报名,防刷屏);**防呆:8s 无触摸自动进 T1**(摸过 1-2 区再宽限 8s),探索是礼物不是门槛——guide.test G2 等无触摸驱动依赖此路径 |
| T1 家区底鼓 | `tut_z_kick`:BC 是家。数拍 1 小节 → 领奏 `1:BC` → 下一小节应答 | 做错:纠正链(§7 归因)→ 重播领奏重试 |
| T2 左右下排 | `tut_z_sides`:BL 军鼓、BR 低嗵。领奏 `1:BL, 3:BR` → 跟打 | 同上 |
| T3 上排三音 | `tut_z_patt`:TL 踩镲、TC 高嗵、TR 吊镲。领奏 `1:TL, 2:TC, 3:TR` → 跟打 | 同上;通过后 `tut_done` 进曲目一 |
- 教学在曲目一无鼓 BGM 上进行;每步领奏前都有 1 小节底鼓数拍(教学期
count-in 不省)。
- 下滑跳过教学(`tut_skip`)保留;教学 Miss 归因真话律沿用(没出手报
「这拍没接」gd_miss_none,不谎报早晚)。
- 加花预告:`tut_fill`「最后有加花,跟着我连打。」在**曲目一终曲加花领奏
前 1 小节**播,每账号一次。
---
## 7. 连击/错拍反馈
| 时机 | 反馈 |
|---|---|
| 出手敲对 | 该区音色(pan 随行)+ Perfect 叠 accent |
| 敲错区(异分区音符半窗内被敲) | 该区音色照样出声(自由演奏语义)+ 闷镲 Miss 音 + error 震 + 播报实际敲中的区名(`zone_*`)+ `retry_wrong`「敲错鼓了,再听一遍。」(教学期)/ gd_miss 归因(正片,连 2 Miss 一次) |
| 早/晚(窗内同区但超 Perfect) | 不出错音(Good 照播),教学期事后归因 `retry_early`/`retry_late` |
| 没出手 | 音符超时闷镲 + error 震;教学归因 gd_miss_none「这拍没接」 |
| 16 连击 / 乐句全 Perfect / 解锁 / S·A 结算 | success 震 / [20,40,20] / success+[20,40,20] / success(沿用旧作) |
| 曲目过半 | `mid_song`(ducking 到 1/4 的旧作工艺沿用) |
---
## 8. 语音配额(拥挤控制)
打击乐密度高,语音是稀缺通道。全表 49 id 双性别(41 新 + 8 向导沿用),
运行时配额:
1. **应答小节内零语音**(含 guide 插播——idle-only 语义天然保证)。
2. 教学纠正:每次失败至多 1 条纠正链(归因 1 句 + 重听 1 句),同一步不叠加。
3. 正片:连 2 Miss 才播 gd_miss,**同曲一次**;mid_song 每曲一次;
加花预告 tut_fill 每账号一次。
4. 探索模式区名播报每区每轮教学一次(重复摸只出声)。
5. 结算链 = grade + [new_best_combo] + [unlock] + restart,无其他插话;
结算 BGM 看门狗按链长缩放(旧作 P1 修复沿用)。
6. 语音风暴保险丝(引擎 3s/5 次)是上届,游戏层不得绕过它连播同 id。
---
## 9. 音频资产规格
### 9.1 打击乐 sfx(程序合成,tools/gen_rhythm_syn.py 新建)
7 件 one-shot:`inst_kick(沿用旧 id)/ dm_snare / dm_hat / dm_tomhi / dm_crash /
dm_tomlo / dm_accent`。QC 红线(gen_cipher_syn.py 同款):去直流 + 首尾 8ms cos² 淡化 +
编码后解码回读真峰值 ≤ -3dBFS(MDCT 过冲坑,超限自动降档重编)。起音必须硬
(盲玩对拍靠起音);kick/snare/hat ≤0.3s,tom ≤0.4s,crash ≤1.3s,accent ≤0.1s。
### 9.2 BGM(3 首无鼓伴奏,Mureka ≤$0.30)
| 曲 | BPM | prompt 特征 |
|---|---|---|
| track90《启程》 | 90 | bright optimistic pop rock backing, bass guitar + strummed acoustic + light synth pads, **no drums no percussion**, steady 90 bpm, A minor |
| track105《追风》 | 105 | driving energetic pop backing, punchy bass + rhythmic guitar chops + synth, **no drums no percussion**, steady 105 bpm, A minor |
| track120《星爆》 | 120 | high energy synth rock backing, aggressive bass + arpeggio synth + power chords, **no drums no percussion**, steady 120 bpm, A minor |
- `bpm_precise.py` 实测:|偏差|>0.5 BPM 则 atempo 拉伸到标称(R3 track90 先例),
成品复测记录台账。
- 后处理:强 onset 起点 → 整数小节裁剪(时长为 4 拍整数倍)→ 末 2 拍与开头
acrossfade(tri) 无缝循环 → loudnorm I=-20/TP=-2/LRA=11 → 44.1kHz → ogg q3 + m4a 128k。
- 旧 track90/105/120(含鼓)删除;`bgm/menu`(Mureka 状态曲,无鼓问题不影响
玩法——menu pad 不与演奏同响)保留沿用。
### 9.3 语音
见 24-rhythm-voice-ids.md:41 新 id 双性别(Qwen3-TTS,flock GPU),
gen_voice_dual.py rhythm 段全量替换;`--check`(时长/峰值/两性离群)+
`--convert`(ogg q3 + m4a 128k)。废弃 id(tut_1/tut_2/tut_3/retry_gesture
及旧 welcome/rules/retry_* 文案)的音频文件随 convert 后清理删除。
---
## 10. 进度存储
沿用旧作 schema 1 结构(`{tutorialDone, unlocked, grades, bestCombo, summary}`),
key `soundgame.rhythm.progress` 不变;PUT `/api/progress` 带 summary
(`《曲名》X级 · 已解锁N/3首` 取最高评级曲目)。解锁规则不变(B 级解锁下一首)。
结算手势:单击=再来一遍(同曲)、右滑=下一首(已解锁)、下滑=回大厅,不变。
---
## 11. 断言清单(web/rhythm/test/playtest.mjs 覆盖全集)
> 13 号文档 §4 rhythm 段是旧玩法的断言,已被本文取代但**不回改**(13 号
> 文档冻结);本节是 24 号文档的断言明细,按 §编号可被测试与评审引用。
- **A1 分区映射**:`zoneOf` 3×2 九宫边界正确(角落/边界点逐一断言);payload
缺失兜底 BC。
- **A2 判定窗**:|dt|≤0.15 → Perfect(+accent 叠加);0.15<|dt|≤0.35 → Good;
|dt|>0.35 同区出手 = 自由演奏(出声不判定、不判 Miss)。
- **A3 错鼓 Miss**:异分区音符 GOOD 半窗内被敲 → Miss(闷镲 + error 震 +
combo 清零;区名播报限教学期——正片错音的归因就是那一声敲错的音色本身,不再叠语音(配额)。
- **A4 超时 Miss**:expected+0.45s 无应答 → Miss(GOOD 0.35 + 放宽 0.10)。
- **A5 序曲 tap 撤销**:immediateTap 按下认领 → 引擎 tapcancel → 认领撤销
且不计 Miss(统计回滚)。
- **A6 双击=两连击**:300ms 内同区两 tap + doubletap → 两次敲击各自独立
(可认领两个相邻音符/一次认领一次自由演奏),无撤销、无崩溃。
- **A7 教学流程**:T0 探索摸 3 区(区名每区一次)→ T1-T3 跟打通过 →
`tutorialDone`;全程零 Miss 音、零计分。
- **A8 教学重教**:单步失败 2 次 → 降速 25% 重演(gd_slow,每教学局 1 次)→
再败跳过(retry_skip)不卡死;归因真话律(没出手 → gd_miss_none)。
- **A9 数拍**:曲目开始先排 4 记底鼓 count-in(1 小节)再进句 1 领奏;
网格对齐 BGM t0。
- **A10 菜单**:main 中下滑**无条件**呼菜单(无 down 音符折中);暂停冻结
(泵停、BGM 断点);恢复后 cue.expected/_bgmT0 平移量 = 暂停时长 +
count-in 小节;guide full 档恢复先播 gd_count(每曲限 1 次)。
- **A11 加花**:fill 句领奏为八分密集 + 尾音 TR;应答窗口与普句同规则;
曲目一终曲加花前 1 小节播 tut_fill(每账号一次)。
- **A12 结算评级**:全 Perfect → S + `grades.song1==='s'` + `unlocked===2` +
unlock_song2 播报;B 级也解锁;C 级不解锁。rate 公式与阈值逐档断言。
- **A13 结算导航**:单击=同曲重开、右滑=下一首(未解锁播 song_locked)、
song3 末位右滑播 restart 不播 song_locked(旧作裁定①回归)。
- **A14 连击里程碑**:16 连击 success 震、乐句全 Perfect [20,40,20]。
- **A15 mid_song**:曲中点播报且领奏 duck 到 1/4、播完恢复。
- **A16 进度同步**:PUT body 含 summary/grades/unlocked/bestCombo;B 级提升
才覆写 grades。
- **A17 语音配额**:应答小节内零 voice 调度;gd_miss 同曲一次;探索区名
每区一次(转写级断言放 transcribe 剧本核)。
- **A18 键盘平权**:Digit1-6 注入 = 对应区命中(零延迟判定);空格 tap
(元素中心坐标)→ BC;选曲态数字键无效。
- **A19 guide 接入**:select=menu / main=battle / end=settle 状态签名同点;
首个应答窗口前 1 小节 gd_answer+turn。
- **A20 资产完整**:check_all_assets.py rhythm 段全绿(含新 sfx/bgm/voice 双性别)。
- **A21 旁白跳过**:intro/tut(引导语音)/select/end 语音播放中单击=跳句(12 号
文档 §1.2);main 演奏期单击是演奏不跳。
- **A22 全局**:启动遮罩前无音频;无未捕获异常;settings.voice 切换下一句生效;
bgm=0 时不加载 BGM。
- **A23(R1 F3)** 教学没出手失败的纠正链 retry_listen 只播一次(不二连复读)。
- **A24(R1 F1)** 曲尾 3s 内连 2 Miss 不插播 gd_miss(结算链不被顶断)。
- **A25(R1 F2)** 探针三连摸的三个区名全部播完不被掐断,教学句 queue 其后
(进阶延迟 150ms 起步:引擎入队判定在 await 加载之前,同步连发会误判)。
---
## 12. 资产预算与台账
- 语音:49 id × 2 性别 = 98 文件(Qwen3-TTS,flock .gpu.lock,零 API 费)。
- 打击乐:7 件程序合成(零成本)。
- BGM:3 任务 × $0.03,每首至多 1 次重提 → 上限 $0.18(预算线 $0.30,
累计 $1.26/$2.00 → 本轮后 ≤$1.44+$0.18=$1.56 上限)。台账追加
tools/mureka_log.md。
- 开发/测试/转写:零成本(复用 harness)。