@real-music-packages/web-core 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-ILIXNGPE.js +127 -0
- package/dist/chunk-ILIXNGPE.js.map +1 -0
- package/dist/index.d.ts +100 -1
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/promo.js +11 -1
- package/dist/promo.js.map +1 -1
- package/dist/scene/index.d.ts +19 -2
- package/dist/scene/index.js +169 -29
- package/dist/scene/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-N56UTMWA.js +0 -24
- package/dist/chunk-N56UTMWA.js.map +0 -1
package/dist/scene/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
STRING_COUNT,
|
|
3
|
+
buildPlacementMap,
|
|
2
4
|
intervalBySemitones
|
|
3
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-ILIXNGPE.js";
|
|
4
6
|
import {
|
|
5
7
|
getScaleDegree,
|
|
6
8
|
getStability,
|
|
@@ -666,7 +668,7 @@ function audioPlayheadLine(layout, onsetsMs, tMs, barDurMs, noteCols) {
|
|
|
666
668
|
if (alpha <= 0.02) return null;
|
|
667
669
|
return { x, y0, y1, alpha };
|
|
668
670
|
}
|
|
669
|
-
function vstackAudioPlayheadLine(layout, onsetsMs, tMs, nBars) {
|
|
671
|
+
function vstackAudioPlayheadLine(layout, onsetsMs, tMs, nBars, noteCols) {
|
|
670
672
|
const padV = 10;
|
|
671
673
|
const cols = measureColumnsFromLayout(layout.measures);
|
|
672
674
|
if (!cols.length || !onsetsMs.length || nBars <= 0) return null;
|
|
@@ -674,40 +676,59 @@ function vstackAudioPlayheadLine(layout, onsetsMs, tMs, nBars) {
|
|
|
674
676
|
const first = onsetsMs[0];
|
|
675
677
|
const last = onsetsMs[n - 1];
|
|
676
678
|
const span = last - first;
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
679
|
+
const colX = (m, f) => {
|
|
680
|
+
const sx = Math.min(m.noteStartX, m.x + m.w);
|
|
681
|
+
return sx + Math.min(1, Math.max(0, f)) * (m.x + m.w - sx);
|
|
682
|
+
};
|
|
683
|
+
const useEng = !!noteCols && noteCols.length === n;
|
|
684
|
+
const anchorFor = (k) => {
|
|
685
|
+
const pos = useEng ? noteCols[k] : (n === 1 ? 0 : k / (n - 1)) * cols.length;
|
|
686
|
+
const i = Math.min(cols.length - 1, Math.max(0, Math.floor(pos)));
|
|
687
|
+
const m = cols[i];
|
|
688
|
+
return { x: colX(m, pos - i), rowY: m.y, rowH: m.h };
|
|
689
|
+
};
|
|
690
|
+
const sameRow = (ya, yb) => Math.abs(ya - yb) < Math.max(8, ya * 0 + 8);
|
|
691
|
+
const rowRightEdge = (rowY2) => Math.max(...cols.filter((c) => sameRow(c.y, rowY2)).map((c) => c.x + c.w));
|
|
692
|
+
const rowLeftEdge = (rowY2) => Math.min(...cols.filter((c) => sameRow(c.y, rowY2)).map((c) => Math.min(c.noteStartX, c.x)));
|
|
693
|
+
let a, b, frac;
|
|
694
|
+
if (tMs <= first || n === 1) {
|
|
695
|
+
a = b = anchorFor(0);
|
|
696
|
+
frac = 0;
|
|
697
|
+
} else if (tMs >= last) {
|
|
698
|
+
a = b = anchorFor(n - 1);
|
|
699
|
+
frac = 0;
|
|
700
|
+
} else {
|
|
681
701
|
let lo = 0, hi = n - 1;
|
|
682
702
|
while (lo < hi) {
|
|
683
703
|
const mid = lo + hi + 1 >> 1;
|
|
684
704
|
if (onsetsMs[mid] <= tMs) lo = mid;
|
|
685
705
|
else hi = mid - 1;
|
|
686
706
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
y1 = y1 + (nxt.y + nxt.h + padV - y1) * s;
|
|
707
|
+
a = anchorFor(lo);
|
|
708
|
+
b = anchorFor(lo + 1);
|
|
709
|
+
frac = (tMs - onsetsMs[lo]) / (onsetsMs[lo + 1] - onsetsMs[lo]);
|
|
710
|
+
}
|
|
711
|
+
let x, rowY, rowH;
|
|
712
|
+
if (sameRow(a.rowY, b.rowY)) {
|
|
713
|
+
x = a.x + (b.x - a.x) * frac;
|
|
714
|
+
rowY = a.rowY;
|
|
715
|
+
rowH = a.rowH;
|
|
716
|
+
} else {
|
|
717
|
+
if (frac < 0.5) {
|
|
718
|
+
const s = frac / 0.5;
|
|
719
|
+
x = a.x + (rowRightEdge(a.rowY) - a.x) * s;
|
|
720
|
+
rowY = a.rowY;
|
|
721
|
+
rowH = a.rowH;
|
|
722
|
+
} else {
|
|
723
|
+
const s = (frac - 0.5) / 0.5;
|
|
724
|
+
const nl = rowLeftEdge(b.rowY);
|
|
725
|
+
x = nl + (b.x - nl) * s;
|
|
726
|
+
rowY = b.rowY;
|
|
727
|
+
rowH = b.rowH;
|
|
709
728
|
}
|
|
710
729
|
}
|
|
730
|
+
let y0 = rowY - padV;
|
|
731
|
+
let y1 = rowY + rowH + padV;
|
|
711
732
|
const bandY0 = layout.rect.dy;
|
|
712
733
|
const bandY1 = layout.rect.dy + layout.rect.dh;
|
|
713
734
|
const h = Math.max(1, y1 - y0);
|
|
@@ -935,7 +956,7 @@ function scrollCursorLayer() {
|
|
|
935
956
|
const onsets = onsetsFor(ctx);
|
|
936
957
|
let line;
|
|
937
958
|
if (scrollMode === "vstack") {
|
|
938
|
-
line = vstackAudioPlayheadLine(layout, onsets, tMs, measureCount(eng.rendered));
|
|
959
|
+
line = vstackAudioPlayheadLine(layout, onsets, tMs, measureCount(eng.rendered), noteColsFor(ctx));
|
|
939
960
|
} else {
|
|
940
961
|
line = audioPlayheadLine(layout, onsets, tMs, barDurMs, noteColsFor(ctx));
|
|
941
962
|
}
|
|
@@ -1277,6 +1298,123 @@ var keyboardFactory = {
|
|
|
1277
1298
|
}
|
|
1278
1299
|
};
|
|
1279
1300
|
|
|
1301
|
+
// src/scene/layers/fretboard.ts
|
|
1302
|
+
var INLAY_FRETS = [3, 5, 7, 9, 12];
|
|
1303
|
+
var NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
1304
|
+
function fretboardLayer() {
|
|
1305
|
+
let placement = /* @__PURE__ */ new Map();
|
|
1306
|
+
let visibleFrets = 12;
|
|
1307
|
+
let hands = { R: "#7b2436", L: "#c8a55b" };
|
|
1308
|
+
let showLabels = false;
|
|
1309
|
+
let rect = { x: 0, y: 0, w: 0, h: 0 };
|
|
1310
|
+
return {
|
|
1311
|
+
key: "fretboard",
|
|
1312
|
+
init(ctx, props) {
|
|
1313
|
+
hands = props.handColors ?? { R: ctx.theme.accent, L: ctx.theme.gold };
|
|
1314
|
+
showLabels = props.showLabels ?? false;
|
|
1315
|
+
const midis = (ctx.score?.notes ?? []).map((n) => n.pitchMidi);
|
|
1316
|
+
placement = buildPlacementMap(midis);
|
|
1317
|
+
let maxFret = 0;
|
|
1318
|
+
for (const p of placement.values()) maxFret = Math.max(maxFret, p.fret);
|
|
1319
|
+
visibleFrets = props.visibleFrets ?? Math.min(15, Math.max(5, maxFret + 1));
|
|
1320
|
+
const sb = ctx.safeBox;
|
|
1321
|
+
const height = props.height ?? 220;
|
|
1322
|
+
const bottomY = props.bottomY ?? sb.bottom;
|
|
1323
|
+
rect = { x: sb.left, y: bottomY - height, w: sb.w, h: height };
|
|
1324
|
+
},
|
|
1325
|
+
draw(ctx, tMs) {
|
|
1326
|
+
const c = ctx.ctx2d;
|
|
1327
|
+
const { x, y, w, h } = rect;
|
|
1328
|
+
const nutW = Math.max(6, w * 0.012);
|
|
1329
|
+
const boardX = x + nutW;
|
|
1330
|
+
const boardW = w - nutW;
|
|
1331
|
+
const fretW = boardW / visibleFrets;
|
|
1332
|
+
const stringGap = h / (STRING_COUNT - 1);
|
|
1333
|
+
const stringY = (s) => y + h - s * stringGap;
|
|
1334
|
+
const fretX = (f) => boardX + f * fretW;
|
|
1335
|
+
const dotX = (f) => f === 0 ? x + nutW / 2 : fretX(f) - fretW / 2;
|
|
1336
|
+
c.save();
|
|
1337
|
+
c.fillStyle = "#f1e7d6";
|
|
1338
|
+
c.fillRect(x, y, w, h);
|
|
1339
|
+
c.fillStyle = "#2a2420";
|
|
1340
|
+
c.fillRect(x, y, nutW, h);
|
|
1341
|
+
c.strokeStyle = "#b8a98c";
|
|
1342
|
+
c.lineWidth = 2;
|
|
1343
|
+
for (let f = 1; f <= visibleFrets; f++) {
|
|
1344
|
+
c.beginPath();
|
|
1345
|
+
c.moveTo(fretX(f), y);
|
|
1346
|
+
c.lineTo(fretX(f), y + h);
|
|
1347
|
+
c.stroke();
|
|
1348
|
+
}
|
|
1349
|
+
c.fillStyle = "rgba(60,50,40,0.28)";
|
|
1350
|
+
for (const f of INLAY_FRETS) {
|
|
1351
|
+
if (f > visibleFrets) continue;
|
|
1352
|
+
const cx = dotX(f);
|
|
1353
|
+
const r = Math.min(stringGap, fretW) * 0.18;
|
|
1354
|
+
if (f === 12) {
|
|
1355
|
+
c.beginPath();
|
|
1356
|
+
c.arc(cx, stringY(STRING_COUNT - 1) + stringGap * 0.5, r, 0, Math.PI * 2);
|
|
1357
|
+
c.fill();
|
|
1358
|
+
c.beginPath();
|
|
1359
|
+
c.arc(cx, stringY(0) - stringGap * 0.5, r, 0, Math.PI * 2);
|
|
1360
|
+
c.fill();
|
|
1361
|
+
} else {
|
|
1362
|
+
c.beginPath();
|
|
1363
|
+
c.arc(cx, y + h / 2, r, 0, Math.PI * 2);
|
|
1364
|
+
c.fill();
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
c.strokeStyle = "#5c5345";
|
|
1368
|
+
for (let s = 0; s < STRING_COUNT; s++) {
|
|
1369
|
+
c.lineWidth = 1 + (STRING_COUNT - 1 - s) * 0.5;
|
|
1370
|
+
c.beginPath();
|
|
1371
|
+
c.moveTo(x, stringY(s));
|
|
1372
|
+
c.lineTo(x + w, stringY(s));
|
|
1373
|
+
c.stroke();
|
|
1374
|
+
}
|
|
1375
|
+
const dotR = Math.min(stringGap, fretW) * 0.42;
|
|
1376
|
+
for (const n of ctx.score?.notes ?? []) {
|
|
1377
|
+
if (!(tMs >= n.onsetMs && tMs < n.onsetMs + n.durMs)) continue;
|
|
1378
|
+
const pos = placement.get(n.pitchMidi);
|
|
1379
|
+
if (!pos || pos.fret > visibleFrets) continue;
|
|
1380
|
+
const cx = dotX(pos.fret);
|
|
1381
|
+
const cy = stringY(pos.string);
|
|
1382
|
+
c.fillStyle = n.hand === "L" ? hands.L : hands.R;
|
|
1383
|
+
c.beginPath();
|
|
1384
|
+
c.arc(cx, cy, dotR, 0, Math.PI * 2);
|
|
1385
|
+
c.fill();
|
|
1386
|
+
if (showLabels) {
|
|
1387
|
+
c.fillStyle = "#fff";
|
|
1388
|
+
c.font = `${Math.round(dotR * 1.1)}px sans-serif`;
|
|
1389
|
+
c.textAlign = "center";
|
|
1390
|
+
c.textBaseline = "middle";
|
|
1391
|
+
c.fillText(NOTE_NAMES[n.pitchMidi % 12], cx, cy);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
c.restore();
|
|
1395
|
+
},
|
|
1396
|
+
dispose() {
|
|
1397
|
+
placement = /* @__PURE__ */ new Map();
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
var fretboardFactory = {
|
|
1402
|
+
key: "fretboard",
|
|
1403
|
+
create: fretboardLayer,
|
|
1404
|
+
validateProps(props) {
|
|
1405
|
+
const errs = [];
|
|
1406
|
+
if (props == null || typeof props !== "object") return ["fretboard: props must be an object"];
|
|
1407
|
+
const p = props;
|
|
1408
|
+
if (p.visibleFrets != null && (typeof p.visibleFrets !== "number" || p.visibleFrets < 1 || p.visibleFrets > 24))
|
|
1409
|
+
errs.push("fretboard.visibleFrets must be a number in 1..24");
|
|
1410
|
+
if (p.height != null && (typeof p.height !== "number" || p.height <= 0))
|
|
1411
|
+
errs.push("fretboard.height must be a positive number");
|
|
1412
|
+
if (p.bottomY != null && typeof p.bottomY !== "number") errs.push("fretboard.bottomY must be a number");
|
|
1413
|
+
if (p.showLabels != null && typeof p.showLabels !== "boolean") errs.push("fretboard.showLabels must be a boolean");
|
|
1414
|
+
return errs;
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
|
|
1280
1418
|
// src/scene/layers/fallingNotes.ts
|
|
1281
1419
|
var DEFAULT_LEAD_MS = 2e3;
|
|
1282
1420
|
function rgba(color, a) {
|
|
@@ -4643,6 +4781,7 @@ registerLayer(captionFactory);
|
|
|
4643
4781
|
registerLayer(notationFactory);
|
|
4644
4782
|
registerLayer(scrollCursorFactory);
|
|
4645
4783
|
registerLayer(keyboardFactory);
|
|
4784
|
+
registerLayer(fretboardFactory);
|
|
4646
4785
|
registerLayer(fallingNotesFactory);
|
|
4647
4786
|
registerLayer(hookFactory);
|
|
4648
4787
|
registerLayer(revealFactory);
|
|
@@ -5484,6 +5623,7 @@ export {
|
|
|
5484
5623
|
followWindowStart,
|
|
5485
5624
|
fracSlotPoint,
|
|
5486
5625
|
frameRect,
|
|
5626
|
+
fretboardFactory,
|
|
5487
5627
|
functionColor,
|
|
5488
5628
|
functionalHarmonyFactory,
|
|
5489
5629
|
getKeyboardLayout,
|