plainscore 0.1.0 → 0.1.1
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/README.md +34 -60
- package/docs/grammar.md +183 -0
- package/llms.txt +72 -0
- package/package.json +18 -2
package/README.md
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
# plainscore
|
|
2
2
|
|
|
3
|
-
A plain-text score
|
|
3
|
+
> A plain-text music score. One `.score` file reads like a composer's sketch, edits line by line, and compiles to ABC notation, MIDI and timed note events. TypeScript, no runtime dependencies.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
<details>
|
|
10
|
+
<summary><code>song.score</code> as text</summary>
|
|
4
11
|
|
|
5
12
|
```
|
|
6
13
|
score 0.1
|
|
7
14
|
|
|
8
|
-
title
|
|
15
|
+
title Evening Song
|
|
9
16
|
by You
|
|
10
17
|
|
|
11
18
|
instruments
|
|
12
19
|
piano piano
|
|
13
20
|
cello cello
|
|
14
21
|
|
|
15
|
-
piece
|
|
22
|
+
piece song Evening Song
|
|
16
23
|
tempo q = 72
|
|
17
24
|
key G major
|
|
18
25
|
|
|
@@ -41,71 +48,38 @@ piece waltz Small Waltz
|
|
|
41
48
|
tune, comp p, pad 80%, pedal
|
|
42
49
|
```
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- **Pitches as written**, durations `w h q e s` with dots and ties, bars separated by `|`.
|
|
47
|
-
- **Chord-relative patterns**: write a figure once with `bass`, `t1`–`t4`, `top`, `upper`, and it follows the chords.
|
|
48
|
-
- **Form as an orchestration table**: each section row names the parts that play and how loud (`85%`, `92%-75%`, `bars 3-4`, `on 1 and 3`, `8va`, `in E`).
|
|
49
|
-
- **Loop**: `form loop`, or `form loop from A1` for an intro that plays once.
|
|
50
|
-
- **Variants**: versions of one piece that share a timeline (`drop P`, `Q for P`, `P <options>`).
|
|
51
|
-
- **Doubles**: `part strings violins doubles tune 8vb` plays another part's notes on its own instrument.
|
|
52
|
-
- **Pedal, rolls, staggered entries, ad lib.** (seeded random notes), and a **mix** block for playback balance that notation and MIDI ignore.
|
|
51
|
+
</details>
|
|
53
52
|
|
|
54
|
-
The
|
|
53
|
+

|
|
55
54
|
|
|
56
|
-
|
|
55
|
+

