The current state, the decisions behind it, and the next steps in the order they should happen. Each step lists what has to be true before it counts as done.
StaffInk (pen) ScoreShift (photo OMR)
theory.js ─┐ audio.js ─┐ theory.js ─┐ score.js ─┐ player.js ─┐
▼ ▼ ▼ ▼ ▼
pitch.js duration.js player.js (shared primitives, loaded eagerly)
▲
adapters/staffink.js ◄── ir.js ──► adapters/scoreshift.js (loaded on first use)
fromStaffInk / toStaffInk fromScoreShift
interpret.js anchors.js playback.js layout.js
pitch.js, duration.js, player.js, rational.js) are imported by
both apps directly. Both service workers precache them, so both apps now need ../notation-ir/
deployed next to them.perform() gives the same times, lengths and velocities as StaffInk’s own
performance(), including slurs, staccato, staccatissimo, tenuto, marcato, accents,
fermatas, ties and hairpins.performance().twovoice, where the second voice is dropped and reported.| Decision | Why |
|---|---|
| Time is exact rationals of a whole note | Tuplets of any ratio, no tick rounding. Apps keep their integer ticks and convert at the edge. |
| Content has no measures | Unfinished, unmeasured and misread music is a state the IR can hold, not an error. |
| Bars, ties across bars, rests and spelling are interpretation | The same content can be read in another meter, in free time, or with printed bars winning. |
Problems are reported, never repaired (check, measure status, report) |
ScoreShift’s repair stays in ScoreShift. The IR keeps both what was read and how it fits. |
A head holds pitch (d, optional alter), not a staff position |
A clef change never moves a note. Ink and OMR leave alter empty and the IR resolves it. |
src keeps input geometry; anchors resolve when used |
Markers follow edits, and layout and playheads map back to the ink or the photo. |
| Fragments have their own local time | Sketched ideas need no place on the main timeline. |
One set of articulation rules lives in perform() (StaffInk’s) |
Both apps sound the same through the IR. ScoreShift’s own Play button keeps its slightly different rules. |
Every adapter is from<App>(…) → IR plus, where useful, to<App>(IR) → { score, report }.
si<StaffInk id>.byId (IR id → timeline entry).acc only. OMR and imports set alter. A transposing part sets
part.transpose (written to concert pitch, in semitones).vel holds the dynamic level only. marks uses the IR vocabulary:
staccato staccatissimo tenuto accent marcato fermata up-bow down-bow. Slurs, ties and
hairpins are links. A dynamic is a dynamic direction carrying event.src: { x, y, line } on events. Printed barlines become barline directions
with src.report, never silently:
dropped, approximated, clefs, padded, meter.skipped grace notes.fromScoreShift
toStaffInk
fromStaffInk
interpret.written: false), the rules reproduce the engraved
rhythm of 14 of the 15 fixtures. The exception is sixeight, where the Verovio
engraving lets a quarter cross the second beat.ss:L<line>:q<x in quarter spaces>:<voice> and barlines ssb:L<line>:q<x>.findById matches a moved head within half a staff space, preferring the same voice. A
deleted note returns null and is never replaced by its neighbour.checkProgram lists every anchor that no longer resolves, and perform throws
AnchorError for one..scoreshift project (programs), and
playProgram() with no argument replays it, reporting any markers that lost their note.score.program
(staffink/arrange.js).markers.js). ScoreShift draws the page’s saved program on
the photo, with lost markers red and dashed.arrangement.js), and programs autosave per page as well as going into the project.playheads.js), one per cursor, coloured like their
clip, for normal Play as well as programs.surface.js defines the interface, and both apps’ page geometry
moved behind it (staffink/surface.js, scoreshift/surface.js) with no change in
behaviour (all smoke checks unchanged). Next on it:annotations.js) and ScoreShift’s ✎ Annotate mode (pen and mouse, finger until a pen is
seen; colours, widths, eraser and pen eraser end, undo/redo, tap a mark to recolour,
delete or re-attach it; saved per page and in the project, included in the PNG export).
Exports ask whether to draw them and which colours (PNG and the transposed PDF, whose
pages are drawn in the export workers). Next: carrying the marks into StaffInk with
“Edit in StaffInk” (needs an id mapping);src stays as the photo surface’s identity, and
engraved positions are never copied into the IR.src and changing note.fix.
musicxml.js and midi.js built from interpreted output, so free-time
and reinterpreted music exports.
tempo directions honoured by perform, and al Fine / conditional jumps in
walk.vibes/ folder as a whole. Each app imports ../notation-ir/, and ScoreShift’s
hand-off opens ../staffink/.vibes/ and open each app at its real path, as deployed.