@ugfoundation/swaralipi-js 0.8.1 → 0.9.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/{batch-NDL6CW7Q.js → batch-PYBXQ4OM.js} +5 -5
- package/dist/{batch-NDL6CW7Q.js.map → batch-PYBXQ4OM.js.map} +1 -1
- package/dist/{chunk-LPCKL3AM.js → chunk-3ZQPM2CE.js} +28 -19
- package/dist/chunk-3ZQPM2CE.js.map +1 -0
- package/dist/{chunk-4IVUPMRK.js → chunk-7SMRG4IS.js} +14 -9
- package/dist/chunk-7SMRG4IS.js.map +1 -0
- package/dist/{chunk-4HNT6OAT.js → chunk-MYVIQU4O.js} +12 -3
- package/dist/chunk-MYVIQU4O.js.map +1 -0
- package/dist/{chunk-Z42M5JAA.js → chunk-RT4SVJMK.js} +5 -4
- package/dist/chunk-RT4SVJMK.js.map +1 -0
- package/dist/cli.cjs +33 -19
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/convert/index.cjs +31 -17
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.d.cts +1 -1
- package/dist/convert/index.d.ts +1 -1
- package/dist/convert/index.js +3 -3
- package/dist/index.cjs +38 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{lay-Csa4CD46.d.cts → lay-CZIzR9cM.d.ts} +16 -2
- package/dist/{lay-Ct9ky_EE.d.ts → lay-CfDlPhH2.d.cts} +16 -2
- package/dist/player/index.cjs +80 -48
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +11 -9
- package/dist/player/index.d.ts +11 -9
- package/dist/player/index.js +68 -46
- package/dist/player/index.js.map +1 -1
- package/dist/{schedule-B_UaluBi.d.cts → schedule-9uvjnCG_.d.ts} +15 -10
- package/dist/{schedule-CdILTNsk.d.ts → schedule-TxSfHL-T.d.cts} +15 -10
- package/dist/{schema-DGA_bNVZ.d.ts → schema-Cf_epUIC.d.cts} +333 -63
- package/dist/{schema-DGA_bNVZ.d.cts → schema-Cf_epUIC.d.ts} +333 -63
- package/dist/spec/index.cjs +38 -17
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +26 -8
- package/dist/spec/index.d.ts +26 -8
- package/dist/spec/index.js +2 -2
- package/dist/viewer/index.cjs +306 -178
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +36 -12
- package/dist/viewer/index.d.ts +36 -12
- package/dist/viewer/index.js +234 -135
- package/dist/viewer/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4HNT6OAT.js.map +0 -1
- package/dist/chunk-4IVUPMRK.js.map +0 -1
- package/dist/chunk-LPCKL3AM.js.map +0 -1
- package/dist/chunk-Z42M5JAA.js.map +0 -1
package/dist/viewer/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isArcRaw, glideTargets, buildSchedule, drumPattern } from '../chunk-
|
|
2
|
-
import { hasKomal, TAALS, graceNotes } from '../chunk-
|
|
1
|
+
import { isArcRaw, glideTargets, buildSchedule, drumPattern } from '../chunk-7SMRG4IS.js';
|
|
2
|
+
import { hasKomal, isTaalFerta, sameTaal, TAALS, graceNotes } from '../chunk-MYVIQU4O.js';
|
|
3
3
|
|
|
4
4
|
// src/viewer/glyphs.ts
|
|
5
5
|
var SWARABITAN_SHUDDHA = {
|
|
@@ -49,6 +49,11 @@ var LAY_LABELS = {
|
|
|
49
49
|
hi: { drut: "\u0926\u094D\u0930\u0941\u0924 \u0932\u092F", madhya: "\u092E\u0927\u094D\u092F \u0932\u092F", vilambit: "\u0935\u093F\u0932\u0902\u092C\u093F\u0924 \u0932\u092F" },
|
|
50
50
|
en: { drut: "Fast (drut)", madhya: "Medium (madhya)", vilambit: "Slow (vilambit)" }
|
|
51
51
|
};
|
|
52
|
+
var FREE_TAAL_LABELS = {
|
|
53
|
+
bn: "\u09AE\u09C1\u0995\u09CD\u09A4\u099B\u09A8\u09CD\u09A6",
|
|
54
|
+
hi: "\u092E\u0941\u0915\u094D\u0924\u091B\u0902\u0926",
|
|
55
|
+
en: "Free rhythm"
|
|
56
|
+
};
|
|
52
57
|
function composeCluster(letter, opts) {
|
|
53
58
|
const classes = [];
|
|
54
59
|
if (opts.lang === "bn") {
|
|
@@ -194,42 +199,64 @@ function alignRows(rows) {
|
|
|
194
199
|
}
|
|
195
200
|
function resolveArcs(cells) {
|
|
196
201
|
const arcs = [];
|
|
197
|
-
let
|
|
202
|
+
let open;
|
|
198
203
|
let sawEnds = false;
|
|
199
204
|
const fillCells = [];
|
|
200
|
-
const openAt = (
|
|
205
|
+
const openAt = (cell, note) => {
|
|
201
206
|
sawEnds = true;
|
|
202
|
-
|
|
207
|
+
open ??= { cell, note };
|
|
203
208
|
};
|
|
204
|
-
const closeAt = (
|
|
209
|
+
const closeAt = (cell, note) => {
|
|
205
210
|
sawEnds = true;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
const from = open ?? { cell: 0, note: 0 };
|
|
212
|
+
arcs.push({
|
|
213
|
+
start: from.cell,
|
|
214
|
+
startNote: from.note,
|
|
215
|
+
end: cell,
|
|
216
|
+
endNote: note,
|
|
217
|
+
openLeft: open === void 0,
|
|
218
|
+
openRight: false
|
|
219
|
+
});
|
|
220
|
+
open = void 0;
|
|
212
221
|
};
|
|
213
222
|
cells.forEach((cell, i) => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
const pitched = cell.notes.flatMap((n, j) => isPitched(n) ? [j] : []);
|
|
224
|
+
const first = pitched[0] ?? 0;
|
|
225
|
+
const last = pitched[pitched.length - 1] ?? cell.notes.length - 1;
|
|
226
|
+
let marks = "";
|
|
227
|
+
cell.notes.forEach((note, j) => {
|
|
228
|
+
if (note.meend === "start") openAt(i, j);
|
|
229
|
+
else if (note.meend === "end") closeAt(i, j);
|
|
230
|
+
for (const b of note.below ?? []) if (isArcRaw(b.raw)) marks += b.raw;
|
|
231
|
+
});
|
|
232
|
+
const reopens = /[xX].*[wW]/.test(marks);
|
|
233
|
+
for (const ch of marks) {
|
|
234
|
+
if (ch === "w" || ch === "W") openAt(i, reopens ? last : first);
|
|
235
|
+
else if (ch === "x" || ch === "X") closeAt(i, reopens ? first : last);
|
|
236
|
+
else if (ch === "$") {
|
|
237
|
+
openAt(i, first);
|
|
238
|
+
closeAt(i, last);
|
|
239
|
+
} else fillCells.push(i);
|
|
225
240
|
}
|
|
226
241
|
});
|
|
227
|
-
|
|
228
|
-
|
|
242
|
+
const lastCell = Math.max(cells.length - 1, 0);
|
|
243
|
+
const lastNote = (c) => Math.max((cells[c]?.notes.length ?? 1) - 1, 0);
|
|
244
|
+
if (open !== void 0) {
|
|
245
|
+
arcs.push({
|
|
246
|
+
start: open.cell,
|
|
247
|
+
startNote: open.note,
|
|
248
|
+
end: lastCell,
|
|
249
|
+
endNote: lastNote(lastCell),
|
|
250
|
+
openLeft: false,
|
|
251
|
+
openRight: true
|
|
252
|
+
});
|
|
229
253
|
} else if (!sawEnds && fillCells.length > 0) {
|
|
254
|
+
const end = Math.max(...fillCells);
|
|
230
255
|
arcs.push({
|
|
231
256
|
start: Math.min(...fillCells),
|
|
232
|
-
|
|
257
|
+
startNote: 0,
|
|
258
|
+
end,
|
|
259
|
+
endNote: lastNote(end),
|
|
233
260
|
openLeft: true,
|
|
234
261
|
openRight: true,
|
|
235
262
|
line: true
|
|
@@ -237,6 +264,35 @@ function resolveArcs(cells) {
|
|
|
237
264
|
}
|
|
238
265
|
return arcs;
|
|
239
266
|
}
|
|
267
|
+
function arcInk(cells, arcs) {
|
|
268
|
+
const ink = { cells: cells.map(() => ""), notes: cells.map((c) => c.notes.map(() => "")) };
|
|
269
|
+
const mark = (c, j, cls, clip = false) => {
|
|
270
|
+
ink.notes[c][j] += ` ${cls}`;
|
|
271
|
+
if (clip && !ink.cells[c].includes(" sw-mk")) ink.cells[c] += " sw-mk";
|
|
272
|
+
};
|
|
273
|
+
for (const arc of arcs) {
|
|
274
|
+
const { start: s, end: e, startNote: j, endNote: k } = arc;
|
|
275
|
+
const hookL = arc.line !== true && !arc.openLeft;
|
|
276
|
+
const hookR = arc.line !== true && !arc.openRight;
|
|
277
|
+
if (s === e && hookL && hookR) {
|
|
278
|
+
if (j === k) {
|
|
279
|
+
mark(s, j, "sw-mos");
|
|
280
|
+
mark(s, j, "sw-mcs");
|
|
281
|
+
} else {
|
|
282
|
+
mark(s, j, "sw-mob");
|
|
283
|
+
for (let n = j + 1; n < k; n++) mark(s, n, "sw-mf");
|
|
284
|
+
mark(s, k, "sw-mcb");
|
|
285
|
+
}
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
for (let c = s; c <= e; c++) {
|
|
289
|
+
if (c === s && hookL) mark(c, j, "sw-mo", true);
|
|
290
|
+
else if (c === e && hookR) mark(c, k, "sw-mc", true);
|
|
291
|
+
else mark(c, 0, "sw-mm", true);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return ink;
|
|
295
|
+
}
|
|
240
296
|
var SVARA_CHAR = /[srgmpqndtkuv]/;
|
|
241
297
|
function rawMarks(raw, count) {
|
|
242
298
|
if (raw === void 0) return void 0;
|
|
@@ -412,25 +468,9 @@ function renderNote(note, kind, opts, ctx) {
|
|
|
412
468
|
const raw = rawDisplay(note);
|
|
413
469
|
return bracketGlyphs(note, "open") + (raw !== void 0 ? `<span class="sw-raw" title="${escapeHtml(raw)}">${escapeHtml(raw)}</span>` : "") + bracketGlyphs(note, "close");
|
|
414
470
|
}
|
|
415
|
-
function renderArc(arc, offset) {
|
|
416
|
-
const open = arc.line === true;
|
|
417
|
-
const openL = arc.openLeft || open;
|
|
418
|
-
const openR = arc.openRight || open;
|
|
419
|
-
const fill = '<i class="sw-arc-m"></i>';
|
|
420
|
-
const hookL = openL ? "" : '<i class="sw-arc-l">w</i>';
|
|
421
|
-
const hookR = openR ? "" : '<i class="sw-arc-r">x</i>';
|
|
422
|
-
const piece = (col, cls, inner) => `<span class="sw-arc${cls}" style="grid-column:${col}">${inner}</span>`;
|
|
423
|
-
const f = offset + arc.start + COL_BASE;
|
|
424
|
-
const t = offset + arc.end + COL_BASE;
|
|
425
|
-
const openCls = (openL ? " sw-arc--openl" : "") + (openR ? " sw-arc--openr" : "");
|
|
426
|
-
if (t === f) return piece(`${f}`, openCls, hookL + fill + hookR);
|
|
427
|
-
const head = piece(`${f}`, openL ? " sw-arc--openl" : " sw-arc--head", hookL + fill);
|
|
428
|
-
const tail = piece(`${t}`, openR ? " sw-arc--openr" : " sw-arc--tail", fill + hookR);
|
|
429
|
-
const mid = t > f + 1 ? piece(`${f + 1} / ${t}`, " sw-arc--mid", fill) : "";
|
|
430
|
-
return head + mid + tail;
|
|
431
|
-
}
|
|
432
471
|
function renderRow(row, partIdx, rowIdx, offset, ctx) {
|
|
433
472
|
const cells = groupRowIntoCells(row.notes);
|
|
473
|
+
const ink = arcInk(cells, resolveArcs(cells));
|
|
434
474
|
const out = [];
|
|
435
475
|
if (row.label !== void 0)
|
|
436
476
|
out.push(`<span class="sw-label">${escapeHtml(ctx.tr(row.label))}</span>`);
|
|
@@ -440,11 +480,10 @@ function renderRow(row, partIdx, rowIdx, offset, ctx) {
|
|
|
440
480
|
const barCls = cell.kind === "bar" ? " sw-bar-cell" : "";
|
|
441
481
|
const notes = cell.notes.map((note, j) => {
|
|
442
482
|
const path = `${partIdx}.${rowIdx}.${cell.start + j}`;
|
|
443
|
-
const cls = isPitched(note) ? " sw-p" : "";
|
|
444
483
|
const bar = note.bar !== void 0 ? ` sw-bar sw-bar--${note.bar}` : "";
|
|
445
|
-
return `<span class="sw-n${
|
|
484
|
+
return `<span class="sw-n${bar}${ink.notes[ci][j]}" data-note="${path}">${renderNote(note, cell.kind, opts[j], ctx)}</span>`;
|
|
446
485
|
}).join("");
|
|
447
|
-
out.push(`<span class="sw-cell${barCls}" style="grid-column:${col}">${notes}</span>`);
|
|
486
|
+
out.push(`<span class="sw-cell${barCls}${ink.cells[ci]}" style="grid-column:${col}">${notes}</span>`);
|
|
448
487
|
const slices = cellLyricSlices(cell);
|
|
449
488
|
if (slices !== void 0) {
|
|
450
489
|
const html = slices.map((t) => `<span>${escapeHtml(lyricText(t, ctx))}</span>`).join("");
|
|
@@ -457,7 +496,6 @@ function renderRow(row, partIdx, rowIdx, offset, ctx) {
|
|
|
457
496
|
if (below.length > 0)
|
|
458
497
|
out.push(`<span class="sw-below" style="grid-column:${col}">${renderAnnotation(below, ctx)}</span>`);
|
|
459
498
|
});
|
|
460
|
-
for (const arc of resolveArcs(cells)) out.push(renderArc(arc, offset));
|
|
461
499
|
return `<div class="sw-row">${out.join("")}</div>`;
|
|
462
500
|
}
|
|
463
501
|
function renderHeader(meta, ctx) {
|
|
@@ -488,15 +526,25 @@ function renderToHtml(doc, opts = {}) {
|
|
|
488
526
|
out.push(`<div class="sw-scroll"><div class="sw-block" style="${tracks}">`);
|
|
489
527
|
let n = 0;
|
|
490
528
|
let lay;
|
|
529
|
+
let taal = doc.meta.taal;
|
|
530
|
+
let named = !isTaalFerta(doc.meta.taalName);
|
|
491
531
|
doc.parts.forEach((part, p) => {
|
|
492
532
|
out.push('<section class="sw-part">');
|
|
493
533
|
if (part.name !== void 0)
|
|
494
534
|
out.push(`<h3 class="sw-part-name">${escapeHtml(ctx.tr(part.name))}</h3>`);
|
|
495
535
|
part.rows.forEach((row, r) => {
|
|
536
|
+
const marks = [];
|
|
537
|
+
if (row.taal !== void 0 && (!named || !sameTaal(row.taal, taal))) {
|
|
538
|
+
taal = row.taal;
|
|
539
|
+
named = true;
|
|
540
|
+
const name = row.taalName ?? (taal === null ? FREE_TAAL_LABELS[ctx.lang] : taal.id);
|
|
541
|
+
marks.push(`<span class="sw-taal" data-taal="${escapeHtml(taal?.id ?? "free")}">${escapeHtml(ctx.tr(name))}</span>`);
|
|
542
|
+
}
|
|
496
543
|
if (row.lay !== void 0 && row.lay !== lay) {
|
|
497
544
|
lay = row.lay;
|
|
498
|
-
|
|
545
|
+
marks.push(`<span class="sw-lay" data-lay="${lay}">${escapeHtml(LAY_LABELS[ctx.lang][lay])}</span>`);
|
|
499
546
|
}
|
|
547
|
+
if (marks.length > 0) out.push(`<div class="sw-change">${marks.join("")}</div>`);
|
|
500
548
|
out.push(renderRow(row, p, r, layout.offsets[n++], ctx));
|
|
501
549
|
});
|
|
502
550
|
out.push("</section>");
|
|
@@ -515,7 +563,7 @@ function fitSwaralipi(root, minScale = 0, zoom = 1, minSpacing = 0.35, minSize =
|
|
|
515
563
|
sheet.style.removeProperty("--sw-size");
|
|
516
564
|
sheet.style.removeProperty("--sw-tight");
|
|
517
565
|
const available = scroll.clientWidth;
|
|
518
|
-
let natural = block.
|
|
566
|
+
let natural = block.offsetWidth;
|
|
519
567
|
if (available === 0 || natural === 0) continue;
|
|
520
568
|
if (natural > available && minSpacing < 1) {
|
|
521
569
|
let loose = minSpacing;
|
|
@@ -523,11 +571,11 @@ function fitSwaralipi(root, minScale = 0, zoom = 1, minSpacing = 0.35, minSize =
|
|
|
523
571
|
for (let i = 0; i < 6; i++) {
|
|
524
572
|
const mid = (loose + tight) / 2;
|
|
525
573
|
sheet.style.setProperty("--sw-tight", String(mid));
|
|
526
|
-
if (block.
|
|
574
|
+
if (block.offsetWidth <= available) loose = mid;
|
|
527
575
|
else tight = mid;
|
|
528
576
|
}
|
|
529
577
|
sheet.style.setProperty("--sw-tight", String(loose));
|
|
530
|
-
natural = block.
|
|
578
|
+
natural = block.offsetWidth;
|
|
531
579
|
}
|
|
532
580
|
const base = parseFloat(getComputedStyle(sheet).fontSize);
|
|
533
581
|
if (!(base > 0)) continue;
|
|
@@ -590,10 +638,12 @@ ${FONT_FACES}
|
|
|
590
638
|
|
|
591
639
|
.sw-part-name { font-size: 0.85em; font-weight: 600; margin: 1em 0 0.2em; opacity: 0.85; }
|
|
592
640
|
|
|
593
|
-
/* \u09B2\u09AF\u09BC
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
641
|
+
/* \u09A4\u09BE\u09B2 / \u09B2\u09AF\u09BC change: the line where the \u09A4\u09BE\u09B2\u09AB\u09C7\u09B0\u09A4\u09BE or the tempo tier turns over,
|
|
642
|
+
centred between two systems the way \u09B8\u09CD\u09AC\u09B0\u09AC\u09BF\u09A4\u09BE\u09A8 prints "\u099D\u09BE\u0981\u09AA\u09A4\u09BE\u09B2" or
|
|
643
|
+
"\u09A6\u09CD\u09B0\u09C1\u09A4 \u09B2\u09AF\u09BC". A full-width band rather than a row label \u2014 the label track is
|
|
644
|
+
what the notation columns are measured against, and a six-em word in it
|
|
645
|
+
would shove the whole sheet right. */
|
|
646
|
+
.sw-change {
|
|
597
647
|
grid-column: 1 / -1;
|
|
598
648
|
display: flex;
|
|
599
649
|
align-items: center;
|
|
@@ -607,12 +657,14 @@ ${FONT_FACES}
|
|
|
607
657
|
opacity: 0.75;
|
|
608
658
|
break-after: avoid;
|
|
609
659
|
}
|
|
610
|
-
.sw-
|
|
660
|
+
.sw-change::before,
|
|
661
|
+
.sw-change::after {
|
|
611
662
|
content: '';
|
|
612
663
|
flex: 1;
|
|
613
664
|
height: 1px;
|
|
614
665
|
background: var(--sw-rule, rgba(128, 128, 128, 0.35));
|
|
615
666
|
}
|
|
667
|
+
.sw-taal + .sw-lay::before { content: '\xB7'; margin-right: 0.6em; }
|
|
616
668
|
|
|
617
669
|
/* ---- block grid: ONE column set for the whole sheet ----
|
|
618
670
|
* NLTR emits a song as a single html table, which is why its lines align
|
|
@@ -625,31 +677,45 @@ ${FONT_FACES}
|
|
|
625
677
|
column-gap: calc(var(--sw-cell-gap, 0.4em) * var(--sw-tight, 1));
|
|
626
678
|
align-items: end;
|
|
627
679
|
width: max-content;
|
|
680
|
+
/* The last column's cell spacers overhang half a gap past the grid; clip
|
|
681
|
+
that sideways so it neither counts as width to fit nor raises a scrollbar. */
|
|
682
|
+
overflow-x: clip;
|
|
628
683
|
}
|
|
629
684
|
.sw-part { display: contents; }
|
|
685
|
+
/* Four FIXED bands per row \u2014 above / notation / arc / lyric \u2014 so a beat label,
|
|
686
|
+
* a \u0995\u09A3 or a \u09AE\u09C0\u09A1\u09BC never changes how far a line sits from its words: the
|
|
687
|
+
* notation hugs its lyric and the next system starts a little further off,
|
|
688
|
+
* as the printed \u09B8\u09CD\u09AC\u09B0\u09AC\u09BF\u09A4\u09BE\u09A8 sets it. Content taller than its band overflows
|
|
689
|
+
* rather than pushing the rhythm apart. */
|
|
630
690
|
.sw-row {
|
|
631
691
|
grid-column: 1 / -1;
|
|
632
692
|
display: grid;
|
|
633
693
|
grid-template-columns: subgrid;
|
|
634
|
-
grid-template-rows:
|
|
694
|
+
grid-template-rows: 0.95em 1.35em 0.4em 1.35em;
|
|
635
695
|
align-items: end;
|
|
636
|
-
padding: 0.
|
|
696
|
+
padding-bottom: 0.35em;
|
|
697
|
+
line-height: 1.35;
|
|
637
698
|
}
|
|
638
699
|
.sw-label { grid-column: 1; grid-row: 2; padding-right: 0.5em; font-weight: 600; }
|
|
639
700
|
|
|
640
|
-
/* One column per cell
|
|
641
|
-
* flex
|
|
642
|
-
*
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
.sw-cell
|
|
646
|
-
.sw-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
.sw-
|
|
652
|
-
.sw-
|
|
701
|
+
/* One column per cell, its notes set tight as one word (\u09B0\u09B8\u09BE) and centred by
|
|
702
|
+
* two flex spacers. Each spacer overhangs half the column gap (a min-width
|
|
703
|
+
* paid back by a negative margin, so the column is sized by the notes alone),
|
|
704
|
+
* which lets neighbouring cells' spacers meet mid-gap: a \u09AE\u09C0\u09A1\u09BC crossing cells is
|
|
705
|
+
* inked by these boxes with no hole between them. */
|
|
706
|
+
.sw-cell, .sw-lyr { display: flex; justify-content: center; white-space: nowrap; }
|
|
707
|
+
.sw-cell {
|
|
708
|
+
--sw-half-gap: calc(var(--sw-cell-gap, 0.4em) * var(--sw-tight, 1) / 2);
|
|
709
|
+
grid-row: 2;
|
|
710
|
+
align-self: stretch;
|
|
711
|
+
}
|
|
712
|
+
.sw-cell::before, .sw-cell::after { content: ''; flex: 1 0 0; min-width: var(--sw-half-gap); }
|
|
713
|
+
.sw-cell::before { margin-left: calc(var(--sw-half-gap) * -1); }
|
|
714
|
+
.sw-cell::after { margin-right: calc(var(--sw-half-gap) * -1); }
|
|
715
|
+
.sw-cell > .sw-n { position: relative; }
|
|
716
|
+
.sw-above { grid-row: 1; font-size: 0.62em; line-height: 1; justify-self: center; white-space: nowrap; }
|
|
717
|
+
.sw-below { grid-row: 3; font-size: 0.62em; line-height: 1; justify-self: center; white-space: nowrap; }
|
|
718
|
+
.sw-lyr { grid-row: 4; }
|
|
653
719
|
|
|
654
720
|
/* ---- notation clusters & marks ---- */
|
|
655
721
|
.sw-cl { position: relative; display: inline-block; }
|
|
@@ -670,12 +736,16 @@ ${FONT_FACES}
|
|
|
670
736
|
.sw-ati-taar::before { top: -0.3em; }
|
|
671
737
|
.sw-ati-mandra::before { bottom: -0.35em; }
|
|
672
738
|
|
|
673
|
-
/* grace notes: small raised clusters \u2014 \u0995\u09A3 before the note, \u09B0\u09C7\u09B6 after it
|
|
739
|
+
/* grace notes: small raised clusters \u2014 \u0995\u09A3 before the note, \u09B0\u09C7\u09B6 after it.
|
|
740
|
+
* line-height 0 (here and on .sw-micro): a raised inline box taller than the
|
|
741
|
+
* strut grows its line box and drops that one note's baseline below its
|
|
742
|
+
* neighbours' (css2 \xA710.8). */
|
|
674
743
|
.sw-kan,
|
|
675
744
|
.sw-resh {
|
|
676
745
|
position: relative;
|
|
677
746
|
display: inline-block;
|
|
678
747
|
font-size: 0.58em;
|
|
748
|
+
line-height: 0;
|
|
679
749
|
vertical-align: 0.75em;
|
|
680
750
|
}
|
|
681
751
|
.sw-kan { margin-right: 0.08em; }
|
|
@@ -707,7 +777,7 @@ ${FONT_FACES}
|
|
|
707
777
|
.sw-cut::before { top: 0.2em; }
|
|
708
778
|
.sw-cut::after { top: 0.55em; }
|
|
709
779
|
.sw-br { opacity: 0.8; padding: 0 0.06em; }
|
|
710
|
-
.sw-micro { font-size: 0.6em; vertical-align: super; opacity: 0.8; }
|
|
780
|
+
.sw-micro { font-size: 0.6em; line-height: 0; vertical-align: super; opacity: 0.8; }
|
|
711
781
|
|
|
712
782
|
/* raw fallback: never dropped, visibly non-notation. Swarabitan first \u2014
|
|
713
783
|
* raw IS that font's encoding, so undecoded cells show NLTR's own glyphs
|
|
@@ -727,49 +797,46 @@ ${FONT_FACES}
|
|
|
727
797
|
/* a bar owns a whole column but needs no matra of width */
|
|
728
798
|
.sw-bar-cell { min-width: 0; }
|
|
729
799
|
|
|
730
|
-
/* ---- \u09AE\u09C0\u09A1\u09BC under-arcs (from w/x segments and spec meend) ----
|
|
731
|
-
* Swarabitan
|
|
732
|
-
* flat
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
.sw-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
.sw-
|
|
745
|
-
.sw-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
.sw-
|
|
759
|
-
.sw-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
margin-bottom: -0.01em;
|
|
771
|
-
background: currentColor;
|
|
772
|
-
}
|
|
800
|
+
/* ---- \u09AE\u09C0\u09A1\u09BC under-arcs (from w/x/$ segments and spec meend) ----
|
|
801
|
+
* Swarabitan's own arc, all langs: w is the opening hook, curling down from
|
|
802
|
+
* its tip into a flat, x its mirror, y that flat alone \u2014 and they join
|
|
803
|
+
* hole-free, so one run of text is a whole end (tools/align-check/glyph.html).
|
|
804
|
+
* Each end sets its hook tip on its note's centre and runs the flat toward the
|
|
805
|
+
* other end (render.ts arcInk): across cells it is cut at the mid-gap, where
|
|
806
|
+
* the next cell's run carries on (.sw-mk clips a cell half a gap past each
|
|
807
|
+
* side, as far as its spacers reach); inside one cell, at its note's edge. So
|
|
808
|
+
* a long arc extends the flat and a short one trims it off. line-height 0 sets
|
|
809
|
+
* every run's baseline the same distance below its note. The clip reaches
|
|
810
|
+
* 0.5px further so two cells' anti-aliased edges overlap instead of leaving a
|
|
811
|
+
* hairline seam between their identical flats. */
|
|
812
|
+
.sw-mk { clip-path: inset(-3em calc(var(--sw-half-gap) * -1 - 0.5px)); }
|
|
813
|
+
.sw-mo::after, .sw-mob::after, .sw-mos::after,
|
|
814
|
+
.sw-mc::before, .sw-mcb::before, .sw-mcs::before,
|
|
815
|
+
.sw-mf::before, .sw-mm::before {
|
|
816
|
+
content: 'yyyyyyyyyyyy';
|
|
817
|
+
position: absolute;
|
|
818
|
+
top: calc(100% + 0.05em);
|
|
819
|
+
font-family: Swarabitan;
|
|
820
|
+
font-size: 0.9em;
|
|
821
|
+
line-height: 0;
|
|
822
|
+
white-space: pre;
|
|
823
|
+
overflow-x: clip;
|
|
824
|
+
}
|
|
825
|
+
.sw-mo::after, .sw-mob::after, .sw-mos::after { content: 'wyyyyyyyyyyyy'; }
|
|
826
|
+
/* rtl: a closing run cut short keeps its hook on its right edge */
|
|
827
|
+
.sw-mc::before, .sw-mcb::before, .sw-mcs::before { content: 'yyyyyyyyyyyyx'; direction: rtl; }
|
|
828
|
+
.sw-mo::after, .sw-mob::after { left: 50%; }
|
|
829
|
+
.sw-mc::before, .sw-mcb::before { right: 50%; }
|
|
830
|
+
.sw-mos::after { left: 0; }
|
|
831
|
+
.sw-mcs::before { right: 0; }
|
|
832
|
+
.sw-mf::before { left: 0; right: 0; }
|
|
833
|
+
.sw-mm::before { left: -12em; }
|
|
834
|
+
/* Inside one cell a hook gets half a note to curl in, about a third of its
|
|
835
|
+
* curl: squeezed to half width (same stroke) it lands flat at the cut, so the
|
|
836
|
+
* two flats meet level whatever the notes' widths \u2014 a small arc, not a V. */
|
|
837
|
+
.sw-mob::after, .sw-mos::after, .sw-mcb::before, .sw-mcs::before { width: 100%; transform: scaleX(0.5); }
|
|
838
|
+
.sw-mob::after, .sw-mos::after { transform-origin: left; }
|
|
839
|
+
.sw-mcb::before, .sw-mcs::before { transform-origin: right; }
|
|
773
840
|
|
|
774
841
|
/* ---- annotation content ---- */
|
|
775
842
|
.sw-beat { position: relative; display: inline-block; padding: 0 0.1em; }
|
|
@@ -815,7 +882,7 @@ var SWARALIPI_PRINT_CSS = `
|
|
|
815
882
|
.sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }
|
|
816
883
|
.sw-scroll { overflow: visible !important; }
|
|
817
884
|
.sw-row { break-inside: avoid; page-break-inside: avoid; }
|
|
818
|
-
.sw-part-name, .sw-header, .sw-
|
|
885
|
+
.sw-part-name, .sw-header, .sw-change { break-after: avoid-page; page-break-after: avoid; }
|
|
819
886
|
.sw-hl { background: none !important; }
|
|
820
887
|
/* Keep each engraved staff system whole across a page break (the abcjs line
|
|
821
888
|
is a <div> wrapping the <svg>, so the break rule must sit on the div). */
|
|
@@ -872,11 +939,15 @@ function eventPaths(schedule) {
|
|
|
872
939
|
}
|
|
873
940
|
return paths;
|
|
874
941
|
}
|
|
942
|
+
var barLen = (m) => m.beatsPerBar > 0 ? m.beatsPerBar : 4;
|
|
875
943
|
function abcText(s) {
|
|
876
944
|
return s.replace(/[\r\n]+/g, " ").trim();
|
|
877
945
|
}
|
|
878
946
|
function buildAbc(schedule, lyricOf, opts) {
|
|
879
|
-
const
|
|
947
|
+
const meters = opts.meters?.length ? opts.meters : [{ beat: 0, beatsPerBar: opts.beatsPerBar, drums: opts.drums }];
|
|
948
|
+
const withDrums = opts.drums !== void 0 || meters.some((m2) => m2.drums !== void 0);
|
|
949
|
+
let m = 0;
|
|
950
|
+
let beatsPerBar = barLen(meters[0]);
|
|
880
951
|
const tokens = [];
|
|
881
952
|
const words = [];
|
|
882
953
|
let barPos = 0;
|
|
@@ -890,6 +961,17 @@ function buildAbc(schedule, lyricOf, opts) {
|
|
|
890
961
|
}
|
|
891
962
|
};
|
|
892
963
|
schedule.events.forEach((ev, i) => {
|
|
964
|
+
while (m + 1 < meters.length && ev.beat >= meters[m + 1].beat - EPS2) {
|
|
965
|
+
m++;
|
|
966
|
+
if (barPos > EPS2) {
|
|
967
|
+
tokens.push("|");
|
|
968
|
+
words.push("|");
|
|
969
|
+
}
|
|
970
|
+
barPos = 0;
|
|
971
|
+
const next = barLen(meters[m]);
|
|
972
|
+
if (next !== beatsPerBar) tokens.push(`[M:${ratioMeter(next)}]`);
|
|
973
|
+
beatsPerBar = next;
|
|
974
|
+
}
|
|
893
975
|
if (ev.midi === null) {
|
|
894
976
|
if (ev.kind === "rest") {
|
|
895
977
|
tokens.push(`z${abcDuration(ev.durationBeats)}`);
|
|
@@ -908,16 +990,16 @@ function buildAbc(schedule, lyricOf, opts) {
|
|
|
908
990
|
});
|
|
909
991
|
if (tokens.length && tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
910
992
|
else if (tokens.length) tokens[tokens.length - 1] = "|]";
|
|
911
|
-
const meter = ratioMeter(
|
|
993
|
+
const meter = ratioMeter(barLen(meters[0]));
|
|
912
994
|
const head = ["X:1", `M:${meter}`, "L:1/4", `Q:1/4=${Math.round(opts.bpm)}`];
|
|
913
|
-
if (
|
|
995
|
+
if (withDrums) head.push(...percMapLines(meters.flatMap((x) => x.drums ?? [])));
|
|
914
996
|
head.push("K:C treble");
|
|
915
997
|
const melodyBody = tokens.length ? tokens.join(" ") : "z |]";
|
|
916
998
|
const lines = [...head];
|
|
917
|
-
if (
|
|
999
|
+
if (withDrums) {
|
|
918
1000
|
lines.push("V:1", melodyBody);
|
|
919
1001
|
if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
|
|
920
|
-
lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats,
|
|
1002
|
+
lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats, meters));
|
|
921
1003
|
} else {
|
|
922
1004
|
lines.push(melodyBody);
|
|
923
1005
|
if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
|
|
@@ -946,13 +1028,21 @@ function percMapLines(voices) {
|
|
|
946
1028
|
}
|
|
947
1029
|
return lines;
|
|
948
1030
|
}
|
|
949
|
-
function buildDrumVoice(totalBeats,
|
|
950
|
-
const n = Math.max(1, Math.ceil(totalBeats));
|
|
1031
|
+
function buildDrumVoice(totalBeats, meters) {
|
|
951
1032
|
const tokens = [];
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
if (
|
|
955
|
-
|
|
1033
|
+
meters.forEach((m, i) => {
|
|
1034
|
+
const bpb = barLen(m);
|
|
1035
|
+
if (i > 0) {
|
|
1036
|
+
if (tokens.length > 0 && tokens[tokens.length - 1] !== "|") tokens.push("|");
|
|
1037
|
+
if (bpb !== barLen(meters[i - 1])) tokens.push(`[M:${ratioMeter(bpb)}]`);
|
|
1038
|
+
}
|
|
1039
|
+
const end = meters[i + 1]?.beat ?? Math.max(1, Math.ceil(totalBeats));
|
|
1040
|
+
const n = Math.ceil(end - m.beat - 1e-6);
|
|
1041
|
+
for (let beat = 0; beat < n; beat++) {
|
|
1042
|
+
tokens.push(m.drums?.length ? DRUM_ABC[m.drums[beat % m.drums.length]].note : "z");
|
|
1043
|
+
if ((beat + 1) % bpb === 0) tokens.push("|");
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
956
1046
|
if (tokens.length === 0 || tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
957
1047
|
else tokens[tokens.length - 1] = "|]";
|
|
958
1048
|
return tokens.join(" ");
|
|
@@ -963,16 +1053,25 @@ function lyricLookup(doc) {
|
|
|
963
1053
|
return doc.parts[p]?.rows[r]?.notes[i]?.lyric;
|
|
964
1054
|
};
|
|
965
1055
|
}
|
|
966
|
-
function resolveDrumTaal(
|
|
967
|
-
const id =
|
|
1056
|
+
function resolveDrumTaal(taal) {
|
|
1057
|
+
const id = taal?.id;
|
|
968
1058
|
return id !== void 0 && id in TAALS ? TAALS[id] : { numBeats: 4, sam: 0 };
|
|
969
1059
|
}
|
|
970
1060
|
function docToAbc(doc, opts = {}) {
|
|
971
1061
|
const schedule = buildSchedule(doc, { tonic: opts.tonic, baseOctave: opts.baseOctave, lay: false });
|
|
972
1062
|
const bpm = opts.bpm ?? doc.meta.bpm ?? 90;
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
1063
|
+
const meters = schedule.tempo.map((seg) => ({
|
|
1064
|
+
beat: seg.beat,
|
|
1065
|
+
beatsPerBar: opts.beatsPerBar ?? seg.taal?.numBeats ?? 4,
|
|
1066
|
+
drums: opts.drums && seg.taal !== null ? drumPattern(resolveDrumTaal(seg.taal)) : void 0
|
|
1067
|
+
}));
|
|
1068
|
+
return buildAbc(schedule, lyricLookup(doc), {
|
|
1069
|
+
bpm,
|
|
1070
|
+
lyrics: opts.lyrics !== false,
|
|
1071
|
+
beatsPerBar: meters[0].beatsPerBar,
|
|
1072
|
+
drums: opts.drums ? meters[0].drums ?? [] : void 0,
|
|
1073
|
+
meters
|
|
1074
|
+
});
|
|
976
1075
|
}
|
|
977
1076
|
async function abcToSvg(abc, opts = {}, notePaths) {
|
|
978
1077
|
if (typeof document === "undefined") {
|
|
@@ -1051,7 +1150,7 @@ function buildTaalAbc(taal, opts = {}) {
|
|
|
1051
1150
|
const voices = Array.from({ length: n }, (_, i) => src[i % src.length]);
|
|
1052
1151
|
const bpm = Math.round(opts.bpm ?? 90);
|
|
1053
1152
|
const head = ["X:1", `M:${ratioMeter(n)}`, "L:1/4", `Q:1/4=${bpm}`, ...percMapLines(voices), "K:C"];
|
|
1054
|
-
const lines = [...head, "V:1 clef=perc stem=up", buildDrumVoice(n, n, voices)];
|
|
1153
|
+
const lines = [...head, "V:1 clef=perc stem=up", buildDrumVoice(n, [{ beat: 0, beatsPerBar: n, drums: voices }])];
|
|
1055
1154
|
if (opts.bols?.length) {
|
|
1056
1155
|
const w = opts.bols.slice(0, n).map((b) => b.trim() ? abcText(b).replace(/\s+/g, "~") : "*");
|
|
1057
1156
|
while (w.length < n) w.push("*");
|
|
@@ -1439,6 +1538,6 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
|
|
|
1439
1538
|
return ctor;
|
|
1440
1539
|
}
|
|
1441
1540
|
|
|
1442
|
-
export { DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, LABELS, LAY_LABELS, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
|
|
1541
|
+
export { DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, FREE_TAAL_LABELS, LABELS, LAY_LABELS, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
|
|
1443
1542
|
//# sourceMappingURL=index.js.map
|
|
1444
1543
|
//# sourceMappingURL=index.js.map
|