助手的 Strum(扫弦)和 Tab(指弹)两种模式靠规则读句子,不经过模型。这里写下的就是这些规则能读懂的句法:一方面让玩家知道该怎么打字,另一方面让 src/lib/assistant/general/tools.ts 里的工具描述有个对照的基准。General 模式会把玩家的原句一字不改地交给同一套读法器,所以这里说的在 General 里同样成立。
代码才是最终依据。每一节都标了读它的文件;这一页和代码说得不一样时,改其中一个。
有两条规则贯穿始终:
- 一句话要么整句读懂,要么不读。 每个读法器都会把它读懂的部分去掉,再看剩下什么。客套话("give me"、"请"、"a bit"、"的")会被忽略,但只要有一个词读法器放不进去("C G Am F but dreamy"),这句就算没读懂。回复会说明读到了什么,并给出几句本来能读懂的写法。
- 光凭一句话不会保存任何东西。 请求会变成一张卡片等玩家确认;修改会说明它要改什么,然后等着。
回复的语言跟着消息走:句子里有汉字就用中文回答,否则用英文。和弦字母和记谱符号不带语言。
两种模式都先读的东西
这几样最先跑,按这个顺序,针对整条消息。
闲聊(smallTalk.ts)。"hi"、"thanks"、"what can you do"、"你好"、"在吗"——只匹配整条消息——会得到一句回应和几句起手句。它们永远不会被当成请求读。
和弦提问(chordAsk.ts)。问某个和弦怎么按,在任一页面都会用指法图回答:
| 英文 | 中文 |
|---|---|
how do I play F#m7 |
C和弦怎么按 |
show me C Am F G |
看看 Am |
F chord、Cmaj7 fingering、C G Am F chord shapes |
F#m7 的指法、怎么弹 G、Bm 和弦 |
what's the shape for Bb |
请问 C G Am F 的指法 |
一串里的每个词都得是和弦库里以那个名字存着的和弦;有一个不认识,整句就算没读懂,不会只回答其余的。光写 play C G 不算提问——在扫弦页上那是在要一个节奏型。
错字(fuzzy.ts)。读法器认识的词——风格词、速度词、修改动词、strum、pattern、capo、string、fret、拍号和音符时值的词——会在读之前按编辑距离纠正:五六个字母的词允许错一个,更长的允许错两个,中文词允许错一个字。四个字母的词只有 string/fret 后面跟着数字时才纠正,所以 "slow" 不会变成 "show"。和弦名和玩家自己的 pattern 名永远不纠正。回复会坦白:Took "travs" as "travis".
两种模式共用的子句
这几样在 Strum 和 Tab 里读法相同(strum/readPhrase.ts、strum/readCapo.ts),会先从句子里抹掉,别的读法器才接着读。
速度。 写明的速度按字面取:92 bpm、at 92 bpm、92拍。两位或三位数。
名字。 新 pattern 该叫什么:name it test、call it sunday、call this X、named X、name: X、叫 test、叫做 X、命名为 X、取名 X、名字叫 X。引号可有可无(叫「夏天」)。名字一直取到下一个标点,最多 40 个字符;结尾的 "pattern"、"模式"、"节奏型"、"的" 会去掉。名字里的和弦词属于名字(call it C jam)。
变调夹。 capo 2、capo on 2、capo at the 4th fret、capo on fret 2、2nd fret capo、变调夹 2 品、变调夹夹在第 2 品、夹三品、二品变调夹。no capo、without a capo、不用变调夹、无变调夹 表示 0 品。0 到 12 品,阿拉伯数字或到十二的中文数字都行;capo 15 不算变调夹,会留作没读懂。变调夹属于和弦进行——没有和弦可移调时它会被丢掉,回复会说明。
扫弦
由 strum/router.ts、strum/readPhrase.ts、strum/parseRhythm.ts 和 strum/editIntent.ts 读,顺序是:先修改句,再严格的和弦/节奏行,最后是词表读整句。
和弦行
和弦词之间用空格、逗号、、、竖线或短横分隔。每个词都要能通过和弦选择器用的那套排序搜索解析出来,否则这行不算和弦行。
C Am F G
c am f g
C-G-Am-F
Am | F | C | G
C/G G/B Am7 Fmaj7
D# Gb A#m → 存为 Eb F# Bbm
单独的数字永远不是和弦。匹配不上的词(C Am Zq F)会让这行读失败;回复会报出那个词,而不是把它丢掉。
只写和弦行,得到的是配默认节奏 Old Faithful(D DU UD)的和弦进行,卡片会说明节奏是猜的。
节奏记谱
一个字符一格,和 patternNotation 写出来的记谱相同。空格是空格子,不是分隔符。
| 写法 | 格子 |
|---|---|
D d 下 |
下扫 |
U u 上 |
上扫 |
X x 〇 |
闷音 |
空格、 、.、-、_、· |
空(不扫) |
| |
小节线 |
D DU UD
DUDUDUDU
D-DU-UDU → D DU UDU
下上下上
D DU UD|D DU UDU
一小节四拍。格子数决定细分:最多 4 格是每拍一格,最多 8 格是八分,最多 12 格是三连音,最多 16 格是十六分,最多 24 格是六连音;超过 24 格报错("用 | 分小节")。后面的小节沿用第一小节的细分。格子不够的小节会用空格子补齐并标出。结尾的空格子会丢掉。扫弦里没有拍号词——这里一小节就是 4/4;别的拍号去 pattern 编辑器里设。
幽灵扫弦从不写入:打了什么存什么,来回摆动的手是从实扫的格子推出来画的。
小节线两侧要紧挨着写:| 旁边的空格算一格,D DU UD | D DU UDU 会让第二小节变成九格而溢出。
一行里同时写和弦和节奏
段落在 , , 、 ; ; 和换行处切开。每一段必须是和弦行或节奏,节奏最多一段。打平手时和弦赢:单独一个 D 是和弦。
C Am F G, DUDUDUDU → 每个和弦一小节那个节奏
DUDU, UDUD → 两段节奏是一句话,不是节奏型:不读
一句话
严格行读失败时,词表会来读这句——而且只读整句。它认这些:
一串和弦:至少两个和弦挨在一起,用空格、逗号、
、、短横、→、>或|连着。句子别处单独出现的一个和弦(folk in the key of C)算落单,会让整句读不成——调不是和弦进行,大写的 A 也可能只是冠词。写出来的节奏:一串记谱词,至少两个实扫格子,除非每个词本身都是和弦(
D D D是和弦)。风格词,它会选一个节奏和一个速度。节奏会被标成猜的。
词 节奏 BPM 民谣、folkD DU UD80 流行、popD DU UD95 摇滚、rockD DU UDU110 抒情、慢歌、balladD DU UD65 速度词:
slower、slowly、slow、慢一点、慢一些、慢点、慢些、缓慢、舒缓、慢;faster、fast、quick、upbeat、快一点、快一些、快点、快些、快。它把风格的速度(没有风格就是默认的 80)挪 15%,取整到最近的 5。写明的速度优先于速度词;快慢两种词同时出现就是一句话,不读。速度、名字和变调夹,同上。
客套话会被去掉:给我一个、来个、帮我、我想要、请、一个、一段、扫弦、节奏型、伴奏、和弦进行、风格、感觉、稍微、的、吧、呢;give me、i want、can you、please、make it、something、strumming、strum、pattern、progression、chords、tempo、a bit、a little、more、and、with、for、the、in、at、of、a、an、some。
给我一个 C-G-Am-F 的民谣扫弦,慢一点 → C G Am F,D DU UD(猜的),70 bpm
give me a slow folk strum in C G Am F → 同上
C G Am F 慢一点 → 默认节奏,放慢到 70
来个摇滚节奏 → 只有风格也是一个节奏型
fast rock, C G D → D DU UDU,125
C G Am F at 92 bpm
D DU UD in 140 bpm, name it test
C G Am F, capo 2
抒情一点的 Am F C G
因为有词剩下或缺了意思而读不了的:给我一个 C 调的民谣扫弦(是调,不是和弦)、something like Wonderwall、C G Am F but dreamy、三拍子的华尔兹 C G7(扫弦里没有拍号词)、单独的 慢一点(没有可放慢的东西)、DUDUDUDU but muted on 2 and 4。
修改句
修改句要点名玩家自己的某个 pattern,并说对它做什么。它在和弦行之前读,所以 add C G to belief 是修改,不是三个和弦的进行。三个部分,中英文都行,顺序随意:
动词。
| 操作 | 英文 | 中文 |
|---|---|---|
| 挂上和弦 | add、append、attach、put |
添加、新增、加上、加入、加到、加进、放到、放进、配上、加 |
| 改名 | rename、call |
改名、重命名、改叫、命名 |
| 删除 | delete、remove |
删除、删掉、删了、去掉、移除 |
call 和 命名 只有在句子里点了玩家自己的 pattern 时才算改名;否则 call it X 是在给新东西起名。
对象。 玩家库里的一个 pattern 名,整词匹配、不分大小写;能对上的名字里最长的赢(add C G to old faithful 不是在改 old)。一个字符的名字不会去找。引号和名字周围的词都没关系:the belief pattern、「夏天」、belief 里。
没点到玩家自己的 pattern 时,句子看起来指向的名字——在 to/into/onto/in、到/去/给/进 之后,或在引号里的——会被报出来,并问玩家指的是哪个。
和弦(只对挂上)。任何长得像和弦的词,不管库里有没有:Em9、C#、F#m7、Cmaj7#11、G7b9。这里小写也读(add c am f g to on the beat),只是单独一个小写字母会被忽略(add a progression to belief)。and、then、plus、和、与、及 用来连接列表。夹在两个和弦之间的短生词会被当成玩家想写的和弦一起带上,并标红(C G AM RM F C 会把 RM 留在原位)。库里没有的词会标红,绝不丢掉。
新名字(改名):跟在 to、as、改名为、改名成、改名叫、重命名为、改叫、命名为、叫做、叫 之后。没写新名字的改名会追问。
add C G Am F to belief
add Em9 D C# F#m7 to belief
add C G Am F to belief with capo 2
add C and G to belief
把 C G Am F 加到 belief 里,变调夹 2 品
给 belief 加上 C G Am F,夹三品
添加一个 Em9-D-C#-F#m7 和弦进行去 belief 里
rename belief to faith
把 belief 改名为 faith
belief 改叫 faith
delete belief
remove the wonderwall pattern
删掉「夏天」
把 belief 删了
删除会删掉整个 pattern 和它上面的所有和弦进行。句子里点了和弦或说了 "progression"、"进行"、"和弦"(delete the C G Am F progression from belief、把 belief 的那个和弦进行删了)时,玩家想删的是某一条进行,会被拒绝并指向 progressions 标签页。内置 pattern 不能改名也不能删除。
指弹
由 tab/router.ts、tab/readTabSentence.ts、tab/parsePickOrder.ts(基于 fingerpickPickSequence.ts)、tab/parseStringFret.ts、tab/parseAsciiTab.ts、tab/styles.ts 和 tab/editIntent.ts 读。先读修改句,再读贴进来的 tab,最后读句子。品格永远来自库里和弦的指型或句子里写的数字——读法器从不自己编。
和弦加分解顺序
先和弦词,再右手顺序。冒号可有可无。
Am: 5 3 2 1 3 2 1 3
C G: R_32^132R_32^132
Em 根3231323
C: 6(32)1(32)
C: 5/4 2 1 3
顺序里的记号(fingerpickPickSequence.ts、tab/parsePickOrder.ts):
| 记号 | 含义 |
|---|---|
1 – 6 |
一根弦,1 = 高音 e,6 = 低音 E |
根、R、r |
根音:大拇指弹和弦根音所在的那根弦,所以同一个顺序能跟着和弦变化走 |
0、- |
休止 |
_、^(也可写 _、^) |
延音:前一格继续响一格。前面必须有一个音 |
( … )(也可写 ( )) |
同时拨:几根弦一起弹,(32)、(R1) |
5/4、6/4、6/5 |
交替低音:大拇指这次弹第一根弦,下次弹第二根。顺序会被写两遍,所以 5/4 2 1 3 就是 5 2 1 3 4 2 1 3 |
记号之间的空格可有可无。一串至少要两格,且至少有一格拨弦;单独一个数字可能是品格、是数量、是什么都行,不算顺序。
音符时值。 没说就是八分:/16、16ths、sixteenths、十六分;/8、eighths、八分。(/4 不会被读成时值——5/4 是大拇指。)没写时值而顺序的格数超过一小节的八分、又能把小节均分成某个普通时值时,就是一小节那个时值:十六格就是一小节十六分。十二格仍按八分,会流到第二小节。
填满小节。 比一小节短、又能整除小节的顺序会重复到填满——5 3 2 1 是那个分解,不是半小节。其余情况在结尾处用休止补齐,卡片会说明。延音会把前一个音拉长,条件是拉长后的音是一个普通时值、且不跨拍,或者从它自身长度能整除的位置起占整拍;跨拍线或跨小节线时,写成连音线连着的新音。
每个和弦过一遍。 顺序会按和弦词的先后各写一次:C G: … 是两小节。和弦指型里没有的弦会写成 x(闷音)并报出来。完全没有和弦时写空弦,并会说明。
只有和弦,或一个风格词
只有和弦行、没有顺序,会按分解和弦弹,标成猜的,下面列出常用顺序(53231323、R3231323、R323、R3(12)3)。风格词则取内置 pattern 的第一小节,铺到每个和弦上:
| 词 | Pattern |
|---|---|
travis picking、travis、三指法、交替低音 |
Travis |
arpeggio、arpeggios、arpeggiated、分解和弦、琶音 |
Arpeggio |
waltz、华尔兹、圆舞曲 |
Waltz(3/4) |
celtic fingerstyle、celtic、凯尔特 |
Celtic |
C G Am F
travis picking in Am
Am F 三指法
waltz in C
歌曲预设(斑马斑马)是旋律,不是风格,没有对应的词。
拍号
3/4、6/8、in 12/8、3/4拍、四四拍、四拍子、三拍子、四三拍、三四拍、二拍子、四二拍、六八拍、八六拍、十二八拍。只读应用支持的拍号——4/4、3/4、2/4、6/8、12/8——所以顺序里的 5/4 是大拇指。拍号决定一小节有多少格。
Em: R 3 2 1 2 3 in 3/4
Am: R 3 2 3 2 1 in 6/8
直接写弦号和品格
两个列表,一根弦对应一个品格,先后随意;每一对是一小节。
string:66544322, fret:8-11-10-8-10-8-8-11
弦:6654 品:8-11-10-8
Am string:654 fret:x-0-2
string:654 fret:0-2-2 /16
- 关键词是
string、strings、弦;fret、frets、品。冒号(:或:)可有可无。 - 弦号是一位数,可以连写,也可以用空格、逗号或短横分开。
- 品格可以用分隔符写成列表(
8-11-10-8),也可以连写、一位一个,两位数的品格用括号:5768(11)(12)。x是闷音。单独一组不超过 24 的两位数是一个品格(fret:11);超过琴颈的只能是两个品格。 - 两个列表长度必须相同,否则回复会说哪一小节差了几个。
- 比拍号短的小节用休止补齐;超出的会拆开。一条消息里的几对就是几小节,按写的顺序,所以一段乐句可以一小节一小节地打。
- 列表前的和弦词会标在这一小节上;品格仍按写的算。
ASCII tab
连续六行 tab 会被当成导入读,节奏从间距推出来。
e|-----0-----0-|
B|---1-----1---|
G|-0-----0-----|
D|-------------|
A|-------------|
E|-0-----0-----|
- 一行里某处有两个连着的短横就算 tab。弦名字母和开头的
|或:可有可无。默认高音 e 在最上面;E在上、e在下会按反过来读。 |或:是小节线。数字是品格(两位数是一个品格)。x是闷音,和同一列的其他音一起发声。- 技巧:音前的
h击弦、p勾弦、/、\或s滑音、t点弦;(7)是延音;音后的b推弦(7b9推到 9 品的音高,7b8r回落,7pb9预推,5b¼四分之一推弦)、~揉弦(~~大幅)、tr颤音。每个x都是它所在弦上的闷音,照原样读入。谱下的P.M.或let ring行标记其上方那段音。其他记号会跳过并给出警告。 - 一小节的字符宽度就是它的长度;一个音到下一个音的距离就是它的时值。宽度不能均分小节时会取最近的时值并标出。超出小节末尾的音会丢掉并标出。
- tab 周围的行只读名字、速度和拍号(
name it lick, 3/4, 90 bpm);那里别的东西,比如标题,会原样留着,不会被当成请求读。
句子里的和弦词
每个和弦词都得是库里以那个名字存着的和弦。小写没问题(am、g7、c);单独的 a 是冠词,除非后面跟着冒号(a: 5321)。拼得像和弦但库里没有的词会占住它的小节、写成休止,回复会报出它。
客套话去掉的和扫弦一样,再加上 指弹、分解、fingerpicking、fingerstyle、picking、pick、over、style。别的东西剩下来就是没读懂:给我 Em 温柔一点的 是一种感觉,读不了。
修改句
修改句要点名玩家的某个指弹 pattern,并说往里写什么。要写的小节和新建 pattern 的写法一样,按对象的拍号来排。内容按 ;、; 和换行切段。预设不能原地改:改预设会生成一份叫 "…(mine)" 的副本,副本一旦存在,预设的名字就指这份副本。
追加小节:add to NAME: SPEC、append a bar to NAME: SPEC、给 NAME 加:SPEC、在 NAME 里加上 SPEC、往 NAME 里添加 SPEC。
add to travis: string:6654, fret:8-11-10-8; Am: 5 3 2 1
add to my arp: travis in C
给 travis 加:string:6654, fret:8-11-10-8
在 my arp 里加上 Am: 5 3 2 1
替换一小节:replace bar N of NAME: SPEC(也可用 set、change、rewrite;in/on;冒号可换成 to/with)、把 NAME 的第 N 小节改成 SPEC。小节从 1 数起。
replace bar 2 of travis: C: 5/4 2 1 3
set bar 1 in my arp to travis in C
把 travis 的第 2 小节改成 Am: 5 3 2 1
在已有的小节上标和弦。 一小节或一个范围,一拍(按玩家数拍的方式)或一个格子(按编辑器网格的编号,优先于拍),再加和弦:
add chord Am to bar 2 beat 3 of travis
mark C G Am F on bars 1-4 of lick
add Am to measure 1 slot 3 of lick
lick bar 2 beat 3: Am
chords for lick bars 1-4: C G Am F
in lick, add Cm7 to bar 1, add F7 to bar 2 beat 3
add Am7, D7, Gm7 to bars 2-4 of lick
给 lick 第 2 小节第 3 拍加和弦 Am
lick 第 1-4 小节和弦:C G Am F
把 lick 的第 1 小节配和弦 C
给 lick 第 1 小节加 Cm7,第 2 小节第 3 拍加 F7
没说拍就是第一拍。一个和弦配一个范围,范围里每一小节都标上;几个和弦就一小节一个,范围至少要和列表一样长——或者不写范围,由列表决定。和弦比范围里的小节少时,最后一个和弦一直延续下去,和乐谱一样。
改名:rename NAME to NEW、rename NAME as NEW、把 NAME 改名为 NEW、重命名 NAME 为 NEW、NAME 改叫 NEW。只写 rename NAME 会追问新名字。
删除:delete NAME、remove the NAME pattern、删掉 NAME、把 NAME 删了。
速度或拍号:set NAME to 90 bpm、NAME at 72 bpm、change NAME to 3/4、set NAME to 3/4 at 80 bpm、把 NAME 设为 60 bpm、把 NAME 改成 三拍子。值可以是一个数字、一个速度、一个拍号,或拍号加速度。改拍号走的是编辑器拍号下拉框的同一套重排,回复会列出哪几小节会丢音。
对象。 名字按写的算,不分大小写,周围的 the、my、pattern 忽略;带引号也行。没有哪个 pattern 正好叫这个名字时,会试名字里包含它的那些:travis 找到 "Travis Picking",arp 同时找到 "my arp" 和 "Arpeggio",会问是哪一个。
模式怎么选
模式芯片是玩家的:新对话在当前页面的模式里打开,玩家不切它就一直不变。读法器从不根据句子挑领域。唯一的安全网(readAs.ts):所选模式的读法器对一句话什么都没读出来、而另一模式的读法器能整句读懂——包括点名了另一页面某个 pattern 的修改句——回复会附带一个 改按 Tab/Strum 读 的选项。
在哪里校验
- 离线,每次
npm test:strum/__tests__、tab/__tests__、__tests__/chordAsk.test.ts、__tests__/fuzzy.test.ts,以及__evals__/cases.ts里的路由用例(经__evals__/offline.test.ts跑)。 - 带模型,
npm run evals:__evals__/cases.ts和__evals__/tabCases.ts,断言 General 助手对上面这些句子会去调read_*/edit_*而不是自己编。 general/tools.ts里的描述引用了这一页的例子;这里加了记号或句型,要去检查它们。