Playback Mode Pages
ci / checks (push) Successful in 50m29s

This commit is contained in:
khodowany
2026-10-06 23:11:25 -05:00
parent 125aaa1637
commit d2f493c17b
33 changed files with 620 additions and 130 deletions
+58 -1
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import fractions
import hashlib
import json
import math
import pathlib
import re
import shutil
@@ -23,7 +24,63 @@ def page_timing(document, page):
times = boundaries(document)
start, end = page.systems[0].start, page.systems[-1].end
first, last = times.index(start), times.index(end)
return dict(first_measure=first + 1, measure_count=last - first, start_beat=generator.model.rational(start), end_beat=generator.model.rational(end))
measures = document['staves'][0]['measures']
systems = []
for system in page.systems:
a, b = times.index(system.start), times.index(system.end)
labels = {p.sources[0]: p for p in system.primitives if p.role == 'measure_number'}
boxes = []
for i in range(a, b):
box = labels[measures[i]['id']].box
boxes.append(dict(measure=i + 1, bounds=[box.left, box.top, box.right, box.bottom]))
systems.append(dict(first_measure=a + 1, measure_count=b - a, start_beat=generator.model.rational(system.start), end_beat=generator.model.rational(system.end), labels=boxes))
return dict(first_measure=first + 1, measure_count=last - first, start_beat=generator.model.rational(start), end_beat=generator.model.rational(end), systems=systems)
def validate_views(document, pages, playback_pages):
validate_pages(document, pages)
times = boundaries(document)
canonical = []
def system_range(system):
a, count = system['first_measure'], system['measure_count']
if type(a) is not int or type(count) is not int or a < 1 or count < 1 or a + count > len(times):
raise ValueError('invalid system measure coverage')
if generator.model.fraction(system['start_beat']) != times[a - 1] or generator.model.fraction(system['end_beat']) != times[a + count - 1]:
raise ValueError('invalid system timing')
if [label['measure'] for label in system['labels']] != list(range(a, a + count)):
raise ValueError('invalid measure label coverage')
for label in system['labels']:
box = label['bounds']
if not isinstance(box, list) or len(box) != 4 or any(type(v) not in (int, float) or not math.isfinite(v) for v in box) or not (4 <= box[0] < box[2] <= 972 and 4 <= box[1] < box[3] <= 480):
raise ValueError('invalid measure label bounds')
return a, count
for page in pages:
systems = page.get('systems', [])
if len(systems) != 2 and (page is not pages[-1] or len(systems) != 1):
raise ValueError('invalid page systems')
cursor = page['first_measure']
for system in systems:
a, count = system_range(system)
if a != cursor:
raise ValueError('system gap or overlap')
cursor += count
canonical.append(system)
if cursor != page['first_measure'] + page['measure_count']:
raise ValueError('systems disagree with page coverage')
if not isinstance(playback_pages, list) or len(playback_pages) != len(canonical):
raise ValueError('missing playback pages; regenerate score assets')
for i, page in enumerate(playback_pages):
expected = canonical[i:i + 2]
systems = page.get('systems', [])
if len(systems) != len(expected):
raise ValueError('invalid playback systems')
for actual, source in zip(systems, expected):
system_range(actual)
if any(actual[k] != source[k] for k in ('first_measure', 'measure_count', 'start_beat', 'end_beat')):
raise ValueError('playback changes system boundaries')
if any(page[k] != expected[0][k] for k in ('first_measure', 'start_beat')) or page['end_beat'] != expected[-1]['end_beat'] or page['measure_count'] != sum(s['measure_count'] for s in expected):
raise ValueError('invalid playback page timing')
def validate_pages(document, pages):
times = boundaries(document)