diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 713e321..511408d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: - name: Test native release run: python3 modulus.py test --target x86_64_gcc12_release - name: Test asset generation - run: python3 -m unittest tests.test_score_generator + run: python3 -m unittest discover -s tests -p 'test_*.py' - name: Fetch Pico SDK if: ${{ gitea.event_name == 'workflow_dispatch' || (gitea.event_name == 'push' && gitea.ref == 'refs/heads/master') }} env: diff --git a/README.md b/README.md index 391aa9a..973030b 100644 --- a/README.md +++ b/README.md @@ -70,34 +70,18 @@ Artifacts are published to the khodo generic package registry. ## Score Assets -Source score JSON lives in `data/`. Generated score JSON is not committed. To inspect generated assets without building the full package, run: +Uncompressed MusicXML 3.1/4.0 scores exported from MuseScore live in `data/`. Generated score JSON is not committed. To inspect generated assets without building the full package, run: ```sh python3 -m generator --data-dir data --output-dir /tmp/product-vamp-generated --static-assets-dir package/assets --clean --check ``` The `--check` path compares generation invariants against `data/expected/performance_summary.json`. +The pinned build-only dependencies are in `tools/requirements.txt`. Configure/build automatically refresh the project build image when this file changes; direct builds must install it in their Python environment. -Authored score files use compact JSON. Metadata stays as normal JSON fields, while each measure is a readable notation string. The optional `style` field must be a supported built-in player style; compact aliases `Med.`, `Med. Swing`, and `Medium` normalize to `Medium Swing`. +The build-time pipeline is MuseScore -> `.musicxml` -> importer -> arranger/transposer -> schema-v2 JSON. The Pi reads only JSON. Performance sources produce acoustic, electric, and vocal parts; melody exercises produce twelve tonic variants, and chord exercises keep their authored key. -```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, `[(Chord)]` marks an optional turnaround chord that is used between generated performance iterations but omitted from the final ending, 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. A measure may also be an object with `notes` plus MusicXML-style metadata fields such as `attributes`, `directions`, `harmony`, or `barlines`. +See [Score Generation](TECHNICAL_GUIDE.md#score-generation) for supported MusicXML notation, Vamp properties, schema details, and diagnostics. There is no compact-JSON source format or legacy runtime adapter. Unsupported notation fails generation before existing assets are replaced. Bundled fonts are OTF files in `package/assets/fonts/petaluma`: `Petaluma.otf` supplies music symbols and `PetalumaScript.otf` supplies text. Petaluma is from Steinberg under the SIL Open Font License. Runtime packages include these two fonts and `OFL.txt`; the upstream metadata is kept only as a source reference. diff --git a/TECHNICAL_GUIDE.md b/TECHNICAL_GUIDE.md index 7eb699e..4a772b6 100644 --- a/TECHNICAL_GUIDE.md +++ b/TECHNICAL_GUIDE.md @@ -9,7 +9,7 @@ Product Vamp follows the Modulus product layout used by the khodo repos: - `include/vamp_protocol` and `src/vamp_protocol`: shared route and page-command contracts used by both devices. - `cmake/pico.cmake` and `cmake/zero.cmake`: device-specific build definitions selected by the root `CMakeLists.txt`. - `include/vamp_style.hpp`, `include/vamp_style/font.hpp`, and `src/vamp_style.cpp`: shared color palette and Petaluma font interfaces. Bitmap font data is generated in the build directory. -- `data`: authored source score JSON for performance songs and practice exercises. +- `data`: authored MusicXML for performance songs and practice exercises, plus the generator regression summary. - `generator`: build-time Python preprocessing for performance arrangements and practice variants. - `package`: generic bundle inputs, scripts, config, static font assets, and device install files. @@ -37,7 +37,59 @@ 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 keeps notation metadata while removing generator-only optional chord markers; 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; 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, optional turnaround markers as `[(Chord)]`, quoted lyrics, `~` tie starts/stops, and tuplets as `{N:M ...}`. Measure objects may also carry notation metadata such as attributes, directions, harmony, and barlines so the data shape can grow toward MusicXML-style concepts without changing the compact note syntax. 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. +The source pipeline is MuseScore -> uncompressed `.musicxml` -> `generator/musicxml.py` -> arranger/transposer -> schema-v2 JSON. Export MusicXML 3.1 or 4.0, `score-partwise`, with one part, one staff, and one rhythmic voice. Chord tones share an onset but retain their own spelling, written duration, and tie links. Separate voices/staves must not be flattened into the source voice. The schema keeps staff/voice ownership explicit for a future polyphonic renderer. + +### MusicXML Contract + +| Area | Accepted notation | +| --- | --- | +| Pitch | Twelve-tone letter/alteration/octave spelling, including double/triple accidentals and courtesy accidentals. B-sharp and C-flat retain the correct octave. | +| Rhythm | All fourteen MusicXML types, maxima through 1024th; up to sixteen augmentation dots; exact positive rational metric durations and separately stored written values. Time modifications must reconcile the two. | +| Chords and rests | Simultaneous chord tones, shorter additional chord tones, rests, full-measure rests, hidden events, forward spacers, and rhythm slashes. | +| Ties and tuplets | Per-pitch ties, including partial chord ties; nested and cross-bar tuplet spans. Nested tuplets require explicit `tuplet-actual` and `tuplet-normal` numbers. | +| Small notes | Grace notes with zero metric duration, cue notes, and measured single/paired tremolos. | +| Staff state | G/F/C clefs on lines 1-5, octave clefs and octave-shift spans; standard major/minor/modal keys or custom seven-letter alterations; key, clef, and meter changes. | +| Meter | Simple, compound, additive, mixed-denominator, and unmetered timelines. A partial measure must be marked `implicit="yes"`; an initial implicit measure is the arrangement pickup. Leading rests are retained. | +| Harmony | Structured roots, kinds, inversions, slash basses, and degree additions/alterations/subtractions. Harmony events have their own offsets, including inside held notes. Their positions never split or shorten notes. | +| Text | Title, composer, part name, one lyric verse with syllabification and extenders, rehearsal labels at measure starts, and Vamp directives. | + +Microtones, independent voices, multiple staves/parts, percussion/tab staves, repeats/navigation, slurs, dynamics, articulations, and expressive ornaments are rejected. Unknown musical elements also fail, rather than disappearing. Custom key-octave placement is not supported. Resource limits are explicit: serialized times use reduced signed-64-bit numerators/positive denominators; pitches use octaves -4 through 16 and alterations -3 through 3; clef/octave shifts span at most three octaves; meter counts are 1-1024 with denominator units 1-4096. These limits produce diagnostics, not clamping or truncation. + +Layout/exporter information such as page defaults, credits, print layout, font/color/position attributes, and source pagination does not determine the small-screen layout. Engraving is regenerated. Beam groups and written note values are retained; unrelated engraving metadata does not authorize ignoring musical descendants. `defusedxml==0.7.1` forbids XML entities and external access. Ordinary MusicXML DOCTYPE declarations are allowed without fetching their DTDs. + +### Vamp Metadata + +In MuseScore's score properties, set `vamp:style` to a supported player style. It exports as standard MusicXML miscellaneous metadata: + +```xml + + Medium Swing + +``` + +The default is Medium Swing; `Med.`, `Med. Swing`, and `Medium` remain aliases. A keyless melody exercise needs `vamp:tonic` (for example, `C`) as its transposition reference. Modes and local key changes are preserved when generating practice variants. + +Place staff text `vamp:optional-chord` at the same onset as exactly one harmony to mark a turnaround omitted from the final arrangement section. The text may be hidden. Parenthesizing an ordinary chord does not make it optional. Orphaned/duplicate markers and unknown `vamp:` properties are errors; consumed directives never enter runtime JSON. + +### Runtime Schema + +`schema_version: 2` is mandatory. `metadata` and one `staves[]` entry contain measures and an explicit voice. Measure `duration`, event `onset`/`duration`, harmony offsets, and span endpoints use `{ "numerator": N, "denominator": D }` in quarter-note units. Fractions are reduced; there is no fixed tick grid and no remainder distribution. The native loader uses exact arithmetic for validation, converting only layout coordinates to floating point. + +Events have `kind`, `written: { "type", "dots", "ratio"? }`, and independently identified pitches. Each pitch carries integer `alter`, `octave`, its own `duration` and `written`, and optional reciprocal `tie_from`/`tie_to` pitch IDs. Staff-level tuplets reference event IDs; octave spans reference absolute musical time. Attributes and harmonies are timestamped independently. The normalized harmony degree alteration is relative to the natural diatonic interval; the importer converts MusicXML's kind-relative alterations and dominant-relative additions. + +The arranger normalizes transposing instruments to concert pitch before generating parts. Guitar parts explicitly use written pitches one octave above sounding pitch and a -1 octave instrument transposition. Chord pitch definitions are separate from playable voicing choices; voicings may omit a natural fifth or lower unaltered extensions, but never change a chord kind or replace an unsupported chord with an unrelated major chord. Unplayable combinations produce a labelled rhythmic slash and a build warning. + +The renderer uses written values for glyphs, meter-aware/multilevel beams, per-pitch ties, timestamped harmony, and OTF-derived glyph bounds. Content-aware systems split dense measures into continuations. A canonical 976x484 layout makes page navigation stable across viewport sizes; vertical extent affects pagination instead of clamping pitches. The active page geometry is cached between redraws. + +### Validation And Migration + +Generation validates sources and complete staged output before replacing the previous asset directory. CMake tracks the input list as well as file contents, so additions and removals regenerate the catalogue. Runtime packages contain JSON and OTF fonts, not MusicXML or Python importer dependencies. Python test discovery runs all generator/importer tests; `tests/fixtures/notation.musicxml` exercises the broader renderer contract. The optional `vamp-render-score` test target captures a page using a desktop-capable Raylib build. + +Diagnostics identify the file, part, measure, and unsupported element or invalid relationship. Examples include `part P1, measure 4: unsupported musical element `, `tie stop has no contiguous matching start`, and `optional-chord marker must identify exactly one harmony`. + +The six compact source files were replaced, not retained as a second authoring format. Existing catalogue rows refresh from regenerated JSON at startup. Custom database rows (`catalog_managed = 0`) are retained, including ID conflicts, and are never automatically deleted to resolve a schema mismatch. Custom legacy JSON must be reimported as schema v2; the loader reports a regeneration error instead of interpreting old ticks heuristically. + +References: [MusicXML durations](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/duration/), [tuplets](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/tuplet/), [degree alterations](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/degree-alter/), and [SMuFL accidentals](https://smufl.formats.music/latest/tables/standard-accidentals-12-edo.html). 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. diff --git a/cmake/zero.cmake b/cmake/zero.cmake index 1925eaf..0cb8725 100644 --- a/cmake/zero.cmake +++ b/cmake/zero.cmake @@ -61,6 +61,7 @@ set(VAMP_CORE_SOURCES src/vamp_zero/errors.cpp src/vamp_zero/library.cpp src/vamp_zero/navigation.cpp + src/vamp_zero/notation.cpp src/vamp_zero/page_control.cpp src/vamp_zero/portal.cpp src/vamp_zero/score.cpp @@ -69,6 +70,17 @@ set(VAMP_CORE_SOURCES ) add_library(vamp-zero-core STATIC ${VAMP_CORE_SOURCES}) +set(VAMP_SCORE_METRICS ${CMAKE_CURRENT_BINARY_DIR}/generated/vamp_zero/score_metrics.hpp) +add_custom_command( + OUTPUT ${VAMP_SCORE_METRICS} + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_score_metrics.py + --font ${CMAKE_CURRENT_SOURCE_DIR}/package/assets/fonts/petaluma/Petaluma.otf + --output ${VAMP_SCORE_METRICS} + DEPENDS tools/generate_score_metrics.py tools/requirements.txt package/assets/fonts/petaluma/Petaluma.otf + VERBATIM +) +target_sources(vamp-zero-core PRIVATE ${VAMP_SCORE_METRICS}) +target_include_directories(vamp-zero-core PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated) target_include_directories(vamp-zero-core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ) @@ -103,15 +115,20 @@ set(VAMP_MANIFEST ${VAMP_BUNDLE_DIR}/manifest.json) configure_file(package/manifest.json.in ${VAMP_MANIFEST} @ONLY) file(GLOB_RECURSE VAMP_GENERATOR_INPUTS CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/data/*.musicxml ${CMAKE_CURRENT_SOURCE_DIR}/data/*.json ${CMAKE_CURRENT_SOURCE_DIR}/data/**/*.json ${CMAKE_CURRENT_SOURCE_DIR}/generator/*.py ${CMAKE_CURRENT_SOURCE_DIR}/generator/**/*.py + ${CMAKE_CURRENT_SOURCE_DIR}/tools/requirements.txt ${CMAKE_CURRENT_SOURCE_DIR}/package/assets/fonts/* ${CMAKE_CURRENT_SOURCE_DIR}/package/assets/fonts/**/* ) set(VAMP_GENERATED_ASSETS_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated-assets) set(VAMP_GENERATED_ASSETS_STAMP ${CMAKE_CURRENT_BINARY_DIR}/generated-assets.stamp) +string(JOIN "\n" VAMP_GENERATOR_INPUT_MANIFEST ${VAMP_GENERATOR_INPUTS}) +file(CONFIGURE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated-assets.inputs + CONTENT "@VAMP_GENERATOR_INPUT_MANIFEST@\n" @ONLY) add_custom_command( OUTPUT ${VAMP_GENERATED_ASSETS_STAMP} COMMAND ${Python3_EXECUTABLE} @@ -123,10 +140,13 @@ add_custom_command( --check COMMAND ${CMAKE_COMMAND} -E touch ${VAMP_GENERATED_ASSETS_STAMP} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${VAMP_GENERATOR_INPUTS} + DEPENDS ${VAMP_GENERATOR_INPUTS} ${CMAKE_CURRENT_BINARY_DIR}/generated-assets.inputs VERBATIM ) add_custom_target(vamp-generated-assets ALL DEPENDS ${VAMP_GENERATED_ASSETS_STAMP}) +if(BUILD_TESTING) + add_dependencies(product-vamp-test vamp-generated-assets) +endif() install(TARGETS vamp-zero RUNTIME DESTINATION zero/bin diff --git a/conanfile.py b/conanfile.py index f988695..9469004 100644 --- a/conanfile.py +++ b/conanfile.py @@ -22,9 +22,15 @@ class product_vamp_conan(conan.ConanFile): exports = "modulus.json" exports_sources = ( "CMakeLists.txt", + "modulus.py", + "modulus.json", "cmake/*.cmake", "data/*.json", "data/**/*.json", + "data/*.musicxml", + "data/**/*.musicxml", + "tests/fixtures/*", + "tests/fixtures/**/*", "generator/*.py", "generator/**/*.py", "include/*.hpp", diff --git a/data/expected/performance_summary.json b/data/expected/performance_summary.json index 1bedbd9..06fe4d4 100644 --- a/data/expected/performance_summary.json +++ b/data/expected/performance_summary.json @@ -39,1369 +39,2034 @@ { "measure": 1, "section_label": "Rhythm Lead-in", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 2, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 3, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 4, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 5, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 6, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 7, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 8, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 9, "section_label": "Vocal Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 10, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 11, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 12, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 13, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 14, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 15, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 16, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 17, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 18, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 19, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 20, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 21, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 22, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 23, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 24, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 25, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 26, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 27, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 28, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 29, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 30, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 31, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 32, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 33, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 34, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 35, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 36, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 37, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 38, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 39, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 40, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 41, "section_label": "Guitar Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 42, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 43, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 44, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 45, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 46, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 47, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 48, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 49, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 50, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 51, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 52, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 53, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 54, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 55, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 56, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 57, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 58, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 59, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 60, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 61, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 62, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 63, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 64, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 65, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 66, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 67, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 68, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 69, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 70, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 71, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 72, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 73, "section_label": "Guitar Solo", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 74, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 75, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 76, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 77, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 78, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 79, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 80, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 81, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 82, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 83, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 84, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 85, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 86, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 87, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 88, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 89, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 90, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 91, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 92, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 93, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 94, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 95, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 96, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 97, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 98, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 99, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 100, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 101, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 102, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 103, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 104, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 105, "section_label": "Guitar Solo", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 106, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 107, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 108, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 109, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 110, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 111, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 112, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 113, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 114, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 115, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 116, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 117, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 118, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 119, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 120, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 121, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 122, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 123, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 124, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 125, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 126, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 127, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 128, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 129, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 130, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 131, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 132, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 133, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 134, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 135, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 136, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 137, "section_label": "Vocal Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 138, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 139, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 140, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 141, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 142, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 143, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 144, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 145, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 146, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 147, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 148, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 149, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 150, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 151, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 152, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 153, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 154, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 155, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 156, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 157, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 158, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 159, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 160, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 161, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 162, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 163, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 164, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 165, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 166, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 167, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 168, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 169, "section_label": "Ending", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 170, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 171, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 172, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 173, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 174, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 175, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 176, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 } ] @@ -1446,1369 +2111,2034 @@ { "measure": 1, "section_label": "Rhythm Lead-in", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 2, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 3, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 4, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 5, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 6, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 7, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 8, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 9, "section_label": "Vocal Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 10, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 11, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 12, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 13, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 14, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 15, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 16, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 17, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 18, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 19, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 20, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 21, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 22, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 23, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 24, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 25, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 26, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 27, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 28, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 29, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 30, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 31, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 32, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 33, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 34, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 35, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 36, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 37, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 38, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 39, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 40, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 41, "section_label": "Guitar Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 42, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 43, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 44, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 45, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 46, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 47, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 48, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 49, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 50, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 51, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 52, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 53, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 54, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 55, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 56, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 57, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 58, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 59, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 60, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 61, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 62, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 63, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 64, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 65, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 66, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 67, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 68, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 69, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 70, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 71, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 72, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 73, "section_label": "Guitar Solo", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 74, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 75, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 76, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 77, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 78, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 79, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 80, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 81, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 82, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 83, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 84, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 85, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 86, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 87, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 88, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 89, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 90, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 91, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 92, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 93, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 94, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 95, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 96, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 97, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 98, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 99, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 100, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 101, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 102, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 103, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 104, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 105, "section_label": "Guitar Solo", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 106, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 107, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 108, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 109, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 110, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 111, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 112, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 113, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 114, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 115, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 116, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 117, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 118, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 119, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 120, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 121, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 122, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 123, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 124, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 125, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 126, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 127, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 128, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 129, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 130, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 131, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 132, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 133, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 134, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 135, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 136, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 4 }, { "measure": 137, "section_label": "Vocal Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 138, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 139, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 140, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 141, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 142, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 143, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 144, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 145, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 146, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 147, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 148, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 149, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 150, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 151, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 152, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 153, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 154, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 155, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 156, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 157, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 158, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 159, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 160, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 161, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 162, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 163, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 164, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 165, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 166, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 167, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 168, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 }, { "measure": 169, "section_label": "Ending", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 170, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 171, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 172, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + }, + { + "onset": "2", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 173, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 174, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 175, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 176, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 } ] @@ -2853,3557 +4183,2684 @@ { "measure": 1, "section_label": "Rhythm Lead-in", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 2, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 3, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "D-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 4, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "C-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "F7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 5, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 6, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 7, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 8, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter rest ", + "1+1 quarter note D4 The", + "2+1 quarter note E4 fall-", + "3+1 quarter note F4 ing" ] }, { "measure": 9, "section_label": "Vocal Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note Bb4 leaves" ] }, { "measure": 10, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 ", + "1+1 quarter note C4 drift", + "2+1 quarter note D4 by", + "3+1 quarter note E4 my" ] }, { "measure": 11, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note A4 win-", + "2+2 half note A4 dow," ] }, { "measure": 12, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note A4 ", + "1+1 quarter note Bb3 the", + "2+1 quarter note C4 au-", + "3+1 quarter note D4 tumn" ] }, { "measure": 13, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 leaves" ] }, { "measure": 14, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note A3 of", + "2+1 quarter note B3 red", + "3+1 quarter note C#4 and" ] }, { "measure": 15, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note F4 gold." ] }, { "measure": 16, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter rest ", + "1+1 quarter note D4 I", + "2+1 quarter note E4 see", + "3+1 quarter note F4 your" ] }, { "measure": 17, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note Bb4 lips," ] }, { "measure": 18, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 ", + "1+1 quarter note C4 the", + "2+1 quarter note D4 sum-", + "3+1 quarter note E4 mer" ] }, { "measure": 19, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note A4 kiss-", + "2+2 half note A4 es," ] }, { "measure": 20, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note A4 ", + "1+1 quarter note Bb3 the", + "2+1 quarter note C4 sun-", + "3+1 quarter note D4 burnt" ] }, { "measure": 21, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 hands" ] }, { "measure": 22, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note E4 I", + "2+1 quarter note G4 used", + "3+1 quarter note F4 to" ] }, { "measure": 23, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note D4 hold." ] }, { "measure": 24, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note D4 ", + "1+1 quarter rest ", + "2+1 quarter note C#4 Since", + "3+1 quarter note D4 you" ] }, { "measure": 25, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note E4 went", + "1+1 quarter note A3 a-", + "2+2 half note E4 way" ] }, { "measure": 26, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note E4 ", + "1+1 quarter note E4 the", + "2+1 quarter note D4 days", + "3+1 quarter note E4 grow" ] }, { "measure": 27, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note F4 long," ] }, { "measure": 28, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note F4 ", + "1+1 quarter note F4 and", + "2+1 quarter note E4 soon", + "3+1 quarter note F4 I'll" ] }, { "measure": 29, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 hear" ] }, { "measure": 30, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note C4 old", + "2+1 quarter note C5 win-", + "3+1 quarter note Bb4 ter's" ] }, { "measure": 31, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note A4 song." ] }, { "measure": 32, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note A4 ", + "1+1 quarter rest ", + "2+1 quarter note G#4 But", + "3+1 quarter note A4 I" ] }, { "measure": 33, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 miss", + "1+1 quarter note Bb4 you", + "2+1 quarter note G4 most", + "3+1 quarter note G4 of" ] }, { "measure": 34, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note E4 all,", + "3+1 quarter note Bb4 my" ] }, { "measure": 35, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "0", + "symbol": "D-7" }, { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+2 half note A4 dar-", + "2+2 half note A4 ling," ] }, { "measure": 36, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "C-7" }, { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "2", + "symbol": "F7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+2 half note A4 ", + "2+2 half note D4 when" ] }, { "measure": 37, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note G4 au-", + "3+1 quarter note F4 tumn" ] }, { "measure": 38, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note E4 leaves", + "2+1 quarter note F4 start", + "3+1 quarter note A3 to" ] }, { "measure": 39, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note D4 fall." ] }, { "measure": 40, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 41, "section_label": "Guitar Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 42, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 43, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 44, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 45, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 46, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 47, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 48, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 49, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 50, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 51, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 52, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 53, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 54, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 55, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 56, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 57, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 58, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 59, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 60, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 61, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 62, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 63, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 64, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 65, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 66, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 67, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "D-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 68, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "C-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "F7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 69, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 70, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 71, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 72, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 73, "section_label": "Guitar Solo", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 74, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 75, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 76, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 77, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 78, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 79, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 80, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 81, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 82, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 83, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 84, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 85, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 86, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 87, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 88, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 89, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 90, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 91, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 92, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 93, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 94, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 95, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 96, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 97, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 98, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 99, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "D-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 100, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "C-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "F7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 101, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 102, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 103, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 104, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 105, "section_label": "Guitar Solo", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 106, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 107, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 108, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 109, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 110, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 111, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 112, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 113, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 114, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 115, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 116, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 117, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 118, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 119, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 120, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 121, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 122, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 123, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 124, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 125, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 126, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 127, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 128, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 129, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 130, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 131, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "D-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 132, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "C-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "F7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 133, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 134, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 135, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 136, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter rest ", + "1+1 quarter note D4 The", + "2+1 quarter note E4 fall-", + "3+1 quarter note F4 ing" ] }, { "measure": 137, "section_label": "Vocal Melody", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note Bb4 leaves" ] }, { "measure": 138, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 ", + "1+1 quarter note C4 drift", + "2+1 quarter note D4 by", + "3+1 quarter note E4 my" ] }, { "measure": 139, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note A4 win-", + "2+2 half note A4 dow," ] }, { "measure": 140, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note A4 ", + "1+1 quarter note Bb3 the", + "2+1 quarter note C4 au-", + "3+1 quarter note D4 tumn" ] }, { "measure": 141, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 leaves" ] }, { "measure": 142, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note A3 of", + "2+1 quarter note B3 red", + "3+1 quarter note C#4 and" ] }, { "measure": 143, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note F4 gold." ] }, { "measure": 144, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter rest ", + "1+1 quarter note D4 I", + "2+1 quarter note E4 see", + "3+1 quarter note F4 your" ] }, { "measure": 145, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note Bb4 lips," ] }, { "measure": 146, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 ", + "1+1 quarter note C4 the", + "2+1 quarter note D4 sum-", + "3+1 quarter note E4 mer" ] }, { "measure": 147, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note A4 kiss-", + "2+2 half note A4 es," ] }, { "measure": 148, "section_label": "", - "chord_symbols": [ - "Bbmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bbmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note A4 ", + "1+1 quarter note Bb3 the", + "2+1 quarter note C4 sun-", + "3+1 quarter note D4 burnt" ] }, { "measure": 149, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 hands" ] }, { "measure": 150, "section_label": "", - "chord_symbols": [ - "A7" + "harmonies": [ + { + "onset": "0", + "symbol": "A7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note E4 I", + "2+1 quarter note G4 used", + "3+1 quarter note F4 to" ] }, { "measure": 151, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note D4 hold." ] }, { "measure": 152, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note D4 ", + "1+1 quarter rest ", + "2+1 quarter note C#4 Since", + "3+1 quarter note D4 you" ] }, { "measure": 153, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note E4 went", + "1+1 quarter note A3 a-", + "2+2 half note E4 way" ] }, { "measure": 154, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note E4 ", + "1+1 quarter note E4 the", + "2+1 quarter note D4 days", + "3+1 quarter note E4 grow" ] }, { "measure": 155, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note F4 long," ] }, { "measure": 156, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note F4 ", + "1+1 quarter note F4 and", + "2+1 quarter note E4 soon", + "3+1 quarter note F4 I'll" ] }, { "measure": 157, "section_label": "", - "chord_symbols": [ - "G-7" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 hear" ] }, { "measure": 158, "section_label": "", - "chord_symbols": [ - "C7" + "harmonies": [ + { + "onset": "0", + "symbol": "C7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note C4 old", + "2+1 quarter note C5 win-", + "3+1 quarter note Bb4 ter's" ] }, { "measure": 159, "section_label": "", - "chord_symbols": [ - "Fmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Fmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note A4 song." ] }, { "measure": 160, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note A4 ", + "1+1 quarter rest ", + "2+1 quarter note G#4 But", + "3+1 quarter note A4 I" ] }, { "measure": 161, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 miss", + "1+1 quarter note Bb4 you", + "2+1 quarter note G4 most", + "3+1 quarter note G4 of" ] }, { "measure": 162, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note E4 all,", + "3+1 quarter note Bb4 my" ] }, { "measure": 163, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "0", + "symbol": "D-7" }, { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+2 half note A4 dar-", + "2+2 half note A4 ling," ] }, { "measure": 164, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "C-7" }, { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "2", + "symbol": "F7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+2 half note A4 ", + "2+2 half note D4 when" ] }, { "measure": 165, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note G4 au-", + "3+1 quarter note F4 tumn" ] }, { "measure": 166, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note E4 leaves", + "2+1 quarter note F4 start", + "3+1 quarter note A3 to" ] }, { "measure": 167, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note D4 fall." ] }, { "measure": 168, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 169, "section_label": "Ending", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 miss", + "1+1 quarter note Bb4 you", + "2+1 quarter note G4 most", + "3+1 quarter note G4 of" ] }, { "measure": 170, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note E4 all,", + "3+1 quarter note Bb4 my" ] }, { "measure": 171, "section_label": "", - "chord_symbols": [ - "D-7", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "0", + "symbol": "D-7" }, { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+2 half note A4 dar-", + "2+2 half note A4 ling," ] }, { "measure": 172, "section_label": "", - "chord_symbols": [ - "C-7", - "F7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "C-7" }, { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true + "onset": "2", + "symbol": "F7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+2 half note A4 ", + "2+2 half note D4 when" ] }, { "measure": 173, "section_label": "", - "chord_symbols": [ - "E-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "E-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note G4 au-", + "3+1 quarter note F4 tumn" ] }, { "measure": 174, "section_label": "", - "chord_symbols": [ - "A7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note E4 leaves", + "2+1 quarter note F4 start", + "3+1 quarter note A3 to" ] }, { "measure": 175, "section_label": "", - "chord_symbols": [ - "D-" + "harmonies": [ + { + "onset": "0", + "symbol": "D-" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note D4 fall." ] }, { "measure": 176, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] } ] @@ -6448,1445 +6905,2260 @@ { "measure": 1, "section_label": "Rhythm Lead-in", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 2, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 3, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 4, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 5, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 6, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 7, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 8, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 9, "section_label": "Vocal Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 10, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 11, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 12, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 13, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 14, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 15, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 16, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 17, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 18, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 19, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 20, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 21, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 22, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 23, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 24, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 25, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 26, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 27, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 28, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 29, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 30, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 31, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 32, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 33, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 34, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 35, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 36, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 37, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 38, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 39, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 40, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 41, "section_label": "Guitar Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 42, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 43, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 44, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 45, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 46, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 47, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 48, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 49, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 50, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 51, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 52, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 53, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 54, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 55, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 56, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 57, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 58, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 59, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 60, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 61, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 62, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 63, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 64, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 65, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 66, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 67, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 68, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 69, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 70, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 71, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 72, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 73, "section_label": "Guitar Solo", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 74, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 75, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 76, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 77, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 78, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 79, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 80, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 81, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 82, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 83, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 84, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 85, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 86, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 87, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 88, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 89, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 90, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 91, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 92, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 93, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 94, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 95, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 96, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 97, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 98, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 99, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 100, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 101, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 102, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 103, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 104, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 105, "section_label": "Guitar Solo", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 106, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 107, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 108, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 109, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 110, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 111, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 112, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 113, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 114, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 115, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 116, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 117, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 118, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 119, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 120, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 121, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 122, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 123, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 124, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 125, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 126, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 127, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 128, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 129, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 130, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 131, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 132, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 133, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 134, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 135, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 136, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 137, "section_label": "Vocal Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 138, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 139, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 140, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 141, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 142, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 143, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 144, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 145, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 146, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 147, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 148, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 149, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 150, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 151, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 152, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 153, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 154, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 155, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 156, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 157, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 158, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 159, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 160, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 161, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 162, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 163, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 164, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 165, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 166, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 167, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 168, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 169, "section_label": "Ending", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 170, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 171, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 172, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 173, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 174, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 175, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 176, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 } ] @@ -7931,1445 +9203,2260 @@ { "measure": 1, "section_label": "Rhythm Lead-in", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 2, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 3, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 4, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 5, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 6, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 7, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 8, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 9, "section_label": "Vocal Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 10, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 11, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 12, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 13, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 14, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 15, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 16, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 17, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 18, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 19, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 20, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 21, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 22, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 23, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 24, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 25, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 26, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 27, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 28, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 29, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 30, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 31, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 32, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 33, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 34, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 35, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 36, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 37, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 38, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 39, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 40, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 41, "section_label": "Guitar Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 42, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 43, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 44, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 45, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 46, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 47, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 48, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 49, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 50, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 51, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 52, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 53, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 54, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 55, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 56, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 57, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 58, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 59, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 60, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 61, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 62, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 63, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 64, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 65, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 66, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 67, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 68, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 69, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 70, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 71, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 72, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 73, "section_label": "Guitar Solo", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 74, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 75, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 76, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 77, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 78, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 79, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 80, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 81, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 82, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 83, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 84, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 85, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 86, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 87, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 88, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 89, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 90, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 91, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 92, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 93, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 94, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 95, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 96, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 97, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 98, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 99, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 100, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 101, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 102, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 103, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 104, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 105, "section_label": "Guitar Solo", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 106, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 107, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 108, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 109, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 110, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 111, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 112, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 113, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 114, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 115, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 116, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 117, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 118, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 119, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 120, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 121, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 122, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 123, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 124, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 125, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 126, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 127, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 128, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 129, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 130, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 131, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 132, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 133, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 134, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 135, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 136, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 4 }, { "measure": 137, "section_label": "Vocal Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 138, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 139, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 140, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 141, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 142, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 143, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 144, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 145, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 146, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 147, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 148, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 149, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 150, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 151, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 152, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 153, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 154, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 155, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 156, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 157, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 158, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 159, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 160, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 161, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 162, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 163, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 164, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 165, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 166, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 167, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 168, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 169, "section_label": "Ending", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 170, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 171, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 172, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 173, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 174, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 175, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0 }, { "measure": 176, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0 } ] @@ -9414,3801 +11501,2923 @@ { "measure": 1, "section_label": "Rhythm Lead-in", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 2, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 3, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "Ebmaj7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Ab7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 4, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 5, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 6, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 7, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 8, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "F-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Bb7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 9, "section_label": "Vocal Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note Bb3 Hide", + "3+1 quarter note C4 your" ] }, { "measure": 10, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Db4 heart", + "1+1/2 eighth note C4 from", + "3/2+1/2 eighth note G4 sight,", + "2+2 half note G4 " ] }, { "measure": 11, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note C4 lock", + "3+1 quarter note D4 your" ] }, { "measure": 12, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Eb4 dreams", + "1+1/2 eighth note D4 at", + "3/2+1/2 eighth note A4 night,", + "2+2 half note A4 " ] }, { "measure": 13, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Bb4 it", + "2+2 half note Bb4 could" ] }, { "measure": 14, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 hap-", + "1+2 half note Ab4 pen", + "3+1 quarter note Eb4 to" ] }, { "measure": 15, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 you." ] }, { "measure": 16, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 17, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note G4 Don't", + "2+2 half note G4 count" ] }, { "measure": 18, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 stars", + "1+1 quarter note Ab4 or", + "2+1 quarter note Eb4 you", + "3+1/2 eighth note F4 might", + "7/2+1/2 eighth note G4 stum-" ] }, { "measure": 19, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note G4 ", + "2+2 half note G4 ble," ] }, { "measure": 20, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 21, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Eb4 some-", + "2+2 half note Eb4 one" ] }, { "measure": 22, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Eb4 drops", + "1+1 quarter note F4 a", + "2+1 quarter note C4 sigh", + "3+1/2 eighth note D4 and", + "7/2+1/2 eighth note Eb4 down" ] }, { "measure": 23, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Eb4 ", + "2+2 half note F4 you" ] }, { "measure": 24, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 6144, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+3/2 quarter. note C4 tum-", + "3/2+1/2 eighth note D4 ble.", + "2+2 half note D4 " ] }, { "measure": 25, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note Bb3 Keep", + "3+1 quarter note C4 an" ] }, { "measure": 26, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Db4 eye", + "1+1/2 eighth note C4 on", + "3/2+1/2 eighth note G4 spring,", + "2+2 half note G4 " ] }, { "measure": 27, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note C4 run", + "3+1 quarter note D4 when" ] }, { "measure": 28, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Eb4 church", + "1+1/2 eighth note D4 bells", + "3/2+1/2 eighth note A4 ring,", + "2+2 half note A4 " ] }, { "measure": 29, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Bb4 it", + "2+2 half note Bb4 could" ] }, { "measure": 30, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 hap-", + "1+2 half note Ab4 pen", + "3+1 quarter note Eb4 to" ] }, { "measure": 31, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 you." ] }, { "measure": 32, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 33, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note G4 All", + "2+2 half note G4 I" ] }, { "measure": 34, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 did", + "1+1 quarter note Ab4 was", + "2+1 quarter note Eb4 won-", + "3+1/2 eighth note F4 der", + "7/2+1/2 eighth note G4 how" ] }, { "measure": 35, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note D4 your", + "2+1 quarter note F4 arms", + "3+1/2 eighth note Eb4 would", + "7/2+1/2 eighth note Bb4 be," ] }, { "measure": 36, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole note Bb4 " ] }, { "measure": 37, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Bb3 and", + "2+2 half note Bb3 it" ] }, { "measure": 38, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Ab4 hap-", + "1+2 half note F4 pened", + "3+1 quarter note G4 to" ] }, { "measure": 39, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note Eb4 me." ] }, { "measure": 40, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 41, "section_label": "Guitar Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 42, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "G-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "C7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 43, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 44, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "A-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "D7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 45, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 46, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 47, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 48, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 49, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 50, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 51, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 52, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "D-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 53, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 54, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 55, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 56, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 57, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 58, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "G-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "C7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 59, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 60, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "A-7b9" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "D7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 61, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 62, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 63, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 64, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 65, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 66, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 67, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "Ebmaj7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Ab7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 68, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 69, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 70, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 71, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 72, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "F-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Bb7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 73, "section_label": "Guitar Solo", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 74, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "G-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "C7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 75, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 76, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "A-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "D7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 77, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 78, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 79, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 80, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 81, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 82, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 83, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 84, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "D-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 85, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 86, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 87, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 88, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 89, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 90, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "G-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "C7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 91, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 92, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "A-7b9" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "D7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 93, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 94, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 95, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 96, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 97, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 98, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 99, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "Ebmaj7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Ab7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 100, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 101, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 102, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 103, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 104, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "F-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Bb7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 105, "section_label": "Guitar Solo", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 106, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "G-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "C7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 107, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 108, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "A-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "D7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 109, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 110, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 111, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 112, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 113, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 114, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 115, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 116, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "D-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "G7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 117, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 118, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 119, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 120, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 121, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 122, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "G-7b5" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "C7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 123, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 124, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "A-7b9" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "D7b9" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 125, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 126, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 127, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 128, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 129, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 130, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 131, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "Ebmaj7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Ab7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 132, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 133, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 134, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 135, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole rest " ] }, { "measure": 136, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" - ], - "slash_count": 0, - "vocal_skeleton": [ + "harmonies": [ { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "0", + "symbol": "F-7" }, { - "kind": "rest", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false + "onset": "2", + "symbol": "Bb7" } + ], + "duration": "4", + "slash_count": 0, + "vocal_skeleton": [ + "0+4 whole rest " ] }, { "measure": 137, "section_label": "Vocal Melody", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note Bb3 Hide", + "3+1 quarter note C4 your" ] }, { "measure": 138, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Db4 heart", + "1+1/2 eighth note C4 from", + "3/2+1/2 eighth note G4 sight,", + "2+2 half note G4 " ] }, { "measure": 139, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note C4 lock", + "3+1 quarter note D4 your" ] }, { "measure": 140, "section_label": "", - "chord_symbols": [ - "A-7b5", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b5" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Eb4 dreams", + "1+1/2 eighth note D4 at", + "3/2+1/2 eighth note A4 night,", + "2+2 half note A4 " ] }, { "measure": 141, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Bb4 it", + "2+2 half note Bb4 could" ] }, { "measure": 142, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 hap-", + "1+2 half note Ab4 pen", + "3+1 quarter note Eb4 to" ] }, { "measure": 143, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 you." ] }, { "measure": 144, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 145, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note G4 Don't", + "2+2 half note G4 count" ] }, { "measure": 146, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 stars", + "1+1 quarter note Ab4 or", + "2+1 quarter note Eb4 you", + "3+1/2 eighth note F4 might", + "7/2+1/2 eighth note G4 stum-" ] }, { "measure": 147, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note G4 ", + "2+2 half note G4 ble," ] }, { "measure": 148, "section_label": "", - "chord_symbols": [ - "D-7b5", - "G7" + "harmonies": [ + { + "onset": "0", + "symbol": "D-7b5" + }, + { + "onset": "2", + "symbol": "G7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 149, "section_label": "", - "chord_symbols": [ - "C-7" + "harmonies": [ + { + "onset": "0", + "symbol": "C-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Eb4 some-", + "2+2 half note Eb4 one" ] }, { "measure": 150, "section_label": "", - "chord_symbols": [ - "F7" + "harmonies": [ + { + "onset": "0", + "symbol": "F7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Eb4 drops", + "1+1 quarter note F4 a", + "2+1 quarter note C4 sigh", + "3+1/2 eighth note D4 and", + "7/2+1/2 eighth note Eb4 down" ] }, { "measure": 151, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Eb4 ", + "2+2 half note F4 you" ] }, { "measure": 152, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 6144, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+3/2 quarter. note C4 tum-", + "3/2+1/2 eighth note D4 ble.", + "2+2 half note D4 " ] }, { "measure": 153, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note Bb3 Keep", + "3+1 quarter note C4 an" ] }, { "measure": 154, "section_label": "", - "chord_symbols": [ - "G-7b5", - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + }, + { + "onset": "2", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Db4 eye", + "1+1/2 eighth note C4 on", + "3/2+1/2 eighth note G4 spring,", + "2+2 half note G4 " ] }, { "measure": 155, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 12288, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+3 half. note C4 run", + "3+1 quarter note D4 when" ] }, { "measure": 156, "section_label": "", - "chord_symbols": [ - "A-7b9", - "D7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "A-7b9" + }, + { + "onset": "2", + "symbol": "D7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter note Eb4 church", + "1+1/2 eighth note D4 bells", + "3/2+1/2 eighth note A4 ring,", + "2+2 half note A4 " ] }, { "measure": 157, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Bb4 it", + "2+2 half note Bb4 could" ] }, { "measure": 158, "section_label": "", - "chord_symbols": [ - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Bb4 hap-", + "1+2 half note Ab4 pen", + "3+1 quarter note Eb4 to" ] }, { "measure": 159, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note G4 you." ] }, { "measure": 160, "section_label": "", - "chord_symbols": [ - "C7b9" + "harmonies": [ + { + "onset": "0", + "symbol": "C7b9" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 161, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note G4 All", + "2+2 half note G4 I" ] }, { "measure": 162, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 did", + "1+1 quarter note Ab4 was", + "2+1 quarter note Eb4 won-", + "3+1/2 eighth note F4 der", + "7/2+1/2 eighth note G4 how" ] }, { "measure": 163, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note D4 your", + "2+1 quarter note F4 arms", + "3+1/2 eighth note Eb4 would", + "7/2+1/2 eighth note Bb4 be," ] }, { "measure": 164, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole note Bb4 " ] }, { "measure": 165, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Bb3 and", + "2+2 half note Bb3 it" ] }, { "measure": 166, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Ab4 hap-", + "1+2 half note F4 pened", + "3+1 quarter note G4 to" ] }, { "measure": 167, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note Eb4 me." ] }, { "measure": 168, "section_label": "", - "chord_symbols": [ - "F-7", - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + }, + { + "onset": "2", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] }, { "measure": 169, "section_label": "Ending", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note G4 All", + "2+2 half note G4 I" ] }, { "measure": 170, "section_label": "", - "chord_symbols": [ - "Db7" + "harmonies": [ + { + "onset": "0", + "symbol": "Db7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 did", + "1+1 quarter note Ab4 was", + "2+1 quarter note Eb4 won-", + "3+1/2 eighth note F4 der", + "7/2+1/2 eighth note G4 how" ] }, { "measure": 171, "section_label": "", - "chord_symbols": [ - "Ebmaj7", - "Ab7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + }, + { + "onset": "2", + "symbol": "Ab7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 2048, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note G4 ", + "1+1 quarter note D4 your", + "2+1 quarter note F4 arms", + "3+1/2 eighth note Eb4 would", + "7/2+1/2 eighth note Bb4 be," ] }, { "measure": 172, "section_label": "", - "chord_symbols": [ - "G-7b5" + "harmonies": [ + { + "onset": "0", + "symbol": "G-7b5" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": false - } + "0+4 whole note Bb4 " ] }, { "measure": 173, "section_label": "", - "chord_symbols": [ - "F-7" + "harmonies": [ + { + "onset": "0", + "symbol": "F-7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - } + "0+2 half note Bb3 and", + "2+2 half note Bb3 it" ] }, { "measure": 174, "section_label": "", - "chord_symbols": [ - "Bb7" + "harmonies": [ + { + "onset": "0", + "symbol": "Bb7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 8192, - "display_ticks": null, - "has_lyric": true - }, - { - "kind": "note", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": true - } + "0+1 quarter note Ab4 hap-", + "1+2 half note F4 pened", + "3+1 quarter note G4 to" ] }, { "measure": 175, "section_label": "", - "chord_symbols": [ - "Ebmaj7" + "harmonies": [ + { + "onset": "0", + "symbol": "Ebmaj7" + } ], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "note", - "duration_ticks": 16384, - "display_ticks": null, - "has_lyric": true - } + "0+4 whole note Eb4 me." ] }, { "measure": 176, "section_label": "", - "chord_symbols": [], + "harmonies": [], + "duration": "4", "slash_count": 0, "vocal_skeleton": [ - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - }, - { - "kind": "rest", - "duration_ticks": 4096, - "display_ticks": null, - "has_lyric": false - } + "0+1 quarter rest ", + "1+1 quarter rest ", + "2+1 quarter rest ", + "3+1 quarter rest " ] } ] 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 deleted file mode 100644 index 90571c5..0000000 --- a/data/practice/chord/01_three_note_shell_voicings/01-cycles.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "title": "01 Cycles", - "composer": "", - "key": "C", - "time": "4/4", - "clef": "treble", - "part": "Vocal", - "role": "vocal", - "style": "Medium 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/chord/01_three_note_shell_voicings/01-cycles.musicxml b/data/practice/chord/01_three_note_shell_voicings/01-cycles.musicxml new file mode 100644 index 0000000..73c46a0 --- /dev/null +++ b/data/practice/chord/01_three_note_shell_voicings/01-cycles.musicxml @@ -0,0 +1,580 @@ + + + + 01 Cycles + + + + Medium Swing + + + + + Vocal + + + + + + 12 + + 0 + major + + + + G + 2 + + + + + G + + dominant + + + + G + 4 + + 12 + 1 + quarter + + + + + B + 4 + + 12 + 1 + quarter + + + + + F + 5 + + 12 + 1 + quarter + + + + G + 4 + + 12 + 1 + quarter + + + + + B + 4 + + 12 + 1 + quarter + + + + + F + 5 + + 12 + 1 + quarter + + + + C + + dominant + + + + C + 4 + + 12 + 1 + quarter + + + + + B + -1 + 4 + + 12 + 1 + quarter + + + + + E + 5 + + 12 + 1 + quarter + + + + C + 4 + + 12 + 1 + quarter + + + + + B + -1 + 4 + + 12 + 1 + quarter + + + + + E + 5 + + 12 + 1 + quarter + + + + + + F + + dominant + + + + F + 4 + + 12 + 1 + quarter + + + + + A + 4 + + 12 + 1 + quarter + + + + + E + -1 + 5 + + 12 + 1 + quarter + + + + F + 4 + + 12 + 1 + quarter + + + + + A + 4 + + 12 + 1 + quarter + + + + + E + -1 + 5 + + 12 + 1 + quarter + + + + B + -1 + + dominant + + + + B + -1 + 3 + + 12 + 1 + quarter + + + + + A + -1 + 4 + + 12 + 1 + quarter + + + + + D + 5 + + 12 + 1 + quarter + + + + B + -1 + 3 + + 12 + 1 + quarter + + + + + A + -1 + 4 + + 12 + 1 + quarter + + + + + D + 5 + + 12 + 1 + quarter + + + + + + E + -1 + + dominant + + + + E + -1 + 4 + + 12 + 1 + quarter + + + + + G + 4 + + 12 + 1 + quarter + + + + + D + -1 + 5 + + 12 + 1 + quarter + + + + E + -1 + 4 + + 12 + 1 + quarter + + + + + G + 4 + + 12 + 1 + quarter + + + + + D + -1 + 5 + + 12 + 1 + quarter + + + + A + -1 + + dominant + + + + A + -1 + 3 + + 12 + 1 + quarter + + + + + G + -1 + 4 + + 12 + 1 + quarter + + + + + C + 5 + + 12 + 1 + quarter + + + + A + -1 + 3 + + 12 + 1 + quarter + + + + + G + -1 + 4 + + 12 + 1 + quarter + + + + + C + 5 + + 12 + 1 + quarter + + + + + + D + -1 + + major-seventh + + + + D + -1 + 4 + + 12 + 1 + quarter + + + + + F + 4 + + 12 + 1 + quarter + + + + + C + 5 + + 12 + 1 + quarter + + + + D + -1 + 4 + + 12 + 1 + quarter + + + + + F + 4 + + 12 + 1 + quarter + + + + + C + 5 + + 12 + 1 + quarter + + + + D + -1 + + major-sixth + + + + D + -1 + 4 + + 12 + 1 + quarter + + + + + F + 4 + + 12 + 1 + quarter + + + + + B + -1 + 4 + + 12 + 1 + quarter + + + + D + -1 + 4 + + 12 + 1 + quarter + + + + + F + 4 + + 12 + 1 + quarter + + + + + B + -1 + 4 + + 12 + 1 + quarter + + + + \ No newline at end of file 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 deleted file mode 100644 index e390923..0000000 --- a/data/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "title": "01 The Diatonic Scales", - "composer": "", - "key": "C", - "time": "4/4", - "clef": "treble", - "part": "Vocal", - "role": "vocal", - "style": "Medium 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/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.musicxml b/data/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.musicxml new file mode 100644 index 0000000..a38c885 --- /dev/null +++ b/data/practice/melody/01_diatonic_excercises/01-the-diatonic-scales.musicxml @@ -0,0 +1,1327 @@ + + + + 01 The Diatonic Scales + + + + Medium Swing + + + + + Vocal + + + + + + 12 + + 0 + major + + + + G + 2 + + + + + C + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + D + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + E + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + D + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + E + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + F + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + E + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + F + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + G + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + F + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + G + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + A + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + + + G + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + A + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + B + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + A + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + B + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + C + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + B + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + D + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + D + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + E + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + + + D + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + E + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + F + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + E + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + F + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + G + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + F + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + G + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + A + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + G + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + A + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + B + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + + + A + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + B + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + C + 6 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + B + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 6 + + 4 + 1 + eighth + + 3 + 2 + + + + + D + 6 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 6 + + 24 + 1 + half + + + + + + C + 6 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + B + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + A + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + B + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + A + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + G + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + A + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + G + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + F + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + G + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + F + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + E + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + + + F + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + E + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + D + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + E + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + D + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + C + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + D + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + B + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 5 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + B + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + A + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + + + B + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + A + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + G + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + A + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + G + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + F + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + G + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + F + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + E + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + F + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + E + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + D + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + + + E + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + D + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + C + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + D + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 4 + + 4 + 1 + eighth + + 3 + 2 + + + + + B + 3 + + 4 + 1 + eighth + + 3 + 2 + + + + + + + + C + 4 + + 24 + 1 + half + + + + \ No newline at end of file diff --git a/data/practice/melody/01_diatonic_excercises/02-the-diatonic-scales.json b/data/practice/melody/01_diatonic_excercises/02-the-diatonic-scales.json deleted file mode 100644 index 398b5c9..0000000 --- a/data/practice/melody/01_diatonic_excercises/02-the-diatonic-scales.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "title": "02 The Diatonic Scales", - "composer": "", - "key": "C", - "time": "4/4", - "clef": "treble", - "part": "Vocal", - "role": "vocal", - "style": "Medium Swing", - "measures": [ - "C4:8 D4:8 E4:8 D4:8 E4:8 F4:8 E4:8 F4:8", - "G4:8 F4:8 G4:8 A4:8 G4:8 A4:8 B4:8 A4:8", - "B4:8 C5:8 B4:8 C5:8 D6:8 C5:8 D5:8 E5:8", - "D5:8 E5:8 F5:8 E5:8 F5:8 G5:8 F5:8 G5:8", - "A5:8 G5:8 A5:8 B5:8 A5:8 B5:8 C6:8 B5:8", - "C6:8 D6:8 C6:8 B5:8 C6:8 B5:8 A5:8 B5:8", - "A5:8 G5:8 A5:8 G5:8 F5:8 G5:8 F5:8 E5:8", - "F5:8 E5:8 D5:8 E5:8 D5:8 C5:8 D5:8 C5:8", - "B4:8 C5:8 B4:8 A4:8 G4:8 F4:8 E4:8 D4:8", - "C4:1" - ] -} diff --git a/data/practice/melody/01_diatonic_excercises/02-the-diatonic-scales.musicxml b/data/practice/melody/01_diatonic_excercises/02-the-diatonic-scales.musicxml new file mode 100644 index 0000000..d43a4be --- /dev/null +++ b/data/practice/melody/01_diatonic_excercises/02-the-diatonic-scales.musicxml @@ -0,0 +1,710 @@ + + + + 02 The Diatonic Scales + + + + Medium Swing + + + + + Vocal + + + + + + 12 + + 0 + major + + + + G + 2 + + + + + C + 4 + + 6 + 1 + eighth + + + + D + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + D + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + F + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + F + 4 + + 6 + 1 + eighth + + + + + + G + 4 + + 6 + 1 + eighth + + + + F + 4 + + 6 + 1 + eighth + + + + G + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + G + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + + + B + 4 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + D + 6 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + + + D + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + + + A + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + C + 6 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + + + C + 6 + + 6 + 1 + eighth + + + + D + 6 + + 6 + 1 + eighth + + + + C + 6 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + C + 6 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + + + A + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + + + F + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + + + B + 4 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + G + 4 + + 6 + 1 + eighth + + + + F + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + D + 4 + + 6 + 1 + eighth + + + + + + C + 4 + + 48 + 1 + whole + + + + \ No newline at end of file diff --git a/data/practice/melody/01_diatonic_excercises/03-the-diatonic-scales.json b/data/practice/melody/01_diatonic_excercises/03-the-diatonic-scales.json deleted file mode 100644 index d8767b5..0000000 --- a/data/practice/melody/01_diatonic_excercises/03-the-diatonic-scales.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "title": "03 The Diatonic Scales", - "composer": "", - "key": "C", - "time": "4/4", - "clef": "treble", - "part": "Vocal", - "role": "vocal", - "style": "Medium Swing", - "measures": [ - "B3:8 C4:8 D4:8 E4:8 F4:8 E4:8 F4:8 G4:8", - "F4:8 E4:8 F4:8 G4:8 A4:8 B4:8 C5:8 B4:8", - "A4:8 B4:8 C5:8 D5:8 E5:8 F5:8 E5:8 D5:8", - "C5:8 B4:8 C5:8 D5:8 E5:8 F5:8 G5:8 A5:8", - "B5:8 C6:8 B5:8 A5:8 G5:8 A5:8 B5:8 C6:8", - "D6:8 E6:8 D6:8 C6:8 B5:8 C6:8 B5:8 A5:8", - "G5:8 A5:8 B5:8 A5:8 G5:8 F5:8 E5:8 F5:8", - "G5:8 F5:8 E5:8 D5:8 E5:8 F5:8 E5:8 D5:8", - "C5:8 B4:8 A4:8 G4:8 A4:8 B4:8 A4:8 G4:8", - "F4:8 E4:8 D4:8 C4:8 B3:8 C4:8 D4:8 E4:8", - "F4:1" - ] -} diff --git a/data/practice/melody/01_diatonic_excercises/03-the-diatonic-scales.musicxml b/data/practice/melody/01_diatonic_excercises/03-the-diatonic-scales.musicxml new file mode 100644 index 0000000..3137009 --- /dev/null +++ b/data/practice/melody/01_diatonic_excercises/03-the-diatonic-scales.musicxml @@ -0,0 +1,784 @@ + + + + 03 The Diatonic Scales + + + + Medium Swing + + + + + Vocal + + + + + + 12 + + 0 + major + + + + G + 2 + + + + + B + 3 + + 6 + 1 + eighth + + + + C + 4 + + 6 + 1 + eighth + + + + D + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + F + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + F + 4 + + 6 + 1 + eighth + + + + G + 4 + + 6 + 1 + eighth + + + + + + F + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + F + 4 + + 6 + 1 + eighth + + + + G + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + + + A + 4 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + + + C + 5 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + C + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + + + B + 5 + + 6 + 1 + eighth + + + + C + 6 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + C + 6 + + 6 + 1 + eighth + + + + + + D + 6 + + 6 + 1 + eighth + + + + E + 6 + + 6 + 1 + eighth + + + + D + 6 + + 6 + 1 + eighth + + + + C + 6 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + C + 6 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + + + G + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + B + 5 + + 6 + 1 + eighth + + + + A + 5 + + 6 + 1 + eighth + + + + G + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + + + G + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + F + 5 + + 6 + 1 + eighth + + + + E + 5 + + 6 + 1 + eighth + + + + D + 5 + + 6 + 1 + eighth + + + + + + C + 5 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + G + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + B + 4 + + 6 + 1 + eighth + + + + A + 4 + + 6 + 1 + eighth + + + + G + 4 + + 6 + 1 + eighth + + + + + + F + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + D + 4 + + 6 + 1 + eighth + + + + C + 4 + + 6 + 1 + eighth + + + + B + 3 + + 6 + 1 + eighth + + + + C + 4 + + 6 + 1 + eighth + + + + D + 4 + + 6 + 1 + eighth + + + + E + 4 + + 6 + 1 + eighth + + + + + + F + 4 + + 48 + 1 + whole + + + + \ No newline at end of file diff --git a/data/songs/autumn-leaves.json b/data/songs/autumn-leaves.json deleted file mode 100644 index 83b49e7..0000000 --- a/data/songs/autumn-leaves.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "title": "Autumn Leaves", - "composer": "Joseph Kosma / Johnny Mercer / Jaques Prevert", - "key": "F", - "time": "4/4", - "clef": "treble", - "part": "Vocal", - "role": "vocal", - "style": "Medium Swing", - "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" - ] -} diff --git a/data/songs/autumn-leaves.musicxml b/data/songs/autumn-leaves.musicxml new file mode 100644 index 0000000..bbd3622 --- /dev/null +++ b/data/songs/autumn-leaves.musicxml @@ -0,0 +1,1442 @@ + + + + Autumn Leaves + + + Joseph Kosma / Johnny Mercer / Jaques Prevert + + Medium Swing + + + + + Vocal + + + + + + 12 + + -1 + major + + + + G + 2 + + + + + 12 + 1 + quarter + + + + D + 4 + + 12 + 1 + quarter + + single + The + + + + + E + 4 + + 12 + 1 + quarter + + begin + fall + + + + + F + 4 + + 12 + 1 + quarter + + single + ing + + + + + + + G + + minor-seventh + + + + B + -1 + 4 + + 48 + + 1 + whole + + + + + single + leaves + + + + + + + C + + dominant + + + + B + -1 + 4 + + 12 + + 1 + quarter + + + + + + + C + 4 + + 12 + 1 + quarter + + single + drift + + + + + D + 4 + + 12 + 1 + quarter + + single + by + + + + + E + 4 + + 12 + 1 + quarter + + single + my + + + + + + + F + + major-seventh + + + + A + 4 + + 24 + 1 + half + + begin + win + + + + + A + 4 + + 24 + + 1 + half + + + + + single + dow, + + + + + + + B + -1 + + major-seventh + + + + A + 4 + + 12 + + 1 + quarter + + + + + + + B + -1 + 3 + + 12 + 1 + quarter + + single + the + + + + + C + 4 + + 12 + 1 + quarter + + begin + au + + + + + D + 4 + + 12 + 1 + quarter + + single + tumn + + + + + + + E + + half-diminished + + + + G + 4 + + 48 + + 1 + whole + + + + + single + leaves + + + + + + + A + + dominant + + + + G + 4 + + 12 + + 1 + quarter + + + + + + + A + 3 + + 12 + 1 + quarter + + single + of + + + + + B + 3 + + 12 + 1 + quarter + + single + red + + + + + C + 1 + 4 + + 12 + 1 + quarter + + single + and + + + + + + + D + + minor + + + + F + 4 + + 48 + 1 + whole + + single + gold. + + + + + + + 12 + 1 + quarter + + + + D + 4 + + 12 + 1 + quarter + + single + I + + + + + E + 4 + + 12 + 1 + quarter + + single + see + + + + + F + 4 + + 12 + 1 + quarter + + single + your + + + + + + + G + + minor-seventh + + + + B + -1 + 4 + + 48 + + 1 + whole + + + + + single + lips, + + + + + + + C + + dominant + + + + B + -1 + 4 + + 12 + + 1 + quarter + + + + + + + C + 4 + + 12 + 1 + quarter + + single + the + + + + + D + 4 + + 12 + 1 + quarter + + begin + sum + + + + + E + 4 + + 12 + 1 + quarter + + single + mer + + + + + + + F + + major-seventh + + + + A + 4 + + 24 + 1 + half + + begin + kiss + + + + + A + 4 + + 24 + + 1 + half + + + + + single + es, + + + + + + + B + -1 + + major-seventh + + + + A + 4 + + 12 + + 1 + quarter + + + + + + + B + -1 + 3 + + 12 + 1 + quarter + + single + the + + + + + C + 4 + + 12 + 1 + quarter + + begin + sun + + + + + D + 4 + + 12 + 1 + quarter + + single + burnt + + + + + + + E + + half-diminished + + + + G + 4 + + 48 + + 1 + whole + + + + + single + hands + + + + + + + A + + dominant + + + + G + 4 + + 12 + + 1 + quarter + + + + + + + E + 4 + + 12 + 1 + quarter + + single + I + + + + + G + 4 + + 12 + 1 + quarter + + single + used + + + + + F + 4 + + 12 + 1 + quarter + + single + to + + + + + + + D + + minor + + + + D + 4 + + 48 + + 1 + whole + + + + + single + hold. + + + + + + + D + 4 + + 12 + + 1 + quarter + + + + + + + 12 + 1 + quarter + + + + C + 1 + 4 + + 12 + 1 + quarter + + single + Since + + + + + D + 4 + + 12 + 1 + quarter + + single + you + + + + + + + E + + half-diminished + + + + E + 4 + + 12 + 1 + quarter + + single + went + + + + + A + 3 + + 12 + 1 + quarter + + begin + a + + + + + E + 4 + + 24 + + 1 + half + + + + + single + way + + + + + + + A + + dominant + + 9 + -1 + add + + + + + E + 4 + + 12 + + 1 + quarter + + + + + + + E + 4 + + 12 + 1 + quarter + + single + the + + + + + D + 4 + + 12 + 1 + quarter + + single + days + + + + + E + 4 + + 12 + 1 + quarter + + single + grow + + + + + + + D + + minor + + + + F + 4 + + 48 + + 1 + whole + + + + + single + long, + + + + + + + F + 4 + + 12 + + 1 + quarter + + + + + + + F + 4 + + 12 + 1 + quarter + + single + and + + + + + E + 4 + + 12 + 1 + quarter + + single + soon + + + + + F + 4 + + 12 + 1 + quarter + + single + I'll + + + + + + + G + + minor-seventh + + + + G + 4 + + 48 + + 1 + whole + + + + + single + hear + + + + + + + C + + dominant + + + + G + 4 + + 12 + + 1 + quarter + + + + + + + C + 4 + + 12 + 1 + quarter + + single + old + + + + + C + 5 + + 12 + 1 + quarter + + begin + win + + + + + B + -1 + 4 + + 12 + 1 + quarter + + single + ter's + + + + + + + F + + major-seventh + + + + A + 4 + + 48 + + 1 + whole + + + + + single + song. + + + + + + + A + 4 + + 12 + + 1 + quarter + + + + + + + 12 + 1 + quarter + + + + G + 1 + 4 + + 12 + 1 + quarter + + single + But + + + + + A + 4 + + 12 + 1 + quarter + + single + I + + + + + + + E + + half-diminished + + + + B + -1 + 4 + + 12 + 1 + quarter + + single + miss + + + + + B + -1 + 4 + + 12 + 1 + quarter + + single + you + + + + + G + 4 + + 12 + 1 + quarter + + single + most + + + + + G + 4 + + 12 + 1 + quarter + + single + of + + + + + + + A + + dominant + + 9 + -1 + add + + + + + E + 4 + + 36 + 1 + half + + + single + all, + + + + + B + -1 + 4 + + 12 + 1 + quarter + + single + my + + + + + + + D + + minor-seventh + + + + A + 4 + + 24 + 1 + half + + begin + dar + + + + + G + + dominant + + + + A + 4 + + 24 + + 1 + half + + + + + single + ling, + + + + + + + C + + minor-seventh + + + + A + 4 + + 24 + + 1 + half + + + + + + + F + + dominant + + + + D + 4 + + 24 + 1 + half + + single + when + + + + + + + E + + half-diminished + + + + G + 4 + + 36 + 1 + half + + + begin + au + + + + + F + 4 + + 12 + 1 + quarter + + single + tumn + + + + + + + A + + dominant + + 9 + -1 + add + + + + + E + 4 + + 24 + 1 + half + + single + leaves + + + + + F + 4 + + 12 + 1 + quarter + + single + start + + + + + A + 3 + + 12 + 1 + quarter + + single + to + + + + + + + D + + minor + + + + D + 4 + + 48 + 1 + whole + + single + fall. + + + + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + \ 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 deleted file mode 100644 index 5b30380..0000000 --- a/data/songs/it-could-happen-to-you.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "title": "It Could Happen to You", - "composer": "James Van Heusen / Johnny Burke", - "key": "Eb", - "time": "4/4", - "clef": "treble", - "part": "Vocal", - "role": "vocal", - "style": "Medium Swing", - "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" - ] -} diff --git a/data/songs/it-could-happen-to-you.musicxml b/data/songs/it-could-happen-to-you.musicxml new file mode 100644 index 0000000..3ed6910 --- /dev/null +++ b/data/songs/it-could-happen-to-you.musicxml @@ -0,0 +1,1549 @@ + + + + It Could Happen to You + + + James Van Heusen / Johnny Burke + + Medium Swing + + + + + Vocal + + + + + + 12 + + -3 + major + + + + G + 2 + + + + + E + -1 + + major-seventh + + + + B + -1 + 3 + + 36 + 1 + half + + + single + Hide + + + + + C + 4 + + 12 + 1 + quarter + + single + your + + + + + + + G + + half-diminished + + + + D + -1 + 4 + + 12 + 1 + quarter + + single + heart + + + + + C + 4 + + 6 + 1 + eighth + + single + from + + + + + G + 4 + + 6 + + 1 + eighth + + + + + single + sight, + + + + + C + + dominant + + 9 + -1 + add + + + + + G + 4 + + 24 + + 1 + half + + + + + + + + + F + + minor-seventh + + + + C + 4 + + 36 + 1 + half + + + single + lock + + + + + D + 4 + + 12 + 1 + quarter + + single + your + + + + + + + A + + half-diminished + + + + E + -1 + 4 + + 12 + 1 + quarter + + single + dreams + + + + + D + 4 + + 6 + 1 + eighth + + single + at + + + + + A + 4 + + 6 + + 1 + eighth + + + + + single + night, + + + + + D + + dominant + + 9 + -1 + add + + + + + A + 4 + + 24 + + 1 + half + + + + + + + + + E + -1 + + major-seventh + + + + B + -1 + 4 + + 24 + 1 + half + + single + it + + + + + B + -1 + 4 + + 24 + 1 + half + + single + could + + + + + + + A + -1 + + dominant + + + + B + -1 + 4 + + 12 + 1 + quarter + + begin + hap + + + + + A + -1 + 4 + + 24 + 1 + half + + single + pen + + + + + E + -1 + 4 + + 12 + 1 + quarter + + single + to + + + + + + + G + + half-diminished + + + + G + 4 + + 48 + 1 + whole + + single + you. + + + + + + + C + + dominant + + 9 + -1 + add + + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + + + F + + minor-seventh + + + + G + 4 + + 24 + 1 + half + + single + Don't + + + + + G + 4 + + 24 + 1 + half + + single + count + + + + + + + D + -1 + + dominant + + + + G + 4 + + 12 + 1 + quarter + + single + stars + + + + + A + -1 + 4 + + 12 + 1 + quarter + + single + or + + + + + E + -1 + 4 + + 12 + 1 + quarter + + single + you + + + + + F + 4 + + 6 + 1 + eighth + + single + might + + + + + G + 4 + + 6 + + 1 + eighth + + + + + begin + stum + + + + + + + E + -1 + + major-seventh + + + + G + 4 + + 24 + + 1 + half + + + + + + + G + 4 + + 24 + 1 + half + + single + ble, + + + + + + + D + + half-diminished + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + G + + dominant + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + + + C + + minor-seventh + + + + E + -1 + 4 + + 24 + 1 + half + + begin + some + + + + + E + -1 + 4 + + 24 + 1 + half + + single + one + + + + + + + F + + dominant + + + + E + -1 + 4 + + 12 + 1 + quarter + + single + drops + + + + + F + 4 + + 12 + 1 + quarter + + single + a + + + + + C + 4 + + 12 + 1 + quarter + + single + sigh + + + + + D + 4 + + 6 + 1 + eighth + + single + and + + + + + E + -1 + 4 + + 6 + + 1 + eighth + + + + + single + down + + + + + + + F + + minor-seventh + + + + E + -1 + 4 + + 24 + + 1 + half + + + + + + + F + 4 + + 24 + 1 + half + + single + you + + + + + + + B + -1 + + dominant + + + + C + 4 + + 18 + 1 + quarter + + + begin + tum + + + + + D + 4 + + 6 + + 1 + eighth + + + + + single + ble. + + + + + D + 4 + + 24 + + 1 + half + + + + + + + + + E + -1 + + major-seventh + + + + B + -1 + 3 + + 36 + 1 + half + + + single + Keep + + + + + C + 4 + + 12 + 1 + quarter + + single + an + + + + + + + G + + half-diminished + + + + D + -1 + 4 + + 12 + 1 + quarter + + single + eye + + + + + C + 4 + + 6 + 1 + eighth + + single + on + + + + + G + 4 + + 6 + + 1 + eighth + + + + + single + spring, + + + + + C + + dominant + + 9 + -1 + add + + + + + G + 4 + + 24 + + 1 + half + + + + + + + + + F + + minor-seventh + + + + C + 4 + + 36 + 1 + half + + + single + run + + + + + D + 4 + + 12 + 1 + quarter + + single + when + + + + + + + A + + minor-seventh + + 9 + -1 + add + + + + + E + -1 + 4 + + 12 + 1 + quarter + + single + church + + + + + D + 4 + + 6 + 1 + eighth + + single + bells + + + + + A + 4 + + 6 + + 1 + eighth + + + + + single + ring, + + + + + D + + dominant + + 9 + -1 + add + + + + + A + 4 + + 24 + + 1 + half + + + + + + + + + E + -1 + + major-seventh + + + + B + -1 + 4 + + 24 + 1 + half + + single + it + + + + + B + -1 + 4 + + 24 + 1 + half + + single + could + + + + + + + A + -1 + + dominant + + + + B + -1 + 4 + + 12 + 1 + quarter + + begin + hap + + + + + A + -1 + 4 + + 24 + 1 + half + + single + pen + + + + + E + -1 + 4 + + 12 + 1 + quarter + + single + to + + + + + + + G + + half-diminished + + + + G + 4 + + 48 + 1 + whole + + single + you. + + + + + + + C + + dominant + + 9 + -1 + add + + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + + + F + + minor-seventh + + + + G + 4 + + 24 + 1 + half + + single + All + + + + + G + 4 + + 24 + 1 + half + + single + I + + + + + + + D + -1 + + dominant + + + + G + 4 + + 12 + 1 + quarter + + single + did + + + + + A + -1 + 4 + + 12 + 1 + quarter + + single + was + + + + + E + -1 + 4 + + 12 + 1 + quarter + + begin + won + + + + + F + 4 + + 6 + 1 + eighth + + single + der + + + + + G + 4 + + 6 + + 1 + eighth + + + + + single + how + + + + + + + E + -1 + + major-seventh + + + + G + 4 + + 12 + + 1 + quarter + + + + + + + D + 4 + + 12 + 1 + quarter + + single + your + + + + + A + -1 + + dominant + + + + F + 4 + + 12 + 1 + quarter + + single + arms + + + + + E + -1 + 4 + + 6 + 1 + eighth + + single + would + + + + + B + -1 + 4 + + 6 + + 1 + eighth + + + + + single + be, + + + + + + + G + + half-diminished + + + + B + -1 + 4 + + 48 + + 1 + whole + + + + + + + + + F + + minor-seventh + + + + B + -1 + 3 + + 24 + 1 + half + + single + and + + + + + B + -1 + 3 + + 24 + 1 + half + + single + it + + + + + + + B + -1 + + dominant + + + + A + -1 + 4 + + 12 + 1 + quarter + + begin + hap + + + + + F + 4 + + 24 + 1 + half + + single + pened + + + + + G + 4 + + 12 + 1 + quarter + + single + to + + + + + + + E + -1 + + major-seventh + + + + E + -1 + 4 + + 48 + 1 + whole + + single + me. + + + + + + + vamp:optional-chord + + + + + F + + minor-seventh + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + vamp:optional-chord + + + + + B + -1 + + dominant + + + + 12 + 1 + quarter + + + + 12 + 1 + quarter + + + + \ No newline at end of file diff --git a/generator/__main__.py b/generator/__main__.py index d2969a3..c044ec9 100644 --- a/generator/__main__.py +++ b/generator/__main__.py @@ -2,12 +2,14 @@ from __future__ import annotations import argparse import shutil +import tempfile from pathlib import Path from generator.arrange.generate import generate_performance_scores from generator.practice.generate import generate_practice_scores from generator.score import count_json_files, validate_generated_tree from generator.summary import validate_expected_performance_summary, validate_practice_variants +from generator.musicxml import load_musicxml RUNTIME_FONT_FILES = ("Petaluma.otf", "PetalumaScript.otf", "OFL.txt") @@ -30,33 +32,43 @@ def parse_args() -> argparse.Namespace: return parser.parse_args() -def main() -> int: - args = parse_args() +def generate_assets(args: argparse.Namespace) -> None: data_dir = args.data_dir.resolve() output_dir = args.output_dir.resolve() static_assets_dir = args.static_assets_dir.resolve() - if args.clean and output_dir.exists(): - shutil.rmtree(output_dir) - output_dir.mkdir(parents=True, exist_ok=True) + for source in sorted(data_dir.rglob('*.musicxml')): + load_musicxml(source) - copy_runtime_fonts(static_assets_dir, output_dir) + output_dir.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(prefix='.vamp-assets-', dir=output_dir.parent) as temporary: + staged = Path(temporary) / 'assets' + if not args.clean and output_dir.exists(): + shutil.copytree(output_dir, staged) + staged.mkdir(parents=True, exist_ok=True) + copy_runtime_fonts(static_assets_dir, staged) + performance_count = generate_performance_scores(data_dir / 'songs', staged / 'songs') + practice_count = generate_practice_scores(data_dir / 'practice', staged / 'practice') + validate_generated_tree(staged) + if args.check: + validate_expected_performance_summary(data_dir / 'expected' / 'performance_summary.json', staged / 'songs') + validate_practice_variants(staged / 'practice') + backup = Path(temporary) / 'previous' + if output_dir.exists(): + output_dir.rename(backup) + try: + staged.rename(output_dir) + except OSError: + if backup.exists(): + backup.rename(output_dir) + raise + print(f'Generated Product Vamp assets: {performance_count} performance scores, ' + f'{practice_count} practice score variants, {count_json_files(output_dir)} total JSON files.') + if args.check: + print('Generator check passed.') - performance_count = generate_performance_scores(data_dir / "songs", output_dir / "songs") - practice_count = generate_practice_scores(data_dir / "practice", output_dir / "practice") - - validate_generated_tree(output_dir) - print( - "Generated Product Vamp assets: " - f"{performance_count} performance scores, " - f"{practice_count} practice score variants, " - f"{count_json_files(output_dir)} total JSON files." - ) - - if args.check: - validate_expected_performance_summary(data_dir / "expected" / "performance_summary.json", output_dir / "songs") - validate_practice_variants(output_dir / "practice") - print("Generator check passed.") +def main() -> int: + generate_assets(parse_args()) return 0 diff --git a/generator/arrange/fretboard.py b/generator/arrange/fretboard.py index 02ae65d..4b4ff09 100644 --- a/generator/arrange/fretboard.py +++ b/generator/arrange/fretboard.py @@ -4,6 +4,8 @@ from itertools import product from typing import NamedTuple from generator.transposition import mod12 +from generator import harmony +from generator.model import NATURAL STANDARD_TUNING = ((6, 40), (5, 45), (4, 50), (3, 55), (2, 59), (1, 64)) WRITTEN_GUITAR_OCTAVE_OFFSET = 12 @@ -15,134 +17,8 @@ class TabPosition(NamedTuple): midi: int -class ChordSymbol(NamedTuple): - root: str - quality: str - bass: str - - -ROOTS = { - "C": 0, - "C#": 1, - "Db": 1, - "D": 2, - "D#": 3, - "Eb": 3, - "E": 4, - "F": 5, - "F#": 6, - "Gb": 6, - "G": 7, - "G#": 8, - "Ab": 8, - "A": 9, - "A#": 10, - "Bb": 10, - "B": 11, -} - - -def parse_root(symbol: str) -> tuple[str, str] | None: - symbol = symbol.strip() - if not symbol or symbol[0].upper() not in "ABCDEFG": - return None - length = 1 - if len(symbol) > 1 and symbol[1] in {"#", "b"}: - length = 2 - return symbol[:length], symbol[length:] - - -def parse_chord_symbol(symbol: str) -> ChordSymbol | None: - clean = symbol.strip().replace(" ", "") - base, _, slash = clean.partition("/") - parsed = parse_root(base) - if parsed is None: - return None - root, quality = parsed - bass = "" - if slash: - bass_parsed = parse_root(slash) - if bass_parsed is not None: - bass = bass_parsed[0] - return ChordSymbol(root, quality, bass) - - -def unique_intervals(values: tuple[int, ...]) -> tuple[int, ...]: - result: list[int] = [] - seen: set[int] = set() - for value in values: - interval = mod12(value) - if interval in seen: - continue - seen.add(interval) - result.append(interval) - return tuple(result) - - -def color_intervals(value: str) -> tuple[int, ...]: - colors: list[int] = [] - if "alt" in value: - colors.append(1) - if "b9" in value: - colors.append(1) - if "#9" in value: - colors.append(3) - if "9" in value and "b9" not in value and "#9" not in value: - colors.append(2) - if "#11" in value: - colors.append(6) - elif "11" in value: - colors.append(5) - if "b13" in value: - colors.append(8) - elif "13" in value: - colors.append(9) - return tuple(colors) - - -def chord_intervals(quality: str) -> tuple[int, ...]: - value = quality.lower().replace(" ", "") - if "7b5" in value or "ø" in value or "half" in value: - return (0, 3, 6, 10) - if "dim" in value or "o" in value: - return (0, 3, 6, 9) - colors = color_intervals(value) - if "aug" in value or "+" in value: - return unique_intervals((0, 4, 8, 10) + colors) if ("7" in value or colors) else (0, 4, 8) - if "sus" in value: - return unique_intervals((0, 5, 10) + colors) if ("7" in value or colors) else (0, 5, 7) - minor = value.startswith("-") or (value.startswith("m") and not value.startswith(("maj", "ma"))) - if minor: - if "6" in value and "7" not in value: - return unique_intervals((0, 3, 7, 9) + colors) - if "7" in value or colors: - return unique_intervals((0, 3, 10) + colors) - return (0, 3, 7) - if "maj" in value or value.startswith("ma") or "Δ" in value: - return unique_intervals((0, 4, 11) + colors) - if "6" in value and "7" not in value: - return unique_intervals((0, 4, 7, 9) + colors) - if "7" in value or colors: - return unique_intervals((0, 4, 10) + colors) - return (0, 4, 7) - - -def pitch_class_for_root(root: str) -> int | None: - return ROOTS.get(root[0].upper() + root[1:], ROOTS.get(root.upper())) - - -def chord_pitch_classes(symbol: str) -> tuple[int, ...]: - parsed = parse_chord_symbol(symbol) - if parsed is None: - return () - root_pc = pitch_class_for_root(parsed.root) - if root_pc is None: - return () - pitch_classes = [mod12(root_pc + interval) for interval in chord_intervals(parsed.quality)] - bass_pc = pitch_class_for_root(parsed.bass) if parsed.bass else None - if bass_pc is not None and bass_pc not in pitch_classes: - pitch_classes.append(bass_pc) - return tuple(pitch_classes) +def chord_pitch_classes(symbol: str | dict) -> tuple[int, ...]: + return harmony.pitch_classes(harmony.parse_symbol(symbol) if isinstance(symbol, str) else symbol) def positions_for_string(string_number: int, open_midi: int, pitch_classes: tuple[int, ...]) -> list[TabPosition]: @@ -168,8 +44,23 @@ def candidate_score(candidate: tuple[TabPosition, ...], pitch_classes: tuple[int return missing * 1000 + span * 80 + abs(low - 5) * 12 + open_penalty * 8 + sum(position.fret for position in candidate) -def find_playable_voicing(symbol: str) -> list[TabPosition]: - pitch_classes = chord_pitch_classes(symbol) +def find_playable_voicing(symbol: str | dict) -> list[TabPosition]: + definition = harmony.parse_symbol(symbol) if isinstance(symbol, str) else symbol + semantic_degrees = harmony.degrees(definition) + voicing = dict(semantic_degrees) + if 7 in voicing and voicing.get(5) == 0: + del voicing[5] + if len(voicing) > 4: + for degree in (11, 9, 5): + if len(voicing) > 4 and voicing.get(degree) == 0: + del voicing[degree] + root = definition.get('root', {'letter':'c','alter':0}) + root_pc = NATURAL[root['letter']] + root['alter'] + pitch_classes = tuple(dict.fromkeys((root_pc + harmony.degree_semitones(d,a)) % 12 for d,a in voicing.items())) + bass = definition.get('bass') + bass_pc = (NATURAL[bass['letter']] + bass['alter']) % 12 if bass else None + if bass_pc is not None and bass_pc not in pitch_classes: + pitch_classes += (bass_pc,) if not pitch_classes: return [] @@ -182,11 +73,13 @@ def find_playable_voicing(symbol: str) -> list[TabPosition]: if any(not item for item in options): continue for candidate in product(*options): + if bass_pc is not None and min(candidate, key=lambda p:p.midi).midi % 12 != bass_pc: + continue fretted = [position.fret for position in candidate if position.fret > 0] if fretted and max(fretted) - min(fretted) > 4: continue covered = {mod12(position.midi) for position in candidate} - if len(covered & set(pitch_classes)) < min(3, len(set(pitch_classes))): + if not set(pitch_classes).issubset(covered): continue score = candidate_score(candidate, pitch_classes) if score < best_score: @@ -195,24 +88,20 @@ def find_playable_voicing(symbol: str) -> list[TabPosition]: return sorted(best or [], key=lambda item: item.midi) -def pitch_from_midi(midi: int) -> dict[str, int | str]: - names = ( - ("c", "n"), - ("c", "#"), - ("d", "n"), - ("e", "b"), - ("e", "n"), - ("f", "n"), - ("f", "#"), - ("g", "n"), - ("a", "b"), - ("a", "n"), - ("b", "b"), - ("b", "n"), - ) - letter, accidental = names[mod12(midi)] - return {"letter": letter, "accidental": accidental, "octave": midi // 12 - 1} - - -def pitches_for_voicing(symbol: str) -> list[dict[str, int | str]]: - return [pitch_from_midi(position.midi + WRITTEN_GUITAR_OCTAVE_OFFSET) for position in find_playable_voicing(symbol)] +def pitches_for_voicing(symbol: str | dict) -> list[dict[str, int | str]]: + definition = harmony.parse_symbol(symbol) if isinstance(symbol, str) else symbol + from generator.transposition import shift_pitch + root = definition.get('root', {'letter':'c', 'alter':0}) + spellings = {} + for degree, alter in harmony.degrees(definition).items(): + p = shift_pitch(root, degree-1, harmony.degree_semitones(degree, alter)) + spellings[(NATURAL[p['letter']]+p['alter']) % 12] = p + if bass := definition.get('bass'): + spellings[(NATURAL[bass['letter']]+bass['alter']) % 12] = bass + result = [] + for position in find_playable_voicing(definition): + midi = position.midi + WRITTEN_GUITAR_OCTAVE_OFFSET + p = spellings[midi % 12].copy() + p['octave'] = (midi - NATURAL[p['letter']] - p['alter']) // 12 - 1 + result.append(p) + return result diff --git a/generator/arrange/generate.py b/generator/arrange/generate.py index ef0b29c..40b318c 100644 --- a/generator/arrange/generate.py +++ b/generator/arrange/generate.py @@ -1,279 +1,180 @@ from __future__ import annotations +import copy +import warnings +from fractions import Fraction from pathlib import Path -from generator.arrange.form import ArrangementSection, full_arrangement_sections +from generator.arrange.form import full_arrangement_sections from generator.arrange.fretboard import pitches_for_voicing -from generator.score import ( - QUARTER_TICKS, - apply_source_chord_markers, - active_chord_at_offset, - chord_markers, - chord_symbol_at_offset, - copy_source_voice, - first_chord_symbol, - first_voice, - leading_pickup_measure_index, - load_json, - make_arranged_document, - make_measure, - measure_capacity_ticks, - renderable_duration, - renderable_rest_notes, - rest_note, - sanitized_note, - slash_note, - slugify, - source_staff, - source_note_attack_at, - write_json, -) - -PART_TYPES = ("acoustic", "electric", "vocal") - +from generator.model import beat_lengths, fraction, key_label, make_event, meter_label, rational +from generator.musicxml import load_musicxml +from generator.transposition import concert_document, shift_pitch +from generator.score import copy_source_voice, make_arranged_document, make_measure, measure_notes, reidentify, slugify, source_staff, write_json +PART_TYPES = ('acoustic','electric','vocal') FINAL_SECTION_INDEX = 6 -def propagated_chords(measures: list[dict], *, include_optional: bool = True) -> list[str]: - result: list[str] = [] - current = "" +def propagated_chords(measures: list[dict], *, include_optional: bool = True) -> list[dict | None]: + result = [] + current = None for measure in measures: - current = first_chord_symbol(measure, current, include_optional=include_optional) result.append(current) + for h in measure.get('harmonies', []): + if include_optional or not h.get('chord_optional'): + current = h return result -def playable_chord_note(note_id: str, duration_ticks: int, voicing_chord_symbol: str, visible_chord_symbol: str = "") -> dict: - note = { - "id": note_id, - "kind": "note", - "duration_ticks": duration_ticks, - "pitches": pitches_for_voicing(voicing_chord_symbol), - "hidden": False, - } - if not note["pitches"]: - return rest_note(note_id, duration_ticks, visible_chord_symbol) - if visible_chord_symbol: - note["chord_symbol"] = visible_chord_symbol - return note - - -def rhythm_notes(measure: dict, chord_symbol: str, sequence: int, *, include_optional: bool = True) -> tuple[list[dict], int]: - capacity = measure_capacity_ticks(str(measure.get("time_signature", "4/4"))) - hit_offsets = (0, QUARTER_TICKS + QUARTER_TICKS // 2, QUARTER_TICKS * 2, QUARTER_TICKS * 2 + QUARTER_TICKS // 2, QUARTER_TICKS * 3 + QUARTER_TICKS // 2) - hit_duration = QUARTER_TICKS // 2 - notes: list[dict] = [] - cursor = 0 - for offset in hit_offsets: - if offset >= capacity: +def active_chord(measure: dict, onset: Fraction, previous: dict | None) -> dict | None: + active = previous + for h in measure['harmonies']: + if fraction(h['onset']) > onset: break - if offset > cursor: - rests, sequence = renderable_rest_notes("arr-rest", offset - cursor, sequence) - notes.extend(rests) - cursor = offset - active_chord = active_chord_at_offset(measure, offset, chord_symbol, include_optional=include_optional) - visible_chord = chord_symbol_at_offset(measure, offset, include_optional=include_optional) - sequence += 1 - notes.append(playable_chord_note(f"arr-note-{sequence}", min(hit_duration, capacity - offset), active_chord, visible_chord)) - cursor = offset + min(hit_duration, capacity - offset) - if cursor < capacity: - rests, sequence = renderable_rest_notes("arr-rest", capacity - cursor, sequence) - notes.extend(rests) - return apply_source_chord_markers(notes, measure, include_optional=include_optional), sequence + active = h + return active if active and active['kind'] != 'none' else None -def accompaniment_notes(measure: dict, chord_symbol: str, sequence: int, *, include_optional: bool = True) -> tuple[list[dict], int]: - capacity = measure_capacity_ticks(str(measure.get("time_signature", "4/4"))) - notes: list[dict] = [] - lead_rest = min(QUARTER_TICKS // 2, capacity) - if lead_rest: - rests, sequence = renderable_rest_notes("arr-rest", lead_rest, sequence, chord_symbol_at_offset(measure, 0, include_optional=include_optional)) - notes.extend(rests) - if lead_rest < capacity: - hit = min(QUARTER_TICKS // 2, capacity - lead_rest) - visible_chord = chord_symbol_at_offset(measure, lead_rest, include_optional=include_optional) - active_chord = active_chord_at_offset(measure, lead_rest, chord_symbol, include_optional=include_optional) - sequence += 1 - if source_note_attack_at(measure, lead_rest): - notes.append(rest_note(f"arr-rest-{sequence}", hit, visible_chord)) - else: - notes.append(playable_chord_note(f"arr-note-{sequence}", hit, active_chord, visible_chord)) - cursor = lead_rest + hit - if cursor < capacity: - markers = [marker for marker in chord_markers(measure, include_optional=include_optional) if marker[0] >= cursor] - if markers: - segment_cursor = cursor - for index, (start, _, chord) in enumerate(markers): - if start > segment_cursor: - rests, sequence = renderable_rest_notes("arr-rest", start - segment_cursor, sequence) - notes.extend(rests) - next_start = markers[index + 1][0] if index + 1 < len(markers) else capacity - rests, sequence = renderable_rest_notes("arr-rest", next_start - start, sequence, chord) - notes.extend(rests) - segment_cursor = next_start - if segment_cursor < capacity: - rests, sequence = renderable_rest_notes("arr-rest", capacity - segment_cursor, sequence) - notes.extend(rests) - else: - rests, sequence = renderable_rest_notes("arr-rest", capacity - cursor, sequence) - notes.extend(rests) - return apply_source_chord_markers(notes, measure, include_optional=include_optional), sequence - - -def slash_notes(measure: dict, chord_symbol: str, sequence: int, *, include_optional: bool = True) -> tuple[list[dict], int]: - capacity = measure_capacity_ticks(str(measure.get("time_signature", "4/4"))) - notes: list[dict] = [] - cursor = 0 - while cursor < capacity: - sequence += 1 - duration = min(QUARTER_TICKS, capacity - cursor) - notes.append(slash_note(f"arr-slash-{sequence}", duration)) - cursor += duration - return apply_source_chord_markers(notes, measure, include_optional=include_optional), sequence - - -def rest_notes(measure: dict, chord_symbol: str, sequence: int, *, include_optional: bool = True) -> tuple[list[dict], int]: - capacity = measure_capacity_ticks(str(measure.get("time_signature", "4/4"))) - markers = chord_markers(measure, include_optional=include_optional) - if not markers: - notes, sequence = renderable_rest_notes("arr-rest", capacity, sequence) - return notes, sequence - - notes: list[dict] = [] - cursor = 0 - for index, (start, _, chord) in enumerate(markers): - if start > cursor: - if not renderable_duration(start - cursor): - return source_rest_skeleton_notes(measure, sequence, include_optional=include_optional) - rests, sequence = renderable_rest_notes("arr-rest", start - cursor, sequence) - notes.extend(rests) - next_start = markers[index + 1][0] if index + 1 < len(markers) else capacity - if not renderable_duration(next_start - start): - return source_rest_skeleton_notes(measure, sequence, include_optional=include_optional) - rests, sequence = renderable_rest_notes("arr-rest", next_start - start, sequence, chord) - notes.extend(rests) - cursor = next_start - if cursor < capacity: - if not renderable_duration(capacity - cursor): - return source_rest_skeleton_notes(measure, sequence, include_optional=include_optional) - rests, sequence = renderable_rest_notes("arr-rest", capacity - cursor, sequence) - notes.extend(rests) - return notes, sequence - - -def source_rest_skeleton_notes(measure: dict, sequence: int, *, include_optional: bool = True) -> tuple[list[dict], int]: - notes: list[dict] = [] - for source_note in first_voice(measure).get("notes", []): - if not isinstance(source_note, dict): - continue - sequence += 1 - chord = "" if not include_optional and source_note.get("chord_optional") is True else str(source_note.get("chord_symbol", "")) - note = rest_note(f"arr-rest-{sequence}", int(source_note.get("duration_ticks", QUARTER_TICKS)), chord) - if "display_ticks" in source_note: - note["display_ticks"] = source_note["display_ticks"] - if "tuplet_id" in source_note: - note["tuplet_id"] = source_note["tuplet_id"] - notes.append(note) - if not notes: - sequence += 1 - notes.append(rest_note(f"arr-rest-{sequence}", measure_capacity_ticks(str(measure.get("time_signature", "4/4"))))) - return notes, sequence - - -def melody_voice(measure: dict, *, strip_lyrics: bool, include_optional: bool = True) -> dict: - voice = copy_source_voice(measure, strip_lyrics=strip_lyrics, include_optional=include_optional) - voice["id"] = "voice-arranged-source" - voice["notes"] = [sanitized_note(note, strip_lyrics=strip_lyrics, include_optional=include_optional) for note in first_voice(measure).get("notes", []) if isinstance(note, dict)] - return voice - - -def apply_vocal_pickup_overlap(output: list[dict], source_measures: list[dict], section: ArrangementSection) -> None: - pickup = leading_pickup_measure_index(source_measures) - if pickup is None or section.source_measure_index != pickup + 1 or not output: - return - voice = copy_source_voice(source_measures[pickup], strip_lyrics=False) - for note in voice["notes"]: - if isinstance(note, dict): - note.pop("chord_symbol", None) - output[-1]["voices"][0]["id"] = "voice-arranged-vocal" - output[-1]["voices"][0]["tuplets"] = voice["tuplets"] - output[-1]["voices"][0]["notes"] = voice["notes"] - - -def voice_for_section(section: ArrangementSection, measure: dict, chord_symbol: str, sequence: int, *, include_optional: bool = True) -> tuple[dict, int]: - if section.role == "vocal": - return melody_voice(measure, strip_lyrics=False, include_optional=include_optional), sequence - if section.role == "melody": - return melody_voice(measure, strip_lyrics=True, include_optional=include_optional), sequence - if section.role == "rhythm": - notes, sequence = rhythm_notes(measure, chord_symbol, sequence, include_optional=include_optional) - elif section.role == "accompaniment": - notes, sequence = accompaniment_notes(measure, chord_symbol, sequence, include_optional=include_optional) - elif section.role == "slash": - notes, sequence = slash_notes(measure, chord_symbol, sequence, include_optional=include_optional) +def generated_voice(measure: dict, role: str, previous: dict | None) -> list[dict]: + length = fraction(measure['duration']) + if role == 'rest': + return [make_event('rest', Fraction(), length, 'rest', written={'type':'whole','dots':0}) | {'measure_rest':True}] + if role == 'rhythm' and measure['time_signature']=='4/4' and not measure.get('attributes'): + attacks = [(Fraction(n,2), Fraction(1,2)) for n in (0,3,4,5,7)] + elif role == 'accompaniment' and measure['time_signature']=='4/4' and not measure.get('attributes'): + attacks = [(Fraction(1,2),Fraction(1,2))] else: - notes, sequence = rest_notes(measure, chord_symbol, sequence, include_optional=include_optional) - return {"id": "voice-arranged-guitar", "tuplets": [], "notes": notes}, sequence - - -def arranged_measures(source_measures: list[dict], part_type: str) -> list[dict]: - sections = full_arrangement_sections(source_measures, part_type) - all_chords = propagated_chords(source_measures) - required_chords = propagated_chords(source_measures, include_optional=False) - output: list[dict] = [] - sequence = 0 - for section in sections: - include_optional = section.section_index != FINAL_SECTION_INDEX - chords = all_chords if include_optional else required_chords - if part_type == "vocal" and section.role == "vocal": - apply_vocal_pickup_overlap(output, source_measures, section) - for offset in range(section.measure_count): - source_index = section.source_measure_index + offset - source_measure = source_measures[source_index] - chord_symbol = chords[source_index] if source_index < len(chords) else "" - voice, sequence = voice_for_section(section, source_measure, chord_symbol, sequence, include_optional=include_optional) - output_measure = make_measure( - f"measure-{len(output) + 1}", - source_measure, - voice.get("notes", []), - section_label=section.label if offset == 0 else "", - tuplets=voice.get("tuplets", []), - ) - output_measure["voices"][0]["id"] = str(voice.get("id", output_measure["voices"][0]["id"])) - output.append(output_measure) - return output + attacks = [] + states = [(Fraction(), measure['meter'])] + states.extend((fraction(a['onset']), a['meter']) for a in measure.get('attributes', []) if 'meter' in a) + for index, (onset, meter) in enumerate(states): + end = states[index+1][0] if index+1 < len(states) else length + beats = beat_lengths(meter) + beat_index = 0 + while onset < end: + beat = min(beats[beat_index % len(beats)] if beats else end-onset, end-onset) + duration = beat if role == 'slash' or not beats else min(beat/2, Fraction(1,2)) + attacks.append((onset, duration)) + onset += beat + beat_index += 1 + events = [] + cursor = Fraction() + for onset, duration in attacks: + if onset >= length: + break + if onset > cursor: + events.append(make_event(f'rest-{len(events)}',cursor,onset-cursor,'rest')) + duration = min(duration,length-onset) + chord = active_chord(measure,onset,previous) + source_attack = any(fraction(n['onset'])==onset and n['kind']=='note' and not n.get('grace') and any(not p.get('tie_from') for p in n['pitches']) for n in measure_notes(measure)) + if role=='slash': + event = make_event(f'slash-{len(events)}',onset,duration,'note') + event['notehead'] = 'slash' + elif not chord or role=='accompaniment' and source_attack: + event = make_event(f'rest-{len(events)}',onset,duration,'rest') + else: + pitches = pitches_for_voicing(chord) + event = make_event(f'chord-{len(events)}',onset,duration,'note',pitches) + if not pitches: + warnings.warn(f'{measure["id"]}: no playable voicing for {chord["symbol"]}; using labelled rhythmic slashes', stacklevel=2) + event['notehead'] = 'slash' + events.append(event) + cursor = onset + duration + if cursor < length: + events.append(make_event(f'rest-{len(events)}',cursor,length-cursor,'rest')) + return events def arrange_document(document: dict, part_type: str) -> dict: + if part_type not in PART_TYPES: + raise ValueError(f'unknown arrangement part: {part_type}') + document = concert_document(document) staff = source_staff(document) - measures = staff.get("measures") - if not isinstance(measures, list) or not measures: - raise ValueError("performance arrangement requires at least one source measure") - output = make_arranged_document( - document, - staff, - staff_id="staff-arranged-vocal" if part_type == "vocal" else "staff-arranged-guitar", - staff_name=part_type.capitalize(), - role="vocal" if part_type == "vocal" else "guitar", - arrangement_part_type=part_type, - measures=arranged_measures([measure for measure in measures if isinstance(measure, dict)], part_type), - ) - metadata = output.setdefault("metadata", {}) - if isinstance(metadata, dict): - metadata.setdefault("tempo", 124) - return output + source = staff['measures'] + sections = full_arrangement_sections(source,part_type) + output, tuplets, octave_shifts = [], [], [] + absolute = Fraction() + source_starts = [] + source_time = Fraction() + for m in source: + source_starts.append(source_time) + source_time += fraction(m['duration']) + for section in sections: + previous = propagated_chords(source, include_optional=section.section_index!=FINAL_SECTION_INDEX) + segment = [] + for si in range(section.source_measure_index,section.source_measure_index+section.measure_count): + m = copy.deepcopy(source[si]) + m['harmonies'] = [h for h in m['harmonies'] if section.section_index!=FINAL_SECTION_INDEX or not h.get('chord_optional')] + for h in m['harmonies']: + h.pop('chord_optional',None) + notes = copy_source_voice(m,strip_lyrics=section.role=='melody')['notes'] if section.role in ('vocal','melody') else generated_voice(m,section.role,previous[si]) + if section.role == 'melody': + for note in notes: + note['pitches'] = [shift_pitch(p,7,12) for p in note['pitches']] + result = make_measure(f'measure-{len(output)+len(segment)+1}',m,notes,section_label=section.label if not segment else '') + if part_type!='vocal': + result['clef'] = {'sign':'G','line':2,'octave_change':0} + result['transpose'] = {'diatonic':0,'chromatic':0,'octave_change':-1} + for change in result.get('attributes',[]): + change.pop('clef',None) + change.pop('transpose',None) + segment.append(result) + identity_segment = segment + segment_length = sum((fraction(m['duration']) for m in segment),Fraction()) + if section.role in ('vocal','melody'): + start = source_starts[section.source_measure_index] + for shift in staff.get('octave_shifts',[]): + low, high = max(start,fraction(shift['start'])), min(start+segment_length,fraction(shift['end'])) + if low < high: + octave_shifts.append({**shift,'id':f'section-{section.section_index}-{shift["id"]}','start':rational(absolute+low-start),'end':rational(absolute+high-start)}) + if part_type=='vocal' and section.section_index in (1,5) and section.source_measure_index==1 and source[0].get('implicit') and output: + pickup = copy.deepcopy(source[0]) + destination = output[-1] + pad = fraction(destination['duration']) - fraction(pickup['duration']) + if pad < 0: + raise ValueError('pickup is longer than the preceding arrangement measure') + notes = copy_source_voice(pickup,strip_lyrics=False)['notes'] + for n in notes: + n['onset'] = rational(fraction(n['onset'])+pad) + if pad: + notes.insert(0,make_event('pickup-rest',Fraction(),pad,'rest')) + destination['voices'][0]['notes'] = notes + identity_segment = [destination, *segment] + if pickup['harmonies']: + destination['harmonies'] = [h for h in destination['harmonies'] if fraction(h['onset']) < pad] + for h in pickup['harmonies']: + h['onset'] = rational(fraction(h['onset'])+pad) + destination['harmonies'].append(h) + state = {k:copy.deepcopy(pickup[k]) for k in ('key','clef','meter','transpose')} + if pad: + destination['attributes'].append({'onset':rational(pad), **state}) + else: + destination.update(state) + destination['key_signature'] = key_label(state['key']) + destination['time_signature'] = meter_label(state['meter']) + for change in pickup.get('attributes',[]): + destination['attributes'].append({**change,'onset':rational(fraction(change['onset'])+pad)}) + for shift in staff.get('octave_shifts',[]): + low, high = fraction(shift['start']), min(fraction(pickup['duration']),fraction(shift['end'])) + if low < high: + offset = absolute-fraction(pickup['duration']) + octave_shifts.append({**shift,'id':f'pickup-{section.section_index}-{shift["id"]}','start':rational(offset+low),'end':rational(offset+high)}) + tuplets.extend(reidentify(identity_segment,staff.get('tuplets',[]))) + output.extend(segment) + absolute += segment_length + result = make_arranged_document(document,staff,staff_id='staff-arranged-'+part_type,staff_name=part_type.capitalize(),role='vocal' if part_type=='vocal' else 'guitar',arrangement_part_type=part_type,measures=output,tuplets=tuplets,octave_shifts=octave_shifts) + result['metadata'].setdefault('tempo',124) + return result def generate_performance_scores(source_dir: Path, output_dir: Path) -> int: if not source_dir.exists(): - raise ValueError(f"performance source directory not found: {source_dir}") + raise ValueError(f'performance source directory not found: {source_dir}') count = 0 - for source_path in sorted(source_dir.glob("*.json")): - document = load_json(source_path) - for part_type in PART_TYPES: - output = arrange_document(document, part_type) - target = output_dir / f"{slugify(source_path.stem)}-{part_type}.json" - write_json(target, output) + for path in sorted(source_dir.glob('*.musicxml')): + document = load_musicxml(path) + for part in PART_TYPES: + write_json(output_dir/f'{slugify(path.stem)}-{part}.json',arrange_document(document,part)) count += 1 return count diff --git a/generator/harmony.py b/generator/harmony.py new file mode 100644 index 0000000..0446caa --- /dev/null +++ b/generator/harmony.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +from generator.model import NATURAL, parse_pitch_name, pitch_name + +# Semantic degrees are independent of guitar voicing omissions. +KINDS = { + 'major': {1:0, 3:0, 5:0}, 'minor': {1:0, 3:-1, 5:0}, + 'augmented': {1:0, 3:0, 5:1}, 'diminished': {1:0, 3:-1, 5:-1}, + 'dominant': {1:0, 3:0, 5:0, 7:-1}, 'major-seventh': {1:0, 3:0, 5:0, 7:0}, + 'minor-seventh': {1:0, 3:-1, 5:0, 7:-1}, 'diminished-seventh': {1:0, 3:-1, 5:-1, 7:-2}, + 'augmented-seventh': {1:0, 3:0, 5:1, 7:-1}, 'augmented-major-seventh': {1:0, 3:0, 5:1, 7:0}, + 'half-diminished': {1:0, 3:-1, 5:-1, 7:-1}, 'major-minor': {1:0, 3:-1, 5:0, 7:0}, + 'major-sixth': {1:0, 3:0, 5:0, 6:0}, 'minor-sixth': {1:0, 3:-1, 5:0, 6:0}, + 'suspended-second': {1:0, 2:0, 5:0}, 'suspended-fourth': {1:0, 4:0, 5:0}, + 'power': {1:0, 5:0}, 'pedal': {1:0}, 'other': {}, 'none': {}, + 'Neapolitan': {1:0, 3:0, 5:0}, 'Italian': {1:0, 3:0, 6:1}, + 'French': {1:0, 3:0, 4:1, 6:1}, 'German': {1:0, 3:0, 5:0, 6:1}, +} +for family, base in (('dominant', 'dominant'), ('major', 'major-seventh'), ('minor', 'minor-seventh')): + for suffix, highest in (('ninth',9), ('11th',11), ('13th',13)): + KINDS[f'{family}-{suffix}'] = {**KINDS[base], **{i:0 for i in range(9, highest+1, 2)}} + +SUFFIXES = {'major':'', 'minor':'-', 'augmented':'+', 'diminished':'dim', 'dominant':'7', 'major-seventh':'maj7', 'minor-seventh':'-7', 'diminished-seventh':'dim7', 'augmented-seventh':'+7', 'augmented-major-seventh':'+maj7', 'half-diminished':'-7b5', 'major-minor':'-maj7', 'major-sixth':'6', 'minor-sixth':'-6', 'suspended-second':'sus2', 'suspended-fourth':'sus4', 'power':'5', 'pedal':'', 'other':''} +for family, prefix in (('dominant',''), ('major','maj'), ('minor','-')): + for suffix, number in (('ninth','9'), ('11th','11'), ('13th','13')): + SUFFIXES[f'{family}-{suffix}'] = prefix + number + + +def degrees(harmony: dict) -> dict[int, int]: + kind = harmony['kind'] + if kind not in KINDS: + raise ValueError(f'unsupported harmony kind: {kind}') + result = KINDS[kind].copy() + for change in harmony.get('degrees', []): + degree, alter, operation = change['value'], change['alter'], change['type'] + if type(degree) is not int or degree < 1 or type(alter) is not int: + raise ValueError('invalid harmony degree') + if operation == 'subtract': + if degree not in result: + raise ValueError(f'cannot subtract absent chord degree {degree}') + del result[degree] + elif operation == 'alter': + if degree not in result: + raise ValueError(f'cannot alter absent chord degree {degree}; use add') + result[degree] = alter + elif operation == 'add': + if degree in result: + raise ValueError(f'duplicate added chord degree {degree}') + result[degree] = alter + else: + raise ValueError(f'unsupported degree operation: {operation}') + if kind != 'none' and not result: + raise ValueError('harmony contains no pitch information') + return result + + +def degree_semitones(degree: int, alter: int) -> int: + return 12 * ((degree-1)//7) + (0,2,4,5,7,9,11)[(degree-1)%7] + alter + + +def pitch_classes(harmony: dict) -> tuple[int, ...]: + if harmony['kind'] == 'none': + return () + root = harmony['root'] + root_pc = NATURAL[root['letter']] + root['alter'] + values = [(root_pc + degree_semitones(d, a)) % 12 for d, a in degrees(harmony).items()] + if bass := harmony.get('bass'): + values.append((NATURAL[bass['letter']] + bass['alter']) % 12) + return tuple(dict.fromkeys(values)) + + +def symbol(harmony: dict) -> str: + if harmony['kind'] == 'none': + return 'N.C.' + suffix = harmony.get('kind_text', SUFFIXES.get(harmony['kind'], harmony['kind'])) + if 'kind_text' not in harmony: + for d in harmony.get('degrees', []): + sign = '#' * max(d['alter'], 0) + 'b' * max(-d['alter'], 0) + suffix += ('no' if d['type']=='subtract' else 'add' if d['type']=='add' and not sign else '') + sign + str(d['value']) + result = pitch_name(harmony['root']) + suffix + if bass := harmony.get('bass'): + result += '/' + pitch_name(bass) + return result + + +def parse_symbol(text: str) -> dict: + import re + match = re.fullmatch(r'([A-Ga-g][#b]*)([^/]*)(?:/([A-Ga-g][#b]*))?', text.strip()) + if not match: + raise ValueError(f'invalid chord symbol: {text!r}') + root, suffix, bass = match.groups() + aliases = {'m':'minor', 'min':'minor', 'maj':'major', 'M':'major', 'm7':'minor-seventh', 'm6':'minor-sixth', 'm7b5':'half-diminished', 'o':'diminished', 'o7':'diminished-seventh', 'aug':'augmented', 'sus':'suspended-fourth'} + aliases.update({v:k for k,v in SUFFIXES.items() if k not in ('other','pedal')}) + core = suffix + changes = [] + while core not in aliases: + m = re.search(r'(add|no)?([b#]?)(\d+)$', core) + if not m: + raise ValueError(f'unsupported chord spelling: {text!r}') + op, accidental, value = m.groups() + changes.insert(0, {'value':int(value), 'alter':1 if accidental=='#' else -1 if accidental=='b' else 0, 'type':'subtract' if op=='no' else 'add'}) + core = core[:m.start()] + kind = aliases[core] + for d in changes: + if d['type']=='add' and d['value'] in KINDS[kind]: + d['type'] = 'alter' + result = {'root':parse_pitch_name(root), 'kind':kind, 'degrees':changes, 'kind_text':suffix} + if bass: + result['bass'] = parse_pitch_name(bass) + degrees(result) + return result diff --git a/generator/model.py b/generator/model.py new file mode 100644 index 0000000..321810b --- /dev/null +++ b/generator/model.py @@ -0,0 +1,243 @@ +from __future__ import annotations + +import copy +from fractions import Fraction +from typing import Any + +SCHEMA_VERSION = 2 +MAX_INTEGER = (1 << 63) - 1 +NOTE_TYPES = ('maxima', 'long', 'breve', 'whole', 'half', 'quarter', 'eighth', '16th', '32nd', '64th', '128th', '256th', '512th', '1024th') +TYPE_LENGTHS = {name: Fraction(32, 2 ** index) for index, name in enumerate(NOTE_TYPES)} +NATURAL = {'c': 0, 'd': 2, 'e': 4, 'f': 5, 'g': 7, 'a': 9, 'b': 11} +LETTERS = 'cdefgab' +MODE_STEPS = {'major': 0, 'ionian': 0, 'dorian': 1, 'phrygian': 2, 'lydian': 3, 'mixolydian': 4, 'minor': 5, 'aeolian': 5, 'locrian': 6} + + +def rational(value: Fraction | int) -> dict[str, int]: + value = Fraction(value) + if abs(value.numerator) > MAX_INTEGER or value.denominator > MAX_INTEGER: + raise ValueError('musical time exceeds the supported 64-bit rational range') + return {'numerator': value.numerator, 'denominator': value.denominator} + + +def fraction(value: Any) -> Fraction: + if not isinstance(value, dict) or set(value) != {'numerator', 'denominator'}: + raise ValueError('musical time must be a numerator/denominator object') + n, d = value['numerator'], value['denominator'] + if type(n) is not int or type(d) is not int or d <= 0 or abs(n) > MAX_INTEGER or d > MAX_INTEGER: + raise ValueError('invalid musical time') + result = Fraction(n, d) + if rational(result) != value: + raise ValueError('musical time must be reduced') + return result + + +def written_length(written: dict) -> Fraction: + kind, dots = written['type'], written.get('dots', 0) + if kind not in TYPE_LENGTHS or type(dots) is not int or not 0 <= dots <= 16: + raise ValueError('invalid written duration or augmentation-dot count (maximum 16)') + return TYPE_LENGTHS[kind] * (2 - Fraction(1, 2 ** dots)) + + +def written_for(length: Fraction) -> dict: + for name, base in TYPE_LENGTHS.items(): + for dots in range(17): + if length == base * (2 - Fraction(1, 2 ** dots)): + return {'type': name, 'dots': dots} + # Generated rational spans need an explicit time modification, not rounded ticks. + base = next((name for name, size in TYPE_LENGTHS.items() if size <= length), '1024th') + return {'type': base, 'dots': 0, 'ratio': rational(length / TYPE_LENGTHS[base])} + + +def metric_length(written: dict) -> Fraction: + ratio = fraction(written.get('ratio', rational(1))) + if ratio <= 0: + raise ValueError('written duration ratio must be positive') + if 'normal' in written: + written_length(written['normal']) + return written_length(written) * ratio + + +def pitch_number(pitch: dict) -> int: + letter = pitch['letter'].lower() + alter, octave = pitch.get('alter', 0), pitch['octave'] + if letter not in NATURAL or type(alter) is not int or type(octave) is not int or not -3 <= alter <= 3 or not -4 <= octave <= 16: + raise ValueError('invalid spelled pitch') + return 12 * (octave + 1) + NATURAL[letter] + alter + + +def pitch_name(pitch: dict) -> str: + alter = pitch.get('alter', 0) + return pitch['letter'].upper() + ('#' * alter if alter > 0 else 'b' * -alter) + + +def parse_pitch_name(name: str) -> dict: + if not name or name[0].lower() not in NATURAL or any(c not in '#b' for c in name[1:]): + raise ValueError(f'invalid pitch name: {name!r}') + return {'letter': name[0].lower(), 'alter': name[1:].count('#') - name[1:].count('b')} + + +def key_from_fifths(fifths: int, mode: str = 'major') -> dict: + if mode not in MODE_STEPS and mode != 'none': + raise ValueError(f'unsupported mode: {mode}') + if not -14 <= fifths <= 14: + raise ValueError('key signatures support up to fourteen fifths in either direction') + degree = fifths * 4 + MODE_STEPS.get(mode, 0) + letter = LETTERS[degree % 7] + signature = [0] * 7 + order = 'fcgdaeb' if fifths > 0 else 'beadgcf' + for i in range(abs(fifths)): + signature[LETTERS.index(order[i % 7])] += 1 if fifths > 0 else -1 + tonic = {'letter': letter, 'alter': signature[LETTERS.index(letter)]} + return {'fifths': fifths, 'mode': mode, 'tonic': tonic if mode != 'none' else None, 'alterations': signature} + + +def key_label(key: dict) -> str: + if not key.get('tonic'): + return 'No key' + label = pitch_name(key['tonic']) + return label if key['mode'] in ('major', 'ionian') else label + ' ' + key['mode'] + + +def meter_length(meter: dict) -> Fraction: + return sum((Fraction(sum(group['beats']) * 4, group['unit']) for group in meter.get('groups', [])), Fraction()) + + +def meter_label(meter: dict) -> str: + if meter.get('unmetered'): + return 'senza misura' + return '+'.join('+'.join(map(str, group['beats'])) + '/' + str(group['unit']) for group in meter['groups']) + + +def beat_lengths(meter: dict) -> list[Fraction]: + result = [] + for group in meter.get('groups', []): + beats, unit = group['beats'], group['unit'] + if len(beats) > 1: + result.extend(Fraction(b * 4, unit) for b in beats) + elif beats[0] > 3 and beats[0] % 3 == 0 and unit >= 8: + result.extend([Fraction(12, unit)] * (beats[0] // 3)) + else: + result.extend([Fraction(4, unit)] * beats[0]) + return result + + +def validate_meter(meter: dict) -> None: + groups = meter.get('groups', []) + if not groups and not meter.get('unmetered'): + raise ValueError('meter requires beat groups or senza misura') + for group in groups: + if type(group['unit']) is not int or not 0 < group['unit'] <= 4096 or not group['beats']: + raise ValueError('meter unit must be between 1 and 4096 with nonempty beats') + if any(type(b) is not int or not 0 < b <= 1024 for b in group['beats']): + raise ValueError('meter count must be between 1 and 1024') + + +def make_event(identifier: str, onset: Fraction, duration: Fraction, kind: str, pitches: list[dict] | None = None, written: dict | None = None) -> dict: + if duration <= 0: + raise ValueError('generated event duration must be positive') + notation = written or written_for(duration) + event = {'id': identifier, 'kind': kind, 'onset': rational(onset), 'duration': rational(duration), 'written': notation, 'hidden': False, 'pitches': []} + for i, p in enumerate(pitches or []): + event['pitches'].append({**copy.deepcopy(p), 'id': f'{identifier}-p{i+1}', 'duration': rational(duration), 'written': copy.deepcopy(notation)}) + return event + + +def validate_document(document: dict) -> None: + if document.get('schema_version') != SCHEMA_VERSION: + raise ValueError('unsupported score schema; regenerate score assets with the current generator') + staves = document.get('staves', []) + if len(staves) != 1: + raise ValueError('exactly one staff is currently supported') + ids: set[str] = set() + events: dict[str, dict] = {} + tones: dict[str, tuple[dict, Fraction, Fraction]] = {} + absolute = Fraction() + for measure in staves[0]['measures']: + length = fraction(measure['duration']) + if length <= 0: + raise ValueError('measure duration must be positive') + voices = measure.get('voices', []) + if len(voices) != 1: + raise ValueError('exactly one rhythmic voice is currently supported') + previous_end = Fraction() + for note in voices[0]['notes']: + if not note['id'] or note['id'] in ids: + raise ValueError(f'duplicate event ID: {note["id"]}') + ids.add(note['id']) + events[note['id']] = note + onset, duration = fraction(note['onset']), fraction(note['duration']) + if onset < 0 or duration < 0 or onset + duration > length: + raise ValueError(f'event outside measure: {note["id"]}') + if note['kind'] not in ('note', 'rest', 'spacer'): + raise ValueError('invalid event kind') + if not note.get('grace'): + if duration == 0 or onset < previous_end: + raise ValueError('overlapping events in one rhythmic voice') + previous_end = onset + duration + elif duration != 0: + raise ValueError('grace notes must not advance metric time') + expected = metric_length(note['written']) + if not note.get('grace') and not note.get('measure_rest') and duration != expected: + raise ValueError('metric and written event durations disagree') + if note['kind'] != 'note' and note.get('pitches'): + raise ValueError('rests and spacers cannot contain pitches') + if note['kind'] == 'note' and not note.get('pitches') and note.get('notehead') != 'slash': + raise ValueError('pitched event requires at least one pitch') + for p in note.get('pitches', []): + pitch_number(p) + pd = fraction(p['duration']) + if pd < 0 or (pd == 0 and not note.get('grace')) or pd > duration or not p['id'] or p['id'] in tones: + raise ValueError('invalid chord-tone duration or duplicate ID') + if not note.get('grace') and pd != metric_length(p['written']): + raise ValueError('metric and written chord-tone durations disagree') + tones[p['id']] = (p, absolute + onset, pd) + for harmony in measure.get('harmonies', []): + if not 0 <= fraction(harmony['onset']) < length: + raise ValueError('harmony outside measure') + for change in measure.get('attributes', []): + if not 0 <= fraction(change['onset']) < length: + raise ValueError('attribute change outside measure') + for state in [measure, *measure.get('attributes', [])]: + if 'meter' in state: + validate_meter(state['meter']) + if 'key' in state and (len(state['key']['alterations']) != 7 or any(type(a) is not int or not -3 <= a <= 3 for a in state['key']['alterations'])): + raise ValueError('key alterations must contain seven integers from -3 through 3') + if 'clef' in state and (state['clef']['sign'] not in ('G','F','C') or not 1 <= state['clef']['line'] <= 5 or not -3 <= state['clef']['octave_change'] <= 3): + raise ValueError('invalid pitched clef') + absolute += length + for identifier, (p, start, duration) in tones.items(): + if target := p.get('tie_to'): + if target not in tones: + raise ValueError(f'dangling tie: {identifier} -> {target}') + other, next_start, _ = tones[target] + if other.get('tie_from') != identifier or pitch_number(p) != pitch_number(other) or next_start != start + duration: + raise ValueError(f'invalid tie: {identifier} -> {target}') + if source := p.get('tie_from'): + if source not in tones or tones[source][0].get('tie_to') != identifier: + raise ValueError(f'dangling incoming tie: {identifier}') + span_ids = set() + event_order = {identifier:index for index, identifier in enumerate(events)} + for span in staves[0].get('tuplets', []): + if not span['id'] or span['id'] in span_ids or span['start'] not in ids or span['end'] not in ids or not 0 < span['note_count'] <= 1000000 or not 0 < span['space_count'] <= 1000000 or not 0 <= span['depth'] <= 16: + raise ValueError('invalid tuplet span') + members = [n['id'] for n in events.values() if span['id'] in n.get('tuplet_ids', [])] + if not members or members[0] != span['start'] or members[-1] != span['end'] or event_order[span['start']] > event_order[span['end']]: + raise ValueError('tuplet endpoints disagree with member events') + if span.get('bracket', 'auto') not in ('yes','no','auto') or span.get('show_number','actual') not in ('none','actual','both'): + raise ValueError('invalid tuplet appearance') + span_ids.add(span['id']) + for note in events.values(): + if any(t not in span_ids for t in note.get('tuplet_ids', [])): + raise ValueError('event refers to an unknown tuplet') + if tremolo := note.get('tremolo'): + if tremolo['type'] not in ('single','start','stop') or type(tremolo['marks']) is not int or not 0 <= tremolo['marks'] <= 8: + raise ValueError('invalid measured tremolo') + if tremolo['type'] in ('start','stop'): + other = events.get(tremolo.get('partner')) + expected_type = 'stop' if tremolo['type'] == 'start' else 'start' + if not other or other.get('tremolo',{}).get('partner') != note['id'] or other['tremolo']['type'] != expected_type or other['tremolo']['marks'] != tremolo['marks']: + raise ValueError('invalid paired tremolo') + for span in staves[0].get('octave_shifts', []): + if not 0 <= fraction(span['start']) < fraction(span['end']) <= absolute or not -3 <= span['octaves'] <= 3: + raise ValueError('invalid octave-shift span') diff --git a/generator/musicxml.py b/generator/musicxml.py new file mode 100644 index 0000000..728499c --- /dev/null +++ b/generator/musicxml.py @@ -0,0 +1,562 @@ +from __future__ import annotations + +import copy +from fractions import Fraction +from pathlib import Path + +from defusedxml import ElementTree as XML + +from generator import harmony +from generator.model import ( + SCHEMA_VERSION, TYPE_LENGTHS, fraction, key_from_fifths, key_label, + meter_label, meter_length, pitch_number, rational, validate_document, + written_for, written_length, +) +from generator.styles import canonical_style + + +class MusicXMLError(ValueError): + pass + + +# Ignoring a container never authorizes ignoring its musical descendants. +LAYOUT = {'default-x', 'default-y', 'relative-x', 'relative-y', 'color', 'font-family', 'font-size', 'font-style', 'font-weight', 'halign', 'valign', 'justify', 'placement', 'id'} +IGNORED_TOP = {'defaults', 'credit', 'work', 'movement-number', 'movement-title', 'identification', 'part-list'} + + +class Importer: + def __init__(self, path: Path) -> None: + self.path = path + self.location = str(path) + self.divisions = Fraction(1) + self.key = key_from_fifths(0) + self.meter = {'groups':[{'beats':[4], 'unit':4}], 'symbol':'normal'} + self.clef = {'sign':'G', 'line':2, 'octave_change':0} + self.transpose = {'diatonic':0, 'chromatic':0, 'octave_change':0} + self.sequence = 0 + self.tuplet_sequence = 0 + self.active_tuplets: dict[str, dict] = {} + self.tuplets: list[dict] = [] + self.open_ties: dict[int, dict] = {} + self.open_octaves: dict[str, dict] = {} + self.octaves: list[dict] = [] + self.voice_id = None + self.lyric_number = None + self.absolute = Fraction() + self.tremolo_start = None + + def fail(self, text: str): + raise MusicXMLError(f'{self.location}: {text}') + + def children(self, node, allowed: set[str]) -> None: + for item in node: + if item.tag not in allowed: + self.fail(f'unsupported musical element <{item.tag}> in <{node.tag}>') + + def attrs(self, node, allowed=()) -> None: + for name in node.attrib: + if name not in LAYOUT and name not in allowed: + self.fail(f'unsupported {node.tag} attribute {name!r}') + + def number(self, value: str | None, *, integer: bool = False, positive: bool = False) -> Fraction | int: + try: + result = Fraction(value or '') + if integer and result.denominator != 1 or positive and result <= 0: + raise ValueError() + rational(result) + return int(result) if integer else result + except (ValueError, ZeroDivisionError, OverflowError): + self.fail(f'invalid {"integer" if integer else "number"}: {value!r}') + + def offset(self, node, cursor: Fraction) -> Fraction: + return cursor + self.number(node.findtext('offset', '0')) / self.divisions + + def identity(self, node) -> None: + voice = node.findtext('voice', '1' if node.tag in ('note','forward') else self.voice_id) + staff = node.findtext('staff', '1') + if staff != '1': + self.fail('multiple staves are not supported') + if voice is None: + return + if self.voice_id is None: + self.voice_id = voice + if voice != self.voice_id: + self.fail('multiple rhythmic voices are not supported') + + def attributes(self, node, onset: Fraction) -> dict: + self.children(node, {'divisions', 'key', 'time', 'clef', 'staves', 'transpose', 'instruments'}) + result = {'onset':rational(onset)} + for item in node: + if item.get('number', '1') != '1': + self.fail('attributes for multiple staves are not supported') + if item.tag == 'divisions': + self.divisions = self.number(item.text, positive=True) + elif item.tag in ('staves', 'instruments'): + if item.text != '1': + self.fail(f'multiple {item.tag} are not supported') + elif item.tag == 'key': + self.attrs(item, {'number', 'print-object'}) + self.children(item, {'cancel', 'fifths', 'mode', 'key-step', 'key-alter', 'key-accidental', 'key-octave'}) + if item.find('fifths') is not None: + self.key = key_from_fifths(self.number(item.findtext('fifths'), integer=True), item.findtext('mode', 'major')) + else: + self.key = {'fifths':0, 'mode':'none', 'tonic':None, 'alterations':[0]*7, 'custom':True} + pending = None + for field in item: + if field.tag == 'key-step': + pending = (field.text or '').lower() + if pending not in 'cdefgab' or len(pending) != 1: + self.fail('invalid custom key step') + elif field.tag == 'key-alter': + if pending is None: + self.fail('key alteration without a step') + self.key['alterations']['cdefgab'.index(pending)] = self.number(field.text, integer=True) + pending = None + elif field.tag in ('key-octave', 'key-accidental'): + self.fail(f'<{field.tag}> requires unsupported custom key placement') + if pending is not None: + self.fail('custom key step without alteration') + if item.find('cancel') is not None: + self.key['cancel'] = self.number(item.findtext('cancel'), integer=True) + result['key'] = copy.deepcopy(self.key) + elif item.tag == 'time': + self.attrs(item, {'number', 'symbol', 'separator', 'print-object'}) + self.children(item, {'beats', 'beat-type', 'senza-misura'}) + self.meter = {'groups':[], 'symbol':item.get('symbol', 'normal')} + if self.meter['symbol'] not in ('normal', 'common', 'cut', 'single-number', 'note', 'dotted-note'): + self.fail('unsupported meter symbol') + if item.find('senza-misura') is not None: + self.meter['unmetered'] = True + else: + pending = None + for field in item: + if field.tag == 'beats': + if pending is not None: + self.fail('beats without beat-type') + pending = [self.number(v, integer=True, positive=True) for v in (field.text or '').split('+')] + elif field.tag == 'beat-type': + if not pending: + self.fail('beat-type without beats') + self.meter['groups'].append({'beats':pending, 'unit':self.number(field.text, integer=True, positive=True)}) + pending = None + if pending is not None or not self.meter['groups']: + self.fail('incomplete meter') + result['meter'] = copy.deepcopy(self.meter) + elif item.tag == 'clef': + self.attrs(item, {'number', 'additional', 'size', 'after-barline', 'print-object'}) + self.children(item, {'sign', 'line', 'clef-octave-change'}) + sign = item.findtext('sign') + if sign not in ('G','F','C'): + self.fail(f'unsupported clef {sign!r}; only pitched G/F/C staves are supported') + line = self.number(item.findtext('line', {'G':'2','F':'4','C':'3'}[sign]), integer=True) + if not 1 <= line <= 5: + self.fail('clef line must be between 1 and 5') + self.clef = {'sign':sign, 'line':line, 'octave_change':self.number(item.findtext('clef-octave-change','0'), integer=True)} + result['clef'] = copy.deepcopy(self.clef) + elif item.tag == 'transpose': + self.children(item, {'diatonic', 'chromatic', 'octave-change'}) + self.transpose = {key:self.number(item.findtext(tag, '0'), integer=True) for key,tag in (('diatonic','diatonic'),('chromatic','chromatic'),('octave_change','octave-change'))} + if item.find('chromatic') is None: + self.fail('instrument transposition requires chromatic semitones') + if item.find('diatonic') is None: + semitones = self.transpose['chromatic'] + magnitude = abs(semitones) + self.transpose['diatonic'] = (1 if semitones >= 0 else -1) * (7*(magnitude//12)+(0,0,1,2,2,3,3,4,5,5,6,6)[magnitude%12]) + result['transpose'] = copy.deepcopy(self.transpose) + return result + + def read_harmony(self, node, cursor: Fraction) -> dict: + self.attrs(node, {'print-object', 'print-frame', 'type'}) + self.children(node, {'root', 'kind', 'degree', 'bass', 'inversion', 'offset', 'staff'}) + if len(node.findall('kind')) != 1 or len(node.findall('root')) > 1: + self.fail('stacked harmonies require separate harmony events') + self.identity(node) + kind = node.find('kind') + result = {'id':f'harmony-{self.sequence}-{cursor}', 'onset':rational(self.offset(node, cursor)), 'kind':kind.text, 'degrees':[]} + self.attrs(kind, {'text', 'use-symbols', 'stack-degrees', 'parentheses-degrees', 'bracket-degrees'}) + if 'text' in kind.attrib: + result['kind_text'] = kind.get('text') + for tag in ('root','bass'): + item = node.find(tag) + if item is None: + if tag == 'root' and kind.text != 'none': + self.fail('pitched harmony requires a root') + continue + self.children(item, {tag+'-step', tag+'-alter'}) + step = item.findtext(tag+'-step','').lower() + if len(step)!=1 or step not in 'cdefgab': + self.fail('invalid harmony root/bass') + result[tag] = {'letter':step, 'alter':self.number(item.findtext(tag+'-alter','0'), integer=True)} + for degree in node.findall('degree'): + self.children(degree, {'degree-value','degree-alter','degree-type'}) + value = self.number(degree.findtext('degree-value'), integer=True, positive=True) + alteration = self.number(degree.findtext('degree-alter','0'), integer=True) + operation = degree.findtext('degree-type') + # MusicXML alterations are relative to the kind; additions use dominant intervals. + if operation == 'alter': + alteration += harmony.KINDS.get(result['kind'], {}).get(value, 0) + elif operation == 'add' and (value-1)%7 == 6: + alteration -= 1 + elif operation == 'subtract' and alteration: + self.fail('cannot subtract a degree different from the one in the chord kind') + result['degrees'].append({'value':value, 'alter':alteration, 'type':operation}) + chord_degrees = harmony.degrees(result) + if 'kind_text' in result and result['kind'] != 'none': + from generator.model import pitch_name + label = pitch_name(result['root']) + result['kind_text'] + try: + displayed = harmony.degrees(harmony.parse_symbol(label)) + except ValueError: + self.fail(f'unmapped harmony display text {label!r}; use a standard chord kind/degree spelling') + if displayed != chord_degrees: + if displayed != harmony.KINDS[result['kind']]: + self.fail(f'contradictory harmony text {label!r} and structured chord degrees') + result.pop('kind_text') + if node.find('inversion') is not None: + inversion = self.number(node.findtext('inversion'), integer=True) + result['inversion'] = inversion + if not 0 <= inversion < len(chord_degrees): + self.fail('invalid chord inversion') + if inversion and not result.get('bass'): + degree = sorted(chord_degrees)[inversion] + root = result['root'] + letter_index = 'cdefgab'.index(root['letter']) + degree - 1 + bass_letter = 'cdefgab'[letter_index % 7] + from generator.model import NATURAL + target = NATURAL[root['letter']] + root['alter'] + harmony.degree_semitones(degree, chord_degrees[degree]) + result['bass'] = {'letter':bass_letter, 'alter':target - (12*(letter_index//7)+NATURAL[bass_letter])} + result['symbol'] = harmony.symbol(result) + return result + + def direction(self, node, cursor: Fraction, measure: dict, optional: list[Fraction]) -> None: + self.attrs(node, {'print-object', 'directive'}) + self.children(node, {'direction-type','offset','staff','voice','sound'}) + onset = self.offset(node, cursor) + for group in node.findall('direction-type'): + self.children(group, {'words','octave-shift','rehearsal','metronome'}) + for item in group: + if item.tag == 'words': + value = ''.join(item.itertext()).strip() + if value == 'vamp:optional-chord': + optional.append(onset) + elif value: + self.fail(f'unsupported staff text {value!r}; only Vamp directives are accepted') + elif item.tag == 'rehearsal': + if onset != 0: + self.fail('section labels must be at measure starts') + measure['section_label'] = ''.join(item.itertext()) + elif item.tag == 'octave-shift': + number, action = item.get('number','1'), item.get('type') + size = self.number(item.get('size','8'), integer=True) + if size not in (8,15,22): + self.fail('octave shifts support 8, 15, or 22') + position = rational(self.absolute + onset) + if action in ('up','down'): + if number in self.open_octaves: + self.fail('duplicate octave-shift start') + span = {'id':f'octave-{len(self.octaves)+1}', 'start':position, 'octaves':(size-1)//7 * (1 if action=='up' else -1)} + self.open_octaves[number] = span + self.octaves.append(span) + elif action == 'stop': + if number not in self.open_octaves: + self.fail('octave-shift stop without start') + self.open_octaves.pop(number)['end'] = position + elif action != 'continue' or number not in self.open_octaves: + self.fail('invalid octave-shift continuation') + elif item.tag == 'metronome': + self.children(item, {'beat-unit','beat-unit-dot','per-minute'}) + written = {'type':item.findtext('beat-unit'), 'dots':len(item.findall('beat-unit-dot'))} + written_length(written) + bpm = self.number(item.findtext('per-minute'), positive=True) + measure.setdefault('tempos', []).append({'onset':rational(onset), 'written':written, 'bpm':rational(bpm)}) + sound = node.find('sound') + if sound is not None: + self.attrs(sound, {'tempo'}) + self.children(sound, set()) + if sound.get('tempo') and not any(fraction(t['onset'])==onset for t in measure.get('tempos', [])): + measure.setdefault('tempos', []).append({'onset':rational(onset), 'written':{'type':'quarter','dots':0}, 'bpm':rational(self.number(sound.get('tempo'), positive=True))}) + + def read_note(self, node, cursor: Fraction, previous: dict | None) -> tuple[dict, Fraction, bool]: + self.attrs(node, {'print-object', 'print-dot', 'print-spacing', 'default-x', 'dynamics', 'end-dynamics'}) + if node.get('dynamics') or node.get('end-dynamics'): + self.fail('note dynamics are not supported') + self.children(node, {'pitch','rest','chord','duration','voice','type','dot','accidental','time-modification','stem','staff','beam','notations','lyric','tie','grace','cue','notehead'}) + for tag in ('pitch','rest','chord','duration','voice','type','accidental','time-modification','stem','staff','grace','cue','notehead'): + if len(node.findall(tag)) > 1: + self.fail(f'duplicate <{tag}> in ') + if node.find('rest') is not None and node.find('pitch') is not None: + self.fail('note cannot contain both pitch and rest') + self.identity(node) + is_chord = node.find('chord') is not None + grace = node.find('grace') + duration = Fraction() if grace is not None else self.number(node.findtext('duration'), positive=True) / self.divisions + onset = fraction(previous['onset']) if is_chord and previous else cursor + if is_chord and (previous is None or previous['kind']!='note' or duration > fraction(previous['duration'])): + self.fail('invalid chord tone or chord tone longer than its anchor') + if is_chord and (grace is not None) != ('grace' in previous): + self.fail('chord tones must share grace/regular timing') + written = {'type':node.findtext('type'), 'dots':len(node.findall('dot'))} + if written['type'] is None: + written = {'type':'whole','dots':0} if node.find('rest') is not None and node.find('rest').get('measure')=='yes' else written_for(duration) + written_length(written) + modulation = node.find('time-modification') + if modulation is not None: + self.children(modulation, {'actual-notes','normal-notes','normal-type','normal-dot'}) + actual = self.number(modulation.findtext('actual-notes'), integer=True, positive=True) + normal = self.number(modulation.findtext('normal-notes'), integer=True, positive=True) + written['ratio'] = rational(Fraction(normal, actual)) + if modulation.find('normal-type') is not None: + written['normal'] = {'type':modulation.findtext('normal-type'), 'dots':len(modulation.findall('normal-dot'))} + written_length(written['normal']) + elif modulation.findall('normal-dot'): + self.fail(' requires ') + measure_rest = node.find('rest') is not None and node.find('rest').get('measure') == 'yes' + if grace is None and not measure_rest: + expected = written_length(written) * fraction(written.get('ratio', rational(1))) + if duration != expected: + self.fail(f' duration {duration} disagrees with written duration/time-modification {expected}') + self.sequence += 1 + note = {'id':f'note-{self.sequence}', 'kind':'rest' if node.find('rest') is not None else 'note', 'onset':rational(onset), 'duration':rational(duration), 'written':written, 'hidden':node.get('print-object')=='no', 'pitches':[]} + if is_chord: + note = previous + if grace is not None: + self.attrs(grace, {'slash','steal-time-previous','steal-time-following','make-time'}) + note['grace'] = {'slash':grace.get('slash')=='yes'} + for name in ('steal-time-previous','steal-time-following','make-time'): + if name in grace.attrib: + note['grace'][name] = rational(self.number(grace.get(name), positive=True)) + if node.find('cue') is not None: + note['cue'] = True + if node.find('rest') is not None: + self.children(node.find('rest'), {'display-step','display-octave'}) + if node.find('rest').get('measure') == 'yes': + note['measure_rest'] = True + if node.find('rest/display-step') is not None: + note['rest_position'] = {'letter':node.findtext('rest/display-step').lower(), 'octave':self.number(node.findtext('rest/display-octave'), integer=True)} + else: + pitch_node = node.find('pitch') + if pitch_node is None: + self.fail('note has neither pitch nor rest') + self.children(pitch_node, {'step','alter','octave'}) + p = {'id':f'{note["id"]}-p{len(note["pitches"])+1}', 'letter':pitch_node.findtext('step','').lower(), 'alter':self.number(pitch_node.findtext('alter','0'), integer=True), 'octave':self.number(pitch_node.findtext('octave'), integer=True), 'duration':rational(duration), 'written':written} + value = pitch_number(p) + p['_absolute'] = self.absolute + onset + accidental = node.find('accidental') + if accidental is not None: + self.attrs(accidental, {'cautionary','editorial','parentheses','bracket','size','smufl'}) + allowed = {'natural':0,'sharp':1,'flat':-1,'double-sharp':2,'sharp-sharp':2,'flat-flat':-2,'triple-sharp':3,'triple-flat':-3,'natural-sharp':1,'natural-flat':-1} + if accidental.text not in allowed or allowed[accidental.text] != p['alter']: + self.fail('unsupported or contradictory accidental') + p['accidental_display'] = {'name':accidental.text, 'parentheses':accidental.get('parentheses')=='yes' or accidental.get('cautionary')=='yes', 'bracket':accidental.get('bracket')=='yes'} + ties = {t.get('type') for t in node.findall('tie')} + notations = node.find('notations') + if notations is not None: + visual_ties = {t.get('type') for t in notations.findall('tied')} + if 'continue' in visual_ties: + visual_ties.remove('continue') + visual_ties.update({'start','stop'}) + ties |= visual_ties + if ties - {'start','stop'}: + self.fail('unsupported tie notation') + if 'stop' in ties: + source = self.open_ties.pop(value, None) + if source is None or source['_absolute'] + fraction(source['duration']) != p['_absolute']: + self.fail('tie stop has no contiguous matching start') + source['tie_to'], p['tie_from'] = p['id'], source['id'] + if 'start' in ties: + if value in self.open_ties: + self.fail('ambiguous duplicate tie at the same pitch') + self.open_ties[value] = p + note['pitches'].append(p) + if not is_chord: + if node.find('stem') is not None: + stem = node.findtext('stem') + if stem not in ('up','down','none','double'): + self.fail('unsupported stem') + note['stem'] = stem + head = node.findtext('notehead') + if head is not None: + if head not in ('normal','slash'): + self.fail(f'unsupported notehead: {head}') + note['notehead'] = head + beams = [] + for beam in node.findall('beam'): + level = self.number(beam.get('number','1'), integer=True) + if not 1 <= level <= 8 or beam.text not in ('begin','continue','end','forward hook','backward hook'): + self.fail('invalid beam') + beams.append({'level':level,'type':beam.text}) + if beams: + note['beams'] = beams + for lyric in node.findall('lyric'): + self.children(lyric, {'syllabic','text','elision','extend','end-line','end-paragraph'}) + number = lyric.get('number', '1') + if self.lyric_number is None: + self.lyric_number = number + if self.lyric_number != number or 'lyric' in note: + self.fail('only one lyric verse is supported') + pieces = [] + for item in lyric: + if item.tag in ('text','elision'): + pieces.append(item.text or (' ' if item.tag=='elision' else '')) + syllabic = lyric.findtext('syllabic','single') + if syllabic not in ('single','begin','middle','end'): + self.fail('invalid lyric syllabification') + note['lyric'] = ''.join(pieces) + ('-' if syllabic in ('begin','middle') else '') + note['lyric_notation'] = {'text':''.join(pieces), 'syllabic':syllabic} + if lyric.find('extend') is not None: + note['lyric_notation']['extend'] = lyric.find('extend').get('type','start') + stops = [] + for notations in node.findall('notations'): + self.children(notations, {'tied','tuplet','ornaments'}) + for item in notations: + if item.tag == 'tuplet' and not is_chord: + number, action = item.get('number','1'), item.get('type') + self.children(item, {'tuplet-actual','tuplet-normal'}) + if action == 'start': + if number in self.active_tuplets: + self.fail('duplicate tuplet start') + self.tuplet_sequence += 1 + actual = item.findtext('tuplet-actual/tuplet-number', modulation.findtext('actual-notes') if modulation is not None else '') + normal = item.findtext('tuplet-normal/tuplet-number', modulation.findtext('normal-notes') if modulation is not None else '') + if self.active_tuplets and (item.find('tuplet-actual') is None or item.find('tuplet-normal') is None): + self.fail('nested tuplets require explicit tuplet-actual and tuplet-normal numbers') + span = {'id':f'tuplet-{self.tuplet_sequence}', 'start':note['id'], 'note_count':self.number(actual, integer=True, positive=True), 'space_count':self.number(normal, integer=True, positive=True), 'depth':len(self.active_tuplets), 'bracket':item.get('bracket','auto'), 'show_number':item.get('show-number','actual')} + self.active_tuplets[number] = span + self.tuplets.append(span) + elif action == 'stop': + stops.append(number) + else: + self.fail('invalid tuplet type') + elif item.tag == 'ornaments': + self.children(item, {'tremolo'}) + for tremolo in item: + marks = self.number(tremolo.text, integer=True) + if not 0 <= marks <= 8 or tremolo.get('type','single') == 'unmeasured': + self.fail('only measured tremolos are supported') + action = tremolo.get('type','single') + note['tremolo'] = {'marks':marks, 'type':action} + if action == 'start': + if self.tremolo_start is not None: + self.fail('nested tremolo') + self.tremolo_start = note + elif action == 'stop': + if self.tremolo_start is None: + self.fail('tremolo stop without start') + note['tremolo']['partner'] = self.tremolo_start['id'] + self.tremolo_start['tremolo']['partner'] = note['id'] + self.tremolo_start = None + elif action != 'single': + self.fail('unsupported tremolo type') + if not is_chord: + note['tuplet_ids'] = [t['id'] for t in self.active_tuplets.values()] + for number in stops: + if number not in self.active_tuplets: + self.fail('tuplet stop without start') + self.active_tuplets.pop(number)['end'] = note['id'] + return note, cursor if is_chord else cursor + duration, is_chord + + def read(self) -> dict: + root = XML.parse(self.path, forbid_entities=True, forbid_external=True).getroot() + if root.tag != 'score-partwise' or root.get('version') not in ('3.1','4.0'): + self.fail('expected uncompressed score-partwise MusicXML 3.1 or 4.0') + self.children(root, IGNORED_TOP | {'part'}) + parts = root.findall('part') + definitions = root.findall('part-list/score-part') + if len(parts) != 1 or len(definitions) != 1 or parts[0].get('id') != definitions[0].get('id'): + self.fail('exactly one matching score part is required') + fields = {} + for item in root.findall('identification/miscellaneous/miscellaneous-field'): + name = item.get('name','') + if name.startswith('vamp:'): + if name not in ('vamp:style','vamp:tonic') or name in fields: + self.fail(f'unknown or duplicate Vamp property {name!r}') + fields[name] = item.text or '' + metadata = {'title':root.findtext('work/work-title') or root.findtext('movement-title') or self.path.stem, 'composer':' / '.join(c.text for c in root.findall('identification/creator') if c.get('type')=='composer' and c.text), 'style':canonical_style(fields.get('vamp:style'))} + if 'vamp:tonic' in fields: + from generator.model import parse_pitch_name + metadata['transpose_origin'] = parse_pitch_name(fields['vamp:tonic']) + measures = [] + self.children(parts[0], {'measure'}) + for index, bar in enumerate(parts[0]): + self.location = f'{self.path}: part {parts[0].get("id")}, measure {bar.get("number",str(index+1))}' + self.children(bar, {'attributes','note','direction','harmony','forward','backup','barline','print'}) + self.attrs(bar, {'number','implicit','non-controlling','width','text'}) + cursor = Fraction() + events = [] + previous = None + optional = [] + measure = {'id':f'measure-{index+1}', 'number':bar.get('number',str(index+1)), 'implicit':bar.get('implicit')=='yes', 'harmonies':[], 'attributes':[], 'key':copy.deepcopy(self.key), 'meter':copy.deepcopy(self.meter), 'clef':copy.deepcopy(self.clef), 'transpose':copy.deepcopy(self.transpose)} + for item in bar: + if item.tag == 'attributes': + state = self.attributes(item, cursor) + if cursor == 0: + for k in ('key','meter','clef','transpose'): + measure[k] = copy.deepcopy(getattr(self,k)) + elif len(state) > 1: + measure['attributes'].append(state) + elif item.tag == 'note': + previous, cursor, is_chord = self.read_note(item, cursor, previous) + if not is_chord: + events.append(previous) + elif item.tag == 'harmony': + measure['harmonies'].append(self.read_harmony(item, cursor)) + elif item.tag == 'direction': + self.direction(item, cursor, measure, optional) + elif item.tag == 'backup': + self.fail('backward timeline movement requires unsupported independent voices') + elif item.tag == 'forward': + self.children(item, {'duration','voice','staff'}) + self.identity(item) + duration = self.number(item.findtext('duration'), positive=True)/self.divisions + self.sequence += 1 + from generator.model import make_event + events.append(make_event(f'spacer-{self.sequence}', cursor, duration, 'spacer')) + cursor += duration + previous = None + elif item.tag == 'barline': + self.children(item, {'bar-style'}) + style = item.findtext('bar-style','regular') + if style not in ('regular','light-light','light-heavy','heavy-light','heavy-heavy','heavy','dashed','dotted','none','tick','short'): + self.fail('unsupported barline') + measure.setdefault('barlines', {})[item.get('location','right')] = style + if not events or cursor <= 0: + self.fail('empty or zero-duration measure') + expected = meter_length(measure['meter']) + meter_changed = any('meter' in a for a in measure['attributes']) + if expected and cursor != expected and not measure['implicit'] and not meter_changed: + self.fail(f'measure has {cursor} quarter notes, expected {expected}; mark a partial measure implicit') + measure['duration'] = rational(cursor) + measure['key_signature'], measure['time_signature'] = key_label(measure['key']), meter_label(measure['meter']) + measure['voices'] = [{'id':'voice-source', 'notes':events}] + measure['harmonies'].sort(key=lambda h:fraction(h['onset'])) + used = set() + for onset in optional: + matches = [h for h in measure['harmonies'] if fraction(h['onset'])==onset] + if len(matches)!=1 or onset in used: + self.fail('optional-chord marker must identify exactly one harmony and occur only once') + matches[0]['chord_optional'] = True + used.add(onset) + measures.append(measure) + self.absolute += cursor + if self.open_ties or self.active_tuplets or self.open_octaves or self.tremolo_start: + self.fail('unterminated tie, tuplet, octave shift, or tremolo') + if not measures: + self.fail('score has no measures') + for measure in measures: + for n in measure['voices'][0]['notes']: + for p in n['pitches']: + p.pop('_absolute', None) + metadata.update({'key_signature':measures[0]['key_signature'], 'time_signature':measures[0]['time_signature'], 'key':copy.deepcopy(measures[0]['key'])}) + result = {'schema_version':SCHEMA_VERSION, 'metadata':metadata, 'staves':[{'id':'staff-source', 'name':definitions[0].findtext('part-name','Source'), 'role':'vocal', 'clef':copy.deepcopy(measures[0]['clef']), 'tuplets':self.tuplets, 'octave_shifts':self.octaves, 'measures':measures}]} + validate_document(result) + return result + + +def load_musicxml(path: Path) -> dict: + importer = Importer(Path(path)) + try: + return importer.read() + except MusicXMLError: + raise + except Exception as error: + raise MusicXMLError(f'{importer.location}: {error}') from error diff --git a/generator/practice/generate.py b/generator/practice/generate.py index 8c85936..0268038 100644 --- a/generator/practice/generate.py +++ b/generator/practice/generate.py @@ -2,7 +2,8 @@ from __future__ import annotations from pathlib import Path -from generator.score import copy_source_voice, load_json, make_arranged_document, make_measure, slugify, source_staff, write_json +from generator.score import copy_source_voice, make_arranged_document, make_measure, reidentify, slugify, source_staff, write_json +from generator.musicxml import load_musicxml from generator.transposition import TARGET_KEYS, transpose_document @@ -19,10 +20,10 @@ def practice_document(document: dict) -> dict: measure, voice["notes"], section_label=str(measure.get("section_label", "")), - tuplets=voice["tuplets"], ) output_measure["voices"][0]["id"] = str(voice.get("id", output_measure["voices"][0]["id"])) measures.append(output_measure) + tuplets = reidentify(measures, staff.get('tuplets', [])) return make_arranged_document( document, staff, @@ -31,15 +32,17 @@ def practice_document(document: dict) -> dict: role="vocal", arrangement_part_type="practice", measures=measures, + tuplets=tuplets, + octave_shifts=staff.get('octave_shifts', []), ) def key_file_name(key: str) -> str: - return key.lower().replace("#", "sharp").replace("b", "b") + ".json" + return slugify(key.replace("#", "sharp")) + ".json" def generate_chord_practice(source_path: Path, source_root: Path, output_dir: Path) -> int: - document = practice_document(load_json(source_path)) + document = practice_document(load_musicxml(source_path)) metadata = document.get("metadata", {}) key = str(metadata.get("key_signature", "C") if isinstance(metadata, dict) else "C") relative = source_path.relative_to(source_root) @@ -50,7 +53,7 @@ def generate_chord_practice(source_path: Path, source_root: Path, output_dir: Pa def generate_melody_practice(source_path: Path, source_root: Path, output_dir: Path) -> int: - base_document = practice_document(load_json(source_path)) + base_document = practice_document(load_musicxml(source_path)) relative = source_path.relative_to(source_root) lesson_group = relative.parent.name exercise_id = slugify(source_path.stem) @@ -67,11 +70,10 @@ def generate_practice_scores(source_dir: Path, output_dir: Path) -> int: count = 0 chord_root = source_dir / "chord" if chord_root.exists(): - for path in sorted(chord_root.rglob("*.json")): + for path in sorted(chord_root.rglob("*.musicxml")): count += generate_chord_practice(path, chord_root, output_dir) melody_root = source_dir / "melody" if melody_root.exists(): - for path in sorted(melody_root.rglob("*.json")): + for path in sorted(melody_root.rglob("*.musicxml")): count += generate_melody_practice(path, melody_root, output_dir) return count - diff --git a/generator/score.py b/generator/score.py index 9a674b6..c526359 100644 --- a/generator/score.py +++ b/generator/score.py @@ -4,812 +4,117 @@ import copy import json import re from pathlib import Path -from typing import Any -from generator.styles import canonical_style - -WHOLE_TICKS = 16384 -HALF_TICKS = WHOLE_TICKS // 2 -QUARTER_TICKS = WHOLE_TICKS // 4 -EIGHTH_TICKS = WHOLE_TICKS // 8 -SIXTEENTH_TICKS = WHOLE_TICKS // 16 -THIRTY_SECOND_TICKS = WHOLE_TICKS // 32 -RENDERABLE_TICKS = ( - WHOLE_TICKS + HALF_TICKS, - WHOLE_TICKS, - HALF_TICKS + QUARTER_TICKS, - HALF_TICKS, - QUARTER_TICKS + EIGHTH_TICKS, - QUARTER_TICKS, - EIGHTH_TICKS + SIXTEENTH_TICKS, - EIGHTH_TICKS, - SIXTEENTH_TICKS + THIRTY_SECOND_TICKS, - SIXTEENTH_TICKS, - THIRTY_SECOND_TICKS, -) - -REST_PITCH = {"letter": "b", "accidental": "n", "octave": 4} -FORBIDDEN_OUTPUT_FIELDS = { - "chord_optional", -} +from generator.model import SCHEMA_VERSION, validate_document -def load_json(path: Path) -> dict[str, Any]: - with path.open("r", encoding="utf-8") as stream: - document = json.load(stream) - if not isinstance(document, dict): - raise ValueError(f"{path} is not a score document") - return normalize_score_document(document, path) +def load_json(path: Path) -> dict: + document = json.loads(path.read_text(encoding='utf-8')) + validate_document(document) + return document -def write_json(path: Path, document: dict[str, Any]) -> None: +def write_json(path: Path, document: dict) -> None: + validate_document(document) path.parent.mkdir(parents=True, exist_ok=True) - with path.open("w", encoding="utf-8") as stream: - json.dump(document, stream, indent=2, ensure_ascii=False) - stream.write("\n") + path.write_text(json.dumps(document, indent=2, ensure_ascii=False) + '\n', encoding='utf-8') def slugify(value: str) -> str: - value = value.strip().lower() - value = re.sub(r"[^a-z0-9]+", "-", value) - value = value.strip("-") - return value or "score" + return re.sub(r'[^a-z0-9]+', '-', value.strip().lower()).strip('-') or 'score' def human_label(value: str) -> str: - text = value.replace("_", " ").replace("-", " ").strip() - return " ".join(part.capitalize() for part in text.split()) or "General" + return ' '.join(v.capitalize() for v in value.replace('_',' ').replace('-',' ').split()) or 'General' -class CompactScoreError(ValueError): - pass +def source_staff(document: dict) -> dict: + if len(document.get('staves', [])) != 1: + raise ValueError('exactly one source staff is required') + return document['staves'][0] -DURATION_TICKS = { - "1": WHOLE_TICKS, - "2": HALF_TICKS, - "4": QUARTER_TICKS, - "8": EIGHTH_TICKS, - "16": SIXTEENTH_TICKS, - "32": THIRTY_SECOND_TICKS, -} +def first_voice(measure: dict) -> dict: + if len(measure.get('voices', [])) != 1: + raise ValueError('exactly one source voice is required') + return measure['voices'][0] -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 measure_notes(measure: dict) -> list[dict]: + return first_voice(measure)['notes'] -def canonicalize_score_metadata(metadata: dict[str, Any], path: Path | None = None) -> dict[str, Any]: - output = sanitize_json(metadata) - if not isinstance(output, dict): - output = {} - style_source = output.get("style") - try: - output["style"] = canonical_style(style_source) - except ValueError as error: - location = f"{path}: " if path is not None else "" - raise CompactScoreError(f"{location}{error}") from error - return output +def leading_pickup_measure_index(measures: list[dict]) -> int | None: + return 0 if len(measures)>1 and measures[0].get('implicit') else None -def normalize_full_score_document(document: dict[str, Any], path: Path | None = None) -> dict[str, Any]: - output = copy.deepcopy(document) - output.pop("version", None) - metadata_value = output.get("metadata", {}) - if metadata_value is not None and not isinstance(metadata_value, dict): - location = f"{path}: " if path is not None else "" - raise CompactScoreError(f"{location}metadata must be an object") - metadata = copy.deepcopy(metadata_value) if isinstance(metadata_value, dict) else {} - for key in ("style",): - if key in output and key not in metadata: - metadata[key] = output[key] - output.pop(key, None) - output["metadata"] = canonicalize_score_metadata(metadata, path) - return output +def body_start_measure_index(measures: list[dict]) -> int: + return 1 if leading_pickup_measure_index(measures) is not None else 0 -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.pending_chord_optional = False - 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 - chord = self.parse_until("]", "chord symbol").strip() - optional = chord.startswith("(") and chord.endswith(")") - if optional: - chord = chord[1:-1].strip() - if not chord: - raise self.error("chord symbol must not be empty") - self.pending_chord = chord - self.pending_chord_optional = optional - - 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 - if self.pending_chord_optional: - note["chord_optional"] = True - self.pending_chord = "" - self.pending_chord_optional = False - 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 - - -COMPACT_MEASURE_CONTROL_FIELDS = { - "notes", - "notation", - "section", - "section_label", - "key", - "key_signature", - "time", - "time_signature", - "id", - "voices", -} - -GENERATED_MEASURE_FIELDS = { - "id", - "section_label", - "time_signature", - "key_signature", - "voices", -} - - -def compact_measure_text(value: Any, index: int) -> tuple[str, dict[str, str], dict[str, Any]]: - 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 - extra_fields = { - key: sanitize_json(extra_value) - for key, extra_value in value.items() - if key not in COMPACT_MEASURE_CONTROL_FIELDS - } - return text, overrides, extra_fields - 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 normalize_full_score_document(document, path) - measures_value = document.get("measures") - if not isinstance(measures_value, list): - return normalize_full_score_document(document, path) - 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 {"version", "title", "composer", "key", "key_signature", "time", "time_signature", "clef", "part", "name", "role", "measures", "metadata"}: - continue - output_metadata[key] = sanitize_json(value) - output_metadata = canonicalize_score_metadata(output_metadata, path) - - measures: list[dict[str, Any]] = [] - for index, value in enumerate(measures_value): - text, overrides, extra_fields = 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() - measure = { - "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, - }], - } - measure.update(extra_fields) - measures.append(measure) - - return { - "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: - raise ValueError("score document must contain at least one staff") - first_staff: dict[str, Any] | None = None - for value in staves: - if not isinstance(value, dict): - continue - if first_staff is None: - first_staff = value - staff_id = str(value.get("id", "")) - if not value.get("arranged") and staff_id not in {"staff-arranged-guitar", "staff-arranged-vocal", "staff-arranged-practice"} and value.get("role") != "guitar": - return value - if first_staff is None: - raise ValueError("score document does not contain an object staff") - return first_staff - - -def measure_capacity_ticks(time_signature: str) -> int: - try: - beats_text, unit_text = time_signature.split("/", 1) - beats = int(beats_text) - unit = int(unit_text) - except ValueError: - return WHOLE_TICKS - if beats <= 0 or unit <= 0: - return WHOLE_TICKS - return WHOLE_TICKS * beats // unit - - -def note_duration(note: dict[str, Any]) -> int: - value = note.get("duration_ticks", QUARTER_TICKS) - return max(1, int(value)) if isinstance(value, int) else QUARTER_TICKS - - -def renderable_duration(duration_ticks: int) -> bool: - return duration_ticks in RENDERABLE_TICKS - - -def renderable_duration_parts(duration_ticks: int) -> list[int]: - parts: list[int] = [] - remaining = duration_ticks - while remaining > 0: - match = next((duration for duration in RENDERABLE_TICKS if duration <= remaining), None) - if match is None: - return [duration_ticks] - parts.append(match) - remaining -= match - return parts - - -def first_voice(measure: dict[str, Any]) -> dict[str, Any]: - voices = measure.get("voices") - if isinstance(voices, list) and voices and isinstance(voices[0], dict): - return voices[0] - return {"id": "voice-empty", "tuplets": [], "notes": []} - - -def note_chord_symbol(note: dict[str, Any], *, include_optional: bool = True) -> str: - chord = note.get("chord_symbol") - if not isinstance(chord, str) or not chord: - return "" - if not include_optional and note.get("chord_optional") is True: - return "" - return chord - - -def first_chord_symbol(measure: dict[str, Any], fallback: str = "", *, include_optional: bool = True) -> str: - voice = first_voice(measure) - notes = voice.get("notes") - if not isinstance(notes, list): - return fallback - has_filtered_chord = False - for note in notes: - if isinstance(note, dict): - if isinstance(note.get("chord_symbol"), str) and note.get("chord_symbol"): - has_filtered_chord = True - chord = note_chord_symbol(note, include_optional=include_optional) - if chord: - return chord - if not include_optional and has_filtered_chord: - return "" - return fallback - - -def measure_notes(measure: dict[str, Any]) -> list[dict[str, Any]]: - voice = first_voice(measure) - notes = voice.get("notes") - return [note for note in notes if isinstance(note, dict)] if isinstance(notes, list) else [] - - -def timed_measure_notes(measure: dict[str, Any]) -> list[tuple[int, dict[str, Any]]]: - cursor = 0 - result: list[tuple[int, dict[str, Any]]] = [] - for note in measure_notes(measure): - result.append((cursor, note)) - cursor += note_duration(note) - return result - - -def chord_markers(measure: dict[str, Any], *, include_optional: bool = True) -> list[tuple[int, int, str]]: - markers: list[tuple[int, int, str]] = [] - for start_ticks, note in timed_measure_notes(measure): - chord = note_chord_symbol(note, include_optional=include_optional) - if chord: - markers.append((start_ticks, note_duration(note), chord)) - return markers - - -def chord_symbol_at_offset(measure: dict[str, Any], offset_ticks: int, *, include_optional: bool = True) -> str: - for start_ticks, _, chord in chord_markers(measure, include_optional=include_optional): - if start_ticks == offset_ticks: - return chord - return "" - - -def active_chord_at_offset(measure: dict[str, Any], offset_ticks: int, fallback: str = "", *, include_optional: bool = True) -> str: - current = fallback - for start_ticks, _, chord in chord_markers(measure, include_optional=include_optional): - if start_ticks > offset_ticks: - break - current = chord - return current - - -def source_note_attack_at(measure: dict[str, Any], offset_ticks: int) -> bool: - for start_ticks, note in timed_measure_notes(measure): - if start_ticks == offset_ticks and note.get("kind", "note") == "note" and isinstance(note.get("pitches"), list) and note["pitches"]: - return True - return False - - -def first_pitched_note_start(measure: dict[str, Any]) -> int | None: - cursor = 0 - for note in measure_notes(measure): - if note.get("kind", "note") == "note" and isinstance(note.get("pitches"), list) and note["pitches"]: - return cursor - cursor += note_duration(note) - return None - - -def leading_pickup_measure_index(measures: list[dict[str, Any]]) -> int | None: - if len(measures) < 2 or first_chord_symbol(measures[0]): - return None - first_pitch = first_pitched_note_start(measures[0]) - if first_pitch is None or first_pitch <= 0: - return None - for index, measure in enumerate(measures): - if first_chord_symbol(measure): - return 0 if index == 1 else None - return None - - -def body_start_measure_index(measures: list[dict[str, Any]]) -> int: - pickup = leading_pickup_measure_index(measures) - return pickup + 1 if pickup is not None else 0 - - -def rest_note(note_id: str, duration_ticks: int, chord_symbol: str = "") -> dict[str, Any]: - note: dict[str, Any] = { - "id": note_id, - "kind": "rest", - "duration_ticks": duration_ticks, - "pitches": [copy.deepcopy(REST_PITCH)], - "hidden": False, - } - if chord_symbol: - note["chord_symbol"] = chord_symbol - return note - - -def renderable_rest_notes(note_id_prefix: str, duration_ticks: int, sequence: int, chord_symbol: str = "") -> tuple[list[dict[str, Any]], int]: - notes: list[dict[str, Any]] = [] - for index, duration in enumerate(renderable_duration_parts(duration_ticks)): - sequence += 1 - notes.append(rest_note(f"{note_id_prefix}-{sequence}", duration, chord_symbol if index == 0 else "")) - return notes, sequence - - -def apply_source_chord_markers(notes: list[dict[str, Any]], measure: dict[str, Any], *, include_optional: bool = True) -> list[dict[str, Any]]: - markers = [(start, chord) for start, _, chord in chord_markers(measure, include_optional=include_optional)] - if not markers: - result = [copy.deepcopy(note) for note in notes] - for note in result: - note.pop("chord_symbol", None) - return result - - unique_markers: list[tuple[int, str]] = [] - for start, chord in markers: - if unique_markers and unique_markers[-1][0] == start: - unique_markers[-1] = (start, chord) - else: - unique_markers.append((start, chord)) - - marker_by_start = dict(unique_markers) - marker_starts = [start for start, _ in unique_markers] - result: list[dict[str, Any]] = [] - cursor = 0 - split_sequence = 0 - for source_note in notes: - note = copy.deepcopy(source_note) - note.pop("chord_symbol", None) - duration = note_duration(note) - end = cursor + duration - split_points = [cursor] + [start for start in marker_starts if cursor < start < end] + [end] - for index in range(len(split_points) - 1): - start = split_points[index] - stop = split_points[index + 1] - part_duration = stop - start - if part_duration <= 0: - continue - part = copy.deepcopy(note) - part["duration_ticks"] = part_duration - if len(split_points) > 2: - split_sequence += 1 - part["id"] = f"{note.get('id', 'note')}-split-{split_sequence}" - if part.get("kind", "note") == "note": - part["tie_to_next"] = index + 2 < len(split_points) - part["tie_from_previous"] = index > 0 - if start in marker_by_start: - part["chord_symbol"] = marker_by_start[start] - result.append(part) - cursor = end - return result - - -def slash_note(note_id: str, duration_ticks: int, chord_symbol: str = "") -> dict[str, Any]: - note: dict[str, Any] = { - "id": note_id, - "kind": "note", - "duration_ticks": duration_ticks, - "pitches": [copy.deepcopy(REST_PITCH)], - "notehead": "slash", - "hidden": False, - } - if chord_symbol: - note["chord_symbol"] = chord_symbol - return note - - -def sanitize_json(value: Any) -> Any: - if isinstance(value, dict): - return {key: sanitize_json(item) for key, item in value.items() if key not in FORBIDDEN_OUTPUT_FIELDS} - if isinstance(value, list): - return [sanitize_json(item) for item in value] - return copy.deepcopy(value) - - -def sanitized_note(note: dict[str, Any], *, strip_lyrics: bool, include_optional: bool = True) -> dict[str, Any]: - output = sanitize_json(note) +def copy_source_voice(measure: dict, *, strip_lyrics: bool) -> dict: + voice = copy.deepcopy(first_voice(measure)) if strip_lyrics: - output.pop("lyric", None) - if not include_optional and note.get("chord_optional") is True: - output.pop("chord_symbol", None) - output.setdefault("hidden", False) - return output + for note in voice['notes']: + note.pop('lyric', None) + note.pop('lyric_notation', None) + return voice -def copy_source_voice(measure: dict[str, Any], *, strip_lyrics: bool, include_optional: bool = True) -> dict[str, Any]: - voice = first_voice(measure) - tuplets = sanitize_json(voice.get("tuplets", [])) - notes = [sanitized_note(note, strip_lyrics=strip_lyrics, include_optional=include_optional) for note in measure_notes(measure)] - return { - "id": str(voice.get("id", "voice-source")), - "tuplets": tuplets if isinstance(tuplets, list) else [], - "notes": notes or [rest_note("arr-rest-empty", measure_capacity_ticks(str(measure.get("time_signature", "4/4"))))], - } +def make_measure(measure_id: str, source: dict, notes: list[dict], *, section_label: str = '') -> dict: + result = copy.deepcopy(source) + result.update({'id':measure_id, 'section_label':section_label, 'voices':[{'id':'voice-arranged', 'notes':notes}]}) + return result -def measure_notation_metadata(source: dict[str, Any]) -> dict[str, Any]: - return { - key: sanitize_json(value) - for key, value in source.items() - if key not in GENERATED_MEASURE_FIELDS - } +def reidentify(measures: list[dict], source_tuplets: list[dict]) -> list[dict]: + """Clone-local links survive; links cut by an arrangement boundary become attacks.""" + tone_map, event_map = {}, {} + tuplets = [] + for measure in measures: + for ni, note in enumerate(measure_notes(measure)): + old = note['id'] + note['id'] = f'{measure["id"]}-n{ni+1}' + event_map[old] = note['id'] + for pi, pitch in enumerate(note['pitches']): + old_pitch = pitch['id'] + pitch['id'] = f'{note["id"]}-p{pi+1}' + tone_map[old_pitch] = pitch['id'] + for measure in measures: + for note in measure_notes(measure): + for p in note['pitches']: + for field in ('tie_from','tie_to'): + if p.get(field) in tone_map: + p[field] = tone_map[p[field]] + else: + p.pop(field,None) + if note.get('tremolo', {}).get('partner'): + partner = note['tremolo']['partner'] + if partner in event_map: + note['tremolo']['partner'] = event_map[partner] + else: + note['tremolo'] = {**note['tremolo'], 'type':'single'} + note['tremolo'].pop('partner',None) + for source in source_tuplets: + members = [n for m in measures for n in measure_notes(m) if source['id'] in n.get('tuplet_ids', [])] + if not members: + continue + t = copy.deepcopy(source) + t.update({'id':f'{measures[0]["id"]}-{source["id"]}', 'start':members[0]['id'], 'end':members[-1]['id'], 'continued_start':source['start'] not in event_map, 'continued_end':source['end'] not in event_map}) + for n in members: + n['tuplet_ids'] = [t['id'] if v==source['id'] else v for v in n['tuplet_ids']] + tuplets.append(t) + return tuplets -def make_measure(measure_id: str, source: dict[str, Any], notes: list[dict[str, Any]], *, section_label: str = "", tuplets: list[Any] | None = None) -> dict[str, Any]: - measure: dict[str, Any] = { - "id": measure_id, - "time_signature": str(source.get("time_signature", "4/4")), - "key_signature": str(source.get("key_signature", "C")), - "voices": [{ - "id": f"voice-{measure_id}", - "tuplets": tuplets or [], - "notes": notes, - }], - } - if section_label: - measure["section_label"] = section_label - measure.update(measure_notation_metadata(source)) - return measure - - -def make_arranged_document(source_document: dict[str, Any], staff: dict[str, Any], *, staff_id: str, staff_name: str, role: str, arrangement_part_type: str, measures: list[dict[str, Any]]) -> dict[str, Any]: - source_metadata = source_document.get("metadata", {}) - metadata = canonicalize_score_metadata(source_metadata if isinstance(source_metadata, dict) else {}) - output: dict[str, Any] = { - "metadata": metadata, - "staves": [{ - "id": staff_id, - "name": staff_name, - "clef": str(staff.get("clef", "treble")), - "role": role, - "arranged": True, - "arrangement_part_type": arrangement_part_type, - "measures": measures, - }], - } - return sanitize_json(output) +def make_arranged_document(source_document: dict, staff: dict, *, staff_id: str, staff_name: str, role: str, arrangement_part_type: str, measures: list[dict], tuplets=None, octave_shifts=None) -> dict: + return {'schema_version':SCHEMA_VERSION, 'metadata':copy.deepcopy(source_document['metadata']), 'staves':[{'id':staff_id, 'name':staff_name, 'clef':copy.deepcopy(staff['clef']), 'role':role, 'arranged':True, 'arrangement_part_type':arrangement_part_type, 'measures':measures, 'tuplets':tuplets or [], 'octave_shifts':octave_shifts or []}]} def count_json_files(root: Path) -> int: - return sum(1 for path in root.rglob("*.json") if path.is_file()) + return sum(1 for p in root.rglob('*.json') if p.is_file()) def validate_generated_tree(root: Path) -> None: - for path in root.rglob("*.json"): - text = path.read_text(encoding="utf-8") - for field in FORBIDDEN_OUTPUT_FIELDS: - if f'"{field}"' in text: - raise ValueError(f"{path} contains forbidden generated field {field}") - json.loads(text) + for path in root.rglob('*.json'): + load_json(path) diff --git a/generator/summary.py b/generator/summary.py index 1e4ebd9..d399b7f 100644 --- a/generator/summary.py +++ b/generator/summary.py @@ -6,6 +6,7 @@ from typing import Any from generator.score import load_json, measure_notes from generator.transposition import TARGET_KEYS +from generator.model import fraction, pitch_name def _arranged_staff(document: dict[str, Any]) -> dict[str, Any]: @@ -20,17 +21,16 @@ def _measure_summary(staff: dict[str, Any], measure: dict[str, Any], measure_num summary: dict[str, Any] = { "measure": measure_number, "section_label": str(measure.get("section_label", "")), - "chord_symbols": [str(note.get("chord_symbol", "")) for note in notes if isinstance(note.get("chord_symbol"), str) and note.get("chord_symbol")], + "harmonies": [{"onset": str(fraction(h["onset"])), "symbol": h["symbol"]} for h in measure["harmonies"]], + "duration": str(fraction(measure["duration"])), "slash_count": sum(1 for note in notes if note.get("kind", "note") == "note" and note.get("notehead") == "slash"), } if staff.get("arrangement_part_type") == "vocal": summary["vocal_skeleton"] = [ - { - "kind": str(note.get("kind", "note")), - "duration_ticks": int(note.get("duration_ticks", 0)), - "display_ticks": int(note["display_ticks"]) if isinstance(note.get("display_ticks"), int) else None, - "has_lyric": isinstance(note.get("lyric"), str) and bool(note.get("lyric")), - } + f'{fraction(note["onset"])}+{fraction(note["duration"])} ' + + note['written']['type'] + '.'*note['written'].get('dots',0) + + ' ' + note['kind'] + ' ' + ','.join(pitch_name(p)+str(p['octave']) for p in note['pitches']) + + ' ' + note.get('lyric','') for note in notes ] return summary diff --git a/generator/transposition.py b/generator/transposition.py index 79a177e..6b4ecc3 100644 --- a/generator/transposition.py +++ b/generator/transposition.py @@ -2,34 +2,20 @@ from __future__ import annotations import copy from dataclasses import dataclass -from typing import Any + +from generator import harmony +from generator.model import LETTERS, NATURAL, key_from_fifths, key_label, parse_pitch_name, pitch_number @dataclass(frozen=True) class KeyProfile: name: str root: int - degrees: tuple[str, str, str, str, str, str, str] - prefer_flats: bool + fifths: int -KEY_PROFILES = ( - KeyProfile("C", 0, ("C", "D", "E", "F", "G", "A", "B"), False), - KeyProfile("Db", 1, ("Db", "Eb", "F", "Gb", "Ab", "Bb", "C"), True), - KeyProfile("D", 2, ("D", "E", "F#", "G", "A", "B", "C#"), False), - KeyProfile("Eb", 3, ("Eb", "F", "G", "Ab", "Bb", "C", "D"), True), - KeyProfile("E", 4, ("E", "F#", "G#", "A", "B", "C#", "D#"), False), - KeyProfile("F", 5, ("F", "G", "A", "Bb", "C", "D", "E"), True), - KeyProfile("Gb", 6, ("Gb", "Ab", "Bb", "Cb", "Db", "Eb", "F"), True), - KeyProfile("G", 7, ("G", "A", "B", "C", "D", "E", "F#"), False), - KeyProfile("Ab", 8, ("Ab", "Bb", "C", "Db", "Eb", "F", "G"), True), - KeyProfile("A", 9, ("A", "B", "C#", "D", "E", "F#", "G#"), False), - KeyProfile("Bb", 10, ("Bb", "C", "D", "Eb", "F", "G", "A"), True), - KeyProfile("B", 11, ("B", "C#", "D#", "E", "F#", "G#", "A#"), False), -) -TARGET_KEYS = tuple(profile.name for profile in KEY_PROFILES) -NATURAL_PITCH_CLASSES = {"c": 0, "d": 2, "e": 4, "f": 5, "g": 7, "a": 9, "b": 11} -LETTER_DEGREES = {"c": 0, "d": 1, "e": 2, "f": 3, "g": 4, "a": 5, "b": 6} +KEY_PROFILES = tuple(KeyProfile(name, root, fifths) for root, (name, fifths) in enumerate((('C',0),('Db',-5),('D',2),('Eb',-3),('E',4),('F',-1),('Gb',-6),('G',1),('Ab',-4),('A',3),('Bb',-2),('B',5)))) +TARGET_KEYS = tuple(p.name for p in KEY_PROFILES) def mod12(value: int) -> int: @@ -37,158 +23,126 @@ def mod12(value: int) -> int: def find_key(name: str) -> KeyProfile: - normalized = name.lower() - aliases = {"c#": "db", "d#": "eb", "f#": "gb", "g#": "ab", "a#": "bb"} - normalized = aliases.get(normalized, normalized) - for profile in KEY_PROFILES: - if profile.name.lower() == normalized: - return profile - raise ValueError(f"unsupported key signature: {name}") + tonic = parse_pitch_name(name) + for fifths in range(-14, 15): + key = key_from_fifths(fifths) + if key['tonic'] == tonic: + return KeyProfile(name, (NATURAL[tonic['letter']] + tonic['alter']) % 12, fifths) + raise ValueError(f'unsupported key signature: {name}') def signed_interval(source: KeyProfile, target: KeyProfile) -> int: - interval = mod12(target.root - source.root) - return interval - 12 if interval > 6 else interval + value = (target.root-source.root) % 12 + return value-12 if value > 6 else value -def split_spelling(value: str) -> tuple[str, str]: - if not value: - return "C", "" - return value[0], value[1:] +def interval_between(source: dict, target: dict) -> tuple[int, int]: + semitones = (NATURAL[target['letter']] + target['alter'] - NATURAL[source['letter']] - source['alter']) % 12 + if semitones > 6: + semitones -= 12 + steps = (LETTERS.index(target['letter']) - LETTERS.index(source['letter'])) % 7 + if semitones < 0 or semitones == 0 and steps > 3: + steps -= 7 + return steps, semitones -def accidental_offset(accidental: str) -> int: - value = accidental.lower() - if value in {"#", "sharp"}: - return 1 - if value in {"b", "flat"}: - return -1 - return 0 - - -def spelling_pitch_class(spelling: str) -> int: - letter, accidental = split_spelling(spelling) - return mod12(NATURAL_PITCH_CLASSES.get(letter.lower(), 0) + accidental_offset(accidental)) - - -def pitch_class(pitch: dict[str, Any]) -> int: - letter = str(pitch.get("letter", "c")).lower() - accidental = str(pitch.get("accidental", "n")) - return mod12(NATURAL_PITCH_CLASSES.get(letter, 0) + accidental_offset(accidental)) - - -def pitch_midi(pitch: dict[str, Any]) -> int: - octave = int(pitch.get("octave", 4)) - return octave * 12 + pitch_class(pitch) - - -def octave_for(midi: int, pitch_class_value: int) -> int: - octave = (midi - pitch_class_value) // 12 - while octave * 12 + pitch_class_value > midi: - octave -= 1 - while (octave + 1) * 12 + pitch_class_value <= midi: - octave += 1 - return octave - - -def matching_source_degree_for_pitch(pitch: dict[str, Any], source: KeyProfile) -> int | None: - actual = pitch_class(pitch) - source_letter_degree = LETTER_DEGREES.get(str(pitch.get("letter", "c")).lower(), 0) - for index, spelling in enumerate(source.degrees): - letter, _ = split_spelling(spelling) - if LETTER_DEGREES.get(letter.lower(), 0) == source_letter_degree and spelling_pitch_class(spelling) == actual: - return index - return None - - -def chromatic_spelling(pitch_class_value: int, prefer_flats: bool) -> str: - flats = ("C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B") - sharps = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") - return flats[pitch_class_value] if prefer_flats else sharps[pitch_class_value] - - -def transpose_pitch(pitch: dict[str, Any], source: KeyProfile, target: KeyProfile, interval: int) -> dict[str, Any]: - transposed_midi = pitch_midi(pitch) + interval - degree = matching_source_degree_for_pitch(pitch, source) - spelling = target.degrees[degree] if degree is not None else chromatic_spelling(mod12(transposed_midi), target.prefer_flats) - letter, accidental = split_spelling(spelling) - pc = spelling_pitch_class(spelling) - return { - "letter": letter.lower(), - "accidental": accidental or "n", - "octave": octave_for(transposed_midi, pc), - } - - -def parse_chord_root(value: str, offset: int) -> tuple[int, str] | None: - if offset >= len(value) or value[offset].upper() not in "ABCDEFG": - return None - length = 1 - if offset + length < len(value) and value[offset + length] in {"#", "b"}: - length += 1 - return length, value[offset:offset + length] - - -def matching_source_degree_for_root(root: str, source: KeyProfile) -> int | None: - root_letter, _ = split_spelling(root) - actual = spelling_pitch_class(root) - for index, spelling in enumerate(source.degrees): - letter, _ = split_spelling(spelling) - if LETTER_DEGREES.get(letter.lower(), 0) == LETTER_DEGREES.get(root_letter.lower(), 0) and spelling_pitch_class(spelling) == actual: - return index - return None - - -def transpose_root(root: str, source: KeyProfile, target: KeyProfile, interval: int) -> str: - degree = matching_source_degree_for_root(root, source) - if degree is not None: - return target.degrees[degree] - return chromatic_spelling(mod12(spelling_pitch_class(root) + interval), target.prefer_flats) - - -def transpose_chord_symbol(symbol: str, source: KeyProfile, target: KeyProfile, interval: int) -> str: - root = parse_chord_root(symbol, 0) - if root is None: - return symbol - root_length, root_text = root - result = transpose_root(root_text, source, target, interval) + symbol[root_length:] - slash = result.find("/") - if slash < 0: - return result - bass = parse_chord_root(result, slash + 1) - if bass is None: - return result - bass_length, bass_text = bass - return result[:slash + 1] + transpose_root(bass_text, source, target, interval) + result[slash + 1 + bass_length:] - - -def transpose_document(document: dict[str, Any], target_key_name: str) -> dict[str, Any]: - output = copy.deepcopy(document) - metadata = output.setdefault("metadata", {}) - if not isinstance(metadata, dict): - raise ValueError("score metadata must be an object") - source_key = find_key(str(metadata.get("key_signature", "C") or "C")) - target_key = find_key(target_key_name) - interval = signed_interval(source_key, target_key) - metadata["key_signature"] = target_key.name - for staff in output.get("staves", []): - if not isinstance(staff, dict): - continue - for measure in staff.get("measures", []): - if not isinstance(measure, dict): - continue - measure["key_signature"] = target_key.name - for voice in measure.get("voices", []): - if not isinstance(voice, dict): - continue - for note in voice.get("notes", []): - if not isinstance(note, dict): - continue - chord = note.get("chord_symbol") - if isinstance(chord, str) and chord: - note["chord_symbol"] = transpose_chord_symbol(chord, source_key, target_key, interval) - pitches = note.get("pitches") - if isinstance(pitches, list): - note["pitches"] = [transpose_pitch(pitch, source_key, target_key, interval) if isinstance(pitch, dict) else pitch for pitch in pitches] +def shift_pitch(pitch: dict, steps: int, semitones: int) -> dict: + output = copy.deepcopy(pitch) + original_octave = pitch.get('octave', 4) + diatonic = original_octave * 7 + LETTERS.index(pitch['letter']) + steps + octave, index = divmod(diatonic, 7) + output['letter'] = LETTERS[index] + output['octave'] = octave + output['alter'] = pitch_number({**pitch, 'octave':original_octave}) + semitones - (12 * (octave+1) + NATURAL[output['letter']]) + if 'octave' not in pitch: + output.pop('octave') + if 'accidental_display' in output: + output['accidental_display']['name'] = {-3:'triple-flat',-2:'flat-flat',-1:'flat',0:'natural',1:'sharp',2:'double-sharp',3:'triple-sharp'}.get(output['alter'], 'compound') return output + +def shift_key(key: dict, steps: int, semitones: int) -> dict: + tonic = shift_pitch(key['tonic'], steps, semitones) if key.get('tonic') else None + alterations = [0] * 7 + for index, letter in enumerate(LETTERS): + p = shift_pitch({'letter':letter, 'alter':key['alterations'][index]}, steps, semitones) + alterations[LETTERS.index(p['letter'])] = p['alter'] + result = {**copy.deepcopy(key), 'tonic':tonic, 'alterations':alterations} + if not key.get('custom') and tonic: + for fifths in range(-14,15): + candidate = key_from_fifths(fifths, key['mode']) + if candidate['tonic']==tonic and candidate['alterations']==alterations: + result['fifths'] = fifths + break + else: + result['custom'] = True + return result + + +def transpose_document(document: dict, target_key_name: str) -> dict: + output = copy.deepcopy(document) + metadata = output['metadata'] + key = metadata['key'] + origin = key.get('tonic') or metadata.get('transpose_origin') + if origin is None: + raise ValueError('keyless melody practice requires a vamp:tonic score property') + target = parse_pitch_name(target_key_name) + steps, semitones = interval_between(origin, target) + metadata['key'] = shift_key(key, steps, semitones) + metadata['key_signature'] = key_label(metadata['key']) + metadata['variant_tonic'] = target_key_name + for staff in output['staves']: + for measure in staff['measures']: + measure['key'] = shift_key(measure['key'], steps, semitones) + measure['key_signature'] = key_label(measure['key']) + for change in measure.get('attributes', []): + if 'key' in change: + change['key'] = shift_key(change['key'], steps, semitones) + for h in measure.get('harmonies', []): + for field in ('root','bass'): + if field in h: + h[field] = shift_pitch(h[field], steps, semitones) + h['symbol'] = harmony.symbol(h) + for voice in measure['voices']: + for note in voice['notes']: + note['pitches'] = [shift_pitch(p, steps, semitones) for p in note.get('pitches', [])] + return output + + +def concert_document(document: dict) -> dict: + """Normalize MusicXML instrument transposition before concert-pitch arranging.""" + output = copy.deepcopy(document) + zero = {'diatonic':0, 'chromatic':0, 'octave_change':0} + for staff in output['staves']: + for measure in staff['measures']: + initial = measure['transpose'] + changes = copy.deepcopy(measure.get('attributes', [])) + + def interval(onset): + state = initial + from generator.model import fraction + for change in changes: + if fraction(change['onset']) <= onset and 'transpose' in change: + state = change['transpose'] + return state['diatonic']+7*state['octave_change'], state['chromatic']+12*state['octave_change'] + + from generator.model import fraction + measure['key'] = shift_key(measure['key'], *interval(0)) + measure['key_signature'] = key_label(measure['key']) + for n in measure['voices'][0]['notes']: + n['pitches'] = [shift_pitch(p,*interval(fraction(n['onset']))) for p in n['pitches']] + for h in measure['harmonies']: + for field in ('root','bass'): + if field in h: + h[field] = shift_pitch(h[field],*interval(fraction(h['onset']))) + h['symbol'] = harmony.symbol(h) + for change in measure.get('attributes',[]): + if 'key' in change: + change['key'] = shift_key(change['key'],*interval(fraction(change['onset']))) + if 'transpose' in change: + change['transpose'] = copy.deepcopy(zero) + measure['transpose'] = copy.deepcopy(zero) + first = output['staves'][0]['measures'][0] + output['metadata']['key'] = copy.deepcopy(first['key']) + output['metadata']['key_signature'] = first['key_signature'] + return output diff --git a/include/vamp_zero/notation.hpp b/include/vamp_zero/notation.hpp new file mode 100644 index 0000000..bd7c422 --- /dev/null +++ b/include/vamp_zero/notation.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace vamp_zero { +auto parse_notation_document(std::string_view json, std::string source_path) -> std::expected; +auto written_quarters(score_written_duration const& written) -> double; +} diff --git a/include/vamp_zero/score.hpp b/include/vamp_zero/score.hpp index 468661e..891e513 100644 --- a/include/vamp_zero/score.hpp +++ b/include/vamp_zero/score.hpp @@ -18,6 +18,10 @@ enum class score_accidental_mark { FLAT, NATURAL, SHARP, + DOUBLE_FLAT, + DOUBLE_SHARP, + TRIPLE_FLAT, + TRIPLE_SHARP, }; enum class score_layout_item_kind { @@ -48,6 +52,8 @@ enum class score_layout_item_role { TIE, TUPLET, TUPLET_BRACKET, + OCTAVE_SHIFT, + TREMOLO, TITLE, COMPOSER, PART, @@ -71,6 +77,7 @@ struct score_layout_item { std::float32_t staff_spacing{}; std::int32_t codepoint{}; std::int32_t text_scale{}; + std::float32_t text_size_multiplier{1.0F}; std::size_t system_index{std::numeric_limits::max()}; std::size_t measure_index{std::numeric_limits::max()}; std::size_t note_index{std::numeric_limits::max()}; @@ -86,6 +93,8 @@ struct score_page_layout { struct score_system_range { std::size_t first_measure{}; std::size_t measure_count{}; + std::size_t first_note{}; + std::size_t note_count{std::numeric_limits::max()}; }; constexpr auto score_measures_per_system = std::size_t{4}; @@ -95,6 +104,7 @@ auto notated_accidentals_for_measure(vamp_zero::score_measure const& measure, st auto score_system_ranges(vamp_zero::score_document const& score) -> std::vector; auto score_page_count(vamp_zero::score_document const& score) -> std::size_t; auto layout_score_page(luma::rect bounds, vamp_zero::score_document const& score, std::size_t page_index) -> vamp_zero::score_page_layout; +auto draw_score_layout(luma::draw_context& context, vamp_zero::score_page_layout const& layout) -> void; auto draw_score_page(luma::draw_context& context, luma::rect bounds, vamp_zero::score_document const& score, std::size_t page_index) -> void; } diff --git a/include/vamp_zero/types.hpp b/include/vamp_zero/types.hpp index 31985a0..91b5ee3 100644 --- a/include/vamp_zero/types.hpp +++ b/include/vamp_zero/types.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -10,6 +12,41 @@ namespace vamp_zero { +struct score_time { + std::int64_t numerator{}; + std::int64_t denominator{1}; + auto quarters() const -> double { return static_cast(numerator) / static_cast(denominator); } +}; + +struct score_written_duration { + std::string type{}; + std::int32_t dots{}; + score_time ratio{1, 1}; + std::string normal_type{}; + std::int32_t normal_dots{}; +}; + +struct score_beam { + std::int32_t level{1}; + std::string type{}; +}; + +struct score_attribute { + score_time onset{}; + std::string clef{}; + std::string time_signature{}; + std::string key_signature{}; + std::optional> key_alterations{}; + std::vector beats{}; +}; + +struct score_harmony { + std::string id{}; + score_time onset{}; + std::string symbol{}; + std::string kind{}; +}; + enum class hardware_mode { FAKE, REAL, @@ -19,14 +56,23 @@ struct score_pitch { std::char_t letter{'c'}; std::int32_t octave{}; std::string accidental{}; + std::string id{}; + std::optional alteration{}; + score_time duration{}; + score_written_duration written{}; + std::string tie_to{}; + std::string tie_from{}; + std::bool_t explicit_accidental{}; + std::bool_t accidental_parentheses{}; + std::bool_t accidental_bracket{}; }; struct score_note { std::string id{}; std::string kind{}; std::bool_t hidden{}; - std::int64_t duration_ticks{}; - std::int64_t display_ticks{}; + double duration_ticks{}; + double display_ticks{}; std::string notehead{}; std::string chord_symbol{}; std::string lyric{}; @@ -34,12 +80,34 @@ struct score_note { std::bool_t tie_from_previous{}; std::string tuplet_id{}; std::vector pitches{}; + score_time onset{}; + score_time duration{}; + score_written_duration written{}; + std::vector beams{}; + std::vector tuplet_ids{}; + std::string stem{}; + std::bool_t grace{}; + std::bool_t grace_slash{}; + std::bool_t cue{}; + std::bool_t measure_rest{}; + std::int32_t tremolo_marks{}; + std::string tremolo_type{}; + std::string tremolo_partner{}; + std::string lyric_extend{}; + std::string clef{}; + std::int32_t display_octaves{}; + std::optional rest_position{}; }; struct score_tuplet { std::string id{}; std::int32_t note_count{}; std::int32_t space_count{}; + std::string start{}; + std::string end{}; + std::int32_t depth{}; + std::string bracket{"auto"}; + std::string show_number{"actual"}; }; struct score_measure { @@ -49,6 +117,21 @@ struct score_measure { std::string key_signature{}; std::vector notes{}; std::vector tuplets{}; + score_time duration{}; + std::string clef{}; + std::array key_alterations{}; + std::bool_t structured_key{}; + std::vector beats{}; + std::vector harmonies{}; + std::vector attributes{}; + std::string right_barline{"regular"}; + std::bool_t implicit{}; +}; + +struct score_octave_shift { + score_time start{}; + score_time end{}; + std::int32_t octaves{}; }; struct score_document { @@ -63,6 +146,7 @@ struct score_document { std::string source_path{}; std::string source_json{}; std::vector measures{}; + std::vector octave_shifts{}; }; struct library_part { diff --git a/modulus.py b/modulus.py index 9a34352..015b0fb 100755 --- a/modulus.py +++ b/modulus.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import json +import hashlib import os import pathlib import shlex @@ -136,6 +137,9 @@ RUN python -m pip install --no-cache-dir conan=={CONAN_VERSION} WORKDIR /workspace """ +def build_requirements_digest(): + return hashlib.sha256((repo_root() / "tools/requirements.txt").read_bytes()).hexdigest() + def product_build_dockerfile(): return f"""FROM {DEFAULT_BUILD_BASE_IMAGE} @@ -202,6 +206,8 @@ COPY tools/requirements.txt /tmp/vamp-build-requirements.txt RUN python -m pip install --no-cache-dir \\ conan=={CONAN_VERSION} -r /tmp/vamp-build-requirements.txt +LABEL org.khodo.product-vamp.build-requirements="{build_requirements_digest()}" + WORKDIR /workspace RUN mkdir -p /opt && touch /opt/modulus-build-image @@ -482,6 +488,19 @@ def delegate(command, argv): ensure_runtime(root) sys.path.insert(0, str(root / RUNTIME_DIR)) import modulus.cli + original_image_available = modulus.cli.docker_image_available + + def current_build_image(context): + if not original_image_available(context): + return False + inspected = subprocess.run( + ["docker", "image", "inspect", context.docker_image, "--format", + '{{index .Config.Labels "org.khodo.product-vamp.build-requirements"}}'], + capture_output=True, text=True, check=False, + ) + return inspected.returncode == 0 and inspected.stdout.strip() == build_requirements_digest() + + modulus.cli.docker_image_available = current_build_image original_docker_run = modulus.cli.docker_run def docker_run_with_host_socket(context, command, docker_args=None, env_overrides=None): @@ -502,6 +521,8 @@ def delegate(command, argv): try: return modulus.cli.main(command) finally: + modulus.cli.docker_image_available = original_image_available + modulus.cli.docker_run = original_docker_run os.chdir(previous_cwd) sys.argv = previous_argv diff --git a/src/vamp_zero/database.cpp b/src/vamp_zero/database.cpp index f4bbf8a..2649570 100644 --- a/src/vamp_zero/database.cpp +++ b/src/vamp_zero/database.cpp @@ -362,6 +362,7 @@ ON CONFLICT(id) DO UPDATE SET THEN excluded.updated_at_ms ELSE songs.updated_at_ms END +WHERE songs.catalog_managed = 1 )sql", std::move(params)); if (!inserted) { @@ -417,6 +418,7 @@ ON CONFLICT(id) DO UPDATE SET THEN excluded.updated_at_ms ELSE practice_exercises.updated_at_ms END +WHERE practice_exercises.catalog_managed = 1 )sql", std::move(params)); if (!inserted) { diff --git a/src/vamp_zero/library.cpp b/src/vamp_zero/library.cpp index ac5f9e5..634c1be 100644 --- a/src/vamp_zero/library.cpp +++ b/src/vamp_zero/library.cpp @@ -20,6 +20,7 @@ #include #include +#include namespace { @@ -27,72 +28,6 @@ auto invalid(std::string message) -> modulus::error { return vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, std::move(message)); } -auto required_string(boost::json::object const& object, std::char_t const* field) -> std::expected { - auto found = object.find(field); - if (found == object.end() || !found->value().is_string()) { - return std::unexpected(invalid(std::string{field} + " must be a string")); - } - auto value = std::string{found->value().as_string()}; - if (value.empty()) { - return std::unexpected(invalid(std::string{field} + " must not be empty")); - } - return value; -} - -auto optional_string_value(boost::json::object const& object, std::char_t const* field, std::string fallback = {}) -> std::expected { - auto found = object.find(field); - if (found == object.end()) { - return fallback; - } - if (!found->value().is_string()) { - return std::unexpected(invalid(std::string{field} + " must be a string")); - } - return std::string{found->value().as_string()}; -} - -auto optional_bool_value(boost::json::object const& object, std::char_t const* field, std::bool_t fallback = false) -> std::expected { - auto found = object.find(field); - if (found == object.end()) { - return fallback; - } - if (!found->value().is_bool()) { - return std::unexpected(invalid(std::string{field} + " must be a boolean")); - } - return found->value().as_bool(); -} - -auto optional_int64_value(boost::json::object const& object, std::char_t const* field, std::int64_t fallback = 0) -> std::expected { - auto found = object.find(field); - if (found == object.end()) { - return fallback; - } - if (found->value().is_int64()) { - return found->value().as_int64(); - } - if (found->value().is_uint64()) { - auto value = found->value().as_uint64(); - if (value <= static_cast(std::numeric_limits::max())) { - return static_cast(value); - } - } - return std::unexpected(invalid(std::string{field} + " must be an integer")); -} - -auto require_object(boost::json::value const& value, std::string_view label) -> std::expected { - if (!value.is_object()) { - return std::unexpected(invalid(std::string{label} + " must be an object")); - } - return &value.as_object(); -} - -auto require_array(boost::json::object const& object, std::char_t const* field) -> std::expected { - auto found = object.find(field); - if (found == object.end() || !found->value().is_array()) { - return std::unexpected(invalid(std::string{field} + " must be an array")); - } - return &found->value().as_array(); -} - auto human_label(std::string value) -> std::string { if (value.empty()) { return "Score"; @@ -116,164 +51,6 @@ auto practice_type_label(std::string const& value) -> std::string { return human_label(value) + " Practice"; } -auto generated_source_id(std::string const& source_path, std::string const& title, std::string const& part) -> std::string { - if (!source_path.empty()) { - auto stem = std::filesystem::path{source_path}.stem().string(); - if (!stem.empty()) { - return stem; - } - } - auto value = title + "-" + part; - for (auto& ch : value) { - if (ch >= 'A' && ch <= 'Z') { - ch = static_cast(ch + ('a' - 'A')); - } else if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))) { - ch = '-'; - } - } - return value.empty() ? "score" : value; -} - -auto parse_pitch(boost::json::value const& value) -> std::expected { - auto object = require_object(value, "pitch"); - if (!object) { - return std::unexpected(object.error()); - } - auto letter = optional_string_value(**object, "letter", "c"); - auto octave = optional_int64_value(**object, "octave", 4); - auto accidental = optional_string_value(**object, "accidental", {}); - if (!letter || !octave || !accidental) { - return std::unexpected(!letter ? letter.error() : (!octave ? octave.error() : accidental.error())); - } - return vamp_zero::score_pitch{ - .letter = letter->empty() ? 'c' : (*letter)[0], - .octave = static_cast(*octave), - .accidental = std::move(*accidental), - }; -} - -auto parse_note(boost::json::value const& value) -> std::expected { - auto object = require_object(value, "note"); - if (!object) { - return std::unexpected(object.error()); - } - auto id = optional_string_value(**object, "id", {}); - auto kind = optional_string_value(**object, "kind", "note"); - auto hidden = optional_bool_value(**object, "hidden", false); - auto duration = optional_int64_value(**object, "duration_ticks", 0); - auto display = std::expected{0}; - if (duration) { - display = optional_int64_value(**object, "display_ticks", *duration); - } - auto notehead = optional_string_value(**object, "notehead", {}); - auto chord = optional_string_value(**object, "chord_symbol", {}); - auto lyric = optional_string_value(**object, "lyric", {}); - auto tie_to_next = optional_bool_value(**object, "tie_to_next", false); - auto tie_from_previous = optional_bool_value(**object, "tie_from_previous", false); - auto tuplet_id = optional_string_value(**object, "tuplet_id", {}); - if (!id || !kind || !hidden || !duration || !display || !notehead || !chord || !lyric || !tie_to_next || !tie_from_previous || !tuplet_id) { - return std::unexpected(!id ? id.error() : (!kind ? kind.error() : (!hidden ? hidden.error() : (!duration ? duration.error() : (!display ? display.error() : (!notehead ? notehead.error() : (!chord ? chord.error() : (!lyric ? lyric.error() : (!tie_to_next ? tie_to_next.error() : (!tie_from_previous ? tie_from_previous.error() : tuplet_id.error())))))))))); - } - auto note = vamp_zero::score_note{ - .id = std::move(*id), - .kind = std::move(*kind), - .hidden = *hidden, - .duration_ticks = *duration, - .display_ticks = *display, - .notehead = std::move(*notehead), - .chord_symbol = std::move(*chord), - .lyric = std::move(*lyric), - .tie_to_next = *tie_to_next, - .tie_from_previous = *tie_from_previous, - .tuplet_id = std::move(*tuplet_id), - }; - if (auto found = (*object)->find("pitches"); found != (*object)->end()) { - if (!found->value().is_array()) { - return std::unexpected(invalid("pitches must be an array")); - } - for (auto const& pitch_value : found->value().as_array()) { - auto pitch = parse_pitch(pitch_value); - if (!pitch) { - return std::unexpected(pitch.error()); - } - note.pitches.push_back(std::move(*pitch)); - } - } - return note; -} - -auto parse_tuplet(boost::json::value const& value) -> std::expected { - auto object = require_object(value, "tuplet"); - if (!object) { - return std::unexpected(object.error()); - } - auto id = required_string(**object, "id"); - auto note_count = optional_int64_value(**object, "note_count", 0); - auto space_count = optional_int64_value(**object, "space_count", 0); - if (!id || !note_count || !space_count) { - return std::unexpected(!id ? id.error() : (!note_count ? note_count.error() : space_count.error())); - } - return vamp_zero::score_tuplet{ - .id = std::move(*id), - .note_count = static_cast(*note_count), - .space_count = static_cast(*space_count), - }; -} - -auto parse_measure(boost::json::value const& value) -> std::expected { - auto object = require_object(value, "measure"); - if (!object) { - return std::unexpected(object.error()); - } - auto id = optional_string_value(**object, "id", {}); - auto section = optional_string_value(**object, "section_label", {}); - auto time = optional_string_value(**object, "time_signature", {}); - auto key = optional_string_value(**object, "key_signature", {}); - if (!id || !section || !time || !key) { - return std::unexpected(!id ? id.error() : (!section ? section.error() : (!time ? time.error() : key.error()))); - } - auto measure = vamp_zero::score_measure{ - .id = std::move(*id), - .section_label = std::move(*section), - .time_signature = std::move(*time), - .key_signature = std::move(*key), - }; - auto voices = require_array(**object, "voices"); - if (!voices) { - return std::unexpected(voices.error()); - } - for (auto const& voice_value : **voices) { - auto voice = require_object(voice_value, "voice"); - if (!voice) { - return std::unexpected(voice.error()); - } - auto notes = require_array(**voice, "notes"); - if (!notes) { - return std::unexpected(notes.error()); - } - for (auto const& note_value : **notes) { - auto note = parse_note(note_value); - if (!note) { - return std::unexpected(note.error()); - } - measure.notes.push_back(std::move(*note)); - } - if (auto tuplets_value = (*voice)->find("tuplets"); tuplets_value != (*voice)->end()) { - if (!tuplets_value->value().is_array()) { - return std::unexpected(invalid("tuplets must be an array")); - } - for (auto const& tuplet_value : tuplets_value->value().as_array()) { - auto tuplet = parse_tuplet(tuplet_value); - if (!tuplet) { - return std::unexpected(tuplet.error()); - } - measure.tuplets.push_back(std::move(*tuplet)); - } - } - } - return measure; -} - auto load_score_documents(std::filesystem::path const& directory, std::bool_t required) -> std::expected, modulus::error> { auto error = std::error_code{}; if (!std::filesystem::exists(directory, error)) { @@ -334,7 +111,8 @@ auto key_rank(std::string const& key) -> std::size_t { "Bb", "B", }; - auto found = std::find(keys.begin(), keys.end(), key); + auto tonic = key.substr(0, key.find(' ')); + auto found = std::find(keys.begin(), keys.end(), tonic); return found == keys.end() ? keys.size() : static_cast(std::distance(keys.begin(), found)); } @@ -367,90 +145,7 @@ auto same_practice_exercise(vamp_zero::practice_exercise const& left, vamp_zero: } auto vamp_zero::parse_score_document(std::string_view json, std::string source_path) -> std::expected { - auto error = boost::system::error_code{}; - auto parsed = boost::json::parse(json, error); - if (error) { - return std::unexpected(invalid("score JSON is invalid: " + error.message())); - } - auto root = require_object(parsed, "score root"); - if (!root) { - return std::unexpected(root.error()); - } - auto metadata_value = (*root)->find("metadata"); - if (metadata_value == (*root)->end()) { - return std::unexpected(invalid("score metadata is required")); - } - auto metadata = require_object(metadata_value->value(), "metadata"); - if (!metadata) { - return std::unexpected(metadata.error()); - } - auto title = required_string(**metadata, "title"); - auto composer = optional_string_value(**metadata, "composer", {}); - auto key = optional_string_value(**metadata, "key_signature", {}); - auto time = optional_string_value(**metadata, "time_signature", {}); - if (!title || !composer || !key || !time) { - return std::unexpected(!title ? title.error() : (!composer ? composer.error() : (!key ? key.error() : time.error()))); - } - - auto staves = require_array(**root, "staves"); - if (!staves) { - return std::unexpected(staves.error()); - } - if ((*staves)->empty() || !(*staves)->front().is_object()) { - return std::unexpected(invalid("score must contain at least one staff")); - } - auto const& staff = (*staves)->front().as_object(); - auto part = optional_string_value(staff, "arrangement_part_type", {}); - if (!part || part->empty()) { - part = optional_string_value(staff, "role", {}); - } - if (!part || part->empty()) { - part = optional_string_value(staff, "name", "score"); - } - if (!part) { - return std::unexpected(part.error()); - } - auto clef = optional_string_value(staff, "clef", "treble"); - if (!clef) { - return std::unexpected(clef.error()); - } - - auto measures = require_array(staff, "measures"); - if (!measures) { - return std::unexpected(measures.error()); - } - auto document = vamp_zero::score_document{ - .id = generated_source_id(source_path, *title, *part), - .title = std::move(*title), - .composer = std::move(*composer), - .key_signature = std::move(*key), - .time_signature = std::move(*time), - .clef = clef->empty() ? std::string{"treble"} : std::move(*clef), - .part = human_label(*part), - .arrangement_part_type = std::move(*part), - .source_path = std::move(source_path), - .source_json = std::string{json}, - }; - for (auto const& measure_value : **measures) { - auto measure = parse_measure(measure_value); - if (!measure) { - return std::unexpected(measure.error()); - } - if (document.key_signature.empty() && !measure->key_signature.empty()) { - document.key_signature = measure->key_signature; - } - if (document.time_signature.empty() && !measure->time_signature.empty()) { - document.time_signature = measure->time_signature; - } - document.measures.push_back(std::move(*measure)); - } - if (document.key_signature.empty()) { - document.key_signature = "C"; - } - if (document.time_signature.empty()) { - document.time_signature = "4/4"; - } - return document; + return vamp_zero::parse_notation_document(json, std::move(source_path)); } auto vamp_zero::group_library_parts(std::vector parts) -> vamp_zero::library_state { diff --git a/src/vamp_zero/notation.cpp b/src/vamp_zero/notation.cpp new file mode 100644 index 0000000..e95f718 --- /dev/null +++ b/src/vamp_zero/notation.cpp @@ -0,0 +1,347 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +using object = boost::json::object; +using value = boost::json::value; +using exact_time = boost::rational; +auto exact(vamp_zero::score_time t) -> exact_time { return {t.numerator, t.denominator}; } +constexpr auto types = std::array{"maxima", "long", "breve", "whole", "half", "quarter", "eighth", "16th", "32nd", "64th", "128th", "256th", "512th", "1024th"}; + +auto notation_error(std::string const& message) -> void { throw std::invalid_argument(message); } +auto text(object const& o, char const* key, std::string fallback = {}) -> std::string { + auto p = o.if_contains(key); + return p == nullptr ? fallback : std::string{p->as_string()}; +} +auto integer(value const& v) -> std::int64_t { return boost::json::value_to(v); } +auto integer(object const& o, char const* key, std::int64_t fallback = 0) -> std::int64_t { + auto p = o.if_contains(key); + return p == nullptr ? fallback : integer(*p); +} +auto boolean(object const& o, char const* key) -> bool { + auto p = o.if_contains(key); + return p != nullptr && p->as_bool(); +} +auto time(value const& v) -> vamp_zero::score_time { + auto const& o = v.as_object(); + auto n = integer(o.at("numerator")); + auto d = integer(o.at("denominator")); + if (o.size() != 2 || n < 0 || d <= 0 || std::gcd(n, d) != 1) notation_error("invalid or unreduced musical time"); + return {n, d}; +} +auto written(value const& v) -> vamp_zero::score_written_duration { + auto const& o = v.as_object(); + auto name = text(o, "type"); + auto dots = integer(o, "dots"); + if (std::find(types.begin(), types.end(), name) == types.end() || dots < 0 || dots > 16) notation_error("invalid written duration"); + auto result = vamp_zero::score_written_duration{.type = name, .dots = static_cast(dots)}; + if (auto ratio = o.if_contains("ratio")) { + result.ratio = time(*ratio); + if (result.ratio.numerator == 0) notation_error("duration ratio must be positive"); + } + if (auto normal = o.if_contains("normal")) { + auto parsed = written(*normal); + result.normal_type = parsed.type; + result.normal_dots = parsed.dots; + } + return result; +} +auto metric_length(vamp_zero::score_written_duration const& written) -> exact_time { + auto index = std::distance(types.begin(), std::find(types.begin(), types.end(), written.type)); + auto dots = std::int64_t{1} << written.dots; + return exact_time{32, std::int64_t{1} << index} * exact_time{2*dots-1, dots} * exact(written.ratio); +} +auto clef(value const& v) -> std::string { + auto const& o = v.as_object(); + auto sign = text(o, "sign"); + auto line = integer(o, "line"); + auto octave = integer(o, "octave_change"); + if ((sign != "G" && sign != "F" && sign != "C") || line < 1 || line > 5 || octave < -3 || octave > 3) notation_error("invalid pitched clef"); + return sign + ":" + std::to_string(line) + ":" + std::to_string(octave); +} +auto alterations(value const& v) -> std::array { + auto const& values = v.as_object().at("alterations").as_array(); + if (values.size() != 7) notation_error("key must have seven diatonic alterations"); + auto result = std::array{}; + for (auto i = std::size_t{}; i < 7; ++i) { + auto a = integer(values[i]); + if (a < -3 || a > 3) notation_error("key alteration outside supported engraving range"); + result[i] = static_cast(a); + } + return result; +} +auto pitch(value const& v) -> vamp_zero::score_pitch { + auto const& o = v.as_object(); + auto letter = text(o, "letter"); + auto alter = integer(o.at("alter")); + auto octave = integer(o.at("octave")); + if (letter.size() != 1 || std::string_view{"cdefgab"}.find(letter[0]) == std::string_view::npos || alter < -3 || alter > 3 || octave < -4 || octave > 16) notation_error("invalid spelled pitch"); + auto result = vamp_zero::score_pitch{.letter = letter[0], .octave = static_cast(octave), .accidental = alter == 0 ? "n" : std::string(static_cast(std::abs(alter)), alter > 0 ? '#' : 'b'), .id = text(o, "id"), .alteration = static_cast(alter), .duration = time(o.at("duration")), .written = written(o.at("written")), .tie_to = text(o, "tie_to"), .tie_from = text(o, "tie_from")}; + if (auto a = o.if_contains("accidental_display")) { + result.explicit_accidental = true; + result.accidental_parentheses = boolean(a->as_object(), "parentheses"); + result.accidental_bracket = boolean(a->as_object(), "bracket"); + } + return result; +} +auto note(value const& v) -> vamp_zero::score_note { + auto const& o = v.as_object(); + auto result = vamp_zero::score_note{}; + result.id = text(o, "id"); + result.kind = text(o, "kind"); + if (result.kind != "note" && result.kind != "rest" && result.kind != "spacer") notation_error("invalid event kind"); + result.hidden = boolean(o, "hidden") || result.kind == "spacer"; + result.onset = time(o.at("onset")); + result.duration = time(o.at("duration")); + result.written = written(o.at("written")); + // Layout coordinates are floating point; the parsed rational remains authoritative. + result.duration_ticks = result.duration.quarters() * 4096.0; + result.display_ticks = vamp_zero::written_quarters(result.written) * 4096.0; + result.lyric = text(o, "lyric"); + result.notehead = text(o, "notehead"); + result.stem = text(o, "stem"); + result.cue = boolean(o, "cue"); + result.measure_rest = boolean(o, "measure_rest"); + if (auto position = o.if_contains("rest_position")) { + auto const& p = position->as_object(); + auto letter = text(p,"letter"); + auto octave = integer(p,"octave"); + if (letter.size()!=1 || std::string_view{"cdefgab"}.find(letter[0])==std::string_view::npos || octave < -4 || octave > 16) notation_error("invalid rest position"); + result.rest_position = vamp_zero::score_pitch{.letter=letter[0], .octave=static_cast(octave)}; + } + if (auto grace = o.if_contains("grace")) { + result.grace = true; + result.grace_slash = boolean(grace->as_object(), "slash"); + } + if (result.grace != (result.duration.numerator == 0)) notation_error("only grace notes may have zero metric duration"); + if (!result.grace && !result.measure_rest && exact(result.duration) != metric_length(result.written)) notation_error("metric and written event durations disagree"); + for (auto const& p : o.at("pitches").as_array()) { + result.pitches.push_back(pitch(p)); + if (!result.grace && result.pitches.back().duration.numerator == 0) notation_error("metric chord tones require positive duration"); + if (!result.grace && exact(result.pitches.back().duration) != metric_length(result.pitches.back().written)) notation_error("metric and written chord-tone durations disagree"); + if (exact(result.pitches.back().duration) > exact(result.duration)) notation_error("chord tone exceeds its anchor duration"); + } + if (result.kind != "note" && !result.pitches.empty()) notation_error("rests/spacers cannot contain pitches"); + if (result.kind == "note" && result.pitches.empty() && result.notehead != "slash") notation_error("pitched event has no pitches"); + result.tie_to_next = std::any_of(result.pitches.begin(), result.pitches.end(), [](auto const& p) { return !p.tie_to.empty(); }); + result.tie_from_previous = std::all_of(result.pitches.begin(), result.pitches.end(), [](auto const& p) { return !p.tie_from.empty(); }); + if (auto tuplets = o.if_contains("tuplet_ids")) for (auto const& id : tuplets->as_array()) result.tuplet_ids.emplace_back(id.as_string()); + if (!result.tuplet_ids.empty()) result.tuplet_id = result.tuplet_ids.back(); + if (auto beams = o.if_contains("beams")) for (auto const& b : beams->as_array()) { + auto const& beam = b.as_object(); + auto level = integer(beam, "level"); + auto type = text(beam, "type"); + if (level < 1 || level > 8 || (type != "begin" && type != "continue" && type != "end" && type != "forward hook" && type != "backward hook")) notation_error("invalid beam"); + result.beams.push_back({static_cast(level), type}); + } + if (auto tremolo = o.if_contains("tremolo")) { + auto marks = integer(tremolo->as_object(), "marks"); + if (marks < 0 || marks > 8) notation_error("invalid measured tremolo"); + result.tremolo_marks = static_cast(marks); + result.tremolo_type = text(tremolo->as_object(), "type"); + if (result.tremolo_type != "single" && result.tremolo_type != "start" && result.tremolo_type != "stop") notation_error("invalid tremolo type"); + result.tremolo_partner = text(tremolo->as_object(), "partner"); + } + if (auto lyric = o.if_contains("lyric_notation")) result.lyric_extend = text(lyric->as_object(), "extend"); + return result; +} +auto beats(value const& v) -> std::vector { + auto result = std::vector{}; + if (v.as_object().at("groups").as_array().empty() && !boolean(v.as_object(), "unmetered")) notation_error("meter requires beat groups or senza misura"); + for (auto const& group : v.as_object().at("groups").as_array()) { + auto unit = integer(group.as_object().at("unit")); + auto const& counts = group.as_object().at("beats").as_array(); + if (unit <= 0 || unit > 4096 || counts.empty()) notation_error("invalid meter group"); + for (auto const& count : counts) { + auto n = integer(count); + if (n <= 0 || n > 1024) notation_error("invalid meter count"); + auto compound = counts.size() == 1 && unit >= 8 && n > 3 && n % 3 == 0; + auto repetitions = counts.size() > 1 ? 1 : compound ? n / 3 : n; + auto numerator = counts.size() > 1 ? n * 4 : compound ? 12 : 4; + auto divisor = std::gcd(numerator, unit); + for (auto i = std::int64_t{}; i < repetitions; ++i) result.push_back({numerator / divisor, unit / divisor}); + } + } + return result; +} + +auto meter_text(value const& v) -> std::string { + auto const& meter = v.as_object(); + auto symbol = text(meter,"symbol","normal"); + if (symbol == "common" || symbol == "cut") return symbol; + if (boolean(meter,"unmetered")) return "senza misura"; + auto result = std::string{}; + for (auto const& group : meter.at("groups").as_array()) { + if (!result.empty()) result += '+'; + auto numerator = std::string{}; + for (auto const& count : group.as_object().at("beats").as_array()) { + if (!numerator.empty()) numerator += '+'; + numerator += std::to_string(integer(count)); + } + result += numerator + '/' + std::to_string(integer(group.as_object().at("unit"))); + } + return result; +} +auto measure(value const& v) -> vamp_zero::score_measure { + auto const& o = v.as_object(); + auto result = vamp_zero::score_measure{.id = text(o, "id"), .section_label = text(o, "section_label"), .time_signature = text(o, "time_signature"), .key_signature = text(o, "key_signature")}; + result.duration = time(o.at("duration")); + if (result.duration.numerator == 0) notation_error("measure has zero duration"); + result.clef = clef(o.at("clef")); + result.key_alterations = alterations(o.at("key")); + result.structured_key = true; + result.beats = beats(o.at("meter")); + result.time_signature = meter_text(o.at("meter")); + result.implicit = boolean(o, "implicit"); + auto const& voices = o.at("voices").as_array(); + if (voices.size() != 1) notation_error("exactly one voice is supported; voices cannot be flattened"); + auto cursor = exact_time{}; + for (auto const& item : voices.front().as_object().at("notes").as_array()) { + auto n = note(item); + if (exact(n.onset) + exact(n.duration) > exact(result.duration)) notation_error("event extends beyond measure"); + if (!n.grace) { + if (exact(n.onset) < cursor) notation_error("events overlap within one rhythmic voice"); + cursor = exact(n.onset) + exact(n.duration); + } + n.clef = result.clef; + result.notes.push_back(std::move(n)); + } + if (auto harmonies = o.if_contains("harmonies")) for (auto const& item : harmonies->as_array()) { + auto const& h = item.as_object(); + auto onset = time(h.at("onset")); + if (exact(onset) >= exact(result.duration)) notation_error("harmony outside measure"); + result.harmonies.push_back({text(h,"id"), onset, text(h,"symbol"), text(h,"kind")}); + } + if (auto attrs = o.if_contains("attributes")) for (auto const& item : attrs->as_array()) { + auto const& a = item.as_object(); + auto change = vamp_zero::score_attribute{.onset = time(a.at("onset"))}; + if (auto c = a.if_contains("clef")) change.clef = clef(*c); + if (auto k = a.if_contains("key")) change.key_alterations = alterations(*k); + if (auto m = a.if_contains("meter")) { change.time_signature = meter_text(*m); change.beats = beats(*m); } + if (exact(change.onset) >= exact(result.duration)) notation_error("attribute change outside measure"); + result.attributes.push_back(std::move(change)); + } + if (auto bars = o.if_contains("barlines")) result.right_barline = text(bars->as_object(), "right", "regular"); + for (auto& n : result.notes) { + for (auto const& a : result.attributes) + if (exact(a.onset) <= exact(n.onset) && !a.clef.empty()) n.clef = a.clef; + } + return result; +} +} + +auto vamp_zero::written_quarters(score_written_duration const& written) -> double { + auto found = std::find(types.begin(), types.end(), written.type); + if (found == types.end()) return 0.0; + return std::ldexp(32.0, -static_cast(std::distance(types.begin(), found))) * (2.0 - std::ldexp(1.0, -written.dots)); +} + +auto vamp_zero::parse_notation_document(std::string_view json, std::string source_path) -> std::expected { + try { + auto parsed = boost::json::parse(json); + auto const& root = parsed.as_object(); + if (integer(root, "schema_version") != 2) notation_error("unsupported score schema; regenerate assets with the current Product Vamp generator"); + auto const& meta = root.at("metadata").as_object(); + auto const& staves = root.at("staves").as_array(); + if (staves.size() != 1) notation_error("exactly one staff is currently supported"); + auto const& staff = staves.front().as_object(); + auto result = score_document{}; + result.title = text(meta, "title"); + if (result.title.empty()) notation_error("score title is required"); + result.composer = text(meta, "composer"); + result.key_signature = text(meta, "key_signature"); + result.time_signature = text(meta, "time_signature"); + result.clef = clef(staff.at("clef")); + result.arrangement_part_type = text(staff, "arrangement_part_type", "practice"); + result.part = text(staff, "name"); + result.source_path = source_path; + result.source_json = std::string{json}; + result.id = std::filesystem::path(result.source_path).stem().string(); + if (result.id.empty()) result.id = result.title + "-" + result.part; + for (auto const& item : staff.at("measures").as_array()) result.measures.push_back(measure(item)); + if (result.measures.empty()) notation_error("score has no measures"); + auto ids = std::set{}; + auto events = std::map{}; + auto event_order = std::map{}; + struct tone_ref { score_pitch const* pitch; exact_time start; }; + auto tones = std::map{}; + auto absolute = exact_time{}; + for (auto const& m : result.measures) { + for (auto const& n : m.notes) { + if (n.id.empty() || !ids.insert(n.id).second) notation_error("duplicate or empty event ID"); + event_order.emplace(n.id, events.size()); + events.emplace(n.id, &n); + for (auto const& p : n.pitches) { + if (p.id.empty() || !tones.emplace(p.id, tone_ref{&p, absolute+exact(n.onset)}).second) notation_error("duplicate or empty pitch ID"); + } + } + absolute += exact(m.duration); + } + constexpr auto natural = std::array{0,2,4,5,7,9,11}; + auto midi = [&](score_pitch const& p) { return 12*(p.octave+1)+natural[std::string_view{"cdefgab"}.find(p.letter)]+p.alteration.value_or(0); }; + for (auto const& [id, ref] : tones) { + auto const& p = *ref.pitch; + if (!p.tie_to.empty()) { + auto next = tones.find(p.tie_to); + if (next == tones.end() || next->second.pitch->tie_from != id || midi(p) != midi(*next->second.pitch) || ref.start+exact(p.duration) != next->second.start) notation_error("invalid or dangling pitch tie"); + } + if (!p.tie_from.empty()) { + auto prev = tones.find(p.tie_from); + if (prev == tones.end() || prev->second.pitch->tie_to != id) notation_error("dangling incoming pitch tie"); + } + } + auto span_ids = std::set{}; + if (auto spans = staff.if_contains("tuplets")) for (auto const& item : spans->as_array()) { + auto const& t = item.as_object(); + if (integer(t,"note_count") <= 0 || integer(t,"note_count") > 1000000 || integer(t,"space_count") <= 0 || integer(t,"space_count") > 1000000 || integer(t,"depth") < 0 || integer(t,"depth") > 16) notation_error("invalid tuplet ratio or nesting depth"); + auto span = score_tuplet{.id = text(t,"id"), .note_count = static_cast(integer(t,"note_count")), .space_count = static_cast(integer(t,"space_count")), .start = text(t,"start"), .end = text(t,"end"), .depth = static_cast(integer(t,"depth")), .bracket = text(t,"bracket","auto"), .show_number = text(t,"show_number","actual")}; + if (span.id.empty() || !span_ids.insert(span.id).second || !ids.contains(span.start) || !ids.contains(span.end) || event_order.at(span.start) > event_order.at(span.end)) notation_error("invalid tuplet span"); + if ((span.bracket != "auto" && span.bracket != "yes" && span.bracket != "no") || (span.show_number != "none" && span.show_number != "actual" && span.show_number != "both")) notation_error("invalid tuplet appearance"); + for (auto const& [id, n] : events) { + auto member = std::find(n->tuplet_ids.begin(), n->tuplet_ids.end(), span.id) != n->tuplet_ids.end(); + if ((id == span.start || id == span.end) && !member) notation_error("tuplet endpoint is not a member"); + if (member && (event_order.at(id) < event_order.at(span.start) || event_order.at(id) > event_order.at(span.end))) notation_error("tuplet member outside span"); + } + for (auto& m : result.measures) if (std::any_of(m.notes.begin(), m.notes.end(), [&](auto const& n) { return std::find(n.tuplet_ids.begin(), n.tuplet_ids.end(), span.id) != n.tuplet_ids.end(); })) m.tuplets.push_back(span); + } + for (auto const& [id, n] : events) { + for (auto const& span_id : n->tuplet_ids) if (!span_ids.contains(span_id)) notation_error("event refers to an unknown tuplet"); + if (n->tremolo_type == "start" || n->tremolo_type == "stop") { + auto other = events.find(n->tremolo_partner); + auto expected = n->tremolo_type == "start" ? "stop" : "start"; + if (other == events.end() || other->second->tremolo_partner != id || other->second->tremolo_type != expected || other->second->tremolo_marks != n->tremolo_marks) notation_error("invalid paired tremolo"); + } + } + if (auto spans = staff.if_contains("octave_shifts")) for (auto const& item : spans->as_array()) { + auto const& span = item.as_object(); + if (integer(span,"octaves") < -3 || integer(span,"octaves") > 3 || exact(time(span.at("end"))) <= exact(time(span.at("start"))) || exact(time(span.at("end"))) > absolute) notation_error("invalid octave-shift span"); + result.octave_shifts.push_back({time(span.at("start")), time(span.at("end")), static_cast(integer(span,"octaves"))}); + } + auto start = exact_time{}; + for (auto& m : result.measures) { + for (auto& n : m.notes) { + for (auto const& span : result.octave_shifts) { + if (exact(span.end) <= exact(span.start) || exact(span.end) > absolute || span.octaves < -3 || span.octaves > 3) notation_error("invalid octave-shift span"); + if (start+exact(n.onset) >= exact(span.start) && start+exact(n.onset) < exact(span.end)) n.display_octaves += span.octaves; + } + } + start += exact(m.duration); + } + return result; + } catch (std::exception const& error) { + return std::unexpected(make_error(error_code::INVALID_ARGUMENT, (source_path.empty() ? std::string{} : source_path + ": ") + "score notation: " + error.what())); + } +} diff --git a/src/vamp_zero/score.cpp b/src/vamp_zero/score.cpp index 72fb25f..577d5aa 100644 --- a/src/vamp_zero/score.cpp +++ b/src/vamp_zero/score.cpp @@ -18,6 +18,7 @@ #include #include +#include namespace { @@ -27,10 +28,20 @@ constexpr auto quarter_note_ticks = std::int64_t{4096}; constexpr auto eighth_note_ticks = std::int64_t{2048}; enum class duration_base { + MAXIMA, + LONG, + BREVE, WHOLE, HALF, QUARTER, EIGHTH, + SIXTEENTH, + THIRTY_SECOND, + SIXTY_FOURTH, + ONE_TWENTY_EIGHTH, + TWO_FIFTY_SIXTH, + FIVE_TWELFTH, + TEN_TWENTY_FOURTH, }; struct duration_shape { @@ -134,34 +145,8 @@ auto music_font(std::float32_t size) -> Font { return found->second; } - auto codepoints = std::vector{ - glyph_g_clef, - glyph_f_clef, - glyph_notehead_whole, - glyph_notehead_half, - glyph_notehead_black, - glyph_notehead_slash, - glyph_rest_whole, - glyph_rest_half, - glyph_rest_quarter, - glyph_rest_eighth, - glyph_flag_eighth_up, - glyph_flag_eighth_down, - glyph_augmentation_dot, - glyph_accidental_flat, - glyph_accidental_natural, - glyph_accidental_sharp, - glyph_time_sig_0, - glyph_time_sig_0 + 1, - glyph_time_sig_0 + 2, - glyph_time_sig_0 + 3, - glyph_time_sig_0 + 4, - glyph_time_sig_0 + 5, - glyph_time_sig_0 + 6, - glyph_time_sig_0 + 7, - glyph_time_sig_0 + 8, - glyph_time_sig_0 + 9, - }; + auto codepoints = std::vector{}; + for (auto const& glyph : vamp_zero::font_data::glyphs) codepoints.push_back(glyph.codepoint); auto path = std::string{vamp_style::score_music_font_path}; auto loaded = LoadFontEx(path.c_str(), key, codepoints.data(), static_cast(codepoints.size())); if (loaded.texture.id == 0) { @@ -172,62 +157,10 @@ auto music_font(std::float32_t size) -> Font { } auto glyph_box(std::int32_t codepoint) -> smufl_box { - switch (codepoint) { - case glyph_g_clef: - return {.west = 0.0F, .south = -2.236F, .east = 2.656F, .north = 4.036F}; - case glyph_f_clef: - return {.west = 0.0F, .south = -1.984F, .east = 3.104F, .north = 0.864F}; - case glyph_notehead_whole: - return {.west = 0.0F, .south = -0.744684F, .east = 1.520666F, .north = 0.66F}; - case glyph_notehead_half: - return {.west = 0.0F, .south = -0.696F, .east = 1.336F, .north = 0.7F}; - case glyph_notehead_black: - return {.west = 0.0F, .south = -0.656F, .east = 1.336186F, .north = 0.656F}; - case glyph_notehead_slash: - return {.west = 0.0F, .south = -1.016F, .east = 1.952F, .north = 1.016F}; - case glyph_rest_whole: - return {.west = 0.003563F, .south = -0.48F, .east = 2.0F, .north = 0.056F}; - case glyph_rest_half: - return {.west = 0.0F, .south = 0.0F, .east = 2.073599F, .north = 0.648F}; - case glyph_rest_quarter: - return {.west = -0.001514F, .south = -1.66F, .east = 1.052F, .north = 1.66F}; - case glyph_rest_eighth: - return {.west = 0.0F, .south = -1.04F, .east = 1.156F, .north = 1.044F}; - case glyph_flag_eighth_up: - return {.west = 0.0F, .south = -3.276F, .east = 1.044F, .north = 0.0F}; - case glyph_flag_eighth_down: - return {.west = 0.0F, .south = 0.0F, .east = 1.044F, .north = 3.276F}; - case glyph_augmentation_dot: - return {.west = 0.0F, .south = -0.228F, .east = 0.384F, .north = 0.228F}; - case glyph_accidental_flat: - return {.west = 0.004F, .south = -0.832F, .east = 0.836F, .north = 1.888F}; - case glyph_accidental_natural: - return {.west = 0.0F, .south = -1.824F, .east = 0.854F, .north = 1.848F}; - case glyph_accidental_sharp: - return {.west = -0.088F, .south = -1.536F, .east = 1.56F, .north = 1.532F}; - case glyph_time_sig_0: - return {.west = 0.08F, .south = -1.432638F, .east = 2.052F, .north = 1.436F}; - case glyph_time_sig_0 + 1: - return {.west = 0.08F, .south = -1.456F, .east = 1.132F, .north = 1.464F}; - case glyph_time_sig_0 + 2: - return {.west = 0.08F, .south = -1.514881F, .east = 2.641855F, .north = 1.528F}; - case glyph_time_sig_0 + 3: - return {.west = 0.079297F, .south = -1.568F, .east = 2.16F, .north = 1.568F}; - case glyph_time_sig_0 + 4: - return {.west = 0.08F, .south = -1.958364F, .east = 2.532314F, .north = 1.964742F}; - case glyph_time_sig_0 + 5: - return {.west = 0.078655F, .south = -1.556F, .east = 2.332F, .north = 1.556F}; - case glyph_time_sig_0 + 6: - return {.west = 0.08F, .south = -1.452F, .east = 2.26F, .north = 1.452F}; - case glyph_time_sig_0 + 7: - return {.west = 0.081447F, .south = -1.38F, .east = 2.368F, .north = 1.38F}; - case glyph_time_sig_0 + 8: - return {.west = 0.08F, .south = -1.712F, .east = 2.016604F, .north = 1.712F}; - case glyph_time_sig_0 + 9: - return {.west = 0.08F, .south = -1.684F, .east = 1.976F, .north = 1.684F}; - default: - return {}; - } + auto const& glyphs = vamp_zero::font_data::glyphs; + auto found = std::lower_bound(glyphs.begin(), glyphs.end(), codepoint, [](auto const& g, int value) { return g.codepoint < value; }); + if (found == glyphs.end() || found->codepoint != codepoint) return {}; + return {found->bounds[0], found->bounds[1], found->bounds[2], found->bounds[3]}; } auto glyph_center_x(std::int32_t codepoint) -> std::float32_t { @@ -505,48 +438,32 @@ auto add_text( }); } -auto duration_for(std::int64_t ticks) -> duration_shape { - if (ticks >= whole_note_ticks) { - return {.base = duration_base::WHOLE, .dots = 0, .ticks = whole_note_ticks}; - } - if (ticks >= half_note_ticks + quarter_note_ticks) { - return {.base = duration_base::HALF, .dots = 1, .ticks = half_note_ticks + quarter_note_ticks}; - } - if (ticks >= half_note_ticks) { - return {.base = duration_base::HALF, .dots = 0, .ticks = half_note_ticks}; - } - if (ticks >= quarter_note_ticks + eighth_note_ticks) { - return {.base = duration_base::QUARTER, .dots = 1, .ticks = quarter_note_ticks + eighth_note_ticks}; - } - if (ticks >= quarter_note_ticks) { - return {.base = duration_base::QUARTER, .dots = 0, .ticks = quarter_note_ticks}; - } - return {.base = duration_base::EIGHTH, .dots = 0, .ticks = eighth_note_ticks}; +auto duration_for(vamp_zero::score_written_duration const& written, double fallback_ticks = 4096.0) -> duration_shape { + constexpr auto names = std::array{"maxima", "long", "breve", "whole", "half", "quarter", "eighth", "16th", "32nd", "64th", "128th", "256th", "512th", "1024th"}; + auto found = std::find(names.begin(), names.end(), written.type); + if (found != names.end()) return {.base = static_cast(found - names.begin()), .dots = written.dots}; + // Directly constructed layout documents use the same exact written-value lookup. + for (auto index = std::size_t{}; index < names.size(); ++index) + for (auto dots = 0; dots <= 16; ++dots) + if (std::abs(fallback_ticks - std::ldexp(131072.0, -static_cast(index)) * (2.0 - std::ldexp(1.0, -dots))) < 0.00001) + return {.base = static_cast(index), .dots = dots}; + return {}; } -auto spacing_ticks(vamp_zero::score_note const& note) -> std::int64_t { - return std::max(1, note.duration_ticks); +auto spacing_ticks(vamp_zero::score_note const& note) -> double { return note.duration_ticks; } +auto measure_ticks(vamp_zero::score_measure const& measure) -> double { + if (measure.duration.numerator > 0) return measure.duration.quarters() * 4096.0; + auto total = 0.0; + for (auto const& note : measure.notes) total += spacing_ticks(note); + return std::max(1.0, total); } - -auto display_ticks(vamp_zero::score_note const& note) -> std::int64_t { - return note.display_ticks <= 0 ? note.duration_ticks : note.display_ticks; +auto has_stem(duration_shape duration) -> bool { + return duration.base == duration_base::LONG || duration.base == duration_base::MAXIMA || duration.base >= duration_base::HALF; } - -auto measure_ticks(vamp_zero::score_measure const& measure) -> std::int64_t { - auto total = std::int64_t{}; - for (auto const& note : measure.notes) { - total += spacing_ticks(note); - } - return std::max(whole_note_ticks, total); -} - -auto has_stem(duration_shape duration) -> std::bool_t { - return duration.base != duration_base::WHOLE; -} - -auto has_flag(duration_shape duration) -> std::bool_t { - return duration.base == duration_base::EIGHTH; +auto flag_count(duration_shape duration) -> int { + return std::max(0, static_cast(duration.base) - static_cast(duration_base::QUARTER)); } +auto has_flag(duration_shape duration) -> bool { return flag_count(duration) > 0; } auto letter_index(std::char_t letter) -> std::int32_t { switch (letter) { @@ -587,6 +504,12 @@ auto normalized_clef(std::string_view clef) -> std::string { } auto clef_bottom_line_pitch_index(std::string_view clef) -> std::int32_t { + if (clef.size() >= 5 && clef[1] == ':') { + auto line = clef[2] - '0'; + auto octave = std::stoi(std::string{clef.substr(4)}); + auto anchor = clef[0] == 'F' ? 3 * 7 + 3 : clef[0] == 'C' ? 4 * 7 : 4 * 7 + 4; + return anchor + octave * 7 - 2 * (line - 1); + } auto name = normalized_clef(clef); if (name == "bass" || name == "f") { return 2 * 7 + letter_index('g'); @@ -608,6 +531,10 @@ auto accidental_glyph(vamp_zero::score_accidental_mark mark) -> std::optional std::char_t { } auto pitch_alteration(std::string_view accidental) -> std::int32_t { + if (accidental == "##" || accidental == "double-sharp") return 2; + if (accidental == "bb" || accidental == "flat-flat") return -2; + if (accidental == "###") return 3; + if (accidental == "bbb") return -3; if (accidental == "#" || accidental == "sharp") { return 1; } @@ -632,6 +563,10 @@ auto pitch_alteration(std::string_view accidental) -> std::int32_t { } auto alteration_mark(std::int32_t alteration) -> vamp_zero::score_accidental_mark { + if (alteration == -3) return vamp_zero::score_accidental_mark::TRIPLE_FLAT; + if (alteration == 3) return vamp_zero::score_accidental_mark::TRIPLE_SHARP; + if (alteration == -2) return vamp_zero::score_accidental_mark::DOUBLE_FLAT; + if (alteration == 2) return vamp_zero::score_accidental_mark::DOUBLE_SHARP; if (alteration < 0) { return vamp_zero::score_accidental_mark::FLAT; } @@ -732,15 +667,15 @@ auto make_accidental_state(std::string_view key_signature) -> accidental_state { } auto notated_accidental_for_pitch(accidental_state& state, vamp_zero::score_pitch const& pitch, std::bool_t tied_from_previous) -> vamp_zero::score_accidental_mark { - if (tied_from_previous) { + if (tied_from_previous && !pitch.explicit_accidental) { return vamp_zero::score_accidental_mark::NONE; } auto const letter = letter_index(normalized_letter(pitch.letter)); - auto const desired = pitch_alteration(pitch.accidental); + auto const desired = pitch.alteration.value_or(pitch_alteration(pitch.accidental)); auto const key = std::pair{letter, pitch.octave}; auto const found = state.measure_alterations.find(key); auto const current = found == state.measure_alterations.end() ? state.key_alterations[static_cast(letter)] : found->second; - if (desired == current) { + if (desired == current && !pitch.explicit_accidental) { return vamp_zero::score_accidental_mark::NONE; } state.measure_alterations[key] = desired; @@ -754,7 +689,7 @@ auto notated_accidentals_for_note(accidental_state& state, vamp_zero::score_note } marks.reserve(note.pitches.size()); for (auto const& pitch : note.pitches) { - marks.push_back(notated_accidental_for_pitch(state, pitch, note.tie_from_previous)); + marks.push_back(notated_accidental_for_pitch(state, pitch, !pitch.tie_from.empty() || (pitch.id.empty() && note.tie_from_previous))); } return marks; } @@ -786,7 +721,30 @@ auto notation_state_changes_before(vamp_zero::score_document const& score, std:: auto const& previous = score.measures[measure_index - 1U]; auto const& current = score.measures[measure_index]; return effective_key_signature(score, previous) != effective_key_signature(score, current) - || effective_time_signature(score, &previous) != effective_time_signature(score, ¤t); + || effective_time_signature(score, &previous) != effective_time_signature(score, ¤t) + || previous.clef != current.clef || previous.key_alterations != current.key_alterations; +} + +auto event_width(vamp_zero::score_note const& note) -> float { + if (note.hidden) return 8.0F; + auto accidentals = std::count_if(note.pitches.begin(), note.pitches.end(), [](auto const& p) { return p.alteration.value_or(pitch_alteration(p.accidental)) != 0 || p.explicit_accidental; }); + return 30.0F + 12.0F * std::min(3, accidentals) + note.written.dots*8.0F + std::max(0.0F, static_cast(note.lyric.size())*7-30); +} + +auto attribute_width(vamp_zero::score_attribute const& a) -> float { + auto result = a.clef.empty() ? 0.0F : 45.0F; + if (a.key_alterations) result += 35.0F + 22.0F*std::count_if(a.key_alterations->begin(), a.key_alterations->end(), [](int v) { return v!=0; }); + if (!a.time_signature.empty()) result += 50.0F; + return result; +} + +auto measure_width(vamp_zero::score_measure const& measure) -> float { + auto width = 45.0F; + for (auto const& note : measure.notes) width += event_width(note); + auto harmony_width = 45.0F; + for (auto const& h : measure.harmonies) harmony_width += static_cast(h.symbol.size()) * 14 + 16; + for (auto const& a : measure.attributes) width += attribute_width(a); + return std::max({150.0F, width, harmony_width}); } auto make_score_system_ranges(vamp_zero::score_document const& score) -> std::vector { @@ -796,13 +754,36 @@ auto make_score_system_ranges(vamp_zero::score_document const& score) -> std::ve } auto current = vamp_zero::score_system_range{.first_measure = 0U, .measure_count = 0U}; + auto used_width = 0.0F; for (auto measure_index = std::size_t{}; measure_index < score.measures.size(); ++measure_index) { + auto const& measure = score.measures[measure_index]; + auto width = measure_width(measure); if (current.measure_count != 0U - && (current.measure_count >= vamp_zero::score_measures_per_system || notation_state_changes_before(score, measure_index))) { + && (current.measure_count >= vamp_zero::score_measures_per_system || used_width+width > 760.0F || notation_state_changes_before(score, measure_index))) { ranges.push_back(current); current = vamp_zero::score_system_range{.first_measure = measure_index, .measure_count = 0U}; + used_width = 0; + } + if (width > 760.0F && measure.notes.size() > 1) { + auto first = std::size_t{}; + while (first < measure.notes.size()) { + auto end = first; + auto fragment_width = 45.0F; + while (end < measure.notes.size() && (end == first || fragment_width + event_width(measure.notes[end]) <= 660.0F)) { + fragment_width += event_width(measure.notes[end]); + ++end; + } + // Keep an attached grace group with its metric anchor. + while (end < measure.notes.size() && measure.notes[end-1].grace) ++end; + ranges.push_back({.first_measure=measure_index, .measure_count=1, .first_note=first, .note_count=end-first}); + first = end; + } + current = {.first_measure=measure_index+1}; + used_width = 0; + continue; } ++current.measure_count; + used_width += width; } if (current.measure_count != 0U) { ranges.push_back(current); @@ -818,6 +799,48 @@ auto show_time_signature_for_system(vamp_zero::score_document const& score, vamp && effective_time_signature(score, &score.measures[range.first_measure - 1U]) != effective_time_signature(score, &score.measures[range.first_measure]); } +struct system_metrics { + float above{5.5F}; + float below{6.0F}; + auto height() const -> float { return (above+below)*12.0F; } +}; + +auto metrics_for(vamp_zero::score_document const& score, vamp_zero::score_system_range const& range) -> system_metrics { + auto result = system_metrics{}; + for (auto mi = range.first_measure; mi < range.first_measure+range.measure_count; ++mi) { + auto const& measure = score.measures[mi]; + if (!measure.section_label.empty()) result.above = std::max(result.above, 9.0F); + auto end = range.first_note + std::min(range.note_count, measure.notes.size()-range.first_note); + auto depth = 0; + for (auto const& t : measure.tuplets) depth = std::max(depth, t.depth+1); + for (auto ni = range.first_note; ni < end; ++ni) { + auto const& n = measure.notes[ni]; + if (n.hidden) continue; + for (auto p : n.pitches) { + p.octave += n.display_octaves; + auto y = pitch_y(p, 0, 1, n.clef.empty() ? score.clef : n.clef); + result.above = std::max(result.above, -y + 4.0F + depth*1.25F + (measure.harmonies.empty() ? 0.0F : 2.0F) + (measure.section_label.empty() ? 0.0F : 3.5F)); + result.below = std::max(result.below, y + 4.0F + (n.lyric.empty() ? 0.0F : 2.0F)); + } + } + } + return result; +} + +auto page_starts(vamp_zero::score_document const& score, std::vector const& ranges) -> std::vector { + auto starts = std::vector{0}; + auto height = 0.0F; + for (auto i = std::size_t{}; i < ranges.size(); ++i) { + auto next = std::min(376.0F, metrics_for(score, ranges[i]).height()); + if (i > starts.back() && (height+next > 376.0F || i-starts.back() == vamp_zero::score_systems_per_page)) { + starts.push_back(i); + height = 0; + } + height += next; + } + return starts; +} + auto draw_curve(luma::draw_context& context, luma::point start, luma::point control, luma::point end, std::float32_t thickness, luma::color color) -> void { constexpr auto segments = std::int32_t{10}; auto previous = start; @@ -847,8 +870,8 @@ struct notehead_layout { struct note_layout { vamp_zero::score_note const* source{}; std::size_t note_index{}; - std::int64_t elapsed_ticks{}; - std::int64_t duration_ticks{}; + double elapsed_ticks{}; + double duration_ticks{}; duration_shape duration{}; std::int32_t notehead{}; std::bool_t hidden{}; @@ -868,6 +891,8 @@ struct note_layout { std::float32_t stem_base_y{}; std::float32_t stem_tip_y{}; std::vector heads{}; + float size{1.0F}; + float leading_space{}; }; struct beam_group_layout { @@ -921,13 +946,18 @@ auto layout_staff( auto layout_clef(vamp_zero::score_page_layout& layout, std::string_view clef, std::float32_t x, std::float32_t staff_top, std::float32_t spacing, std::size_t system_index) -> std::float32_t { auto name = normalized_clef(clef); - auto codepoint = name == "bass" || name == "f" ? glyph_f_clef : glyph_g_clef; + auto codepoint = name == "bass" || name == "f" || name.starts_with("f:") ? glyph_f_clef : name.starts_with("c:") ? 0xe05c : glyph_g_clef; auto reference_pitch = codepoint == glyph_f_clef ? vamp_zero::score_pitch{.letter = 'f', .octave = 3} : vamp_zero::score_pitch{.letter = 'g', .octave = 4}; - auto baseline_y = pitch_y(reference_pitch, staff_top, spacing, clef) + scaled(clef_vertical_correction_spaces, spacing); + auto baseline_y = clef.size() >= 5 && clef[1] == ':' ? staff_top + spacing * (5 - (clef[2] - '0')) : pitch_y(reference_pitch, staff_top, spacing, clef); + baseline_y += scaled(clef_vertical_correction_spaces, spacing); auto origin = origin_for_left_baseline(codepoint, x, baseline_y, spacing); add_glyph(layout, vamp_zero::score_layout_item_role::CLEF, codepoint, origin, spacing, score_ink(), system_index); + if (clef.size() >= 5 && clef[1] == ':') { + auto octave = std::stoi(std::string{clef.substr(4)}); + if (octave) add_text(layout, vamp_zero::score_layout_item_role::CLEF, x + spacing, staff_top + spacing * (octave > 0 ? -2.5F : 5.5F), std::to_string(std::abs(octave)*7+1), 1, score_ink(), spacing*3, system_index); + } return glyph_width(codepoint, spacing) + spacing * 0.90F; } @@ -1012,7 +1042,32 @@ auto layout_key_signature( return current_x - x + spacing * 0.55F; } +auto layout_structured_key(vamp_zero::score_page_layout& layout, std::string_view clef, + std::array const& alterations, std::float32_t x, std::float32_t top, + std::float32_t spacing, std::size_t system, std::array const* previous = nullptr) -> std::float32_t { + auto start = x; + auto paint = [&](int letter, int alteration) { + auto glyph = accidental_glyph(alteration_mark(alteration)); + if (!glyph) return; + auto pitch = vamp_zero::score_pitch{.letter = "cdefgab"[letter], .octave = 4}; + while (pitch_y(pitch, top, spacing, clef) < top - spacing * 0.5F) --pitch.octave; + while (pitch_y(pitch, top, spacing, clef) > top + spacing * 3.5F) ++pitch.octave; + add_glyph(layout, vamp_zero::score_layout_item_role::KEY_SIGNATURE, *glyph, + origin_for_left_baseline(*glyph, x, pitch_y(pitch, top, spacing, clef), spacing), spacing, score_ink(), system); + x += glyph_width(*glyph, spacing) + spacing * 0.15F; + }; + if (previous) for (int i = 0; i < 7; ++i) + if ((*previous)[i] && (*previous)[i] != alterations[i]) paint(i, 0); + auto flats = std::any_of(alterations.begin(), alterations.end(), [](int a) { return a < 0; }); + for (auto letter : std::string_view{flats ? "beadgcf" : "fcgdaeb"}) { + auto index = letter_index(letter); + if (alterations[index]) paint(index, alterations[index]); + } + return x - start + (x == start ? 0.0F : spacing * 0.55F); +} + auto time_signature_digit(std::char_t digit) -> std::optional { + if (digit == '+') return 0xe08c; if (digit < '0' || digit > '9') { return std::nullopt; } @@ -1068,10 +1123,23 @@ auto layout_time_signature( std::float32_t spacing, std::size_t system_index ) -> std::float32_t { + if (time_signature == "common" || time_signature == "cut") { + auto glyph = time_signature == "common" ? 0xe08a : 0xe08b; + add_glyph(layout, vamp_zero::score_layout_item_role::TIME_SIGNATURE, glyph, + origin_for_left_baseline(glyph, x, staff_top + spacing * 2.0F, spacing), spacing, score_ink(), system_index); + return glyph_width(glyph, spacing) + spacing; + } auto slash = time_signature.find('/'); if (slash == std::string_view::npos) { return 0.0F; } + auto group_end = time_signature.find('+', slash); + if (group_end != std::string_view::npos) { + auto width = layout_time_signature(layout, time_signature.substr(0, group_end), x, staff_top, spacing, system_index); + layout_time_signature_run(layout, "+", x+width, staff_top+spacing*2.0F, spacing, system_index); + width += spacing*2; + return width + layout_time_signature(layout, time_signature.substr(group_end+1), x+width, staff_top, spacing, system_index); + } auto numerator = time_signature.substr(0, slash); auto denominator = time_signature.substr(slash + 1U); auto numerator_width = time_signature_run_width(numerator, spacing); @@ -1094,24 +1162,44 @@ auto layout_system_preamble( std::float32_t staff_top, std::float32_t spacing, std::bool_t show_time_signature, - std::size_t system_index + std::size_t system_index, + std::size_t first_note = 0 ) -> std::float32_t { auto current_x = x + spacing * 0.35F; - current_x += layout_clef(layout, score.clef, current_x, staff_top, spacing, system_index); + auto clef = first_measure && !first_measure->clef.empty() ? std::string_view{first_measure->clef} : std::string_view{score.clef}; + auto key = first_measure ? first_measure->key_alterations : std::array{}; + auto time = effective_time_signature(score, first_measure); + if (first_measure && first_note > 0 && first_note < first_measure->notes.size()) { + auto onset = first_measure->notes[first_note].onset.quarters(); + for (auto const& a : first_measure->attributes) if (a.onset.quarters() < onset) { + if (!a.clef.empty()) clef = a.clef; + if (a.key_alterations) key = *a.key_alterations; + if (!a.time_signature.empty()) time = a.time_signature; + } + } + current_x += layout_clef(layout, clef, current_x, staff_top, spacing, system_index); auto key_width = layout_key_signature( layout, - score.clef, + clef, first_measure == nullptr ? score.key_signature : effective_key_signature(score, *first_measure), current_x, staff_top, spacing, system_index); + if (first_measure && first_measure->structured_key) { + // The structured key is authoritative for minor, modal, and custom signatures. + layout.items.erase(std::remove_if(layout.items.begin(), layout.items.end(), [&](auto const& item) { + return item.system_index == system_index && item.role == vamp_zero::score_layout_item_role::KEY_SIGNATURE; + }), layout.items.end()); + auto previous = first_measure > score.measures.data() && first_measure <= score.measures.data()+score.measures.size() ? &(first_measure-1)->key_alterations : nullptr; + key_width = layout_structured_key(layout, clef, key, current_x, staff_top, spacing, system_index, first_note ? nullptr : previous); + } current_x += key_width; if (key_width == 0.0F) { current_x += spacing * 0.45F; } if (show_time_signature) { - current_x += layout_time_signature(layout, effective_time_signature(score, first_measure), current_x, staff_top, spacing, system_index); + current_x += layout_time_signature(layout, time, current_x, staff_top, spacing, system_index); } return current_x + spacing * 0.35F; } @@ -1127,6 +1215,9 @@ auto dot_y_for(std::float32_t note_y, std::float32_t staff_top, std::float32_t s auto rest_codepoint(duration_shape duration) -> std::int32_t { switch (duration.base) { + case duration_base::MAXIMA: return 0xe4e0; + case duration_base::LONG: return 0xe4e1; + case duration_base::BREVE: return 0xe4e2; case duration_base::WHOLE: return glyph_rest_whole; case duration_base::HALF: @@ -1136,7 +1227,7 @@ auto rest_codepoint(duration_shape duration) -> std::int32_t { case duration_base::EIGHTH: return glyph_rest_eighth; } - return glyph_rest_quarter; + return glyph_rest_eighth + flag_count(duration) - 1; } auto rest_baseline_y(duration_shape duration, std::float32_t staff_top, std::float32_t spacing) -> std::float32_t { @@ -1200,6 +1291,9 @@ auto notehead_codepoint(duration_shape duration, std::bool_t slash) -> std::int3 return glyph_notehead_slash; } switch (duration.base) { + case duration_base::MAXIMA: return 0xe0a0; + case duration_base::LONG: return 0xe0a1; + case duration_base::BREVE: return 0xe0a0; case duration_base::WHOLE: return glyph_notehead_whole; case duration_base::HALF: @@ -1354,20 +1448,23 @@ auto update_stem_geometry(note_layout& note, std::float32_t staff_top, std::floa note.stem_tip_y = note.stem_up ? std::min(note.high_y, center) - scaled(normal_stem_length_spaces, spacing) : std::max(note.low_y, center) + scaled(normal_stem_length_spaces, spacing); + if (note.source->grace || note.source->cue) { + note.stem_tip_y = head->center_y + (note.stem_up ? -1.0F : 1.0F)*spacing*2.5F; + } } auto build_note_layout( vamp_zero::score_document const& score, vamp_zero::score_note const& note, std::size_t note_index, - std::int64_t elapsed_ticks, - std::int64_t duration_ticks, + double elapsed_ticks, + double duration_ticks, std::float32_t x, std::float32_t staff_top, std::float32_t spacing, std::vector const& accidentals ) -> note_layout { - auto duration = duration_for(display_ticks(note)); + auto duration = note.measure_rest ? duration_shape{.base = duration_base::WHOLE} : duration_for(note.written, note.display_ticks > 0 ? note.display_ticks : note.duration_ticks); auto slash = note.notehead == "slash"; auto result = note_layout{ .source = ¬e, @@ -1385,6 +1482,7 @@ auto build_note_layout( .visual_left = x, .visual_right = x, }; + result.size = note.grace || note.cue ? 0.65F : 1.0F; if (note.hidden) { return result; } @@ -1412,12 +1510,14 @@ auto build_note_layout( auto raw_heads = std::vector{}; for (auto pitch_index = std::size_t{}; pitch_index < note.pitches.size(); ++pitch_index) { auto const& pitch = note.pitches[pitch_index]; - auto yy = std::clamp(pitch_y(pitch, staff_top, spacing, score.clef), staff_top - spacing * 2.0F, bottom + spacing * 2.0F); + auto displayed_pitch = pitch; + displayed_pitch.octave += note.display_octaves; + auto yy = pitch_y(displayed_pitch, staff_top, spacing, note.clef.empty() ? score.clef : note.clef); result.low_y = std::max(result.low_y, yy); result.high_y = std::min(result.high_y, yy); raw_heads.push_back({ .source_pitch_index = pitch_index, - .codepoint = result.notehead, + .codepoint = pitch.written.type.empty() ? result.notehead : notehead_codepoint(duration_for(pitch.written), false), .center_x = x, .center_y = yy, .accidental = pitch_index < accidentals.size() ? accidentals[pitch_index] : vamp_zero::score_accidental_mark::NONE, @@ -1448,13 +1548,13 @@ auto build_note_layout( for (auto& head : raw_heads) { head.center_x = center_by_pitch[head.source_pitch_index]; head.displaced = displaced_by_pitch[head.source_pitch_index]; - head.origin = origin_for_centered_baseline(head.codepoint, head.center_x, head.center_y, spacing); - head.bounds = glyph_bounds(head.codepoint, head.origin, spacing); + head.origin = origin_for_centered_baseline(head.codepoint, head.center_x, head.center_y, spacing*result.size); + head.bounds = glyph_bounds(head.codepoint, head.origin, spacing*result.size); result.heads.push_back(head); } } - result.stem_up = (result.low_y + result.high_y) * 0.5F >= center; + result.stem_up = note.stem == "up" || (note.stem != "down" && (result.low_y + result.high_y) * 0.5F >= center); update_note_visual_extents(result, spacing); auto accidental_extent = note_accidental_extent(result, spacing); if (accidental_extent > 0.0F) { @@ -1523,7 +1623,7 @@ auto keep_notes_inside_right_guard(std::vector& notes, luma::rect b } auto eligible_for_beam(note_layout const& note) -> std::bool_t { - return !note.hidden && !note.rest && has_stem(note.duration) && note.duration.base == duration_base::EIGHTH; + return !note.hidden && !note.rest && has_stem(note.duration) && has_flag(note.duration) && !note.source->grace; } auto update_beam_group_geometry(std::vector& notes, beam_group_layout& group, std::float32_t staff_top, std::float32_t spacing) -> void { @@ -1554,7 +1654,12 @@ auto update_beam_group_geometry(std::vector& notes, beam_group_layo } } -auto make_beam_groups(std::vector& notes, std::float32_t staff_top, std::float32_t spacing) -> std::vector { +auto beam_type(note_layout const& note, int level) -> std::string_view { + for (auto const& beam : note.source->beams) if (beam.level == level) return beam.type; + return {}; +} + +auto make_beam_groups(std::vector& notes, vamp_zero::score_measure const& measure, double offset_ticks, std::float32_t staff_top, std::float32_t spacing) -> std::vector { auto groups = std::vector{}; auto index = std::size_t{}; while (index < notes.size()) { @@ -1566,7 +1671,11 @@ auto make_beam_groups(std::vector& notes, std::float32_t staff_top, auto start = index; auto end = index; auto const& tuplet_id = notes[index].source == nullptr ? std::string{} : notes[index].source->tuplet_id; - if (!tuplet_id.empty()) { + auto explicit_beam = beam_type(notes[index], 1); + if (!explicit_beam.empty()) { + while (end + 1U < notes.size() && beam_type(notes[end], 1) != "end" + && eligible_for_beam(notes[end+1]) && (beam_type(notes[end+1], 1) == "continue" || beam_type(notes[end+1], 1) == "end")) ++end; + } else if (!tuplet_id.empty()) { while (end + 1U < notes.size() && eligible_for_beam(notes[end + 1U]) && notes[end + 1U].source != nullptr @@ -1574,10 +1683,30 @@ auto make_beam_groups(std::vector& notes, std::float32_t staff_top, ++end; } } else { - auto beat_end = ((notes[index].elapsed_ticks / quarter_note_ticks) + 1) * quarter_note_ticks; + auto onset = notes[index].elapsed_ticks + offset_ticks; + auto beat_end = (std::floor(onset / quarter_note_ticks) + 1) * quarter_note_ticks; + auto const* beat_pattern = &measure.beats; + auto beat_start = 0.0; + for (auto const& a : measure.attributes) if (!a.beats.empty() && a.onset.quarters()*4096.0 <= onset) { + beat_pattern = &a.beats; + beat_start = a.onset.quarters()*4096.0; + } + if (!beat_pattern->empty()) { + auto cycle = 0.0; + for (auto beat : *beat_pattern) cycle += beat.quarters()*quarter_note_ticks; + beat_end = beat_start + std::floor((onset-beat_start)/cycle)*cycle; + for (auto beat : *beat_pattern) { + beat_end += beat.quarters() * quarter_note_ticks; + if (beat_end > onset+0.00001) break; + } + } + for (auto const& a : measure.attributes) + if (!a.beats.empty() && a.onset.quarters()*4096.0 > onset) beat_end = std::min(beat_end, a.onset.quarters()*4096.0); + beat_end -= offset_ticks; while (end + 1U < notes.size() && eligible_for_beam(notes[end + 1U]) && notes[end + 1U].source != nullptr + && notes[end + 1U].source->grace == notes[index].source->grace && notes[end + 1U].source->tuplet_id.empty() && notes[end + 1U].elapsed_ticks < beat_end) { ++end; @@ -1657,13 +1786,13 @@ auto accidental_layouts(note_layout const& note, std::float32_t spacing) -> std: } auto add_flag(vamp_zero::score_page_layout& layout, note_layout const& note, std::float32_t spacing, std::size_t system_index, std::size_t measure_index) -> void { - auto codepoint = note.stem_up ? glyph_flag_eighth_up : glyph_flag_eighth_down; + auto codepoint = (note.stem_up ? glyph_flag_eighth_up : glyph_flag_eighth_down) + 2 * (flag_count(note.duration) - 1); auto anchor = note.stem_up ? smufl_anchor{.x = 0.0F, .y = -0.176F} : smufl_anchor{.x = 0.0F, .y = 0.176F}; auto origin = luma::point{ .x = note.stem_x - anchor.x * spacing, .y = note.stem_tip_y + anchor.y * spacing, }; - add_glyph(layout, vamp_zero::score_layout_item_role::FLAG, codepoint, origin, spacing, score_ink(), system_index, measure_index, note.note_index); + add_glyph(layout, vamp_zero::score_layout_item_role::FLAG, codepoint, origin, spacing*note.size, score_ink(), system_index, measure_index, note.note_index); } auto include_visual_bounds(note_layout& note, luma::rect bounds) -> void { @@ -1674,17 +1803,37 @@ auto include_visual_bounds(note_layout& note, luma::rect bounds) -> void { } auto flag_bounds(note_layout const& note, std::float32_t spacing) -> luma::rect { - auto codepoint = note.stem_up ? glyph_flag_eighth_up : glyph_flag_eighth_down; + auto codepoint = (note.stem_up ? glyph_flag_eighth_up : glyph_flag_eighth_down) + 2 * (flag_count(note.duration) - 1); auto anchor = note.stem_up ? smufl_anchor{.x = 0.0F, .y = -0.176F} : smufl_anchor{.x = 0.0F, .y = 0.176F}; auto origin = luma::point{ .x = note.stem_x - anchor.x * spacing, .y = note.stem_tip_y + anchor.y * spacing, }; - return glyph_bounds(codepoint, origin, spacing); + return glyph_bounds(codepoint, origin, spacing*note.size); +} + +struct secondary_stem_layout { + luma::point base; + luma::point tip; + std::optional flag; +}; + +auto secondary_stem(note_layout const& note, notehead_layout const& head, float spacing) -> std::optional { + if (head.source_pitch_index >= note.source->pitches.size() || note.source->stem == "none") return std::nullopt; + auto const& tone = note.source->pitches[head.source_pitch_index]; + if (tone.written.type.empty() || tone.written.type == note.source->written.type) return std::nullopt; + auto duration = duration_for(tone.written); + if (!has_stem(duration)) return std::nullopt; + auto up = !note.stem_up; + auto x = up ? head.bounds.x+head.bounds.width : head.bounds.x; + auto result = secondary_stem_layout{{x, head.center_y}, {x, head.center_y + (up ? -1.0F : 1.0F)*spacing*3.5F*note.size}, std::nullopt}; + if (has_flag(duration)) result.flag = (up ? glyph_flag_eighth_up : glyph_flag_eighth_down)+2*(flag_count(duration)-1); + return result; } auto refresh_note_visual_extents(note_layout& note, std::float32_t spacing) -> void { update_note_visual_extents(note, spacing); + note.visual_left -= note.leading_space; if (note.hidden || note.rest) { return; } @@ -1694,6 +1843,10 @@ auto refresh_note_visual_extents(note_layout& note, std::float32_t spacing) -> v note.visual_left -= accidental_extent; } for (auto const& head : note.heads) { + if (auto stem = secondary_stem(note, head, spacing)) { + include_visual_bounds(note, {.x=stem->base.x, .y=std::min(stem->base.y, stem->tip.y), .width=1, .height=std::abs(stem->base.y-stem->tip.y)}); + if (stem->flag) include_visual_bounds(note, glyph_bounds(*stem->flag, stem->tip, spacing*note.size)); + } if (auto glyph = accidental_glyph(head.accidental)) { auto box = glyph_box(*glyph); note.visual_top = std::min(note.visual_top, head.center_y - box.north * spacing); @@ -1724,15 +1877,16 @@ auto layout_rest( std::size_t measure_index ) -> void { auto codepoint = rest_codepoint(note.duration); - auto origin = origin_for_centered_baseline(codepoint, note.x, rest_baseline_y(note.duration, staff_top, spacing), spacing); + auto y = note.source->rest_position ? pitch_y(*note.source->rest_position, staff_top, spacing, note.source->clef) : rest_baseline_y(note.duration, staff_top, spacing); + auto origin = origin_for_centered_baseline(codepoint, note.x, y, spacing); add_glyph(layout, vamp_zero::score_layout_item_role::REST, codepoint, origin, spacing, score_ink(), system_index, measure_index, note.note_index); - if (note.duration.dots != 0) { + for (auto dot = 0; dot < note.duration.dots; ++dot) { auto bounds = glyph_bounds(codepoint, origin, spacing); add_glyph( layout, vamp_zero::score_layout_item_role::DOT, glyph_augmentation_dot, - origin_for_centered_baseline(glyph_augmentation_dot, bounds.x + bounds.width + spacing * 0.75F, staff_top + spacing * 2.0F, spacing), + origin_for_centered_baseline(glyph_augmentation_dot, bounds.x + bounds.width + spacing * (0.75F + dot * 0.6F), staff_top + spacing * 2.0F, spacing), spacing, score_ink(), system_index, @@ -1760,18 +1914,26 @@ auto layout_note_symbols( layout_ledger_lines(layout, note, staff_top, spacing, system_index, measure_index); for (auto const& accidental : accidental_layouts(note, spacing)) { add_glyph(layout, vamp_zero::score_layout_item_role::ACCIDENTAL, accidental.codepoint, accidental.origin, spacing, score_ink(), system_index, measure_index, note.note_index); + auto const& p = note.source->pitches[accidental.source_pitch_index]; + if (p.accidental_parentheses || p.accidental_bracket) { + auto left = p.accidental_bracket ? 0xe26c : 0xe26a; + auto right = p.accidental_bracket ? 0xe26d : 0xe26b; + add_glyph(layout, vamp_zero::score_layout_item_role::ACCIDENTAL, left, origin_for_left_baseline(left, accidental.bounds.x-spacing*0.7F, accidental.center_y, spacing), spacing, score_ink(), system_index, measure_index, note.note_index); + add_glyph(layout, vamp_zero::score_layout_item_role::ACCIDENTAL, right, origin_for_left_baseline(right, accidental.bounds.x+accidental.bounds.width+spacing*0.1F, accidental.center_y, spacing), spacing, score_ink(), system_index, measure_index, note.note_index); + } } for (auto const& head : note.heads) { - add_glyph(layout, vamp_zero::score_layout_item_role::NOTEHEAD, head.codepoint, head.origin, spacing, score_ink(), system_index, measure_index, note.note_index); + add_glyph(layout, vamp_zero::score_layout_item_role::NOTEHEAD, head.codepoint, head.origin, spacing*note.size, score_ink(), system_index, measure_index, note.note_index); } - if (note.duration.dots != 0) { + { auto dot_x = note.visual_right - spacing * 0.65F; for (auto const& head : note.heads) { - add_glyph( + auto dots = note.source != nullptr && head.source_pitch_index < note.source->pitches.size() && !note.source->pitches[head.source_pitch_index].written.type.empty() ? note.source->pitches[head.source_pitch_index].written.dots : note.duration.dots; + for (auto dot = 0; dot < dots; ++dot) add_glyph( layout, vamp_zero::score_layout_item_role::DOT, glyph_augmentation_dot, - origin_for_centered_baseline(glyph_augmentation_dot, dot_x, dot_y_for(head.center_y, staff_top, spacing), spacing), + origin_for_centered_baseline(glyph_augmentation_dot, dot_x + spacing * dot * 0.6F, dot_y_for(head.center_y, staff_top, spacing), spacing), spacing, score_ink(), system_index, @@ -1781,7 +1943,7 @@ auto layout_note_symbols( } } -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 { +auto note_x(std::float32_t start_x, std::float32_t width, double total_ticks, double elapsed_ticks, double duration_ticks) -> std::float32_t { auto center_ticks = static_cast(elapsed_ticks) + static_cast(duration_ticks) * 0.5F; auto ratio = std::clamp(center_ticks / static_cast(total_ticks), std::float32_t{0.0F}, std::float32_t{1.0F}); return start_x + width * ratio; @@ -1803,7 +1965,7 @@ auto layout_tuplets( auto first = invalid_layout_index; auto last = invalid_layout_index; for (auto index = std::size_t{}; index < notes.size(); ++index) { - if (notes[index].source != nullptr && notes[index].source->tuplet_id == tuplet.id) { + if (notes[index].source != nullptr && (notes[index].source->tuplet_id == tuplet.id || std::find(notes[index].source->tuplet_ids.begin(), notes[index].source->tuplet_ids.end(), tuplet.id) != notes[index].source->tuplet_ids.end())) { first = std::min(first, index); last = index; } @@ -1818,8 +1980,9 @@ auto layout_tuplets( top_y = std::min(top_y, notes[index].beamed ? notes[index].stem_tip_y : notes[index].high_y); same_beam = same_beam && notes[index].beamed && notes[index].beam_group_index == beam_index; } - auto text = std::to_string(tuplet.note_count); - auto y = top_y - spacing * 1.55F; + auto text = tuplet.show_number == "none" ? std::string{} : std::to_string(tuplet.note_count); + if (tuplet.show_number == "both") text += ":" + std::to_string(tuplet.space_count); + auto y = top_y - spacing * (1.55F + 1.25F * tuplet.depth); auto x0 = notes[first].x; auto x1 = notes[last].x; add_text( @@ -1830,11 +1993,11 @@ auto layout_tuplets( std::move(text), 1, score_muted_ink(), - spacing * 1.6F, + spacing * 5.0F, system_index, measure_index, first); - if (!same_beam) { + if (tuplet.bracket == "yes" || (!same_beam && tuplet.bracket != "no")) { auto bracket_y = y + spacing * 0.65F; add_stroke_line(layout, vamp_zero::score_layout_item_role::TUPLET_BRACKET, {.x = x0, .y = bracket_y}, {.x = x1, .y = bracket_y}, 1.0F, score_muted_ink(), system_index, measure_index); add_stroke_line(layout, vamp_zero::score_layout_item_role::TUPLET_BRACKET, {.x = x0, .y = bracket_y}, {.x = x0, .y = bracket_y + spacing * 0.35F}, 1.0F, score_muted_ink(), system_index, measure_index); @@ -1866,7 +2029,7 @@ auto measure_notation_top_y( auto first = invalid_layout_index; auto last = invalid_layout_index; for (auto index = std::size_t{}; index < notes.size(); ++index) { - if (notes[index].source != nullptr && notes[index].source->tuplet_id == tuplet.id) { + if (notes[index].source != nullptr && (notes[index].source->tuplet_id == tuplet.id || std::find(notes[index].source->tuplet_ids.begin(), notes[index].source->tuplet_ids.end(), tuplet.id) != notes[index].source->tuplet_ids.end())) { first = std::min(first, index); last = index; } @@ -1956,7 +2119,7 @@ auto find_measure_ref(std::vector const& refs, std::size_t 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 pitch.id.empty() ? same_pitch(head.pitch, pitch) : head.pitch.id == pitch.id || head.pitch.id == pitch.tie_to; }); return found == ref.heads.end() ? nullptr : &*found; } @@ -1972,16 +2135,15 @@ auto find_tied_destination(vamp_zero::score_document const& score, std::size_t m 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; - } + if (note.grace) continue; + if (note.kind == "rest" || note.kind == "spacer") return std::nullopt; auto matching_pitch = std::find_if(note.pitches.begin(), note.pitches.end(), [&](auto const& candidate) { - return same_pitch(candidate, pitch); + return pitch.id.empty() ? same_pitch(candidate, pitch) : candidate.id == pitch.tie_to; }); if (matching_pitch == note.pitches.end()) { return std::nullopt; } - if (note.tie_from_previous) { + if (note.tie_from_previous || matching_pitch->tie_from == pitch.id) { return tied_note_destination{.measure_index = current_measure, .note_index = current_note}; } return std::nullopt; @@ -2037,6 +2199,7 @@ auto layout_page_ties( } auto const* start_ref = find_note_ref(note_refs, measure_index, note_index); for (auto const& pitch : note.pitches) { + if (!pitch.id.empty() && pitch.tie_to.empty()) continue; 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); @@ -2103,10 +2266,10 @@ auto layout_measure( std::float32_t staff_top, std::float32_t spacing, std::vector& note_refs, - std::vector& measure_refs + std::vector& measure_refs, + std::size_t first_note = 0, + std::size_t note_count = std::numeric_limits::max() ) -> 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); - auto notation_x = bounds.x + spacing * 1.15F; 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); @@ -2116,21 +2279,45 @@ auto layout_measure( .notation_bounds = notation_bounds, }); auto total_ticks = measure_ticks(measure); - auto elapsed = std::int64_t{}; + auto last_note = first_note + std::min(note_count, measure.notes.size()-first_note); + auto offset_ticks = 0.0; + for (auto index = std::size_t{}; index < first_note; ++index) offset_ticks += spacing_ticks(measure.notes[index]); + if (first_note < measure.notes.size() && !measure.notes[first_note].written.type.empty()) offset_ticks = measure.notes[first_note].onset.quarters()*4096.0; + auto end_ticks = total_ticks; + if (last_note < measure.notes.size()) end_ticks = !measure.notes[last_note].written.type.empty() ? measure.notes[last_note].onset.quarters()*4096.0 : offset_ticks; + if (last_note < measure.notes.size() && measure.notes[last_note].written.type.empty()) + for (auto i = first_note; i < last_note; ++i) end_ticks += spacing_ticks(measure.notes[i]); + total_ticks = std::max(0.00001, end_ticks-offset_ticks); + auto elapsed = 0.0; auto accidentals = make_accidental_state(effective_key_signature(score, measure)); + if (measure.structured_key) accidentals.key_alterations = measure.key_alterations; auto notes = std::vector{}; notes.reserve(measure.notes.size()); - for (auto index = std::size_t{}; index < measure.notes.size(); ++index) { + for (auto index = std::size_t{}; index < first_note; ++index) notated_accidentals_for_note(accidentals, measure.notes[index], measure.notes[index].notehead == "slash"); + for (auto index = first_note; index < last_note; ++index) { auto const& note = measure.notes[index]; auto duration_ticks = spacing_ticks(note); + if (!note.written.type.empty()) elapsed = note.onset.quarters() * 4096.0 - offset_ticks; auto x = note_x(notation_x, notation_width, total_ticks, elapsed, duration_ticks); + for (auto const& a : measure.attributes) { + if (a.onset.quarters() * 4096.0 == elapsed+offset_ticks && a.key_alterations) { + accidentals.key_alterations = *a.key_alterations; + accidentals.measure_alterations.clear(); + } + } + if (!note.written.type.empty() && !note.measure_rest) x = notation_x + spacing + (notation_width - spacing*2) * static_cast(elapsed / total_ticks); + if (note.grace) x -= spacing * 1.5F; auto const slash = note.notehead == "slash"; auto accidental_marks = notated_accidentals_for_note(accidentals, note, slash); notes.push_back(build_note_layout(score, note, index, elapsed, duration_ticks, x, staff_top, spacing, accidental_marks)); + for (auto const& a : measure.attributes) if (a.onset.quarters()*4096.0 == elapsed+offset_ticks) { + notes.back().leading_space += attribute_width(a); + } + notes.back().visual_left -= notes.back().leading_space; elapsed += duration_ticks; } fit_note_spacing(notes, notation_bounds, spacing); - auto beam_groups = make_beam_groups(notes, staff_top, spacing); + auto beam_groups = make_beam_groups(notes, measure, offset_ticks, staff_top, spacing); for (auto& note : notes) { refresh_note_visual_extents(note, spacing); } @@ -2144,14 +2331,59 @@ auto layout_measure( 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); + add_text(layout, vamp_zero::score_layout_item_role::MEASURE_NUMBER, bounds.x + spacing*0.20F, + chord_y-text_line_height(1)-3.0F, std::to_string(measure_number), 1, score_muted_ink(), spacing*4, system_index, measure_index); 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); + if (!measure.section_label.empty() && first_note == 0) { + auto section_y = chord_y-text_line_height(2)-text_line_height(1)-9.0F; 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); } + auto at_time = [&](double ticks) { + if (notes.empty()) return notation_x; + auto before = notation_x; + auto before_time = 0.0; + for (auto const& n : notes) { + if (n.source->grace) continue; + if (std::abs(n.elapsed_ticks - ticks) < 0.00001) return n.x; + if (n.elapsed_ticks > ticks) return before + (n.x-before)*static_cast((ticks-before_time)/(n.elapsed_ticks-before_time)); + before = n.x; + before_time = n.elapsed_ticks; + } + return before + (notation_right_x-before)*static_cast((ticks-before_time)/std::max(0.00001,total_ticks-before_time)); + }; + for (auto const& h : measure.harmonies) { + auto time = h.onset.quarters()*4096.0; + if (time < offset_ticks || time >= end_ticks) continue; + auto x = at_time(time-offset_ticks); + add_text(layout, vamp_zero::score_layout_item_role::CHORD_SYMBOL, x-spacing, chord_y, h.symbol, 2, score_ink(), + std::max(spacing*2, bounds.x+bounds.width-x), system_index, measure_index); + } + for (auto const& a : measure.attributes) { + auto time = a.onset.quarters()*4096.0; + if (time < offset_ticks || time >= end_ticks) continue; + auto x = at_time(time-offset_ticks) - attribute_width(a); + if (!a.clef.empty()) x += layout_clef(layout, a.clef, x, staff_top, spacing*0.75F, system_index); + if (a.key_alterations) x += layout_structured_key(layout, measure.clef, *a.key_alterations, x, staff_top, spacing*0.75F, system_index, &measure.key_alterations); + if (!a.time_signature.empty()) layout_time_signature(layout, a.time_signature, x, staff_top, spacing*0.75F, system_index); + } + auto measure_start = 0.0; + for (auto i = std::size_t{}; i < measure_index; ++i) measure_start += measure_ticks(score.measures[i]); + for (auto const& shift : score.octave_shifts) { + auto start = std::max(offset_ticks, shift.start.quarters()*4096.0-measure_start); + auto end = std::min(end_ticks, shift.end.quarters()*4096.0-measure_start); + if (start >= end) continue; + auto x0 = at_time(start-offset_ticks), x1 = at_time(end-offset_ticks); + auto above = shift.octaves < 0; + auto y = above ? notation_top_y-spacing*2 : notation_bottom_y+spacing*2; + auto label = std::to_string(std::abs(shift.octaves)*7+1) + (above ? "va" : "vb"); + add_text(layout, vamp_zero::score_layout_item_role::OCTAVE_SHIFT, x0, y-spacing, label, 1, score_ink(), spacing*4, system_index, measure_index); + for (auto x = x0+spacing*3; x < x1; x += spacing) add_stroke_line(layout, vamp_zero::score_layout_item_role::OCTAVE_SHIFT, + {.x=x,.y=y}, {.x=std::min(x+spacing*0.5F,x1),.y=y}, 1, score_ink(), system_index, measure_index); + } + auto previous_chord = std::string{}; for (auto const& note : notes) { if (note.source == nullptr || note.hidden || note.source->chord_symbol.empty() || note.source->chord_symbol == previous_chord) { @@ -2173,7 +2405,7 @@ auto layout_measure( } for (auto const& note : notes) { - if (note.hidden || note.rest || !has_stem(note.duration)) { + if (note.hidden || note.rest || !has_stem(note.duration) || note.source->stem == "none") { continue; } add_stroke_line( @@ -2198,6 +2430,51 @@ auto layout_measure( system_index, measure_index); } + for (auto const& group : beam_groups) { + for (auto index = group.first_note; index <= group.last_note; ++index) { + auto const& n = notes[index]; + for (int level = 2; level <= flag_count(n.duration); ++level) { + auto type = beam_type(n, level); + auto next = index < group.last_note && flag_count(notes[index+1].duration) >= level; + auto previous = index > group.first_note && flag_count(notes[index-1].duration) >= level; + if (type == "backward hook" || type == "forward hook") { next = false; previous = false; } + else if (type == "end") next = false; + if (!next && previous) continue; + auto y = group.beam_y + (group.stem_up ? 1.0F : -1.0F) * spacing * 0.8F * (level-1); + auto end = next ? notes[index+1].stem_x : n.stem_x + spacing * (type == "backward hook" || index == group.last_note ? -1.0F : 1.0F); + add_stroke_line(layout, vamp_zero::score_layout_item_role::BEAM, {.x=n.stem_x,.y=y}, {.x=end,.y=y}, + scaled(beam_thickness_spaces, spacing), score_ink(), system_index, measure_index, n.note_index); + } + } + } + for (auto const& n : notes) { + if (n.hidden || n.rest) continue; + for (auto const& head : n.heads) { + auto stem = secondary_stem(n, head, spacing); + if (!stem) continue; + add_stroke_line(layout, vamp_zero::score_layout_item_role::STEM, stem->base, stem->tip, 1, score_ink(), system_index, measure_index, n.note_index); + if (stem->flag) add_glyph(layout, vamp_zero::score_layout_item_role::FLAG, *stem->flag, stem->tip, spacing*n.size, score_ink(), system_index, measure_index, n.note_index); + } + if (n.source->grace_slash) { + auto direction = n.stem_up ? 1.0F : -1.0F; + add_stroke_line(layout, vamp_zero::score_layout_item_role::STEM, + {.x=n.stem_x-spacing*n.size*0.5F,.y=n.stem_tip_y+direction*spacing*n.size*1.5F}, + {.x=n.stem_x+spacing*n.size,.y=n.stem_tip_y+direction*spacing*n.size*0.3F}, 1.0F, score_ink(), system_index, measure_index, n.note_index); + } + if (n.source->tremolo_marks) { + auto partner = std::find_if(notes.begin(), notes.end(), [&](auto const& candidate) { return candidate.source->id == n.source->tremolo_partner; }); + if (n.source->tremolo_type == "stop" && partner != notes.end()) continue; + auto x0 = n.stem_x - spacing*0.5F; + auto x1 = partner == notes.end() ? n.stem_x+spacing*0.7F : partner->stem_x; + if (partner == notes.end() && n.source->tremolo_type == "start") x1 = notation_right_x; + if (n.source->tremolo_type == "stop") { x0 = notation_x; x1 = n.stem_x; } + for (int mark = 0; mark < n.source->tremolo_marks; ++mark) { + auto y = n.stem_tip_y + (n.stem_up ? 1.0F : -1.0F) * spacing * (1.0F + mark*0.8F); + add_stroke_line(layout, vamp_zero::score_layout_item_role::TREMOLO, {.x=x0,.y=y+spacing*0.3F}, + {.x=x1,.y=y-spacing*0.3F}, spacing*0.45F, score_ink(), system_index, measure_index, n.note_index); + } + } + } for (auto const& note : notes) { if (note.hidden || note.rest || note.beamed || !has_flag(note.duration)) { continue; @@ -2206,6 +2483,12 @@ auto layout_measure( } for (auto index = std::size_t{}; index < notes.size(); ++index) { layout_note_symbols(layout, notes[index], staff_top, spacing, system_index, measure_index); + auto const& source = *notes[index].source; + if (source.written.ratio.numerator != source.written.ratio.denominator && source.tuplet_ids.empty() && !source.tremolo_marks && !source.hidden) { + auto label = std::to_string(source.written.ratio.denominator) + ":" + std::to_string(source.written.ratio.numerator); + if (!source.written.normal_type.empty()) label += " " + source.written.normal_type + std::string(source.written.normal_dots, '.'); + add_text(layout, vamp_zero::score_layout_item_role::TUPLET, notes[index].x-spacing, notation_top_y-spacing*1.5F, label, 1, score_ink(), spacing*9, system_index, measure_index, notes[index].note_index); + } if (notes[index].source != nullptr && !notes[index].hidden && !notes[index].source->lyric.empty()) { add_text( layout, @@ -2220,6 +2503,10 @@ auto layout_measure( measure_index, notes[index].note_index); } + if (!source.lyric_extend.empty() && !source.hidden) { + auto end = index+1 std::vector { auto state = make_accidental_state(measure.key_signature.empty() ? inherited_key_signature : measure.key_signature); + if (measure.structured_key) state.key_alterations = measure.key_alterations; auto accidentals = std::vector{}; for (auto const& note : measure.notes) { + for (auto const& a : measure.attributes) if (a.onset.quarters() == note.onset.quarters() && a.key_alterations) { + state.key_alterations = *a.key_alterations; + state.measure_alterations.clear(); + } auto marks = notated_accidentals_for_note(state, note, note.notehead == "slash"); accidentals.insert(accidentals.end(), marks.begin(), marks.end()); } @@ -2269,13 +2563,25 @@ auto vamp_zero::score_system_ranges(vamp_zero::score_document const& score) -> s auto vamp_zero::score_page_count(vamp_zero::score_document const& score) -> std::size_t { auto ranges = make_score_system_ranges(score); - if (ranges.empty()) { - return 1U; - } - return (ranges.size() + vamp_zero::score_systems_per_page - 1U) / vamp_zero::score_systems_per_page; + return page_starts(score, ranges).size(); } auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document const& score, std::size_t page_index) -> vamp_zero::score_page_layout { + if (bounds.width != 976.0F || bounds.height != 484.0F || bounds.x != 0 || bounds.y != 0) { + auto layout = layout_score_page({.x=0,.y=0,.width=976,.height=484}, score, page_index); + auto scale = std::min(bounds.width/976.0F, bounds.height/484.0F); + auto ox = bounds.x+(bounds.width-976*scale)/2; + auto oy = bounds.y+(bounds.height-484*scale)/2; + auto point = [&](luma::point p) { return luma::point{.x=ox+p.x*scale,.y=oy+p.y*scale}; }; + for (auto& item : layout.items) { + item.origin = point(item.origin); item.from = point(item.from); item.to = point(item.to); item.control = point(item.control); + item.bounds = {.x=ox+item.bounds.x*scale,.y=oy+item.bounds.y*scale,.width=item.bounds.width*scale,.height=item.bounds.height*scale}; + item.thickness *= scale; item.staff_spacing *= scale; item.text_size_multiplier *= scale; + } + layout.staff_spacing *= scale; + layout.bounds = bounds; + return layout; + } auto layout = vamp_zero::score_page_layout{ .bounds = bounds, .staff_spacing = 12.0F, @@ -2293,47 +2599,57 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c auto const content_x = bounds.x + margin; auto const content_y = bounds.y + 84.0F; 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 system_ranges = make_score_system_ranges(score); - auto const first_system = page_index * vamp_zero::score_systems_per_page; + auto const starts = page_starts(score, system_ranges); + auto const first_system = page_index < starts.size() ? starts[page_index] : system_ranges.size(); + auto const last_system = page_index+1 < starts.size() ? starts[page_index+1] : system_ranges.size(); 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; - auto const staff_top = system_y + spacing * 4.25F; + auto system_y = content_y; + for (auto system = std::size_t{}; system < last_system-first_system; ++system) { auto const range_index = first_system + system; auto const* range = range_index < system_ranges.size() ? &system_ranges[range_index] : nullptr; auto const* first_system_measure = range != nullptr && range->first_measure < score.measures.size() ? &score.measures[range->first_measure] : nullptr; + auto metrics = metrics_for(score, *range); + auto system_height = std::min(376.0F, metrics.height()); + auto spacing = std::min(12.0F, system_height/(metrics.above+metrics.below)); + auto const staff_top = system_y + spacing*metrics.above; layout_staff(layout, content_x, staff_top, content_width, spacing, system); auto const show_time = range == nullptr ? page_index == 0U && system == 0U : show_time_signature_for_system(score, *range); - auto const measure_start_x = layout_system_preamble(layout, score, first_system_measure, content_x, staff_top, spacing, show_time, system); + auto const measure_start_x = layout_system_preamble(layout, score, first_system_measure, content_x, staff_top, spacing, show_time, system, range ? range->first_note : 0); auto const measure_area_width = std::max(spacing * 12.0F, content_x + content_width - measure_start_x); if (range == nullptr || range->measure_count == 0U) { continue; } - auto const measure_width = measure_area_width / static_cast(range->measure_count); + auto total_width = 0.0F; + for (auto i = std::size_t{}; i < range->measure_count; ++i) total_width += measure_width(score.measures[range->first_measure+i]); + auto x = measure_start_x; for (auto measure = std::size_t{}; measure < range->measure_count; ++measure) { auto const measure_index = range->first_measure + measure; - auto const x = measure_start_x + static_cast(measure) * measure_width; + auto const width = measure_area_width * measure_width(score.measures[measure_index]) / total_width; if (measure_index < score.measures.size()) { layout_measure( layout, score, score.measures[measure_index], - {.x = x, .y = system_y, .width = measure_width, .height = system_height}, + {.x = x, .y = system_y, .width = width, .height = system_height}, measure_index + 1U, system, measure_index, staff_top, spacing, note_refs, - measure_refs); + measure_refs, + range->first_note, + range->note_count); } - auto const bar_x = x + measure_width; - auto const final_bar = measure + 1U == range->measure_count || measure_index + 1U >= score.measures.size(); + auto const bar_x = x + width; + x += width; + if (range->first_note + std::min(range->note_count, score.measures[measure_index].notes.size()-range->first_note) < score.measures[measure_index].notes.size()) continue; + auto const final_bar = score.measures[measure_index].right_barline == "light-heavy" || measure_index + 1U >= score.measures.size(); add_stroke_line( layout, final_bar ? vamp_zero::score_layout_item_role::FINAL_BARLINE : vamp_zero::score_layout_item_role::BARLINE, @@ -2344,6 +2660,7 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c system, measure_index); } + system_y += system_height; } layout_page_ties(layout, score, note_refs, measure_refs, spacing); @@ -2353,6 +2670,11 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c auto vamp_zero::draw_score_page(luma::draw_context& context, luma::rect bounds, vamp_zero::score_document const& score, std::size_t page_index) -> void { auto layout = vamp_zero::layout_score_page(bounds, score, page_index); + vamp_zero::draw_score_layout(context, layout); +} + +auto vamp_zero::draw_score_layout(luma::draw_context& context, vamp_zero::score_page_layout const& layout) -> void { + auto bounds = layout.bounds; for (auto const& item : layout.items) { if (item.role == vamp_zero::score_layout_item_role::PAPER) { draw_layout_item(context, item); diff --git a/src/vamp_zero/ui.cpp b/src/vamp_zero/ui.cpp index 117c4c7..20da88f 100644 --- a/src/vamp_zero/ui.cpp +++ b/src/vamp_zero/ui.cpp @@ -60,6 +60,19 @@ struct vamp_zero::score_draw_cache { } std::unordered_map entries{}; + std::shared_ptr page_document{}; + std::size_t page_index{}; + std::optional page{}; + + auto draw(luma::draw_context& context, luma::rect bounds, std::shared_ptr document, std::size_t index) -> void { + if (!page || page_document != document || index != page_index + || page->bounds.x != bounds.x || page->bounds.y != bounds.y || page->bounds.width != bounds.width || page->bounds.height != bounds.height) { + page = vamp_zero::layout_score_page(bounds, *document, index); + page_document = std::move(document); + page_index = index; + } + vamp_zero::draw_score_layout(context, *page); + } }; namespace { @@ -353,7 +366,7 @@ auto draw_score(luma::draw_context& context, luma::rect bounds, vamp_zero::app_s .width = bounds.width - 48.0F, .height = bounds.height - 36.0F, }; - vamp_zero::draw_score_page(context, paper_bounds, **parsed, state.page_index); + score_cache.draw(context, paper_bounds, *parsed, state.page_index); } class vamp_root_control final : public luma::control { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7c35f26..d05b697 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,15 +11,19 @@ target_link_libraries(product-vamp-test PRIVATE target_compile_features(product-vamp-test PRIVATE cxx_std_23) target_compile_definitions(product-vamp-test PRIVATE VAMP_TEST_SOURCE_DIR="${CMAKE_SOURCE_DIR}" + VAMP_TEST_ASSETS_DIR="${CMAKE_BINARY_DIR}/generated-assets" ) add_test(NAME product-vamp COMMAND product-vamp-test) +add_executable(vamp-render-score EXCLUDE_FROM_ALL render_score.cpp) +target_link_libraries(vamp-render-score PRIVATE vamp-zero-core) +target_compile_features(vamp-render-score PRIVATE cxx_std_23) set_tests_properties(product-vamp PROPERTIES TIMEOUT 60) add_test( NAME product-vamp-generator - COMMAND ${Python3_EXECUTABLE} -m unittest tests.test_score_generator + COMMAND ${Python3_EXECUTABLE} -m unittest discover -s tests -p test_*.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) -set_tests_properties(product-vamp-generator PROPERTIES TIMEOUT 60) +set_tests_properties(product-vamp-generator PROPERTIES TIMEOUT 180) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --test-dir ${CMAKE_BINARY_DIR} @@ -28,3 +32,4 @@ add_custom_target(check --timeout 60 USES_TERMINAL ) +add_dependencies(check product-vamp-test) diff --git a/tests/fixtures/notation.musicxml b/tests/fixtures/notation.musicxml new file mode 100644 index 0000000..4f2921c --- /dev/null +++ b/tests/fixtures/notation.musicxml @@ -0,0 +1,41 @@ + + + + Pitch and Rhythm Regression + Notation + + + 160G2 + Cdiminished + G516th + C1528quartersharp + E58eighth + 416th + B432halfsingleheld + + + B416quarter + D516quarter + -1minorC3 + A332half2 + + + 24G2 + + G512eighthbegin + A512eighthcontinue + B-1512eighthend + C612eighthbegin + D612eighthcontinue + E612eighthend + + + + 3 + C52quarter32 + D52quarter32 + E52quarter32 + light-heavy + + + diff --git a/tests/render_score.cpp b/tests/render_score.cpp new file mode 100644 index 0000000..3f3901e --- /dev/null +++ b/tests/render_score.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +class score_view final : public luma::control { +public: + score_view(luma::application& app, vamp_zero::score_document score, std::string output, std::size_t page, bool& saved) + : app_{app}, score_{std::move(score)}, output_{std::move(output)}, page_{page}, saved_{saved} {} + auto measure(luma::draw_context&, luma::theme const&, luma::constraints) -> luma::size override { return {976,484}; } + auto draw(luma::draw_context& context, luma::theme const&) const -> void override { + vamp_zero::draw_score_page(context, bounds(), score_, page_); + rlDrawRenderBatchActive(); + auto image = LoadImageFromScreen(); + saved_ = ExportImage(image, output_.c_str()); + UnloadImage(image); + app_.request_stop(); + } +private: + luma::application& app_; + vamp_zero::score_document score_; + std::string output_; + std::size_t page_; + bool& saved_; +}; +} + +int main(int argc, char** argv) { + if (argc < 3) { std::cerr << "Usage: vamp-render-score input.json output.png [page]\n"; return 2; } + auto input = std::ifstream{argv[1]}; + auto json = std::string{std::istreambuf_iterator{input},std::istreambuf_iterator{}}; + auto score = vamp_zero::parse_score_document(json,argv[1]); + if (!score) { std::cerr << score.error().describe() << '\n'; return 1; } + auto app = luma::application{{.title="Notation Regression",.display={.width=976,.height=484,.target_fps=30,.fullscreen=false}}}; + auto saved = false; + auto root = app.set_root(std::make_unique(app,std::move(*score),argv[2],argc>3 ? std::stoul(argv[3]) : 0,saved)); + if (!root) return 1; + auto ran = app.run(); + return ran && saved ? 0 : 1; +} diff --git a/tests/test.cpp b/tests/test.cpp index 4a3fe80..f6d58c5 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include @@ -275,45 +277,24 @@ auto has_value(noto::query_result const& result, std::size_t column, std::string } auto score_asset_json(std::string title, std::string key = "C") -> std::string { - return std::string{R"json({ - "metadata": { - "title": ")json"} + title + R"json(", - "composer": "Composer", - "key_signature": ")json" + key + R"json(", - "time_signature": "4/4" - }, - "staves": [ - { - "id": "staff-lead", - "name": "Lead", - "clef": "treble", - "role": "lead", - "measures": [ - { - "id": "measure-1", - "time_signature": "4/4", - "key_signature": ")json" + key + R"json(", - "voices": [ - { - "id": "voice-1", - "notes": [ - { - "id": "note-1", - "kind": "note", - "duration_ticks": 4096, - "pitches": [ - {"letter": "c", "octave": 4, "accidental": "n"} - ] - } - ] - } - ] - } - ] - } - ] -} -)json"; + auto value = boost::json::parse(R"json({ + "schema_version":2, + "metadata":{"title":"Fixture","composer":"Composer","key_signature":"C","time_signature":"4/4"}, + "staves":[{"id":"staff-lead","name":"Lead","role":"lead","clef":{"sign":"G","line":2,"octave_change":0}, + "measures":[{"id":"measure-1","duration":{"numerator":4,"denominator":1},"key_signature":"C","time_signature":"4/4", + "key":{"alterations":[0,0,0,0,0,0,0]},"meter":{"groups":[{"beats":[4],"unit":4}]}, + "clef":{"sign":"G","line":2,"octave_change":0}, + "voices":[{"id":"voice-1","notes":[{"id":"note-1","kind":"note", + "onset":{"numerator":0,"denominator":1},"duration":{"numerator":4,"denominator":1}, + "written":{"type":"whole","dots":0}, + "pitches":[{"id":"pitch-1","letter":"c","octave":4,"alter":0, + "duration":{"numerator":4,"denominator":1},"written":{"type":"whole","dots":0}}]}]}]}]}] + })json"); + auto& root = value.as_object(); + root["metadata"].as_object()["title"] = title; + root["metadata"].as_object()["key_signature"] = key; + root["staves"].as_array()[0].as_object()["measures"].as_array()[0].as_object()["key_signature"] = key; + return boost::json::serialize(value); } auto navigation_state() -> vamp_zero::app_state { @@ -504,75 +485,180 @@ BOOST_AUTO_TEST_CASE(main_menu_metronome_setting_survives_score_selection) { } BOOST_AUTO_TEST_CASE(score_parser_preserves_notation_preamble) { - auto parsed = vamp_zero::parse_score_document(R"json( -{ - "metadata": { - "title": "Standard Tune", - "composer": "Composer", - "key_signature": "Eb", - "time_signature": "3/4" - }, - "staves": [ - { - "id": "staff-lead", - "name": "Lead", - "clef": "treble", - "role": "lead", - "measures": [ - { - "id": "measure-1", - "time_signature": "3/4", - "key_signature": "Eb", - "voices": [ - { - "id": "voice-1", - "notes": [ - { - "id": "note-1", - "kind": "note", - "duration_ticks": 4096, - "tuplet_id": "triplet-1", - "pitches": [ - { - "letter": "b", - "octave": 4, - "accidental": "b" - } - ] - } - ], - "tuplets": [ - { - "id": "triplet-1", - "note_count": 3, - "space_count": 2 - } - ] - } - ] - } - ] - } - ] -} -)json", "standard-tune.json"); - - BOOST_REQUIRE(static_cast(parsed)); + auto parsed = vamp_zero::parse_score_document(score_asset_json("Standard Tune", "Eb"), "standard-tune.json"); + BOOST_REQUIRE(static_cast(parsed)); BOOST_TEST(parsed->title == "Standard Tune"); BOOST_TEST(parsed->composer == "Composer"); BOOST_TEST(parsed->key_signature == "Eb"); - BOOST_TEST(parsed->time_signature == "3/4"); - BOOST_TEST(parsed->clef == "treble"); + BOOST_TEST(parsed->time_signature == "4/4"); + BOOST_TEST(parsed->clef == "G:2:0"); BOOST_TEST(parsed->part == "Lead"); BOOST_REQUIRE_EQUAL(parsed->measures.size(), 1U); - BOOST_TEST(parsed->measures.front().key_signature == "Eb"); - BOOST_TEST(parsed->measures.front().time_signature == "3/4"); - BOOST_REQUIRE_EQUAL(parsed->measures.front().notes.size(), 1U); - BOOST_TEST(parsed->measures.front().notes.front().tuplet_id == "triplet-1"); - BOOST_REQUIRE_EQUAL(parsed->measures.front().tuplets.size(), 1U); - BOOST_TEST(parsed->measures.front().tuplets.front().id == "triplet-1"); - BOOST_TEST(parsed->measures.front().tuplets.front().note_count == 3); - BOOST_TEST(parsed->measures.front().tuplets.front().space_count == 2); + BOOST_TEST(parsed->measures.front().duration.numerator == 4); + BOOST_TEST(parsed->measures.front().notes.front().written.type == "whole"); +} + +BOOST_AUTO_TEST_CASE(score_parser_loads_the_generated_catalogue) { + auto count = std::size_t{}; + for (auto const& entry : std::filesystem::recursive_directory_iterator{VAMP_TEST_ASSETS_DIR}) { + if (entry.path().extension() != ".json") continue; + auto input = std::ifstream{entry.path()}; + auto json = std::string{std::istreambuf_iterator{input}, std::istreambuf_iterator{}}; + auto parsed = vamp_zero::parse_score_document(json, entry.path().string()); + BOOST_TEST_CONTEXT(entry.path().string()) { + BOOST_REQUIRE(parsed.has_value()); + BOOST_TEST(vamp_zero::score_page_count(*parsed) > 0U); + } + ++count; + } + BOOST_TEST(count > 0U); +} + +BOOST_AUTO_TEST_CASE(score_parser_rejects_old_schema_and_extra_voices) { + BOOST_TEST(!vamp_zero::parse_score_document(R"({"metadata":{"title":"Old"},"staves":[]})").has_value()); + auto source = boost::json::parse(score_asset_json("Fixture")); + auto& voices = source.as_object()["staves"].as_array()[0].as_object()["measures"].as_array()[0].as_object()["voices"].as_array(); + auto copy = voices[0]; + voices.push_back(copy); + BOOST_TEST(!vamp_zero::parse_score_document(boost::json::serialize(source)).has_value()); +} + +BOOST_AUTO_TEST_CASE(score_parser_rejects_invalid_rational_and_dangling_tie) { + auto source = boost::json::parse(score_asset_json("Fixture")); + auto& n = source.as_object()["staves"].as_array()[0].as_object()["measures"].as_array()[0].as_object()["voices"].as_array()[0].as_object()["notes"].as_array()[0].as_object(); + n["duration"].as_object()["denominator"] = 0; + BOOST_TEST(!vamp_zero::parse_score_document(boost::json::serialize(source)).has_value()); + n["duration"].as_object()["denominator"] = 1; + n["pitches"].as_array()[0].as_object()["tie_to"] = "missing"; + BOOST_TEST(!vamp_zero::parse_score_document(boost::json::serialize(source)).has_value()); +} + +BOOST_AUTO_TEST_CASE(score_layout_supports_all_written_durations_and_multiple_dots) { + auto names = std::vector{"maxima","long","breve","whole","half","quarter","eighth","16th","32nd","64th","128th","256th","512th","1024th"}; + for (auto const& name : names) { + auto n = note({pitch('c',"n",4)}); + n.written = {.type=name,.dots=2}; + n.pitches.front().written = n.written; + auto layout = vamp_zero::layout_score_page({.x=0,.y=0,.width=976,.height=484},score_with_notes({n}),0); + auto dots = layout_items(layout,vamp_zero::score_layout_item_role::DOT,0,0,0); + BOOST_TEST(dots.size()==2U); + for (auto const& item : layout.items) if (item.kind == vamp_zero::score_layout_item_kind::GLYPH) { + BOOST_TEST(item.bounds.width > 0); + BOOST_TEST(item.bounds.height > 0); + } + n.kind = "rest"; + n.pitches.clear(); + layout = vamp_zero::layout_score_page({.x=0,.y=0,.width=976,.height=484},score_with_notes({n}),0); + auto rests = layout_items(layout,vamp_zero::score_layout_item_role::REST,0,0,0); + BOOST_REQUIRE_EQUAL(rests.size(),1U); + BOOST_TEST(rests[0]->bounds.width > 0); + BOOST_TEST(rests[0]->bounds.height > 0); + } +} + +BOOST_AUTO_TEST_CASE(score_layout_paginates_dense_measures_without_losing_events) { + auto notes = std::vector{}; + for (int i = 0; i < 128; ++i) { + auto n = note_with_ticks({pitch('c',"#",4)},128); + n.written = {.type="128th"}; + n.duration = {1,32}; + n.onset = {i,32}; + notes.push_back(n); + } + auto score = score_with_notes(notes); + auto total = std::size_t{}; + auto pages = vamp_zero::score_page_count(score); + BOOST_TEST(pages>1U); + for (auto page = std::size_t{}; page < pages; ++page) { + auto layout = vamp_zero::layout_score_page({.x=0,.y=0,.width=976,.height=484},score,page); + for (auto const& item : layout.items) if (item.role == vamp_zero::score_layout_item_role::NOTEHEAD) { + ++total; + BOOST_TEST(item.bounds.x >= 0); + BOOST_TEST(item.bounds.x+item.bounds.width <= 976); + BOOST_TEST(item.bounds.y >= 0); + BOOST_TEST(item.bounds.y+item.bounds.height <= 484); + } + } + BOOST_TEST(total==notes.size()); +} + +BOOST_AUTO_TEST_CASE(score_layout_uses_independent_harmony_onsets) { + auto source = boost::json::parse(score_asset_json("Fixture")); + auto& m = source.as_object()["staves"].as_array()[0].as_object()["measures"].as_array()[0].as_object(); + m["harmonies"] = boost::json::parse(R"([{"id":"h1","onset":{"numerator":2,"denominator":1},"kind":"major","symbol":"F"}])"); + auto score = vamp_zero::parse_score_document(boost::json::serialize(source)); + BOOST_REQUIRE(score.has_value()); + auto layout = vamp_zero::layout_score_page({.x=0,.y=0,.width=976,.height=484},*score,0); + auto heads = layout_items(layout,vamp_zero::score_layout_item_role::NOTEHEAD,0,0); + auto chords = layout_items(layout,vamp_zero::score_layout_item_role::CHORD_SYMBOL,0,0); + BOOST_REQUIRE_EQUAL(heads.size(),1U); + BOOST_REQUIRE_EQUAL(chords.size(),1U); + BOOST_TEST(chords[0]->origin.x > heads[0]->origin.x); +} + +BOOST_AUTO_TEST_CASE(score_layout_keeps_section_measure_and_harmony_labels_separate) { + auto score = score_with_notes({note({pitch('e', "n", 5)})}); + score.measures[0].section_label = "Melody"; + score.measures[0].harmonies.push_back({"h1", {0,1}, "Cmaj7", "major-seventh"}); + for (auto bounds : {luma::rect{.x=0,.y=0,.width=976,.height=484}, luma::rect{.x=0,.y=0,.width=480,.height=320}}) { + auto layout = vamp_zero::layout_score_page(bounds, score, 0); + auto sections = layout_items(layout, vamp_zero::score_layout_item_role::SECTION_LABEL); + auto numbers = layout_items(layout, vamp_zero::score_layout_item_role::MEASURE_NUMBER); + auto chords = layout_items(layout, vamp_zero::score_layout_item_role::CHORD_SYMBOL); + BOOST_REQUIRE_EQUAL(sections.size(), 1U); + BOOST_REQUIRE_EQUAL(numbers.size(), 1U); + BOOST_REQUIRE_EQUAL(chords.size(), 1U); + BOOST_TEST(sections[0]->bounds.y >= 0); + BOOST_TEST(bottom_edge(*sections[0]) < numbers[0]->bounds.y); + BOOST_TEST(bottom_edge(*numbers[0]) < chords[0]->bounds.y); + } +} + +BOOST_AUTO_TEST_CASE(score_layout_ties_only_the_linked_chord_tone) { + auto source = boost::json::parse(score_asset_json("Partial tie")); + auto& ns = source.as_object()["staves"].as_array()[0].as_object()["measures"].as_array()[0].as_object()["voices"].as_array()[0].as_object()["notes"].as_array(); + auto first = ns[0].as_object(); + first["id"] = "a"; + first["duration"].as_object()["numerator"] = 2; + first["written"].as_object()["type"] = "half"; + auto& p = first["pitches"].as_array()[0].as_object(); + p["id"] = "a-c"; + p["duration"] = first["duration"]; + p["written"] = first["written"]; + auto other = p; + other["id"] = "a-e"; + other["letter"] = "e"; + first["pitches"].as_array().push_back(other); + auto second = first; + second["id"] = "b"; + second["onset"].as_object()["numerator"] = 2; + second["pitches"].as_array()[0].as_object()["id"] = "b-c"; + second["pitches"].as_array()[0].as_object()["tie_from"] = "a-c"; + second["pitches"].as_array()[1].as_object()["id"] = "b-g"; + second["pitches"].as_array()[1].as_object()["letter"] = "g"; + first["pitches"].as_array()[0].as_object()["tie_to"] = "b-c"; + ns = {first, second}; + auto parsed = vamp_zero::parse_score_document(boost::json::serialize(source)); + BOOST_REQUIRE(parsed.has_value()); + auto layout = vamp_zero::layout_score_page({.x=0,.y=0,.width=976,.height=484}, *parsed, 0); + BOOST_TEST(layout_items(layout, vamp_zero::score_layout_item_role::NOTEHEAD).size() == 4U); + BOOST_TEST(layout_items(layout, vamp_zero::score_layout_item_role::TIE).size() == 1U); + ns[0].as_object()["pitches"].as_array()[1].as_object()["duration"].as_object()["numerator"] = 0; + BOOST_TEST(!vamp_zero::parse_score_document(boost::json::serialize(source)).has_value()); +} + +BOOST_AUTO_TEST_CASE(score_layout_clears_secondary_chord_stems_above_notation) { + auto n = note({pitch('c', "#", 5), pitch('e', "n", 5)}); + n.written = {.type="quarter", .dots=2}; + n.pitches[0].written = n.written; + n.pitches[1].written = {.type="eighth"}; + auto score = score_with_notes({n}); + score.measures[0].harmonies.push_back({"h",{0,1},"Cdim","diminished"}); + auto layout = vamp_zero::layout_score_page({.x=0,.y=0,.width=976,.height=484}, score, 0); + auto chords = layout_items(layout, vamp_zero::score_layout_item_role::CHORD_SYMBOL); + BOOST_REQUIRE_EQUAL(chords.size(), 1U); + for (auto role : {vamp_zero::score_layout_item_role::STEM, vamp_zero::score_layout_item_role::FLAG}) + for (auto item : layout_items(layout, role)) BOOST_TEST(bottom_edge(*chords[0]) < item->bounds.y); } BOOST_AUTO_TEST_CASE(accidentals_are_suppressed_when_the_key_signature_already_covers_the_pitch) { @@ -813,6 +899,15 @@ BOOST_AUTO_TEST_CASE(database_import_upserts_catalog_rows_and_keeps_unmanaged_ro {noto::sqlite_value{score_asset_json("Custom Tune")}}); BOOST_REQUIRE(inserted_custom); + write_text(assets / "songs" / "custom.json", score_asset_json("Catalog Collision")); + BOOST_REQUIRE(run_import(config, assets)); + auto custom = query_database(database_path, "SELECT title, catalog_managed FROM songs WHERE id = 'custom'"); + BOOST_REQUIRE(custom); + BOOST_REQUIRE_EQUAL(custom->rows.size(), 1U); + BOOST_TEST(text_value(custom->rows[0][0]) == "Custom Tune"); + BOOST_TEST(integer_value(custom->rows[0][1]) == 0); + std::filesystem::remove(assets / "songs" / "custom.json"); + std::filesystem::remove(song_path); BOOST_REQUIRE(run_import(config, assets)); auto counts = query_database(database_path, "SELECT sum(CASE WHEN id = 'tune' THEN 1 ELSE 0 END), sum(CASE WHEN id = 'custom' THEN 1 ELSE 0 END) FROM songs"); @@ -919,7 +1014,7 @@ BOOST_AUTO_TEST_CASE(score_page_count_uses_forced_notation_breaks) { .measures = std::move(measures), }; - BOOST_TEST(vamp_zero::score_page_count(score) == 2U); + BOOST_TEST(vamp_zero::score_page_count(score) == 3U); } BOOST_AUTO_TEST_CASE(score_layout_attaches_stems_to_notehead_anchors) { diff --git a/tests/test_build_bootstrap.py b/tests/test_build_bootstrap.py new file mode 100644 index 0000000..a00e3ac --- /dev/null +++ b/tests/test_build_bootstrap.py @@ -0,0 +1,34 @@ +import hashlib +import importlib.util +from pathlib import Path +import subprocess +import sys +import types +import unittest +from unittest.mock import Mock, patch + + +class BuildBootstrapTests(unittest.TestCase): + def test_requirements_fingerprint_invalidates_cached_build_image(self): + root = Path(__file__).resolve().parents[1] + spec = importlib.util.spec_from_file_location('vamp_bootstrap', root / 'modulus.py') + bootstrap = importlib.util.module_from_spec(spec) + spec.loader.exec_module(bootstrap) + digest = hashlib.sha256((root / 'tools/requirements.txt').read_bytes()).hexdigest() + self.assertEqual(bootstrap.build_requirements_digest(), digest) + self.assertIn(f'build-requirements="{digest}"', bootstrap.product_build_dockerfile()) + + package = types.ModuleType('modulus') + cli = types.ModuleType('modulus.cli') + package.cli = cli + available, docker_run = Mock(return_value=True), Mock() + cli.docker_image_available, cli.docker_run = available, docker_run + cli.main = lambda command: cli.docker_image_available(types.SimpleNamespace(docker_image='test-image')) + for label, expected in [('',False), ('outdated',False), (digest,True)]: + result = subprocess.CompletedProcess([], 0, label+'\n', '') + with self.subTest(label=label), patch.dict(sys.modules, {'modulus':package, 'modulus.cli':cli}), \ + patch.object(bootstrap, 'ensure_runtime'), patch.object(bootstrap.subprocess, 'run', return_value=result), \ + patch.object(sys, 'path', list(sys.path)): + self.assertEqual(bootstrap.delegate('configure', []), expected) + self.assertIs(cli.docker_image_available, available) + self.assertIs(cli.docker_run, docker_run) diff --git a/tests/test_music_model.py b/tests/test_music_model.py new file mode 100644 index 0000000..4c59cb8 --- /dev/null +++ b/tests/test_music_model.py @@ -0,0 +1,139 @@ +import argparse +import copy +from fractions import Fraction +from pathlib import Path +import tempfile +import unittest + +from generator import harmony +from generator.__main__ import generate_assets +from generator.arrange.generate import active_chord, arrange_document, generated_voice, propagated_chords +from generator.arrange.fretboard import pitches_for_voicing +from generator.model import beat_lengths, fraction, key_from_fifths, pitch_number, rational, validate_document +from generator.musicxml import load_musicxml +from generator.styles import BUILT_IN_STYLES, canonical_style +from generator.transposition import concert_document, shift_pitch, transpose_document +from tests.test_musicxml import parse, notes, xml_note, xml_score + + +class MusicModelTests(unittest.TestCase): + def test_chord_semantics_are_not_voicing_heuristics(self): + cases = {'C':{0,4,7},'Cdim':{0,3,6},'Cdim7':{0,3,6,9},'Csus2':{0,2,7}, + 'C7b5':{0,4,6,10},'G7b9':{7,11,2,5,8},'Fmaj7#11':{5,9,0,4,11}} + for label, expected in cases.items(): + self.assertEqual(set(harmony.pitch_classes(harmony.parse_symbol(label))),expected,label) + for label, essentials in [('G7b9',{11,5,8}),('C13',{4,10,9})]: + pitches = pitches_for_voicing(label) + self.assertTrue(essentials.issubset({pitch_number(p)%12 for p in pitches}),label) + with self.assertRaises(ValueError): + harmony.parse_symbol('Cunrecognised') + + def test_transposition_preserves_spelling_octaves_modes(self): + for p in ({'letter':'b','alter':1,'octave':4},{'letter':'c','alter':-1,'octave':4},{'letter':'f','alter':2,'octave':4}): + self.assertEqual(shift_pitch(p,0,0),p) + self.assertEqual(pitch_number(shift_pitch(p,1,2)),pitch_number(p)+2) + source = parse(xml_score(xml_note(),attributes='-3minor')) + result = transpose_document(source,'D') + self.assertEqual(result['metadata']['key_signature'],'D minor') + self.assertEqual(result['metadata']['key']['alterations'],key_from_fifths(-1,'minor')['alterations']) + + def test_additive_and_compound_grouping(self): + self.assertEqual(beat_lengths({'groups':[{'beats':[3,2],'unit':8}]}),[Fraction(3,2),Fraction(1)]) + self.assertEqual(beat_lengths({'groups':[{'beats':[6],'unit':8}]}),[Fraction(3,2)]*2) + self.assertEqual(beat_lengths({'groups':[{'beats':[3],'unit':4}]}),[Fraction(1)]*3) + + def test_generated_rhythm_follows_mid_measure_meter(self): + change = '' + source = parse(xml_score(xml_note(duration=1,kind='quarter')+change+xml_note(duration=3,kind='half',extra=''))) + measure = source['staves'][0]['measures'][0] + events = generated_voice(measure,'slash',None) + self.assertEqual([fraction(n['onset']) for n in events],[0,1,Fraction(5,2)]) + self.assertEqual([fraction(n['duration']) for n in events],[1,Fraction(3,2),Fraction(3,2)]) + + def test_harmony_carry_uses_last_event_and_never_future_event(self): + c = harmony.parse_symbol('C7') | {'onset':rational(0)} + f = harmony.parse_symbol('F7') | {'onset':rational(2)} + measures = [{'harmonies':[c,f]},{'harmonies':[]}] + self.assertEqual(propagated_chords(measures),[None,f]) + self.assertIsNone(active_chord({'harmonies':[f]},Fraction(0),None)) + self.assertEqual(active_chord(measures[0],Fraction(1),None),c) + + def test_tuplet_ratios_and_tie_links_validated(self): + document = parse(xml_score(xml_note())) + notes(document)[0]['pitches'][0]['tie_to'] = 'missing' + with self.assertRaisesRegex(ValueError,'dangling tie'): + validate_document(document) + document = parse(xml_score(xml_note())) + notes(document)[0]['duration'] = {'numerator':8,'denominator':2} + with self.assertRaisesRegex(ValueError,'reduced'): + validate_document(document) + + def test_optional_turnaround_and_form(self): + source = load_musicxml(Path('data/songs/it-could-happen-to-you.musicxml')) + for part in ('acoustic','electric','vocal'): + result = arrange_document(source,part) + validate_document(result) + measures = result['staves'][0]['measures'] + self.assertEqual(sum(bool(m.get('section_label')) for m in measures),7) + self.assertFalse(measures[-1]['harmonies']) + self.assertTrue(any(h for m in measures[:-1] for h in m['harmonies'])) + self.assertFalse(any('chord_optional' in h for m in measures for h in m['harmonies'])) + + def test_player_styles_preserved(self): + self.assertEqual(len(BUILT_IN_STYLES),50) + for text in ('Med.','Med. Swing','Medium','Medium Swing'): + self.assertEqual(canonical_style(text),'Medium Swing') + with self.assertRaises(ValueError): + canonical_style('Unrecognised style') + + def test_failure_preserves_generated_assets(self): + with tempfile.TemporaryDirectory() as temp: + root = Path(temp) + data = root/'data' + (data/'songs').mkdir(parents=True) + (data/'songs'/'bad.musicxml').write_text(xml_score(xml_note(duration=5)),encoding='utf-8') + output = root/'output' + output.mkdir() + (output/'last-good').write_text('preserved') + args = argparse.Namespace(data_dir=data,output_dir=output,static_assets_dir=Path('package/assets'),clean=True,check=False) + with self.assertRaises(ValueError): + generate_assets(args) + self.assertEqual((output/'last-good').read_text(),'preserved') + (data/'songs'/'bad.musicxml').write_text(xml_score(xml_note()),encoding='utf-8') + args.check = True + with self.assertRaisesRegex(ValueError,'summary'): + generate_assets(args) + self.assertEqual((output/'last-good').read_text(),'preserved') + + def test_instrument_transposition_is_converted_once(self): + source = parse(xml_score(xml_note(),attributes='-1-2')) + concert = concert_document(source) + p = notes(concert)[0]['pitches'][0] + self.assertEqual((p['letter'],p['alter'],p['octave']),('b',-1,3)) + self.assertEqual(concert_document(concert),concert) + self.assertEqual(concert['metadata']['key_signature'],'Bb') + + def test_deterministic_generation_and_removal(self): + with tempfile.TemporaryDirectory() as temp: + data,output = Path(temp)/'data',Path(temp)/'output' + (data/'songs').mkdir(parents=True) + source = data/'songs'/'fixture.musicxml' + source.write_text(xml_score(xml_note()),encoding='utf-8') + args = argparse.Namespace(data_dir=data,output_dir=output,static_assets_dir=Path('package/assets'),clean=True,check=False) + generate_assets(args) + first = {p.relative_to(output):p.read_bytes() for p in output.rglob('*') if p.is_file()} + generate_assets(args) + self.assertEqual(first,{p.relative_to(output):p.read_bytes() for p in output.rglob('*') if p.is_file()}) + source.unlink() + generate_assets(args) + self.assertFalse(list(output.rglob('*.json'))) + + def test_pickup_tie_survives_vocal_restoration(self): + source = xml_score(xml_note(duration=1,kind='quarter',extra=''),implicit=True) + source = source.replace('',''+xml_note(extra='')+'') + result = arrange_document(parse(source),'vocal') + validate_document(result) + measures = result['staves'][0]['measures'] + start = measures[0]['voices'][0]['notes'][-1]['pitches'][0] + end = measures[1]['voices'][0]['notes'][0]['pitches'][0] + self.assertEqual(start['tie_to'],end['id']) diff --git a/tests/test_musicxml.py b/tests/test_musicxml.py new file mode 100644 index 0000000..587516a --- /dev/null +++ b/tests/test_musicxml.py @@ -0,0 +1,188 @@ +from fractions import Fraction +from pathlib import Path +import tempfile +import unittest + +from generator.musicxml import MusicXMLError, load_musicxml +from generator.model import NOTE_TYPES, TYPE_LENGTHS, fraction, pitch_number + + +def xml_score(content, attributes='', *, divisions=1, implicit=False): + return ('' + 'Fixture' + 'Lead' + f'' + f'{divisions}0' + '' + f'G2{attributes}' + f'{content}') + + +def xml_note(step='C', duration=4, kind='whole', extra='', prefix='', alter=0, octave=4): + return (f'{prefix}{step}{alter}{octave}' + f'{duration}{kind}{extra}') + + +def parse(text): + with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / 'fixture.musicxml' + path.write_text(text, encoding='utf-8') + return load_musicxml(path) + + +def notes(document, measure=0): + return document['staves'][0]['measures'][measure]['voices'][0]['notes'] + + +class MusicXMLTests(unittest.TestCase): + def test_regression_fixture(self): + score = load_musicxml(Path('tests/fixtures/notation.musicxml')) + self.assertEqual(len(score['staves'][0]['measures']),4) + self.assertEqual(len(score['staves'][0]['octave_shifts']),1) + + def test_catalogue_imports(self): + sources = sorted(Path('data').rglob('*.musicxml')) + self.assertEqual(len(sources), 6) + for path in sources: + with self.subTest(path=path): + self.assertEqual(load_musicxml(path)['schema_version'], 2) + + def test_all_written_values_and_dots(self): + for kind in NOTE_TYPES: + for dots in (0, 1, 2, 3): + value = TYPE_LENGTHS[kind] * (2-Fraction(1, 2**dots)) + score = parse(xml_score(xml_note(duration=value.numerator, kind=kind, extra=''*dots), divisions=value.denominator, implicit=True)) + self.assertEqual(fraction(notes(score)[0]['duration']), value) + self.assertEqual(notes(score)[0]['written'], {'type':kind, 'dots':dots}) + + def test_divisions_do_not_change_music(self): + a = parse(xml_score(xml_note(duration=4))) + b = parse(xml_score(xml_note(duration=1920), divisions=480)) + self.assertEqual(a, b) + + def test_doctype_allowed_but_entities_rejected(self): + source = xml_score(xml_note()) + source = source.replace(']>') + source += xml_note('E',1,'quarter',prefix='') + source += xml_note(duration=2,kind='half',extra='') + source += xml_note('G',2,'half',prefix='') + result = notes(parse(xml_score(source))) + self.assertEqual(fraction(result[0]['pitches'][1]['duration']), 1) + self.assertEqual(result[0]['pitches'][0]['tie_to'], result[1]['pitches'][0]['id']) + self.assertNotIn('tie_to', result[0]['pitches'][1]) + + def test_grace_cue_and_measured_tremolo(self): + grace = 'D416th' + regular = xml_note(extra='3', prefix='') + result = notes(parse(xml_score(grace+regular))) + self.assertEqual(fraction(result[0]['duration']), 0) + self.assertTrue(result[0]['grace']['slash']) + self.assertTrue(result[1]['cue']) + self.assertEqual(result[1]['tremolo']['marks'], 3) + + def test_exact_quintuplets(self): + content = '' + for i in range(5): + tuplet = '' if i==0 else '' if i==4 else '' + extra = '54' + extra += ''+tuplet+'' + content += xml_note(duration=4, kind='quarter',extra=extra) + result = parse(xml_score(content,divisions=5)) + self.assertEqual(sum(fraction(n['duration']) for n in notes(result)),4) + self.assertTrue(all(fraction(n['duration'])==Fraction(4,5) for n in notes(result))) + self.assertEqual(result['staves'][0]['tuplets'][0]['note_count'],5) + + def test_inconsistent_written_timing_is_rejected(self): + modulation = '32' + with self.assertRaisesRegex(MusicXMLError,'disagrees with written duration'): + parse(xml_score(xml_note(extra=modulation))) + with self.assertRaisesRegex(MusicXMLError,'disagrees with written duration'): + parse(xml_score(xml_note(kind='half'))) + + def test_paired_tremolo_timing(self): + modulation = '21' + content = ''.join(xml_note(step,2,'whole',extra=modulation+f'3') + for step,action in [('C','start'),('G','stop')]) + result = notes(parse(xml_score(content))) + self.assertEqual(result[0]['tremolo']['partner'],result[1]['id']) + self.assertEqual(fraction(result[0]['duration']),2) + + def test_mid_measure_clef_and_key_changes(self): + change = '-3minorC3' + result = parse(xml_score(xml_note(duration=2,kind='half')+change+xml_note(duration=2,kind='half'))) + attr = result['staves'][0]['measures'][0]['attributes'][0] + self.assertEqual(fraction(attr['onset']),2) + self.assertEqual(attr['key']['mode'],'minor') + self.assertEqual(attr['clef']['sign'],'C') + + def test_optional_marker_contract(self): + marker = 'vamp:optional-chord' + h = 'Cmajor' + score = parse(xml_score(marker+h+xml_note())) + self.assertTrue(score['staves'][0]['measures'][0]['harmonies'][0]['chord_optional']) + for content in (marker+xml_note(),marker+marker+h+xml_note(),marker.replace('optional-chord','unknown')+xml_note()): + with self.assertRaisesRegex(MusicXMLError,'measure 1'): + parse(xml_score(content)) + + def test_rejects_musical_content_outside_contract(self): + extras = ['', '', + '22', '', + ''] + for extra in extras: + with self.subTest(extra=extra), self.assertRaises(MusicXMLError): + parse(xml_score(xml_note(extra=extra))) + for extra in ('', '4', + ''): + with self.subTest(extra=extra), self.assertRaises(MusicXMLError): + parse(xml_score(xml_note()+extra)) + with self.assertRaises(MusicXMLError): + parse(xml_score(xml_note(alter='0.5'))) + with self.assertRaises(MusicXMLError): + parse(xml_score(xml_note(extra=''))) + with self.assertRaises(MusicXMLError): + parse(xml_score(xml_note(duration=5))) + + def test_b_sharp_c_flat_preserve_octave(self): + b = notes(parse(xml_score(xml_note('B',alter=1))))[0]['pitches'][0] + c = notes(parse(xml_score(xml_note('C',alter=-1))))[0]['pitches'][0] + self.assertEqual(pitch_number(b),72) + self.assertEqual(pitch_number(c),59) + + def test_musicxml_degree_alterations_are_relative(self): + h = 'Cminor31alter' + from generator.harmony import degrees + score = parse(xml_score(h+xml_note())) + self.assertEqual(degrees(score['staves'][0]['measures'][0]['harmonies'][0])[3],0) + h = h.replace('minor','minor') + with self.assertRaisesRegex(MusicXMLError,'contradictory'): + parse(xml_score(h+xml_note())) + + def test_nested_and_cross_bar_tuplets(self): + modulation = '94' + def tuplet(number,action): + details = '32' if action=='start' else '' + return f'{details}' + first = xml_note(duration=4,kind='quarter',extra=modulation+''+tuplet(1,'start')+tuplet(2,'start')+'') + last = xml_note(duration=4,kind='quarter',extra=modulation+''+tuplet(2,'stop')+tuplet(1,'stop')+'') + text = xml_score(first,divisions=9,implicit=True) + text = text.replace('', ''+last+'') + result = parse(text) + spans = result['staves'][0]['tuplets'] + self.assertEqual([t['depth'] for t in spans],[0,1]) + self.assertEqual(fraction(notes(result,1)[0]['duration']),Fraction(4,9)) + self.assertTrue(all(t['end']==notes(result,1)[0]['id'] for t in spans)) diff --git a/tests/test_score_generator.py b/tests/test_score_generator.py index fb80059..330ba2a 100644 --- a/tests/test_score_generator.py +++ b/tests/test_score_generator.py @@ -8,11 +8,6 @@ import tempfile from pathlib import Path from generator.__main__ import copy_runtime_fonts -from generator.arrange.generate import arrange_document -from generator.arrange.fretboard import chord_pitch_classes, pitches_for_voicing -from generator.styles import BUILT_IN_STYLES -from generator.transposition import mod12 -from generator.score import normalize_score_document from tools import generate_pico_font @@ -42,217 +37,3 @@ class FontAssetsTests(unittest.TestCase): self.assertEqual(before, output.stat().st_mtime_ns) output.write_text("stale", encoding="utf-8") self.assertNotEqual(subprocess.run([*command, "--check"], capture_output=True).returncode, 0) - - -class CompactScoreJsonTests(unittest.TestCase): - def pitch_classes(self, pitches: list[dict]) -> set[int]: - natural = {"c": 0, "d": 2, "e": 4, "f": 5, "g": 7, "a": 9, "b": 11} - alterations = {"bb": -2, "b": -1, "n": 0, "": 0, "#": 1, "x": 2} - result: set[int] = set() - for pitch in pitches: - letter = str(pitch.get("letter", "c")).lower() - accidental = str(pitch.get("accidental", "n")) - result.add(mod12(natural.get(letter, 0) + alterations.get(accidental, 0))) - return result - - def chord_symbols(self, measure: dict) -> list[str]: - return [ - note["chord_symbol"] - for note in measure["voices"][0]["notes"] - if isinstance(note.get("chord_symbol"), str) and note["chord_symbol"] - ] - - def measure_notes(self, measure: dict) -> list[dict]: - return measure["voices"][0]["notes"] - - 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.assertNotIn("version", document) - self.assertEqual(document["metadata"]["key_signature"], "F") - self.assertEqual(document["metadata"]["style"], "Medium Swing") - 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_canonicalizes_player_styles(self) -> None: - self.assertEqual(len(BUILT_IN_STYLES), 50) - - for style in ("Med.", "Med. Swing", "Medium", "Medium Swing"): - document = normalize_score_document({ - "title": "Style Tune", - "style": style, - "measures": ["C4:1"], - }) - self.assertEqual(document["metadata"]["style"], "Medium Swing") - - with self.assertRaisesRegex(ValueError, "supported player style"): - normalize_score_document({ - "title": "Bad Style Tune", - "style": "Medium Bossa That Does Not Exist", - "measures": ["C4:1"], - }) - - def test_compact_json_preserves_musicxml_like_measure_metadata(self) -> None: - document = normalize_score_document({ - "title": "Metadata Tune", - "measures": [ - { - "notes": "C4:1", - "attributes": {"divisions": 4}, - "directions": [{"type": "dynamics", "value": "mf"}], - "harmony": [{"root": "C", "kind": "major-seventh"}], - "barlines": {"right": "light-heavy"}, - }, - ], - }) - - measure = document["staves"][0]["measures"][0] - self.assertEqual(measure["attributes"], {"divisions": 4}) - self.assertEqual(measure["directions"], [{"type": "dynamics", "value": "mf"}]) - self.assertEqual(measure["harmony"], [{"root": "C", "kind": "major-seventh"}]) - self.assertEqual(measure["barlines"], {"right": "light-heavy"}) - - def test_arrangements_preserve_measure_notation_metadata(self) -> None: - document = normalize_score_document({ - "title": "Directions Tune", - "key": "C", - "time": "4/4", - "measures": [ - { - "notes": "[Cmaj7] C4:1", - "directions": [{"type": "rehearsal", "value": "A"}], - "barlines": {"left": "heavy-light"}, - }, - ], - }) - - arranged = arrange_document(document, "vocal") - measure = arranged["staves"][0]["measures"][0] - - self.assertEqual(measure["directions"], [{"type": "rehearsal", "value": "A"}]) - self.assertEqual(measure["barlines"], {"left": "heavy-light"}) - - def test_compact_json_marks_optional_chords(self) -> None: - document = normalize_score_document({ - "title": "Optional Turnaround", - "key": "Eb", - "time": "4/4", - "measures": [ - "[(F-7)] r:2 [(Bb7)] r:2", - ], - }) - - notes = document["staves"][0]["measures"][0]["voices"][0]["notes"] - - self.assertEqual(notes[0]["chord_symbol"], "F-7") - self.assertTrue(notes[0]["chord_optional"]) - self.assertEqual(notes[1]["chord_symbol"], "Bb7") - self.assertTrue(notes[1]["chord_optional"]) - - def test_arrangements_omit_optional_turnaround_from_final_ending(self) -> None: - document = normalize_score_document({ - "title": "Optional Turnaround", - "key": "Eb", - "time": "4/4", - "measures": [ - "[Ebmaj7] C4:1", - "[Ab7] D4:1", - "[Ebmaj7] E4:1", - "[(F-7)] r:2 [(Bb7)] r:2", - ], - }) - - for part_type in ("acoustic", "electric", "vocal"): - arranged = arrange_document(document, part_type) - measures = arranged["staves"][0]["measures"] - non_final_turnaround = measures[7] - final_turnaround = measures[-1] - - self.assertNotIn("version", arranged) - self.assertEqual(self.chord_symbols(non_final_turnaround), ["F-7", "Bb7"]) - self.assertEqual(self.chord_symbols(final_turnaround), []) - self.assertFalse(any("chord_optional" in note for measure in measures for note in self.measure_notes(measure))) - - if part_type in {"acoustic", "electric"}: - self.assertTrue(all(note["kind"] == "rest" for note in self.measure_notes(final_turnaround))) - - def test_slash_arrangements_preserve_source_chord_markers(self) -> None: - document = normalize_score_document({ - "title": "Slash Tune", - "key": "C", - "time": "4/4", - "measures": [ - "[Cmaj7] C4:1", - "[F7#11] F4:1", - "[B-7b5] B4:1", - "[E7b9] E4:1", - ], - }) - - arranged = arrange_document(document, "acoustic") - slash_measure = arranged["staves"][0]["measures"][12] - - self.assertEqual(self.chord_symbols(slash_measure), ["Cmaj7"]) - self.assertTrue(all(note.get("notehead") == "slash" for note in self.measure_notes(slash_measure))) - - def test_jazz_core_chords_include_color_tones(self) -> None: - self.assertEqual(set(chord_pitch_classes("G7b9")), {7, 11, 5, 8}) - self.assertEqual(set(chord_pitch_classes("C13")), {0, 4, 10, 9}) - self.assertEqual(set(chord_pitch_classes("Fmaj7#11")), {5, 9, 4, 11}) - - self.assertTrue({11, 5, 8}.issubset(self.pitch_classes(pitches_for_voicing("G7b9")))) - self.assertTrue({4, 10, 9}.issubset(self.pitch_classes(pitches_for_voicing("C13")))) - - 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() diff --git a/tools/generate_score_metrics.py b/tools/generate_score_metrics.py new file mode 100644 index 0000000..c83dfaa --- /dev/null +++ b/tools/generate_score_metrics.py @@ -0,0 +1,38 @@ +"""Extract notation glyph bounds from the shipped OTF, in staff-space units.""" +import argparse +from pathlib import Path + +from fontTools.pens.boundsPen import BoundsPen +from fontTools.ttLib import TTFont + + +def generate(font_path, output): + with TTFont(font_path) as font: + glyphs, cmap = font.getGlyphSet(), font.getBestCmap() + units = font['head'].unitsPerEm / 4 + ranges = [(0xE050, 0xE06F), (0xE080, 0xE09F), (0xE0A0, 0xE0A5), + (0xE101, 0xE102), (0xE1E7, 0xE1E8), (0xE240, 0xE250), + (0xE260, 0xE270), (0xE4E0, 0xE4EE)] + records = [] + for start, end in ranges: + for codepoint in range(start, end): + if codepoint not in cmap: + continue + pen = BoundsPen(glyphs) + glyphs[cmap[codepoint]].draw(pen) + if pen.bounds: + box = ', '.join(f'{v / units:.8f}F' for v in pen.bounds) + records.append(f' {{{codepoint}, {{{box}}}}},') + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text('#pragma once\n#include \nnamespace vamp_zero::font_data {\n' + 'struct glyph { int codepoint; std::array bounds; };\n' + f'inline constexpr std::array glyphs = {{{{\n' + + '\n'.join(records) + '\n}};\n}\n', encoding='ascii') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--font', type=Path, required=True) + parser.add_argument('--output', type=Path, required=True) + args = parser.parse_args() + generate(args.font, args.output) diff --git a/tools/requirements.txt b/tools/requirements.txt index e7cc6c1..716fff5 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1 +1,3 @@ Pillow==12.3.0 +defusedxml==0.7.1 +fonttools==4.63.0