The handoff was written as a message to relay information; several decisions lived only there. Split into permanent homes: - docs/spec.md — scope, geometry model, pipeline order, detection, levels, editor, project file, markers, bundle format, noteman's changes, reference values - ADR 0002 — raster only in release 1; SVG slices measured at 40x WebP naive, 2.6x with a bounding-box cull, deferred on risk not size - ADR 0003 — lossless WebP with levels and 16-level alpha; every lossy option and alternative format measured larger for line art - ADR 0004 — detection proposes, the human disposes; no unattended mode - ADR 0005 — PyMuPDF for all PDF access, accepting AGPL - ADR 0006 — systems are found by vertical brackets, not row-darkness gaps, which miscount every page of a 6-voice score Also from testing against the hardest score in the repertoire: scanned PDFs carry their scan as an embedded image and must be extracted at native resolution rather than re-rendered at 600 DPI, and per-page deskew is mandatory (skew varies -2.6 to +1.2 within one PDF).
5.8 KiB
noteman-slicer — Context
A local, single-user tool that turns a score PDF into the ordered slice images noteman consumes, plus the navigation markers that sit on them. It automates the mechanical part of noteman's ingestion boundary; it does not replace GIMP for erasing previous-owner annotations.
Language
Score source: The original PDF as distributed to the choir. Either vector (engraved by a notation program) or raster (a flatbed scan of a printed page). Avoid: original, input file
Source type: Whether a score source is vector or raster. Determined once per PDF, not per page; the slicer detects it and asks the user to confirm before routing. Avoid: mode, format
Slice: The atomic visual unit of a song — one system, one full line of music across all voices, typically 4–12 bars with lyrics intact. Same definition as noteman's. Structurally, a horizontal region of a page: a page begins as a single slice and each cut splits one slice into two, so slices always tile the page with no gaps and no overlap. Avoid: segment, strip, row, band
Discard: A flag on a slice meaning it produces no output. How headers, footers and blank regions leave the song — they are slices that are simply not exported. Detection pre-sets it on a page's top and bottom slice when they contain no system. Avoid: delete, skip, exclude
Slice image:
The rendered artifact of a slice: lossless WebP, RGB pure black,
alpha = 255 − luminance, width capped at 1920px — paper is transparency, ink is
alpha. Display-ready as produced; nothing downstream reprocesses it. An SVG form
for vector sources is designed but deferred, which is why the geometry model is
renderer-agnostic.
Avoid: PNG, page image, tile
Marker:
A semantic tag placed on a slice for navigation. Vocabulary is noteman's enum
verbatim (rehearsal_letter, section_label, segno, coda, fine,
repeat_start, repeat_end, volta, to_coda, ds_al_coda, ds_al_fine,
dc_al_coda, dc_al_fine, generic_jump) and is shared coupling between the
two repos.
Project: The persistent state of slicing one song: the source PDF it points at, its cuts, discards, content rectangle, levels, staff-height overrides, markers and metadata. Autosaved beside the PDF; the bundle is generated from it, so any export can be regenerated without repeating human work. One PDF, one song, one project, one bundle — never a many-to-one in any direction. Avoid: session, document, edit list
Bundle: The single compressed file that is the only channel between the slicer and noteman. There is no API call, no direct upload. Carries the slice images in order, their markers, the original PDF and the song-level text metadata. Rehearsal MIDI and MP3s are deliberately out of the first bundle and stay noteman's upload chore until a later phase. Avoid: export, package, upload
Marker label:
The free text some markers carry — rehearsal_letter ("C"), section_label
("CHORUS"), volta ("1."). All other types are bare tags.
Jump source:
A marker that sends the reader elsewhere — to_coda, ds_al_coda,
ds_al_fine, dc_al_coda, dc_al_fine, generic_jump. Every jump source
stores its target slice explicitly; none is resolved by type at read time.
Authoring the target costs one click on a slice already on screen, and in
exchange the bundle is self-describing — no "exactly one Coda per song" rule
living unenforced in two repos, and a score with two codas simply works.
Avoid: link, reference, pointer
Cut: A horizontal line placed on a page that splits one slice into two. Straight at first; a later polyline form handles pages where systems slant or interleave. Placement is forgiving — anywhere inside the whitespace gap yields the same output, because trim crops to ink afterwards. Avoid: split, divider, break
Content rectangle: The region of a page that holds music. Set per PDF, adjustable per page, applied before cutting. Everything outside it — scan-edge bands, spine shadows, margin page numbers — is dropped. Handles margin junk structurally rather than case-by-case, because margin junk is by definition outside the music. Avoid: crop box, mask, margin
Detection: Any automatic proposal the slicer makes — skew angle, cut positions, source type, ink bounds. Detection is always a suggestion: the human confirms or modifies every result before it is committed. Source quality varies too widely for unattended processing to be trustworthy. Avoid: auto-slicing, automatic mode
Trim: Cropping a slice tight to its ink on all four sides, per slice. Normalises away the left-margin drift between scanned pages, and flattens the engraved indent of the first system — correct here, since the printed header it made room for is stripped anyway. The viewer supplies margins with CSS padding.
Staff height: The distance between a system's top and bottom staff line. The invariant a reader perceives as "the notes are the same size", and therefore what slices are normalised against — not their width. Falls out of the same row-darkness profile used for cut detection. Manually overridable, since it is detected on the same speckled scans where detection already struggles.
Song scale: Two steps, both per song, never per slice. First every slice is normalised to a common staff height, so a rescanned page or a re-engraved slice sits at the same note size as its neighbours. Then the whole song is scaled uniformly so its widest slice lands at 1920px — a ceiling, never a target: a song that comes out narrower stays narrower, because upscaling a scan past its real resolution buys softness and bytes and no detail.
Right pad: The transparent space added to the right of every slice narrower than the widest, bringing all slices in a song to the same width. Keeps the music the same size and flush left; a short system simply ends earlier.