@ugfoundation/swaralipi-js 0.11.1 → 0.13.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 +7 -1
- package/dist/{batch-RAM2S4QO.js → batch-BS2AIZOR.js} +6 -5
- package/dist/batch-BS2AIZOR.js.map +1 -0
- package/dist/{chunk-3EWAKXLM.js → chunk-3ZBNANDF.js} +3 -3
- package/dist/{chunk-3EWAKXLM.js.map → chunk-3ZBNANDF.js.map} +1 -1
- package/dist/{chunk-LRIAUFKF.js → chunk-5N6HGPFH.js} +8 -168
- package/dist/chunk-5N6HGPFH.js.map +1 -0
- package/dist/chunk-BSZ3WQUP.js +164 -0
- package/dist/chunk-BSZ3WQUP.js.map +1 -0
- package/dist/{chunk-SR7NFEGL.js → chunk-KOHSKHXI.js} +5 -3
- package/dist/chunk-KOHSKHXI.js.map +1 -0
- package/dist/{chunk-CF3JWIHP.js → chunk-ZNJTMQIX.js} +228 -64
- package/dist/chunk-ZNJTMQIX.js.map +1 -0
- package/dist/cli.cjs +6 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/convert/index.cjs +6 -4
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.js +4 -3
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{lay-DypMIIuf.d.ts → lay-CWlEhImP.d.ts} +8 -1
- package/dist/{lay-CvZeVon3.d.cts → lay-CXhC-HRO.d.cts} +8 -1
- package/dist/player/index.cjs +473 -123
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +14 -4
- package/dist/player/index.d.ts +14 -4
- package/dist/player/index.js +87 -62
- package/dist/player/index.js.map +1 -1
- package/dist/{schedule-DjcqM6A4.d.ts → schedule-ClyON98C.d.ts} +23 -11
- package/dist/{schedule-C4sP7uFu.d.cts → schedule-V-fgepvP.d.cts} +23 -11
- package/dist/spec/index.cjs +4 -2
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +2 -2
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +2 -2
- package/dist/viewer/index.cjs +369 -62
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +2 -2
- package/dist/viewer/index.d.ts +2 -2
- package/dist/viewer/index.js +3 -2
- package/dist/viewer/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/batch-RAM2S4QO.js.map +0 -1
- package/dist/chunk-CF3JWIHP.js.map +0 -1
- package/dist/chunk-LRIAUFKF.js.map +0 -1
- package/dist/chunk-SR7NFEGL.js.map +0 -1
package/dist/viewer/index.cjs
CHANGED
|
@@ -70,8 +70,10 @@ var init_taals = __esm({
|
|
|
70
70
|
// ঝম্পক
|
|
71
71
|
dhamaar: { id: "dhamaar", numBeats: 14, bhaags: [5, 2, 3, 4], sam: 0, khaali: [7] },
|
|
72
72
|
// ধামার
|
|
73
|
-
rupakra: { id: "rupakra", numBeats: 8, bhaags: [3, 2, 3], sam: 0 }
|
|
73
|
+
rupakra: { id: "rupakra", numBeats: 8, bhaags: [3, 2, 3], sam: 0 },
|
|
74
74
|
// রূপকড়া
|
|
75
|
+
khemta: { id: "khemta", numBeats: 12, bhaags: [3, 3, 3, 3], sam: 0, khaali: [6] }
|
|
76
|
+
// খেমটা
|
|
75
77
|
};
|
|
76
78
|
TAAL_FERTA = "\u09A4\u09BE\u09B2\u09AB\u09C7\u09B0\u09A4\u09BE";
|
|
77
79
|
}
|
|
@@ -284,7 +286,7 @@ var init_spec = __esm({
|
|
|
284
286
|
init_grace();
|
|
285
287
|
init_lay();
|
|
286
288
|
init_svara();
|
|
287
|
-
SPEC_VERSION = "0.
|
|
289
|
+
SPEC_VERSION = "0.13.0";
|
|
288
290
|
}
|
|
289
291
|
});
|
|
290
292
|
|
|
@@ -366,9 +368,305 @@ function sampleFileName(midi) {
|
|
|
366
368
|
return midiToSampleName(midi).toLowerCase().replace("#", "s");
|
|
367
369
|
}
|
|
368
370
|
|
|
369
|
-
// src/
|
|
371
|
+
// src/convert/codec.ts
|
|
372
|
+
var LETTERS = {
|
|
373
|
+
s: { letter: "s" },
|
|
374
|
+
r: { letter: "r" },
|
|
375
|
+
g: { letter: "g" },
|
|
376
|
+
m: { letter: "m" },
|
|
377
|
+
p: { letter: "p" },
|
|
378
|
+
q: { letter: "d" },
|
|
379
|
+
// ধ — shuddha dha
|
|
380
|
+
n: { letter: "n" },
|
|
381
|
+
d: { letter: "d", komal: true },
|
|
382
|
+
// দ
|
|
383
|
+
t: { letter: "g", komal: true },
|
|
384
|
+
// জ্ঞ
|
|
385
|
+
k: { letter: "m", tivra: true },
|
|
386
|
+
// ক্ষ
|
|
387
|
+
u: { letter: "n", komal: true },
|
|
388
|
+
// ণ
|
|
389
|
+
v: { letter: "r", komal: true }
|
|
390
|
+
// ঋ
|
|
391
|
+
};
|
|
392
|
+
var STRUCTURALS = {
|
|
393
|
+
"%": () => ({ svara: ["madhyam", "s"] }),
|
|
394
|
+
// precomposed সা
|
|
395
|
+
"&": () => ({ svara: ["madhyam", "n"] })
|
|
396
|
+
// precomposed নি
|
|
397
|
+
};
|
|
398
|
+
var BAR_RUN_RE = /^(-)?([lLA[\]]+)$/;
|
|
399
|
+
function decodeBarRun(body) {
|
|
400
|
+
const m = body.match(BAR_RUN_RE);
|
|
401
|
+
if (!m || !/[lLA]/.test(m[2])) return void 0;
|
|
402
|
+
const notes = [];
|
|
403
|
+
if (m[1]) notes.push({ sustain: true });
|
|
404
|
+
const run = m[2];
|
|
405
|
+
let i = 0;
|
|
406
|
+
while (i < run.length) {
|
|
407
|
+
const ch = run[i];
|
|
408
|
+
if (ch === "l" || ch === "L") {
|
|
409
|
+
let j = i;
|
|
410
|
+
while (j < run.length && run[j] === ch) j++;
|
|
411
|
+
const strokes = (j - i) * (ch === "L" ? 2 : 1);
|
|
412
|
+
notes.push({ bar: strokes >= 4 ? "quad" : strokes >= 2 ? "double" : "single" });
|
|
413
|
+
i = j;
|
|
414
|
+
} else if (ch === "A") {
|
|
415
|
+
notes.push({ bar: "dari" });
|
|
416
|
+
i++;
|
|
417
|
+
} else {
|
|
418
|
+
notes.push({ brackets: [{ kind: "alt", edge: ch === "[" ? "open" : "close" }] });
|
|
419
|
+
i++;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return notes;
|
|
423
|
+
}
|
|
424
|
+
var KNOWN_RAW_RE = [
|
|
425
|
+
/^[wxyjEJ$<>=_]+$/,
|
|
426
|
+
// arc & line drawing segments
|
|
427
|
+
/^\|+$/,
|
|
428
|
+
// thin yugal-dari stop mark
|
|
429
|
+
/^[0-9!@#]{1,2}[fF]?:?$/,
|
|
430
|
+
// beat-number labels incl. 10–16 (sam tick = f/F, e.g. 1f / 1F / 2:)
|
|
431
|
+
/^[oOe`+,.;:iI~^*bcBCWXYFHZz'"?]$/
|
|
432
|
+
// dots, ticks, duration marks, legacy forms
|
|
433
|
+
];
|
|
434
|
+
var OPEN_BRACKETS = {
|
|
435
|
+
"{": "repeat",
|
|
436
|
+
"(": "skip",
|
|
437
|
+
"[": "alt"
|
|
438
|
+
};
|
|
439
|
+
var CLOSE_BRACKETS = {
|
|
440
|
+
"}": "repeat",
|
|
441
|
+
")": "skip",
|
|
442
|
+
"]": "alt"
|
|
443
|
+
};
|
|
444
|
+
function octaveOf(mark) {
|
|
445
|
+
if (mark === "f" || mark === "F") return "taar";
|
|
446
|
+
if (mark === "h" || mark === "H") return "mandra";
|
|
447
|
+
return "madhyam";
|
|
448
|
+
}
|
|
449
|
+
var BODY_RE = /^([0-9!@][fF]?)?([zj]*)(-)?([i:])?(?:([SRGMPQNDTKUV])([fFhH])?)?((?:[srgmpqndtkuv][fh]?[i:]?)+)?(a)?([fh])?([i:])?(?:([SRGMPQNDTKUV])([fFhH])?)?([0-9!@][fF]?)?(-)?$/;
|
|
450
|
+
function decodeChunk(chunk) {
|
|
451
|
+
const opens = [];
|
|
452
|
+
const closes = [];
|
|
453
|
+
let body = chunk;
|
|
454
|
+
while (body.length > 0) {
|
|
455
|
+
const kind = OPEN_BRACKETS[body[0]];
|
|
456
|
+
if (kind === void 0) break;
|
|
457
|
+
opens.push(kind);
|
|
458
|
+
body = body.slice(1);
|
|
459
|
+
}
|
|
460
|
+
while (body.length > 0) {
|
|
461
|
+
const kind = CLOSE_BRACKETS[body[body.length - 1]];
|
|
462
|
+
if (kind === void 0) break;
|
|
463
|
+
closes.unshift(kind);
|
|
464
|
+
body = body.slice(0, -1);
|
|
465
|
+
}
|
|
466
|
+
const finish = (notes, kind) => {
|
|
467
|
+
if (notes.length === 0) notes.push({});
|
|
468
|
+
const first = notes[0];
|
|
469
|
+
const last = notes[notes.length - 1];
|
|
470
|
+
if (opens.length > 0) first.brackets = [...opens.map((kind_) => ({ kind: kind_, edge: "open" })), ...first.brackets ?? []];
|
|
471
|
+
if (closes.length > 0) last.brackets = [...last.brackets ?? [], ...closes.map((kind_) => ({ kind: kind_, edge: "close" }))];
|
|
472
|
+
return { notes, kind };
|
|
473
|
+
};
|
|
474
|
+
if (body === "") return finish([{}], "decoded");
|
|
475
|
+
const barRun = decodeBarRun(body);
|
|
476
|
+
if (barRun) return finish(barRun, "decoded");
|
|
477
|
+
const structural = STRUCTURALS[body];
|
|
478
|
+
if (structural) return finish([structural()], "decoded");
|
|
479
|
+
if (KNOWN_RAW_RE.some((re) => re.test(body))) return finish([{}], "known");
|
|
480
|
+
const m = body.match(BODY_RE);
|
|
481
|
+
if (m) {
|
|
482
|
+
const [, , , hyphen, , kanLetter, kanOct, unitsStr, aakar, postOct, , trailKan, trailKanOct] = m;
|
|
483
|
+
const attachKan = (note, letter, oct) => {
|
|
484
|
+
const def = LETTERS[letter.toLowerCase()];
|
|
485
|
+
note.kan = [octaveOf(oct?.toLowerCase()), def.letter];
|
|
486
|
+
};
|
|
487
|
+
if (unitsStr) {
|
|
488
|
+
const units = unitsStr.match(/[srgmpqndtkuv][fh]?/g) ?? [];
|
|
489
|
+
const notes = units.map((unit) => {
|
|
490
|
+
const def = LETTERS[unit[0]];
|
|
491
|
+
const note = { svara: [octaveOf(unit[1]), def.letter] };
|
|
492
|
+
if (def.komal) note.komal = true;
|
|
493
|
+
if (def.tivra) note.tivra = true;
|
|
494
|
+
if (units.length > 1) note.durationMatra = 1 / units.length;
|
|
495
|
+
return note;
|
|
496
|
+
});
|
|
497
|
+
const last = notes[notes.length - 1];
|
|
498
|
+
if (postOct && last.svara[0] === "madhyam") {
|
|
499
|
+
last.svara = [octaveOf(postOct), last.svara[1]];
|
|
500
|
+
}
|
|
501
|
+
if (hyphen) notes[0].sustain = true;
|
|
502
|
+
if (kanLetter) attachKan(notes[0], kanLetter, kanOct);
|
|
503
|
+
if (trailKan && last.kan === void 0) attachKan(last, trailKan, trailKanOct);
|
|
504
|
+
return finish(notes, "decoded");
|
|
505
|
+
}
|
|
506
|
+
if (aakar) {
|
|
507
|
+
const note = hyphen ? { sustain: true } : { rest: true };
|
|
508
|
+
if (trailKan) attachKan(note, trailKan, trailKanOct);
|
|
509
|
+
else if (kanLetter) attachKan(note, kanLetter, kanOct);
|
|
510
|
+
return finish([note], "decoded");
|
|
511
|
+
}
|
|
512
|
+
if (hyphen && !kanLetter && !trailKan) {
|
|
513
|
+
return finish([{ sustain: true }], "decoded");
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return finish([{}], "unknown");
|
|
517
|
+
}
|
|
518
|
+
function decodeCell(text) {
|
|
519
|
+
const chunks = text.split(/\s+/).filter((c) => c.length > 0);
|
|
520
|
+
const out = { notes: [], unknownTokens: [], knownRawTokens: [], decoded: false };
|
|
521
|
+
for (const chunk of chunks) {
|
|
522
|
+
const { notes, kind } = decodeChunk(chunk);
|
|
523
|
+
notes[0].raw = chunk;
|
|
524
|
+
if (kind === "decoded") out.decoded = true;
|
|
525
|
+
else if (kind === "known") out.knownRawTokens.push(chunk);
|
|
526
|
+
else out.unknownTokens.push(chunk);
|
|
527
|
+
out.notes.push(...notes);
|
|
528
|
+
}
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/player/repeats.ts
|
|
370
533
|
var UNDECODED_CELL = /[srgmpqnvtduka]/i;
|
|
371
534
|
var isSilent = (n) => n.svara === void 0 && n.rest !== true && n.sustain !== true && !(n.raw !== void 0 && UNDECODED_CELL.test(n.raw));
|
|
535
|
+
var STOP_MARK = /^\|+$/;
|
|
536
|
+
var EPS = 1e-6;
|
|
537
|
+
var edges = (n, kind, edge) => n.brackets?.filter((b) => b.kind === kind && b.edge === edge).length ?? 0;
|
|
538
|
+
var sounds = (n) => n.svara !== void 0 || n.rest === true || n.sustain === true;
|
|
539
|
+
var matra = (notes) => notes.reduce((sum, n) => sum + (n.durationMatra ?? 1), 0);
|
|
540
|
+
function playOrder(doc) {
|
|
541
|
+
const tokens = [];
|
|
542
|
+
doc.parts.forEach(
|
|
543
|
+
(part, p) => part.rows.forEach(
|
|
544
|
+
(row, r) => row.notes.forEach((note, i) => tokens.push({ p, r, i, note, timed: note.bar === void 0 && !isSilent(note) }))
|
|
545
|
+
)
|
|
546
|
+
);
|
|
547
|
+
const { asthayi, stop, returns } = form(tokens);
|
|
548
|
+
const changed = changedNotes(tokens);
|
|
549
|
+
const out = [];
|
|
550
|
+
const sing = (lo, hi, useChanged) => {
|
|
551
|
+
const repeats = [];
|
|
552
|
+
let owed = 0;
|
|
553
|
+
for (let k = lo; k < hi; ) {
|
|
554
|
+
const t = tokens[k];
|
|
555
|
+
if (edges(t.note, "repeat", "open") > 0 && repeats.at(-1)?.open !== k) repeats.push({ open: k, again: false });
|
|
556
|
+
const again = repeats.at(-1)?.again === true;
|
|
557
|
+
const skipTo = again && edges(t.note, "skip", "open") > 0 ? skipEnd(tokens, k, hi) : -1;
|
|
558
|
+
if (skipTo >= 0) {
|
|
559
|
+
if (edges(tokens[skipTo].note, "repeat", "close") > 0) repeats.pop();
|
|
560
|
+
k = skipTo + 1;
|
|
561
|
+
owed = 0;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (owed > EPS && t.timed) owed -= matra([t.note]);
|
|
565
|
+
else {
|
|
566
|
+
const sub = useChanged || again ? changed.get(k) : void 0;
|
|
567
|
+
const fits = sub !== void 0 && matra(sub) >= matra([t.note]) - EPS;
|
|
568
|
+
for (const note of fits ? sub : [t.note]) out.push({ p: t.p, r: t.r, i: t.i, note });
|
|
569
|
+
owed = fits ? matra(sub) - matra([t.note]) : 0;
|
|
570
|
+
}
|
|
571
|
+
const loop = edges(t.note, "repeat", "close") > 0 ? repeats.at(-1) : void 0;
|
|
572
|
+
if (loop !== void 0 && !loop.again) {
|
|
573
|
+
loop.again = true;
|
|
574
|
+
k = loop.open;
|
|
575
|
+
owed = 0;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (loop !== void 0) repeats.pop();
|
|
579
|
+
k++;
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
let from = 0;
|
|
583
|
+
for (const ret of returns) {
|
|
584
|
+
sing(from, ret.resume, false);
|
|
585
|
+
sing(asthayi, stop + 1, ret.changed);
|
|
586
|
+
from = ret.resume;
|
|
587
|
+
}
|
|
588
|
+
sing(from, tokens.length, false);
|
|
589
|
+
return out;
|
|
590
|
+
}
|
|
591
|
+
function form(tokens) {
|
|
592
|
+
const first = /* @__PURE__ */ new Map();
|
|
593
|
+
const last = /* @__PURE__ */ new Map();
|
|
594
|
+
tokens.forEach((t, k) => {
|
|
595
|
+
if (!t.timed) return;
|
|
596
|
+
const row = `${t.p}.${t.r}`;
|
|
597
|
+
if (!first.has(row)) first.set(row, k);
|
|
598
|
+
last.set(row, k);
|
|
599
|
+
});
|
|
600
|
+
const opens = [];
|
|
601
|
+
let opening = false;
|
|
602
|
+
tokens.forEach((t, k) => {
|
|
603
|
+
if (t.note.bar !== "double") return;
|
|
604
|
+
const row = `${t.p}.${t.r}`;
|
|
605
|
+
const before = (first.get(row) ?? Infinity) < k;
|
|
606
|
+
const after = (last.get(row) ?? -1) > k;
|
|
607
|
+
opening = before !== after ? after : !opening;
|
|
608
|
+
if (opening) opens.push(k);
|
|
609
|
+
});
|
|
610
|
+
const returns = [];
|
|
611
|
+
for (let k = 0; k < tokens.length; k++) {
|
|
612
|
+
let at = -1;
|
|
613
|
+
let open = false;
|
|
614
|
+
let changed = false;
|
|
615
|
+
let end = false;
|
|
616
|
+
for (; k < tokens.length && !tokens[k].timed; k++) {
|
|
617
|
+
const n = tokens[k].note;
|
|
618
|
+
if (n.bar === "quad") end = true;
|
|
619
|
+
if (n.bar === "double" && !opens.includes(k)) at = k;
|
|
620
|
+
if (edges(n, "alt", "open") > 0) open = true;
|
|
621
|
+
if (open && edges(n, "alt", "close") > 0) {
|
|
622
|
+
at = k;
|
|
623
|
+
changed = true;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (at >= 0 && !end) returns.push({ resume: at + 1, changed });
|
|
627
|
+
}
|
|
628
|
+
const top = opens[0];
|
|
629
|
+
const asthayi = top !== void 0 && top < (returns[0]?.resume ?? Infinity) ? top : 0;
|
|
630
|
+
const asthayiEnd = Math.min(returns[0]?.resume ?? Infinity, opens.find((k) => k > asthayi) ?? Infinity, tokens.length);
|
|
631
|
+
const lastSung = tokens.reduce((last2, t, k) => t.timed ? k : last2, -1);
|
|
632
|
+
const stop = tokens.findIndex(
|
|
633
|
+
(t, k) => k >= asthayi && k < asthayiEnd && k < lastSung && t.note.above?.some((a) => STOP_MARK.test(a.raw ?? "")) === true
|
|
634
|
+
);
|
|
635
|
+
if (stop < 0) return { asthayi, stop, returns: [] };
|
|
636
|
+
const atClose = returns[0]?.resume === asthayiEnd && !tokens.slice(stop + 1, asthayiEnd).some((t) => t.timed);
|
|
637
|
+
return { asthayi, stop, returns: atClose ? returns.slice(1) : returns };
|
|
638
|
+
}
|
|
639
|
+
function changedNotes(tokens) {
|
|
640
|
+
const out = /* @__PURE__ */ new Map();
|
|
641
|
+
let row = "";
|
|
642
|
+
let inside = false;
|
|
643
|
+
tokens.forEach((t, k) => {
|
|
644
|
+
if (row !== `${t.p}.${t.r}`) {
|
|
645
|
+
row = `${t.p}.${t.r}`;
|
|
646
|
+
inside = false;
|
|
647
|
+
}
|
|
648
|
+
const notes = [];
|
|
649
|
+
for (const above of t.note.above ?? []) {
|
|
650
|
+
const read = above.raw !== void 0 && !sounds(above) && above.brackets === void 0;
|
|
651
|
+
for (const a of read ? decodeCell(above.raw).notes : [above]) {
|
|
652
|
+
if (edges(a, "alt", "open") > 0) inside = true;
|
|
653
|
+
if (inside && sounds(a)) notes.push(a);
|
|
654
|
+
if (edges(a, "alt", "close") > 0) inside = false;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (notes.length > 0) out.set(k, notes);
|
|
658
|
+
});
|
|
659
|
+
return out;
|
|
660
|
+
}
|
|
661
|
+
function skipEnd(tokens, k, hi) {
|
|
662
|
+
for (let depth = 0, j = k; j < hi; j++) {
|
|
663
|
+
depth += edges(tokens[j].note, "skip", "open") - edges(tokens[j].note, "skip", "close");
|
|
664
|
+
if (depth <= 0) return j;
|
|
665
|
+
}
|
|
666
|
+
return -1;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/player/schedule.ts
|
|
372
670
|
var ARC_RAW_RE = /^[wWxXyYjJeEzZ$=_<>`\\]+$/;
|
|
373
671
|
var isArcRaw = (raw) => raw !== void 0 && ARC_RAW_RE.test(raw);
|
|
374
672
|
var MEEND_MARKS = { start: "w", end: "x", both: "xw" };
|
|
@@ -412,67 +710,76 @@ function buildSchedule(doc, opts = {}) {
|
|
|
412
710
|
let cursor = 0;
|
|
413
711
|
let lastPitched = -1;
|
|
414
712
|
let samMatra;
|
|
415
|
-
let
|
|
713
|
+
let matra2 = 0;
|
|
416
714
|
const speeds = resolveLaySpeeds(doc.meta.laySpeeds, opts.laySpeeds);
|
|
417
715
|
const withLay = opts.lay !== false;
|
|
418
716
|
const tempo = [{ beat: 0, endBeat: 0, scale: 1, lay: "drut", taal: doc.meta.taal }];
|
|
717
|
+
const rows = /* @__PURE__ */ new Map();
|
|
419
718
|
let scale = 1;
|
|
719
|
+
let layNow = "drut";
|
|
420
720
|
let taal = doc.meta.taal;
|
|
421
|
-
doc.parts.forEach(
|
|
422
|
-
part.rows.forEach((row, r) => {
|
|
721
|
+
doc.parts.forEach(
|
|
722
|
+
(part, p) => part.rows.forEach((row, r) => {
|
|
423
723
|
const lay = withLay ? rowLay(row) : void 0;
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
724
|
+
if (lay !== void 0 && speeds[lay] !== scale) {
|
|
725
|
+
scale = speeds[lay];
|
|
726
|
+
layNow = lay;
|
|
727
|
+
}
|
|
728
|
+
if (row.taal !== void 0) taal = row.taal;
|
|
729
|
+
rows.set(`${p}.${r}`, { scale, lay: layNow, taal, glides: meendContinuations(row.notes) });
|
|
730
|
+
})
|
|
731
|
+
);
|
|
732
|
+
const visits = opts.repeats === true ? playOrder(doc) : doc.parts.flatMap((part, p) => part.rows.flatMap((row, r) => row.notes.map((note, i) => ({ p, r, i, note }))));
|
|
733
|
+
let rowNow = "";
|
|
734
|
+
for (const { p, r, i, note } of visits) {
|
|
735
|
+
const row = rows.get(`${p}.${r}`);
|
|
736
|
+
if (rowNow !== `${p}.${r}`) {
|
|
737
|
+
rowNow = `${p}.${r}`;
|
|
738
|
+
const last = tempo[tempo.length - 1];
|
|
739
|
+
if (row.scale !== last.scale || !sameTaal(row.taal, last.taal)) {
|
|
740
|
+
const seg = { beat: cursor, endBeat: cursor, scale: row.scale, lay: row.lay, taal: row.taal };
|
|
431
741
|
if (last.beat === cursor) tempo[tempo.length - 1] = seg;
|
|
432
742
|
else {
|
|
433
743
|
last.endBeat = cursor;
|
|
434
744
|
tempo.push(seg);
|
|
435
745
|
}
|
|
436
746
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
break;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
if (note.bar !== void 0 || isSilent(note)) return;
|
|
448
|
-
const path = `${p}.${r}.${i}`;
|
|
449
|
-
const written = note.durationMatra ?? 1;
|
|
450
|
-
const dur = written / scale;
|
|
451
|
-
const kan = graceMidis(note.kan, tonicPc, baseOctave);
|
|
452
|
-
const resh = graceMidis(note.resh, tonicPc, baseOctave);
|
|
453
|
-
if (note.svara !== void 0) {
|
|
454
|
-
events.push({
|
|
455
|
-
beat: cursor,
|
|
456
|
-
durationBeats: dur,
|
|
457
|
-
midi: svaraToMidi(note, tonicPc, baseOctave),
|
|
458
|
-
kan,
|
|
459
|
-
resh,
|
|
460
|
-
path,
|
|
461
|
-
kind: note.sustain === true ? "sustain" : "note",
|
|
462
|
-
...glides.has(i) && lastPitched >= 0 ? { glide: true } : {}
|
|
463
|
-
});
|
|
464
|
-
lastPitched = events.length - 1;
|
|
465
|
-
} else if (note.sustain === true) {
|
|
466
|
-
if (lastPitched >= 0) events[lastPitched].durationBeats += dur;
|
|
467
|
-
events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "sustain" });
|
|
468
|
-
} else {
|
|
469
|
-
events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "rest" });
|
|
747
|
+
}
|
|
748
|
+
if (samMatra === void 0 && note.above !== void 0) {
|
|
749
|
+
for (const a of note.above) {
|
|
750
|
+
if (a.raw !== void 0 && SAM_TICK.test(a.raw)) {
|
|
751
|
+
samMatra = matra2;
|
|
752
|
+
break;
|
|
470
753
|
}
|
|
471
|
-
|
|
472
|
-
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (note.bar !== void 0 || isSilent(note)) continue;
|
|
757
|
+
const path = `${p}.${r}.${i}`;
|
|
758
|
+
const written = note.durationMatra ?? 1;
|
|
759
|
+
const dur = written / row.scale;
|
|
760
|
+
const kan = graceMidis(note.kan, tonicPc, baseOctave);
|
|
761
|
+
const resh = graceMidis(note.resh, tonicPc, baseOctave);
|
|
762
|
+
if (note.svara !== void 0) {
|
|
763
|
+
events.push({
|
|
764
|
+
beat: cursor,
|
|
765
|
+
durationBeats: dur,
|
|
766
|
+
midi: svaraToMidi(note, tonicPc, baseOctave),
|
|
767
|
+
kan,
|
|
768
|
+
resh,
|
|
769
|
+
path,
|
|
770
|
+
kind: note.sustain === true ? "sustain" : "note",
|
|
771
|
+
...row.glides.has(i) && lastPitched >= 0 ? { glide: true } : {}
|
|
473
772
|
});
|
|
474
|
-
|
|
475
|
-
|
|
773
|
+
lastPitched = events.length - 1;
|
|
774
|
+
} else if (note.sustain === true) {
|
|
775
|
+
if (lastPitched >= 0) events[lastPitched].durationBeats += dur;
|
|
776
|
+
events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "sustain" });
|
|
777
|
+
} else {
|
|
778
|
+
events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "rest" });
|
|
779
|
+
}
|
|
780
|
+
cursor += dur;
|
|
781
|
+
matra2 += written;
|
|
782
|
+
}
|
|
476
783
|
const numBeats = tempo[0].taal?.numBeats;
|
|
477
784
|
const pickupMatra = samMatra !== void 0 && numBeats !== void 0 && numBeats > 0 ? (numBeats - samMatra % numBeats) % numBeats : 0;
|
|
478
785
|
tempo[tempo.length - 1].endBeat = cursor;
|
|
@@ -564,8 +871,8 @@ function localizeDigits(s, lang) {
|
|
|
564
871
|
}
|
|
565
872
|
|
|
566
873
|
// src/viewer/render.ts
|
|
567
|
-
var
|
|
568
|
-
var isIntegral = (x) => Math.abs(x - Math.round(x)) <
|
|
874
|
+
var EPS2 = 1e-6;
|
|
875
|
+
var isIntegral = (x) => Math.abs(x - Math.round(x)) < EPS2;
|
|
569
876
|
function escapeHtml(s) {
|
|
570
877
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
571
878
|
}
|
|
@@ -606,7 +913,7 @@ function groupRowIntoCells(notes) {
|
|
|
606
913
|
cur ??= { kind: "group", start: i, notes: [] };
|
|
607
914
|
cur.notes.push(note);
|
|
608
915
|
acc += d;
|
|
609
|
-
if (note.halfMatra === true || acc >
|
|
916
|
+
if (note.halfMatra === true || acc > EPS2 && isIntegral(acc)) close();
|
|
610
917
|
});
|
|
611
918
|
close();
|
|
612
919
|
return mergeStructural(absorbBrackets(cells));
|
|
@@ -629,15 +936,15 @@ function absorbBrackets(cells) {
|
|
|
629
936
|
const out = [];
|
|
630
937
|
for (let i = 0; i < cells.length; i++) {
|
|
631
938
|
const cell = cells[i];
|
|
632
|
-
const
|
|
633
|
-
if (
|
|
939
|
+
const edges2 = bracketEdges(cell);
|
|
940
|
+
if (edges2 !== void 0 && edges2.open !== edges2.close) {
|
|
634
941
|
const prev = out[out.length - 1];
|
|
635
942
|
const next = cells[i + 1];
|
|
636
|
-
if (
|
|
943
|
+
if (edges2.close && prev?.kind === "group") {
|
|
637
944
|
prev.notes = [...prev.notes, ...cell.notes];
|
|
638
945
|
continue;
|
|
639
946
|
}
|
|
640
|
-
if (
|
|
947
|
+
if (edges2.open && next?.kind === "group") {
|
|
641
948
|
next.notes = [...cell.notes, ...next.notes];
|
|
642
949
|
next.start = cell.start;
|
|
643
950
|
continue;
|
|
@@ -838,7 +1145,7 @@ function cellMatra(cell) {
|
|
|
838
1145
|
const pitched = cell.notes.filter(isPitched);
|
|
839
1146
|
const total = pitched.reduce((a, n) => a + (n.durationMatra ?? 1), 0);
|
|
840
1147
|
const half = pitched.some((n) => n.halfMatra === true);
|
|
841
|
-
const whole = Math.floor(total +
|
|
1148
|
+
const whole = Math.floor(total + EPS2);
|
|
842
1149
|
return { matras: half ? whole : Math.max(1, whole), half };
|
|
843
1150
|
}
|
|
844
1151
|
function cellPitchedOpts(cell) {
|
|
@@ -1604,19 +1911,19 @@ function buildAbc(schedule, lyricOf, opts) {
|
|
|
1604
1911
|
const tokens = [];
|
|
1605
1912
|
const words = [];
|
|
1606
1913
|
let barPos = 0;
|
|
1607
|
-
const
|
|
1914
|
+
const EPS3 = 1e-6;
|
|
1608
1915
|
const advance = (dur) => {
|
|
1609
1916
|
barPos += dur;
|
|
1610
|
-
while (barPos >= beatsPerBar -
|
|
1917
|
+
while (barPos >= beatsPerBar - EPS3) {
|
|
1611
1918
|
tokens.push("|");
|
|
1612
1919
|
words.push("|");
|
|
1613
1920
|
barPos -= beatsPerBar;
|
|
1614
1921
|
}
|
|
1615
1922
|
};
|
|
1616
1923
|
schedule.events.forEach((ev, i) => {
|
|
1617
|
-
while (m + 1 < meters.length && ev.beat >= meters[m + 1].beat -
|
|
1924
|
+
while (m + 1 < meters.length && ev.beat >= meters[m + 1].beat - EPS3) {
|
|
1618
1925
|
m++;
|
|
1619
|
-
if (barPos >
|
|
1926
|
+
if (barPos > EPS3) {
|
|
1620
1927
|
tokens.push("|");
|
|
1621
1928
|
words.push("|");
|
|
1622
1929
|
}
|