|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
plainscore check song.score
|
|
64
|
-
plainscore abc song.score --piece waltz > song.abc
|
|
65
|
-
plainscore midi song.score --piece waltz -o song.mid
|
|
66
|
-
plainscore events song.score --variant quiet --seed 7 --passes 2
|
|
67
|
-
plainscore legend
|
|
68
|
-
```
|
|
57
|
+
- Install: `pnpm add -D plainscore` (Node 20+).
|
|
58
|
+
- CLI: `plainscore check|abc|midi|events|legend <file> [--piece id] [--variant v] [--seed n] [--passes n] [-o out.mid]`.
|
|
59
|
+
- API: `render(parseScore(text, file), piece, { variant, seed })`, then `toAbc(r)` or `toMidi(r)`.
|
|
60
|
+
- Staves follow `instruments`. The family (`piano harp violins violas cello bass flute oboe clarinet bassoon horn trumpet voice synth`) sets clef, MIDI program and pedal. A piano `lead` gets a Melody staff; `pizz.` gets its own staff.
|
|
61
|
+
- Not in 0.1: meters other than 4/4, tuplets, ties across barlines, tempo changes, hairpins, repeats with endings, imports, percussion.
|
|
69
62
|
|
|
70
|
-
|
|
71
|
-
import { parseScore, render } from 'plainscore'
|
|
72
|
-
import { toAbc } from 'plainscore/abc'
|
|
73
|
-
import { toMidi } from 'plainscore/midi'
|
|
63
|
+
## Docs
|
|
74
64
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const midi = toMidi(rendering)
|
|
79
|
-
```
|
|
65
|
+
- [Grammar](docs/grammar.md): every keyword of score 0.1, with examples
|
|
66
|
+
- [Legend](src/legend.ts): the legend a score can carry so readers need no docs; `plainscore legend`
|
|
67
|
+
- [llms.txt](llms.txt): this index, for agents
|
|
80
68
|
|
|
81
|
-
|
|
69
|
+
## Examples
|
|
82
70
|
|
|
83
|
-
|
|
71
|
+
- [bgm.score](examples/bgm.score): two pieces with variants, loop, ad lib. and mix
|
|
72
|
+
- [road.score](examples/road.score): a full single for piano, harp and strings
|
|
84
73
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- each instrument gets a staff, and `pizz.` parts get their own staff right after it;
|
|
88
|
-
- a `lead` part on a piano gets a Melody staff on top;
|
|
89
|
-
- pianos get a grand staff with pedal marks, and an `inner` part becomes a voice of its own;
|
|
90
|
-
- clefs, MIDI programs and pedal come from the instrument's family (`piano harp violins violas cello bass flute oboe clarinet bassoon horn trumpet voice synth`).
|
|
91
|
-
|
|
92
|
-
The ABC targets [abcjs](https://www.abcjs.net/) 6.x. plainscore has no runtime dependencies and does not render; pass the ABC to abcjs, or to any ABC tool.
|
|
93
|
-
|
|
94
|
-
## Limits of 0.1
|
|
95
|
-
|
|
96
|
-
4/4 only; the smallest grid is the eighth (with `late s` for sixteenth offsets); no tuplets, ties across barlines, tempo changes, hairpins, articulations beyond `pizz.`/`arco`, repeats with endings, imports or percussion.
|
|
97
|
-
|
|
98
|
-
## Develop
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
pnpm install
|
|
102
|
-
pnpm test
|
|
103
|
-
pnpm typecheck
|
|
104
|
-
pnpm build
|
|
105
|
-
```
|
|
74
|
+
## API
|
|
106
75
|
|
|
107
|
-
|
|
76
|
+
- [plainscore](src/index.ts): `parseScore`, `render`, `checkScore`, `layout`, `seeded`
|
|
77
|
+
- [plainscore/abc](src/abc/index.ts): `toAbc(rendering)`, ABC for abcjs 6
|
|
78
|
+
- [plainscore/midi](src/midi.ts): `toMidi(rendering)`, SMF type 1, 480 PPQ, pedal as CC64
|
|
79
|
+
- [plainscore/legend](src/legend.ts): `LEGEND`
|
|
108
80
|
|
|
109
|
-
##
|
|
81
|
+
## Optional
|
|
110
82
|
|
|
111
|
-
|
|
83
|
+
- [Fixtures](fixtures): expected ABC, MIDI and events for the examples; run `pnpm test -u` after an intended change
|
|
84
|
+
- [package.json](package.json): `pnpm test`, `pnpm typecheck`, `pnpm build`
|
|
85
|
+
- [License](LICENSE): MIT
|
package/docs/grammar.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# score 0.1 grammar
|
|
2
|
+
|
|
3
|
+
A score is a UTF-8 text file. Indentation carries structure: file-level lines start at column 0, a piece's blocks at 2 spaces, block rows at 4, and form entries deeper than 4. Lines starting with `#` are comments. Anything after ` — ` (an em dash between spaces) on a line is a note to the reader and is ignored.
|
|
4
|
+
|
|
5
|
+
## File
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
score 0.1 the first line, always
|
|
9
|
+
|
|
10
|
+
title Small Waltz optional, file-wide
|
|
11
|
+
by You optional; written as "Music by You"
|
|
12
|
+
after A song I love optional; written as "After A song I love"
|
|
13
|
+
|
|
14
|
+
instruments
|
|
15
|
+
piano piano <name> <family>
|
|
16
|
+
strings violins
|
|
17
|
+
|
|
18
|
+
piece waltz Small Waltz <id> <title>; a file can hold several pieces
|
|
19
|
+
…
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Families: `piano harp violins violas cello bass flute oboe clarinet bassoon horn trumpet voice synth`. The family sets the clef, MIDI program, pedal and how notes are engraved. The instrument name is yours.
|
|
23
|
+
- Instrument names and part names must differ.
|
|
24
|
+
|
|
25
|
+
## Piece header
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
meter 4/4 only 4/4 in 0.1
|
|
29
|
+
tempo q = 80
|
|
30
|
+
key D major major minor dorian lydian mixolydian
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Pitches, durations, counts
|
|
34
|
+
|
|
35
|
+
- Pitches sound as written: `C4` is middle C, `F#5`, `Bb3`.
|
|
36
|
+
- Durations: `w h q e s` (whole to sixteenth), `.` dotted (`q.`), `+` ties (`w+s`, `h+q`).
|
|
37
|
+
- `r` is a rest in bar lines.
|
|
38
|
+
- Counts are beats and ands: `1 1& 2 2& 3 3& 4 4&`. `2.3&` means bar 2, the and of 3.
|
|
39
|
+
|
|
40
|
+
## Chords
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
chords theme — bass | voicing
|
|
44
|
+
D D2 | D4 F#4 A4
|
|
45
|
+
A/C# C#2 | C#4 E4 A4
|
|
46
|
+
D a symbol alone reuses its last voicing
|
|
47
|
+
|
|
48
|
+
chords stars two bars each one … four, or a number
|
|
49
|
+
Dmaj9(#11) D2 | D3 A3 F#4 C#5 E5 G#5
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
A section is as long as the chord set it names.
|
|
53
|
+
|
|
54
|
+
## Parts
|
|
55
|
+
|
|
56
|
+
Every part has a name and, unless each cue names one, an instrument. Header words, in any order:
|
|
57
|
+
|
|
58
|
+
| word | meaning |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `piano`, `violins pizz.` | the instrument, optionally with `pizz.` or `arco` |
|
|
61
|
+
| `p`, `mf` … | the part's dynamic |
|
|
62
|
+
| `overlap e` | each note rings an eighth past its value |
|
|
63
|
+
| `ring w` | each note rings for a whole note |
|
|
64
|
+
| `once` | counts are within the section (`3.2&`), not repeated per chord |
|
|
65
|
+
| `lead` | the tune: breaths leave it ringing; on a piano it gets the Melody staff |
|
|
66
|
+
| `inner` | engraved as a voice of its own on its staff |
|
|
67
|
+
| `doubles P [8vb]` | plays part P's notes on this part's instrument |
|
|
68
|
+
| `ad lib.` | random notes from rules (below) |
|
|
69
|
+
|
|
70
|
+
### Bars
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
part tune piano mf overlap e lead
|
|
74
|
+
| F#5 h E5 q D5 q | E5 h. A4 q |
|
|
75
|
+
| mp D6 h B5 q D6 q | … | a dynamic before a note applies from there
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Each bar must add up to a whole note.
|
|
79
|
+
|
|
80
|
+
### Cues
|
|
81
|
+
|
|
82
|
+
One line per entry: `<count> [instrument] <pitches> <duration> [options]`. A cue part repeats with every chord.
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
part flow piano
|
|
86
|
+
1 bass w >
|
|
87
|
+
1& fifth q. soft
|
|
88
|
+
2& t2 q. soft late s
|
|
89
|
+
1 chord h soft roll 30ms
|
|
90
|
+
1 cello bass+8va w+s f
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- Chord tones:
|
|
94
|
+
- `bass`; `t1`–`t4` / `v1`–`v6` are voicing tones from low to high; `top` is the highest.
|
|
95
|
+
- `chord` is all of them; `upper` is all but the lowest.
|
|
96
|
+
- `fifth` is the chord tone nearest a fifth above the bass.
|
|
97
|
+
- Shifts: `+5th` (a perfect fifth up, in the chord or not), `+8va`, `+15ma`, `+8vb`, `+15mb`.
|
|
98
|
+
- Options:
|
|
99
|
+
- A dynamic, or a rising roll such as `ppp-mf`.
|
|
100
|
+
- `>` accent, `soft`, `softer`.
|
|
101
|
+
- `roll X`: the notes enter X apart.
|
|
102
|
+
- `late X`: enters X after the count.
|
|
103
|
+
- X is a duration or a time in ms (`30ms`).
|
|
104
|
+
- Pedal: `1 Ped.` presses or changes the sustain pedal; `4& *` lifts it.
|
|
105
|
+
|
|
106
|
+
### Ad lib.
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
part sparkle harp ad lib.
|
|
110
|
+
chance 22%
|
|
111
|
+
notes D lydian, D6 to C#7, 8va 30% of the time
|
|
112
|
+
dynamic pppp-pp
|
|
113
|
+
length h.
|
|
114
|
+
timing anywhere within the eighth or sixteenth, quarter
|
|
115
|
+
avoid quote optional: skip eighths where this part plays
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Dynamics
|
|
119
|
+
|
|
120
|
+
`pppp .10 ppp .16 pp .22 p .32 mp .42 mf .50 f .55`. Accent adds .08, `soft` takes away .07 and `softer` takes away .14.
|
|
121
|
+
|
|
122
|
+
## Form
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
form loop from A1
|
|
126
|
+
Intro prelude — 序
|
|
127
|
+
wash mp, stream p 85%, drone pp 85% bars 1-2, pedal, breath
|
|
128
|
+
A1 theme
|
|
129
|
+
flow p 85%, theme 85%, pedal
|
|
130
|
+
A5 theme in E
|
|
131
|
+
flow mp, theme f 8va, pedal
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- Header:
|
|
135
|
+
- `form`: plays once.
|
|
136
|
+
- `form loop`: repeats from the top.
|
|
137
|
+
- `form loop from A1`: plays through, then repeats from A1.
|
|
138
|
+
- Each row is `<mark> <chord set> [in <key>]`, then the parts that play, comma-separated.
|
|
139
|
+
- A part not named in a row is silent there. Every part must play somewhere.
|
|
140
|
+
- Part options:
|
|
141
|
+
- A dynamic.
|
|
142
|
+
- `85%` shades the part; `92%-75%` shades it bar by bar.
|
|
143
|
+
- `bars 3-4`.
|
|
144
|
+
- `on 1 and 3`: only notes that start on those beats.
|
|
145
|
+
- A shift: `8va`, `8vb`.
|
|
146
|
+
- `breath`: every part but `lead` and `once` parts stops for the last eighth.
|
|
147
|
+
- `cut`: whatever still rings stops at the downbeat.
|
|
148
|
+
- Both also lift the pedal.
|
|
149
|
+
|
|
150
|
+
## Variants
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
variants
|
|
154
|
+
overview
|
|
155
|
+
browse drop stream
|
|
156
|
+
read drop stream, sparse for flow, melody on 1 and 3
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The first variant plays the form as written. The others edit every form row:
|
|
160
|
+
|
|
161
|
+
- `drop P`: P is silent everywhere.
|
|
162
|
+
- `Q for P`: Q plays wherever P was named, with P's options.
|
|
163
|
+
- `P <options>`: adds options to P wherever it plays.
|
|
164
|
+
|
|
165
|
+
All variants share one timeline.
|
|
166
|
+
|
|
167
|
+
## Mix
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
mix — playback only; notation and MIDI ignore it
|
|
171
|
+
violin-theme +13 dB attack 200ms
|
|
172
|
+
piano 75%
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Rows name a part or an instrument.
|
|
176
|
+
|
|
177
|
+
## Errors
|
|
178
|
+
|
|
179
|
+
Every error names the file and line, and says what to write instead:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
song.score line 18: "+3" is mixing; put it in the piece's mix block
|
|
183
|
+
```
|
package/llms.txt
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# plainscore
|
|
2
|
+
|
|
3
|
+
> A plain-text music score. One `.score` file reads like a composer's sketch, edits line by line, and compiles to ABC notation, MIDI and timed note events. TypeScript, no runtime dependencies.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
score 0.1
|
|
7
|
+
|
|
8
|
+
title Evening Song
|
|
9
|
+
by You
|
|
10
|
+
|
|
11
|
+
instruments
|
|
12
|
+
piano piano
|
|
13
|
+
cello cello
|
|
14
|
+
|
|
15
|
+
piece song Evening Song
|
|
16
|
+
tempo q = 72
|
|
17
|
+
key G major
|
|
18
|
+
|
|
19
|
+
chords verse — bass | voicing
|
|
20
|
+
G G2 | B3 D4 G4
|
|
21
|
+
D/F# F#2 | A3 D4 F#4
|
|
22
|
+
Em E2 | B3 E4 G4
|
|
23
|
+
C C3 | C4 E4 G4
|
|
24
|
+
|
|
25
|
+
part tune piano mf overlap e lead
|
|
26
|
+
| B4 h A4 q G4 q | A4 w | G4 h B4 h | C5 w |
|
|
27
|
+
|
|
28
|
+
part comp piano
|
|
29
|
+
1 bass w >
|
|
30
|
+
2 upper q soft
|
|
31
|
+
3& upper e softer
|
|
32
|
+
|
|
33
|
+
part pad cello mp
|
|
34
|
+
1 bass+8va w
|
|
35
|
+
|
|
36
|
+
part pedal
|
|
37
|
+
1 Ped.
|
|
38
|
+
|
|
39
|
+
form loop
|
|
40
|
+
A verse
|
|
41
|
+
tune, comp p, pad 80%, pedal
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Install: `pnpm add -D plainscore` (Node 20+).
|
|
45
|
+
- CLI: `plainscore check|abc|midi|events|legend <file> [--piece id] [--variant v] [--seed n] [--passes n] [-o out.mid]`.
|
|
46
|
+
- API: `render(parseScore(text, file), piece, { variant, seed })`, then `toAbc(r)` or `toMidi(r)`.
|
|
47
|
+
- Staves follow `instruments`. The family (`piano harp violins violas cello bass flute oboe clarinet bassoon horn trumpet voice synth`) sets clef, MIDI program and pedal. A piano `lead` gets a Melody staff; `pizz.` gets its own staff.
|
|
48
|
+
- Not in 0.1: meters other than 4/4, tuplets, ties across barlines, tempo changes, hairpins, repeats with endings, imports, percussion.
|
|
49
|
+
|
|
50
|
+
## Docs
|
|
51
|
+
|
|
52
|
+
- [Grammar](https://raw.githubusercontent.com/Innei/plainscore/main/docs/grammar.md): every keyword of score 0.1, with examples
|
|
53
|
+
- [Legend](https://raw.githubusercontent.com/Innei/plainscore/main/src/legend.ts): the legend a score can carry so readers need no docs; `plainscore legend`
|
|
54
|
+
- [README](https://github.com/Innei/plainscore#readme): the same index with pictures
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
- [bgm.score](https://raw.githubusercontent.com/Innei/plainscore/main/examples/bgm.score): two pieces with variants, loop, ad lib. and mix
|
|
59
|
+
- [road.score](https://raw.githubusercontent.com/Innei/plainscore/main/examples/road.score): a full single for piano, harp and strings
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
- [plainscore](https://raw.githubusercontent.com/Innei/plainscore/main/src/index.ts): `parseScore`, `render`, `checkScore`, `layout`, `seeded`
|
|
64
|
+
- [plainscore/abc](https://raw.githubusercontent.com/Innei/plainscore/main/src/abc/index.ts): `toAbc(rendering)`, ABC for abcjs 6
|
|
65
|
+
- [plainscore/midi](https://raw.githubusercontent.com/Innei/plainscore/main/src/midi.ts): `toMidi(rendering)`, SMF type 1, 480 PPQ, pedal as CC64
|
|
66
|
+
- [plainscore/legend](https://raw.githubusercontent.com/Innei/plainscore/main/src/legend.ts): `LEGEND`
|
|
67
|
+
|
|
68
|
+
## Optional
|
|
69
|
+
|
|
70
|
+
- [Fixtures](https://github.com/Innei/plainscore/tree/main/fixtures): expected ABC, MIDI and events for the examples; run `pnpm test -u` after an intended change
|
|
71
|
+
- [package.json](https://raw.githubusercontent.com/Innei/plainscore/main/package.json): `pnpm test`, `pnpm typecheck`, `pnpm build`
|
|
72
|
+
- [License](https://raw.githubusercontent.com/Innei/plainscore/main/LICENSE): MIT
|
package/package.json
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plainscore",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A plain-text score format a musician can read and an AI can edit, with MIDI and ABC export.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Innei/plainscore.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/Innei/plainscore#readme",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"music",
|
|
13
|
+
"score",
|
|
14
|
+
"notation",
|
|
15
|
+
"abc",
|
|
16
|
+
"midi",
|
|
17
|
+
"dsl",
|
|
18
|
+
"composer"
|
|
19
|
+
],
|
|
6
20
|
"type": "module",
|
|
7
21
|
"engines": {
|
|
8
22
|
"node": ">=20"
|
|
@@ -17,7 +31,9 @@
|
|
|
17
31
|
"./legend": "./dist/legend.js"
|
|
18
32
|
},
|
|
19
33
|
"files": [
|
|
20
|
-
"dist"
|
|
34
|
+
"dist",
|
|
35
|
+
"docs/grammar.md",
|
|
36
|
+
"llms.txt"
|
|
21
37
|
],
|
|
22
38
|
"devDependencies": {
|
|
23
39
|
"@types/node": "^24.19.2",
|