vector-score 1.2.0 → 1.3.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/README.md +101 -51
- package/dist/classes/GuitarChord.d.ts +5 -3
- package/dist/classes/MusicStaff.d.ts +1 -2
- package/dist/classes/RhythmStaff.d.ts +1 -3
- package/dist/classes/SVGRenderer.d.ts +22 -8
- package/dist/classes/ScrollingStaff.d.ts +1 -2
- package/dist/constants.d.ts +6 -6
- package/dist/vector-score.js +481 -425
- package/dist/vector-score.umd.cjs +1 -1
- package/package.json +1 -1
package/dist/vector-score.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const w = {
|
|
2
2
|
treble: {
|
|
3
3
|
staffType: "treble",
|
|
4
4
|
paddingTop: 13,
|
|
@@ -31,16 +31,16 @@ const S = {
|
|
|
31
31
|
topLineYPos: 0,
|
|
32
32
|
bottomLineYPos: 110
|
|
33
33
|
}
|
|
34
|
-
},
|
|
34
|
+
}, I = {
|
|
35
35
|
w: 4,
|
|
36
36
|
h: 2,
|
|
37
37
|
q: 1,
|
|
38
38
|
e: 0.5,
|
|
39
39
|
s: 0.25
|
|
40
|
-
},
|
|
40
|
+
}, it = 6, at = 5, T = 22, m = 30, u = 10, p = 6, ct = 4, ht = u * 2, L = 51, lt = 12, dt = 4, $ = 1, x = 4, ft = 1, W = p * 2 + x + 8, st = 16, R = 12, k = st, Et = 2, Z = 8, ut = 40, rt = {
|
|
41
41
|
sharp: ["F", "C", "G", "D", "A", "E", "B"],
|
|
42
42
|
flat: ["B", "E", "A", "D", "G", "C", "F"]
|
|
43
|
-
},
|
|
43
|
+
}, V = {
|
|
44
44
|
C: { type: "sharp", count: 0 },
|
|
45
45
|
G: { type: "sharp", count: 1 },
|
|
46
46
|
D: { type: "sharp", count: 2 },
|
|
@@ -54,7 +54,7 @@ const S = {
|
|
|
54
54
|
Ab: { type: "flat", count: 4 },
|
|
55
55
|
Db: { type: "flat", count: 5 },
|
|
56
56
|
Gb: { type: "flat", count: 6 }
|
|
57
|
-
},
|
|
57
|
+
}, X = {
|
|
58
58
|
treble: {
|
|
59
59
|
sharp: [5, 5, 5, 5, 4, 5, 4],
|
|
60
60
|
// F5 C5 G5 D5 A4 E5 B4
|
|
@@ -75,22 +75,23 @@ const S = {
|
|
|
75
75
|
},
|
|
76
76
|
grand: { sharp: [], flat: [] }
|
|
77
77
|
};
|
|
78
|
-
function
|
|
79
|
-
const e =
|
|
80
|
-
if (!e) throw new Error(`Unknown key signature "${
|
|
78
|
+
function gt(l) {
|
|
79
|
+
const e = V[l];
|
|
80
|
+
if (!e) throw new Error(`Unknown key signature "${l}". Valid keys: ${Object.keys(V).join(", ")}`);
|
|
81
81
|
return e;
|
|
82
82
|
}
|
|
83
|
-
function
|
|
84
|
-
const r =
|
|
83
|
+
function At(l, e, t, s) {
|
|
84
|
+
const r = gt(t);
|
|
85
85
|
if (r.count === 0) return 0;
|
|
86
|
-
const
|
|
86
|
+
const n = r.type === "sharp" ? "ACCIDENTAL_SHARP" : "ACCIDENTAL_FLAT", o = l.getLayer("staff");
|
|
87
|
+
if (!o) throw new Error("Staff Init Error: Failed to retrieve staff layer for key signature renderering.");
|
|
87
88
|
return e.getKeySignatureYPositions(r.type, r.count).forEach((a) => {
|
|
88
|
-
a.forEach((
|
|
89
|
-
|
|
89
|
+
a.forEach((h, d) => {
|
|
90
|
+
l.drawGlyph(n, o, { xOffset: s + d * Z, yOffset: h });
|
|
90
91
|
});
|
|
91
|
-
}), r.count *
|
|
92
|
+
}), r.count * Z;
|
|
92
93
|
}
|
|
93
|
-
const
|
|
94
|
+
const pt = {
|
|
94
95
|
CLEF_TREBLE: {
|
|
95
96
|
path: "m150 273 10 58c2 7 2 7 12 7 59 0 96 46 96 97 0 45-26 79-67 95-5 2-6 2-5 7 4 25 12 63 12 85 0 68-52 80-79 80-61 0-76-39-76-65 0-25 16-46 43-46 24 0 38 19 38 41 0 23-14 34-27 38-9 2-13 4-13 6 0 6 11 12 32 12 24 0 64-7 64-66 0-19-6-54-11-81-1-5-1-4-6-3l-27 2C48 540 0 474 0 404c0-80 61-138 119-185 5-4 4-5 3-10-2-16-5-42-5-65 0-42 9-92 39-125 8-9 20-19 26-19 4 0 15 11 21 20 16 24 26 58 26 93 0 61-33 112-76 153-3 2-3 2-3 7Zm38-211c-24 0-53 38-53 101l2 37c1 5 3 5 5 3 32-28 70-64 70-108 0-22-11-33-24-33Zm-44 272-8-51c-1-4-2-5-6-1-18 15-37 30-61 56-33 38-37 70-37 93 0 56 45 95 115 95l23-2c6-2 6-2 5-6l-20-119c-1-5-1-5-8-3-24 6-40 24-40 46 0 19 12 36 29 43 3 1 6 3 6 5 0 3-2 5-5 5l-11-3c-27-9-46-34-46-70 0-34 23-66 58-78 8-2 8-2 6-10Zm28 64 20 114c0 5 1 5 6 2 22-11 38-31 38-56 0-36-27-63-60-66-4 0-5 1-4 6Z",
|
|
96
97
|
xOffset: 0,
|
|
@@ -186,28 +187,28 @@ const Et = {
|
|
|
186
187
|
xOffset: 0,
|
|
187
188
|
yOffset: 0
|
|
188
189
|
}
|
|
189
|
-
},
|
|
190
|
-
function
|
|
191
|
-
const e =
|
|
190
|
+
}, It = /^(?<name>[A-Ga-g])(?<accidental>##|bb|[#bn]?)(?<octave>\d)(?<duration>[whqeWHQE]?)$/, _t = /^[whqeWHQE]$/, U = ["C", "D", "E", "F", "G", "A", "B"];
|
|
191
|
+
function Y(l) {
|
|
192
|
+
const e = l.match(It);
|
|
192
193
|
if (!e || !e.groups)
|
|
193
|
-
throw new Error(`Invalid note string format: ${
|
|
194
|
-
let { name: t, accidental: s, octave: r, duration:
|
|
195
|
-
t = t.toUpperCase(),
|
|
196
|
-
const
|
|
194
|
+
throw new Error(`Invalid note string format: ${l}. Expected format: [A-Ga-g][#|b]?[0-9][w|h|q|e].`);
|
|
195
|
+
let { name: t, accidental: s, octave: r, duration: n } = e.groups;
|
|
196
|
+
t = t.toUpperCase(), n = n.toLowerCase(), n || (n = "w");
|
|
197
|
+
const o = {
|
|
197
198
|
name: t,
|
|
198
199
|
octave: parseInt(r),
|
|
199
|
-
duration:
|
|
200
|
+
duration: n
|
|
200
201
|
};
|
|
201
|
-
return s && (
|
|
202
|
+
return s && (o.accidental = s), o;
|
|
202
203
|
}
|
|
203
|
-
function
|
|
204
|
-
const e =
|
|
205
|
-
if (!e) throw new Error(`Invalid note duration '${
|
|
204
|
+
function v(l) {
|
|
205
|
+
const e = l.match(_t);
|
|
206
|
+
if (!e) throw new Error(`Invalid note duration '${l}'. Use w | h | q | e.`);
|
|
206
207
|
return e[0].toString().toLowerCase();
|
|
207
208
|
}
|
|
208
|
-
function
|
|
209
|
+
function M(l) {
|
|
209
210
|
let e;
|
|
210
|
-
switch (
|
|
211
|
+
switch (l) {
|
|
211
212
|
case "treble":
|
|
212
213
|
e = "CLEF_TREBLE";
|
|
213
214
|
break;
|
|
@@ -218,25 +219,25 @@ function x(h) {
|
|
|
218
219
|
e = "CLEF_ALTO";
|
|
219
220
|
break;
|
|
220
221
|
}
|
|
221
|
-
if (!e) throw new Error(`Invalid clef type: ${
|
|
222
|
+
if (!e) throw new Error(`Invalid clef type: ${l}. Valid clef types are: treble, bass, alto.`);
|
|
222
223
|
return e;
|
|
223
224
|
}
|
|
224
|
-
function
|
|
225
|
-
let e =
|
|
226
|
-
return e +=
|
|
225
|
+
function z(l) {
|
|
226
|
+
let e = U.indexOf(l.name);
|
|
227
|
+
return e += l.octave * 12, e;
|
|
227
228
|
}
|
|
228
|
-
function
|
|
229
|
-
return
|
|
229
|
+
function C(l, e) {
|
|
230
|
+
return U.indexOf(l.name) - U.indexOf(e.name) + (l.octave - e.octave) * 7;
|
|
230
231
|
}
|
|
231
|
-
const
|
|
232
|
-
class
|
|
232
|
+
const F = 48, q = 40 + 30 / 2, j = 20, K = 90;
|
|
233
|
+
class nt {
|
|
233
234
|
params;
|
|
234
235
|
rendererRef;
|
|
235
236
|
width = 0;
|
|
236
237
|
trebleStaffHeight = 0;
|
|
237
238
|
constructor(e, t) {
|
|
238
239
|
this.rendererRef = e;
|
|
239
|
-
const s =
|
|
240
|
+
const s = w[t];
|
|
240
241
|
if (!s) throw new Error(`Staff type ${t} is not supported`);
|
|
241
242
|
this.params = s;
|
|
242
243
|
}
|
|
@@ -248,109 +249,109 @@ class tt {
|
|
|
248
249
|
};
|
|
249
250
|
drawStaff = (e) => {
|
|
250
251
|
this.width = e;
|
|
251
|
-
const t = this.rendererRef.
|
|
252
|
+
const t = this.rendererRef.createLayer("staff");
|
|
252
253
|
let s = this.drawStaffLines(0, t), r = this.drawStaffLines(s + 30, t);
|
|
253
254
|
this.rendererRef.drawLine(0, 0, 0, r, t), this.rendererRef.drawLine(this.width, 0, this.width, r, t), this.trebleStaffHeight = s;
|
|
254
|
-
let
|
|
255
|
-
const i =
|
|
256
|
-
this.rendererRef.drawGlyph(i, t), this.rendererRef.drawGlyph(a, t, { yOffset: s + 30 }),
|
|
255
|
+
let n = r, o = 1;
|
|
256
|
+
const i = M("treble"), a = M("bass");
|
|
257
|
+
this.rendererRef.drawGlyph(i, t), this.rendererRef.drawGlyph(a, t, { yOffset: s + 30 }), n += this.params.paddingBottom + 1, o += this.params.paddingTop, this.rendererRef.addTotalRootSvgHeight(n), this.rendererRef.addTotalRootSvgYOffset(o);
|
|
257
258
|
};
|
|
258
259
|
shouldNoteFlip(e) {
|
|
259
|
-
const t = Math.abs(e -
|
|
260
|
-
return e ===
|
|
260
|
+
const t = Math.abs(e - K), s = Math.abs(e - j);
|
|
261
|
+
return e === q ? !1 : t <= s ? !(e >= K) : e <= j;
|
|
261
262
|
}
|
|
262
263
|
getLedgerLinesX(e, t) {
|
|
263
|
-
const s = [], r =
|
|
264
|
-
let
|
|
265
|
-
if (r ===
|
|
266
|
-
return s.push({ x1: -2, x2:
|
|
264
|
+
const s = [], r = z(e);
|
|
265
|
+
let n = e.duration === "w" ? 17 : 12.5;
|
|
266
|
+
if (r === F)
|
|
267
|
+
return s.push({ x1: -2, x2: n, yPos: 0 }), s;
|
|
267
268
|
if (t < this.params.topLineYPos) {
|
|
268
|
-
let
|
|
269
|
-
for (;
|
|
269
|
+
let o = this.params.topLineYPos - 10;
|
|
270
|
+
for (; o >= t; )
|
|
270
271
|
s.push({
|
|
271
272
|
x1: -2,
|
|
272
|
-
x2:
|
|
273
|
-
yPos:
|
|
274
|
-
}),
|
|
273
|
+
x2: n,
|
|
274
|
+
yPos: o - t
|
|
275
|
+
}), o -= 10;
|
|
275
276
|
} else if (t > this.params.bottomLineYPos) {
|
|
276
|
-
let
|
|
277
|
-
for (;
|
|
277
|
+
let o = this.params.bottomLineYPos + 10;
|
|
278
|
+
for (; o <= t; )
|
|
278
279
|
s.push({
|
|
279
280
|
x1: -2,
|
|
280
|
-
x2:
|
|
281
|
-
yPos:
|
|
282
|
-
}),
|
|
281
|
+
x2: n,
|
|
282
|
+
yPos: o - t
|
|
283
|
+
}), o += 10;
|
|
283
284
|
}
|
|
284
285
|
return s;
|
|
285
286
|
}
|
|
286
287
|
calculateNoteYPos = (e) => {
|
|
287
|
-
let s =
|
|
288
|
-
const r =
|
|
289
|
-
return r <
|
|
288
|
+
let s = C(this.params.topLineNote, e) * (10 / 2);
|
|
289
|
+
const r = z(e);
|
|
290
|
+
return r < F ? s += 10 : r === F && (s = q), s;
|
|
290
291
|
};
|
|
291
292
|
getKeySignatureYPositions(e, t) {
|
|
292
|
-
const s =
|
|
293
|
-
(a,
|
|
293
|
+
const s = rt[e].slice(0, t), r = X.treble[e], n = X.bass[e], o = s.map(
|
|
294
|
+
(a, h) => C(w.treble.topLineNote, { name: a, octave: r[h] }) * (10 / 2)
|
|
294
295
|
), i = s.map(
|
|
295
|
-
(a,
|
|
296
|
+
(a, h) => C(w.bass.topLineNote, { name: a, octave: n[h] }) * (10 / 2) + this.trebleStaffHeight + 30
|
|
296
297
|
);
|
|
297
|
-
return [
|
|
298
|
+
return [o, i];
|
|
298
299
|
}
|
|
299
300
|
}
|
|
300
|
-
const
|
|
301
|
-
class
|
|
301
|
+
const Tt = 20;
|
|
302
|
+
class S {
|
|
302
303
|
params;
|
|
303
304
|
rendererRef;
|
|
304
305
|
constructor(e, t) {
|
|
305
306
|
this.rendererRef = e;
|
|
306
|
-
const s =
|
|
307
|
+
const s = w[t];
|
|
307
308
|
if (!s) throw new Error(`Staff type ${t} is not supported`);
|
|
308
309
|
this.params = s;
|
|
309
310
|
}
|
|
310
311
|
drawStaff = (e) => {
|
|
311
|
-
const t = this.rendererRef.
|
|
312
|
+
const t = this.rendererRef.createLayer("staff");
|
|
312
313
|
let s = 0;
|
|
313
314
|
for (let i = 0; i < 5; i++)
|
|
314
315
|
this.rendererRef.drawLine(0, s, e, s, t), s += 10;
|
|
315
316
|
this.rendererRef.drawLine(0, 0, 0, s - 10, t), this.rendererRef.drawLine(e, 0, e, s - 10, t);
|
|
316
|
-
let r = s - 10 + 1,
|
|
317
|
-
const
|
|
318
|
-
this.rendererRef.drawGlyph(
|
|
317
|
+
let r = s - 10 + 1, n = 1;
|
|
318
|
+
const o = M(this.params.staffType);
|
|
319
|
+
this.rendererRef.drawGlyph(o, t), r += this.params.paddingTop + this.params.paddingBottom, n += this.params.paddingTop, this.rendererRef.addTotalRootSvgHeight(r), this.rendererRef.addTotalRootSvgYOffset(n);
|
|
319
320
|
};
|
|
320
321
|
shouldNoteFlip(e) {
|
|
321
|
-
return e <=
|
|
322
|
+
return e <= Tt;
|
|
322
323
|
}
|
|
323
324
|
getLedgerLinesX(e, t) {
|
|
324
325
|
const s = [];
|
|
325
326
|
let r = e.duration === "w" ? 17 : 12.5;
|
|
326
327
|
if (t < this.params.topLineYPos) {
|
|
327
|
-
let
|
|
328
|
-
for (;
|
|
328
|
+
let n = this.params.topLineYPos - 10;
|
|
329
|
+
for (; n >= t; )
|
|
329
330
|
s.push({
|
|
330
331
|
x1: -2,
|
|
331
332
|
x2: r,
|
|
332
|
-
yPos:
|
|
333
|
-
}),
|
|
333
|
+
yPos: n - t
|
|
334
|
+
}), n -= 10;
|
|
334
335
|
} else if (t > this.params.bottomLineYPos) {
|
|
335
|
-
let
|
|
336
|
-
for (;
|
|
336
|
+
let n = this.params.bottomLineYPos + 10;
|
|
337
|
+
for (; n <= t; )
|
|
337
338
|
s.push({
|
|
338
339
|
x1: -2,
|
|
339
340
|
x2: r,
|
|
340
|
-
yPos:
|
|
341
|
-
}),
|
|
341
|
+
yPos: n - t
|
|
342
|
+
}), n += 10;
|
|
342
343
|
}
|
|
343
344
|
return s;
|
|
344
345
|
}
|
|
345
|
-
calculateNoteYPos = (e) =>
|
|
346
|
+
calculateNoteYPos = (e) => C(this.params.topLineNote, e) * (10 / 2);
|
|
346
347
|
getKeySignatureYPositions(e, t) {
|
|
347
|
-
const s =
|
|
348
|
+
const s = rt[e].slice(0, t), r = X[this.params.staffType][e];
|
|
348
349
|
return [s.map(
|
|
349
|
-
(
|
|
350
|
+
(o, i) => this.calculateNoteYPos({ name: o, octave: r[i] })
|
|
350
351
|
)];
|
|
351
352
|
}
|
|
352
353
|
}
|
|
353
|
-
class
|
|
354
|
+
class ot {
|
|
354
355
|
svgRendererInstance;
|
|
355
356
|
strategyInstance;
|
|
356
357
|
constructor(e, t) {
|
|
@@ -361,10 +362,10 @@ class et {
|
|
|
361
362
|
}
|
|
362
363
|
chordOffsetConsecutiveAccidentals(e) {
|
|
363
364
|
let t = 0, s = 0, r = 0;
|
|
364
|
-
for (let
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
367
|
-
const a = Array.from(
|
|
365
|
+
for (let n = 0; n < e.length; n++) {
|
|
366
|
+
const o = e[n];
|
|
367
|
+
if (o.noteObj.accidental && r < 3 ? (t += -8, s = Math.min(s, t), r++) : o.noteObj.accidental && r <= 3 ? (t = -8, r = 1) : (t = 0, r = 0), t !== 0) {
|
|
368
|
+
const a = Array.from(o.noteGroup.getElementsByTagName("use")).find((h) => h.getAttribute("href")?.includes("ACCIDENTAL"));
|
|
368
369
|
if (!a) continue;
|
|
369
370
|
a.setAttribute("transform", `translate(${t + 8}, 0)`);
|
|
370
371
|
}
|
|
@@ -374,58 +375,58 @@ class et {
|
|
|
374
375
|
chordOffsetCloseNotes(e) {
|
|
375
376
|
let t = e[0], s = 0;
|
|
376
377
|
for (let r = 1; r < e.length; r++) {
|
|
377
|
-
const
|
|
378
|
-
if (-
|
|
379
|
-
s = 28 / 2,
|
|
380
|
-
const a = Array.from(
|
|
378
|
+
const n = e[r];
|
|
379
|
+
if (-C(t.noteObj, n.noteObj) === 1) {
|
|
380
|
+
s = 28 / 2, n.noteGroup.setAttribute("transform", `translate(${s}, ${n.noteYPos})`);
|
|
381
|
+
const a = Array.from(n.noteGroup.getElementsByTagName("use")).find((h) => h.getAttribute("href")?.includes("ACCIDENTAL"));
|
|
381
382
|
if (a) {
|
|
382
|
-
const
|
|
383
|
-
let
|
|
384
|
-
|
|
383
|
+
const h = a.getAttribute("transform")?.match(/([-]?\d+)/), d = h && h[0];
|
|
384
|
+
let c = -s;
|
|
385
|
+
d && (c += Number(d)), a.setAttribute("transform", `translate(${c}, 0)`);
|
|
385
386
|
}
|
|
386
387
|
r++, t = e[r];
|
|
387
388
|
continue;
|
|
388
389
|
}
|
|
389
|
-
t =
|
|
390
|
+
t = n;
|
|
390
391
|
}
|
|
391
392
|
return s;
|
|
392
393
|
}
|
|
393
394
|
// Handles drawing the glyphs to internal group, applies the xPositioning to note Cursor X
|
|
394
395
|
renderNote(e) {
|
|
395
|
-
const t = this.svgRendererInstance.createGroup("note"), s =
|
|
396
|
+
const t = this.svgRendererInstance.createGroup("note"), s = Y(e), r = this.strategyInstance.calculateNoteYPos({
|
|
396
397
|
name: s.name,
|
|
397
398
|
octave: s.octave
|
|
398
399
|
});
|
|
399
|
-
let
|
|
400
|
+
let n = this.strategyInstance.shouldNoteFlip(r);
|
|
400
401
|
switch (s.duration) {
|
|
401
402
|
case "h":
|
|
402
|
-
this.svgRendererInstance.drawGlyph("NOTE_HEAD_HALF", t), this.drawStem(t,
|
|
403
|
+
this.svgRendererInstance.drawGlyph("NOTE_HEAD_HALF", t), this.drawStem(t, n);
|
|
403
404
|
break;
|
|
404
405
|
case "q":
|
|
405
|
-
this.svgRendererInstance.drawGlyph("NOTE_HEAD_QUARTER", t), this.drawStem(t,
|
|
406
|
+
this.svgRendererInstance.drawGlyph("NOTE_HEAD_QUARTER", t), this.drawStem(t, n);
|
|
406
407
|
break;
|
|
407
408
|
case "e":
|
|
408
|
-
|
|
409
|
+
n ? this.svgRendererInstance.drawGlyph("EIGHTH_NOTE_FLIPPED", t) : this.svgRendererInstance.drawGlyph("EIGHTH_NOTE", t);
|
|
409
410
|
break;
|
|
410
411
|
default:
|
|
411
412
|
this.svgRendererInstance.drawGlyph("NOTE_HEAD_WHOLE", t);
|
|
412
413
|
}
|
|
413
|
-
let
|
|
414
|
+
let o = 0;
|
|
414
415
|
switch (s.accidental) {
|
|
415
416
|
case "#":
|
|
416
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_SHARP", t),
|
|
417
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_SHARP", t), o -= -8;
|
|
417
418
|
break;
|
|
418
419
|
case "b":
|
|
419
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_FLAT", t),
|
|
420
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_FLAT", t), o -= -8;
|
|
420
421
|
break;
|
|
421
422
|
case "n":
|
|
422
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_NATURAL", t),
|
|
423
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_NATURAL", t), o -= -8;
|
|
423
424
|
break;
|
|
424
425
|
case "##":
|
|
425
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_SHARP", t),
|
|
426
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_SHARP", t), o -= -10;
|
|
426
427
|
break;
|
|
427
428
|
case "bb":
|
|
428
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_FLAT", t),
|
|
429
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_FLAT", t), o -= -14;
|
|
429
430
|
break;
|
|
430
431
|
}
|
|
431
432
|
return this.strategyInstance.getLedgerLinesX(s, r).forEach((a) => {
|
|
@@ -434,14 +435,14 @@ class et {
|
|
|
434
435
|
noteGroup: t,
|
|
435
436
|
noteObj: s,
|
|
436
437
|
noteYPos: r,
|
|
437
|
-
accidentalOffset:
|
|
438
|
+
accidentalOffset: o,
|
|
438
439
|
cursorOffset: 0
|
|
439
440
|
};
|
|
440
441
|
}
|
|
441
442
|
renderChord(e) {
|
|
442
443
|
const t = this.svgRendererInstance.createGroup("chord"), s = [];
|
|
443
|
-
for (const
|
|
444
|
-
const i = this.renderNote(
|
|
444
|
+
for (const o of e) {
|
|
445
|
+
const i = this.renderNote(o);
|
|
445
446
|
i.noteGroup.setAttribute("transform", `translate(0, ${i.noteYPos})`), t.appendChild(i.noteGroup), s.push({
|
|
446
447
|
noteGroup: i.noteGroup,
|
|
447
448
|
noteObj: i.noteObj,
|
|
@@ -450,25 +451,23 @@ class et {
|
|
|
450
451
|
accidentalOffset: 0
|
|
451
452
|
});
|
|
452
453
|
}
|
|
453
|
-
const r = this.chordOffsetConsecutiveAccidentals(s),
|
|
454
|
+
const r = this.chordOffsetConsecutiveAccidentals(s), n = this.chordOffsetCloseNotes(s);
|
|
454
455
|
return {
|
|
455
456
|
noteGroup: t,
|
|
456
457
|
noteObj: s[0].noteObj,
|
|
457
458
|
noteYPos: 0,
|
|
458
459
|
accidentalOffset: r,
|
|
459
|
-
cursorOffset:
|
|
460
|
+
cursorOffset: n
|
|
460
461
|
};
|
|
461
462
|
}
|
|
462
463
|
}
|
|
463
|
-
const
|
|
464
|
-
class
|
|
464
|
+
const A = "http://www.w3.org/2000/svg", St = 0.1, G = 2;
|
|
465
|
+
class O {
|
|
465
466
|
rootElementRef;
|
|
466
467
|
svgElementRef;
|
|
467
468
|
// Layers
|
|
468
469
|
parentGroupContainer;
|
|
469
|
-
|
|
470
|
-
musicNotesLayer;
|
|
471
|
-
musicUILayer;
|
|
470
|
+
layers = {};
|
|
472
471
|
// Positioning variables
|
|
473
472
|
width;
|
|
474
473
|
scale;
|
|
@@ -479,18 +478,36 @@ class L {
|
|
|
479
478
|
// from get rootSvgElement().
|
|
480
479
|
// HEIGHT and YOfsset should be set externally, values are calculated internally.
|
|
481
480
|
constructor(e, t) {
|
|
482
|
-
this.rootElementRef = e, this.width = t.width, this.scale = t.scale, this.
|
|
481
|
+
this.rootElementRef = e, this.width = t.width, this.scale = t.scale, this.svgElementRef = document.createElementNS(A, "svg"), this.svgElementRef.classList.add("vs-svg-renderer-root"), t.svgAutoFill && (this.svgElementRef.style.maxWidth = "100%", this.svgElementRef.style.height = "auto"), this.makeGlyphDefs(t.useGlyphs), this.parentGroupContainer = this.createGroup("svg-renderer-parent"), this.svgElementRef.appendChild(this.parentGroupContainer);
|
|
483
482
|
}
|
|
484
483
|
// Creates SVG defs for all glyphs in GLYPH_ENTRIES, applies global scale and offsets, appends to root SVG
|
|
485
484
|
makeGlyphDefs(e) {
|
|
486
|
-
const t = document.createElementNS(
|
|
487
|
-
Object.entries(
|
|
488
|
-
const
|
|
489
|
-
|
|
485
|
+
const t = document.createElementNS(A, "defs");
|
|
486
|
+
Object.entries(pt).filter(([r]) => e.includes(r)).forEach(([r, n]) => {
|
|
487
|
+
const o = document.createElementNS(A, "path");
|
|
488
|
+
o.setAttribute("id", `glyph-${r}`), o.setAttribute("d", n.path), o.setAttribute("fill", "currentColor"), o.setAttribute("transform", `translate(${n.xOffset}, ${n.yOffset}) scale(${St})`), t.appendChild(o);
|
|
490
489
|
}), this.rootSvgElement.appendChild(t);
|
|
491
490
|
}
|
|
491
|
+
addNamespacedClassesToElement(e, t) {
|
|
492
|
+
[e].flat().forEach((r) => t.classList.add(`vs-${r}`));
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Creates a layer that is appended to parent element.
|
|
496
|
+
* layerName arg is the key that is appended to the internal list of layers, which
|
|
497
|
+
* can be used in method getLayer() arg. Best to call on construction.
|
|
498
|
+
*
|
|
499
|
+
* @param {string} layerName
|
|
500
|
+
* @returns {SVGGElement}
|
|
501
|
+
*/
|
|
502
|
+
createLayer(e) {
|
|
503
|
+
const t = document.createElementNS(A, "g");
|
|
504
|
+
return t.classList.add(`vs-${e}-layer`), this.layers[e] = t, this.parentGroupContainer.appendChild(this.layers[e]), t;
|
|
505
|
+
}
|
|
506
|
+
getLayer(e) {
|
|
507
|
+
return this.layers[e] ?? null;
|
|
508
|
+
}
|
|
492
509
|
createGroup(e) {
|
|
493
|
-
const t = document.createElementNS(
|
|
510
|
+
const t = document.createElementNS(A, "g");
|
|
494
511
|
return e && t.classList.add(`vs-${e}`), t;
|
|
495
512
|
}
|
|
496
513
|
commitElementsToDOM(e, t = this.rootElementRef) {
|
|
@@ -499,18 +516,6 @@ class L {
|
|
|
499
516
|
s.appendChild(r);
|
|
500
517
|
}) : s.appendChild(e), t.appendChild(s);
|
|
501
518
|
}
|
|
502
|
-
getLayerByName(e) {
|
|
503
|
-
switch (e) {
|
|
504
|
-
case "staff":
|
|
505
|
-
return this.musicStaffLayer;
|
|
506
|
-
case "notes":
|
|
507
|
-
return this.musicNotesLayer;
|
|
508
|
-
case "ui":
|
|
509
|
-
return this.musicUILayer;
|
|
510
|
-
default:
|
|
511
|
-
throw new Error(`Layer with name '${e}' does not exist.`);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
519
|
addTotalRootSvgHeight(e) {
|
|
515
520
|
this.totalHeight += e;
|
|
516
521
|
}
|
|
@@ -521,10 +526,10 @@ class L {
|
|
|
521
526
|
this.totalYOffset += e;
|
|
522
527
|
}
|
|
523
528
|
applySizingToRootSvg() {
|
|
524
|
-
this.parentGroupContainer.setAttribute("transform", `translate(
|
|
525
|
-
let e = this.width * this.scale;
|
|
529
|
+
this.parentGroupContainer.setAttribute("transform", `translate(${G / 2}, ${this.totalYOffset})`);
|
|
530
|
+
let e = Math.round(this.width * this.scale);
|
|
526
531
|
const t = (this.totalHeight + this.totalYOffset) * this.scale;
|
|
527
|
-
e +=
|
|
532
|
+
e += G, this.svgElementRef.setAttribute("width", e.toString()), this.svgElementRef.setAttribute("height", t.toString()), this.svgElementRef.setAttribute("viewBox", `0 0 ${this.width + G} ${this.totalHeight + this.totalYOffset}`);
|
|
528
533
|
}
|
|
529
534
|
get rootSvgElement() {
|
|
530
535
|
return this.svgElementRef;
|
|
@@ -533,13 +538,13 @@ class L {
|
|
|
533
538
|
return this.parentGroupContainer;
|
|
534
539
|
}
|
|
535
540
|
// Drawing Methods
|
|
536
|
-
drawLine(e, t, s, r, o) {
|
|
537
|
-
const
|
|
538
|
-
|
|
541
|
+
drawLine(e, t, s, r, n, o) {
|
|
542
|
+
const i = o?.strokeWidth ?? 1, a = document.createElementNS(A, "line");
|
|
543
|
+
a.setAttribute("x1", e.toString()), a.setAttribute("y1", t.toString()), a.setAttribute("x2", s.toString()), a.setAttribute("y2", r.toString()), a.setAttribute("stroke", "currentColor"), a.setAttribute("stroke-width", i.toString()), o?.classes && this.addNamespacedClassesToElement(o.classes, n), n.appendChild(a);
|
|
539
544
|
}
|
|
540
545
|
drawRect(e, t, s, r) {
|
|
541
|
-
const
|
|
542
|
-
return
|
|
546
|
+
const n = document.createElementNS(A, "rect");
|
|
547
|
+
return n.setAttribute("width", e.toString()), n.setAttribute("height", t.toString()), n.setAttribute("x", (r?.x ?? 0).toString()), n.setAttribute("y", (r?.y ?? 0).toString()), r?.fill ? n.setAttribute("fill", r.fill) : n.setAttribute("fill", "currentColor"), r?.rx && n.setAttribute("rx", r.rx.toString()), r?.classes && this.addNamespacedClassesToElement(r.classes, n), s.appendChild(n), n;
|
|
543
548
|
}
|
|
544
549
|
drawGlyph(e, t, s) {
|
|
545
550
|
s = {
|
|
@@ -547,22 +552,22 @@ class L {
|
|
|
547
552
|
yOffset: 0,
|
|
548
553
|
...s
|
|
549
554
|
};
|
|
550
|
-
const r = document.createElementNS(
|
|
555
|
+
const r = document.createElementNS(A, "use");
|
|
551
556
|
r.setAttribute("href", `#glyph-${e}`), (s.xOffset || s.yOffset) && r.setAttribute("transform", `translate(${s.xOffset}, ${s.yOffset})`), t.appendChild(r);
|
|
552
557
|
}
|
|
553
|
-
drawCircle(e, t, s, r,
|
|
554
|
-
const
|
|
555
|
-
return
|
|
558
|
+
drawCircle(e, t, s, r, n) {
|
|
559
|
+
const o = document.createElementNS(A, "circle");
|
|
560
|
+
return o.setAttribute("cx", e.toString()), o.setAttribute("cy", t.toString()), o.setAttribute("r", s.toString()), n?.filled === !1 ? (o.setAttribute("fill", "none"), o.setAttribute("stroke", "currentColor"), o.setAttribute("stroke-width", (n?.strokeWidth ?? 1.5).toString())) : o.setAttribute("fill", n?.fill ?? "currentColor"), n?.classes && this.addNamespacedClassesToElement(n.classes, o), r.appendChild(o), o;
|
|
556
561
|
}
|
|
557
|
-
drawText(e, t, s, r,
|
|
558
|
-
const
|
|
559
|
-
return
|
|
562
|
+
drawText(e, t, s, r, n) {
|
|
563
|
+
const o = document.createElementNS(A, "text");
|
|
564
|
+
return o.setAttribute("x", t.toString()), o.setAttribute("y", s.toString()), o.setAttribute("text-anchor", n?.anchor ?? "middle"), o.setAttribute("fill", n?.fill ?? "currentColor"), n?.fontSize && o.setAttribute("font-size", n.fontSize + "px"), n?.fontWeight && o.setAttribute("font-weight", n.fontWeight), n?.baseline && o.setAttribute("dominant-baseline", n.baseline), n?.classes && this.addNamespacedClassesToElement(n.classes, o), o.textContent = e, r.appendChild(o), o;
|
|
560
565
|
}
|
|
561
566
|
destroy() {
|
|
562
567
|
this.rootElementRef && this.svgElementRef && this.rootElementRef.contains(this.svgElementRef) && this.rootElementRef.removeChild(this.svgElementRef);
|
|
563
568
|
}
|
|
564
569
|
}
|
|
565
|
-
const
|
|
570
|
+
const mt = [
|
|
566
571
|
"CLEF_TREBLE",
|
|
567
572
|
"CLEF_BASS",
|
|
568
573
|
"CLEF_ALTO",
|
|
@@ -577,7 +582,7 @@ const It = [
|
|
|
577
582
|
"ACCIDENTAL_DOUBLE_SHARP",
|
|
578
583
|
"ACCIDENTAL_DOUBLE_FLAT"
|
|
579
584
|
];
|
|
580
|
-
class
|
|
585
|
+
class bt {
|
|
581
586
|
svgRendererInstance;
|
|
582
587
|
strategyInstance;
|
|
583
588
|
noteRendererInstance;
|
|
@@ -599,35 +604,35 @@ class Rt {
|
|
|
599
604
|
staffType: "treble",
|
|
600
605
|
spaceAbove: 0,
|
|
601
606
|
spaceBelow: 0,
|
|
602
|
-
|
|
603
|
-
staffBackgroundColor: "transparent",
|
|
607
|
+
svgAutoFill: !0,
|
|
604
608
|
...t
|
|
605
|
-
}, this.svgRendererInstance = new
|
|
609
|
+
}, this.svgRendererInstance = new O(e, {
|
|
606
610
|
width: this.options.width,
|
|
607
611
|
height: 100,
|
|
608
612
|
scale: this.options.scale,
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
useGlyphs: It
|
|
613
|
+
useGlyphs: mt,
|
|
614
|
+
svgAutoFill: this.options.svgAutoFill
|
|
612
615
|
});
|
|
613
616
|
const s = this.svgRendererInstance.rootSvgElement;
|
|
614
617
|
switch (this.options.staffType) {
|
|
615
618
|
case "grand":
|
|
616
|
-
this.strategyInstance = new
|
|
619
|
+
this.strategyInstance = new nt(this.svgRendererInstance, "grand");
|
|
617
620
|
break;
|
|
618
621
|
case "bass":
|
|
619
|
-
this.strategyInstance = new
|
|
622
|
+
this.strategyInstance = new S(this.svgRendererInstance, "bass");
|
|
620
623
|
break;
|
|
621
624
|
case "treble":
|
|
622
|
-
this.strategyInstance = new
|
|
625
|
+
this.strategyInstance = new S(this.svgRendererInstance, "treble");
|
|
623
626
|
break;
|
|
624
627
|
case "alto":
|
|
625
|
-
this.strategyInstance = new
|
|
628
|
+
this.strategyInstance = new S(this.svgRendererInstance, "alto");
|
|
626
629
|
break;
|
|
627
630
|
default:
|
|
628
631
|
throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
|
|
629
632
|
}
|
|
630
|
-
|
|
633
|
+
this.strategyInstance.drawStaff(this.options.width);
|
|
634
|
+
const r = this.svgRendererInstance.createLayer("notes");
|
|
635
|
+
if (this.noteRendererInstance = new ot(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
|
|
631
636
|
const o = this.options.spaceAbove * 10;
|
|
632
637
|
this.svgRendererInstance.addTotalRootSvgYOffset(o);
|
|
633
638
|
}
|
|
@@ -635,11 +640,11 @@ class Rt {
|
|
|
635
640
|
let o = this.options.spaceBelow * 10;
|
|
636
641
|
this.options.staffType === "grand" && (o -= 10 / 2), this.svgRendererInstance.addTotalRootSvgHeight(o);
|
|
637
642
|
}
|
|
638
|
-
let
|
|
639
|
-
this.options.keySignature && (
|
|
643
|
+
let n = 0;
|
|
644
|
+
this.options.keySignature && (n = this.createKeySignature(this.options.keySignature)), this.noteStartX = 38 + this.options.noteStartX + n, r.setAttribute("transform", `translate(${this.noteStartX}, 0)`), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
|
|
640
645
|
}
|
|
641
646
|
createKeySignature(e) {
|
|
642
|
-
return
|
|
647
|
+
return At(this.svgRendererInstance, this.strategyInstance, e, ut);
|
|
643
648
|
}
|
|
644
649
|
/**
|
|
645
650
|
* Draws a note on the staff.
|
|
@@ -662,21 +667,23 @@ class Rt {
|
|
|
662
667
|
* drawNote("D4w");
|
|
663
668
|
*/
|
|
664
669
|
drawNote(e) {
|
|
665
|
-
const t = Array.isArray(e) ? e : [e], s = this.svgRendererInstance.
|
|
666
|
-
|
|
667
|
-
|
|
670
|
+
const t = Array.isArray(e) ? e : [e], s = this.svgRendererInstance.getLayer("notes");
|
|
671
|
+
if (!s) throw new Error("DrawNote Error: Failed to retrieve notesLayer to append notes.");
|
|
672
|
+
const r = [];
|
|
673
|
+
for (const n of t) {
|
|
674
|
+
let o;
|
|
668
675
|
try {
|
|
669
|
-
|
|
676
|
+
o = this.noteRendererInstance.renderNote(n);
|
|
670
677
|
} catch (i) {
|
|
671
678
|
throw r.length > 0 && this.svgRendererInstance.commitElementsToDOM(r, s), i;
|
|
672
679
|
}
|
|
673
|
-
|
|
674
|
-
gElement:
|
|
675
|
-
note:
|
|
676
|
-
xPos: this.noteCursorX +
|
|
677
|
-
yPos:
|
|
678
|
-
accidentalXOffset:
|
|
679
|
-
}), this.noteCursorX += 28 +
|
|
680
|
+
o.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + o.accidentalOffset}, ${o.noteYPos})`), this.noteEntries.push({
|
|
681
|
+
gElement: o.noteGroup,
|
|
682
|
+
note: o.noteObj,
|
|
683
|
+
xPos: this.noteCursorX + o.accidentalOffset,
|
|
684
|
+
yPos: o.noteYPos,
|
|
685
|
+
accidentalXOffset: o.accidentalOffset
|
|
686
|
+
}), this.noteCursorX += 28 + o.accidentalOffset, r.push(o.noteGroup);
|
|
680
687
|
}
|
|
681
688
|
this.svgRendererInstance.commitElementsToDOM(r, s);
|
|
682
689
|
}
|
|
@@ -697,10 +704,12 @@ class Rt {
|
|
|
697
704
|
*/
|
|
698
705
|
drawChord(e) {
|
|
699
706
|
if (e.length < 2) throw new Error("Provide more than one note for a chord.");
|
|
700
|
-
const t = this.svgRendererInstance.
|
|
707
|
+
const t = this.svgRendererInstance.getLayer("notes");
|
|
708
|
+
if (!t) throw new Error("DrawChord Error: Failed to retrieve notesLayer to append chord.");
|
|
709
|
+
const s = this.noteRendererInstance.renderChord(e);
|
|
701
710
|
s.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + s.accidentalOffset}, 0)`), this.noteEntries.push({
|
|
702
711
|
gElement: s.noteGroup,
|
|
703
|
-
note:
|
|
712
|
+
note: Y(e[0]),
|
|
704
713
|
xPos: this.noteCursorX + s.accidentalOffset,
|
|
705
714
|
yPos: 0,
|
|
706
715
|
accidentalXOffset: s.accidentalOffset
|
|
@@ -714,16 +723,16 @@ class Rt {
|
|
|
714
723
|
const e = this.options.width - this.noteStartX, t = this.noteEntries.length;
|
|
715
724
|
if (t <= 0 || e <= 0) return;
|
|
716
725
|
const s = Math.round(e / t);
|
|
717
|
-
this.noteEntries.map((
|
|
718
|
-
const i = (
|
|
726
|
+
this.noteEntries.map((n, o) => {
|
|
727
|
+
const i = (o + 0.5) * s, a = n.gElement.getBBox(), h = i - a.width / 2 - a.x, d = Math.round(h * 10) / 10;
|
|
719
728
|
return {
|
|
720
|
-
entry:
|
|
721
|
-
newX:
|
|
722
|
-
isChord:
|
|
729
|
+
entry: n,
|
|
730
|
+
newX: d,
|
|
731
|
+
isChord: n.gElement.classList.contains("chord")
|
|
723
732
|
};
|
|
724
|
-
}).forEach((
|
|
725
|
-
const { entry:
|
|
726
|
-
a ?
|
|
733
|
+
}).forEach((n) => {
|
|
734
|
+
const { entry: o, newX: i, isChord: a } = n;
|
|
735
|
+
a ? o.gElement.setAttribute("transform", `translate(${i}, 0)`) : o.gElement.setAttribute("transform", `translate(${i}, ${o.yPos})`), o.xPos = i;
|
|
727
736
|
});
|
|
728
737
|
}
|
|
729
738
|
/**
|
|
@@ -731,7 +740,7 @@ class Rt {
|
|
|
731
740
|
* @returns void
|
|
732
741
|
*/
|
|
733
742
|
clearAllNotes() {
|
|
734
|
-
this.noteCursorX = 0, this.svgRendererInstance.
|
|
743
|
+
this.noteCursorX = 0, this.svgRendererInstance.getLayer("notes")?.replaceChildren(), this.noteEntries = [];
|
|
735
744
|
}
|
|
736
745
|
/**
|
|
737
746
|
* Changes the note by index to the specified note.
|
|
@@ -751,11 +760,12 @@ class Rt {
|
|
|
751
760
|
*/
|
|
752
761
|
changeNoteByIndex(e, t) {
|
|
753
762
|
if (t >= this.noteEntries.length) throw new Error("Note index was out of bounds.");
|
|
754
|
-
const s = this.noteEntries[t], r = this.noteRendererInstance.renderNote(e),
|
|
755
|
-
r.noteGroup.setAttribute("transform", `translate(${
|
|
763
|
+
const s = this.noteEntries[t], r = this.noteRendererInstance.renderNote(e), o = s.xPos - s.accidentalXOffset + r.accidentalOffset;
|
|
764
|
+
if (r.noteGroup.setAttribute("transform", `translate(${o}, ${r.noteYPos})`), !this.svgRendererInstance.getLayer("notes")) throw new Error("ChangeNoteByIndex Error: Failed to retrieve notesLayer to append notes.");
|
|
765
|
+
this.noteEntries[t] = {
|
|
756
766
|
gElement: r.noteGroup,
|
|
757
767
|
note: r.noteObj,
|
|
758
|
-
xPos:
|
|
768
|
+
xPos: o,
|
|
759
769
|
yPos: r.noteYPos,
|
|
760
770
|
accidentalXOffset: r.accidentalOffset
|
|
761
771
|
};
|
|
@@ -779,11 +789,14 @@ class Rt {
|
|
|
779
789
|
changeChordByIndex(e, t) {
|
|
780
790
|
if (t >= this.noteEntries.length) throw new Error("Chord index was out of bounds.");
|
|
781
791
|
if (e.length < 2) throw new Error("Notes provided need to be more than one to be considered a chord.");
|
|
782
|
-
const s = this.noteEntries[t], r = this.noteRendererInstance.renderChord(e),
|
|
783
|
-
r.noteGroup.setAttribute("transform", `translate(${
|
|
792
|
+
const s = this.noteEntries[t], r = this.noteRendererInstance.renderChord(e), o = s.xPos - s.accidentalXOffset + r.accidentalOffset;
|
|
793
|
+
r.noteGroup.setAttribute("transform", `translate(${o}, 0)`);
|
|
794
|
+
const i = this.svgRendererInstance.getLayer("notes");
|
|
795
|
+
if (!i) throw new Error("DrawNote Error: Failed to retrieve notesLayer to append notes.");
|
|
796
|
+
i.replaceChild(r.noteGroup, s.gElement), this.noteEntries[t] = {
|
|
784
797
|
gElement: r.noteGroup,
|
|
785
|
-
note:
|
|
786
|
-
xPos:
|
|
798
|
+
note: Y(e[0]),
|
|
799
|
+
xPos: o,
|
|
787
800
|
yPos: 0,
|
|
788
801
|
accidentalXOffset: r.accidentalOffset
|
|
789
802
|
};
|
|
@@ -818,7 +831,7 @@ class Rt {
|
|
|
818
831
|
this.noteEntries = [], this.svgRendererInstance.destroy();
|
|
819
832
|
}
|
|
820
833
|
}
|
|
821
|
-
const
|
|
834
|
+
const _ = 30, Q = 19, D = 12, Ct = 4, Nt = 6, J = 1, H = 38, Lt = [
|
|
822
835
|
"TIME_4",
|
|
823
836
|
"TIME_3",
|
|
824
837
|
"NOTE_HEAD_WHOLE",
|
|
@@ -830,7 +843,7 @@ const A = 30, z = 19, O = 12, _t = 4, Tt = 6, j = 1, b = 38, mt = [
|
|
|
830
843
|
"REST_QUARTER",
|
|
831
844
|
"REST_EIGHTH"
|
|
832
845
|
];
|
|
833
|
-
class
|
|
846
|
+
class vt {
|
|
834
847
|
rendererInstance;
|
|
835
848
|
options;
|
|
836
849
|
barSpacing;
|
|
@@ -841,7 +854,7 @@ class yt {
|
|
|
841
854
|
currentBeatCount = 0;
|
|
842
855
|
currentBeatUICount = 0;
|
|
843
856
|
currentBeatUIElement = null;
|
|
844
|
-
currentBeatUIXPos =
|
|
857
|
+
currentBeatUIXPos = H;
|
|
845
858
|
/**
|
|
846
859
|
* Creates an instance of a RhythmStaff, A single staff that will automatically apply positioning of elements based on the duration of a note.
|
|
847
860
|
*
|
|
@@ -857,67 +870,66 @@ class yt {
|
|
|
857
870
|
barsCount: 2,
|
|
858
871
|
spaceAbove: 0,
|
|
859
872
|
spaceBelow: 0,
|
|
860
|
-
|
|
861
|
-
staffBackgroundColor: "white",
|
|
862
|
-
currentBeatUIColor: "#24ff7450",
|
|
873
|
+
svgAutoFill: !0,
|
|
863
874
|
...t
|
|
864
|
-
}, this.rendererInstance = new
|
|
875
|
+
}, this.rendererInstance = new O(e, {
|
|
865
876
|
width: this.options.width,
|
|
866
877
|
height: 100,
|
|
867
878
|
scale: this.options.scale,
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
useGlyphs: mt
|
|
879
|
+
useGlyphs: Lt,
|
|
880
|
+
svgAutoFill: this.options.svgAutoFill
|
|
871
881
|
});
|
|
872
|
-
const s = this.rendererInstance.rootSvgElement;
|
|
873
|
-
|
|
882
|
+
const s = this.rendererInstance.rootSvgElement, r = this.rendererInstance.createLayer("staff"), n = this.rendererInstance.createLayer("notes");
|
|
883
|
+
this.rendererInstance.createLayer("ui");
|
|
884
|
+
let o = "TIME_4";
|
|
874
885
|
switch (this.options.topNumber) {
|
|
875
886
|
case 3:
|
|
876
|
-
|
|
887
|
+
o = "TIME_3";
|
|
877
888
|
break;
|
|
878
889
|
case 4:
|
|
879
|
-
|
|
890
|
+
o = "TIME_4";
|
|
880
891
|
break;
|
|
881
892
|
default:
|
|
882
893
|
throw new Error(`Time signature ${this.options.topNumber} not supported. Please use either 3 or 4.`);
|
|
883
894
|
}
|
|
884
895
|
if (this.options.barsCount < 1 || this.options.barsCount > 3) throw new Error(`Bars count ${this.options.barsCount} not supported. Please use 1 - 3`);
|
|
885
896
|
if (this.options.spaceAbove) {
|
|
886
|
-
const
|
|
887
|
-
this.rendererInstance.addTotalRootSvgYOffset(
|
|
897
|
+
const E = this.options.spaceAbove * 10;
|
|
898
|
+
this.rendererInstance.addTotalRootSvgYOffset(E);
|
|
888
899
|
}
|
|
889
900
|
if (this.options.spaceBelow) {
|
|
890
|
-
let
|
|
891
|
-
this.rendererInstance.addTotalRootSvgHeight(
|
|
901
|
+
let E = this.options.spaceBelow * 10;
|
|
902
|
+
this.rendererInstance.addTotalRootSvgHeight(E);
|
|
892
903
|
}
|
|
893
|
-
|
|
894
|
-
this.rendererInstance.
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
this.rendererInstance.getLayerByName("notes").setAttribute("transform", `translate(38, ${A})`), this.rendererInstance.applySizingToRootSvg(), this.rendererInstance.commitElementsToDOM(s);
|
|
904
|
+
this.rendererInstance.addTotalRootSvgHeight(_ * 2);
|
|
905
|
+
const i = this.rendererInstance.createGroup("time-signature");
|
|
906
|
+
r.appendChild(i);
|
|
907
|
+
const a = _ - Q;
|
|
908
|
+
this.rendererInstance.drawGlyph(o, i), this.rendererInstance.drawGlyph("TIME_4", i, { yOffset: Q }), i.setAttribute("transform", `translate(0, ${a})`);
|
|
909
|
+
let h = this.options.width - 38;
|
|
910
|
+
this.options.barsCount > 1 && (h -= (this.options.barsCount - 1) * D), h -= J, this.rendererInstance.drawLine(0, _, this.options.width - J, _, r), this.barSpacing = h / this.options.barsCount, this.quarterNoteSpacing = Math.round(this.barSpacing / this.options.topNumber), this.maxBeatCount = this.options.barsCount * this.options.topNumber;
|
|
911
|
+
let d = this.barSpacing + 38;
|
|
912
|
+
const c = _ / 2, f = _ + c;
|
|
913
|
+
for (let E = 0; E < this.options.barsCount - 1; E++)
|
|
914
|
+
this.rendererInstance.drawLine(d, c, d, f, r), d += this.barSpacing;
|
|
915
|
+
n.setAttribute("transform", `translate(38, ${_})`), this.rendererInstance.applySizingToRootSvg(), this.rendererInstance.commitElementsToDOM(s);
|
|
906
916
|
}
|
|
907
917
|
createBeatUIElement() {
|
|
908
|
-
const e = this.rendererInstance.
|
|
918
|
+
const e = this.rendererInstance.getLayer("ui");
|
|
919
|
+
if (!e) throw new Error("BeatUI Error: Failed to retrieve ui layer");
|
|
909
920
|
this.currentBeatUIElement = this.rendererInstance.drawRect(
|
|
910
921
|
this.quarterNoteSpacing / 2,
|
|
911
|
-
|
|
922
|
+
_ * 2,
|
|
912
923
|
e,
|
|
913
924
|
{
|
|
914
|
-
x:
|
|
915
|
-
fill:
|
|
925
|
+
x: H,
|
|
926
|
+
fill: "rgba(0,255,40,0.4)",
|
|
927
|
+
classes: "rhythm-current-beat"
|
|
916
928
|
}
|
|
917
929
|
);
|
|
918
930
|
}
|
|
919
931
|
handleNewBar() {
|
|
920
|
-
this.noteCursorX +=
|
|
932
|
+
this.noteCursorX += D;
|
|
921
933
|
}
|
|
922
934
|
// Translates group, returns cursor increment amount
|
|
923
935
|
translateGroupByDuration(e, t) {
|
|
@@ -976,20 +988,19 @@ class yt {
|
|
|
976
988
|
let s = e;
|
|
977
989
|
for (; s > 0; ) {
|
|
978
990
|
const r = this.rendererInstance.createGroup("rest");
|
|
979
|
-
let
|
|
980
|
-
s -
|
|
991
|
+
let n = 0;
|
|
992
|
+
s - I.h >= 0 ? (this.rendererInstance.drawGlyph("REST_HALF", r), n = I.h) : s - I.q >= 0 ? (this.rendererInstance.drawGlyph("REST_QUARTER", r), n = I.q) : (this.rendererInstance.drawGlyph("REST_EIGHTH", r), n = I.e), s -= n, this.currentBeatCount += n, r.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += n * this.quarterNoteSpacing, t.push(r);
|
|
981
993
|
}
|
|
982
994
|
return t;
|
|
983
995
|
}
|
|
984
996
|
renderBeamRect(e, t, s, r) {
|
|
985
997
|
this.rendererInstance.drawRect(
|
|
986
998
|
e - t,
|
|
987
|
-
|
|
999
|
+
Ct,
|
|
988
1000
|
s,
|
|
989
1001
|
{
|
|
990
1002
|
x: 10,
|
|
991
|
-
y: -28 + (r ?? 0)
|
|
992
|
-
fill: this.options.staffColor
|
|
1003
|
+
y: -28 + (r ?? 0)
|
|
993
1004
|
}
|
|
994
1005
|
);
|
|
995
1006
|
}
|
|
@@ -1012,24 +1023,26 @@ class yt {
|
|
|
1012
1023
|
* drawNote("w");
|
|
1013
1024
|
*/
|
|
1014
1025
|
drawNote(e) {
|
|
1015
|
-
const t = Array.isArray(e) ? e : [e], s = this.rendererInstance.
|
|
1016
|
-
|
|
1017
|
-
|
|
1026
|
+
const t = Array.isArray(e) ? e : [e], s = this.rendererInstance.getLayer("notes");
|
|
1027
|
+
if (!s) throw new Error("DrawNote Error: Failed to retrieve notes layer");
|
|
1028
|
+
const r = [];
|
|
1029
|
+
for (const n of t) {
|
|
1030
|
+
let o = "w";
|
|
1018
1031
|
try {
|
|
1019
|
-
|
|
1020
|
-
} catch (
|
|
1021
|
-
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s),
|
|
1032
|
+
o = v(n);
|
|
1033
|
+
} catch (c) {
|
|
1034
|
+
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), c;
|
|
1022
1035
|
}
|
|
1023
|
-
const i =
|
|
1036
|
+
const i = I[o];
|
|
1024
1037
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
1025
1038
|
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
|
|
1026
1039
|
this.checkAndCreateNewBar();
|
|
1027
1040
|
const a = this.checkAndFillBarWithRests(i);
|
|
1028
|
-
a && a.forEach((
|
|
1029
|
-
r.push(
|
|
1041
|
+
a && a.forEach((c) => {
|
|
1042
|
+
r.push(c), this.noteEntries.push(c);
|
|
1030
1043
|
});
|
|
1031
|
-
const
|
|
1032
|
-
this.noteCursorX +=
|
|
1044
|
+
const h = this.rendererInstance.createGroup("note"), d = this.translateGroupByDuration(i, h);
|
|
1045
|
+
this.noteCursorX += d, this.currentBeatCount += i, this.renderNote(o, h), r.push(h), this.noteEntries.push(h);
|
|
1033
1046
|
}
|
|
1034
1047
|
this.rendererInstance.commitElementsToDOM(r, s);
|
|
1035
1048
|
}
|
|
@@ -1052,22 +1065,24 @@ class yt {
|
|
|
1052
1065
|
* drawRest("w");
|
|
1053
1066
|
*/
|
|
1054
1067
|
drawRest(e) {
|
|
1055
|
-
const t = Array.isArray(e) ? e : [e], s = this.rendererInstance.
|
|
1056
|
-
|
|
1057
|
-
|
|
1068
|
+
const t = Array.isArray(e) ? e : [e], s = this.rendererInstance.getLayer("notes");
|
|
1069
|
+
if (!s) throw new Error("DrawRest Error: Failed to retrieve notes layer");
|
|
1070
|
+
const r = [];
|
|
1071
|
+
for (const n of t) {
|
|
1072
|
+
let o = "w";
|
|
1058
1073
|
try {
|
|
1059
|
-
|
|
1060
|
-
} catch (
|
|
1061
|
-
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s),
|
|
1074
|
+
o = v(n);
|
|
1075
|
+
} catch (c) {
|
|
1076
|
+
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), c;
|
|
1062
1077
|
}
|
|
1063
|
-
const i = this.rendererInstance.createGroup("rest"), a =
|
|
1078
|
+
const i = this.rendererInstance.createGroup("rest"), a = I[o], h = a * this.quarterNoteSpacing;
|
|
1064
1079
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
1065
1080
|
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
|
|
1066
1081
|
this.checkAndCreateNewBar();
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
r.push(
|
|
1070
|
-
}), this.renderRest(
|
|
1082
|
+
const d = this.checkAndFillBarWithRests(a);
|
|
1083
|
+
d && d.forEach((c) => {
|
|
1084
|
+
r.push(c), this.noteEntries.push(c);
|
|
1085
|
+
}), this.renderRest(o, i), i.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += h, this.currentBeatCount += a, r.push(i), this.noteEntries.push(i);
|
|
1071
1086
|
}
|
|
1072
1087
|
this.rendererInstance.commitElementsToDOM(r, s);
|
|
1073
1088
|
}
|
|
@@ -1094,13 +1109,15 @@ class yt {
|
|
|
1094
1109
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
1095
1110
|
throw new Error("Max beat count reached. Can't add additional beamed note.");
|
|
1096
1111
|
let s = "s";
|
|
1097
|
-
e === "s" ? s = "s" : s =
|
|
1098
|
-
const r = this.rendererInstance.
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1112
|
+
e === "s" ? s = "s" : s = v(e), this.checkAndCreateNewBar();
|
|
1113
|
+
const r = this.rendererInstance.getLayer("notes");
|
|
1114
|
+
if (!r) throw new Error("drawBeamedNotes Error: Failed to retrieve notes layer");
|
|
1115
|
+
const n = I[s], o = n * this.quarterNoteSpacing, i = this.options.topNumber - this.currentBeatCount % this.options.topNumber, a = Math.min(t, i / n), h = this.rendererInstance.createGroup("beamed-note");
|
|
1116
|
+
h.setAttribute("transform", `translate(${this.noteCursorX}, 0)`);
|
|
1117
|
+
let d = 0;
|
|
1118
|
+
for (let c = 0; c < a; c++)
|
|
1119
|
+
this.rendererInstance.drawGlyph("NOTE_HEAD_QUARTER", h, { xOffset: d }), this.drawStem(h, d), d += o, this.currentBeatCount += n;
|
|
1120
|
+
this.renderBeamRect(d, o, h), e === "s" && this.renderBeamRect(d, o, h, Nt), this.noteCursorX += d, this.noteEntries.push(h), this.rendererInstance.commitElementsToDOM(h, r);
|
|
1104
1121
|
}
|
|
1105
1122
|
/**
|
|
1106
1123
|
* Will increment the UI showing the current beat in quarters. Once exceeded, must be reset with `resetCurrentBeatUI()`
|
|
@@ -1111,21 +1128,21 @@ class yt {
|
|
|
1111
1128
|
this.currentBeatUIElement.setAttribute("display", "none");
|
|
1112
1129
|
return;
|
|
1113
1130
|
}
|
|
1114
|
-
this.currentBeatUIElement?.getAttribute("display") === "none" && this.currentBeatUIElement.removeAttribute("display"), this.currentBeatUICount++, this.currentBeatUICount > this.options.topNumber && this.currentBeatUICount % this.options.topNumber === 1 && (this.currentBeatUIXPos +=
|
|
1131
|
+
this.currentBeatUIElement?.getAttribute("display") === "none" && this.currentBeatUIElement.removeAttribute("display"), this.currentBeatUICount++, this.currentBeatUICount > this.options.topNumber && this.currentBeatUICount % this.options.topNumber === 1 && (this.currentBeatUIXPos += D), this.currentBeatUICount > 1 && (this.currentBeatUIXPos += this.quarterNoteSpacing), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString());
|
|
1115
1132
|
}
|
|
1116
1133
|
/**
|
|
1117
1134
|
* Resets the ui showing the current beat value.
|
|
1118
1135
|
* @returns void
|
|
1119
1136
|
*/
|
|
1120
1137
|
resetCurrentBeatUI() {
|
|
1121
|
-
this.currentBeatUICount = 0, this.currentBeatUIXPos =
|
|
1138
|
+
this.currentBeatUICount = 0, this.currentBeatUIXPos = H, this.currentBeatUIElement && (this.currentBeatUIElement.setAttribute("display", "none"), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString()));
|
|
1122
1139
|
}
|
|
1123
1140
|
/**
|
|
1124
1141
|
* Clears staff of notes and resets internal positioning.
|
|
1125
1142
|
* @returns void
|
|
1126
1143
|
*/
|
|
1127
1144
|
clearAllNotes() {
|
|
1128
|
-
this.noteCursorX = 0, this.currentBeatCount = 0, this.rendererInstance.
|
|
1145
|
+
this.noteCursorX = 0, this.currentBeatCount = 0, this.rendererInstance.getLayer("notes")?.replaceChildren(), this.noteEntries = [];
|
|
1129
1146
|
}
|
|
1130
1147
|
/**
|
|
1131
1148
|
* Removes the root svg element and cleans up arrays.
|
|
@@ -1135,7 +1152,7 @@ class yt {
|
|
|
1135
1152
|
this.noteEntries = [], this.rendererInstance.destroy();
|
|
1136
1153
|
}
|
|
1137
1154
|
}
|
|
1138
|
-
const
|
|
1155
|
+
const Rt = [
|
|
1139
1156
|
"CLEF_TREBLE",
|
|
1140
1157
|
"CLEF_BASS",
|
|
1141
1158
|
"CLEF_ALTO",
|
|
@@ -1149,8 +1166,8 @@ const Ct = [
|
|
|
1149
1166
|
"ACCIDENTAL_NATURAL",
|
|
1150
1167
|
"ACCIDENTAL_DOUBLE_SHARP",
|
|
1151
1168
|
"ACCIDENTAL_DOUBLE_FLAT"
|
|
1152
|
-
],
|
|
1153
|
-
class
|
|
1169
|
+
], y = 60, wt = y;
|
|
1170
|
+
class Ft {
|
|
1154
1171
|
svgRendererInstance;
|
|
1155
1172
|
strategyInstance;
|
|
1156
1173
|
noteRendererInstance;
|
|
@@ -1174,49 +1191,48 @@ class wt {
|
|
|
1174
1191
|
staffType: "treble",
|
|
1175
1192
|
spaceAbove: 0,
|
|
1176
1193
|
spaceBelow: 0,
|
|
1177
|
-
|
|
1178
|
-
staffBackgroundColor: "transparent",
|
|
1194
|
+
svgAutoFill: !0,
|
|
1179
1195
|
...t
|
|
1180
|
-
}, this.svgRendererInstance = new
|
|
1196
|
+
}, this.svgRendererInstance = new O(e, {
|
|
1181
1197
|
width: this.options.width,
|
|
1182
1198
|
height: 100,
|
|
1183
1199
|
scale: this.options.scale,
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
useGlyphs: Ct
|
|
1200
|
+
useGlyphs: Rt,
|
|
1201
|
+
svgAutoFill: this.options.svgAutoFill
|
|
1187
1202
|
});
|
|
1188
1203
|
const s = this.svgRendererInstance.rootSvgElement;
|
|
1189
1204
|
switch (this.options.staffType) {
|
|
1190
1205
|
case "grand":
|
|
1191
|
-
this.strategyInstance = new
|
|
1206
|
+
this.strategyInstance = new nt(this.svgRendererInstance, "grand");
|
|
1192
1207
|
break;
|
|
1193
1208
|
case "bass":
|
|
1194
|
-
this.strategyInstance = new
|
|
1209
|
+
this.strategyInstance = new S(this.svgRendererInstance, "bass");
|
|
1195
1210
|
break;
|
|
1196
1211
|
case "treble":
|
|
1197
|
-
this.strategyInstance = new
|
|
1212
|
+
this.strategyInstance = new S(this.svgRendererInstance, "treble");
|
|
1198
1213
|
break;
|
|
1199
1214
|
case "alto":
|
|
1200
|
-
this.strategyInstance = new
|
|
1215
|
+
this.strategyInstance = new S(this.svgRendererInstance, "alto");
|
|
1201
1216
|
break;
|
|
1202
1217
|
default:
|
|
1203
1218
|
throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
|
|
1204
1219
|
}
|
|
1205
|
-
if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new
|
|
1206
|
-
const
|
|
1207
|
-
this.svgRendererInstance.addTotalRootSvgYOffset(
|
|
1220
|
+
if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new ot(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
|
|
1221
|
+
const n = this.options.spaceAbove * 10;
|
|
1222
|
+
this.svgRendererInstance.addTotalRootSvgYOffset(n);
|
|
1208
1223
|
}
|
|
1209
1224
|
if (this.options.spaceBelow) {
|
|
1210
|
-
let
|
|
1211
|
-
this.options.staffType === "grand" && (
|
|
1225
|
+
let n = this.options.spaceBelow * 10;
|
|
1226
|
+
this.options.staffType === "grand" && (n -= 10 / 2), this.svgRendererInstance.addTotalRootSvgHeight(n);
|
|
1212
1227
|
}
|
|
1213
|
-
|
|
1228
|
+
const r = this.svgRendererInstance.createLayer("notes");
|
|
1229
|
+
this.notesLayer = r, r.classList.add("vs-scrolling-notes-layer"), this.noteStartX = 38 + this.options.noteStartX, r.setAttribute("transform", `translate(${this.noteStartX}, 0)`), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
|
|
1214
1230
|
}
|
|
1215
1231
|
renderFirstNoteGroups() {
|
|
1216
|
-
const e = this.options.width - this.options.noteStartX +
|
|
1232
|
+
const e = this.options.width - this.options.noteStartX + wt;
|
|
1217
1233
|
for (; this.noteBuffer.length > 0 && this.noteCursorX < e; )
|
|
1218
|
-
this.renderNextNote(), this.noteCursorX +=
|
|
1219
|
-
this.activeEntries.length > 1 && (this.noteCursorX -=
|
|
1234
|
+
this.renderNextNote(), this.noteCursorX += y;
|
|
1235
|
+
this.activeEntries.length > 1 && (this.noteCursorX -= y);
|
|
1220
1236
|
}
|
|
1221
1237
|
renderNextNote() {
|
|
1222
1238
|
if (this.noteBuffer.length < 1) return;
|
|
@@ -1275,7 +1291,7 @@ class wt {
|
|
|
1275
1291
|
return;
|
|
1276
1292
|
}
|
|
1277
1293
|
this.activeEntries.forEach((t) => {
|
|
1278
|
-
t.xPos -=
|
|
1294
|
+
t.xPos -= y, t.noteWrapper.style.transform = `translate(${t.xPos}px, 0px)`;
|
|
1279
1295
|
});
|
|
1280
1296
|
const e = this.activeEntries[0];
|
|
1281
1297
|
e.xPos <= 0 && (this.notesLayer.removeChild(e.noteWrapper), this.activeEntries.shift()), this.renderNextNote();
|
|
@@ -1295,37 +1311,36 @@ class wt {
|
|
|
1295
1311
|
this.svgRendererInstance.destroy(), this.activeEntries = [], this.noteBuffer = [];
|
|
1296
1312
|
}
|
|
1297
1313
|
}
|
|
1298
|
-
const
|
|
1299
|
-
function
|
|
1314
|
+
const yt = /^[xX\da-zA-Z]+$/, Ot = /^[\d]+$/;
|
|
1315
|
+
function tt(l, e) {
|
|
1300
1316
|
const t = [];
|
|
1301
|
-
for (let s = 0; s <
|
|
1317
|
+
for (let s = 0; s < l.length; s++)
|
|
1302
1318
|
t.push({
|
|
1303
|
-
fret:
|
|
1319
|
+
fret: l[s],
|
|
1304
1320
|
finger: e[s]
|
|
1305
1321
|
});
|
|
1306
1322
|
return t;
|
|
1307
1323
|
}
|
|
1308
|
-
function
|
|
1309
|
-
const t =
|
|
1324
|
+
function et(l, e) {
|
|
1325
|
+
const t = l.filter((n) => n.toLowerCase() !== "x" && n !== "0").map((n) => parseInt(n, 36));
|
|
1310
1326
|
if (t.length === 0) return 1;
|
|
1311
1327
|
const s = Math.min(...t);
|
|
1312
1328
|
return Math.max(...t) <= e ? 1 : s;
|
|
1313
1329
|
}
|
|
1314
|
-
function
|
|
1315
|
-
if (!
|
|
1316
|
-
return
|
|
1330
|
+
function P(l) {
|
|
1331
|
+
if (!yt.test(l)) throw new Error("Invalid frets string. Only 'x', 'X', and digits are allowed.");
|
|
1332
|
+
return l.split("");
|
|
1317
1333
|
}
|
|
1318
|
-
function
|
|
1319
|
-
if (!
|
|
1320
|
-
return
|
|
1334
|
+
function B(l) {
|
|
1335
|
+
if (!Ot.test(l)) throw new Error("Invalid fingers string. Only digits are allowed.");
|
|
1336
|
+
return l.split("");
|
|
1321
1337
|
}
|
|
1322
|
-
class
|
|
1338
|
+
class Gt {
|
|
1323
1339
|
svgRendererInstance;
|
|
1324
1340
|
options;
|
|
1325
1341
|
chordEntries = [];
|
|
1326
1342
|
cursorX = 0;
|
|
1327
1343
|
cursorY = 0;
|
|
1328
|
-
// Fixed per-instance sizing, derived once from options
|
|
1329
1344
|
diagramWidth;
|
|
1330
1345
|
diagramHeight;
|
|
1331
1346
|
gridTopY;
|
|
@@ -1339,58 +1354,78 @@ class Ot {
|
|
|
1339
1354
|
*/
|
|
1340
1355
|
constructor(e, t) {
|
|
1341
1356
|
if (this.options = {
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
stringCount: st,
|
|
1345
|
-
fretCount: rt,
|
|
1357
|
+
stringCount: it,
|
|
1358
|
+
fretCount: at,
|
|
1346
1359
|
stringLabels: [],
|
|
1347
|
-
|
|
1348
|
-
|
|
1360
|
+
inlineChordsAmount: 2,
|
|
1361
|
+
centerChords: !0,
|
|
1362
|
+
scale: 1,
|
|
1363
|
+
svgAutoFill: !0,
|
|
1349
1364
|
...t
|
|
1350
|
-
}, this.diagramWidth = (this.options.stringCount - 1) *
|
|
1365
|
+
}, this.diagramWidth = (this.options.stringCount - 1) * T, this.diagramHeight = this.options.fretCount * m, this.gridTopY = k + W, this.rowHeight = this.gridTopY + this.diagramHeight + $, t?.width ? this.options.width = t.width : this.options.width = (this.diagramWidth + L) * this.options.inlineChordsAmount, this.options.stringLabels.length >= 1 && (this.rowHeight += ht + ct), this.diagramWidth > this.options.width)
|
|
1351
1366
|
throw new Error(`The configured width (${this.options.width}) is too small to fit a single ${this.options.stringCount}-string diagram (needs at least ${this.diagramWidth}). Increase 'width'.`);
|
|
1352
|
-
this.svgRendererInstance = new
|
|
1367
|
+
this.svgRendererInstance = new O(e, {
|
|
1353
1368
|
width: this.options.width,
|
|
1354
1369
|
height: 100,
|
|
1355
1370
|
scale: this.options.scale,
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
useGlyphs: []
|
|
1371
|
+
useGlyphs: [],
|
|
1372
|
+
svgAutoFill: this.options.svgAutoFill
|
|
1359
1373
|
});
|
|
1360
1374
|
const s = this.svgRendererInstance.rootSvgElement;
|
|
1361
1375
|
this.svgRendererInstance.setTotalRootSvgHeight(this.rowHeight), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
|
|
1362
1376
|
}
|
|
1363
|
-
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
const
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1377
|
+
getDotsToHide(e) {
|
|
1378
|
+
const t = /* @__PURE__ */ new Set();
|
|
1379
|
+
return e && e.forEach((s) => {
|
|
1380
|
+
const r = s.fromString - 1, n = s.toString - 1;
|
|
1381
|
+
for (let o = r; o <= n; o++)
|
|
1382
|
+
t.add(`${o}-${s.fret}`);
|
|
1383
|
+
}), t;
|
|
1384
|
+
}
|
|
1385
|
+
drawFretDots(e, t, s, r, n) {
|
|
1386
|
+
const o = this.gridTopY - W / 2;
|
|
1387
|
+
for (let i = 0; i < this.options.stringCount; i++) {
|
|
1388
|
+
const a = i * T, h = e[i].toLowerCase(), d = t[i], c = this.svgRendererInstance.createGroup("");
|
|
1389
|
+
if (h === "x")
|
|
1390
|
+
c.classList.add("vs-guitar-fret-muted"), c.setAttribute("transform", `translate(${a}, ${o})`), this.svgRendererInstance.drawLine(-p, -p, p, p, c, {
|
|
1391
|
+
strokeWidth: 1.5
|
|
1392
|
+
}), this.svgRendererInstance.drawLine(-p, p, p, -p, c, {
|
|
1393
|
+
strokeWidth: 1.5
|
|
1394
|
+
});
|
|
1395
|
+
else if (h === "0")
|
|
1396
|
+
c.classList.add("vs-guitar-fret-open"), c.setAttribute("transform", `translate(${a}, ${o})`), this.svgRendererInstance.drawCircle(0, 0, p, c, { filled: !1 });
|
|
1371
1397
|
else {
|
|
1372
|
-
const
|
|
1373
|
-
if (
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
}
|
|
1398
|
+
const f = parseInt(h, 36);
|
|
1399
|
+
if (r.has(`${i}-${f}`))
|
|
1400
|
+
continue;
|
|
1401
|
+
c.classList.add("vs-guitar-fret-dot");
|
|
1402
|
+
const E = f - s + 1;
|
|
1403
|
+
if (E < 1 || E > this.options.fretCount)
|
|
1404
|
+
throw new Error(`Fret ${h} on string ${i + 1} is outside the visible range (${s}-${s + this.options.fretCount - 1}). Adjust 'startFret' or 'fretCount'.`);
|
|
1405
|
+
const g = this.gridTopY + (E - 0.5) * m;
|
|
1406
|
+
c.setAttribute("transform", `translate(${a}, ${g})`), this.svgRendererInstance.drawCircle(0, 0, u, c, {
|
|
1407
|
+
classes: "guitar-fret-circle"
|
|
1408
|
+
}), d !== "0" && this.svgRendererInstance.drawText(d, 0, -1, c, {
|
|
1409
|
+
fontSize: R,
|
|
1410
|
+
baseline: "central",
|
|
1411
|
+
classes: ["guitar-text", "guitar-fret-text"]
|
|
1412
|
+
});
|
|
1383
1413
|
}
|
|
1414
|
+
n.appendChild(c);
|
|
1384
1415
|
}
|
|
1385
1416
|
}
|
|
1386
1417
|
drawStringLabels(e, t) {
|
|
1387
1418
|
if (!t || t.length === 0) return;
|
|
1388
|
-
const s =
|
|
1389
|
-
for (let
|
|
1390
|
-
const
|
|
1391
|
-
if (!
|
|
1392
|
-
const i =
|
|
1393
|
-
this.svgRendererInstance.
|
|
1419
|
+
const s = u, r = this.rowHeight - s - $;
|
|
1420
|
+
for (let n = 0; n < this.options.stringCount; n++) {
|
|
1421
|
+
const o = t[n];
|
|
1422
|
+
if (!o) continue;
|
|
1423
|
+
const i = n * T;
|
|
1424
|
+
this.svgRendererInstance.drawText(o, i, r, e, {
|
|
1425
|
+
fontSize: R,
|
|
1426
|
+
baseline: "central",
|
|
1427
|
+
classes: "guitar-text"
|
|
1428
|
+
});
|
|
1394
1429
|
}
|
|
1395
1430
|
}
|
|
1396
1431
|
// Passed in 'from' and 'to' index values are zero-based
|
|
@@ -1400,64 +1435,82 @@ class Ot {
|
|
|
1400
1435
|
throw new Error(`Barre fret error: Invalid string range ${t.fromIndex + 1}:${t.toIndex + 1}.`);
|
|
1401
1436
|
if (s < t.startingFret || s > t.startingFret + (this.options.fretCount - 1))
|
|
1402
1437
|
throw new Error(`Barre fret error: ${t.fret} is outside the visible range.`);
|
|
1403
|
-
const r = s - t.startingFret, o = t.fromIndex *
|
|
1404
|
-
|
|
1405
|
-
x: o -
|
|
1406
|
-
y:
|
|
1407
|
-
rx:
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
}
|
|
1438
|
+
const r = this.svgRendererInstance.createGroup("guitar-group-barre"), n = s - t.startingFret, o = t.fromIndex * T, i = t.toIndex * T, a = u * 2, h = i - o + a, d = this.gridTopY + m * n;
|
|
1439
|
+
r.setAttribute("transform", `translate(0, ${d})`), this.svgRendererInstance.drawRect(h, a, r, {
|
|
1440
|
+
x: o - u,
|
|
1441
|
+
y: u / 2,
|
|
1442
|
+
rx: u,
|
|
1443
|
+
classes: "guitar-barre"
|
|
1444
|
+
}), t.finger && this.svgRendererInstance.drawText(t.finger.toString(), (o + i) / 2, u * 1.5 - 1, r, {
|
|
1445
|
+
fontSize: R,
|
|
1446
|
+
baseline: "central",
|
|
1447
|
+
classes: ["guitar-text", "guitar-fret-text"]
|
|
1448
|
+
}), e.appendChild(r);
|
|
1415
1449
|
}
|
|
1416
1450
|
// Builds a single chord diagram's group element: grid, nut/fret-position label, and per-string markers.
|
|
1417
1451
|
renderChordDiagram(e, t, s) {
|
|
1418
|
-
const r = this.svgRendererInstance.createGroup("chord"),
|
|
1419
|
-
s?.label && this.svgRendererInstance.drawText(s.label, this.diagramWidth / 2,
|
|
1420
|
-
fontSize:
|
|
1421
|
-
fontWeight: "bold"
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
const i = this.gridTopY + n * T;
|
|
1425
|
-
n === 0 && o === 1 ? this.svgRendererInstance.drawRect(this.diagramWidth + U * 2, F, r, {
|
|
1426
|
-
x: -U,
|
|
1427
|
-
y: i - F / 2
|
|
1428
|
-
}) : this.svgRendererInstance.drawLine(0, i, this.diagramWidth, i, r);
|
|
1429
|
-
}
|
|
1430
|
-
o > 1 && this.svgRendererInstance.drawText(`${o}fr`, this.diagramWidth + ht, this.gridTopY + T / 2 + 4, r, {
|
|
1452
|
+
const r = this.svgRendererInstance.createGroup("chord"), n = s?.startFret ?? 1;
|
|
1453
|
+
s?.label && this.svgRendererInstance.drawText(s.label, this.diagramWidth / 2, k - 5, r, {
|
|
1454
|
+
fontSize: st,
|
|
1455
|
+
fontWeight: "bold",
|
|
1456
|
+
classes: ["guitar-text", "guitar-diagram-label"]
|
|
1457
|
+
}), n > 1 && this.svgRendererInstance.drawText(`${n}fr`, this.diagramWidth + u + Et, this.gridTopY + m / 2 + 4, r, {
|
|
1431
1458
|
anchor: "start",
|
|
1432
|
-
fontSize:
|
|
1433
|
-
fontWeight: "bold"
|
|
1459
|
+
fontSize: R,
|
|
1460
|
+
fontWeight: "bold",
|
|
1461
|
+
classes: ["guitar-text", "guitar-fret-label"]
|
|
1434
1462
|
});
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1463
|
+
const o = this.svgRendererInstance.createGroup("guitar-group-fret-lines");
|
|
1464
|
+
for (let c = 0; c <= this.options.fretCount; c++) {
|
|
1465
|
+
const f = this.gridTopY + c * m;
|
|
1466
|
+
c === 0 && n === 1 ? this.svgRendererInstance.drawRect(this.diagramWidth + ft, x, o, {
|
|
1467
|
+
x: -0.5,
|
|
1468
|
+
y: f - x / 2
|
|
1469
|
+
}) : this.svgRendererInstance.drawLine(0, f, this.diagramWidth, f, o);
|
|
1470
|
+
}
|
|
1471
|
+
r.appendChild(o);
|
|
1472
|
+
const i = this.svgRendererInstance.createGroup("guitar-group-string-lines");
|
|
1473
|
+
for (let c = 0; c < this.options.stringCount; c++) {
|
|
1474
|
+
const f = c * T;
|
|
1475
|
+
this.svgRendererInstance.drawLine(f, this.gridTopY, f, this.gridTopY + this.diagramHeight, i);
|
|
1438
1476
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1477
|
+
r.appendChild(i);
|
|
1478
|
+
const a = this.svgRendererInstance.createGroup("guitar-group-fret-dots"), h = this.getDotsToHide(s?.barres);
|
|
1479
|
+
this.drawFretDots(e, t, n, h, a), s?.barres?.forEach((c) => {
|
|
1480
|
+
this.drawBarre(a, {
|
|
1481
|
+
fromIndex: c.fromString - 1,
|
|
1482
|
+
toIndex: c.toString - 1,
|
|
1483
|
+
fret: c.fret,
|
|
1484
|
+
finger: c.finger,
|
|
1485
|
+
startingFret: n
|
|
1446
1486
|
});
|
|
1447
|
-
}), r;
|
|
1487
|
+
}), r.appendChild(a);
|
|
1488
|
+
const d = this.svgRendererInstance.createGroup("guitar-group-string-labels");
|
|
1489
|
+
return this.options.stringLabels.length >= 1 && this.drawStringLabels(d, this.options.stringLabels), r.appendChild(d), r;
|
|
1448
1490
|
}
|
|
1449
1491
|
// Repositions every existing chord diagram from scratch, in order, wrapping rows as needed.
|
|
1450
|
-
// Called after any add/remove/change so the layout never has to track
|
|
1492
|
+
// Called after any add/remove/change so the layout never has to track pos.
|
|
1451
1493
|
relayoutChords() {
|
|
1452
|
-
if (this.
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1494
|
+
if (this.chordEntries.length === 0) {
|
|
1495
|
+
this.svgRendererInstance.setTotalRootSvgHeight(0), this.svgRendererInstance.applySizingToRootSvg();
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
const e = [];
|
|
1499
|
+
let t = [], s = u;
|
|
1500
|
+
this.chordEntries.forEach((n, o) => {
|
|
1501
|
+
o > 0 && s + this.diagramWidth > this.options.width && (e.push(t), t = [], s = u), t.push(n), s += this.diagramWidth + L;
|
|
1502
|
+
}), t.length > 0 && e.push(t), this.cursorY = dt, e.forEach((n, o) => {
|
|
1503
|
+
if (o > 0 && (this.cursorY += this.rowHeight + lt), this.options.centerChords) {
|
|
1504
|
+
const i = n.length * this.diagramWidth + (n.length - 1) * L;
|
|
1505
|
+
this.cursorX = this.options.width / 2 - i / 2;
|
|
1506
|
+
} else
|
|
1507
|
+
this.cursorX = u;
|
|
1508
|
+
n.forEach((i) => {
|
|
1509
|
+
i.xPos = this.cursorX, i.yPos = this.cursorY, i.gElement.setAttribute("transform", `translate(${this.cursorX}, ${this.cursorY})`), this.cursorX += this.diagramWidth + L;
|
|
1458
1510
|
});
|
|
1459
|
-
|
|
1460
|
-
this.
|
|
1511
|
+
});
|
|
1512
|
+
const r = this.cursorY + this.rowHeight;
|
|
1513
|
+
this.svgRendererInstance.setTotalRootSvgHeight(r), this.svgRendererInstance.applySizingToRootSvg();
|
|
1461
1514
|
}
|
|
1462
1515
|
/**
|
|
1463
1516
|
* Adds a new chord diagram. Diagrams are placed left-to-right in the order added, wrapping to a
|
|
@@ -1483,14 +1536,14 @@ class Ot {
|
|
|
1483
1536
|
* guitarChord.addChord("x32010", "032010", { label: "C" });
|
|
1484
1537
|
*/
|
|
1485
1538
|
addChord(e, t, s) {
|
|
1486
|
-
const r =
|
|
1539
|
+
const r = P(e), n = B(t);
|
|
1487
1540
|
if (r.length !== this.options.stringCount) throw new Error("Invalid number of frets provided, entries should be equal to number of strings in diagram.");
|
|
1488
|
-
if (
|
|
1489
|
-
const
|
|
1490
|
-
return this.svgRendererInstance.commitElementsToDOM(
|
|
1491
|
-
gElement:
|
|
1492
|
-
strings:
|
|
1493
|
-
...
|
|
1541
|
+
if (n.length !== this.options.stringCount) throw new Error("Invalid number of fingers provided, entries should be equal to number of strings in diagram.");
|
|
1542
|
+
const o = s?.startFret ?? et(r, this.options.fretCount), i = s?.label, a = s?.barres, h = { startFret: o, label: i, barres: a }, d = this.renderChordDiagram(r, n, h), c = this.svgRendererInstance.parentGroupElement;
|
|
1543
|
+
return this.svgRendererInstance.commitElementsToDOM(d, c), this.chordEntries.push({
|
|
1544
|
+
gElement: d,
|
|
1545
|
+
strings: tt(r, n),
|
|
1546
|
+
...h,
|
|
1494
1547
|
xPos: 0,
|
|
1495
1548
|
yPos: 0
|
|
1496
1549
|
}), this.relayoutChords(), this.chordEntries.length - 1;
|
|
@@ -1521,14 +1574,14 @@ class Ot {
|
|
|
1521
1574
|
*/
|
|
1522
1575
|
modifyChordByIndex(e, t, s, r) {
|
|
1523
1576
|
if (s >= this.chordEntries.length || s < 0) throw new Error("Chord index was out of bounds.");
|
|
1524
|
-
const
|
|
1525
|
-
if (
|
|
1526
|
-
if (
|
|
1527
|
-
const i = this.chordEntries[s], a = r && "label" in r ? r.label === "" ? void 0 : r.label : i.label,
|
|
1528
|
-
this.svgRendererInstance.
|
|
1529
|
-
gElement:
|
|
1530
|
-
strings:
|
|
1531
|
-
...
|
|
1577
|
+
const n = P(e), o = B(t);
|
|
1578
|
+
if (n.length !== this.options.stringCount) throw new Error("Invalid number of frets provided, entries should be equal to number of strings in diagram.");
|
|
1579
|
+
if (o.length !== this.options.stringCount) throw new Error("Invalid number of fingers provided, entries should be equal to number of strings in diagram.");
|
|
1580
|
+
const i = this.chordEntries[s], a = r && "label" in r ? r.label === "" ? void 0 : r.label : i.label, h = r?.startFret ?? et(n, this.options.fretCount), d = { ...r, label: a, startFret: h }, c = this.renderChordDiagram(n, o, d);
|
|
1581
|
+
this.svgRendererInstance.parentGroupElement.replaceChild(c, i.gElement), this.chordEntries[s] = {
|
|
1582
|
+
gElement: c,
|
|
1583
|
+
strings: tt(n, o),
|
|
1584
|
+
...d,
|
|
1532
1585
|
xPos: i.xPos,
|
|
1533
1586
|
yPos: i.yPos
|
|
1534
1587
|
}, this.relayoutChords();
|
|
@@ -1548,7 +1601,7 @@ class Ot {
|
|
|
1548
1601
|
* @returns void
|
|
1549
1602
|
*/
|
|
1550
1603
|
clearAllChords() {
|
|
1551
|
-
this.svgRendererInstance.
|
|
1604
|
+
this.svgRendererInstance.parentGroupElement.replaceChildren(), this.chordEntries = [], this.cursorX = 0, this.cursorY = 0, this.svgRendererInstance.setTotalRootSvgHeight(this.rowHeight), this.svgRendererInstance.applySizingToRootSvg();
|
|
1552
1605
|
}
|
|
1553
1606
|
/**
|
|
1554
1607
|
* * Used to automatically determine options for creating barre lines.
|
|
@@ -1556,24 +1609,27 @@ class Ot {
|
|
|
1556
1609
|
* @returns GuitarBarreDef[]
|
|
1557
1610
|
*/
|
|
1558
1611
|
determineBarreOptions(e, t, s) {
|
|
1559
|
-
const r =
|
|
1612
|
+
const r = P(e), n = B(t), o = [];
|
|
1560
1613
|
return s.forEach((i) => {
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
if (
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
}
|
|
1614
|
+
if (i === 0) return;
|
|
1615
|
+
const a = [], h = {};
|
|
1616
|
+
for (let g = 0; g < r.length; g++) {
|
|
1617
|
+
if (r[g].toLowerCase() === "x") continue;
|
|
1618
|
+
const N = parseInt(r[g], 36), b = Number(n[g]);
|
|
1619
|
+
N === i && (h[b] ? h[b] += 1 : h[b] = 1, a.push(g + 1));
|
|
1568
1620
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1621
|
+
a.sort();
|
|
1622
|
+
let d = 0, c = 0;
|
|
1623
|
+
for (const [g, N] of Object.entries(h))
|
|
1624
|
+
N > c && (c = N, d = Number(g));
|
|
1625
|
+
const f = a[0], E = a.at(-1);
|
|
1626
|
+
a.length > 1 && E && o.push({
|
|
1627
|
+
fret: i,
|
|
1628
|
+
fromString: f,
|
|
1629
|
+
toString: E,
|
|
1630
|
+
finger: d
|
|
1631
|
+
});
|
|
1632
|
+
}), o;
|
|
1577
1633
|
}
|
|
1578
1634
|
/**
|
|
1579
1635
|
* Removes the root svg element and cleans up arrays.
|
|
@@ -1584,8 +1640,8 @@ class Ot {
|
|
|
1584
1640
|
}
|
|
1585
1641
|
}
|
|
1586
1642
|
export {
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1643
|
+
Gt as GuitarChord,
|
|
1644
|
+
bt as MusicStaff,
|
|
1645
|
+
vt as RhythmStaff,
|
|
1646
|
+
Ft as ScrollingStaff
|
|
1591
1647
|
};
|