Unicode Piano Roll language spec
Draft Version 2024-01-04
UPR text is in UTF-8, and each line ends with
\n
.
Each line can be parsed individually, and parses to one of these four node types: RollHeader , RollFooter , BarHeader , and BarRow .
A Roll contains a list of those nodes that starts with a RollHeader , ends with a RollFooter , and contains any combination of BarHeader s and BarRow s in the middle.
Example work
Here's the first three bars of ErlkΓΆnig, by Franz Schubert:
πΌ 4/4, g minor, πΉ:πΉ
ββββββΒ²βββββββββββΒ³ββββββββββββ΄βββββββΌββββββββββββ΄ββββββββββββ΅βββββββββ ΒΉ
β·β3 β G G β
β3 β G G β
β3 β G G β
β·β3 β G G β
β3 β G G β
β3 β G G β
β·β3 β G G β
β3 β G G β
β3 β G G β
β·β3 β G G β
β3 β G G β
β3 β G G β
ββββββΒ²βββββββββββΒ³ββββββββββββ΄βββββββΌββββββββββββ΄ββββββββββββ΅βββββββββ€ Β²
β·β3 G β G G β
β3 A β G G β
β3 BΜ² β G G β
β·β3 C β G G β
β3 D β G G β
β3 EΜ² β G G β
β·β3 D β G G β
β3 β΄ β G G β
β3 | β G G β
β·β3 BΜ² β G G β
β3 β΄ β G G β
β3 | β G G β
ββββββΒ²βββββββββββΒ³ββββββββββββ΄βββββββΌββββββββββββ΄ββββββββββββ΅βββββββββ€ Β³
β·β3 G β G G β
β3 β΄ β G G β
β3 | β G G β
β·β3 β G G β
β3 β G G β
β3 β G G β
β·β3 β G G β
β3 β G G β
β3 β G G β
β·β3 β G G β
β3 β G G β
β3 β G G β
ββββββββββββββββββββββββββββββββββββββ§βββββββββββββββββββββββββββββββββ
Example lines
(TODO: better formatting on mobile, mobile-vs-desktop styling)
UPR line | Parsed |
"πΌ 4/4, C major, πΉ:πΉπ€\n"
|
β { _type: "RollHeader", time_signature, key_signature_name, staff_context }
|
" ββββΒ²βββββββββββΒ³βββΌβββΒ³ββββββββββββ΄β ΒΉ\n"
|
β { _type: "BarHeader", is_first_row: true, staff_widths: [18, 16], staff_firstkeynums: [33, 45], bar_label: 1 }
|
" ββββΒ²βββββββββββΒ³βββΌβββΒ³ββββββββββββ΄β€ Β²\n"
|
β { _type: "BarHeader", is_first_row: false, staff_widths: [18, 16], staff_firstkeynums: [33, 45], bar_label: 2 }
|
" β B β β\n"
|
β { _type: "BarRow", is_beat: false, is_triplet: false, staff_content: [{ cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0] }, { cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }] }
|
β·β3 β΄ β β\n
|
{ _type: "BarRow", is_beat: true, is_triplet: true, staff_content: [{ cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,0,0,0] }, { cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }] }
|
ββββββββββββββββββββ§βββββββββββββββββ\n
|
{ _type: "RollFooter", staff_widths: [18, 16] }
|
Table of contents for the rest of this document
Example work |
RollFooter |
BarHeader |
BarRow |
β cell values |
RollHeader |
β TimeSignature |
β KeySignatureName |
β StaffContext |
RollFooter
Examples
UPR line | Parsed |
ββββββββββββββββββββ§βββββββββββββββββ\n
|
{ _type: "RollFooter", staff_widths: [18, 16] }
|
Definition
TODO
Unicode codepoints
Number | Text | Name | UTF-8 |
U+000A
|
LINE FEED (LF)
|
0A
|
|
U+0020
|
|
SPACE
|
20
|
U+2558
|
β
|
BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
|
E2 95 98
|
U+2550
|
β
|
BOX DRAWINGS HORIZONTAL DOUBLE
|
E2 95 90
|
U+2567
|
β§
|
BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
|
E2 95 A7
|
U+255B
|
β
|
BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
|
E2 95 9B
|
BarHeader
Examples
UPR line | Parsed |
ββββΒ²βββββββββββΒ³βββΌβββΒ³ββββββββββββ΄β ΒΉ\n
|
{ _type: "BarHeader", is_first_row: true, staff_widths: [18, 16], staff_firstkeynums: [33, 45], bar_label: 1 }
|
ββββΒ²βββββββββββΒ³βββΌβββΒ³ββββββββββββ΄β€ Β²\n
|
{ _type: "BarHeader", is_first_row: false, staff_widths: [18, 16], staff_firstkeynums: [33, 45], bar_label: 2 }
|
Definition
TODO
Unicode codepoints
Number | Text | Name | UTF-8 |
U+000A
|
LINE FEED (LF)
|
0A
|
|
U+0020
|
|
SPACE
|
20
|
U+250C
|
β
|
BOX DRAWINGS LIGHT DOWN AND RIGHT
|
E2 94 8C
|
U+2500
|
β
|
BOX DRAWINGS LIGHT HORIZONTAL
|
E2 94 80
|
U+2510
|
β
|
BOX DRAWINGS LIGHT DOWN AND LEFT
|
E2 94 90
|
U+251C
|
β
|
BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
E2 94 9C
|
U+253C
|
βΌ
|
BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
E2 94 BC
|
U+2524
|
β€
|
BOX DRAWINGS LIGHT VERTICAL AND LEFT
|
E2 94 A4
|
U+2070
|
β°
|
SUPERSCRIPT ZERO
|
E2 81 B0
|
U+00B9
|
ΒΉ
|
SUPERSCRIPT ONE
|
C2 B9
|
U+00B2
|
Β²
|
SUPERSCRIPT TWO
|
C2 B2
|
U+00B3
|
Β³
|
SUPERSCRIPT THREE
|
C2 B3
|
U+2074
|
β΄
|
SUPERSCRIPT FOUR
|
E2 81 B4
|
U+2075
|
β΅
|
SUPERSCRIPT FIVE
|
E2 81 B5
|
U+2076
|
βΆ
|
SUPERSCRIPT SIX
|
E2 81 B6
|
U+2077
|
β·
|
SUPERSCRIPT SEVEN
|
E2 81 B7
|
U+2078
|
βΈ
|
SUPERSCRIPT EIGHT
|
E2 81 B8
|
U+2079
|
βΉ
|
SUPERSCRIPT NINE
|
E2 81 B9
|
BarRow
Examples
UPR line | Parsed |
β B β β\n
|
{ _type: "BarRow", is_beat: false, is_triplet: false, staff_content: [{ cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0] }, { cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }] }
|
β·β3 β΄ β β\n
|
{ _type: "BarRow", is_beat: true, is_triplet: true, staff_content: [{ cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,0,0,0] }, { cells: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }] }
|
Definition
TODO
Unicode codepoints
Number | Text | Name | UTF-8 |
U+000A
|
LINE FEED (LF)
|
0A
|
|
U+0020
|
|
SPACE
|
20
|
U+0031
|
1
|
DIGIT ONE
|
31
|
U+0033
|
3
|
DIGIT THREE
|
33
|
U+0036
|
6
|
DIGIT SIX
|
36
|
U+0038
|
8
|
DIGIT EIGHT
|
38
|
U+0041
through
U+0047
|
A
through
G
|
LATIN CAPITAL LETTER
+ (
A
through
G
)
|
41
through
47
|
U+0058
|
X
|
LATIN CAPITAL LETTER X
|
58
|
U+007C
|
| |
VERTICAL LINE
|
7C
|
U+0305
|
(n/a) |
COMBINING OVERLINE
|
CC 85
|
U+0332
|
(n/a) |
COMBINING LOW LINE
|
CC B2
|
U+250A
|
β
|
BOX DRAWINGS LIGHT QUADROUPLE DASH VERTICAL
|
E2 94 8A
|
U+25B4
|
β΄
|
BLACK UP-POINTING SMALL TRIANGLE
|
E2 96 B4
|
cell values
For each UPR staff within a bar, there is a mapping from column index to key number. This is what determines the key numbers of the notes that get played.
This means the non-blank cell values don't need to be labelled with a name of the note.
So why are they?
-
To help people reading the text know what the notes are
-
As a cross-check. If the text is incompatible with the column index, that leads to an error.
-
To indicate which name should be used for the piano key (some piano keys have multiple names)
Examples
ID | Text | ||
0 |
|
.Blank
|
Nothing should be playing for this key number |
101 |
|
|
.Continuation
|
The already-playing note in this column continues |
102 |
β΄
|
.Continuation_stacc_up
|
The already-playing note in this column (1) continues, and (2) should have been played staccatissimo |
(1 << 3) | 0 |
C
|
.C
|
|
(2 << 3) | 1 |
CΜ
|
.C_sharp
|
|
(2 << 3) | 2 |
DΜ²
|
.D_flat
|
|
(2 << 3) | 0 |
1Μ
Μ²
|
.AboveC
|
Key class index 1 (0-based), but without indicating any preference for the name
Cβ―
vs
Dβ
|
(3 << 3) | 0 |
D
|
.D
|
|
(4 << 3) | 1 |
DΜ
|
.D_sharp
|
|
(4 << 3) | 2 |
EΜ²
|
.E_flat
|
|
(4 << 3) | 0 |
3Μ
Μ²
|
.AboveD
|
Key class index 3 (0-based), but without indicating any preference for the name
Dβ―
vs
Eβ
|
(5 << 3) | 0 |
E
|
.E
|
|
... | |||
(7 << 3) | 0 |
6Μ
Μ²
|
.AboveF
|
Key class index 6 (0-based), but without indicating any preference for the name
Fβ―
vs
Gβ
|
... | |||
(9 << 3) | 0 |
8Μ
Μ²
|
.AboveG
|
Key class index 8 (0-based), but without indicating any preference for the name
Gβ―
vs
Aβ
|
... | |||
(11 << 3) | 0 |
XΜ
Μ²
|
.AboveA
|
Key class index 10 (0-based), but without indicating any preference for the name
Aβ―
vs
Bβ
|
... |
RollHeader
Examples
UPR line | Parsed |
πΌ 4/4, C major, πΉ:πΉπ€\n
|
{ _type: "RollHeader", time_signature, key_signature_name, staff_context }
|
Definition
TODO
Unicode codepoints
Number | Text | Name | UTF-8 |
U+000A
|
LINE FEED (LF)
|
0A
|
|
U+0020
|
|
SPACE
|
20
|
U+002C
|
,
|
,
|
2C
|
U+002F
|
/
|
/
|
2F
|
U+003A
|
:
|
:
|
3A
|
U+0030
through
U+0039
|
0
through
9
|
DIGIT ONE
..
DIGIT NINE
|
30
through
39
|
U+0041
through
U+005A
|
A
through
Z
|
LATIN CAPITAL LETTER A
..
LATIN CAPITAL LETTER Z
|
41
through
5A
|
U+0061
through
U+007A
|
a
through
z
|
LATIN SMALL LETTER A
..
LATIN SMALL LETTER Z
|
61
through
7A
|
U+266D
|
β
|
MUSIC FLAT SIGN
|
E2 99 AD
|
U+266F
|
β―
|
MUSIC SHARP SIGN
|
E2 99 AF
|
U+1F3BC
|
πΌ
|
MUSICAL SCORE
|
F0 9F 8E BC
|
U+1F3B9
|
πΉ
|
MUSICAL KEYBOARD
|
F0 9F 8E B9
|
U+1F3A4
|
π€
|
MICROPHONE
|
F0 9F 8E A4
|
TimeSignature
TODO
KeySignatureName
TODO
StaffContext
TODO
Performances

TODO