Carry a re-engraved slice's notation in the bundle
A replaced slice shipped as pixels only, so the LilyPond behind it died with the project file — and a project is spent once exported. Correcting one wrong note meant retyping the system. Slices gain an optional `engraving` object: the language, key, time and one entry per voice holding the notes and lyrics verbatim. Structured per staff rather than one blob of source, because that is what both a later edit and a MIDI render want; song-level key and time are resolved per slice so reading one needs no context from its neighbours. Additive and ignorable, so the format stays at version 1.
This commit is contained in:
@@ -39,6 +39,30 @@ METADATA_FIELDS = (
|
||||
NUMERIC_FIELDS = frozenset({"tempo"})
|
||||
|
||||
|
||||
def _engraving(project: Project, page: int, slot: int) -> dict | None:
|
||||
"""The notation behind a re-engraved slice, or None for a scanned one.
|
||||
|
||||
The slice image stays the presentation; this is the notation it was made
|
||||
from, carried so the music can be edited again or turned into sound. Key
|
||||
and time are resolved against the song defaults here — a consumer reading
|
||||
one slice should not have to know what the rest of the song inherited.
|
||||
"""
|
||||
replacement = project.pages[page].replacements[slot]
|
||||
if not replacement or not replacement.voices:
|
||||
return None
|
||||
return {
|
||||
"lang": "lilypond",
|
||||
"key": replacement.key or project.key,
|
||||
"time": replacement.time or project.time,
|
||||
"print_time": replacement.print_time,
|
||||
"voices": [
|
||||
{"clef": v.clef, "notes": v.notes.strip()}
|
||||
| ({"lyrics": v.lyrics.strip()} if v.lyrics.strip() else {})
|
||||
for v in replacement.voices
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def song_json(project: Project, files: list[str]) -> dict:
|
||||
payload: dict = {"v": FORMAT_VERSION}
|
||||
for field in METADATA_FIELDS:
|
||||
@@ -62,6 +86,9 @@ def song_json(project: Project, files: list[str]) -> dict:
|
||||
slices: list[dict] = []
|
||||
for name, (page, slot) in zip(files, kept):
|
||||
entry: dict = {"file": name}
|
||||
engraving = _engraving(project, page, slot)
|
||||
if engraving:
|
||||
entry["engraving"] = engraving
|
||||
markers = []
|
||||
for marker in project.pages[page].markers[slot]:
|
||||
item: dict = {"type": marker.type}
|
||||
|
||||
Reference in New Issue
Block a user