@sudobility/music_drawing 0.0.1
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/canvas-renderer.d.ts +180 -0
- package/dist/canvas-renderer.d.ts.map +1 -0
- package/dist/canvas-renderer.js +831 -0
- package/dist/canvas-renderer.js.map +1 -0
- package/dist/convert.d.ts +122 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +426 -0
- package/dist/convert.js.map +1 -0
- package/dist/display-timing.d.ts +68 -0
- package/dist/display-timing.d.ts.map +1 -0
- package/dist/display-timing.js +192 -0
- package/dist/display-timing.js.map +1 -0
- package/dist/icon-canvas.d.ts +12 -0
- package/dist/icon-canvas.d.ts.map +1 -0
- package/dist/icon-canvas.js +62 -0
- package/dist/icon-canvas.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +159 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +362 -0
- package/dist/layout.js.map +1 -0
- package/dist/measure-content.d.ts +114 -0
- package/dist/measure-content.d.ts.map +1 -0
- package/dist/measure-content.js +553 -0
- package/dist/measure-content.js.map +1 -0
- package/dist/note-color.d.ts +48 -0
- package/dist/note-color.d.ts.map +1 -0
- package/dist/note-color.js +81 -0
- package/dist/note-color.js.map +1 -0
- package/dist/pagination.d.ts +57 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +152 -0
- package/dist/pagination.js.map +1 -0
- package/dist/percussion.d.ts +47 -0
- package/dist/percussion.d.ts.map +1 -0
- package/dist/percussion.js +172 -0
- package/dist/percussion.js.map +1 -0
- package/dist/playhead.d.ts +37 -0
- package/dist/playhead.d.ts.map +1 -0
- package/dist/playhead.js +89 -0
- package/dist/playhead.js.map +1 -0
- package/dist/test/canvas-stub.d.ts +21 -0
- package/dist/test/canvas-stub.d.ts.map +1 -0
- package/dist/test/canvas-stub.js +51 -0
- package/dist/test/canvas-stub.js.map +1 -0
- package/dist/test/fixtures.d.ts +47 -0
- package/dist/test/fixtures.d.ts.map +1 -0
- package/dist/test/fixtures.js +433 -0
- package/dist/test/fixtures.js.map +1 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared VexFlow measure-content builders (spec §7, §26): turn one domain
|
|
3
|
+
* measure into its `Stave`/`Voice`s/`Beam`s, and a track's accumulated
|
|
4
|
+
* note channels into `StaveTie`s. Consumed by `canvas-renderer.ts` per
|
|
5
|
+
* drawn system; kept renderer-agnostic (nothing here draws or touches a
|
|
6
|
+
* context/backend).
|
|
7
|
+
*
|
|
8
|
+
* Pure adapter: no store/React imports (spec §3, §37).
|
|
9
|
+
*/
|
|
10
|
+
import { Accidental, Beam, MultiMeasureRest, Stave, StaveModifierPosition, Barline, Curve, Modifier, Repetition, StaveHairpin, StaveLine, TextBracket, StaveTie, Tuplet, Volta, Stem, TextJustification, Voice, } from 'vexflow';
|
|
11
|
+
import { isNoteEvent } from '@sudobility/music_types';
|
|
12
|
+
import { clefChangesAt, effectiveClef } from '@sudobility/music_types';
|
|
13
|
+
import { buildVoiceContent, keySignatureToVexSpec, pitchToVexKey, } from './convert.js';
|
|
14
|
+
import { displayGroups, drumDisplayGroups } from './display-timing.js';
|
|
15
|
+
import { tupletGroups } from '@sudobility/music_types';
|
|
16
|
+
import { isFootDrum } from './percussion.js';
|
|
17
|
+
import { pitchToMidi } from '@sudobility/music_types';
|
|
18
|
+
/**
|
|
19
|
+
* Glyph scale for cue notes. VexFlow's default is 38; grace notes use a
|
|
20
|
+
* comparable reduction. Small enough to read as "not yours" at a glance,
|
|
21
|
+
* large enough to read at all.
|
|
22
|
+
*/
|
|
23
|
+
export const CUE_GLYPH_SCALE = 26;
|
|
24
|
+
/** Cue labels are small italic by convention, and must not crowd the bar number. */
|
|
25
|
+
export const CUE_LABEL_FONT_SIZE = 9;
|
|
26
|
+
function sameTimeSignature(a, b) {
|
|
27
|
+
return a.numerator === b.numerator && a.denominator === b.denominator;
|
|
28
|
+
}
|
|
29
|
+
function sameKeySignature(a, b) {
|
|
30
|
+
return a.fifths === b.fifths && a.mode === b.mode;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Beams one voice's tickables — flat, and in the voice's own direction, when
|
|
34
|
+
* that voice is a drum voice.
|
|
35
|
+
*
|
|
36
|
+
* Beamed by pitch, as melodic notes are, a drum beam followed the gap between
|
|
37
|
+
* a kick and a hi-hat: it swept diagonally from above the staff down past the
|
|
38
|
+
* bottom of it. Drum notation answers that with flat beams, which is what the
|
|
39
|
+
* convention exists for. Spacers are passed through rather than filtered out,
|
|
40
|
+
* so a beam correctly stops where the voice stops playing.
|
|
41
|
+
*/
|
|
42
|
+
function beamsFor(tickables, stemDirection) {
|
|
43
|
+
if (stemDirection === undefined)
|
|
44
|
+
return Beam.generateBeams(tickables);
|
|
45
|
+
return Beam.generateBeams(tickables, {
|
|
46
|
+
flat_beams: true,
|
|
47
|
+
stem_direction: stemDirection,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Splits a drum voice into what the player's hands do and what their feet do.
|
|
52
|
+
*
|
|
53
|
+
* Feet are the bass drum and the hi-hat pedal; everything else is hands. The
|
|
54
|
+
* split exists so the two can be stemmed in opposite directions — the reason
|
|
55
|
+
* drum charts are readable at a glance.
|
|
56
|
+
*/
|
|
57
|
+
function splitHandsAndFeet(events) {
|
|
58
|
+
const hands = [];
|
|
59
|
+
const feet = [];
|
|
60
|
+
for (const event of events) {
|
|
61
|
+
// Rests belong to the hands: a bar of silence should show one rest, not two.
|
|
62
|
+
if (isNoteEvent(event) && isFootDrum(pitchToMidi(event.pitch)))
|
|
63
|
+
feet.push(event);
|
|
64
|
+
else
|
|
65
|
+
hands.push(event);
|
|
66
|
+
}
|
|
67
|
+
return { hands, feet };
|
|
68
|
+
}
|
|
69
|
+
/** Whether two bars belong to the same volta — the same numbers, in order. */
|
|
70
|
+
function sameEnding(a, b) {
|
|
71
|
+
const left = a?.endingNumbers;
|
|
72
|
+
const right = b?.endingNumbers;
|
|
73
|
+
if (!left || !right)
|
|
74
|
+
return false;
|
|
75
|
+
return left.length === right.length && left.every((n, i) => n === right[i]);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Domain jump -> the `Repetition` VexFlow draws for it.
|
|
79
|
+
*
|
|
80
|
+
* A straight mapping: the two libraries name these the same way, unlike the
|
|
81
|
+
* mordents, because the vocabulary is fixed Italian rather than a description.
|
|
82
|
+
*/
|
|
83
|
+
const JUMP_REPETITION = {
|
|
84
|
+
'da-capo': Repetition.type.DC,
|
|
85
|
+
'da-capo-al-fine': Repetition.type.DC_AL_FINE,
|
|
86
|
+
'da-capo-al-coda': Repetition.type.DC_AL_CODA,
|
|
87
|
+
'dal-segno': Repetition.type.DS,
|
|
88
|
+
'dal-segno-al-fine': Repetition.type.DS_AL_FINE,
|
|
89
|
+
'dal-segno-al-coda': Repetition.type.DS_AL_CODA,
|
|
90
|
+
};
|
|
91
|
+
/** Builds one measure's `Stave`, its VexFlow `Voice`s, and its beams; records notes into `channels` for tie building. */
|
|
92
|
+
export function buildMeasureContent(measure, track,
|
|
93
|
+
/**
|
|
94
|
+
* This measure's position in `track.measures`.
|
|
95
|
+
*
|
|
96
|
+
* Passed in rather than read off `measure.index`: a print-only part collapses
|
|
97
|
+
* runs of rests, which shortens the array while every surviving measure keeps
|
|
98
|
+
* its original `index`, so the two stop agreeing. The caller iterates the
|
|
99
|
+
* array and knows the truth.
|
|
100
|
+
*/
|
|
101
|
+
measureIndex, placement, prevMeasure,
|
|
102
|
+
/** The bar after this one, for deriving where a volta bracket ends. */
|
|
103
|
+
nextMeasure, ppq, channels, allMetas) {
|
|
104
|
+
const { box, isFirstInSystem } = placement;
|
|
105
|
+
// A percussion track's notes name drums, not pitches, which changes both
|
|
106
|
+
// where they sit and whether an accidental could ever apply to them.
|
|
107
|
+
// The clef *in force here*, which is not `track.clef` once a part changes
|
|
108
|
+
// clef mid-piece — a piano left hand crossing into treble is the usual case.
|
|
109
|
+
const clef = effectiveClef(track, measureIndex);
|
|
110
|
+
const isPercussion = clef === 'percussion';
|
|
111
|
+
const stave = new Stave(box.x, box.y, box.width);
|
|
112
|
+
stave.setAttribute('id', measure.id);
|
|
113
|
+
// At a system start the clef is re-stated because every system states it;
|
|
114
|
+
// mid-system it is drawn only where it actually changes — exactly the rule
|
|
115
|
+
// the key signature below already follows.
|
|
116
|
+
if (isFirstInSystem || clefChangesAt(track, measureIndex)) {
|
|
117
|
+
stave.addClef(clef);
|
|
118
|
+
}
|
|
119
|
+
// A drum staff has no key: its lines are instruments, not pitches, so a
|
|
120
|
+
// signature there is meaningless — and the sharps were being drawn against
|
|
121
|
+
// notes no accidental can apply to.
|
|
122
|
+
if (!isPercussion &&
|
|
123
|
+
(isFirstInSystem ||
|
|
124
|
+
!prevMeasure ||
|
|
125
|
+
!sameKeySignature(prevMeasure.keySignature, measure.keySignature))) {
|
|
126
|
+
stave.addKeySignature(keySignatureToVexSpec(measure.keySignature));
|
|
127
|
+
}
|
|
128
|
+
if (!prevMeasure ||
|
|
129
|
+
!sameTimeSignature(prevMeasure.timeSignature, measure.timeSignature)) {
|
|
130
|
+
stave.addTimeSignature(`${measure.timeSignature.numerator}/${measure.timeSignature.denominator}`);
|
|
131
|
+
}
|
|
132
|
+
/*
|
|
133
|
+
Repeat barlines. Stave-level, not note-level: they are the walls of the
|
|
134
|
+
bar, so they belong here rather than anywhere in the note path.
|
|
135
|
+
|
|
136
|
+
Set on both ends independently — a `:|` with no matching `|:` repeats from
|
|
137
|
+
the start of the piece, which is a real marking rather than an error.
|
|
138
|
+
*/
|
|
139
|
+
if (measure.repeatStart)
|
|
140
|
+
stave.setBegBarType(Barline.type.REPEAT_BEGIN);
|
|
141
|
+
if (measure.repeatEnd)
|
|
142
|
+
stave.setEndBarType(Barline.type.REPEAT_END);
|
|
143
|
+
/*
|
|
144
|
+
A section break or the end of the piece.
|
|
145
|
+
|
|
146
|
+
Applied *after* the repeat above and only when there is no repeat end here,
|
|
147
|
+
so a bar that both closes a repeat and ends a section keeps the `:|` — the
|
|
148
|
+
repeat is the instruction a player acts on, and a double bar drawn over it
|
|
149
|
+
would silently remove a repeat from the performance.
|
|
150
|
+
*/
|
|
151
|
+
if (measure.barline && !measure.repeatEnd) {
|
|
152
|
+
stave.setEndBarType(measure.barline === 'final' ? Barline.type.END : Barline.type.DOUBLE);
|
|
153
|
+
}
|
|
154
|
+
/*
|
|
155
|
+
The navigation marks: the sign, the coda, `Fine`, `To Coda` and the jump
|
|
156
|
+
instruction itself.
|
|
157
|
+
|
|
158
|
+
Drawn with VexFlow's `Repetition`, which places each above the stave at
|
|
159
|
+
the side an engraver puts it — the segno and coda open a section so they
|
|
160
|
+
sit on the left, while an instruction obeyed at the end of a bar sits on
|
|
161
|
+
the right. Every one is independent: a bar can carry the coda sign and a
|
|
162
|
+
`Fine`, and one can carry `To Coda` and nothing else.
|
|
163
|
+
*/
|
|
164
|
+
if (measure.segno)
|
|
165
|
+
stave.addModifier(new Repetition(Repetition.type.SEGNO_LEFT, 0, 0));
|
|
166
|
+
if (measure.coda)
|
|
167
|
+
stave.addModifier(new Repetition(Repetition.type.CODA_LEFT, 0, 0));
|
|
168
|
+
if (measure.toCoda)
|
|
169
|
+
stave.addModifier(new Repetition(Repetition.type.TO_CODA, 0, 0));
|
|
170
|
+
if (measure.fine)
|
|
171
|
+
stave.addModifier(new Repetition(Repetition.type.FINE, 0, 0));
|
|
172
|
+
if (measure.jump)
|
|
173
|
+
stave.addModifier(new Repetition(JUMP_REPETITION[measure.jump], 0, 0));
|
|
174
|
+
/*
|
|
175
|
+
The volta bracket, and which pass it covers.
|
|
176
|
+
|
|
177
|
+
Derived from the neighbours rather than stored as a span: a bar opens the
|
|
178
|
+
bracket when the bar before it is not part of the same ending, and closes
|
|
179
|
+
it when the bar after is not. Deleting a bar out of a first ending then
|
|
180
|
+
shortens the bracket instead of leaving one that points at nothing — the
|
|
181
|
+
same reason a tuplet is derived from its durations.
|
|
182
|
+
*/
|
|
183
|
+
if (measure.endingNumbers?.length) {
|
|
184
|
+
const label = `${measure.endingNumbers.join(', ')}.`;
|
|
185
|
+
const opens = !sameEnding(prevMeasure, measure);
|
|
186
|
+
const closes = !sameEnding(nextMeasure, measure);
|
|
187
|
+
const type = opens
|
|
188
|
+
? closes
|
|
189
|
+
? Volta.type.BEGIN_END
|
|
190
|
+
: Volta.type.BEGIN
|
|
191
|
+
: closes
|
|
192
|
+
? Volta.type.END
|
|
193
|
+
: Volta.type.MID;
|
|
194
|
+
// Only the opening bar prints the number; a continuation would repeat it
|
|
195
|
+
// under its own bracket segment.
|
|
196
|
+
stave.setVoltaType(type, opens ? label : '', 0);
|
|
197
|
+
}
|
|
198
|
+
// Boxed, because a bare letter beside a dynamic or a tempo marking is easy
|
|
199
|
+
// to miss — and a rehearsal mark that is missed has failed at its one job.
|
|
200
|
+
// Set before the multi-measure-rest return, so a marked rest still shows its
|
|
201
|
+
// letter: that bar is exactly the one a lost player is hunting for.
|
|
202
|
+
if (measure.rehearsalMark !== undefined) {
|
|
203
|
+
stave.setSection(measure.rehearsalMark, 0, 0, 12, true);
|
|
204
|
+
}
|
|
205
|
+
// A collapsed measure draws one wide rest and a numeral instead of notes.
|
|
206
|
+
// Returned rather than drawn here so the renderer keeps sole responsibility
|
|
207
|
+
// for putting ink on a context.
|
|
208
|
+
if (measure.multiMeasureRestCount !== undefined) {
|
|
209
|
+
return {
|
|
210
|
+
stave,
|
|
211
|
+
voices: [],
|
|
212
|
+
beams: [],
|
|
213
|
+
tuplets: [],
|
|
214
|
+
// The count goes in twice: VexFlow takes it positionally and also
|
|
215
|
+
// requires it in the options, which `Factory.MultiMeasureRest` fills in
|
|
216
|
+
// for you. Constructing directly means supplying both.
|
|
217
|
+
multiMeasureRest: new MultiMeasureRest(measure.multiMeasureRestCount, {
|
|
218
|
+
number_of_measures: measure.multiMeasureRestCount,
|
|
219
|
+
show_number: true,
|
|
220
|
+
}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
// A cue bar draws the cue and nothing else. The player's own whole-bar rest
|
|
224
|
+
// would collide with it — the canvas layout has no collision avoidance — and
|
|
225
|
+
// small notes under an instrument name already read as "not yours".
|
|
226
|
+
//
|
|
227
|
+
// Deliberately not recorded into `channels` or `allMetas`: a cue note is not
|
|
228
|
+
// the player's note, so it must never tie to one or answer a hit test.
|
|
229
|
+
if (measure.cue !== undefined) {
|
|
230
|
+
stave.setText(measure.cue.label, StaveModifierPosition.ABOVE, {
|
|
231
|
+
justification: TextJustification.LEFT,
|
|
232
|
+
});
|
|
233
|
+
// Small italic, the engraving convention for a cue label — and small
|
|
234
|
+
// enough not to crowd the measure number sitting just above it.
|
|
235
|
+
// `setText` returns the stave, not the modifier, so the StaveText has to
|
|
236
|
+
// be fetched back off the stave to restyle it.
|
|
237
|
+
const staveTexts = stave
|
|
238
|
+
.getModifiers()
|
|
239
|
+
.filter(m => m.getCategory() === 'StaveText');
|
|
240
|
+
staveTexts[staveTexts.length - 1]?.setFont({
|
|
241
|
+
family: 'serif',
|
|
242
|
+
size: CUE_LABEL_FONT_SIZE,
|
|
243
|
+
weight: 'normal',
|
|
244
|
+
style: 'italic',
|
|
245
|
+
});
|
|
246
|
+
const { tickables, notes } = buildVoiceContent(displayGroups(measure.cue.events, measure.startTick, measure.durationTicks, ppq), ppq, CUE_GLYPH_SCALE, isPercussion);
|
|
247
|
+
if (notes.length === 0)
|
|
248
|
+
return { stave, voices: [], beams: [], tuplets: [] };
|
|
249
|
+
const cueVoice = new Voice({
|
|
250
|
+
num_beats: measure.timeSignature.numerator,
|
|
251
|
+
beat_value: measure.timeSignature.denominator,
|
|
252
|
+
});
|
|
253
|
+
cueVoice.setMode(Voice.Mode.SOFT);
|
|
254
|
+
cueVoice.addTickables(tickables);
|
|
255
|
+
if (!isPercussion)
|
|
256
|
+
Accidental.applyAccidentals([cueVoice], keySignatureToVexSpec(measure.keySignature));
|
|
257
|
+
return {
|
|
258
|
+
stave,
|
|
259
|
+
voices: [cueVoice],
|
|
260
|
+
beams: beamsFor(tickables, isPercussion ? Stem.UP : undefined),
|
|
261
|
+
// A cue is a reminder of another player's line, printed small; it never
|
|
262
|
+
// carries a bracket of its own.
|
|
263
|
+
tuplets: [],
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const voices = [];
|
|
267
|
+
const beams = [];
|
|
268
|
+
const tuplets = [];
|
|
269
|
+
/** Builds one VexFlow voice, recording its notes under `voiceOrdinal` for ties. */
|
|
270
|
+
const addVoice = (events, voiceOrdinal, stemDirection) => {
|
|
271
|
+
const { tickables, notes, metas } = buildVoiceContent(isPercussion
|
|
272
|
+
? drumDisplayGroups(events, measure.startTick, measure.durationTicks, ppq)
|
|
273
|
+
: displayGroups(events, measure.startTick, measure.durationTicks, ppq), ppq, undefined, isPercussion);
|
|
274
|
+
if (notes.length === 0)
|
|
275
|
+
return;
|
|
276
|
+
if (stemDirection !== undefined)
|
|
277
|
+
for (const note of notes)
|
|
278
|
+
note.setStemDirection(stemDirection);
|
|
279
|
+
const vexVoice = new Voice({
|
|
280
|
+
num_beats: measure.timeSignature.numerator,
|
|
281
|
+
beat_value: measure.timeSignature.denominator,
|
|
282
|
+
});
|
|
283
|
+
// SOFT mode: don't throw if a voice's summed ticks don't exactly match
|
|
284
|
+
// the time signature (e.g. the decomposeDuration nearest-duration
|
|
285
|
+
// fallback for a non-standard remainder can be off by a few ticks).
|
|
286
|
+
vexVoice.setMode(Voice.Mode.SOFT);
|
|
287
|
+
vexVoice.addTickables(tickables);
|
|
288
|
+
voices.push(vexVoice);
|
|
289
|
+
beams.push(...beamsFor(tickables, stemDirection));
|
|
290
|
+
/*
|
|
291
|
+
Triplet brackets, derived from the durations rather than stored: three
|
|
292
|
+
notes each two thirds of a plain value *are* a triplet, and `tupletGroups`
|
|
293
|
+
reads exactly that. Built from `events` and applied to `notes` by index —
|
|
294
|
+
the two are parallel only while nothing decomposes, so a group whose
|
|
295
|
+
notes cannot be found is skipped rather than bracketing the wrong ones.
|
|
296
|
+
*/
|
|
297
|
+
for (const group of tupletGroups(events, ppq)) {
|
|
298
|
+
const members = notes.slice(group.start, group.start + group.length);
|
|
299
|
+
if (members.length !== group.length)
|
|
300
|
+
continue;
|
|
301
|
+
const tuplet = new Tuplet(members, {
|
|
302
|
+
num_notes: group.actualNotes,
|
|
303
|
+
notes_occupied: group.normalNotes,
|
|
304
|
+
});
|
|
305
|
+
if (stemDirection !== undefined)
|
|
306
|
+
tuplet.setTupletLocation(stemDirection);
|
|
307
|
+
tuplets.push(tuplet);
|
|
308
|
+
}
|
|
309
|
+
const channel = channels.get(voiceOrdinal) ?? [];
|
|
310
|
+
notes.forEach((note, i) => channel.push({ note, meta: metas[i] }));
|
|
311
|
+
channels.set(voiceOrdinal, channel);
|
|
312
|
+
allMetas.push(...metas);
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Whether this stave carries more than one voice with anything in it.
|
|
316
|
+
*
|
|
317
|
+
* A single voice keeps VexFlow's automatic stem direction, which is decided
|
|
318
|
+
* per note from its position on the stave and is what an engraver would do.
|
|
319
|
+
* Fixing it up on a one-voice stave would point half the notes the wrong way
|
|
320
|
+
* for no benefit.
|
|
321
|
+
*/
|
|
322
|
+
const soundingVoices = measure.voices.filter(v => v.events.length > 0).length;
|
|
323
|
+
measure.voices.forEach((domainVoice, voiceOrdinal) => {
|
|
324
|
+
if (!isPercussion) {
|
|
325
|
+
// Two voices sharing a stave are told apart by their stems: the upper
|
|
326
|
+
// voice up, the lower voice down. Without this they are drawn
|
|
327
|
+
// identically and there is no way to see which line is being edited —
|
|
328
|
+
// which is the whole reason a second voice exists.
|
|
329
|
+
const stemDirection = soundingVoices > 1
|
|
330
|
+
? voiceOrdinal === 0
|
|
331
|
+
? Stem.UP
|
|
332
|
+
: Stem.DOWN
|
|
333
|
+
: undefined;
|
|
334
|
+
addVoice(domainVoice.events, voiceOrdinal, stemDirection);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
// A drum staff carries two voices, not one: hands stemmed up, feet stemmed
|
|
338
|
+
// down. Struck together in a single voice they became one chord, so a kick
|
|
339
|
+
// in the bottom space and a hi-hat above the top line shared one stem
|
|
340
|
+
// running the height of the staff. Splitting them is what every drum chart
|
|
341
|
+
// does, and it is the only way the two can point in opposite directions.
|
|
342
|
+
const { hands, feet } = splitHandsAndFeet(domainVoice.events);
|
|
343
|
+
addVoice(hands, voiceOrdinal, Stem.UP);
|
|
344
|
+
addVoice(feet, voiceOrdinal, Stem.DOWN);
|
|
345
|
+
});
|
|
346
|
+
// Accidental *glyphs* are decided here, once per measure, across every
|
|
347
|
+
// voice on this stave — never unconditionally per note (see convert.ts):
|
|
348
|
+
// VexFlow's own key-signature-and-context-aware logic decides whether a
|
|
349
|
+
// sharp/flat/natural is actually needed (e.g. no redundant accidental on
|
|
350
|
+
// an in-key F# in G major), reading each note's spelling straight out of
|
|
351
|
+
// the `keys` string `convert.ts` already built.
|
|
352
|
+
if (voices.length > 0) {
|
|
353
|
+
if (!isPercussion)
|
|
354
|
+
Accidental.applyAccidentals(voices, keySignatureToVexSpec(measure.keySignature));
|
|
355
|
+
}
|
|
356
|
+
return { stave, voices, beams, tuplets };
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Ties every adjacent pair of notes in a voice-ordinal channel whose keys
|
|
360
|
+
* actually match by pitch — covers both cross-barline ties and same-measure
|
|
361
|
+
* duration-decomposition ties with one mechanism. Matches by pitch, never
|
|
362
|
+
* by array index/adjacency (a chord where only one of several members ties
|
|
363
|
+
* forward must produce exactly one `StaveTie`, on the correct key indices
|
|
364
|
+
* on each side — see `KeyTie`'s doc in convert.ts and
|
|
365
|
+
* `domain/score/ties.ts`'s `findForwardPartner`/`findBackwardPartner`,
|
|
366
|
+
* which requires the same tick/pitch/tie-flag matching for the same
|
|
367
|
+
* reason: a same-tick note sharing a channel by coincidence must not be
|
|
368
|
+
* spliced in).
|
|
369
|
+
*/
|
|
370
|
+
export function buildTies(channel,
|
|
371
|
+
/**
|
|
372
|
+
* Whether a note was actually drawn this frame.
|
|
373
|
+
*
|
|
374
|
+
* A tie is positioned from its two notes' Y values, which only exist once
|
|
375
|
+
* the note has been drawn — so a tie to a note on a culled (off-screen)
|
|
376
|
+
* stave throws `NoYValues` inside VexFlow. Filtering here rather than
|
|
377
|
+
* catching there is what keeps one such pair from taking the rest of the
|
|
378
|
+
* channel's ties with it.
|
|
379
|
+
*/
|
|
380
|
+
isDrawn = () => true) {
|
|
381
|
+
const ties = [];
|
|
382
|
+
for (let i = 0; i < channel.length - 1; i += 1) {
|
|
383
|
+
const a = channel[i];
|
|
384
|
+
const b = channel[i + 1];
|
|
385
|
+
if (a.meta.isRest || b.meta.isRest || !a.meta.tieStart || !b.meta.tieStop)
|
|
386
|
+
continue;
|
|
387
|
+
if (!isDrawn(a.note) || !isDrawn(b.note))
|
|
388
|
+
continue;
|
|
389
|
+
const firstIndices = [];
|
|
390
|
+
const lastIndices = [];
|
|
391
|
+
const usedB = new Set();
|
|
392
|
+
a.meta.keyTies.forEach((aKey, aIndex) => {
|
|
393
|
+
if (!aKey.tieStart)
|
|
394
|
+
return;
|
|
395
|
+
const aSpelling = pitchToVexKey(aKey.pitch);
|
|
396
|
+
const bIndex = b.meta.keyTies.findIndex((bKey, idx) => !usedB.has(idx) &&
|
|
397
|
+
bKey.tieStop &&
|
|
398
|
+
pitchToVexKey(bKey.pitch) === aSpelling);
|
|
399
|
+
if (bIndex === -1)
|
|
400
|
+
return;
|
|
401
|
+
usedB.add(bIndex);
|
|
402
|
+
firstIndices.push(aIndex);
|
|
403
|
+
lastIndices.push(bIndex);
|
|
404
|
+
});
|
|
405
|
+
if (firstIndices.length === 0)
|
|
406
|
+
continue;
|
|
407
|
+
ties.push(new StaveTie({
|
|
408
|
+
first_note: a.note,
|
|
409
|
+
last_note: b.note,
|
|
410
|
+
first_indices: firstIndices,
|
|
411
|
+
last_indices: lastIndices,
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
return ties;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Draws each phrase mark in a channel as one curve, start note to stop note.
|
|
418
|
+
*
|
|
419
|
+
* Unlike `buildTies` this does not work on adjacent pairs: a slur spans a run
|
|
420
|
+
* of notes, and the pair to join is "the note that opened it" and "the next
|
|
421
|
+
* one that closes it". Matching is by order rather than by pitch for the same
|
|
422
|
+
* reason — a slur groups different pitches, which is what distinguishes it
|
|
423
|
+
* from a tie.
|
|
424
|
+
*
|
|
425
|
+
* `isDrawn` filters the same hazard ties have: a curve is positioned from its
|
|
426
|
+
* two notes' Y values, which only exist once they have been drawn, so one
|
|
427
|
+
* reaching a culled off-screen stave would throw inside VexFlow.
|
|
428
|
+
*/
|
|
429
|
+
/**
|
|
430
|
+
* Draws each hairpin in a channel as one wedge, opening note to closing note.
|
|
431
|
+
*
|
|
432
|
+
* `buildSlurs`' structure exactly, because a hairpin is the same kind of span:
|
|
433
|
+
* opened by one note, closed by a later one, matched in order rather than by
|
|
434
|
+
* pitch. It carries the same culling hazard too — a wedge is positioned from
|
|
435
|
+
* its two notes' Y values, which only exist once they have been drawn, so one
|
|
436
|
+
* reaching a culled off-screen stave throws inside VexFlow.
|
|
437
|
+
*
|
|
438
|
+
* Below the stave, where the dynamics already sit, so a level and the change
|
|
439
|
+
* between two levels read as one system rather than two.
|
|
440
|
+
*/
|
|
441
|
+
export function buildHairpins(channel, isDrawn = () => true) {
|
|
442
|
+
const hairpins = [];
|
|
443
|
+
let open = null;
|
|
444
|
+
for (const entry of channel) {
|
|
445
|
+
if (entry.meta.isRest)
|
|
446
|
+
continue;
|
|
447
|
+
if (!open && entry.meta.hairpinStart) {
|
|
448
|
+
open = { note: entry.note, kind: entry.meta.hairpinStart };
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (open && entry.meta.hairpinStop) {
|
|
452
|
+
if (isDrawn(open.note) && isDrawn(entry.note)) {
|
|
453
|
+
const wedge = new StaveHairpin({ first_note: open.note, last_note: entry.note }, open.kind === 'crescendo'
|
|
454
|
+
? StaveHairpin.type.CRESC
|
|
455
|
+
: StaveHairpin.type.DECRESC);
|
|
456
|
+
wedge.setPosition(Modifier.Position.BELOW);
|
|
457
|
+
hairpins.push(wedge);
|
|
458
|
+
}
|
|
459
|
+
open = null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// An unclosed opening draws nothing: half a wedge is not a wedge, and the
|
|
463
|
+
// command that writes these never produces one.
|
|
464
|
+
return hairpins;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* The octave brackets in a channel, one `TextBracket` per span.
|
|
468
|
+
*
|
|
469
|
+
* Above the stave for `8va`/`15ma` and below for the `b` forms, which is where
|
|
470
|
+
* the displacement points. Culled like the curves: a bracket is positioned
|
|
471
|
+
* from its two notes, so one reaching an off-screen stave throws.
|
|
472
|
+
*/
|
|
473
|
+
export function buildOttavas(channel, isDrawn = () => true) {
|
|
474
|
+
const brackets = [];
|
|
475
|
+
let open = null;
|
|
476
|
+
for (const entry of channel) {
|
|
477
|
+
if (entry.meta.isRest)
|
|
478
|
+
continue;
|
|
479
|
+
if (!open && entry.meta.ottavaStart) {
|
|
480
|
+
open = { note: entry.note, kind: entry.meta.ottavaStart };
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (open && entry.meta.ottavaStop) {
|
|
484
|
+
if (isDrawn(open.note) && isDrawn(entry.note)) {
|
|
485
|
+
const above = open.kind === '8va' || open.kind === '15ma';
|
|
486
|
+
brackets.push(new TextBracket({
|
|
487
|
+
start: open.note,
|
|
488
|
+
stop: entry.note,
|
|
489
|
+
text: open.kind === '8va' || open.kind === '8vb' ? '8' : '15',
|
|
490
|
+
superscript: open.kind.slice(1),
|
|
491
|
+
position: above
|
|
492
|
+
? TextBracket.Position.TOP
|
|
493
|
+
: TextBracket.Position.BOTTOM,
|
|
494
|
+
}));
|
|
495
|
+
}
|
|
496
|
+
open = null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return brackets;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* The slides in a channel, drawn as a straight line between the two notes.
|
|
503
|
+
*
|
|
504
|
+
* VexFlow has no `Glissando`; a `StaveLine` between the notes is what its own
|
|
505
|
+
* examples use, and it is what a glissando is on the page — a line from one
|
|
506
|
+
* notehead to the next.
|
|
507
|
+
*/
|
|
508
|
+
export function buildGlissandos(channel, isDrawn = () => true) {
|
|
509
|
+
const lines = [];
|
|
510
|
+
let open = null;
|
|
511
|
+
for (const entry of channel) {
|
|
512
|
+
if (entry.meta.isRest)
|
|
513
|
+
continue;
|
|
514
|
+
if (!open && entry.meta.glissandoStart) {
|
|
515
|
+
open = entry;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (open && entry.meta.glissandoStop) {
|
|
519
|
+
if (isDrawn(open.note) && isDrawn(entry.note)) {
|
|
520
|
+
lines.push(new StaveLine({
|
|
521
|
+
first_note: open.note,
|
|
522
|
+
last_note: entry.note,
|
|
523
|
+
first_indices: [0],
|
|
524
|
+
last_indices: [0],
|
|
525
|
+
}));
|
|
526
|
+
}
|
|
527
|
+
open = null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return lines;
|
|
531
|
+
}
|
|
532
|
+
export function buildSlurs(channel, isDrawn = () => true) {
|
|
533
|
+
const slurs = [];
|
|
534
|
+
let open = null;
|
|
535
|
+
for (const entry of channel) {
|
|
536
|
+
if (entry.meta.isRest)
|
|
537
|
+
continue;
|
|
538
|
+
if (!open && entry.meta.slurStart) {
|
|
539
|
+
open = entry;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (open && entry.meta.slurStop) {
|
|
543
|
+
if (isDrawn(open.note) && isDrawn(entry.note)) {
|
|
544
|
+
slurs.push(new Curve(open.note, entry.note, {}));
|
|
545
|
+
}
|
|
546
|
+
open = null;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// An unclosed start draws nothing: half a phrase mark is not a phrase mark,
|
|
550
|
+
// and the command that writes these never produces one.
|
|
551
|
+
return slurs;
|
|
552
|
+
}
|
|
553
|
+
//# sourceMappingURL=measure-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"measure-content.js","sourceRoot":"","sources":["../src/measure-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Note color-role resolution. Pure: no VexFlow, no store, no DOM — so the
|
|
3
|
+
* precedence rule is unit-testable on its own and the canvas renderer only
|
|
4
|
+
* has to call it.
|
|
5
|
+
*/
|
|
6
|
+
import type { NoteColorRole, RenderTheme } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* The role to draw a VexFlow note with, given every domain event id it
|
|
9
|
+
* represents (>1 for a chord, or for one segment of a duration-decomposed
|
|
10
|
+
* long note). Ids absent from `noteColors` count as `normal`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveNoteColorRole(eventIds: readonly string[], noteColors: ReadonlyMap<string, NoteColorRole> | undefined): NoteColorRole;
|
|
13
|
+
/**
|
|
14
|
+
* The non-color half of a note's state cue (spec §27: "do not rely on color
|
|
15
|
+
* alone"). The old highlight overlay carried this with solid/dashed/dotted
|
|
16
|
+
* stroke patterns; those went away with the rectangles, so the weight of the
|
|
17
|
+
* glyph carries it now — a thicker `lineWidth` on stems, flags and beams.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NOT a shadow. An earlier version added `shadowBlur` so the cue
|
|
20
|
+
* would reach a notehead too (a filled glyph, which no stroke width can
|
|
21
|
+
* thicken). Canvas shadows force the rasterizer down a separate blur pass per
|
|
22
|
+
* draw, and this is applied to precisely the notes that change most often —
|
|
23
|
+
* every note-on and note-off during playback, on the thread Tone.js schedules
|
|
24
|
+
* from. Playback hesitated. The cue is worth less than smooth audio.
|
|
25
|
+
*
|
|
26
|
+
* The cost of that trade: a *stemless* whole note now has color as its only
|
|
27
|
+
* cue. Every other note keeps the stroke-weight channel, and the selection
|
|
28
|
+
* summary in the status bar and screen-reader text still names the state.
|
|
29
|
+
*
|
|
30
|
+
* One emphasis level for every non-normal state rather than three: the
|
|
31
|
+
* perceptually important distinction is "is this note affected" versus "is it
|
|
32
|
+
* not". `selected` and `regenerated` are mutually exclusive anyway
|
|
33
|
+
* (`regenerated` is a property of the whole selection), and `playing` is
|
|
34
|
+
* transient and accompanied by the moving caret.
|
|
35
|
+
*/
|
|
36
|
+
export declare function noteEmphasisFor(role: NoteColorRole): {
|
|
37
|
+
lineWidth: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The theme color a role draws in.
|
|
41
|
+
*
|
|
42
|
+
* Only `normal` varies by track. A selected, regenerated or playing note keeps
|
|
43
|
+
* its state color wherever it is: those states are the reason to look at the
|
|
44
|
+
* note, and cmd-shift-click selects across every track, so dimming a selection
|
|
45
|
+
* because it is not on the active track would hide what the user just did.
|
|
46
|
+
*/
|
|
47
|
+
export declare function noteColorFor(role: NoteColorRole, theme: RenderTheme, onActiveTrack?: boolean): string;
|
|
48
|
+
//# sourceMappingURL=note-color.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note-color.d.ts","sourceRoot":"","sources":["../src/note-color.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAkB7D;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,GACzD,aAAa,CAUf;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAE1E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,UAAO,GACnB,MAAM,CAWR"}
|