@real-music-packages/web-core 0.46.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-JCDNEJBT.js +176 -0
- package/dist/chunk-JCDNEJBT.js.map +1 -0
- package/dist/notationPlayerVerovio.d.ts +42 -58
- package/dist/notationPlayerVerovio.js +88 -145
- package/dist/notationPlayerVerovio.js.map +1 -1
- package/dist/notationXml.d.ts +175 -0
- package/dist/notationXml.js +19 -0
- package/dist/notationXml.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
declare function stampNoteIds(xml: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Insert `<print new-system="yes"/>` as the FIRST child of the `<measure>`
|
|
4
|
+
* at each 0-based position in `breakBeforeBarIndexes`, in EVERY `<part>`
|
|
5
|
+
* (a score's parts share one measure timeline, so a break must be encoded
|
|
6
|
+
* once per part for Verovio to line the systems up across staves). A
|
|
7
|
+
* position that is ≤ 0 or ≥ that part's own measure count is silently
|
|
8
|
+
* ignored FOR THAT PART (0 is a no-op — a part already starts a new system
|
|
9
|
+
* at its own first measure). Idempotent: a measure that already carries a
|
|
10
|
+
* `<print>` element (from a prior call, or from the source data) gets
|
|
11
|
+
* `new-system="yes"` SET on that existing element rather than gaining a
|
|
12
|
+
* second one — calling this twice with the same positions serializes
|
|
13
|
+
* identically both times. Malformed input (fails to parse) is returned
|
|
14
|
+
* unchanged, same defensive style as `stampNoteIds` above. Only ever ADDS
|
|
15
|
+
* `<print>` elements — never touches a `<note>` — so `stampNoteIds`'s
|
|
16
|
+
* position-based id scheme is unaffected by a prior or subsequent call to
|
|
17
|
+
* this function (see this file's own tests for the pinned invariant).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* How many `<measure>` elements the FIRST `<part>` has — the `totalBars`
|
|
21
|
+
* that `barsPerLineBreaks`/`sectionAwareBreaks` plan against.
|
|
22
|
+
*
|
|
23
|
+
* The first part specifically, because that is the coordinate system
|
|
24
|
+
* `injectSystemBreaks` positions live in: a score's parts share one measure
|
|
25
|
+
* timeline, so the first part's ordinals index every part. And ORDINALS, not
|
|
26
|
+
* `<measure number>` attributes — scores skip and repeat bar numbers, so a
|
|
27
|
+
* count is the only safe answer. Malformed input, or a document with no
|
|
28
|
+
* part, returns 0 rather than throwing.
|
|
29
|
+
*/
|
|
30
|
+
declare function measureCount(xml: string): number;
|
|
31
|
+
declare function injectSystemBreaks(xml: string, breakBeforeBarIndexes: readonly number[]): string;
|
|
32
|
+
/**
|
|
33
|
+
* Evenly-spread system-break positions for `totalMeasures` measures across
|
|
34
|
+
* `systems` systems: `[per, 2·per, …]` (each strictly `< totalMeasures`),
|
|
35
|
+
* with `per = Math.ceil(totalMeasures / systems)` — `Math.ceil` naturally
|
|
36
|
+
* front-loads any remainder into the EARLIER systems (e.g. 7 measures / 2
|
|
37
|
+
* systems -> per=4 -> systems of 4+3, never 3+4), which is what keeps a
|
|
38
|
+
* later system from being the short/orphaned one. `systems <= 1` (nothing to
|
|
39
|
+
* break between) returns `[]`.
|
|
40
|
+
*/
|
|
41
|
+
declare function balancedSystemBreaks(totalMeasures: number, systems: number): number[];
|
|
42
|
+
/**
|
|
43
|
+
* Balanced system-break positions for a target BARS-PER-LINE layout — the
|
|
44
|
+
* caller-facing sibling of `balancedSystemBreaks`, which takes a system
|
|
45
|
+
* COUNT. Rather than stepping `every` bars at a time from bar 0 (which
|
|
46
|
+
* strands a short remainder — a 5-bar excerpt at `every: 4` renders 4+1, a
|
|
47
|
+
* widow of our own making), this decides how many LINES the excerpt needs
|
|
48
|
+
* (`ceil(totalBars / every)`) and hands that count to
|
|
49
|
+
* `balancedSystemBreaks`. `(5, 4)` is `[3]` (3+2) instead of `[4]` (4+1);
|
|
50
|
+
* `(9, 4)` is `[3, 6]` (3+3+3) instead of `[4, 8]` (4+4+1).
|
|
51
|
+
*
|
|
52
|
+
* WIDOW BACK-OFF: a small `every` against a small `totalBars` can make the
|
|
53
|
+
* straight `ceil(totalBars / every)` line count land on a trailing line of
|
|
54
|
+
* exactly 1 bar — the same widow this function exists to remove, reappearing
|
|
55
|
+
* at a different total/every combination (`lines = 3` on 7 bars: `per = 3`
|
|
56
|
+
* fills two 3-bar lines and leaves 1 bar for the third). The line count is
|
|
57
|
+
* therefore backed down (never below 1) until the trailing line holds at
|
|
58
|
+
* least 2 bars — 7 bars lands on 2 lines (4+3) instead of 3 (3+3+1), which
|
|
59
|
+
* is the same "fewer, fuller lines" direction `every` already asks for.
|
|
60
|
+
* This never changes the result for any total/every whose straight `ceil`
|
|
61
|
+
* count already avoids a 1-bar trailing line.
|
|
62
|
+
*
|
|
63
|
+
* `every <= 0` (the "auto — no forced breaks" sentinel callers resolve to)
|
|
64
|
+
* or a non-positive/non-finite `totalBars` returns `[]`. Never throws.
|
|
65
|
+
*
|
|
66
|
+
* Ported from stave-web-sightread (`src/lib/bach/xmlTransforms.ts`), whose
|
|
67
|
+
* copy this replaces — see the CONTRACT DUPLICATION NOTICE above. Reached by
|
|
68
|
+
* consumers through `VerovioLayoutOptions.barsPerLine` rather than called
|
|
69
|
+
* directly, so the player owns both the plan and its interaction with the
|
|
70
|
+
* widow pass and the readability floor.
|
|
71
|
+
*/
|
|
72
|
+
declare function barsPerLineBreaks(totalBars: number, every: number): number[];
|
|
73
|
+
/**
|
|
74
|
+
* Like `barsPerLineBreaks`, but each SECTION's span is balanced on its own
|
|
75
|
+
* instead of one balance running across the whole excerpt — so a line never
|
|
76
|
+
* ends part-way into a neighbouring section's opening bars, and a section's
|
|
77
|
+
* own bars are never split into a widowed last line either.
|
|
78
|
+
*
|
|
79
|
+
* `sectionStarts` are 0-BASED POSITIONS into the excerpt (the same
|
|
80
|
+
* coordinate `injectSystemBreaks` consumes), NOT MusicXML `<measure
|
|
81
|
+
* number>` attributes — scores skip bar numbers, so a caller holding
|
|
82
|
+
* bar-numbered section labels must convert first. `0` (the excerpt's own
|
|
83
|
+
* start) is never a break and is ignored if present.
|
|
84
|
+
*
|
|
85
|
+
* Every section start > 0 is a HARD break in its own right — a new section
|
|
86
|
+
* always begins its own line, not subject to balancing — unioned with
|
|
87
|
+
* `barsPerLineBreaks`' balanced positions computed WITHIN each section's
|
|
88
|
+
* span (its start up to the next section's start, or `totalBars` for the
|
|
89
|
+
* last) and offset back into excerpt-absolute positions.
|
|
90
|
+
*
|
|
91
|
+
* `every <= 0` suppresses the balanced component, matching
|
|
92
|
+
* `barsPerLineBreaks`, but section-start breaks still apply — that is the
|
|
93
|
+
* "sections only, otherwise let the engraver decide" mode. Out-of-range and
|
|
94
|
+
* non-integer starts are ignored. The result is sorted, de-duplicated, and
|
|
95
|
+
* every entry is a valid `injectSystemBreaks` position (integer, `> 0`,
|
|
96
|
+
* `< totalBars`). Never throws.
|
|
97
|
+
*/
|
|
98
|
+
declare function sectionAwareBreaks(totalBars: number, sectionStarts: readonly number[], every: number): number[];
|
|
99
|
+
/** One `<note>`'s MODEL identity — the ground truth `EngravedNote`
|
|
100
|
+
* (notationPlayerSvg.ts) needs that Verovio's rendered SVG cannot supply on
|
|
101
|
+
* its own (a rendered `g.note`/`g.rest` carries geometry, not pitch/tie/
|
|
102
|
+
* duration semantics). Keyed by the note's stamped `id` in
|
|
103
|
+
* `notePositions()`'s return of `verovioEngravedNotes`
|
|
104
|
+
* (notationPlayerVerovio.ts). */
|
|
105
|
+
interface NoteModel {
|
|
106
|
+
/** `12*(octave+1) + stepSemitone + alter` — the standard MusicXML→MIDI
|
|
107
|
+
* conversion (the same formula stave-web-sightread's own
|
|
108
|
+
* `practice/probeInputs.ts:pitchMidi` uses — a universal formula, not app
|
|
109
|
+
* logic, so this is not an owned-layer violation to restate here). `null`
|
|
110
|
+
* for a rest or an `<unpitched>` note (no `<pitch>` child) — mirrors
|
|
111
|
+
* `EngravedNote.midi`'s own "null covers both" contract. */
|
|
112
|
+
midi: number | null;
|
|
113
|
+
/** Has a `<rest/>` child. */
|
|
114
|
+
isRest: boolean;
|
|
115
|
+
/** True for the STOP half of a tie — a direct `<tie type="stop">` child OR
|
|
116
|
+
* `<notations><tied type="stop">` (exporters vary on which they emit;
|
|
117
|
+
* either counts) — matches `EngravedNote.tieContinuation`'s "continuation
|
|
118
|
+
* note of a tie, not the struck start" contract. */
|
|
119
|
+
tieContinuation: boolean;
|
|
120
|
+
/** 0-based, matching `EngravedNote.staffIndex`'s "0 = top staff of the
|
|
121
|
+
* system" contract: every `<part>` is walked in document order, and
|
|
122
|
+
* every DISTINCT staff within it (by `<attributes><staves>` when
|
|
123
|
+
* present, else the highest `<staff>` number any of its notes uses, else
|
|
124
|
+
* 1) is assigned the next index — so a single-part 2-staff piano score
|
|
125
|
+
* numbers 0/1 by `<staff>`, and a 2-part 1-staff-per-part reduction (this
|
|
126
|
+
* app's own chord+bass shape) numbers 0/1 by PART, with neither case
|
|
127
|
+
* needing different code. */
|
|
128
|
+
staffIndex: number;
|
|
129
|
+
/** Whole-note fraction (0.25 = quarter) — `duration / divisions / 4`,
|
|
130
|
+
* divisions tracked per-part from the LAST `<attributes><divisions>`
|
|
131
|
+
* seen at or before this note (MusicXML: divisions persist until
|
|
132
|
+
* overridden, default 1). 0 for a grace note (no `<duration>` child —
|
|
133
|
+
* the true, spec-correct signal; never guessed from `<type>`). */
|
|
134
|
+
durationReal: number;
|
|
135
|
+
/** 0-based position of this note's `<measure>` among its OWN `<part>`'s
|
|
136
|
+
* measure children, in document order. Informational only — the live
|
|
137
|
+
* join (`verovioOnsetColumns`/`verovioEngravedNotes`, both in
|
|
138
|
+
* notationPlayerVerovio.ts) resolves the note's RENDERED measure index
|
|
139
|
+
* from the live SVG DOM independently (Verovio's own render order, which
|
|
140
|
+
* is what geometry/hit-testing must agree with), never from this field. */
|
|
141
|
+
measureIndex: number;
|
|
142
|
+
/** `print-object="no"` on the source `<note>` (stave's `hideDoubledNotes`
|
|
143
|
+
* sets this on editorially-doubled notes/rests before handing MusicXML to
|
|
144
|
+
* this player). Verovio's importer HONORS this for `<note>` elements
|
|
145
|
+
* carrying a `<pitch>` (renders `visibility="hidden"` on its own,
|
|
146
|
+
* empirically confirmed against a real 6.2.0 render) but does NOT honor
|
|
147
|
+
* it for `<rest>` notes (the `<g class="rest">` renders fully visible
|
|
148
|
+
* regardless — same empirical check). `createVerovioNotationPlayer`
|
|
149
|
+
* reads this field to force `visibility="hidden"` after every render for
|
|
150
|
+
* ANY id where it's true — a no-op re-application on notes Verovio
|
|
151
|
+
* already hid, and the actual fix on the rests it doesn't (see that
|
|
152
|
+
* module's `applyPrintObjectHiding`). */
|
|
153
|
+
hidden: boolean;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Pure(ish) — MusicXML-in, `id → NoteModel`-out. Walks every `<part>` in
|
|
157
|
+
* document order, then every `<measure>` in document order, then every
|
|
158
|
+
* DIRECT child in document order — `<attributes>` updates the part's own
|
|
159
|
+
* `divisions` cursor; every other non-`<note>` child (`<backup>`,
|
|
160
|
+
* `<forward>`, `<direction>`, …) is a structural/timeline element this
|
|
161
|
+
* function has NO use for (it reads each note's OWN `<duration>` directly,
|
|
162
|
+
* never a cursor POSITION — see `durationReal`'s doc — so unlike
|
|
163
|
+
* `walkMeasureNotes`-style position walks, `<backup>`/`<forward>` need no
|
|
164
|
+
* special handling here beyond being correctly skipped, which plain
|
|
165
|
+
* tag-name filtering already does) — and every `<note>` becomes one model
|
|
166
|
+
* entry, keyed by its `id` attribute (a note with NO `id` — i.e. input that
|
|
167
|
+
* was never run through `stampNoteIds` — is silently skipped: it has no key
|
|
168
|
+
* to join the render against, so there is nothing useful to record).
|
|
169
|
+
*
|
|
170
|
+
* Never throws: malformed input (fails to parse, or no `<score-partwise>`
|
|
171
|
+
* root) returns an empty Map.
|
|
172
|
+
*/
|
|
173
|
+
declare function noteModelFromXml(stampedXml: string): Map<string, NoteModel>;
|
|
174
|
+
|
|
175
|
+
export { type NoteModel, balancedSystemBreaks, barsPerLineBreaks, injectSystemBreaks, measureCount, noteModelFromXml, sectionAwareBreaks, stampNoteIds };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
balancedSystemBreaks,
|
|
3
|
+
barsPerLineBreaks,
|
|
4
|
+
injectSystemBreaks,
|
|
5
|
+
measureCount,
|
|
6
|
+
noteModelFromXml,
|
|
7
|
+
sectionAwareBreaks,
|
|
8
|
+
stampNoteIds
|
|
9
|
+
} from "./chunk-JCDNEJBT.js";
|
|
10
|
+
export {
|
|
11
|
+
balancedSystemBreaks,
|
|
12
|
+
barsPerLineBreaks,
|
|
13
|
+
injectSystemBreaks,
|
|
14
|
+
measureCount,
|
|
15
|
+
noteModelFromXml,
|
|
16
|
+
sectionAwareBreaks,
|
|
17
|
+
stampNoteIds
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=notationXml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-music-packages/web-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "Shared music-theory + audio primitives for the music-suite web apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"types": "./dist/ornaments.d.ts",
|
|
40
40
|
"import": "./dist/ornaments.js"
|
|
41
41
|
},
|
|
42
|
+
"./notationXml": {
|
|
43
|
+
"types": "./dist/notationXml.d.ts",
|
|
44
|
+
"import": "./dist/notationXml.js"
|
|
45
|
+
},
|
|
42
46
|
"./notationBeams": {
|
|
43
47
|
"types": "./dist/notationBeams.d.ts",
|
|
44
48
|
"import": "./dist/notationBeams.js"
|