@ugfoundation/swaralipi-js 0.2.0 → 0.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/dist/{batch-CIAXVKP5.js → batch-QPSHVNDS.js} +4 -4
- package/dist/{batch-CIAXVKP5.js.map → batch-QPSHVNDS.js.map} +1 -1
- package/dist/{chunk-2NBNF6JP.js → chunk-4YTOLN3C.js} +3 -3
- package/dist/{chunk-2NBNF6JP.js.map → chunk-4YTOLN3C.js.map} +1 -1
- package/dist/chunk-V5ZSUJCA.js +354 -0
- package/dist/chunk-V5ZSUJCA.js.map +1 -0
- package/dist/{chunk-SH2INDZ2.js → chunk-VCRWS3F3.js} +3 -3
- package/dist/{chunk-SH2INDZ2.js.map → chunk-VCRWS3F3.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/convert/index.cjs +1 -1
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/player/index.cjs +58 -2
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +12 -158
- package/dist/player/index.d.ts +12 -158
- package/dist/player/index.js +31 -324
- package/dist/player/index.js.map +1 -1
- package/dist/schedule-DhO_Xzz4.d.cts +167 -0
- package/dist/schedule-PTHUONrA.d.ts +167 -0
- package/dist/spec/index.cjs +1 -1
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +1 -1
- package/dist/spec/index.d.ts +1 -1
- package/dist/spec/index.js +1 -1
- package/dist/viewer/index.cjs +684 -202
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +217 -64
- package/dist/viewer/index.d.ts +217 -64
- package/dist/viewer/index.js +421 -177
- package/dist/viewer/index.js.map +1 -1
- package/package.json +7 -2
package/dist/viewer/index.cjs
CHANGED
|
@@ -11,6 +11,30 @@ var __export = (target, all) => {
|
|
|
11
11
|
for (var name in all)
|
|
12
12
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
13
|
};
|
|
14
|
+
|
|
15
|
+
// src/spec/taals.ts
|
|
16
|
+
var TAALS;
|
|
17
|
+
var init_taals = __esm({
|
|
18
|
+
"src/spec/taals.ts"() {
|
|
19
|
+
TAALS = {
|
|
20
|
+
dadra: { id: "dadra", numBeats: 6, bhaags: [3, 3], sam: 0, khaali: [3] },
|
|
21
|
+
kehrwa: { id: "kehrwa", numBeats: 8, bhaags: [4, 4], sam: 0, khaali: [4] },
|
|
22
|
+
rupak: { id: "rupak", numBeats: 7, bhaags: [3, 2, 2], sam: 0, khaali: [0] },
|
|
23
|
+
jhaptaal: { id: "jhaptaal", numBeats: 10, bhaags: [2, 3, 2, 3], sam: 0, khaali: [5] },
|
|
24
|
+
ektaal: { id: "ektaal", numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] },
|
|
25
|
+
adachautaal: { id: "adachautaal", numBeats: 14, sam: 0 },
|
|
26
|
+
choutaal: { id: "choutaal", numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] },
|
|
27
|
+
// চৌতাল
|
|
28
|
+
shashthi: { id: "shashthi", numBeats: 6, bhaags: [2, 4], sam: 0 },
|
|
29
|
+
// ষষ্ঠী
|
|
30
|
+
teora: { id: "teora", numBeats: 7, bhaags: [3, 2, 2], sam: 0 },
|
|
31
|
+
// টেওরা
|
|
32
|
+
teentaal: { id: "teentaal", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },
|
|
33
|
+
aratheka: { id: "aratheka", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] }
|
|
34
|
+
// আড়াঠেকা
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
14
38
|
var octaveSchema, svaraLetterSchema, svaraSchema, microtoneSchema, barSchema, bracketSchema, noteSchema, rowSchema, partSchema, taalSchema, metaSchema, swaralipiSchema;
|
|
15
39
|
var init_schema = __esm({
|
|
16
40
|
"src/spec/schema.ts"() {
|
|
@@ -123,30 +147,6 @@ var init_schema = __esm({
|
|
|
123
147
|
}
|
|
124
148
|
});
|
|
125
149
|
|
|
126
|
-
// src/spec/taals.ts
|
|
127
|
-
var TAALS;
|
|
128
|
-
var init_taals = __esm({
|
|
129
|
-
"src/spec/taals.ts"() {
|
|
130
|
-
TAALS = {
|
|
131
|
-
dadra: { id: "dadra", numBeats: 6, bhaags: [3, 3], sam: 0, khaali: [3] },
|
|
132
|
-
kehrwa: { id: "kehrwa", numBeats: 8, bhaags: [4, 4], sam: 0, khaali: [4] },
|
|
133
|
-
rupak: { id: "rupak", numBeats: 7, bhaags: [3, 2, 2], sam: 0, khaali: [0] },
|
|
134
|
-
jhaptaal: { id: "jhaptaal", numBeats: 10, bhaags: [2, 3, 2, 3], sam: 0, khaali: [5] },
|
|
135
|
-
ektaal: { id: "ektaal", numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] },
|
|
136
|
-
adachautaal: { id: "adachautaal", numBeats: 14, sam: 0 },
|
|
137
|
-
choutaal: { id: "choutaal", numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] },
|
|
138
|
-
// চৌতাল
|
|
139
|
-
shashthi: { id: "shashthi", numBeats: 6, bhaags: [2, 4], sam: 0 },
|
|
140
|
-
// ষষ্ঠী
|
|
141
|
-
teora: { id: "teora", numBeats: 7, bhaags: [3, 2, 2], sam: 0 },
|
|
142
|
-
// টেওরা
|
|
143
|
-
teentaal: { id: "teentaal", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },
|
|
144
|
-
aratheka: { id: "aratheka", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] }
|
|
145
|
-
// আড়াঠেকা
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
150
|
// src/spec/index.ts
|
|
151
151
|
var spec_exports = {};
|
|
152
152
|
__export(spec_exports, {
|
|
@@ -179,91 +179,67 @@ var init_spec = __esm({
|
|
|
179
179
|
init_schema();
|
|
180
180
|
init_schema();
|
|
181
181
|
init_taals();
|
|
182
|
-
SPEC_VERSION = "0.
|
|
182
|
+
SPEC_VERSION = "0.3.0";
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
// src/viewer/glyphs.ts
|
|
187
|
-
var
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
},
|
|
197
|
-
hi: {
|
|
198
|
-
s: { shuddha: "\u0938" },
|
|
199
|
-
r: { shuddha: "\u0930", komal: "\u090B" },
|
|
200
|
-
g: { shuddha: "\u0917", komal: "\u091C\u094D\u091E" },
|
|
201
|
-
m: { shuddha: "\u092E", tivra: "\u0915\u094D\u0937" },
|
|
202
|
-
p: { shuddha: "\u092A" },
|
|
203
|
-
d: { shuddha: "\u0927", komal: "\u0926" },
|
|
204
|
-
n: { shuddha: "\u0928", komal: "\u0923" }
|
|
205
|
-
},
|
|
206
|
-
en: {
|
|
207
|
-
s: { shuddha: "S" },
|
|
208
|
-
r: { shuddha: "R" },
|
|
209
|
-
g: { shuddha: "G" },
|
|
210
|
-
m: { shuddha: "M" },
|
|
211
|
-
p: { shuddha: "P" },
|
|
212
|
-
d: { shuddha: "D" },
|
|
213
|
-
n: { shuddha: "N" }
|
|
214
|
-
}
|
|
187
|
+
var SWARABITAN_SHUDDHA = {
|
|
188
|
+
s: "s",
|
|
189
|
+
r: "r",
|
|
190
|
+
g: "g",
|
|
191
|
+
m: "m",
|
|
192
|
+
p: "p",
|
|
193
|
+
d: "q",
|
|
194
|
+
n: "n"
|
|
195
|
+
// q = ধ
|
|
215
196
|
};
|
|
197
|
+
var SWARABITAN_KOMAL = {
|
|
198
|
+
r: "v",
|
|
199
|
+
g: "t",
|
|
200
|
+
d: "d",
|
|
201
|
+
n: "u"
|
|
202
|
+
// ঋ জ্ঞ দ ণ
|
|
203
|
+
};
|
|
204
|
+
var SWARABITAN_TIVRA = { m: "k" };
|
|
205
|
+
var BHATKHANDE_OCTAVE = {
|
|
206
|
+
madhyam: "",
|
|
207
|
+
taar: "u",
|
|
208
|
+
mandra: "l",
|
|
209
|
+
"ati-taar": "U",
|
|
210
|
+
"ati-mandra": "L"
|
|
211
|
+
};
|
|
212
|
+
var BHATKHANDE_HAS_KOMAL = /* @__PURE__ */ new Set(["r", "g", "d", "n"]);
|
|
216
213
|
var DIGITS = {
|
|
217
214
|
bn: "\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF",
|
|
218
215
|
hi: "\u0966\u0967\u0968\u0969\u096A\u096B\u096C\u096D\u096E\u096F",
|
|
219
216
|
en: "0123456789"
|
|
220
217
|
};
|
|
221
|
-
var VOWEL_LETTER_GLYPHS = /* @__PURE__ */ new Set(["\u098B", "\u090B"]);
|
|
222
218
|
var MARKS = {
|
|
223
|
-
bn: {
|
|
224
|
-
hi: {
|
|
225
|
-
en: {
|
|
219
|
+
bn: { hyphen: "-", melisma: "\u09F9", cut: "\u0983" },
|
|
220
|
+
hi: { hyphen: "-", melisma: "\u0970", cut: "\u0903" },
|
|
221
|
+
en: { hyphen: "-", melisma: "\xB7", cut: ":" }
|
|
226
222
|
};
|
|
227
223
|
var LABELS = {
|
|
228
|
-
bn: { title: "\u09A8\u09BE\u09AE", taal: "\u09A4\u09BE\u09B2", raag: "\u09B0\u09BE\u0997", avartan: "\u0986\u09AC\u09B0\u09CD\u09A4\u09A8", notationBy: "\u09B8\u09CD\u09AC\u09B0\u09B2\u09BF\u09AA\u09BF" },
|
|
229
|
-
hi: { title: "\u0928\u093E\u092E", taal: "\u0924\u093E\u0932", raag: "\u0930\u093E\u0917", avartan: "\u0906\u0935\u0930\u094D\u0924\u0928", notationBy: "\u0938\u094D\u0935\u0930\u0932\u093F\u092A\u093F" },
|
|
230
|
-
en: { title: "Song", taal: "Taal", raag: "Raag", avartan: "Avartan", notationBy: "
|
|
224
|
+
bn: { title: "\u09A8\u09BE\u09AE", taal: "\u09A4\u09BE\u09B2", raag: "\u09B0\u09BE\u0997", avartan: "\u0986\u09AC\u09B0\u09CD\u09A4\u09A8", notationBy: "\u09B8\u09CD\u09AC\u09B0\u09B2\u09BF\u09AA\u09BF\u0995\u09BE\u09B0" },
|
|
225
|
+
hi: { title: "\u0928\u093E\u092E", taal: "\u0924\u093E\u0932", raag: "\u0930\u093E\u0917", avartan: "\u0906\u0935\u0930\u094D\u0924\u0928", notationBy: "\u0938\u094D\u0935\u0930\u0932\u093F\u092A\u093F\u0915\u093E\u0930" },
|
|
226
|
+
en: { title: "Song", taal: "Taal", raag: "Raag", avartan: "Avartan", notationBy: "Notator" }
|
|
231
227
|
};
|
|
232
228
|
function composeCluster(letter, opts) {
|
|
233
|
-
const forms = LETTERS[opts.lang][letter];
|
|
234
|
-
const marks = MARKS[opts.lang];
|
|
235
229
|
const classes = [];
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
else
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
const isVowelLetter = VOWEL_LETTER_GLYPHS.has(base);
|
|
246
|
-
let text = base;
|
|
247
|
-
switch (opts.octave) {
|
|
248
|
-
case "taar":
|
|
249
|
-
case "ati-taar":
|
|
250
|
-
if (marks.reph === "") classes.push("sw-dot-above");
|
|
251
|
-
else if (isVowelLetter) classes.push("sw-reph-css");
|
|
252
|
-
else text = marks.reph + text;
|
|
253
|
-
if (opts.octave === "ati-taar") classes.push("sw-ati-taar");
|
|
254
|
-
break;
|
|
255
|
-
case "mandra":
|
|
256
|
-
case "ati-mandra":
|
|
257
|
-
classes.push(marks.reph === "" ? "sw-dot-below" : "sw-mandra");
|
|
258
|
-
if (opts.octave === "ati-mandra") classes.push("sw-ati-mandra");
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
let matraSeparate = false;
|
|
262
|
-
if (opts.withMatra && marks.matra !== "") {
|
|
263
|
-
if (isVowelLetter) matraSeparate = true;
|
|
264
|
-
else text += marks.matra;
|
|
230
|
+
if (opts.lang === "bn") {
|
|
231
|
+
let text = opts.tivra && SWARABITAN_TIVRA[letter] || opts.komal && SWARABITAN_KOMAL[letter] || SWARABITAN_SHUDDHA[letter];
|
|
232
|
+
if (opts.octave === "taar" || opts.octave === "ati-taar") text += "f";
|
|
233
|
+
else if (opts.octave === "mandra" || opts.octave === "ati-mandra") text += "h";
|
|
234
|
+
if (opts.octave === "ati-taar") classes.push("sw-ati-taar");
|
|
235
|
+
if (opts.octave === "ati-mandra") classes.push("sw-ati-mandra");
|
|
236
|
+
if (opts.withMatra) text += "a";
|
|
237
|
+
return { text, classes };
|
|
265
238
|
}
|
|
266
|
-
|
|
239
|
+
let base = letter;
|
|
240
|
+
if (opts.tivra && letter === "m") base = "M";
|
|
241
|
+
else if (opts.komal && BHATKHANDE_HAS_KOMAL.has(letter)) base = base.toUpperCase();
|
|
242
|
+
return { text: base + BHATKHANDE_OCTAVE[opts.octave], classes };
|
|
267
243
|
}
|
|
268
244
|
function localizeDigits(s, lang) {
|
|
269
245
|
const digits = DIGITS[lang];
|
|
@@ -386,11 +362,8 @@ function bracketGlyphs(note, edge) {
|
|
|
386
362
|
if (!note.brackets) return "";
|
|
387
363
|
return note.brackets.filter((b) => b.edge === edge).map((b) => `<span class="sw-br">${BRACKET_GLYPHS[edge][b.kind]}</span>`).join("");
|
|
388
364
|
}
|
|
389
|
-
function
|
|
390
|
-
return ctx.
|
|
391
|
-
}
|
|
392
|
-
function restGlyph(ctx) {
|
|
393
|
-
return ctx.marks.matra === "" ? '<span class="sw-iso">\u2013</span>' : isoMatra(ctx);
|
|
365
|
+
function matraGlyph(ctx) {
|
|
366
|
+
return ctx.lang === "bn" ? '<span class="sw-cl">a</span>' : '<span class="sw-iso">\u2013</span>';
|
|
394
367
|
}
|
|
395
368
|
function renderKan(note, ctx) {
|
|
396
369
|
if (!note.kan) return "";
|
|
@@ -412,26 +385,24 @@ function renderPitchedInner(note, opts, ctx) {
|
|
|
412
385
|
lang: ctx.lang
|
|
413
386
|
});
|
|
414
387
|
inner += `<span class="${["sw-cl", ...c.classes].join(" ")}">${c.text}</span>`;
|
|
415
|
-
if (c.matraSeparate) inner += isoMatra(ctx);
|
|
416
388
|
} else if (note.rest === true) {
|
|
417
|
-
inner +=
|
|
389
|
+
inner += matraGlyph(ctx);
|
|
418
390
|
} else if (note.sustain === true && opts.withMatra) {
|
|
419
|
-
inner +=
|
|
391
|
+
inner += matraGlyph(ctx);
|
|
420
392
|
}
|
|
421
393
|
if (note.microtone !== void 0) {
|
|
422
|
-
const mark = note.microtone === "atikomal" ? "
|
|
394
|
+
const mark = localizeDigits(note.microtone === "atikomal" ? "2" : "1", ctx.lang);
|
|
423
395
|
inner += `<span class="sw-micro" title="${note.microtone}">${mark}</span>`;
|
|
424
396
|
}
|
|
425
397
|
const d = note.durationMatra ?? 1;
|
|
426
|
-
if (isIntegral(d) && d > 1 + EPS) inner +=
|
|
398
|
+
if (isIntegral(d) && d > 1 + EPS) inner += matraGlyph(ctx).repeat(Math.round(d) - 1);
|
|
427
399
|
if (opts.cuts > 0) inner += `<span class="sw-cut">${NBSP}${ctx.marks.cut}</span>`.repeat(opts.cuts);
|
|
428
400
|
inner += bracketGlyphs(note, "close");
|
|
429
401
|
return inner;
|
|
430
402
|
}
|
|
431
|
-
var
|
|
403
|
+
var BAR_GLYPHS = { dari: "A", single: "l", double: "L", quad: "LL" };
|
|
432
404
|
function renderBarInner(note) {
|
|
433
|
-
|
|
434
|
-
return `${bracketGlyphs(note, "open")}${strokes}${bracketGlyphs(note, "close")}`;
|
|
405
|
+
return `${bracketGlyphs(note, "open")}${BAR_GLYPHS[note.bar]}${bracketGlyphs(note, "close")}`;
|
|
435
406
|
}
|
|
436
407
|
function renderAnnotation(notes, ctx) {
|
|
437
408
|
const parts = [];
|
|
@@ -510,7 +481,7 @@ function renderRow(row, partIdx, rowIdx, ctx) {
|
|
|
510
481
|
}
|
|
511
482
|
if (note.lyric !== void 0) {
|
|
512
483
|
lyric.push(
|
|
513
|
-
`<span class="sw-lyr" style="grid-column:${col}">${escapeHtml(lyricText(note.lyric, ctx))}</span>`
|
|
484
|
+
`<span class="sw-lyr${j === 0 ? " sw-cs" : ""}" style="grid-column:${col}">${escapeHtml(lyricText(note.lyric, ctx))}</span>`
|
|
514
485
|
);
|
|
515
486
|
}
|
|
516
487
|
if (note.above !== void 0 && note.above.length > 0) {
|
|
@@ -535,21 +506,24 @@ function renderRow(row, partIdx, rowIdx, ctx) {
|
|
|
535
506
|
return `<div class="sw-row">${label}<div class="sw-grid">${above.join("")}${notation.join("")}${below.join("")}${lyric.join("")}</div></div>`;
|
|
536
507
|
}
|
|
537
508
|
function renderHeader(meta, ctx) {
|
|
538
|
-
|
|
509
|
+
return renderMetaHeader(meta, ctx.lang, ctx.tr);
|
|
510
|
+
}
|
|
511
|
+
function renderMetaHeader(meta, lang, tr = (s) => s) {
|
|
512
|
+
const L = LABELS[lang];
|
|
539
513
|
const rows = [];
|
|
540
514
|
const add = (label, value) => rows.push(
|
|
541
515
|
`<div class="sw-meta-row"><span class="sw-meta-label">${escapeHtml(label)}</span><span class="sw-meta-value">${escapeHtml(value)}</span></div>`
|
|
542
516
|
);
|
|
543
|
-
add(L.title,
|
|
517
|
+
add(L.title, tr(meta.title));
|
|
544
518
|
const taal = meta.taalName ?? meta.taal?.id;
|
|
545
|
-
if (taal !== void 0) add(L.taal,
|
|
546
|
-
if (meta.raag !== void 0) add(L.raag,
|
|
547
|
-
if (meta.avartan !== void 0) add(L.avartan, localizeDigits(String(meta.avartan),
|
|
548
|
-
if (meta.notationBy !== void 0) add(L.notationBy,
|
|
519
|
+
if (taal !== void 0) add(L.taal, tr(taal));
|
|
520
|
+
if (meta.raag !== void 0) add(L.raag, tr(meta.raag));
|
|
521
|
+
if (meta.avartan !== void 0) add(L.avartan, localizeDigits(String(meta.avartan), lang));
|
|
522
|
+
if (meta.notationBy !== void 0) add(L.notationBy, tr(meta.notationBy));
|
|
549
523
|
return `<header class="sw-header">${rows.join("")}</header>`;
|
|
550
524
|
}
|
|
551
525
|
function renderToHtml(doc, opts = {}) {
|
|
552
|
-
const lang = opts.lang
|
|
526
|
+
const lang = opts.lang === "hi" || opts.lang === "en" ? opts.lang : "bn";
|
|
553
527
|
const tr = lang !== "bn" && opts.transliterate ? opts.transliterate : (s) => s;
|
|
554
528
|
const ctx = { lang, marks: MARKS[lang], tr };
|
|
555
529
|
const out = [`<div class="sw-sheet" lang="${lang}">`];
|
|
@@ -566,13 +540,29 @@ function renderToHtml(doc, opts = {}) {
|
|
|
566
540
|
}
|
|
567
541
|
|
|
568
542
|
// src/viewer/styles.ts
|
|
543
|
+
var NOTATION_FONT_URLS = {
|
|
544
|
+
Swarabitan: "https://webfonts-4qq.pages.dev/swarabitan/Swarabitan.ttf",
|
|
545
|
+
ome_bhatkhande_hindi: "https://webfonts-4qq.pages.dev/ome-bhatkhande/OmeBhatkhandeHindi.ttf",
|
|
546
|
+
ome_bhatkhande_english: "https://webfonts-4qq.pages.dev/ome-bhatkhande/OmeBhatkhandeEnglish.ttf"
|
|
547
|
+
};
|
|
548
|
+
NOTATION_FONT_URLS.Swarabitan;
|
|
549
|
+
var FONT_FACES = Object.entries(NOTATION_FONT_URLS).map(
|
|
550
|
+
([family, url]) => `@font-face { font-family: ${family}; src: url('${url}') format('truetype'); font-display: block; }`
|
|
551
|
+
).join("\n");
|
|
569
552
|
var SWARALIPI_CSS = `
|
|
553
|
+
${FONT_FACES}
|
|
554
|
+
|
|
570
555
|
.sw-sheet {
|
|
571
556
|
font-family: var(--sw-font, 'Noto Sans Bengali', 'Kohinoor Bangla', 'Bangla Sangam MN', 'Nirmala UI', 'Noto Sans Devanagari', sans-serif);
|
|
572
557
|
font-size: var(--sw-size, 1.125rem);
|
|
573
558
|
line-height: 1.5;
|
|
574
559
|
}
|
|
575
560
|
|
|
561
|
+
/* ---- notation font, per language (the note clusters + grace notes) ---- */
|
|
562
|
+
.sw-sheet[lang="bn"] .sw-cl, .sw-sheet[lang="bn"] .sw-kan { font-family: Swarabitan, var(--sw-font, sans-serif); }
|
|
563
|
+
.sw-sheet[lang="hi"] .sw-cl, .sw-sheet[lang="hi"] .sw-kan { font-family: ome_bhatkhande_hindi, var(--sw-font, sans-serif); }
|
|
564
|
+
.sw-sheet[lang="en"] .sw-cl, .sw-sheet[lang="en"] .sw-kan { font-family: ome_bhatkhande_english, var(--sw-font, sans-serif); }
|
|
565
|
+
|
|
576
566
|
/* ---- header ---- */
|
|
577
567
|
.sw-header {
|
|
578
568
|
margin: 0 0 0.75em;
|
|
@@ -601,10 +591,10 @@ var SWARALIPI_CSS = `
|
|
|
601
591
|
align-items: end;
|
|
602
592
|
}
|
|
603
593
|
.sw-above { grid-row: 1; font-size: 0.62em; justify-self: start; white-space: nowrap; padding-left: 0.2em; }
|
|
604
|
-
.sw-n { grid-row: 2; white-space: nowrap; }
|
|
594
|
+
.sw-n { grid-row: 2; justify-self: center; white-space: nowrap; }
|
|
605
595
|
.sw-arc,
|
|
606
596
|
.sw-below { grid-row: 3; }
|
|
607
|
-
.sw-lyr { grid-row: 4; white-space: nowrap; padding-top: 0.3em; }
|
|
597
|
+
.sw-lyr { grid-row: 4; justify-self: center; white-space: nowrap; padding-top: 0.3em; }
|
|
608
598
|
.sw-below { font-size: 0.62em; justify-self: start; white-space: nowrap; padding-left: 0.2em; }
|
|
609
599
|
|
|
610
600
|
/* inter-cell gap as padding on each cell's first notation item, so arcs
|
|
@@ -614,71 +604,21 @@ var SWARALIPI_CSS = `
|
|
|
614
604
|
/* ---- notation clusters & marks ---- */
|
|
615
605
|
.sw-cl { position: relative; display: inline-block; }
|
|
616
606
|
|
|
617
|
-
/*
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
left: 0.1em;
|
|
622
|
-
bottom: -0.08em;
|
|
623
|
-
width: 0.45em;
|
|
624
|
-
height: 0.09em;
|
|
625
|
-
background: currentColor;
|
|
626
|
-
transform: rotate(-32deg);
|
|
627
|
-
}
|
|
628
|
-
/* ati-mandra: an extra dot below */
|
|
607
|
+
/* bn ati-octave: Swarabitan's f/h give a single reph/hasanta, so ati adds
|
|
608
|
+
* one extra dot above (ati-taar) / below (ati-mandra). bn-only \u2014 hi/en get
|
|
609
|
+
* the two-dot glyph natively from the U/L octave suffix. */
|
|
610
|
+
.sw-ati-taar::before,
|
|
629
611
|
.sw-ati-mandra::before {
|
|
630
612
|
content: '';
|
|
631
613
|
position: absolute;
|
|
632
614
|
left: 0.25em;
|
|
633
|
-
bottom: -0.35em;
|
|
634
615
|
width: 0.16em;
|
|
635
616
|
height: 0.16em;
|
|
636
617
|
border-radius: 50%;
|
|
637
618
|
background: currentColor;
|
|
638
619
|
}
|
|
639
|
-
|
|
640
|
-
.sw-ati-
|
|
641
|
-
content: '';
|
|
642
|
-
position: absolute;
|
|
643
|
-
left: 0.25em;
|
|
644
|
-
top: -0.3em;
|
|
645
|
-
width: 0.16em;
|
|
646
|
-
height: 0.16em;
|
|
647
|
-
border-radius: 50%;
|
|
648
|
-
background: currentColor;
|
|
649
|
-
}
|
|
650
|
-
/* CSS reph fallback for vowel letters \u098B/\u090B that reject a real reph */
|
|
651
|
-
.sw-reph-css::before {
|
|
652
|
-
content: '';
|
|
653
|
-
position: absolute;
|
|
654
|
-
top: -0.12em;
|
|
655
|
-
left: 0.12em;
|
|
656
|
-
width: 0.3em;
|
|
657
|
-
height: 0.3em;
|
|
658
|
-
border: 0.09em solid currentColor;
|
|
659
|
-
border-right: none;
|
|
660
|
-
border-bottom: none;
|
|
661
|
-
border-top-left-radius: 0.3em;
|
|
662
|
-
}
|
|
663
|
-
/* en octave dots (Bhatkhande-style) */
|
|
664
|
-
.sw-dot-above::before,
|
|
665
|
-
.sw-dot-below::before {
|
|
666
|
-
content: '';
|
|
667
|
-
position: absolute;
|
|
668
|
-
left: 50%;
|
|
669
|
-
margin-left: -0.07em;
|
|
670
|
-
width: 0.14em;
|
|
671
|
-
height: 0.14em;
|
|
672
|
-
border-radius: 50%;
|
|
673
|
-
background: currentColor;
|
|
674
|
-
}
|
|
675
|
-
.sw-dot-above::before { top: -0.25em; }
|
|
676
|
-
.sw-dot-below::before { bottom: -0.2em; }
|
|
677
|
-
.sw-ati-taar.sw-dot-above::before,
|
|
678
|
-
.sw-ati-mandra.sw-dot-below::before { box-shadow: 0.24em 0 0 currentColor; margin-left: -0.19em; }
|
|
679
|
-
/* en accidentals (bn/hi use distinct letters instead) */
|
|
680
|
-
.sw-komal { text-decoration: underline; text-underline-offset: 0.18em; }
|
|
681
|
-
.sw-tivra::after { content: '\\2032'; font-size: 0.7em; vertical-align: super; }
|
|
620
|
+
.sw-ati-taar::before { top: -0.3em; }
|
|
621
|
+
.sw-ati-mandra::before { bottom: -0.35em; }
|
|
682
622
|
|
|
683
623
|
/* kan grace: small raised cluster left of the main note */
|
|
684
624
|
.sw-kan {
|
|
@@ -695,29 +635,25 @@ var SWARALIPI_CSS = `
|
|
|
695
635
|
.sw-br { opacity: 0.8; padding: 0 0.06em; }
|
|
696
636
|
.sw-micro { font-size: 0.6em; vertical-align: super; opacity: 0.8; }
|
|
697
637
|
|
|
698
|
-
/* raw fallback: never dropped, visibly non-notation
|
|
638
|
+
/* raw fallback: never dropped, visibly non-notation. Swarabitan first \u2014
|
|
639
|
+
* raw IS that font's encoding, so undecoded cells show NLTR's own glyphs
|
|
640
|
+
* (dimmed: semantics unverified); monospace ASCII when the font is out. */
|
|
699
641
|
.sw-raw {
|
|
700
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
642
|
+
font-family: Swarabitan, ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
701
643
|
font-size: 0.72em;
|
|
702
644
|
opacity: 0.55;
|
|
703
645
|
}
|
|
704
646
|
|
|
705
|
-
/* ---- bars: dari (
|
|
647
|
+
/* ---- bars: dari (A) and danda (l/L) Swarabitan glyphs ---- */
|
|
706
648
|
/* horizontal gap before a bar comes from its own .sw-cs padding-left \u2014
|
|
707
649
|
* never reset it with a padding shorthand here */
|
|
708
650
|
.sw-bar {
|
|
709
651
|
grid-row: 2;
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
align-items: stretch;
|
|
713
|
-
gap: 0.16em;
|
|
652
|
+
font-family: Swarabitan, var(--sw-font, sans-serif);
|
|
653
|
+
white-space: nowrap;
|
|
714
654
|
padding-right: 0.1em;
|
|
715
655
|
}
|
|
716
|
-
.sw-bar
|
|
717
|
-
.sw-bar--dari i { width: 0.05em; opacity: 0.9; }
|
|
718
|
-
.sw-bar--quad i:nth-child(3) { margin-left: 0.18em; }
|
|
719
|
-
.sw-above .sw-bar, .sw-below .sw-bar { display: inline-flex; vertical-align: baseline; }
|
|
720
|
-
.sw-above .sw-bar i, .sw-below .sw-bar i { min-height: 1em; }
|
|
656
|
+
.sw-bar--quad { letter-spacing: 0.15em; }
|
|
721
657
|
|
|
722
658
|
/* ---- under-arcs (slurs decoded from w/x segments and meend) ---- */
|
|
723
659
|
.sw-arc {
|
|
@@ -757,6 +693,9 @@ var SWARALIPI_CSS = `
|
|
|
757
693
|
|
|
758
694
|
/* ---- player sync (P2.3) ---- */
|
|
759
695
|
.sw-hl { background: var(--sw-hl, rgba(255, 196, 64, 0.45)); border-radius: 0.15em; }
|
|
696
|
+
/* Staff notes are SVG <g> \u2014 background doesn't paint there, so recolour the
|
|
697
|
+
glyph fill/stroke instead (abcjs draws note heads/stems as filled paths). */
|
|
698
|
+
.sw-staff .sw-hl, .sw-staff .sw-hl * { fill: var(--sw-hl-note, #d68400); stroke: var(--sw-hl-note, #d68400); }
|
|
760
699
|
|
|
761
700
|
/* ---- element error box ---- */
|
|
762
701
|
.sw-error {
|
|
@@ -772,27 +711,500 @@ var SWARALIPI_CSS = `
|
|
|
772
711
|
var SWARALIPI_PRINT_CSS = `
|
|
773
712
|
@page { margin: 16mm; }
|
|
774
713
|
@media print {
|
|
775
|
-
.sw-sheet { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }
|
|
714
|
+
.sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }
|
|
776
715
|
.sw-row { overflow: visible !important; break-inside: avoid; page-break-inside: avoid; }
|
|
777
716
|
.sw-part { break-inside: auto; }
|
|
778
717
|
.sw-part-name, .sw-header { break-after: avoid-page; page-break-after: avoid; }
|
|
779
718
|
.sw-hl { background: none !important; }
|
|
719
|
+
/* Keep each engraved staff system whole across a page break (the abcjs line
|
|
720
|
+
is a <div> wrapping the <svg>, so the break rule must sit on the div). */
|
|
721
|
+
.sw-staff-lines > div { break-inside: avoid; page-break-inside: avoid; }
|
|
722
|
+
.sw-staff-meta { break-after: avoid-page; page-break-after: avoid; }
|
|
780
723
|
}
|
|
781
724
|
`;
|
|
725
|
+
var SWARALIPI_STAFF_CSS = `
|
|
726
|
+
.sw-staff { color: currentColor; }
|
|
727
|
+
.sw-staff-meta { display: flex; gap: 1.25em; align-items: baseline; margin: 0 0 .5em; font-size: .95em; opacity: .85; }
|
|
728
|
+
.sw-staff-instrument { font-weight: 600; }
|
|
729
|
+
.sw-staff-drums { opacity: .75; }
|
|
730
|
+
.sw-staff-tempo { font-variant-numeric: tabular-nums; }
|
|
731
|
+
/* abcjs (oneSvgPerLine) wraps the music into one <div><svg> per system; stack
|
|
732
|
+
them as short, swaralipi-style lines. abcjs sets a fixed pixel height +
|
|
733
|
+
overflow:hidden on each wrapper div \u2014 neutralise it so the viewBox'd SVG can
|
|
734
|
+
scale to the container (and to the print page) instead of being clipped. */
|
|
735
|
+
.sw-staff-lines > div { margin: 0 0 .35em; height: auto !important; overflow: visible !important; }
|
|
736
|
+
/* Fill up to the engraved (staffwidth) size \u2014 shrink to fit a narrow container,
|
|
737
|
+
but never stretch past natural width (abcToSvg pins per-SVG max-width in px),
|
|
738
|
+
else a short last system balloons. */
|
|
739
|
+
.sw-staff-lines svg { display: block; width: 100%; height: auto; }
|
|
740
|
+
.sw-staff-missing { padding: 1em; border: 1px dashed currentColor; border-radius: 6px; opacity: .8; font-size: .9em; }
|
|
741
|
+
.sw-staff-title { font-size: 1.15em; font-weight: 700; }
|
|
742
|
+
.sw-staff-subtitle { opacity: .8; font-size: .95em; margin-top: .15em; }
|
|
743
|
+
`;
|
|
744
|
+
|
|
745
|
+
// src/player/pitch.ts
|
|
746
|
+
var OCTAVE_OFFSET = {
|
|
747
|
+
"ati-mandra": -2,
|
|
748
|
+
mandra: -1,
|
|
749
|
+
madhyam: 0,
|
|
750
|
+
taar: 1,
|
|
751
|
+
"ati-taar": 2
|
|
752
|
+
};
|
|
753
|
+
var SHUDDHA = {
|
|
754
|
+
s: 0,
|
|
755
|
+
r: 2,
|
|
756
|
+
g: 4,
|
|
757
|
+
m: 5,
|
|
758
|
+
p: 7,
|
|
759
|
+
d: 9,
|
|
760
|
+
n: 11
|
|
761
|
+
};
|
|
762
|
+
var SHARP_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
763
|
+
var PITCH_CLASS = {
|
|
764
|
+
c: 0,
|
|
765
|
+
"c#": 1,
|
|
766
|
+
db: 1,
|
|
767
|
+
d: 2,
|
|
768
|
+
"d#": 3,
|
|
769
|
+
eb: 3,
|
|
770
|
+
e: 4,
|
|
771
|
+
"e#": 5,
|
|
772
|
+
fb: 4,
|
|
773
|
+
f: 5,
|
|
774
|
+
"f#": 6,
|
|
775
|
+
gb: 6,
|
|
776
|
+
g: 7,
|
|
777
|
+
"g#": 8,
|
|
778
|
+
ab: 8,
|
|
779
|
+
a: 9,
|
|
780
|
+
"a#": 10,
|
|
781
|
+
bb: 10,
|
|
782
|
+
b: 11,
|
|
783
|
+
"b#": 0,
|
|
784
|
+
cb: 11
|
|
785
|
+
};
|
|
786
|
+
function parseTonic(tonic) {
|
|
787
|
+
if (!tonic) return 0;
|
|
788
|
+
const key = tonic.trim().toLowerCase().replace(/s/g, "#");
|
|
789
|
+
return PITCH_CLASS[key] ?? 0;
|
|
790
|
+
}
|
|
791
|
+
function svaraSemitone(note) {
|
|
792
|
+
if (note.svara === void 0) return null;
|
|
793
|
+
const [, letter] = note.svara;
|
|
794
|
+
let semi = SHUDDHA[letter];
|
|
795
|
+
if (note.komal === true || note.microtone !== void 0) semi -= 1;
|
|
796
|
+
if (note.tivra === true) semi += 1;
|
|
797
|
+
return semi;
|
|
798
|
+
}
|
|
799
|
+
function svaraToMidi(note, tonicPc, baseOctave = 4) {
|
|
800
|
+
const semi = svaraSemitone(note);
|
|
801
|
+
if (semi === null || note.svara === void 0) return null;
|
|
802
|
+
const [octave] = note.svara;
|
|
803
|
+
return (baseOctave + 1 + OCTAVE_OFFSET[octave]) * 12 + tonicPc + semi;
|
|
804
|
+
}
|
|
805
|
+
function kanToMidi(svara, tonicPc, baseOctave = 4) {
|
|
806
|
+
if (svara === void 0) return null;
|
|
807
|
+
const [octave, letter] = svara;
|
|
808
|
+
return (baseOctave + 1 + OCTAVE_OFFSET[octave]) * 12 + tonicPc + SHUDDHA[letter];
|
|
809
|
+
}
|
|
810
|
+
function midiToSampleName(midi) {
|
|
811
|
+
const pc = (midi % 12 + 12) % 12;
|
|
812
|
+
const octave = Math.floor(midi / 12) - 1;
|
|
813
|
+
return `${SHARP_NAMES[pc]}${octave}`;
|
|
814
|
+
}
|
|
815
|
+
function sampleFileName(midi) {
|
|
816
|
+
return midiToSampleName(midi).toLowerCase().replace("#", "s");
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/player/schedule.ts
|
|
820
|
+
var isSilent = (n) => n.svara === void 0 && n.rest !== true && n.sustain !== true;
|
|
821
|
+
function buildSchedule(doc, opts = {}) {
|
|
822
|
+
const tonicPc = parseTonic(opts.tonic ?? doc.meta.tonic);
|
|
823
|
+
const baseOctave = opts.baseOctave ?? 4;
|
|
824
|
+
const events = [];
|
|
825
|
+
let cursor = 0;
|
|
826
|
+
let lastPitched = -1;
|
|
827
|
+
doc.parts.forEach((part, p) => {
|
|
828
|
+
part.rows.forEach((row, r) => {
|
|
829
|
+
row.notes.forEach((note, i) => {
|
|
830
|
+
if (note.bar !== void 0 || isSilent(note)) return;
|
|
831
|
+
const path = `${p}.${r}.${i}`;
|
|
832
|
+
const dur = note.durationMatra ?? 1;
|
|
833
|
+
if (note.svara !== void 0) {
|
|
834
|
+
events.push({
|
|
835
|
+
beat: cursor,
|
|
836
|
+
durationBeats: dur,
|
|
837
|
+
midi: svaraToMidi(note, tonicPc, baseOctave),
|
|
838
|
+
kan: kanToMidi(note.kan, tonicPc, baseOctave),
|
|
839
|
+
path,
|
|
840
|
+
kind: note.sustain === true ? "sustain" : "note"
|
|
841
|
+
});
|
|
842
|
+
lastPitched = events.length - 1;
|
|
843
|
+
} else if (note.sustain === true) {
|
|
844
|
+
if (lastPitched >= 0) events[lastPitched].durationBeats += dur;
|
|
845
|
+
events.push({ beat: cursor, durationBeats: dur, midi: null, kan: null, path, kind: "sustain" });
|
|
846
|
+
} else {
|
|
847
|
+
events.push({ beat: cursor, durationBeats: dur, midi: null, kan: null, path, kind: "rest" });
|
|
848
|
+
}
|
|
849
|
+
cursor += dur;
|
|
850
|
+
});
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
return { events, totalBeats: cursor };
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/viewer/staff.ts
|
|
857
|
+
init_taals();
|
|
858
|
+
|
|
859
|
+
// src/player/instruments.ts
|
|
860
|
+
init_taals();
|
|
861
|
+
function keyMap(keys) {
|
|
862
|
+
const m = {};
|
|
863
|
+
for (const k of keys) m[k] = `${k.replace("#", "s")}.mp3`;
|
|
864
|
+
return m;
|
|
865
|
+
}
|
|
866
|
+
var PIANO_KEYS = [
|
|
867
|
+
"A0",
|
|
868
|
+
"C1",
|
|
869
|
+
"D#1",
|
|
870
|
+
"F#1",
|
|
871
|
+
"A1",
|
|
872
|
+
"C2",
|
|
873
|
+
"D#2",
|
|
874
|
+
"F#2",
|
|
875
|
+
"A2",
|
|
876
|
+
"C3",
|
|
877
|
+
"D#3",
|
|
878
|
+
"F#3",
|
|
879
|
+
"A3",
|
|
880
|
+
"C4",
|
|
881
|
+
"D#4",
|
|
882
|
+
"F#4",
|
|
883
|
+
"A4",
|
|
884
|
+
"C5",
|
|
885
|
+
"D#5",
|
|
886
|
+
"F#5",
|
|
887
|
+
"A5",
|
|
888
|
+
"C6",
|
|
889
|
+
"D#6",
|
|
890
|
+
"F#6",
|
|
891
|
+
"A6",
|
|
892
|
+
"C7",
|
|
893
|
+
"D#7",
|
|
894
|
+
"F#7",
|
|
895
|
+
"A7",
|
|
896
|
+
"C8"
|
|
897
|
+
];
|
|
898
|
+
var HARMONIUM_KEYS = [
|
|
899
|
+
"C2",
|
|
900
|
+
"C#2",
|
|
901
|
+
"D2",
|
|
902
|
+
"D#2",
|
|
903
|
+
"E2",
|
|
904
|
+
"F2",
|
|
905
|
+
"F#2",
|
|
906
|
+
"G2",
|
|
907
|
+
"G#2",
|
|
908
|
+
"A2",
|
|
909
|
+
"A#2",
|
|
910
|
+
"C3",
|
|
911
|
+
"C#3",
|
|
912
|
+
"D3",
|
|
913
|
+
"D#3",
|
|
914
|
+
"E3",
|
|
915
|
+
"F3",
|
|
916
|
+
"F#3",
|
|
917
|
+
"G3",
|
|
918
|
+
"G#3",
|
|
919
|
+
"A3",
|
|
920
|
+
"A#3",
|
|
921
|
+
"C4",
|
|
922
|
+
"C#4",
|
|
923
|
+
"D4",
|
|
924
|
+
"D#4",
|
|
925
|
+
"E4",
|
|
926
|
+
"F4",
|
|
927
|
+
"F#4",
|
|
928
|
+
"G4",
|
|
929
|
+
"G#4",
|
|
930
|
+
"A4",
|
|
931
|
+
"A#4",
|
|
932
|
+
"C5",
|
|
933
|
+
"C#5",
|
|
934
|
+
"D5"
|
|
935
|
+
];
|
|
936
|
+
var VIOLIN_KEYS = ["A3", "C4", "E4", "G4", "A4", "C5", "E5", "G5", "A5", "C6", "E6", "G6", "A6", "C7"];
|
|
937
|
+
var FLUTE_KEYS = ["C4", "E4", "A4", "C5", "E5", "A5", "C6", "E6", "A6", "C7"];
|
|
938
|
+
({
|
|
939
|
+
piano: { urls: keyMap(PIANO_KEYS)},
|
|
940
|
+
santoor: { urls: santoorUrls()},
|
|
941
|
+
harmonium: { urls: keyMap(HARMONIUM_KEYS)},
|
|
942
|
+
violin: { urls: keyMap(VIOLIN_KEYS)},
|
|
943
|
+
flute: { urls: keyMap(FLUTE_KEYS)}
|
|
944
|
+
});
|
|
945
|
+
new Set(
|
|
946
|
+
Object.keys(TAALS)
|
|
947
|
+
);
|
|
948
|
+
Array.from({ length: 17 }, (_, i) => 60 + i * 15);
|
|
949
|
+
function santoorUrls() {
|
|
950
|
+
const urls = {};
|
|
951
|
+
const add = (midi) => {
|
|
952
|
+
const stem = sampleFileName(midi);
|
|
953
|
+
const name = stem.replace("s", "#").replace(/^(.)/, (c) => c.toUpperCase());
|
|
954
|
+
urls[name] = `${stem}.mp3`;
|
|
955
|
+
};
|
|
956
|
+
for (let midi = 60; midi <= 95; midi++) add(midi);
|
|
957
|
+
add(96);
|
|
958
|
+
return urls;
|
|
959
|
+
}
|
|
960
|
+
function drumPattern(taal) {
|
|
961
|
+
const n = taal.numBeats;
|
|
962
|
+
const sam = taal.sam ?? 0;
|
|
963
|
+
const khaali = new Set(taal.khaali ?? []);
|
|
964
|
+
const bhaags = taal.bhaags ?? [n];
|
|
965
|
+
const pattern = new Array(n).fill("hihat");
|
|
966
|
+
let pos = 0;
|
|
967
|
+
for (const len of bhaags) {
|
|
968
|
+
pattern[pos] = khaali.has(pos) ? "hihat" : "snare";
|
|
969
|
+
pos += len;
|
|
970
|
+
}
|
|
971
|
+
pattern[sam] = "kick";
|
|
972
|
+
return pattern;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// src/viewer/staff.ts
|
|
976
|
+
var ABC_SHARP = ["C", "^C", "D", "^D", "E", "F", "^F", "G", "^G", "A", "^A", "B"];
|
|
977
|
+
function midiToAbc(midi) {
|
|
978
|
+
const pc = (midi % 12 + 12) % 12;
|
|
979
|
+
const octave = Math.floor(midi / 12) - 1;
|
|
980
|
+
const token = ABC_SHARP[pc];
|
|
981
|
+
const acc = token[0] === "^" ? "^" : "";
|
|
982
|
+
const letter = acc ? token[1] : token[0];
|
|
983
|
+
return octave >= 5 ? acc + letter.toLowerCase() + "'".repeat(octave - 5) : acc + letter + ",".repeat(4 - octave);
|
|
984
|
+
}
|
|
985
|
+
function abcDuration(beats) {
|
|
986
|
+
if (Math.abs(beats - 1) < 1e-6) return "";
|
|
987
|
+
if (Math.abs(beats - Math.round(beats)) < 1e-6) return String(Math.round(beats));
|
|
988
|
+
const inv = 1 / beats;
|
|
989
|
+
if (Math.abs(inv - Math.round(inv)) < 1e-6) return `/${Math.round(inv)}`;
|
|
990
|
+
const sixteenths = Math.max(1, Math.round(beats * 4));
|
|
991
|
+
return `${sixteenths}/4`;
|
|
992
|
+
}
|
|
993
|
+
function abcNote(ev) {
|
|
994
|
+
const grace = ev.kan !== null ? `{${midiToAbc(ev.kan)}}` : "";
|
|
995
|
+
return `${grace}${midiToAbc(ev.midi)}${abcDuration(ev.durationBeats)}`;
|
|
996
|
+
}
|
|
997
|
+
function eventPaths(schedule) {
|
|
998
|
+
const paths = [];
|
|
999
|
+
for (const ev of schedule.events) {
|
|
1000
|
+
if (ev.midi !== null || ev.kind === "rest") paths.push(ev.path);
|
|
1001
|
+
}
|
|
1002
|
+
return paths;
|
|
1003
|
+
}
|
|
1004
|
+
function abcText(s) {
|
|
1005
|
+
return s.replace(/[\r\n]+/g, " ").trim();
|
|
1006
|
+
}
|
|
1007
|
+
function buildAbc(schedule, lyricOf, opts) {
|
|
1008
|
+
const beatsPerBar = opts.beatsPerBar > 0 ? opts.beatsPerBar : 4;
|
|
1009
|
+
const tokens = [];
|
|
1010
|
+
const words = [];
|
|
1011
|
+
let barPos = 0;
|
|
1012
|
+
const EPS2 = 1e-6;
|
|
1013
|
+
const advance = (dur) => {
|
|
1014
|
+
barPos += dur;
|
|
1015
|
+
while (barPos >= beatsPerBar - EPS2) {
|
|
1016
|
+
tokens.push("|");
|
|
1017
|
+
words.push("|");
|
|
1018
|
+
barPos -= beatsPerBar;
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
for (const ev of schedule.events) {
|
|
1022
|
+
if (ev.midi === null) {
|
|
1023
|
+
if (ev.kind === "rest") {
|
|
1024
|
+
tokens.push(`z${abcDuration(ev.durationBeats)}`);
|
|
1025
|
+
words.push("*");
|
|
1026
|
+
advance(ev.durationBeats);
|
|
1027
|
+
}
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
tokens.push(abcNote(ev));
|
|
1031
|
+
const lyr = lyricOf(ev);
|
|
1032
|
+
words.push(lyr ? abcText(lyr).replace(/\s+/g, "~") : "*");
|
|
1033
|
+
advance(ev.durationBeats);
|
|
1034
|
+
}
|
|
1035
|
+
if (tokens.length && tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
1036
|
+
else if (tokens.length) tokens[tokens.length - 1] = "|]";
|
|
1037
|
+
const meter = ratioMeter(beatsPerBar);
|
|
1038
|
+
const head = ["X:1", `M:${meter}`, "L:1/4", `Q:1/4=${Math.round(opts.bpm)}`];
|
|
1039
|
+
if (opts.drums) head.push(...percMapLines(opts.drums));
|
|
1040
|
+
head.push("K:C treble");
|
|
1041
|
+
const melodyBody = tokens.length ? tokens.join(" ") : "z |]";
|
|
1042
|
+
const lines = [...head];
|
|
1043
|
+
if (opts.drums) {
|
|
1044
|
+
lines.push("V:1", melodyBody);
|
|
1045
|
+
if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
|
|
1046
|
+
lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats, beatsPerBar, opts.drums));
|
|
1047
|
+
} else {
|
|
1048
|
+
lines.push(melodyBody);
|
|
1049
|
+
if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
|
|
1050
|
+
}
|
|
1051
|
+
return lines.join("\n");
|
|
1052
|
+
}
|
|
1053
|
+
function ratioMeter(beats) {
|
|
1054
|
+
return `${Math.max(1, Math.round(beats))}/4`;
|
|
1055
|
+
}
|
|
1056
|
+
var DRUM_ABC = {
|
|
1057
|
+
kick: { note: "F,", gm: "bass-drum-1" },
|
|
1058
|
+
snare: { note: "C", gm: "acoustic-snare" },
|
|
1059
|
+
hihat: { note: "g", gm: "closed-hi-hat" },
|
|
1060
|
+
tom1: { note: "E", gm: "high-tom" },
|
|
1061
|
+
tom2: { note: "A,", gm: "low-mid-tom" },
|
|
1062
|
+
tom3: { note: "D,", gm: "low-floor-tom" }
|
|
1063
|
+
};
|
|
1064
|
+
function percMapLines(voices) {
|
|
1065
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1066
|
+
const lines = [];
|
|
1067
|
+
for (const v of voices) {
|
|
1068
|
+
if (seen.has(v)) continue;
|
|
1069
|
+
seen.add(v);
|
|
1070
|
+
const { note, gm } = DRUM_ABC[v];
|
|
1071
|
+
lines.push(`%%percmap ${note} ${gm}`);
|
|
1072
|
+
}
|
|
1073
|
+
return lines;
|
|
1074
|
+
}
|
|
1075
|
+
function buildDrumVoice(totalBeats, beatsPerBar, pattern) {
|
|
1076
|
+
const n = Math.max(1, Math.ceil(totalBeats));
|
|
1077
|
+
const tokens = [];
|
|
1078
|
+
for (let beat = 0; beat < n; beat++) {
|
|
1079
|
+
tokens.push(DRUM_ABC[pattern[beat % pattern.length]].note);
|
|
1080
|
+
if ((beat + 1) % beatsPerBar === 0) tokens.push("|");
|
|
1081
|
+
}
|
|
1082
|
+
if (tokens.length === 0 || tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
1083
|
+
else tokens[tokens.length - 1] = "|]";
|
|
1084
|
+
return tokens.join(" ");
|
|
1085
|
+
}
|
|
1086
|
+
function lyricLookup(doc) {
|
|
1087
|
+
return (ev) => {
|
|
1088
|
+
const [p, r, i] = ev.path.split(".").map(Number);
|
|
1089
|
+
return doc.parts[p]?.rows[r]?.notes[i]?.lyric;
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
function resolveDrumTaal(doc) {
|
|
1093
|
+
const id = doc.meta.taal?.id;
|
|
1094
|
+
return id !== void 0 && id in TAALS ? TAALS[id] : { numBeats: 4, sam: 0 };
|
|
1095
|
+
}
|
|
1096
|
+
function docToAbc(doc, opts = {}) {
|
|
1097
|
+
const schedule = buildSchedule(doc, { tonic: opts.tonic, baseOctave: opts.baseOctave });
|
|
1098
|
+
const bpm = opts.bpm ?? doc.meta.bpm ?? 90;
|
|
1099
|
+
const beatsPerBar = opts.beatsPerBar ?? doc.meta.taal?.numBeats ?? 4;
|
|
1100
|
+
const drums = opts.drums ? drumPattern(resolveDrumTaal(doc)) : void 0;
|
|
1101
|
+
return buildAbc(schedule, lyricLookup(doc), { bpm, lyrics: opts.lyrics !== false, beatsPerBar, drums });
|
|
1102
|
+
}
|
|
1103
|
+
async function abcToSvg(abc, opts = {}, notePaths) {
|
|
1104
|
+
if (typeof document === "undefined") {
|
|
1105
|
+
throw new Error("abcToSvg requires a DOM; render staff notation in the browser or print iframe");
|
|
1106
|
+
}
|
|
1107
|
+
let ABCJS;
|
|
1108
|
+
try {
|
|
1109
|
+
const mod = await import('abcjs');
|
|
1110
|
+
ABCJS = mod.default ?? mod;
|
|
1111
|
+
} catch {
|
|
1112
|
+
return `<div class="sw-staff-missing">Staff notation needs the optional <code>abcjs</code> package.</div>`;
|
|
1113
|
+
}
|
|
1114
|
+
const host = document.createElement("div");
|
|
1115
|
+
host.style.cssText = "position:fixed; left:-10000px; top:0; width:720px; visibility:hidden;";
|
|
1116
|
+
const mount = document.body ?? document.documentElement;
|
|
1117
|
+
mount.appendChild(host);
|
|
1118
|
+
try {
|
|
1119
|
+
ABCJS.renderAbc(host, abc, {
|
|
1120
|
+
add_classes: true,
|
|
1121
|
+
staffwidth: opts.staffwidth ?? 680,
|
|
1122
|
+
scale: opts.scale ?? 1,
|
|
1123
|
+
// Reflow into short lines. staffwidth is the width budget;
|
|
1124
|
+
// preferredMeasuresPerLine keeps systems swaralipi-short.
|
|
1125
|
+
// (Not `responsive: resize` — its absolute-positioned aspect-ratio wrapper
|
|
1126
|
+
// overlaps the header and mis-sizes on the print page.)
|
|
1127
|
+
wrap: { minSpacing: 1.8, maxSpacing: 2.7, preferredMeasuresPerLine: opts.measuresPerLine ?? 4 },
|
|
1128
|
+
// CRUCIAL: put each wrapped system in its OWN <svg> (default is all systems
|
|
1129
|
+
// stacked in one tall SVG, which is atomic and can't page-break — that was
|
|
1130
|
+
// the near-blank-first-page / mid-staff-cutoff print bug). One SVG per line
|
|
1131
|
+
// lets `break-inside: avoid` keep each staff whole while pages fill.
|
|
1132
|
+
oneSvgPerLine: true
|
|
1133
|
+
});
|
|
1134
|
+
for (const svg of host.querySelectorAll("svg")) {
|
|
1135
|
+
const w = parseFloat(svg.getAttribute("width") ?? "");
|
|
1136
|
+
const h = parseFloat(svg.getAttribute("height") ?? "");
|
|
1137
|
+
if (w > 0 && h > 0) {
|
|
1138
|
+
if (!svg.getAttribute("viewBox")) svg.setAttribute("viewBox", `0 0 ${w} ${h}`);
|
|
1139
|
+
svg.setAttribute("preserveAspectRatio", "xMinYMin meet");
|
|
1140
|
+
svg.removeAttribute("width");
|
|
1141
|
+
svg.removeAttribute("height");
|
|
1142
|
+
svg.setAttribute("style", `max-width:${w}px`);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (notePaths) {
|
|
1146
|
+
const melody = host.querySelectorAll(".abcjs-v0.abcjs-note, .abcjs-v0.abcjs-rest");
|
|
1147
|
+
let i = 0;
|
|
1148
|
+
for (const el of melody) {
|
|
1149
|
+
const path = notePaths[i++];
|
|
1150
|
+
if (path !== void 0) el.setAttribute("data-note", path);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return host.innerHTML;
|
|
1154
|
+
} finally {
|
|
1155
|
+
host.remove();
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
async function renderStaffHtml(doc, opts = {}) {
|
|
1159
|
+
const bpm = opts.bpm ?? doc.meta.bpm ?? 90;
|
|
1160
|
+
const abc = docToAbc(doc, { ...opts, bpm });
|
|
1161
|
+
const schedule = buildSchedule(doc, { tonic: opts.tonic, baseOctave: opts.baseOctave });
|
|
1162
|
+
const engraved = await abcToSvg(abc, opts, eventPaths(schedule));
|
|
1163
|
+
const lines = engraved.includes("sw-staff-missing") ? engraved : `<div class="sw-staff-lines">${engraved}</div>`;
|
|
1164
|
+
const header = opts.showHeader === false ? "" : renderMetaHeader(doc.meta, opts.headerLang ?? "en", opts.transliterate);
|
|
1165
|
+
const metaParts = [];
|
|
1166
|
+
if (opts.instrument) metaParts.push(`<span class="sw-staff-instrument">${escapeAttr(opts.instrument)}</span>`);
|
|
1167
|
+
if (opts.drums) metaParts.push(`<span class="sw-staff-drums">+ drums</span>`);
|
|
1168
|
+
const meta = metaParts.length ? `<div class="sw-staff-meta">${metaParts.join("")}</div>` : "";
|
|
1169
|
+
return `<div class="sw-staff">${header}${meta}${lines}</div>`;
|
|
1170
|
+
}
|
|
1171
|
+
function escapeAttr(s) {
|
|
1172
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1173
|
+
}
|
|
782
1174
|
|
|
783
1175
|
// src/viewer/print.ts
|
|
1176
|
+
var DEFAULT_WATERMARK = "www.gitabitan.io";
|
|
1177
|
+
var DEFAULT_NOTATOR = "gitabitan.io Team";
|
|
784
1178
|
function escapeHtml2(s) {
|
|
785
1179
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
786
1180
|
}
|
|
787
|
-
function
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1181
|
+
function watermarkCss(text) {
|
|
1182
|
+
if (!text) return "";
|
|
1183
|
+
return `
|
|
1184
|
+
.sw-watermark {
|
|
1185
|
+
position: fixed; inset: 0; z-index: 0; pointer-events: none;
|
|
1186
|
+
display: flex; align-items: center; justify-content: center;
|
|
1187
|
+
overflow: hidden;
|
|
1188
|
+
-webkit-print-color-adjust: exact; print-color-adjust: exact;
|
|
1189
|
+
}
|
|
1190
|
+
.sw-watermark span {
|
|
1191
|
+
font-size: 11vw; font-weight: 700; letter-spacing: .08em;
|
|
1192
|
+
color: rgba(0,0,0,0.07); transform: rotate(-35deg); white-space: nowrap;
|
|
1193
|
+
}
|
|
1194
|
+
body > .sw-sheet, body > .sw-footer { position: relative; z-index: 1; }
|
|
1195
|
+
`;
|
|
1196
|
+
}
|
|
1197
|
+
function chrome(text, notator) {
|
|
1198
|
+
const top = text ? `<div class="sw-watermark" aria-hidden="true"><span>${escapeHtml2(text)}</span></div>` : "";
|
|
1199
|
+
const bottom = notator ? `<footer class="sw-footer">${escapeHtml2(notator)}</footer>` : "";
|
|
1200
|
+
return { top, bottom };
|
|
1201
|
+
}
|
|
1202
|
+
function resolveNotator(doc, opts) {
|
|
1203
|
+
return doc.meta.notationBy ?? opts.notator ?? DEFAULT_NOTATOR;
|
|
1204
|
+
}
|
|
1205
|
+
function pageHtml(lang, title, bodyHtml, opts, doc) {
|
|
1206
|
+
const watermark = opts.watermark ?? DEFAULT_WATERMARK;
|
|
1207
|
+
const { top, bottom } = chrome(watermark, resolveNotator(doc, opts));
|
|
796
1208
|
return `<!doctype html>
|
|
797
1209
|
<html lang="${escapeHtml2(lang)}">
|
|
798
1210
|
<head>
|
|
@@ -801,20 +1213,42 @@ function renderPrintDocument(doc, opts = {}) {
|
|
|
801
1213
|
<title>${escapeHtml2(title)}</title>
|
|
802
1214
|
<style>
|
|
803
1215
|
${SWARALIPI_CSS}
|
|
1216
|
+
${SWARALIPI_STAFF_CSS}
|
|
804
1217
|
${SWARALIPI_PRINT_CSS}
|
|
1218
|
+
${watermarkCss(watermark)}
|
|
805
1219
|
html, body { margin: 0; }
|
|
806
1220
|
body { color: #111; background: #fff; padding: 16px; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
807
1221
|
.sw-sheet { max-width: 100%; }
|
|
1222
|
+
.sw-footer { margin-top: 12px; padding-top: 6px; border-top: 1px solid #ddd; font-size: 12px; color: #555; text-align: right; }
|
|
808
1223
|
</style>
|
|
809
1224
|
</head>
|
|
810
|
-
<body>${
|
|
1225
|
+
<body>${top}${bodyHtml}${bottom}</body>
|
|
811
1226
|
</html>`;
|
|
812
1227
|
}
|
|
1228
|
+
function renderPrintDocument(doc, opts = {}) {
|
|
1229
|
+
const lang = opts.lang === "staff" ? "bn" : opts.lang ?? "bn";
|
|
1230
|
+
const tr = lang !== "bn" && opts.transliterate ? opts.transliterate : (s) => s;
|
|
1231
|
+
const title = opts.title ?? tr(doc.meta.title);
|
|
1232
|
+
const body = renderToHtml(doc, { lang, showHeader: opts.showHeader, transliterate: opts.transliterate });
|
|
1233
|
+
return pageHtml(lang, title, body, opts, doc);
|
|
1234
|
+
}
|
|
1235
|
+
async function renderStaffPrintDocument(doc, opts = {}) {
|
|
1236
|
+
if (opts.lang !== "staff") return renderPrintDocument(doc, opts);
|
|
1237
|
+
const title = opts.title ?? doc.meta.title;
|
|
1238
|
+
const staff = await renderStaffHtml(doc, opts);
|
|
1239
|
+
return pageHtml("en", title, staff, opts, doc);
|
|
1240
|
+
}
|
|
813
1241
|
function printSwaralipi(doc, opts = {}) {
|
|
814
1242
|
if (typeof document === "undefined") {
|
|
815
1243
|
throw new Error("printSwaralipi requires a DOM; use renderPrintDocument() for server-side PDF");
|
|
816
1244
|
}
|
|
817
|
-
|
|
1245
|
+
if (opts.lang === "staff") {
|
|
1246
|
+
void renderStaffPrintDocument(doc, opts).then(printHtmlInIframe);
|
|
1247
|
+
} else {
|
|
1248
|
+
printHtmlInIframe(renderPrintDocument(doc, opts));
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
function printHtmlInIframe(html) {
|
|
818
1252
|
const iframe = document.createElement("iframe");
|
|
819
1253
|
iframe.setAttribute("aria-hidden", "true");
|
|
820
1254
|
iframe.style.cssText = "position:fixed; left:-10000px; top:0; width:800px; height:600px; border:0;";
|
|
@@ -826,7 +1260,10 @@ function printSwaralipi(doc, opts = {}) {
|
|
|
826
1260
|
}
|
|
827
1261
|
const cleanup = () => setTimeout(() => iframe.remove(), 1e3);
|
|
828
1262
|
win.addEventListener("afterprint", cleanup, { once: true });
|
|
1263
|
+
let printed = false;
|
|
829
1264
|
const go = () => {
|
|
1265
|
+
if (printed) return;
|
|
1266
|
+
printed = true;
|
|
830
1267
|
try {
|
|
831
1268
|
win.focus();
|
|
832
1269
|
win.print();
|
|
@@ -834,8 +1271,14 @@ function printSwaralipi(doc, opts = {}) {
|
|
|
834
1271
|
}
|
|
835
1272
|
};
|
|
836
1273
|
const fonts = iframe.contentDocument?.fonts;
|
|
837
|
-
if (fonts
|
|
838
|
-
|
|
1274
|
+
if (fonts && typeof fonts.forEach === "function") {
|
|
1275
|
+
const loads = [Promise.resolve(fonts.ready)];
|
|
1276
|
+
fonts.forEach((face) => loads.push(face.load().catch(() => void 0)));
|
|
1277
|
+
void Promise.all(loads).then(go);
|
|
1278
|
+
setTimeout(go, 3e3);
|
|
1279
|
+
} else {
|
|
1280
|
+
setTimeout(go, 250);
|
|
1281
|
+
}
|
|
839
1282
|
};
|
|
840
1283
|
iframe.srcdoc = html;
|
|
841
1284
|
document.body.appendChild(iframe);
|
|
@@ -847,7 +1290,7 @@ var TARGET = {
|
|
|
847
1290
|
en: "iast"
|
|
848
1291
|
};
|
|
849
1292
|
async function loadLyricTransliterator(lang) {
|
|
850
|
-
if (lang === "bn") return void 0;
|
|
1293
|
+
if (lang === "bn" || lang === "staff") return void 0;
|
|
851
1294
|
const mod = await import('@indic-transliteration/sanscript');
|
|
852
1295
|
const sanscript = mod.default ?? mod;
|
|
853
1296
|
const target = TARGET[lang];
|
|
@@ -855,7 +1298,16 @@ async function loadLyricTransliterator(lang) {
|
|
|
855
1298
|
}
|
|
856
1299
|
|
|
857
1300
|
// src/viewer/element.ts
|
|
858
|
-
var LANGS = /* @__PURE__ */ new Set(["bn", "hi", "en"]);
|
|
1301
|
+
var LANGS = /* @__PURE__ */ new Set(["bn", "hi", "en", "staff"]);
|
|
1302
|
+
function ensureNotationFonts() {
|
|
1303
|
+
if (typeof FontFace === "undefined" || typeof document === "undefined") return;
|
|
1304
|
+
const present = /* @__PURE__ */ new Set();
|
|
1305
|
+
for (const f of document.fonts) present.add(f.family.replace(/['"]/g, ""));
|
|
1306
|
+
for (const [family, url] of Object.entries(NOTATION_FONT_URLS)) {
|
|
1307
|
+
if (present.has(family)) continue;
|
|
1308
|
+
document.fonts.add(new FontFace(family, `url(${url})`, { display: "block" }));
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
859
1311
|
var sharedSheet;
|
|
860
1312
|
function createElementClass() {
|
|
861
1313
|
return class SwaralipiSheet extends HTMLElement {
|
|
@@ -866,6 +1318,8 @@ function createElementClass() {
|
|
|
866
1318
|
#abort;
|
|
867
1319
|
#translit;
|
|
868
1320
|
#translitLang;
|
|
1321
|
+
#renderSeq = 0;
|
|
1322
|
+
#staffOptions = {};
|
|
869
1323
|
validate = false;
|
|
870
1324
|
constructor() {
|
|
871
1325
|
super();
|
|
@@ -873,7 +1327,7 @@ function createElementClass() {
|
|
|
873
1327
|
if (typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype && "adoptedStyleSheets" in this.#root) {
|
|
874
1328
|
if (!sharedSheet) {
|
|
875
1329
|
sharedSheet = new CSSStyleSheet();
|
|
876
|
-
sharedSheet.replaceSync(SWARALIPI_CSS);
|
|
1330
|
+
sharedSheet.replaceSync(SWARALIPI_CSS + SWARALIPI_STAFF_CSS);
|
|
877
1331
|
}
|
|
878
1332
|
this.#root.adoptedStyleSheets = [...this.#root.adoptedStyleSheets, sharedSheet];
|
|
879
1333
|
this.#adopted = true;
|
|
@@ -892,6 +1346,13 @@ function createElementClass() {
|
|
|
892
1346
|
}
|
|
893
1347
|
this.#render();
|
|
894
1348
|
}
|
|
1349
|
+
get staffOptions() {
|
|
1350
|
+
return this.#staffOptions;
|
|
1351
|
+
}
|
|
1352
|
+
set staffOptions(value) {
|
|
1353
|
+
this.#staffOptions = value;
|
|
1354
|
+
if (this.#lang === "staff") this.#render();
|
|
1355
|
+
}
|
|
895
1356
|
get #lang() {
|
|
896
1357
|
const attr = this.getAttribute("lang");
|
|
897
1358
|
return attr !== null && LANGS.has(attr) ? attr : "bn";
|
|
@@ -938,14 +1399,22 @@ function createElementClass() {
|
|
|
938
1399
|
}).catch(() => void 0);
|
|
939
1400
|
}
|
|
940
1401
|
#styleFallback() {
|
|
941
|
-
return this.#adopted ? "" : `<style>${SWARALIPI_CSS}</style>`;
|
|
1402
|
+
return this.#adopted ? "" : `<style>${SWARALIPI_CSS}${SWARALIPI_STAFF_CSS}</style>`;
|
|
942
1403
|
}
|
|
943
1404
|
#render() {
|
|
1405
|
+
const seq = ++this.#renderSeq;
|
|
944
1406
|
if (this.#doc === void 0) {
|
|
945
1407
|
this.#root.innerHTML = this.#styleFallback();
|
|
946
1408
|
return;
|
|
947
1409
|
}
|
|
948
1410
|
const lang = this.#lang;
|
|
1411
|
+
if (lang === "staff") {
|
|
1412
|
+
const doc = this.#doc;
|
|
1413
|
+
void renderStaffHtml(doc, this.staffOptions).then((html2) => {
|
|
1414
|
+
if (seq === this.#renderSeq) this.#root.innerHTML = this.#styleFallback() + html2;
|
|
1415
|
+
}).catch(() => void 0);
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
949
1418
|
const html = renderToHtml(this.#doc, {
|
|
950
1419
|
lang,
|
|
951
1420
|
showHeader: !this.hasAttribute("hide-header"),
|
|
@@ -976,6 +1445,7 @@ function createElementClass() {
|
|
|
976
1445
|
lang,
|
|
977
1446
|
showHeader: !this.hasAttribute("hide-header"),
|
|
978
1447
|
transliterate: this.#translitLang === lang ? this.#translit : void 0,
|
|
1448
|
+
...lang === "staff" ? this.staffOptions : {},
|
|
979
1449
|
...options
|
|
980
1450
|
});
|
|
981
1451
|
}
|
|
@@ -988,25 +1458,37 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
|
|
|
988
1458
|
}
|
|
989
1459
|
const existing = customElements.get(tagName);
|
|
990
1460
|
if (existing) return existing;
|
|
1461
|
+
ensureNotationFonts();
|
|
991
1462
|
const ctor = registered.get(tagName) ?? createElementClass();
|
|
992
1463
|
registered.set(tagName, ctor);
|
|
993
1464
|
customElements.define(tagName, ctor);
|
|
994
1465
|
return ctor;
|
|
995
1466
|
}
|
|
996
1467
|
|
|
1468
|
+
exports.DEFAULT_NOTATOR = DEFAULT_NOTATOR;
|
|
1469
|
+
exports.DEFAULT_WATERMARK = DEFAULT_WATERMARK;
|
|
997
1470
|
exports.DIGITS = DIGITS;
|
|
998
1471
|
exports.LABELS = LABELS;
|
|
999
|
-
exports.LETTERS = LETTERS;
|
|
1000
1472
|
exports.MARKS = MARKS;
|
|
1473
|
+
exports.NOTATION_FONT_URLS = NOTATION_FONT_URLS;
|
|
1001
1474
|
exports.SWARALIPI_CSS = SWARALIPI_CSS;
|
|
1002
1475
|
exports.SWARALIPI_PRINT_CSS = SWARALIPI_PRINT_CSS;
|
|
1476
|
+
exports.SWARALIPI_STAFF_CSS = SWARALIPI_STAFF_CSS;
|
|
1477
|
+
exports.abcDuration = abcDuration;
|
|
1478
|
+
exports.abcToSvg = abcToSvg;
|
|
1479
|
+
exports.buildAbc = buildAbc;
|
|
1003
1480
|
exports.composeCluster = composeCluster;
|
|
1004
1481
|
exports.defineSwaralipiSheet = defineSwaralipiSheet;
|
|
1482
|
+
exports.docToAbc = docToAbc;
|
|
1005
1483
|
exports.groupRowIntoCells = groupRowIntoCells;
|
|
1006
1484
|
exports.loadLyricTransliterator = loadLyricTransliterator;
|
|
1007
1485
|
exports.localizeDigits = localizeDigits;
|
|
1486
|
+
exports.midiToAbc = midiToAbc;
|
|
1008
1487
|
exports.printSwaralipi = printSwaralipi;
|
|
1488
|
+
exports.renderMetaHeader = renderMetaHeader;
|
|
1009
1489
|
exports.renderPrintDocument = renderPrintDocument;
|
|
1490
|
+
exports.renderStaffHtml = renderStaffHtml;
|
|
1491
|
+
exports.renderStaffPrintDocument = renderStaffPrintDocument;
|
|
1010
1492
|
exports.renderToHtml = renderToHtml;
|
|
1011
1493
|
exports.resolveArcs = resolveArcs;
|
|
1012
1494
|
//# sourceMappingURL=index.cjs.map
|