符号乐谱输出(Symbolic Score)
一个发射器,四个目标格式,两个用途。
ARR-SPEC(+ LYR-SPEC 的歌词)
↓ mc-symbolic-score
├─→ ABC → YuE2 的 abc= 输入(★ 让规格被"执行"而不是"提示");大模型的母语
├─→ MusicXML → 记谱软件 / 交接给乐手(领奏谱:旋律、和弦、歌词、段落、力度、换气)
├─→ MIDI → DAW + 音源,确定性渲染
└─→ ChordPro → 吉他手、排练(段落、和弦、歌词)
为什么是同一个发射器:ABC ↔ MusicXML ↔ MIDI 可互转,ChordPro 是前三者的子集, 四者承载的是同一套信息(音高、时值、小节、调号、拍号、力度、和弦、歌词)。 分成四个 skill 会让同一份规格产生四份不一致的乐谱。 规格单可执行字段的拼写就是按这四种格式定的(schema §1.3.1、§4),发射器不需要翻译符号。
按任务读哪几节
| 任务 | 读 |
|---|---|
| 给 YuE2 供谱 | §3 ABC + §7 |
| 出 MIDI 给 DAW | §5 |
| 出 MusicXML 交给乐手 | §4 |
| 要一个 AB 的确定性基准 | §2.2 |
| 级数转字母 | §3.3 |
| 表情/力度怎么写进 MIDI | §5.2 |
| 发射完怎么验 | §8 |
边界
| 不归这里 | 归哪 |
|---|---|
| 提示词、段落标签、后端选择 | mc-render-compile |
| 旋律/和声/对位的内容本身 | L1 各 skill |
| 用什么音源、采样库选型、keyswitch | 出库 → 制作工具库(不在本包);MIDI CC 在配器上的含义归 mc-orchestration reference §5 |
| MIDI CC 在配器上的含义 | mc-orchestration reference §5 |
| 量化、velocity 的律动设计 | mc-rhythm-section |
| 记谱法的完整规范(连音线、装饰音写法) | reference.md |
1. 动笔前必填
| 必填 | 问法 |
|---|---|
| ① 目标格式 | ABC / MusicXML / MIDI / ChordPro。能承载的不一样(§6);meta.deliverable 里列了哪些就发哪些 |
| ② 声部数 | 只发主旋律+和弦?还是全谱? |
| ③ 谁来读 | YuE2 / DAW / 人。决定详细程度 |
| ④ 这次是不是做基准 | 是的话所有随机性都要关掉 |
2. 为什么要走符号路径
2.1 它是唯一能让规格被"执行"的路
其余后端都只能"提示"。YuE2 的合成步骤直接吃 ABC——
form[].harmony_letters / meta.key / meta.meter / meta.tempo
编译成 ABC 之后,它们就不再是建议了。
把模型自己的符号规划那一步整个旁路掉,把我们的 ABC 直接送进合成步骤。 支持 YuE2 的载体都能这么接(见
mc-render-compile§8.2);怎么接线是用户环境的事。
2.2 它是唯一完全可复现的输出
同一份规格渲染出来永远一样。
这让它成为 AB 实验的控制变量基准: 想知道"改这一个字段到底带来多大差别",用符号路径渲染两版,差别 100% 来自那个字段。 生成后端做不到这一点(即使固定种子,改提示词也会改变别的东西)。
2.3 日式打ち込み 工作流本来就是这个形态
LLM 直出 MIDI/MusicXML,进 DAW,配音源。 ★ 这条路没有人声,人声要另接(或走生成后端做人声、符号路径做伴奏)。
3. ABC 的映射
3.1 头部(Header)
| ABC 字段 | 来自 | 例 |
|---|---|---|
X: |
固定 1 | X:1 |
T: |
meta.title |
T:Night Drive |
M: |
meta.meter |
M:4/4 |
L: |
默认单位时值(通常 1/8 或 1/16) | L:1/8 |
Q: |
meta.tempo |
Q:1/4=112 |
K: |
meta.key |
K:D |
★ tempo_map 有多项时:Q: 取首项,后续变速写成小节内的 [Q:...] 内联域。
3.2 主体
| 内容 | 来自 | 写法 |
|---|---|---|
| 小节线 | form[].bars / start_bar |
` |
| 段落标记 | form[].id + name |
★ 写成注释 % 或 [P:A1] 分段标记,见 §7.2 |
| 和弦 | form[].harmony_letters |
"F#m7" 放在音符前的引号里 |
| 旋律 | L1 写的旋律 | 标准 ABC 音高记号 |
| 反复 | 段落重复 | ` |
3.3 ★ 级数 → 字母
ARR-SPEC 里 harmony 是级数、harmony_letters 是字母,两者双记。
发射 ABC 时用字母,但必须校验两者一致。
校验动作:把 harmony_letters 按 meta.key 反推成级数,
与 harmony 逐和弦比对。对不上就是规格里有笔误——
这是 ARR-SPEC 双记号约定的实际用处,不只是为了可读。
移调表见 mc-progressions reference.md §8。
3.4 ★ 反复记号的陷阱
ABC 的 |: :| 会让两遍完全一样。
但本库的 lint #16 要求副歌逐遍有变化——
所以多数情况下不要用反复记号,要把每一遍展开写。
★ 例外:确实完全一样的段落(比如纯器乐的 vamp)可以用反复。 但副歌永远展开写。
4. MusicXML
用途:交给记谱软件、交给乐手。
它比 ABC 多能承载的:
- 完整的谱表布局、声部分配
- 演奏法记号(弓法、连音线、articulation)
- 力度记号(p / mf / f 及渐变)
- 歌词(逐音节对齐)
它比 MIDI 多能承载的:记谱意图(同一个音高时值,是升记号还是降记号、是三连音还是附点)。
★ 什么时候必须用它:mc-orchestration 的产出要交给真人演奏时。
ABC 和 MIDI 都表达不了弓法和分谱。
5. MIDI
5.1 基本映射
| 内容 | 来自 |
|---|---|
| 音符 | 旋律与和声 |
| 轨道/通道 | arrangement.roster[],一件一轨 |
| 音源映射 | roster[].midi({program, bank, channel},GM 号,鼓组 channel 10);render.midi.instrument_map 只放 VST 名等备注 |
| 速度 | meta.tempo / tempo_map |
5.2 ★ 表情:velocity 与 CC
MIDI 相对 ABC 的最大优势是能写连续控制。
| 来自 ARR-SPEC | 写成 |
|---|---|
arrangement.energy_curve |
CC11(表情)曲线 + velocity 层次 |
vocal.dynamics_by_section |
人声轨的 CC11(若有人声音源) |
groove.push_pull |
★ 音符位置的实际偏移(这是唯一能精确执行 push_pull 的路径) |
groove.quantize_policy |
逐轨的量化程度 |
roster[].synth_params |
音源参数或 CC74(亮度)等 |
★ CC7 与 CC11 的分工不能混(→ mc-orchestration reference §5.1):
- CC7 定这一轨在配器里的位置
- CC11 定这一句怎么呼吸
混用的结果是"推了推子但乐句还是死的"。
5.3 ★ 符号路径上 push_pull 是 exact
backends.yaml 里 symbolic 后端的 groove 相关项是 exact——
因为音符位置可以精确写。
这是验证"push_pull 到底有没有用"的唯一可靠手段:
同一份谱,一版对齐网格、一版按 push_pull 偏移,直接对比。
6. 四种格式能承载什么
| ABC | MusicXML | MIDI | ChordPro | |
|---|---|---|---|---|
| 音高/时值/小节 | ✅ | ✅ | ✅ | ❌ 只有段落 |
| 调号/拍号/速度 | ✅ | ✅ | ✅ | ✅ 头部指令 |
| 和弦符号 | ✅ | ✅ | ⚠ 只能作为文本事件 | ✅ |
| 歌词 | ✅ 基本 | ✅ 完整 | ⚠ 只能作为文本事件 | ✅ |
| 记谱意图(升/降、三连音写法) | 部分 | ✅ | ❌ | ❌ |
| 演奏法记号(弓法、articulation) | ❌ | ✅ | ⚠ 只能靠 CC/keyswitch 近似 | ❌ |
| 连续控制(CC 曲线) | ❌ | ❌ | ✅ | ❌ |
| 微时间偏移(push_pull) | ❌ | ❌ | ✅ | ❌ |
| YuE2 直接吃 | ✅ | ❌ | ❌ | ❌ |
★ 结论:
- 要 YuE2 执行 → ABC
- 要交给人 → MusicXML
- 要表情与律动 → MIDI
- 要给吉他手和排练 → ChordPro
- 几个都要就几个都发,但从同一份规格发,不要各写各的
6b. YuE2 方言:实测出来的四条(2026-09-17)
同一份从 ARR-SPEC 编译的谱,三种写法送进 YuE2 的 abc 口,结果差得远:L:1/16 单声部出 2:18(谱面 4:00,模型提前停),删掉段落注释更短(1:58),改成 L:1/32 跑满 3:56。所以给 YuE2 的 ABC 照它自己写谱的样子来:
L:1/32:所有时值按三十二分音符计,四分音符写 8、附点二分写 24、整小节休止z32。用L:1/16它会把谱当成一半长,提前结束。 ⚠ 但只改这一条不够:同一份谱改成L:1/32后时长跑满,whisper 却一个字都识别不出(直出版 76%,模型自写谱 65%,这版 2%)。原因在旋律本身:每个字的时值太长、音太少、音区比模型自写的谱低一个八度。给 YuE2 的人声线要像它自己写的那样:小写音名区(c 到 a 一带)、每小节四到六个音、单字很少超过一拍。实测:同一份谱整体升八度到小写音名区,whisper 识别率从 2% 到 47%;再加上 w: 歌词行到 56%(直出是 76%)。这就是第 5、6 条的来历:带
w:歌词行(见第 6 条)。声部头写
V: Vocal clef=treble name="Vocal Melody" snm="Vocal",段落用% intro/% verse/% pre-chorus/% chorus/% bridge/% outro注释;它自写的谱还有一条V: Ins声部放器乐句,单声部也能跑满。标题行
T:可空;调用K:D这类标准写法,模型自写时倾向关系小调(同一首它写成K:Dm),不影响。人声线写在小写音名区(ABC 的 c 到 a,即 C5 到 A5 一带),大写区它会唱成含混的元音;这与真人音域无关,是模型的习惯。
带
w:歌词行(每个音符一个字,字数与音符数逐行相等)比只在 lyrics 口给词识别率高;w: 行照带,lyrics 口也照给。待做:旋律密度。编译器目前给每个字配长音(附点二分、全音符),模型自写谱每小节四到六个音、单字很少超过一拍;识别率剩下的差距多半在这里。
ABC 表达不了的规格字段(减法事件、残响切断、逐件进退场)只能靠 style 文本,见 mc-render-compile。
7. 给 YuE2 供谱的实操
7.1 接法
[我们的 ABC] ──→ YuE2GenerateMusic ──→ 音频
↑
(YuE2GenerateABC 被旁路掉)
本 skill 只管左边那一格:把 ARR-SPEC 编译成 ABC 文本。
右边这条线为什么要接、接了之后规格为何不再只是"提示"、
用什么载体(官方脚本、ComfyUI 节点)接线,见 mc-render-compile §8.2。
7.2 方言已经摸清
YuE2GenerateMusic 的 abc 输入端吃什么,§6b 已按实测写死:段落用 % 注释,和弦用引号里的标准拼写("F#m7"),V: Vocal 加可选的 V: Ins 两个声部都能跑。仍不确定的东西只剩旋律密度那一刀(§6b 第 7 条)。
7.3 上游有官方 skill
YuE2 官方仓库自带 skills/yue2-music/(SKILL.md 格式,含"检查音乐不变量")。
★ 不要重写它。 我们写 ARR-SPEC→ABC 的适配器,模型操作的知识引用它们的。 它的 musical invariants 应该与我们的 20 条自查 对齐或互补——
8. ★ 发射后的验证回路
不要把没验过的乐谱送进合成器。 三步:
| # | 检查 | 怎么做 |
|---|---|---|
| 1 | 语法 | ABC → 用解析器过一遍;MusicXML → schema 校验;MIDI → 读回来看轨数与时长 |
| 2 | 与规格一致 | ★ 小节总数 == Σ form[].bars;调号拍号速度对得上;字母和弦反推级数 == harmony |
| 3 | 听一遍最简渲染 | 用任意音源快速渲染,只听结构对不对,不听音色 |
★ 第 2 步的小节总数校验最值钱—— 发射器最常见的 bug 是段落边界算错,而那个错误在音频里表现为"结构乱了", 很容易被误判成后端不听话。
这三步用眼睛就能查;仓库内部若做自动化也不随包发布。
实测过的一条路(2026-09):本 skill 写法的 ABC(含 w: 行、引号和弦、% 段落注释、V: 头)用 music21 解析零警告,能直接转出 MIDI 与 MusicXML,MIDI 配任意 GM 音色就是第 3 步的最简渲染,不经过生成模型,倒字与旋律密度在这一步就能听到。两个注意:music21 不读 w: 行,交给乐手的 MusicXML 要用会读 w: 的转换器(abc2xml、abcm2ps 一类)或按"每个音符领一个音节"自行贴回;引号和弦默认不发声,想听和声垫底要自己把它们展开成一条钢琴声部。
9. 与 ARR-SPEC 的字段对应(四个导出目标)
规格单每个字段都有去向(schema §4)。可执行的字段按下表落到四种格式;本 skill 负责其中的发射规则。
| ARR-SPEC 字段 | ABC | MusicXML | MIDI | ChordPro | 本 skill |
|---|---|---|---|---|---|
meta.title |
T: |
work-title | 轨名 | {title} |
§3.1 |
meta.key / mode |
K:(K:Ddor) |
key 的 fifths + mode | 调号 meta | {key} |
§3.1 |
meta.meter / meter_map |
M:,行内变拍 |
time,逐小节 | 拍号 meta | {time} |
§3.1 |
meta.tempo / tempo_map |
Q: |
metronome / sound tempo | set_tempo | {tempo} |
§3.1 |
form[].id / name |
P: 或 % 注释 |
rehearsal 记号 + words | marker | {start_of_verse} 等 |
§3.2 |
form[].bars / start_bar |
小节线 | measure number | tick | §3.2;§8 第 2 步 | |
form[].harmony_letters |
"F#m7" |
harmony 的 root / kind / bass,映射见 schema §1.3.1 | 和弦轨或 marker 文本 | [F#m7] |
§3.3 |
material.motif_abc / arrangement_hook.abc |
原样嵌入 | 实际音符 | 实际音符 | §3 | |
roster[].id / midi |
V: 名 |
part-name + midi-instrument | 轨名 + program change | §5.1 | |
roster[].entry / exit、form[].events |
段前 Z 多小节休止 |
多小节休止 | 轨内容范围 | §5.1 | |
energy_curve、vocal.dynamics_by_section |
!p! !f! 力度记号 |
dynamics(映射见 schema §1.5) | 力度缩放 / cc11 | §5.2 | |
groove.push_pull |
无 | 无 | tick 偏移(★ 唯一 exact 的路径) | §5.3 | |
vocal.breath_points |
!breath! |
breath-mark | 无 | §4 | |
mix_intent.sound_stage[].pan |
无 | midi-instrument 的 pan | cc10 | §5.1 | |
depth_map |
无 | 无 | cc91 | §5.2 | |
LYR-SPEC lyrics[].lines |
w: 行 |
lyric,syllabic=single,中文一字一音节 | 无 | 歌词行 | §3 |
render.yue.abc |
本体 | §7 | |||
render.musicxml / render.midi / render.chordpro |
目标开关 | 目标开关 | 目标开关 | §4 §5 |
仅意图的块(intent、material 的文字、mix_intent 与 vocal 的大部分)整块作为文本随文件走:ABC 用 % 注释,MusicXML 用 miscellaneous,MIDI 用第一小节的 text 事件。四个目标各给谁、带什么,见 schema §4。
10. 发射完必过
- 目标格式是按 §6 选的,不是习惯性选 MIDI
- 小节总数 ==
Σ form[].bars - 调号、拍号、速度与
meta一致 - ★ 字母和弦反推级数,与
harmony逐和弦对得上 - 副歌各遍是展开写的,没有用反复记号(lint #16)
- MIDI 里 CC7 与 CC11 分工正确
- 若要验证 push_pull,做了对齐版和偏移版两份
- 送进合成器前跑过 §8 三步
- 给 YuE2 供谱时,用的是已确认的 ABC 方言子集
附:来源
- §8 的三步校验是人工的;其中"小节总数"与"和弦级数反推"两项是纯数据校验,不需要听。
- ⚠ 未确认:YuE2 的 ABC 方言(§7.2)
- 记谱法细则、ABC 语法速查、GM 音色表在 reference.md。