diff --git a/README.md b/README.md index bac2e39..7115840 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,27 @@ python3 -m generator --data-dir data --output-dir /tmp/product-vamp-generated -- The `--check` path compares generation invariants against `data/expected/performance_summary.json`. +Authored score files use compact JSON. Metadata stays as normal JSON fields, while each measure is a readable notation string: + +```json +{ + "title": "Autumn Leaves", + "key": "F", + "time": "4/4", + "clef": "treble", + "part": "Vocal", + "role": "vocal", + "measures": [ + "[G-7] Bb4:1~ \"leaves\"", + "[C7] ~Bb4:4 C4:4 \"drift\" D4:4 \"by\" E4:4 \"my\"", + "{5:2 C4:4 D4:4 E4:4 F4:4 G4:4}", + "[G7] (G4 B4 F5):4 r:4" + ] +} +``` + +Notation tokens are `PITCH:DURATION`, `r:DURATION`, and `(PITCH PITCH ...):DURATION` for written chord tones. Durations use `1`, `2`, `4`, `8`, `16`, `32`, plus `.` for dotted values. `[Chord]` applies to the next token, quoted text applies as a lyric to the previous note, `~` starts or ends ties, and `{N:M ...}` writes tuplets such as five quarter notes in the space of two quarter notes. + Bundled third-party font assets live in `package/assets/fonts/petaluma`. Petaluma is from Steinberg and is distributed under the SIL Open Font License; the package keeps `OFL.txt` next to the font files. If the Pico bitmap font needs to be refreshed after changing the bundled UI font, run: ```sh diff --git a/TECHNICAL_GUIDE.md b/TECHNICAL_GUIDE.md index 16f3f44..4a9ee40 100644 --- a/TECHNICAL_GUIDE.md +++ b/TECHNICAL_GUIDE.md @@ -37,6 +37,8 @@ Petaluma music symbols are placed from SMuFL bounding boxes and stem/flag anchor The build runs `python3 -m generator` against `data/` and writes build-only assets under the CMake binary tree. Performance source songs become acoustic, electric, and vocal arrangements. Melody practice sources are emitted in all 12 keys at build time. Chord practice sources are emitted once in their authored key. Generated JSON is sanitized so packaged scores do not contain tab positions or fingering fields; guitar playability logic is used only inside the arrangement generator. +Authored source files use compact JSON and are normalized by the generator before arrangement. A root-level `measures` array means compact source; legacy staff/voice JSON with `staves` is still accepted. Measure notation supports `PITCH:DURATION`, `r:DURATION`, parenthesized chord tones like `(G4 B4 F5):4`, `[Chord]` markers, quoted lyrics, `~` tie starts/stops, and tuplets as `{N:M ...}`. Tuplet normalization keeps the written duration in `display_ticks` and distributes rounded timeline ticks across the tuplet group so the measure total remains stable on the existing 16,384 ticks-per-whole-note grid. + Generator checks compare the generated performance summary against `data/expected/performance_summary.json`. The summary stores compatibility invariants such as section layout, chord-label placement, slash-section placement, and vocal timing without committing the generated score files themselves. Refresh that file only when an intentional arrangement-logic change should become the new baseline. ## Pico Runtime diff --git a/data/practice/chord/01_three_note_shell_voicings/01-cycles.json b/data/practice/chord/01_three_note_shell_voicings/01-cycles.json index f7411d7..5a6c85f 100644 --- a/data/practice/chord/01_three_note_shell_voicings/01-cycles.json +++ b/data/practice/chord/01_three_note_shell_voicings/01-cycles.json @@ -1,448 +1,17 @@ { "version": 1, - "metadata": { - "title": "01 Cycles", - "composer": "", - "key_signature": "C", - "time_signature": "4/4", - "style": "Med. Swing" - }, - "staves": [ - { - "id": "staff-vocal", - "name": "Vocal", - "clef": "treble", - "role": "vocal", - "measures": [ - { - "id": "measure-1", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-2vg7k4p1", - "notes": [ - { - "id": "note-ekasof25", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - }, - { - "letter": "b", - "accidental": "n", - "octave": 4 - }, - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "chord_symbol": "G7", - "hidden": false - }, - { - "id": "note-1fafh8eh", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - }, - { - "letter": "b", - "accidental": "n", - "octave": 4 - }, - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "hidden": false - }, - { - "id": "note-46dyougy", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - }, - { - "letter": "b", - "accidental": "b", - "octave": 4 - }, - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "chord_symbol": "C7", - "hidden": false - }, - { - "id": "note-qsxuq33n", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - }, - { - "letter": "b", - "accidental": "b", - "octave": 4 - }, - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "hidden": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-tn8h5n0h", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-8hwk9b7q", - "notes": [ - { - "id": "note-64c6gqr5", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - }, - { - "letter": "a", - "accidental": "n", - "octave": 4 - }, - { - "letter": "e", - "accidental": "b", - "octave": 5 - } - ], - "chord_symbol": "F7", - "hidden": false - }, - { - "id": "note-hbgzjjm4", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - }, - { - "letter": "a", - "accidental": "n", - "octave": 4 - }, - { - "letter": "e", - "accidental": "b", - "octave": 5 - } - ], - "hidden": false - }, - { - "id": "note-bpc3jl26", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 3 - }, - { - "letter": "a", - "accidental": "b", - "octave": 4 - }, - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "chord_symbol": "Bb7", - "hidden": false - }, - { - "id": "note-tph2glun", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 3 - }, - { - "letter": "a", - "accidental": "b", - "octave": 4 - }, - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "hidden": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-toxu9etb", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-rz7qmbk5", - "notes": [ - { - "id": "note-rfziub9v", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - }, - { - "letter": "g", - "accidental": "n", - "octave": 4 - }, - { - "letter": "d", - "accidental": "b", - "octave": 5 - } - ], - "chord_symbol": "Eb7", - "hidden": false - }, - { - "id": "note-q6he4t8j", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - }, - { - "letter": "g", - "accidental": "n", - "octave": 4 - }, - { - "letter": "d", - "accidental": "b", - "octave": 5 - } - ], - "hidden": false - }, - { - "id": "note-bwh485jp", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "accidental": "b", - "octave": 3 - }, - { - "letter": "g", - "accidental": "b", - "octave": 4 - }, - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "chord_symbol": "Ab7", - "hidden": false - }, - { - "id": "note-rjm4vz5k", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "accidental": "b", - "octave": 3 - }, - { - "letter": "g", - "accidental": "b", - "octave": 4 - }, - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "hidden": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-vj7x9zvk", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-9sgs888d", - "notes": [ - { - "id": "note-v1y57u53", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "b", - "octave": 4 - }, - { - "letter": "f", - "accidental": "n", - "octave": 4 - }, - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "chord_symbol": "Dbmaj7", - "hidden": false - }, - { - "id": "note-qvbgpked", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "b", - "octave": 4 - }, - { - "letter": "f", - "accidental": "n", - "octave": 4 - }, - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "hidden": false - }, - { - "id": "note-gy3ukk29", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "b", - "octave": 4 - }, - { - "letter": "f", - "accidental": "n", - "octave": 4 - }, - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "Db6", - "hidden": false - }, - { - "id": "note-0vcz65hi", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "b", - "octave": 4 - }, - { - "letter": "f", - "accidental": "n", - "octave": 4 - }, - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false - } - ], - "tuplets": [] - } - ] - } - ] - } + "title": "01 Cycles", + "composer": "", + "key": "C", + "time": "4/4", + "clef": "treble", + "part": "Vocal", + "role": "vocal", + "style": "Med. Swing", + "measures": [ + "[G7] (G4 B4 F5):4 (G4 B4 F5):4 [C7] (C4 Bb4 E5):4 (C4 Bb4 E5):4", + "[F7] (F4 A4 Eb5):4 (F4 A4 Eb5):4 [Bb7] (Bb3 Ab4 D5):4 (Bb3 Ab4 D5):4", + "[Eb7] (Eb4 G4 Db5):4 (Eb4 G4 Db5):4 [Ab7] (Ab3 Gb4 C5):4 (Ab3 Gb4 C5):4", + "[Dbmaj7] (Db4 F4 C5):4 (Db4 F4 C5):4 [Db6] (Db4 F4 Bb4):4 (Db4 F4 Bb4):4" ] } diff --git a/data/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.json b/data/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.json index 1be6d19..d71d6a8 100644 --- a/data/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.json +++ b/data/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.json @@ -1,1558 +1,21 @@ { "version": 1, - "metadata": { - "title": "01 The Diatonic Scales", - "composer": "", - "key_signature": "C", - "time_signature": "4/4", - "style": "Med. Swing" - }, - "staves": [ - { - "id": "staff-vocal", - "name": "Vocal", - "clef": "treble", - "role": "vocal", - "measures": [ - { - "id": "measure-1", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-qb98gzde", - "notes": [ - { - "id": "note-bxlkf4ug", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-0c6c5tws", - "hidden": false - }, - { - "id": "note-i2tjegsv", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-0c6c5tws", - "hidden": false - }, - { - "id": "note-46thuc68", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-0c6c5tws", - "hidden": false - }, - { - "id": "note-r5zek4mx", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-7br9xx6i", - "hidden": false - }, - { - "id": "note-m5w6wcw4", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-7br9xx6i", - "hidden": false - }, - { - "id": "note-2fmh7gqz", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-7br9xx6i", - "hidden": false - }, - { - "id": "note-kw6fhfap", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-qv21cdbr", - "hidden": false - }, - { - "id": "note-jygl12v0", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-qv21cdbr", - "hidden": false - }, - { - "id": "note-imtgvxtj", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-qv21cdbr", - "hidden": false - }, - { - "id": "note-xt2llp3p", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-vdvtkj4m", - "hidden": false - }, - { - "id": "note-xxf30jqc", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-vdvtkj4m", - "hidden": false - }, - { - "id": "note-s06gf3nr", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-vdvtkj4m", - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-0c6c5tws", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-7br9xx6i", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-qv21cdbr", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-vdvtkj4m", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - }, - { - "id": "measure-rg2scwei", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-9px0mgan", - "notes": [ - { - "id": "note-ka5cwduo", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-0hgigtlc", - "hidden": false - }, - { - "id": "note-a0jm57pv", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-0hgigtlc", - "hidden": false - }, - { - "id": "note-a6dew1x8", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-0hgigtlc", - "hidden": false - }, - { - "id": "note-pp5ix20g", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-sk4f28v4", - "hidden": false - }, - { - "id": "note-2r5lj3d7", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-sk4f28v4", - "hidden": false - }, - { - "id": "note-tsbdlnpw", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-sk4f28v4", - "hidden": false - }, - { - "id": "note-1n8gcxul", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-ql3hxzpa", - "hidden": false - }, - { - "id": "note-lo0hh86o", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-ql3hxzpa", - "hidden": false - }, - { - "id": "note-30g0w375", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-ql3hxzpa", - "hidden": false - }, - { - "id": "note-vdsp6lyd", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-bfsqd36b", - "hidden": false - }, - { - "id": "note-6mesdjsd", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-bfsqd36b", - "hidden": false - }, - { - "id": "note-ryiqaves", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-bfsqd36b", - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-0hgigtlc", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-sk4f28v4", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-ql3hxzpa", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-bfsqd36b", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - }, - { - "id": "measure-gxys5m6z", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-fbr2eqd5", - "notes": [ - { - "id": "note-57ucdp89", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-sjd8geyw", - "hidden": false - }, - { - "id": "note-se99qakq", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-sjd8geyw", - "hidden": false - }, - { - "id": "note-lq6gd2o8", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-sjd8geyw", - "hidden": false - }, - { - "id": "note-u1ih3c89", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-jglcvslm", - "hidden": false - }, - { - "id": "note-1hdahj72", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-jglcvslm", - "hidden": false - }, - { - "id": "note-186nmb17", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-jglcvslm", - "hidden": false - }, - { - "id": "note-oryk1kwq", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-lzam84h2", - "hidden": false - }, - { - "id": "note-9cfwrwm1", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-lzam84h2", - "hidden": false - }, - { - "id": "note-dkg7bffj", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-lzam84h2", - "hidden": false - }, - { - "id": "note-9u1uww79", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-3kg6c83v", - "hidden": false - }, - { - "id": "note-q1a9k3qe", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-3kg6c83v", - "hidden": false - }, - { - "id": "note-ndravo0j", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-3kg6c83v", - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-sjd8geyw", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-jglcvslm", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-lzam84h2", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-3kg6c83v", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - }, - { - "id": "measure-4vbri0zj", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-wdewvnpc", - "notes": [ - { - "id": "note-ucj0etgg", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-7pxfmh32", - "hidden": false - }, - { - "id": "note-xb6yrv8c", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-7pxfmh32", - "hidden": false - }, - { - "id": "note-ietm2w5r", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 6 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-7pxfmh32", - "hidden": false - }, - { - "id": "note-9iza7rvh", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-ugbzsnpb", - "hidden": false - }, - { - "id": "note-1xheqp5s", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 6 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-ugbzsnpb", - "hidden": false - }, - { - "id": "note-1t3pebnn", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 6 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-ugbzsnpb", - "hidden": false - }, - { - "id": "note-ga6v2g2o", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 6 - } - ], - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-7pxfmh32", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-ugbzsnpb", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - }, - { - "id": "measure-2ipnsuly", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-etcsr918", - "notes": [ - { - "id": "note-n1y3pcno", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 6 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-fpud02wc", - "hidden": false - }, - { - "id": "note-to1jn24c", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-fpud02wc", - "hidden": false - }, - { - "id": "note-f1ca9l1o", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-fpud02wc", - "hidden": false - }, - { - "id": "note-bvrdcejv", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-cael92rs", - "hidden": false - }, - { - "id": "note-wjcprqb3", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-cael92rs", - "hidden": false - }, - { - "id": "note-di1cz37y", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-cael92rs", - "hidden": false - }, - { - "id": "note-wkic5hag", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-vafmlhht", - "hidden": false - }, - { - "id": "note-pvji76v1", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-vafmlhht", - "hidden": false - }, - { - "id": "note-3tld6udb", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-vafmlhht", - "hidden": false - }, - { - "id": "note-y1s32dwx", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-mnku0rkq", - "hidden": false - }, - { - "id": "note-cs88wbjs", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-mnku0rkq", - "hidden": false - }, - { - "id": "note-20kf276x", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-mnku0rkq", - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-fpud02wc", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-cael92rs", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-vafmlhht", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-mnku0rkq", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - }, - { - "id": "measure-zs1vs6x5", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-0160bcf2", - "notes": [ - { - "id": "note-xq0eb1xd", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-poawgjl8", - "hidden": false - }, - { - "id": "note-vop6rzh7", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-poawgjl8", - "hidden": false - }, - { - "id": "note-vr92r23w", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-poawgjl8", - "hidden": false - }, - { - "id": "note-v8fs9ztq", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-44dvhfxz", - "hidden": false - }, - { - "id": "note-rogj5cev", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-44dvhfxz", - "hidden": false - }, - { - "id": "note-9wd4q2fz", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-44dvhfxz", - "hidden": false - }, - { - "id": "note-950rnkfe", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-g84u6o31", - "hidden": false - }, - { - "id": "note-ja6ss3bh", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-g84u6o31", - "hidden": false - }, - { - "id": "note-wftoujxu", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-g84u6o31", - "hidden": false - }, - { - "id": "note-aofm5qua", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 5 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-5xuqvazv", - "hidden": false - }, - { - "id": "note-kv79naw4", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-5xuqvazv", - "hidden": false - }, - { - "id": "note-niov875z", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-5xuqvazv", - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-poawgjl8", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-44dvhfxz", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-g84u6o31", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-5xuqvazv", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - }, - { - "id": "measure-bg26hhcc", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-p94lw6g6", - "notes": [ - { - "id": "note-1cnd787r", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-e1s1uefl", - "hidden": false - }, - { - "id": "note-igxn5vko", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-e1s1uefl", - "hidden": false - }, - { - "id": "note-e8v88wkp", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-e1s1uefl", - "hidden": false - }, - { - "id": "note-el762yor", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-oatrapdk", - "hidden": false - }, - { - "id": "note-m1gpu28c", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-oatrapdk", - "hidden": false - }, - { - "id": "note-an156n90", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-oatrapdk", - "hidden": false - }, - { - "id": "note-edgh0gjq", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-769g892y", - "hidden": false - }, - { - "id": "note-r3xgtdxq", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-769g892y", - "hidden": false - }, - { - "id": "note-154fr5rm", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-769g892y", - "hidden": false - }, - { - "id": "note-c67fzxu6", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-5dnmbgya", - "hidden": false - }, - { - "id": "note-eh1y44pm", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-5dnmbgya", - "hidden": false - }, - { - "id": "note-hyuiw6ok", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-5dnmbgya", - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-e1s1uefl", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-oatrapdk", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-769g892y", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-5dnmbgya", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - }, - { - "id": "measure-poqg1x91", - "time_signature": "4/4", - "key_signature": "C", - "voices": [ - { - "id": "voice-uruarcnq", - "notes": [ - { - "id": "note-oz7r0sve", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "e", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-nijslfzp", - "hidden": false - }, - { - "id": "note-xrmv3n5y", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-nijslfzp", - "hidden": false - }, - { - "id": "note-b9ekeqii", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-nijslfzp", - "hidden": false - }, - { - "id": "note-yzywgfvc", - "kind": "note", - "duration_ticks": 1366, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-4h58hac4", - "hidden": false - }, - { - "id": "note-niurzq3k", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-4h58hac4", - "hidden": false - }, - { - "id": "note-jtyj8v4f", - "kind": "note", - "duration_ticks": 1365, - "pitches": [ - { - "letter": "b", - "accidental": "n", - "octave": 3 - } - ], - "display_ticks": 2048, - "tuplet_id": "tuplet-4h58hac4", - "hidden": false - }, - { - "id": "note-vtnila6w", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false - } - ], - "tuplets": [ - { - "id": "tuplet-nijslfzp", - "note_count": 3, - "space_count": 2 - }, - { - "id": "tuplet-4h58hac4", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - } - ] - } + "title": "01 The Diatonic Scales", + "composer": "", + "key": "C", + "time": "4/4", + "clef": "treble", + "part": "Vocal", + "role": "vocal", + "style": "Med. Swing", + "measures": [ + "{3:2 C4:8 D4:8 E4:8} {3:2 D4:8 E4:8 F4:8} {3:2 E4:8 F4:8 G4:8} {3:2 F4:8 G4:8 A4:8}", + "{3:2 G4:8 A4:8 B4:8} {3:2 A4:8 B4:8 C5:8} {3:2 B4:8 C5:8 D5:8} {3:2 C5:8 D5:8 E5:8}", + "{3:2 D5:8 E5:8 F5:8} {3:2 E5:8 F5:8 G5:8} {3:2 F5:8 G5:8 A5:8} {3:2 G5:8 A5:8 B5:8}", + "{3:2 A5:8 B5:8 C6:8} {3:2 B5:8 C6:8 D6:8} C6:2", + "{3:2 C6:8 B5:8 A5:8} {3:2 B5:8 A5:8 G5:8} {3:2 A5:8 G5:8 F5:8} {3:2 G5:8 F5:8 E5:8}", + "{3:2 F5:8 E5:8 D5:8} {3:2 E5:8 D5:8 C5:8} {3:2 D5:8 C5:8 B4:8} {3:2 C5:8 B4:8 A4:8}", + "{3:2 B4:8 A4:8 G4:8} {3:2 A4:8 G4:8 F4:8} {3:2 G4:8 F4:8 E4:8} {3:2 F4:8 E4:8 D4:8}", + "{3:2 E4:8 D4:8 C4:8} {3:2 D4:8 C4:8 B3:8} C4:2" ] } diff --git a/data/songs/autumn-leaves.json b/data/songs/autumn-leaves.json index 90f8536..40b3e1d 100644 --- a/data/songs/autumn-leaves.json +++ b/data/songs/autumn-leaves.json @@ -1,1867 +1,46 @@ { "version": 1, - "metadata": { - "title": "Autumn Leaves", - "composer": "Joseph Kosma / Johnny Mercer / Jaques Prevert", - "key_signature": "F", - "time_signature": "4/4", - "style": "Med." - }, - "staves": [ - { - "id": "staff-vocal", - "name": "Vocal", - "clef": "treble", - "role": "vocal", - "arranged": false, - "measures": [ - { - "id": "measure-1", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-cvatw8bc", - "notes": [ - { - "id": "note-kftmjdeo", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-ort00jrw", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "The", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-adk5a1tq", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "fall-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-wtrdl5cd", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "ing", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-og0kdby4", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-w6nqydxy", - "notes": [ - { - "id": "note-2x980i92", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "chord_symbol": "G-7", - "lyric": "leaves", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-7sdwgg2f", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-it2dteqr", - "notes": [ - { - "id": "note-ce95hhjb", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "chord_symbol": "C7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-f5kh0w5d", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "drift", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-wp6hjfyh", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "by", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-8trg90ha", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "my", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-q3ofslil", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-k7ov9eg7", - "notes": [ - { - "id": "note-n2lycist", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "Fmaj7", - "lyric": "win-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-eysxu055", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "dow,", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-pztvr49j", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-k0tkcjg6", - "notes": [ - { - "id": "note-pr7ph4u9", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "Bbmaj7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-ok9748wx", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 3, - "accidental": "b" - } - ], - "lyric": "the", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-v8zo070i", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "au-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-1t8ne5bg", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "tumn", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-b5b4z30o", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-nmnhixb5", - "notes": [ - { - "id": "note-91smlkm9", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "E-7b5", - "lyric": "leaves", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-imn1szfs", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-yo3sky5a", - "notes": [ - { - "id": "note-yua3rxui", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "A7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-xe74q1me", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "octave": 3, - "accidental": "n" - } - ], - "lyric": "of", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-28zu0ten", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 3, - "accidental": "n" - } - ], - "lyric": "red", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-tszfdakz", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 4, - "accidental": "#" - } - ], - "lyric": "and", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-v8ibq8ds", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-tqioavxl", - "notes": [ - { - "id": "note-zfrtmg7a", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "D-", - "lyric": "gold.", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-hbqisegy", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-5cm3falc", - "notes": [ - { - "id": "note-ytgdeo67", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-fp7ucokq", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "I", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-hbzpl1k5", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "see", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-9o8q2n3z", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "your", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-sylh5t5d", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-q098mba5", - "notes": [ - { - "id": "note-llocjynv", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "chord_symbol": "G-7", - "lyric": "lips,", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-thgn02dn", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-4n9g7k5j", - "notes": [ - { - "id": "note-emdutch0", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "chord_symbol": "C7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-dklu2n82", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "the", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-t2qdgqo4", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "sum-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-8xzd5r9l", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "mer", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-afhotm0x", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-gpwnaxt6", - "notes": [ - { - "id": "note-gjbcbj36", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "Fmaj7", - "lyric": "kiss-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-a9cmn74e", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "es,", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-pjz4md5h", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-7b9xo3y9", - "notes": [ - { - "id": "note-ket17i9z", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "Bbmaj7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-gp8yy9vj", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 3, - "accidental": "b" - } - ], - "lyric": "the", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-9vbtjqon", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "sun-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-abs2war7", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "burnt", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-see1f4xb", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-rowy0opm", - "notes": [ - { - "id": "note-2xxtk304", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "E-7b5", - "lyric": "hands", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-49aemr1t", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-ycs189a1", - "notes": [ - { - "id": "note-x2q6822u", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "A7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-zxdcl5bc", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "I", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-k7qb52mx", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "used", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-es78bjke", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "to", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-uzgt546a", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-ig2djaqw", - "notes": [ - { - "id": "note-iqzkoutt", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "D-", - "lyric": "hold.", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-s4yzxinj", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-buq54egy", - "notes": [ - { - "id": "note-xtqweec3", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-f0vw2sct", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-fhvpnqlr", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 4, - "accidental": "#" - } - ], - "lyric": "Since", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-9m0g5h7u", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "you", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-x79rfne1", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-1u6nndng", - "notes": [ - { - "id": "note-qrwcj60o", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "E-7b5", - "lyric": "went", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-65pdz9zy", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "octave": 3, - "accidental": "n" - } - ], - "lyric": "a-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-arjkwn1m", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "way", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-00plz0km", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-7px6hs9q", - "notes": [ - { - "id": "note-vlq587o8", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "A7b9", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-fnfv02nq", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "the", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-rwm28uqb", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "days", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-8zum2xrn", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "grow", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-lmk6mq1c", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-unkg2a7i", - "notes": [ - { - "id": "note-2e3ec0ay", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "D-", - "lyric": "long,", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-7w1ev64s", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-9lqag21l", - "notes": [ - { - "id": "note-otsf2fod", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-vscu5kjl", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "and", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-8qdeyzjb", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "soon", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-t5h4al7h", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "I'll", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-ueqif3lp", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-9kuvfh0u", - "notes": [ - { - "id": "note-w444uqj3", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "G-7", - "lyric": "hear", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-aylagr2s", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-egs69tp3", - "notes": [ - { - "id": "note-ddwkim0c", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "C7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-jynigw67", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "old", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-sipp7fqi", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "octave": 5, - "accidental": "n" - } - ], - "lyric": "win-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-7euzlojx", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "lyric": "ter's", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-4htrpn3s", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-jqd5sf7s", - "notes": [ - { - "id": "note-r4th9nxz", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "Fmaj7", - "lyric": "song.", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-w64pueqa", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-rf52sqot", - "notes": [ - { - "id": "note-02jr3xk8", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-wv1bi4gn", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-rxl190tg", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "#" - } - ], - "lyric": "But", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-8j3fvdpc", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "I", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-mql83vyd", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-8mxoy6p0", - "notes": [ - { - "id": "note-d1epy4lw", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "chord_symbol": "E-7b5", - "lyric": "miss", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-wuowf662", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "lyric": "you", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-jr5q5iyc", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "most", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-e2hslw12", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "of", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-ymn81b1d", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-bvp2pidx", - "notes": [ - { - "id": "note-px4kf5mp", - "kind": "note", - "hidden": false, - "duration_ticks": 12288, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "A7b9", - "lyric": "all,", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-s0cjqrtx", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ], - "lyric": "my", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-vnqmp01v", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-auijfivx", - "notes": [ - { - "id": "note-tcl6axz2", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "D-7", - "lyric": "dar-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-iz6cyq64", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "G7", - "lyric": "ling,", - "tie_to_next": true, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-lh1if10x", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-5ce4xnk3", - "notes": [ - { - "id": "note-kjct60cu", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "C-7", - "tie_to_next": false, - "tie_from_previous": true - }, - { - "id": "note-a8c2k54x", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "F7", - "lyric": "when", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-3kozczl8", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-lcphsjhh", - "notes": [ - { - "id": "note-7eifmvr3", - "kind": "note", - "hidden": false, - "duration_ticks": 12288, - "pitches": [ - { - "letter": "g", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "E-7b5", - "lyric": "au-", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-2tamuxj0", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "tumn", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-ebotxuh3", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-dkpubweu", - "notes": [ - { - "id": "note-9unebppn", - "kind": "note", - "hidden": false, - "duration_ticks": 8192, - "pitches": [ - { - "letter": "e", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "A7b9", - "lyric": "leaves", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-jhanjbm3", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "octave": 4, - "accidental": "n" - } - ], - "lyric": "start", - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-ytb1byx8", - "kind": "note", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "octave": 3, - "accidental": "n" - } - ], - "lyric": "to", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-bm7bgjko", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-1ceiqh93", - "notes": [ - { - "id": "note-0gehejln", - "kind": "note", - "hidden": false, - "duration_ticks": 16384, - "pitches": [ - { - "letter": "d", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "D-", - "lyric": "fall.", - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-q8k7806a", - "time_signature": "4/4", - "key_signature": "F", - "voices": [ - { - "id": "voice-wduskp4m", - "notes": [ - { - "id": "note-tm7ku54y", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-8rzwfa1n", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-8h0hmq20", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - }, - { - "id": "note-ck5hol0w", - "kind": "rest", - "hidden": false, - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "tie_to_next": false, - "tie_from_previous": false - } - ], - "tuplets": [] - } - ] - } - ] - } + "title": "Autumn Leaves", + "composer": "Joseph Kosma / Johnny Mercer / Jaques Prevert", + "key": "F", + "time": "4/4", + "clef": "treble", + "part": "Vocal", + "role": "vocal", + "style": "Med.", + "measures": [ + "r:4 D4:4 \"The\" E4:4 \"fall-\" F4:4 \"ing\"", + "[G-7] Bb4:1~ \"leaves\"", + "[C7] ~Bb4:4 C4:4 \"drift\" D4:4 \"by\" E4:4 \"my\"", + "[Fmaj7] A4:2 \"win-\" A4:2~ \"dow,\"", + "[Bbmaj7] ~A4:4 Bb3:4 \"the\" C4:4 \"au-\" D4:4 \"tumn\"", + "[E-7b5] G4:1~ \"leaves\"", + "[A7] ~G4:4 A3:4 \"of\" B3:4 \"red\" C#4:4 \"and\"", + "[D-] F4:1 \"gold.\"", + "r:4 D4:4 \"I\" E4:4 \"see\" F4:4 \"your\"", + "[G-7] Bb4:1~ \"lips,\"", + "[C7] ~Bb4:4 C4:4 \"the\" D4:4 \"sum-\" E4:4 \"mer\"", + "[Fmaj7] A4:2 \"kiss-\" A4:2~ \"es,\"", + "[Bbmaj7] ~A4:4 Bb3:4 \"the\" C4:4 \"sun-\" D4:4 \"burnt\"", + "[E-7b5] G4:1~ \"hands\"", + "[A7] ~G4:4 E4:4 \"I\" G4:4 \"used\" F4:4 \"to\"", + "[D-] D4:1~ \"hold.\"", + "~D4:4 r:4 C#4:4 \"Since\" D4:4 \"you\"", + "[E-7b5] E4:4 \"went\" A3:4 \"a-\" E4:2~ \"way\"", + "[A7b9] ~E4:4 E4:4 \"the\" D4:4 \"days\" E4:4 \"grow\"", + "[D-] F4:1~ \"long,\"", + "~F4:4 F4:4 \"and\" E4:4 \"soon\" F4:4 \"I'll\"", + "[G-7] G4:1~ \"hear\"", + "[C7] ~G4:4 C4:4 \"old\" C5:4 \"win-\" Bb4:4 \"ter's\"", + "[Fmaj7] A4:1~ \"song.\"", + "~A4:4 r:4 G#4:4 \"But\" A4:4 \"I\"", + "[E-7b5] Bb4:4 \"miss\" Bb4:4 \"you\" G4:4 \"most\" G4:4 \"of\"", + "[A7b9] E4:2. \"all,\" Bb4:4 \"my\"", + "[D-7] A4:2 \"dar-\" [G7] A4:2~ \"ling,\"", + "[C-7] ~A4:2 [F7] D4:2 \"when\"", + "[E-7b5] G4:2. \"au-\" F4:4 \"tumn\"", + "[A7b9] E4:2 \"leaves\" F4:4 \"start\" A3:4 \"to\"", + "[D-] D4:1 \"fall.\"", + "r:4 r:4 r:4 r:4" ] -} \ No newline at end of file +} diff --git a/data/songs/it-could-happen-to-you.json b/data/songs/it-could-happen-to-you.json index 4c7ad2d..8f9bd76 100644 --- a/data/songs/it-could-happen-to-you.json +++ b/data/songs/it-could-happen-to-you.json @@ -1,1740 +1,45 @@ { "version": 1, - "metadata": { - "title": "It Could Happen to You", - "composer": "James Van Heusen / Johnny Burke", - "key_signature": "Eb", - "time_signature": "4/4", - "style": "Med." - }, - "staves": [ - { - "id": "staff-vocal", - "name": "Vocal", - "clef": "treble", - "role": "vocal", - "measures": [ - { - "id": "measure-1", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-5pn6gd3v", - "notes": [ - { - "id": "note-kuiyrgkn", - "kind": "note", - "duration_ticks": 12288, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 3 - } - ], - "chord_symbol": "Ebmaj7", - "hidden": false, - "lyric": "Hide" - }, - { - "id": "note-iz1k23sv", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "your" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-k3lqg44o", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-pix1zraq", - "notes": [ - { - "id": "note-yw8a8z8s", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "G-7b5", - "hidden": false, - "lyric": "heart" - }, - { - "id": "note-9yek441g", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "from" - }, - { - "id": "note-i0o0s8ef", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "sight," - }, - { - "id": "note-7sp45zld", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "C7b9", - "hidden": false, - "tie_from_previous": true - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-nxug7h75", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-evww19bt", - "notes": [ - { - "id": "note-yhhanabs", - "kind": "note", - "duration_ticks": 12288, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "F-7", - "hidden": false, - "lyric": "lock" - }, - { - "id": "note-ox6q8p3f", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "your" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-is7t6uss", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-te98z581", - "notes": [ - { - "id": "note-ylc4xlxy", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "A-7b5", - "hidden": false, - "lyric": "dreams" - }, - { - "id": "note-qbbxzeg2", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "at" - }, - { - "id": "note-d61uoqsd", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "night," - }, - { - "id": "note-u8mxymql", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "D7b9", - "hidden": false, - "tie_from_previous": true - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-2z4szbbx", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-n8p5z1bh", - "notes": [ - { - "id": "note-gye69ads", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "Ebmaj7", - "hidden": false, - "lyric": "it" - }, - { - "id": "note-ny4b1gma", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "could" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-60xy76js", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-h8i79syb", - "notes": [ - { - "id": "note-vhq0leeo", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "Ab7", - "hidden": false, - "lyric": "hap-" - }, - { - "id": "note-c5lb0ws3", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "pen" - }, - { - "id": "note-1kfy9yl0", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "to" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-cny54efj", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-nym3aeo4", - "notes": [ - { - "id": "note-k8nlot0n", - "kind": "note", - "duration_ticks": 16384, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "G-7b5", - "hidden": false, - "lyric": "you." - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-65am4rw4", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-8oovtl0v", - "notes": [ - { - "id": "note-8llz3qnm", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "C7b9" - }, - { - "id": "note-r0rc4u4u", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - }, - { - "id": "note-bohzxdcg", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - }, - { - "id": "note-y0aufljh", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-wotid6so", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-5hb08eka", - "notes": [ - { - "id": "note-mggawdka", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "F-7", - "hidden": false, - "lyric": "Don't" - }, - { - "id": "note-ag8yif9r", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "count" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-6i2qhlv2", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-67hetdfn", - "notes": [ - { - "id": "note-obpikul5", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "Db7", - "hidden": false, - "lyric": "stars" - }, - { - "id": "note-8bih3kg7", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "or" - }, - { - "id": "note-tw4cn4ph", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "you" - }, - { - "id": "note-2r2q349z", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "might" - }, - { - "id": "note-u2ros5zw", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "stum-" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-hfrhzer7", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-1vorjn9z", - "notes": [ - { - "id": "note-u65ems92", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "Ebmaj7", - "hidden": false, - "tie_from_previous": true - }, - { - "id": "note-3km66pic", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "ble," - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-n9f993bg", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-4g9sbvq6", - "notes": [ - { - "id": "note-e8iclu4a", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "D-7b5" - }, - { - "id": "note-1v0tyulw", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - }, - { - "id": "note-dl1pgdvd", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "G7" - }, - { - "id": "note-byz4jhwc", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-1c0ciyw4", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-uiunwi47", - "notes": [ - { - "id": "note-k7069vr8", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "C-7", - "hidden": false, - "lyric": "some-" - }, - { - "id": "note-wtw8u0pe", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "one" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-d20ryz87", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-izb44ir3", - "notes": [ - { - "id": "note-jmvd1d5d", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "F7", - "hidden": false, - "lyric": "drops" - }, - { - "id": "note-ko7pf8ta", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "a" - }, - { - "id": "note-bqv3m7zk", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "sigh" - }, - { - "id": "note-jpmylsfs", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "and" - }, - { - "id": "note-7x2p92i7", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "down" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-omwmbsxg", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-lhyh6eto", - "notes": [ - { - "id": "note-cdqqeh9z", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "F-7", - "hidden": false, - "tie_from_previous": true - }, - { - "id": "note-z854ug7z", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "you" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-0pv3rjyi", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-87ihdgf9", - "notes": [ - { - "id": "note-vo4bl9ww", - "kind": "note", - "duration_ticks": 6144, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "Bb7", - "hidden": false, - "lyric": "tum-" - }, - { - "id": "note-q0kwjwz9", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "ble." - }, - { - "id": "note-2jafn1rx", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_from_previous": true - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-emaxg70d", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-p8k5sc7t", - "notes": [ - { - "id": "note-zbeip48s", - "kind": "note", - "duration_ticks": 12288, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 3 - } - ], - "chord_symbol": "Ebmaj7", - "hidden": false, - "lyric": "Keep" - }, - { - "id": "note-nndmo2ix", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "an" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-4gedf949", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-jlvgs55c", - "notes": [ - { - "id": "note-i6ypequk", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "G-7b5", - "hidden": false, - "lyric": "eye" - }, - { - "id": "note-0m6reggh", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "on" - }, - { - "id": "note-xwjd2gd2", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "spring," - }, - { - "id": "note-69glta5f", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "C7b9", - "hidden": false, - "tie_from_previous": true - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-9cehautu", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-530rnrt5", - "notes": [ - { - "id": "note-jopxmfvg", - "kind": "note", - "duration_ticks": 12288, - "pitches": [ - { - "letter": "c", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "F-7", - "hidden": false, - "lyric": "run" - }, - { - "id": "note-rrw00a1j", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "when" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-v34tymkr", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-n2541r6g", - "notes": [ - { - "id": "note-3m44ed29", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "A-7b9", - "hidden": false, - "lyric": "church" - }, - { - "id": "note-hm8euexe", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "bells" - }, - { - "id": "note-yxakpaqn", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "ring," - }, - { - "id": "note-f9wcddmz", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "D7b9", - "hidden": false, - "tie_from_previous": true - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-mth3p6xr", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-za6bru5e", - "notes": [ - { - "id": "note-q7kqglnq", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "Ebmaj7", - "hidden": false, - "lyric": "it" - }, - { - "id": "note-tm0fk4h3", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "could" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-cvqbyk45", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-4ehj66zt", - "notes": [ - { - "id": "note-7mi2nfo8", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "Ab7", - "hidden": false, - "lyric": "hap-" - }, - { - "id": "note-m9quav8w", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "a", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "pen" - }, - { - "id": "note-el0diqd8", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "to" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-dmfg986x", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-qqidkgap", - "notes": [ - { - "id": "note-ycljp8e0", - "kind": "note", - "duration_ticks": 16384, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "G-7b5", - "hidden": false, - "lyric": "you." - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-c5153ghx", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-la9t1z91", - "notes": [ - { - "id": "note-mmj7w8go", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "C7b9" - }, - { - "id": "note-5o2sfcw1", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - }, - { - "id": "note-4fvnubzs", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - }, - { - "id": "note-2lfybg24", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-4mb2skuk", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-obks9c6s", - "notes": [ - { - "id": "note-1d9wlwi9", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "F-7", - "hidden": false, - "lyric": "All" - }, - { - "id": "note-s0k5nska", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "I" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-6qc76e48", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-h47x88u0", - "notes": [ - { - "id": "note-kyueh4u9", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "Db7", - "hidden": false, - "lyric": "did" - }, - { - "id": "note-i5lf2fb0", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "was" - }, - { - "id": "note-ff5amvez", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "won-" - }, - { - "id": "note-m94d1kvm", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "der" - }, - { - "id": "note-5s7022k0", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "how" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-vu7wroo6", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-v1fa1ey0", - "notes": [ - { - "id": "note-g702zkvd", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "Ebmaj7", - "hidden": false, - "tie_from_previous": true - }, - { - "id": "note-awpxmxrp", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "d", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "your" - }, - { - "id": "note-u7ljw048", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "chord_symbol": "Ab7", - "hidden": false, - "lyric": "arms" - }, - { - "id": "note-gw3nkn0r", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "lyric": "would" - }, - { - "id": "note-lth1pkjg", - "kind": "note", - "duration_ticks": 2048, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "hidden": false, - "tie_to_next": true, - "lyric": "be," - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-yepi7qn2", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-yauzir01", - "notes": [ - { - "id": "note-ak2l1teq", - "kind": "note", - "duration_ticks": 16384, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "G-7b5", - "hidden": false, - "tie_from_previous": true - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-xru0shgp", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-xbf773uk", - "notes": [ - { - "id": "note-wsy00yxy", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 3 - } - ], - "chord_symbol": "F-7", - "hidden": false, - "lyric": "and" - }, - { - "id": "note-12fmjz0z", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "b", - "accidental": "b", - "octave": 3 - } - ], - "hidden": false, - "lyric": "it" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-bjecb6gs", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-y4wt02vw", - "notes": [ - { - "id": "note-6kh4v2ql", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "a", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "Bb7", - "hidden": false, - "lyric": "hap-" - }, - { - "id": "note-mw2uwy6p", - "kind": "note", - "duration_ticks": 8192, - "pitches": [ - { - "letter": "f", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "pened" - }, - { - "id": "note-66fg8qvq", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "g", - "accidental": "n", - "octave": 4 - } - ], - "hidden": false, - "lyric": "to" - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-9jy6byfe", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-j8gkm4cm", - "notes": [ - { - "id": "note-eib9ym2k", - "kind": "note", - "duration_ticks": 16384, - "pitches": [ - { - "letter": "e", - "accidental": "b", - "octave": 4 - } - ], - "chord_symbol": "Ebmaj7", - "hidden": false, - "lyric": "me." - } - ], - "tuplets": [] - } - ] - }, - { - "id": "measure-gnwn1v6i", - "time_signature": "4/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-6caphzu5", - "notes": [ - { - "id": "note-h04zhpyx", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "F-7" - }, - { - "id": "note-c4wrn4pn", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - }, - { - "id": "note-aivh8atp", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ], - "chord_symbol": "Bb7" - }, - { - "id": "note-yo6pxzqs", - "kind": "rest", - "duration_ticks": 4096, - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "n" - } - ] - } - ], - "tuplets": [] - } - ] - } - ] - } + "title": "It Could Happen to You", + "composer": "James Van Heusen / Johnny Burke", + "key": "Eb", + "time": "4/4", + "clef": "treble", + "part": "Vocal", + "role": "vocal", + "style": "Med.", + "measures": [ + "[Ebmaj7] Bb3:2. \"Hide\" C4:4 \"your\"", + "[G-7b5] Db4:4 \"heart\" C4:8 \"from\" G4:8~ \"sight,\" [C7b9] ~G4:2", + "[F-7] C4:2. \"lock\" D4:4 \"your\"", + "[A-7b5] Eb4:4 \"dreams\" D4:8 \"at\" A4:8~ \"night,\" [D7b9] ~A4:2", + "[Ebmaj7] Bb4:2 \"it\" Bb4:2 \"could\"", + "[Ab7] Bb4:4 \"hap-\" Ab4:2 \"pen\" Eb4:4 \"to\"", + "[G-7b5] G4:1 \"you.\"", + "[C7b9] r:4 r:4 r:4 r:4", + "[F-7] G4:2 \"Don't\" G4:2 \"count\"", + "[Db7] G4:4 \"stars\" Ab4:4 \"or\" Eb4:4 \"you\" F4:8 \"might\" G4:8~ \"stum-\"", + "[Ebmaj7] ~G4:2 G4:2 \"ble,\"", + "[D-7b5] r:4 r:4 [G7] r:4 r:4", + "[C-7] Eb4:2 \"some-\" Eb4:2 \"one\"", + "[F7] Eb4:4 \"drops\" F4:4 \"a\" C4:4 \"sigh\" D4:8 \"and\" Eb4:8~ \"down\"", + "[F-7] ~Eb4:2 F4:2 \"you\"", + "[Bb7] C4:4. \"tum-\" D4:8~ \"ble.\" ~D4:2", + "[Ebmaj7] Bb3:2. \"Keep\" C4:4 \"an\"", + "[G-7b5] Db4:4 \"eye\" C4:8 \"on\" G4:8~ \"spring,\" [C7b9] ~G4:2", + "[F-7] C4:2. \"run\" D4:4 \"when\"", + "[A-7b9] Eb4:4 \"church\" D4:8 \"bells\" A4:8~ \"ring,\" [D7b9] ~A4:2", + "[Ebmaj7] Bb4:2 \"it\" Bb4:2 \"could\"", + "[Ab7] Bb4:4 \"hap-\" Ab4:2 \"pen\" Eb4:4 \"to\"", + "[G-7b5] G4:1 \"you.\"", + "[C7b9] r:4 r:4 r:4 r:4", + "[F-7] G4:2 \"All\" G4:2 \"I\"", + "[Db7] G4:4 \"did\" Ab4:4 \"was\" Eb4:4 \"won-\" F4:8 \"der\" G4:8~ \"how\"", + "[Ebmaj7] ~G4:4 D4:4 \"your\" [Ab7] F4:4 \"arms\" Eb4:8 \"would\" Bb4:8~ \"be,\"", + "[G-7b5] ~Bb4:1", + "[F-7] Bb3:2 \"and\" Bb3:2 \"it\"", + "[Bb7] Ab4:4 \"hap-\" F4:2 \"pened\" G4:4 \"to\"", + "[Ebmaj7] Eb4:1 \"me.\"", + "[F-7] r:4 r:4 [Bb7] r:4 r:4" ] -} \ No newline at end of file +} diff --git a/generator/score.py b/generator/score.py index d292277..4382715 100644 --- a/generator/score.py +++ b/generator/score.py @@ -43,7 +43,7 @@ def load_json(path: Path) -> dict[str, Any]: document = json.load(stream) if not isinstance(document, dict): raise ValueError(f"{path} is not a score document") - return document + return normalize_score_document(document, path) def write_json(path: Path, document: dict[str, Any]) -> None: @@ -65,6 +65,368 @@ def human_label(value: str) -> str: return " ".join(part.capitalize() for part in text.split()) or "General" +class CompactScoreError(ValueError): + pass + + +DURATION_TICKS = { + "1": WHOLE_TICKS, + "2": HALF_TICKS, + "4": QUARTER_TICKS, + "8": EIGHTH_TICKS, + "16": SIXTEENTH_TICKS, + "32": THIRTY_SECOND_TICKS, +} + + +def score_field(document: dict[str, Any], metadata: dict[str, Any], *names: str, fallback: str = "") -> str: + for name in names: + value = document.get(name) + if isinstance(value, str): + return value + value = metadata.get(name) + if isinstance(value, str): + return value + return fallback + + +def parse_duration_token(text: str, label: str) -> int: + dotted = text.endswith(".") + base_text = text[:-1] if dotted else text + if base_text not in DURATION_TICKS: + raise CompactScoreError(f"{label}: unsupported duration {text!r}") + ticks = DURATION_TICKS[base_text] + return ticks + ticks // 2 if dotted else ticks + + +def compact_pitch(letter: str, accidental: str, octave: int) -> dict[str, Any]: + return { + "letter": letter.lower(), + "accidental": accidental or "n", + "octave": octave, + } + + +class CompactMeasureParser: + def __init__(self, text: str, *, measure_number: int, time_signature: str) -> None: + self.text = text + self.index = 0 + self.measure_number = measure_number + self.time_signature = time_signature + self.sequence = 0 + self.tuplet_sequence = 0 + self.pending_chord = "" + self.last_note: dict[str, Any] | None = None + self.last_octave = 4 + self.tuplets: list[dict[str, Any]] = [] + + def label(self) -> str: + return f"measure {self.measure_number}" + + def error(self, message: str) -> CompactScoreError: + return CompactScoreError(f"{self.label()}, column {self.index + 1}: {message}") + + def peek(self) -> str: + return self.text[self.index] if self.index < len(self.text) else "" + + def skip_space(self) -> None: + while self.peek() and self.peek().isspace(): + self.index += 1 + + def parse_until(self, close: str, label: str) -> str: + start = self.index + escaped = False + while self.index < len(self.text): + char = self.text[self.index] + if escaped: + escaped = False + self.index += 1 + continue + if char == "\\": + escaped = True + self.index += 1 + continue + if char == close: + value = self.text[start:self.index] + self.index += 1 + return bytes(value, "utf-8").decode("unicode_escape") + self.index += 1 + raise self.error(f"unterminated {label}") + + def parse_chord(self) -> None: + self.index += 1 + self.pending_chord = self.parse_until("]", "chord symbol").strip() + if not self.pending_chord: + raise self.error("chord symbol must not be empty") + + def parse_lyric(self) -> None: + if self.last_note is None: + raise self.error("lyric must follow a note") + self.index += 1 + lyric = self.parse_until('"', "lyric") + self.last_note["lyric"] = lyric + + def parse_tuplet_header(self) -> tuple[int, int]: + self.skip_space() + match = re.match(r"(\d+)\s*:\s*(\d+)", self.text[self.index:]) + if match is None: + raise self.error("tuplet must start with N:M") + self.index += match.end() + note_count = int(match.group(1)) + space_count = int(match.group(2)) + if note_count <= 0 or space_count <= 0: + raise self.error("tuplet counts must be positive") + return note_count, space_count + + def parse_tuplet(self) -> list[dict[str, Any]]: + self.index += 1 + note_count, space_count = self.parse_tuplet_header() + self.tuplet_sequence += 1 + tuplet_id = f"tuplet-{self.measure_number}-{self.tuplet_sequence}" + notes: list[dict[str, Any]] = [] + while True: + self.skip_space() + if not self.peek(): + raise self.error("unterminated tuplet") + if self.peek() == "}": + self.index += 1 + break + if self.peek() == "{": + raise self.error("nested tuplets are not supported") + notes.extend(self.parse_item()) + if len(notes) != note_count: + raise self.error(f"tuplet {tuplet_id} contains {len(notes)} notes/rests, expected {note_count}") + self.apply_tuplet_duration(notes, note_count, space_count) + for note in notes: + note["tuplet_id"] = tuplet_id + self.tuplets.append({ + "id": tuplet_id, + "note_count": note_count, + "space_count": space_count, + }) + return notes + + def apply_tuplet_duration(self, notes: list[dict[str, Any]], note_count: int, space_count: int) -> None: + numerators = [int(note["duration_ticks"]) * space_count for note in notes] + floors = [value // note_count for value in numerators] + target_total = sum(numerators) // note_count + remainder = target_total - sum(floors) + order = sorted(range(len(notes)), key=lambda index: numerators[index] % note_count, reverse=True) + actual = floors[:] + for index in order[:remainder]: + actual[index] += 1 + for note, ticks in zip(notes, actual): + display = int(note["duration_ticks"]) + note["duration_ticks"] = max(1, ticks) + note["display_ticks"] = display + + def parse_pitch_atom(self) -> dict[str, Any]: + start = self.index + char = self.peek() + if char.lower() not in "abcdefg": + raise self.error("expected pitch") + self.index += 1 + accidental = "" + if self.peek() in {"#", "b", "n"}: + accidental = self.peek() + self.index += 1 + octave_start = self.index + while self.peek().isdigit(): + self.index += 1 + if self.index > octave_start: + self.last_octave = int(self.text[octave_start:self.index]) + if self.index == start: + raise self.error("expected pitch") + return compact_pitch(char, accidental, self.last_octave) + + def parse_pitch_group(self) -> list[dict[str, Any]]: + self.index += 1 + pitches: list[dict[str, Any]] = [] + while True: + self.skip_space() + if not self.peek(): + raise self.error("unterminated pitch group") + if self.peek() == ")": + self.index += 1 + break + pitches.append(self.parse_pitch_atom()) + if not pitches: + raise self.error("pitch group must contain at least one pitch") + return pitches + + def parse_note(self, tie_from_previous: bool = False) -> dict[str, Any]: + start = self.index + char = self.peek() + if not char: + raise self.error("expected note or rest") + kind = "rest" if char.lower() == "r" else "note" + if kind == "rest": + self.index += 1 + pitches = [copy.deepcopy(REST_PITCH)] + elif char == "(": + pitches = self.parse_pitch_group() + else: + pitches = [self.parse_pitch_atom()] + if self.peek() != ":": + raise self.error(f"expected ':' after {self.text[start:self.index]!r}") + self.index += 1 + duration_start = self.index + while self.peek().isdigit(): + self.index += 1 + if self.peek() == ".": + self.index += 1 + duration_text = self.text[duration_start:self.index] + if not duration_text: + raise self.error("duration is required") + self.sequence += 1 + note: dict[str, Any] = { + "id": f"note-{self.measure_number}-{self.sequence}", + "kind": kind, + "duration_ticks": parse_duration_token(duration_text, self.label()), + "pitches": pitches, + "hidden": False, + } + if self.pending_chord: + note["chord_symbol"] = self.pending_chord + self.pending_chord = "" + if tie_from_previous: + note["tie_from_previous"] = True + if self.peek() == "~": + note["tie_to_next"] = True + self.index += 1 + self.last_note = note + return note + + def parse_item(self) -> list[dict[str, Any]]: + self.skip_space() + char = self.peek() + if not char: + return [] + if char == "[": + self.parse_chord() + return [] + if char == '"': + self.parse_lyric() + return [] + if char == "{": + return self.parse_tuplet() + if char == "}": + return [] + if char == "~": + self.index += 1 + self.skip_space() + next_char = self.peek() + if next_char and (next_char.lower() in "abcdefgr" or next_char == "("): + return [self.parse_note(tie_from_previous=True)] + if self.last_note is None: + raise self.error("tie marker must attach to a note") + self.last_note["tie_to_next"] = True + return [] + if char.lower() in "abcdefgr" or char == "(": + return [self.parse_note()] + raise self.error("expected note, rest, chord, lyric, tie, or tuplet") + + def parse(self) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + notes: list[dict[str, Any]] = [] + while True: + self.skip_space() + if not self.peek(): + break + notes.extend(self.parse_item()) + if self.pending_chord: + raise self.error(f"chord {self.pending_chord!r} is not followed by a note or rest") + total = sum(int(note.get("duration_ticks", 0)) for note in notes) + capacity = measure_capacity_ticks(self.time_signature) + if total > capacity: + raise CompactScoreError(f"{self.label()}: duration total {total} exceeds {self.time_signature} capacity {capacity}") + return notes, self.tuplets + + +def compact_measure_text(value: Any, index: int) -> tuple[str, dict[str, str]]: + if isinstance(value, str): + return value, {} + if isinstance(value, dict): + text = value.get("notes", value.get("notation", "")) + if not isinstance(text, str): + raise CompactScoreError(f"measure {index + 1}: notes must be a string") + overrides: dict[str, str] = {} + for source, target in ( + ("section", "section_label"), + ("section_label", "section_label"), + ("key", "key_signature"), + ("key_signature", "key_signature"), + ("time", "time_signature"), + ("time_signature", "time_signature"), + ): + field = value.get(source) + if isinstance(field, str): + overrides[target] = field + return text, overrides + raise CompactScoreError(f"measure {index + 1}: compact measure must be a string or object") + + +def normalize_score_document(document: dict[str, Any], path: Path | None = None) -> dict[str, Any]: + if isinstance(document.get("staves"), list): + return document + measures_value = document.get("measures") + if not isinstance(measures_value, list): + return document + metadata_value = document.get("metadata", {}) + metadata = metadata_value if isinstance(metadata_value, dict) else {} + title = score_field(document, metadata, "title", fallback=human_label(path.stem if path is not None else "score")) + composer = score_field(document, metadata, "composer") + key_signature = score_field(document, metadata, "key_signature", "key", fallback="C") + time_signature = score_field(document, metadata, "time_signature", "time", fallback="4/4") + clef = score_field(document, metadata, "clef", fallback="treble") + part = score_field(document, metadata, "part", "name", fallback="Vocal") + role = score_field(document, metadata, "role", fallback="vocal") + + output_metadata = sanitize_json(metadata) + if not isinstance(output_metadata, dict): + output_metadata = {} + output_metadata.update({ + "title": title, + "composer": composer, + "key_signature": key_signature, + "time_signature": time_signature, + }) + for key, value in document.items(): + if key in {"title", "composer", "key", "key_signature", "time", "time_signature", "clef", "part", "name", "role", "measures", "metadata"}: + continue + output_metadata[key] = sanitize_json(value) + + measures: list[dict[str, Any]] = [] + for index, value in enumerate(measures_value): + text, overrides = compact_measure_text(value, index) + measure_time = overrides.get("time_signature", time_signature) + measure_key = overrides.get("key_signature", key_signature) + parser = CompactMeasureParser(text, measure_number=index + 1, time_signature=measure_time) + notes, tuplets = parser.parse() + measures.append({ + "id": f"measure-{index + 1}", + "section_label": overrides.get("section_label", ""), + "time_signature": measure_time, + "key_signature": measure_key, + "voices": [{ + "id": f"voice-{index + 1}", + "notes": notes, + "tuplets": tuplets, + }], + }) + + return { + "version": int(document.get("version", 1)) if isinstance(document.get("version", 1), int) else 1, + "metadata": output_metadata, + "staves": [{ + "id": "staff-source", + "name": part, + "clef": clef, + "role": role, + "measures": measures, + }], + } + + def source_staff(document: dict[str, Any]) -> dict[str, Any]: staves = document.get("staves") if not isinstance(staves, list) or not staves: diff --git a/include/vamp_zero/ui.hpp b/include/vamp_zero/ui.hpp index 7348f27..069f41e 100644 --- a/include/vamp_zero/ui.hpp +++ b/include/vamp_zero/ui.hpp @@ -1,16 +1,21 @@ #pragma once #include +#include #include +#include #include #include +#include #include #include +#include #include #include +#include #include #include @@ -25,17 +30,31 @@ struct ui_state { class zero_ui { public: - zero_ui(boost::asio::any_io_executor executor, luma::application_config config); + using command_handler = std::function; + + zero_ui(boost::asio::any_io_executor executor, luma::application_config config, command_handler command_handler = {}); auto run() -> std::expected; auto stop() -> void; auto publish(vamp_zero::ui_state state) -> std::expected; auto snapshot() const -> vamp_zero::ui_state; private: + auto handle_touch(luma::touch_event const& event) -> std::bool_t; + auto send_touch_command(vamp_protocol::page_command command) -> void; + auto arm_pending_tap() -> void; + auto cancel_pending_tap_locked() -> void; auto make_root(vamp_zero::ui_state const& state) -> std::expected, modulus::error>; boost::asio::any_io_executor executor_; + boost::asio::steady_timer tap_timer_; luma::application application_; + command_handler command_handler_{}; mutable std::mutex mutex_{}; vamp_zero::ui_state state_{}; + mutable std::mutex touch_mutex_{}; + std::optional active_touch_id_{}; + std::chrono::steady_clock::time_point touch_down_time_{}; + std::chrono::steady_clock::time_point last_tap_time_{}; + std::bool_t hold_sent_{}; + std::bool_t pending_tap_{}; }; auto make_ui_state(vamp_zero::config const& config, vamp_zero::app_state app) -> vamp_zero::ui_state; diff --git a/src/vamp_pico/main.cpp b/src/vamp_pico/main.cpp index bd82656..812b373 100644 --- a/src/vamp_pico/main.cpp +++ b/src/vamp_pico/main.cpp @@ -38,6 +38,10 @@ auto render(vamp_pico::display& display, link_status status, vamp_pico::remote_d display.show_remote(color_for(status), remote.title, remote.part, remote.page, remote.pages, remote.active); } +auto clear_remote(vamp_pico::remote_display_state& remote) -> void { + remote = vamp_pico::remote_display_state{}; +} + auto connect_wifi(vamp_pico::display& display) -> std::bool_t { auto empty = vamp_pico::remote_display_state{}; render(display, link_status::CONNECTING, empty); @@ -58,6 +62,7 @@ auto connect_wifi(vamp_pico::display& display) -> std::bool_t { auto send_command(vamp_protocol::page_command command, link_status& status, vamp_pico::remote_display_state& remote, vamp_pico::display& display) -> void { if (!vamp_pico::post_remote_command(command, remote)) { status = link_status::RETRYING; + clear_remote(remote); render(display, status, remote); return; } @@ -136,6 +141,7 @@ auto main() -> int { status = link_status::CONNECTED; } else { status = link_status::RETRYING; + clear_remote(remote); } render(display, status, remote); } diff --git a/src/vamp_zero/app.cpp b/src/vamp_zero/app.cpp index 288b84b..f1fa992 100644 --- a/src/vamp_zero/app.cpp +++ b/src/vamp_zero/app.cpp @@ -168,7 +168,9 @@ auto run_display(vamp_zero::config const& config, vamp_zero::control_state& stat if (!endpoint) { return std::unexpected(endpoint.error()); } - auto ui = vamp_zero::zero_ui{io.get_executor(), config.display}; + auto ui = vamp_zero::zero_ui{io.get_executor(), config.display, [&state](vamp_protocol::page_command command) { + static_cast(state.apply(command)); + }}; state.set_observer([&ui, &config](vamp_zero::app_state app) { auto status = ui.publish(vamp_zero::make_ui_state(config, std::move(app))); if (!status) { diff --git a/src/vamp_zero/score.cpp b/src/vamp_zero/score.cpp index 6e977a1..38a8d2b 100644 --- a/src/vamp_zero/score.cpp +++ b/src/vamp_zero/score.cpp @@ -86,10 +86,15 @@ constexpr auto tie_thickness_spaces = std::float32_t{0.13F}; constexpr auto normal_stem_length_spaces = std::float32_t{3.50F}; constexpr auto preamble_vertical_correction_spaces = std::float32_t{0.50F}; constexpr auto stem_head_horizontal_overlap_spaces = std::float32_t{0.06F}; -constexpr auto stem_head_vertical_overlap_spaces = std::float32_t{0.20F}; -constexpr auto measure_right_guard_spaces = std::float32_t{1.50F}; +constexpr auto stem_head_vertical_overlap_spaces = std::float32_t{0.38F}; +constexpr auto measure_right_guard_spaces = std::float32_t{2.05F}; constexpr auto chord_above_staff_spaces = std::float32_t{3.65F}; constexpr auto chord_clearance_spaces = std::float32_t{0.45F}; +constexpr auto lyric_below_staff_spaces = std::float32_t{6.15F}; +constexpr auto lyric_clearance_spaces = std::float32_t{0.75F}; +constexpr auto tie_endpoint_gap_spaces = std::float32_t{0.28F}; +constexpr auto tie_note_offset_spaces = std::float32_t{0.50F}; +constexpr auto tie_arch_spaces = std::float32_t{0.72F}; auto to_raylib(luma::color color) -> Color { return { @@ -832,6 +837,26 @@ struct beam_group_layout { std::float32_t beam_y{}; }; +struct laid_out_tie_head { + vamp_zero::score_pitch pitch{}; + luma::rect bounds{}; + std::float32_t center_y{}; +}; + +struct laid_out_note_ref { + std::size_t system_index{}; + std::size_t measure_index{}; + std::size_t note_index{}; + luma::rect notation_bounds{}; + std::bool_t stem_up{true}; + std::vector heads{}; +}; + +struct laid_out_measure_ref { + std::size_t measure_index{}; + luma::rect notation_bounds{}; +}; + auto layout_staff( vamp_zero::score_page_layout& layout, std::float32_t x, @@ -1679,8 +1704,6 @@ auto layout_note_symbols( note_layout const& note, std::float32_t staff_top, std::float32_t spacing, - std::float32_t next_x, - std::float32_t measure_right_x, std::size_t system_index, std::size_t measure_index ) -> void { @@ -1714,25 +1737,6 @@ auto layout_note_symbols( note.note_index); } } - if (note.source != nullptr && note.source->tie_to_next) { - auto tie_y = note.low_y + spacing * 0.62F; - auto tie_start_x = note.visual_right - spacing * 0.45F; - auto tie_end_x = std::min(next_x - spacing * 0.45F, measure_right_x - spacing * 0.30F); - if (tie_end_x <= tie_start_x + spacing * 0.50F) { - return; - } - add_curve( - layout, - vamp_zero::score_layout_item_role::TIE, - {.x = tie_start_x, .y = tie_y}, - {.x = (tie_start_x + tie_end_x) * 0.5F, .y = tie_y + spacing * 0.78F}, - {.x = tie_end_x, .y = tie_y}, - at_least_one_pixel(scaled(tie_thickness_spaces, spacing)), - score_ink(), - system_index, - measure_index, - note.note_index); - } } auto note_x(std::float32_t start_x, std::float32_t width, std::int64_t total_ticks, std::int64_t elapsed_ticks, std::int64_t duration_ticks) -> std::float32_t { @@ -1837,6 +1841,23 @@ auto measure_notation_top_y( return top_y; } +auto measure_notation_bottom_y(std::vector const& notes, std::vector const& beam_groups, std::float32_t staff_top, std::float32_t spacing) -> std::float32_t { + auto bottom_y = staff_top + spacing * 4.0F; + for (auto const& note : notes) { + if (!note.hidden) { + bottom_y = std::max(bottom_y, note.visual_bottom); + } + } + for (auto const& group : beam_groups) { + bottom_y = std::max(bottom_y, group.beam_y + scaled(beam_thickness_spaces, spacing) * 0.5F); + } + return bottom_y; +} + +auto lyric_y(std::float32_t staff_top, std::float32_t spacing, std::float32_t notation_bottom_y) -> std::float32_t { + return std::max(staff_top + scaled(lyric_below_staff_spaces, spacing), notation_bottom_y + scaled(lyric_clearance_spaces, spacing)); +} + auto chord_symbol_y(std::float32_t staff_top, std::float32_t spacing, std::float32_t notation_top_y) -> std::float32_t { auto y = staff_top - scaled(chord_above_staff_spaces, spacing); auto const bottom_limit = notation_top_y - scaled(chord_clearance_spaces, spacing); @@ -1846,6 +1867,189 @@ auto chord_symbol_y(std::float32_t staff_top, std::float32_t spacing, std::float return y; } +auto same_pitch(vamp_zero::score_pitch const& left, vamp_zero::score_pitch const& right) -> std::bool_t { + return normalized_letter(left.letter) == normalized_letter(right.letter) + && left.octave == right.octave + && pitch_alteration(left.accidental) == pitch_alteration(right.accidental); +} + +auto make_laid_out_note_ref(note_layout const& note, luma::rect notation_bounds, std::size_t system_index, std::size_t measure_index) -> laid_out_note_ref { + auto ref = laid_out_note_ref{ + .system_index = system_index, + .measure_index = measure_index, + .note_index = note.note_index, + .notation_bounds = notation_bounds, + .stem_up = note.stem_up, + }; + if (note.source == nullptr || note.hidden || note.rest) { + return ref; + } + ref.heads.reserve(note.heads.size()); + for (auto const& head : note.heads) { + if (head.source_pitch_index >= note.source->pitches.size()) { + continue; + } + ref.heads.push_back({ + .pitch = note.source->pitches[head.source_pitch_index], + .bounds = head.bounds, + .center_y = head.center_y, + }); + } + return ref; +} + +auto find_note_ref(std::vector const& refs, std::size_t measure_index, std::size_t note_index) -> laid_out_note_ref const* { + auto found = std::find_if(refs.begin(), refs.end(), [&](auto const& ref) { + return ref.measure_index == measure_index && ref.note_index == note_index; + }); + return found == refs.end() ? nullptr : &*found; +} + +auto find_measure_ref(std::vector const& refs, std::size_t measure_index) -> laid_out_measure_ref const* { + auto found = std::find_if(refs.begin(), refs.end(), [&](auto const& ref) { + return ref.measure_index == measure_index; + }); + return found == refs.end() ? nullptr : &*found; +} + +auto find_tie_head(laid_out_note_ref const& ref, vamp_zero::score_pitch const& pitch) -> laid_out_tie_head const* { + auto found = std::find_if(ref.heads.begin(), ref.heads.end(), [&](auto const& head) { + return same_pitch(head.pitch, pitch); + }); + return found == ref.heads.end() ? nullptr : &*found; +} + +struct tied_note_destination { + std::size_t measure_index{}; + std::size_t note_index{}; +}; + +auto find_tied_destination(vamp_zero::score_document const& score, std::size_t measure_index, std::size_t note_index, vamp_zero::score_pitch const& pitch) -> std::optional { + for (auto current_measure = measure_index; current_measure < score.measures.size(); ++current_measure) { + auto const& measure = score.measures[current_measure]; + auto first_note = current_measure == measure_index ? note_index + 1U : 0U; + for (auto current_note = first_note; current_note < measure.notes.size(); ++current_note) { + auto const& note = measure.notes[current_note]; + if (note.hidden || note.kind == "rest") { + continue; + } + auto matching_pitch = std::find_if(note.pitches.begin(), note.pitches.end(), [&](auto const& candidate) { + return same_pitch(candidate, pitch); + }); + if (matching_pitch == note.pitches.end()) { + return std::nullopt; + } + if (note.tie_from_previous) { + return tied_note_destination{.measure_index = current_measure, .note_index = current_note}; + } + return std::nullopt; + } + } + return std::nullopt; +} + +auto add_tie_curve( + vamp_zero::score_page_layout& layout, + luma::point from, + luma::point to, + std::bool_t below_note, + std::float32_t spacing, + std::size_t system_index, + std::size_t measure_index, + std::size_t note_index +) -> void { + if (to.x <= from.x + spacing * 0.40F) { + return; + } + auto direction = below_note ? std::float32_t{1.0F} : std::float32_t{-1.0F}; + auto control = luma::point{ + .x = (from.x + to.x) * 0.5F, + .y = (from.y + to.y) * 0.5F + direction * scaled(tie_arch_spaces, spacing), + }; + add_curve( + layout, + vamp_zero::score_layout_item_role::TIE, + from, + control, + to, + at_least_one_pixel(scaled(tie_thickness_spaces, spacing)), + score_ink(), + system_index, + measure_index, + note_index); +} + +auto layout_page_ties( + vamp_zero::score_page_layout& layout, + vamp_zero::score_document const& score, + std::vector const& note_refs, + std::vector const& measure_refs, + std::float32_t spacing +) -> void { + for (auto measure_index = std::size_t{}; measure_index < score.measures.size(); ++measure_index) { + auto const& measure = score.measures[measure_index]; + for (auto note_index = std::size_t{}; note_index < measure.notes.size(); ++note_index) { + auto const& note = measure.notes[note_index]; + if (!note.tie_to_next || note.hidden || note.kind == "rest") { + continue; + } + auto const* start_ref = find_note_ref(note_refs, measure_index, note_index); + for (auto const& pitch : note.pitches) { + auto destination = find_tied_destination(score, measure_index, note_index, pitch); + auto const* destination_ref = destination ? find_note_ref(note_refs, destination->measure_index, destination->note_index) : nullptr; + auto const* start_head = start_ref == nullptr ? nullptr : find_tie_head(*start_ref, pitch); + auto const* destination_head = destination_ref == nullptr ? nullptr : find_tie_head(*destination_ref, pitch); + + if (start_ref != nullptr && start_head != nullptr && destination_ref != nullptr && destination_head != nullptr && start_ref->system_index == destination_ref->system_index) { + auto below = start_ref->stem_up; + auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F); + add_tie_curve( + layout, + {.x = start_head->bounds.x + start_head->bounds.width + scaled(tie_endpoint_gap_spaces, spacing), .y = start_head->center_y + y_offset}, + {.x = destination_head->bounds.x - scaled(tie_endpoint_gap_spaces, spacing), .y = destination_head->center_y + y_offset}, + below, + spacing, + start_ref->system_index, + start_ref->measure_index, + start_ref->note_index); + continue; + } + + if (start_ref != nullptr && start_head != nullptr) { + auto below = start_ref->stem_up; + auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F); + add_tie_curve( + layout, + {.x = start_head->bounds.x + start_head->bounds.width + scaled(tie_endpoint_gap_spaces, spacing), .y = start_head->center_y + y_offset}, + {.x = start_ref->notation_bounds.x + start_ref->notation_bounds.width, .y = start_head->center_y + y_offset}, + below, + spacing, + start_ref->system_index, + start_ref->measure_index, + start_ref->note_index); + } + if (destination && destination_ref != nullptr && destination_head != nullptr) { + auto const* measure_ref = find_measure_ref(measure_refs, destination->measure_index); + if (measure_ref == nullptr) { + continue; + } + auto below = destination_ref->stem_up; + auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F); + add_tie_curve( + layout, + {.x = measure_ref->notation_bounds.x, .y = destination_head->center_y + y_offset}, + {.x = destination_head->bounds.x - scaled(tie_endpoint_gap_spaces, spacing), .y = destination_head->center_y + y_offset}, + below, + spacing, + destination_ref->system_index, + destination_ref->measure_index, + destination_ref->note_index); + } + } + } + } +} + auto layout_measure( vamp_zero::score_page_layout& layout, vamp_zero::score_document const& score, @@ -1855,7 +2059,9 @@ auto layout_measure( std::size_t system_index, std::size_t measure_index, std::float32_t staff_top, - std::float32_t spacing + std::float32_t spacing, + std::vector& note_refs, + std::vector& measure_refs ) -> void { add_text(layout, vamp_zero::score_layout_item_role::MEASURE_NUMBER, bounds.x + spacing * 0.20F, bounds.y + spacing * 0.05F, std::to_string(measure_number), 1, score_muted_ink(), spacing * 4.0F, system_index, measure_index); @@ -1863,6 +2069,10 @@ auto layout_measure( auto notation_right_x = bounds.x + bounds.width - scaled(measure_right_guard_spaces, spacing); auto notation_width = std::max(spacing * 3.0F, notation_right_x - notation_x); auto notation_bounds = luma::rect{.x = notation_x, .y = bounds.y, .width = notation_width, .height = bounds.height}; + measure_refs.push_back({ + .measure_index = measure_index, + .notation_bounds = notation_bounds, + }); auto total_ticks = measure_ticks(measure); auto elapsed = std::int64_t{}; auto accidentals = make_accidental_state(effective_key_signature(score, measure)); @@ -1889,7 +2099,12 @@ auto layout_measure( } auto const notation_top_y = measure_notation_top_y(measure, notes, beam_groups, staff_top, spacing); + auto const notation_bottom_y = measure_notation_bottom_y(notes, beam_groups, staff_top, spacing); auto const chord_y = chord_symbol_y(staff_top, spacing, notation_top_y); + auto const lyrics_y = lyric_y(staff_top, spacing, notation_bottom_y); + for (auto const& note : notes) { + note_refs.push_back(make_laid_out_note_ref(note, notation_bounds, system_index, measure_index)); + } if (!measure.section_label.empty()) { auto section_y = std::min(staff_top - spacing * 4.70F, chord_y - spacing * 0.65F); add_text(layout, vamp_zero::score_layout_item_role::SECTION_LABEL, bounds.x + spacing * 2.10F, section_y, measure.section_label, 2, score_accent_ink(), bounds.width - spacing * 2.6F, system_index, measure_index); @@ -1948,14 +2163,13 @@ auto layout_measure( add_flag(layout, note, spacing, system_index, measure_index); } for (auto index = std::size_t{}; index < notes.size(); ++index) { - auto next_x = index + 1U < notes.size() ? notes[index + 1U].x : notes[index].x + spacing * 3.0F; - layout_note_symbols(layout, notes[index], staff_top, spacing, next_x, notation_bounds.x + notation_bounds.width, system_index, measure_index); + layout_note_symbols(layout, notes[index], staff_top, spacing, system_index, measure_index); if (notes[index].source != nullptr && !notes[index].hidden && !notes[index].source->lyric.empty()) { add_text( layout, vamp_zero::score_layout_item_role::LYRIC, notes[index].x - spacing, - staff_top + spacing * 5.35F, + lyrics_y, notes[index].source->lyric, 1, score_muted_ink(), @@ -2027,6 +2241,8 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c auto const content_width = std::max(spacing * 18.0F, bounds.width - margin * 2.0F); auto const system_height = (bounds.height - 108.0F) / static_cast(vamp_zero::score_systems_per_page); auto const first_measure = page_index * vamp_zero::score_measures_per_page; + auto note_refs = std::vector{}; + auto measure_refs = std::vector{}; for (auto system = std::size_t{}; system < vamp_zero::score_systems_per_page; ++system) { auto const system_y = content_y + static_cast(system) * system_height; @@ -2052,7 +2268,9 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c system, measure_index, staff_top, - spacing); + spacing, + note_refs, + measure_refs); } auto const bar_x = x + measure_width; @@ -2069,6 +2287,7 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c } } + layout_page_ties(layout, score, note_refs, measure_refs, spacing); add_stroke_rect(layout, vamp_zero::score_layout_item_role::BORDER, bounds, 1.1F, score_border()); return layout; } diff --git a/src/vamp_zero/ui.cpp b/src/vamp_zero/ui.cpp index 8b5c303..3397499 100644 --- a/src/vamp_zero/ui.cpp +++ b/src/vamp_zero/ui.cpp @@ -11,6 +11,7 @@ #include #include +#include #include @@ -22,6 +23,11 @@ namespace { +constexpr auto touch_hold_duration = std::chrono::milliseconds{700}; +constexpr auto touch_double_tap_duration = std::chrono::milliseconds{220}; + +using touch_handler = std::function; + auto bind_port(vamp_zero::config const& config) -> std::uint16_t { auto const& bind = config.control.endpoint.bind; auto colon = bind.rfind(':'); @@ -404,7 +410,7 @@ auto draw_score(luma::draw_context& context, luma::rect bounds, vamp_zero::app_s class vamp_root_control final : public luma::control { public: - explicit vamp_root_control(vamp_zero::ui_state state) : state_{std::move(state)} {} + explicit vamp_root_control(vamp_zero::ui_state state, touch_handler on_touch) : state_{std::move(state)}, on_touch_{std::move(on_touch)} {} auto measure(luma::draw_context&, luma::theme const&, luma::constraints limits) -> luma::size override { return luma::clamp({.width = 1024.0F, .height = 600.0F}, limits); } @@ -438,13 +444,117 @@ public: draw_main_menu(context, content, state_.app); } } + auto handle_touch(luma::touch_event const& event) -> std::bool_t override { + return on_touch_ ? on_touch_(event) : false; + } private: vamp_zero::ui_state state_{}; + touch_handler on_touch_{}; }; } -vamp_zero::zero_ui::zero_ui(boost::asio::any_io_executor executor, luma::application_config config) : executor_{std::move(executor)}, application_{std::move(config)} {} +vamp_zero::zero_ui::zero_ui(boost::asio::any_io_executor executor, luma::application_config config, vamp_zero::zero_ui::command_handler command_handler) + : executor_{std::move(executor)}, tap_timer_{executor_}, application_{std::move(config)}, command_handler_{std::move(command_handler)} {} + +auto vamp_zero::zero_ui::send_touch_command(vamp_protocol::page_command command) -> void { + if (!command_handler_) { + return; + } + boost::asio::post(executor_, [handler = command_handler_, command] { + handler(command); + }); +} + +auto vamp_zero::zero_ui::cancel_pending_tap_locked() -> void { + pending_tap_ = false; +} + +auto vamp_zero::zero_ui::arm_pending_tap() -> void { + tap_timer_.expires_after(touch_double_tap_duration); + tap_timer_.async_wait([this](boost::system::error_code const& error) { + if (error) { + return; + } + auto send_next = false; + { + auto lock = std::scoped_lock{touch_mutex_}; + send_next = pending_tap_; + pending_tap_ = false; + } + if (send_next) { + send_touch_command(vamp_protocol::page_command::NEXT); + } + }); +} + +auto vamp_zero::zero_ui::handle_touch(luma::touch_event const& event) -> std::bool_t { + if (!command_handler_ || event.points.empty()) { + return false; + } + + auto consumed = false; + auto commands = std::vector{}; + auto arm_tap = false; + { + auto lock = std::scoped_lock{touch_mutex_}; + for (auto const& point : event.points) { + if (point.phase == luma::touch_phase::DOWN && !active_touch_id_) { + active_touch_id_ = point.id; + touch_down_time_ = event.time; + hold_sent_ = false; + tap_timer_.cancel(); + consumed = true; + continue; + } + if (!active_touch_id_ || point.id != *active_touch_id_) { + continue; + } + consumed = true; + if (point.phase == luma::touch_phase::CANCEL) { + active_touch_id_.reset(); + hold_sent_ = false; + cancel_pending_tap_locked(); + tap_timer_.cancel(); + continue; + } + if (point.phase == luma::touch_phase::MOVE) { + if (!hold_sent_ && event.time - touch_down_time_ >= touch_hold_duration) { + hold_sent_ = true; + cancel_pending_tap_locked(); + tap_timer_.cancel(); + commands.push_back(vamp_protocol::page_command::SELECT); + } + continue; + } + if (point.phase == luma::touch_phase::UP) { + active_touch_id_.reset(); + if (!hold_sent_ && event.time - touch_down_time_ >= touch_hold_duration) { + commands.push_back(vamp_protocol::page_command::SELECT); + } else if (!hold_sent_) { + if (pending_tap_ && event.time - last_tap_time_ <= touch_double_tap_duration) { + cancel_pending_tap_locked(); + tap_timer_.cancel(); + commands.push_back(vamp_protocol::page_command::PREVIOUS); + } else { + pending_tap_ = true; + last_tap_time_ = event.time; + arm_tap = true; + } + } + hold_sent_ = false; + } + } + } + + for (auto command : commands) { + send_touch_command(command); + } + if (arm_tap) { + arm_pending_tap(); + } + return consumed; +} auto vamp_zero::zero_ui::run() -> std::expected { auto state = snapshot(); @@ -459,6 +569,7 @@ auto vamp_zero::zero_ui::run() -> std::expected { } auto vamp_zero::zero_ui::stop() -> void { + tap_timer_.cancel(); application_.request_stop(); } @@ -482,7 +593,9 @@ auto vamp_zero::zero_ui::snapshot() const -> vamp_zero::ui_state { } auto vamp_zero::zero_ui::make_root(vamp_zero::ui_state const& state) -> std::expected, modulus::error> { - return std::make_unique(state); + return std::make_unique(state, [this](luma::touch_event const& event) { + return handle_touch(event); + }); } auto vamp_zero::make_ui_state(vamp_zero::config const& config, vamp_zero::app_state app) -> vamp_zero::ui_state { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f1eed4c..306cbc0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,10 +10,16 @@ target_link_libraries(product-vamp-test PRIVATE target_compile_features(product-vamp-test PRIVATE cxx_std_23) add_test(NAME product-vamp COMMAND product-vamp-test) set_tests_properties(product-vamp PROPERTIES TIMEOUT 60) +add_test( + NAME product-vamp-generator + COMMAND ${Python3_EXECUTABLE} -m unittest tests.test_score_generator + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} +) +set_tests_properties(product-vamp-generator PROPERTIES TIMEOUT 60) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --test-dir ${CMAKE_BINARY_DIR} - -R ^product-vamp$ + -R ^product-vamp --output-on-failure --timeout 60 USES_TERMINAL diff --git a/tests/test.cpp b/tests/test.cpp index 195ef42..918293c 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -158,6 +158,10 @@ auto right_edge(vamp_zero::score_layout_item const& item) -> std::float32_t { return item.bounds.x + item.bounds.width; } +auto bottom_edge(vamp_zero::score_layout_item const& item) -> std::float32_t { + return item.bounds.y + item.bounds.height; +} + auto near(std::float32_t left, std::float32_t right) -> std::bool_t { return std::abs(left - right) < 0.02F; } @@ -448,6 +452,99 @@ BOOST_AUTO_TEST_CASE(score_layout_keeps_dense_notation_inside_measure_guard) { } } +BOOST_AUTO_TEST_CASE(score_layout_places_lyrics_below_low_vocal_notation) { + auto low = note({pitch('c', "n", 3)}); + low.lyric = "low"; + auto score = score_with_notes({std::move(low)}); + auto layout = vamp_zero::layout_score_page({.x = 0.0F, .y = 0.0F, .width = 976.0F, .height = 484.0F}, score, 0U); + + auto lyrics = layout_items(layout, vamp_zero::score_layout_item_role::LYRIC, 0U, 0U, 0U); + BOOST_REQUIRE_EQUAL(lyrics.size(), 1U); + + auto notation_bottom = std::numeric_limits::lowest(); + auto roles = { + vamp_zero::score_layout_item_role::NOTEHEAD, + vamp_zero::score_layout_item_role::ACCIDENTAL, + vamp_zero::score_layout_item_role::STEM, + vamp_zero::score_layout_item_role::FLAG, + vamp_zero::score_layout_item_role::DOT, + vamp_zero::score_layout_item_role::STAFF_LINE, + }; + for (auto role : roles) { + for (auto const* item : layout_items(layout, role, 0U, 0U, 0U)) { + notation_bottom = std::max(notation_bottom, bottom_edge(*item)); + } + } + + BOOST_TEST(lyrics.front()->bounds.y >= notation_bottom + layout.staff_spacing * 0.70F); +} + +BOOST_AUTO_TEST_CASE(score_layout_ties_to_the_actual_next_measure_notehead) { + auto tied = note_with_ticks({pitch('b', "b")}, 16384); + tied.tie_to_next = true; + auto target = note_with_ticks({pitch('b', "b")}, 4096); + target.tie_from_previous = true; + auto score = score_with_measure({ + .time_signature = "4/4", + .key_signature = "F", + .notes = {std::move(tied)}, + }, "F"); + score.measures.push_back({ + .time_signature = "4/4", + .key_signature = "F", + .notes = {std::move(target)}, + }); + + auto layout = vamp_zero::layout_score_page({.x = 0.0F, .y = 0.0F, .width = 976.0F, .height = 484.0F}, score, 0U); + + auto ties = layout_items(layout, vamp_zero::score_layout_item_role::TIE, 0U, 0U, 0U); + auto start_heads = layout_items(layout, vamp_zero::score_layout_item_role::NOTEHEAD, 0U, 0U, 0U); + auto target_heads = layout_items(layout, vamp_zero::score_layout_item_role::NOTEHEAD, 0U, 1U, 0U); + BOOST_REQUIRE_EQUAL(ties.size(), 1U); + BOOST_REQUIRE_EQUAL(start_heads.size(), 1U); + BOOST_REQUIRE_EQUAL(target_heads.size(), 1U); + + BOOST_TEST(ties.front()->from.x > right_edge(*start_heads.front())); + BOOST_TEST(ties.front()->to.x < target_heads.front()->bounds.x); + BOOST_TEST(ties.front()->to.x > right_edge(*start_heads.front())); +} + +BOOST_AUTO_TEST_CASE(score_layout_draws_tie_continuations_across_system_breaks) { + auto measures = std::vector{}; + for (auto index = std::size_t{}; index < 5U; ++index) { + auto value = note_with_ticks({pitch('a', "n")}, 16384); + if (index == 3U) { + value.tie_to_next = true; + } + if (index == 4U) { + value.tie_from_previous = true; + } + measures.push_back({ + .time_signature = "4/4", + .key_signature = "C", + .notes = {std::move(value)}, + }); + } + auto score = vamp_zero::score_document{ + .title = "Break Tie", + .composer = "Composer", + .key_signature = "C", + .time_signature = "4/4", + .clef = "treble", + .part = "Lead", + .measures = std::move(measures), + }; + + auto layout = vamp_zero::layout_score_page({.x = 0.0F, .y = 0.0F, .width = 976.0F, .height = 484.0F}, score, 0U); + auto outgoing = layout_items(layout, vamp_zero::score_layout_item_role::TIE, 0U, 3U, 0U); + auto incoming = layout_items(layout, vamp_zero::score_layout_item_role::TIE, 1U, 4U, 0U); + + BOOST_REQUIRE_EQUAL(outgoing.size(), 1U); + BOOST_REQUIRE_EQUAL(incoming.size(), 1U); + BOOST_TEST(outgoing.front()->to.x > outgoing.front()->from.x); + BOOST_TEST(incoming.front()->to.x > incoming.front()->from.x); +} + BOOST_AUTO_TEST_CASE(score_layout_raises_chord_symbols_above_high_notation) { auto high = note({pitch('c', "n", 6)}); high.chord_symbol = "Cmaj7"; diff --git a/tests/test_score_generator.py b/tests/test_score_generator.py new file mode 100644 index 0000000..707b084 --- /dev/null +++ b/tests/test_score_generator.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +import unittest + +from generator.score import normalize_score_document + + +class CompactScoreJsonTests(unittest.TestCase): + def test_compact_json_normalizes_notation_tokens(self) -> None: + document = normalize_score_document({ + "title": "Compact Tune", + "composer": "Composer", + "key": "F", + "time": "4/4", + "clef": "treble", + "part": "Vocal", + "role": "vocal", + "measures": [ + "[G-7] Bb4:1~ \"leaves\"", + "[C7] ~Bb4:4 C4:4 \"drift\" D4:4 \"by\" E4:4 \"my\"", + ], + }) + + staff = document["staves"][0] + first_note = staff["measures"][0]["voices"][0]["notes"][0] + second_measure_first_note = staff["measures"][1]["voices"][0]["notes"][0] + + self.assertEqual(document["metadata"]["key_signature"], "F") + self.assertEqual(staff["name"], "Vocal") + self.assertEqual(first_note["duration_ticks"], 16384) + self.assertEqual(first_note["chord_symbol"], "G-7") + self.assertEqual(first_note["lyric"], "leaves") + self.assertTrue(first_note["tie_to_next"]) + self.assertTrue(second_measure_first_note["tie_from_previous"]) + + def test_compact_json_normalizes_pitch_groups_and_tuplets(self) -> None: + document = normalize_score_document({ + "title": "Practice", + "key": "C", + "time": "4/4", + "measures": [ + "[G7] (G4 B4 F5):4 r:4", + "{5:2 C4:4 D4:4 E4:4 F4:4 G4:4}", + ], + }) + + first_measure_notes = document["staves"][0]["measures"][0]["voices"][0]["notes"] + second_voice = document["staves"][0]["measures"][1]["voices"][0] + tuplet_notes = second_voice["notes"] + + self.assertEqual(len(first_measure_notes[0]["pitches"]), 3) + self.assertEqual(first_measure_notes[1]["kind"], "rest") + self.assertEqual(second_voice["tuplets"][0]["note_count"], 5) + self.assertEqual(second_voice["tuplets"][0]["space_count"], 2) + self.assertEqual(sum(note["duration_ticks"] for note in tuplet_notes), 8192) + self.assertTrue(all(note["display_ticks"] == 4096 for note in tuplet_notes)) + + def test_compact_json_rejects_overfull_measure(self) -> None: + with self.assertRaises(ValueError): + normalize_score_document({ + "title": "Bad", + "time": "4/4", + "measures": ["C4:1 D4:4"], + }) + + +if __name__ == "__main__": + unittest.main()