@ugfoundation/swaralipi-js 0.8.2 → 0.9.1
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 +1 -1
- package/dist/{batch-6FPBT5DP.js → batch-NEM7JXHV.js} +5 -5
- package/dist/{batch-6FPBT5DP.js.map → batch-NEM7JXHV.js.map} +1 -1
- package/dist/{chunk-UMBKIGVQ.js → chunk-DOEFSS66.js} +5 -4
- package/dist/chunk-DOEFSS66.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-4IVUPMRK.js → chunk-SEN266BY.js} +28 -13
- package/dist/chunk-SEN266BY.js.map +1 -0
- package/dist/{chunk-FULC22MN.js → chunk-Z6A3CP5V.js} +28 -19
- package/dist/chunk-Z6A3CP5V.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 +116 -71
- 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 +89 -66
- package/dist/player/index.js.map +1 -1
- package/dist/{schedule-B_UaluBi.d.cts → schedule-BaovCtmf.d.ts} +31 -12
- package/dist/{schedule-CdILTNsk.d.ts → schedule-CU-VECDH.d.cts} +31 -12
- 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 +156 -73
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +25 -7
- package/dist/viewer/index.d.ts +25 -7
- package/dist/viewer/index.js +78 -27
- 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-FULC22MN.js.map +0 -1
- package/dist/chunk-UMBKIGVQ.js.map +0 -1
package/dist/viewer/index.cjs
CHANGED
|
@@ -38,30 +38,16 @@ var init_lay = __esm({
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
// src/spec/
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
return typeof grace[0] === "string" ? [{ svara: grace }] : grace;
|
|
45
|
-
}
|
|
46
|
-
var init_grace = __esm({
|
|
47
|
-
"src/spec/grace.ts"() {
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// src/spec/svara.ts
|
|
52
|
-
function hasKomal(letter) {
|
|
53
|
-
return letter === "r" || letter === "g" || letter === "d" || letter === "n";
|
|
41
|
+
// src/spec/taals.ts
|
|
42
|
+
function isTaalFerta(name) {
|
|
43
|
+
return name !== void 0 && name.normalize("NFC").replace(/[\s-]/g, "") === TAAL_FERTA;
|
|
54
44
|
}
|
|
55
|
-
function
|
|
56
|
-
|
|
45
|
+
function sameTaal(a, b) {
|
|
46
|
+
if (a === b) return true;
|
|
47
|
+
if (!a || !b) return false;
|
|
48
|
+
return a.id === b.id && a.numBeats === b.numBeats && a.variation === b.variation && String(a.bhaags) === String(b.bhaags);
|
|
57
49
|
}
|
|
58
|
-
var
|
|
59
|
-
"src/spec/svara.ts"() {
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// src/spec/taals.ts
|
|
64
|
-
var TAALS;
|
|
50
|
+
var TAALS, TAAL_FERTA;
|
|
65
51
|
var init_taals = __esm({
|
|
66
52
|
"src/spec/taals.ts"() {
|
|
67
53
|
TAALS = {
|
|
@@ -85,9 +71,32 @@ var init_taals = __esm({
|
|
|
85
71
|
dhamaar: { id: "dhamaar", numBeats: 14, bhaags: [5, 2, 3, 4], sam: 0, khaali: [7] }
|
|
86
72
|
// ধামার
|
|
87
73
|
};
|
|
74
|
+
TAAL_FERTA = "\u09A4\u09BE\u09B2\u09AB\u09C7\u09B0\u09A4\u09BE";
|
|
88
75
|
}
|
|
89
76
|
});
|
|
90
|
-
|
|
77
|
+
|
|
78
|
+
// src/spec/grace.ts
|
|
79
|
+
function graceNotes(grace) {
|
|
80
|
+
if (grace === void 0) return [];
|
|
81
|
+
return typeof grace[0] === "string" ? [{ svara: grace }] : grace;
|
|
82
|
+
}
|
|
83
|
+
var init_grace = __esm({
|
|
84
|
+
"src/spec/grace.ts"() {
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// src/spec/svara.ts
|
|
89
|
+
function hasKomal(letter) {
|
|
90
|
+
return letter === "r" || letter === "g" || letter === "d" || letter === "n";
|
|
91
|
+
}
|
|
92
|
+
function hasTivra(letter) {
|
|
93
|
+
return letter === "m";
|
|
94
|
+
}
|
|
95
|
+
var init_svara = __esm({
|
|
96
|
+
"src/spec/svara.ts"() {
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
var octaveSchema, svaraLetterSchema, svaraSchema, microtoneSchema, barSchema, bracketSchema, graceSchema, noteSchema, laySchema, taalSchema, rowSchema, partSchema, metaSchema, swaralipiSchema;
|
|
91
100
|
var init_schema = __esm({
|
|
92
101
|
"src/spec/schema.ts"() {
|
|
93
102
|
octaveSchema = zod.z.enum([
|
|
@@ -140,22 +149,6 @@ var init_schema = __esm({
|
|
|
140
149
|
)
|
|
141
150
|
);
|
|
142
151
|
laySchema = zod.z.enum(["drut", "madhya", "vilambit"]);
|
|
143
|
-
rowSchema = zod.z.object({
|
|
144
|
-
/** Optional line label (e.g. a tuk/section marker). */
|
|
145
|
-
label: zod.z.string().optional(),
|
|
146
|
-
/**
|
|
147
|
-
* লয়: the tempo tier this row switches to, held until another row changes
|
|
148
|
-
* it (absent on every row = the whole song at `meta.bpm`, i.e. দ্রুত).
|
|
149
|
-
* Read it with `rowLay()`, which also picks up the mark NLTR *prints*
|
|
150
|
-
* ("বিলম্বিত লয়:") in already-converted documents.
|
|
151
|
-
*/
|
|
152
|
-
lay: laySchema.optional(),
|
|
153
|
-
notes: zod.z.array(noteSchema)
|
|
154
|
-
});
|
|
155
|
-
partSchema = zod.z.object({
|
|
156
|
-
name: zod.z.string().optional(),
|
|
157
|
-
rows: zod.z.array(rowSchema)
|
|
158
|
-
});
|
|
159
152
|
taalSchema = zod.z.object({
|
|
160
153
|
/** Player sample key, e.g. `dadra`, `teentaal`. */
|
|
161
154
|
id: zod.z.string(),
|
|
@@ -176,6 +169,31 @@ var init_schema = __esm({
|
|
|
176
169
|
}).refine((t) => t.bhaags === void 0 || t.bhaags.reduce((a, b) => a + b, 0) === t.numBeats, {
|
|
177
170
|
message: "bhaags must sum to numBeats"
|
|
178
171
|
});
|
|
172
|
+
rowSchema = zod.z.object({
|
|
173
|
+
/** Optional line label (e.g. a tuk/section marker). */
|
|
174
|
+
label: zod.z.string().optional(),
|
|
175
|
+
/**
|
|
176
|
+
* লয়: the tempo tier this row switches to, held until another row changes
|
|
177
|
+
* it (absent on every row = the whole song at `meta.bpm`, i.e. দ্রুত).
|
|
178
|
+
* Read it with `rowLay()`, which also picks up the mark NLTR *prints*
|
|
179
|
+
* ("বিলম্বিত লয়:") in already-converted documents.
|
|
180
|
+
*/
|
|
181
|
+
lay: laySchema.optional(),
|
|
182
|
+
/**
|
|
183
|
+
* তালফেরতা: the তাল this row switches to, held until another row changes it
|
|
184
|
+
* (absent on every row = the whole song in `meta.taal`). `null` = মুক্তছন্দ
|
|
185
|
+
* from here on: the accompaniment rests until a later row names a taal.
|
|
186
|
+
* Independent of `lay` — either, both or neither may change on one row.
|
|
187
|
+
*/
|
|
188
|
+
taal: taalSchema.nullable().optional(),
|
|
189
|
+
/** That taal's name as printed (e.g. `ষষ্ঠী(২/৪)`) — `meta.taalName`'s twin. */
|
|
190
|
+
taalName: zod.z.string().optional(),
|
|
191
|
+
notes: zod.z.array(noteSchema)
|
|
192
|
+
});
|
|
193
|
+
partSchema = zod.z.object({
|
|
194
|
+
name: zod.z.string().optional(),
|
|
195
|
+
rows: zod.z.array(rowSchema)
|
|
196
|
+
});
|
|
179
197
|
metaSchema = zod.z.object({
|
|
180
198
|
title: zod.z.string(),
|
|
181
199
|
/** Cross-reference to a gitabitan.io song content id (filename, immutable). */
|
|
@@ -222,12 +240,14 @@ __export(spec_exports, {
|
|
|
222
240
|
LAY_SPEEDS: () => LAY_SPEEDS,
|
|
223
241
|
SPEC_VERSION: () => SPEC_VERSION,
|
|
224
242
|
TAALS: () => TAALS,
|
|
243
|
+
TAAL_FERTA: () => TAAL_FERTA,
|
|
225
244
|
barSchema: () => barSchema,
|
|
226
245
|
bracketSchema: () => bracketSchema,
|
|
227
246
|
graceNotes: () => graceNotes,
|
|
228
247
|
graceSchema: () => graceSchema,
|
|
229
248
|
hasKomal: () => hasKomal,
|
|
230
249
|
hasTivra: () => hasTivra,
|
|
250
|
+
isTaalFerta: () => isTaalFerta,
|
|
231
251
|
laySchema: () => laySchema,
|
|
232
252
|
metaSchema: () => metaSchema,
|
|
233
253
|
microtoneSchema: () => microtoneSchema,
|
|
@@ -240,6 +260,7 @@ __export(spec_exports, {
|
|
|
240
260
|
rowLay: () => rowLay,
|
|
241
261
|
rowSchema: () => rowSchema,
|
|
242
262
|
safeParseSwaralipi: () => safeParseSwaralipi,
|
|
263
|
+
sameTaal: () => sameTaal,
|
|
243
264
|
svaraLetterSchema: () => svaraLetterSchema,
|
|
244
265
|
svaraSchema: () => svaraSchema,
|
|
245
266
|
swaralipiSchema: () => swaralipiSchema,
|
|
@@ -260,12 +281,13 @@ var init_spec = __esm({
|
|
|
260
281
|
init_grace();
|
|
261
282
|
init_lay();
|
|
262
283
|
init_svara();
|
|
263
|
-
SPEC_VERSION = "0.
|
|
284
|
+
SPEC_VERSION = "0.9.1";
|
|
264
285
|
}
|
|
265
286
|
});
|
|
266
287
|
|
|
267
288
|
// src/player/schedule.ts
|
|
268
289
|
init_lay();
|
|
290
|
+
init_taals();
|
|
269
291
|
|
|
270
292
|
// src/player/pitch.ts
|
|
271
293
|
init_grace();
|
|
@@ -389,18 +411,23 @@ function buildSchedule(doc, opts = {}) {
|
|
|
389
411
|
let matra = 0;
|
|
390
412
|
const speeds = resolveLaySpeeds(doc.meta.laySpeeds, opts.laySpeeds);
|
|
391
413
|
const withLay = opts.lay !== false;
|
|
392
|
-
const tempo = [{ beat: 0, endBeat: 0, scale: 1, lay: "drut" }];
|
|
414
|
+
const tempo = [{ beat: 0, endBeat: 0, scale: 1, lay: "drut", taal: doc.meta.taal }];
|
|
393
415
|
let scale = 1;
|
|
416
|
+
let taal = doc.meta.taal;
|
|
394
417
|
doc.parts.forEach((part, p) => {
|
|
395
418
|
part.rows.forEach((row, r) => {
|
|
396
419
|
const lay = withLay ? rowLay(row) : void 0;
|
|
397
|
-
|
|
398
|
-
|
|
420
|
+
const layChanged = lay !== void 0 && speeds[lay] !== scale;
|
|
421
|
+
const nextTaal = row.taal !== void 0 ? row.taal : taal;
|
|
422
|
+
if (layChanged || !sameTaal(nextTaal, taal)) {
|
|
423
|
+
if (layChanged) scale = speeds[lay];
|
|
424
|
+
taal = nextTaal;
|
|
399
425
|
const last = tempo[tempo.length - 1];
|
|
400
|
-
|
|
426
|
+
const seg = { beat: cursor, endBeat: cursor, scale, lay: layChanged ? lay : last.lay, taal };
|
|
427
|
+
if (last.beat === cursor) tempo[tempo.length - 1] = seg;
|
|
401
428
|
else {
|
|
402
429
|
last.endBeat = cursor;
|
|
403
|
-
tempo.push(
|
|
430
|
+
tempo.push(seg);
|
|
404
431
|
}
|
|
405
432
|
}
|
|
406
433
|
const glides = meendContinuations(row.notes);
|
|
@@ -417,12 +444,15 @@ function buildSchedule(doc, opts = {}) {
|
|
|
417
444
|
const path = `${p}.${r}.${i}`;
|
|
418
445
|
const written = note.durationMatra ?? 1;
|
|
419
446
|
const dur = written / scale;
|
|
447
|
+
const kan = graceMidis(note.kan, tonicPc, baseOctave);
|
|
448
|
+
const resh = graceMidis(note.resh, tonicPc, baseOctave);
|
|
420
449
|
if (note.svara !== void 0) {
|
|
421
450
|
events.push({
|
|
422
451
|
beat: cursor,
|
|
423
452
|
durationBeats: dur,
|
|
424
453
|
midi: svaraToMidi(note, tonicPc, baseOctave),
|
|
425
|
-
kan
|
|
454
|
+
kan,
|
|
455
|
+
resh,
|
|
426
456
|
path,
|
|
427
457
|
kind: note.sustain === true ? "sustain" : "note",
|
|
428
458
|
...glides.has(i) && lastPitched >= 0 ? { glide: true } : {}
|
|
@@ -430,16 +460,16 @@ function buildSchedule(doc, opts = {}) {
|
|
|
430
460
|
lastPitched = events.length - 1;
|
|
431
461
|
} else if (note.sustain === true) {
|
|
432
462
|
if (lastPitched >= 0) events[lastPitched].durationBeats += dur;
|
|
433
|
-
events.push({ beat: cursor, durationBeats: dur, midi: null, kan
|
|
463
|
+
events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "sustain" });
|
|
434
464
|
} else {
|
|
435
|
-
events.push({ beat: cursor, durationBeats: dur, midi: null, kan
|
|
465
|
+
events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "rest" });
|
|
436
466
|
}
|
|
437
467
|
cursor += dur;
|
|
438
468
|
matra += written;
|
|
439
469
|
});
|
|
440
470
|
});
|
|
441
471
|
});
|
|
442
|
-
const numBeats =
|
|
472
|
+
const numBeats = tempo[0].taal?.numBeats;
|
|
443
473
|
const pickupMatra = samMatra !== void 0 && numBeats !== void 0 && numBeats > 0 ? (numBeats - samMatra % numBeats) % numBeats : 0;
|
|
444
474
|
tempo[tempo.length - 1].endBeat = cursor;
|
|
445
475
|
return { events, totalBeats: cursor, pickupBeats: pickupMatra / tempo[0].scale, tempo };
|
|
@@ -447,6 +477,7 @@ function buildSchedule(doc, opts = {}) {
|
|
|
447
477
|
|
|
448
478
|
// src/viewer/render.ts
|
|
449
479
|
init_grace();
|
|
480
|
+
init_taals();
|
|
450
481
|
|
|
451
482
|
// src/viewer/glyphs.ts
|
|
452
483
|
init_svara();
|
|
@@ -497,6 +528,11 @@ var LAY_LABELS = {
|
|
|
497
528
|
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" },
|
|
498
529
|
en: { drut: "Fast (drut)", madhya: "Medium (madhya)", vilambit: "Slow (vilambit)" }
|
|
499
530
|
};
|
|
531
|
+
var FREE_TAAL_LABELS = {
|
|
532
|
+
bn: "\u09AE\u09C1\u0995\u09CD\u09A4\u099B\u09A8\u09CD\u09A6",
|
|
533
|
+
hi: "\u092E\u0941\u0915\u094D\u0924\u091B\u0902\u0926",
|
|
534
|
+
en: "Free rhythm"
|
|
535
|
+
};
|
|
500
536
|
function composeCluster(letter, opts) {
|
|
501
537
|
const classes = [];
|
|
502
538
|
if (opts.lang === "bn") {
|
|
@@ -969,15 +1005,25 @@ function renderToHtml(doc, opts = {}) {
|
|
|
969
1005
|
out.push(`<div class="sw-scroll"><div class="sw-block" style="${tracks}">`);
|
|
970
1006
|
let n = 0;
|
|
971
1007
|
let lay;
|
|
1008
|
+
let taal = doc.meta.taal;
|
|
1009
|
+
let named = !isTaalFerta(doc.meta.taalName);
|
|
972
1010
|
doc.parts.forEach((part, p) => {
|
|
973
1011
|
out.push('<section class="sw-part">');
|
|
974
1012
|
if (part.name !== void 0)
|
|
975
1013
|
out.push(`<h3 class="sw-part-name">${escapeHtml(ctx.tr(part.name))}</h3>`);
|
|
976
1014
|
part.rows.forEach((row, r) => {
|
|
1015
|
+
const marks = [];
|
|
1016
|
+
if (row.taal !== void 0 && (!named || !sameTaal(row.taal, taal))) {
|
|
1017
|
+
taal = row.taal;
|
|
1018
|
+
named = true;
|
|
1019
|
+
const name = row.taalName ?? (taal === null ? FREE_TAAL_LABELS[ctx.lang] : taal.id);
|
|
1020
|
+
marks.push(`<span class="sw-taal" data-taal="${escapeHtml(taal?.id ?? "free")}">${escapeHtml(ctx.tr(name))}</span>`);
|
|
1021
|
+
}
|
|
977
1022
|
if (row.lay !== void 0 && row.lay !== lay) {
|
|
978
1023
|
lay = row.lay;
|
|
979
|
-
|
|
1024
|
+
marks.push(`<span class="sw-lay" data-lay="${lay}">${escapeHtml(LAY_LABELS[ctx.lang][lay])}</span>`);
|
|
980
1025
|
}
|
|
1026
|
+
if (marks.length > 0) out.push(`<div class="sw-change">${marks.join("")}</div>`);
|
|
981
1027
|
out.push(renderRow(row, p, r, layout.offsets[n++], ctx));
|
|
982
1028
|
});
|
|
983
1029
|
out.push("</section>");
|
|
@@ -1071,10 +1117,12 @@ ${FONT_FACES}
|
|
|
1071
1117
|
|
|
1072
1118
|
.sw-part-name { font-size: 0.85em; font-weight: 600; margin: 1em 0 0.2em; opacity: 0.85; }
|
|
1073
1119
|
|
|
1074
|
-
/* \u09B2\u09AF\u09BC
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1120
|
+
/* \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,
|
|
1121
|
+
centred between two systems the way \u09B8\u09CD\u09AC\u09B0\u09AC\u09BF\u09A4\u09BE\u09A8 prints "\u099D\u09BE\u0981\u09AA\u09A4\u09BE\u09B2" or
|
|
1122
|
+
"\u09A6\u09CD\u09B0\u09C1\u09A4 \u09B2\u09AF\u09BC". A full-width band rather than a row label \u2014 the label track is
|
|
1123
|
+
what the notation columns are measured against, and a six-em word in it
|
|
1124
|
+
would shove the whole sheet right. */
|
|
1125
|
+
.sw-change {
|
|
1078
1126
|
grid-column: 1 / -1;
|
|
1079
1127
|
display: flex;
|
|
1080
1128
|
align-items: center;
|
|
@@ -1088,12 +1136,14 @@ ${FONT_FACES}
|
|
|
1088
1136
|
opacity: 0.75;
|
|
1089
1137
|
break-after: avoid;
|
|
1090
1138
|
}
|
|
1091
|
-
.sw-
|
|
1139
|
+
.sw-change::before,
|
|
1140
|
+
.sw-change::after {
|
|
1092
1141
|
content: '';
|
|
1093
1142
|
flex: 1;
|
|
1094
1143
|
height: 1px;
|
|
1095
1144
|
background: var(--sw-rule, rgba(128, 128, 128, 0.35));
|
|
1096
1145
|
}
|
|
1146
|
+
.sw-taal + .sw-lay::before { content: '\xB7'; margin-right: 0.6em; }
|
|
1097
1147
|
|
|
1098
1148
|
/* ---- block grid: ONE column set for the whole sheet ----
|
|
1099
1149
|
* NLTR emits a song as a single html table, which is why its lines align
|
|
@@ -1311,7 +1361,7 @@ var SWARALIPI_PRINT_CSS = `
|
|
|
1311
1361
|
.sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }
|
|
1312
1362
|
.sw-scroll { overflow: visible !important; }
|
|
1313
1363
|
.sw-row { break-inside: avoid; page-break-inside: avoid; }
|
|
1314
|
-
.sw-part-name, .sw-header, .sw-
|
|
1364
|
+
.sw-part-name, .sw-header, .sw-change { break-after: avoid-page; page-break-after: avoid; }
|
|
1315
1365
|
.sw-hl { background: none !important; }
|
|
1316
1366
|
/* Keep each engraved staff system whole across a page break (the abcjs line
|
|
1317
1367
|
is a <div> wrapping the <svg>, so the break rule must sit on the div). */
|
|
@@ -1487,11 +1537,15 @@ function eventPaths(schedule) {
|
|
|
1487
1537
|
}
|
|
1488
1538
|
return paths;
|
|
1489
1539
|
}
|
|
1540
|
+
var barLen = (m) => m.beatsPerBar > 0 ? m.beatsPerBar : 4;
|
|
1490
1541
|
function abcText(s) {
|
|
1491
1542
|
return s.replace(/[\r\n]+/g, " ").trim();
|
|
1492
1543
|
}
|
|
1493
1544
|
function buildAbc(schedule, lyricOf, opts) {
|
|
1494
|
-
const
|
|
1545
|
+
const meters = opts.meters?.length ? opts.meters : [{ beat: 0, beatsPerBar: opts.beatsPerBar, drums: opts.drums }];
|
|
1546
|
+
const withDrums = opts.drums !== void 0 || meters.some((m2) => m2.drums !== void 0);
|
|
1547
|
+
let m = 0;
|
|
1548
|
+
let beatsPerBar = barLen(meters[0]);
|
|
1495
1549
|
const tokens = [];
|
|
1496
1550
|
const words = [];
|
|
1497
1551
|
let barPos = 0;
|
|
@@ -1505,6 +1559,17 @@ function buildAbc(schedule, lyricOf, opts) {
|
|
|
1505
1559
|
}
|
|
1506
1560
|
};
|
|
1507
1561
|
schedule.events.forEach((ev, i) => {
|
|
1562
|
+
while (m + 1 < meters.length && ev.beat >= meters[m + 1].beat - EPS2) {
|
|
1563
|
+
m++;
|
|
1564
|
+
if (barPos > EPS2) {
|
|
1565
|
+
tokens.push("|");
|
|
1566
|
+
words.push("|");
|
|
1567
|
+
}
|
|
1568
|
+
barPos = 0;
|
|
1569
|
+
const next = barLen(meters[m]);
|
|
1570
|
+
if (next !== beatsPerBar) tokens.push(`[M:${ratioMeter(next)}]`);
|
|
1571
|
+
beatsPerBar = next;
|
|
1572
|
+
}
|
|
1508
1573
|
if (ev.midi === null) {
|
|
1509
1574
|
if (ev.kind === "rest") {
|
|
1510
1575
|
tokens.push(`z${abcDuration(ev.durationBeats)}`);
|
|
@@ -1523,16 +1588,16 @@ function buildAbc(schedule, lyricOf, opts) {
|
|
|
1523
1588
|
});
|
|
1524
1589
|
if (tokens.length && tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
1525
1590
|
else if (tokens.length) tokens[tokens.length - 1] = "|]";
|
|
1526
|
-
const meter = ratioMeter(
|
|
1591
|
+
const meter = ratioMeter(barLen(meters[0]));
|
|
1527
1592
|
const head = ["X:1", `M:${meter}`, "L:1/4", `Q:1/4=${Math.round(opts.bpm)}`];
|
|
1528
|
-
if (
|
|
1593
|
+
if (withDrums) head.push(...percMapLines(meters.flatMap((x) => x.drums ?? [])));
|
|
1529
1594
|
head.push("K:C treble");
|
|
1530
1595
|
const melodyBody = tokens.length ? tokens.join(" ") : "z |]";
|
|
1531
1596
|
const lines = [...head];
|
|
1532
|
-
if (
|
|
1597
|
+
if (withDrums) {
|
|
1533
1598
|
lines.push("V:1", melodyBody);
|
|
1534
1599
|
if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
|
|
1535
|
-
lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats,
|
|
1600
|
+
lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats, meters));
|
|
1536
1601
|
} else {
|
|
1537
1602
|
lines.push(melodyBody);
|
|
1538
1603
|
if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
|
|
@@ -1561,13 +1626,21 @@ function percMapLines(voices) {
|
|
|
1561
1626
|
}
|
|
1562
1627
|
return lines;
|
|
1563
1628
|
}
|
|
1564
|
-
function buildDrumVoice(totalBeats,
|
|
1565
|
-
const n = Math.max(1, Math.ceil(totalBeats));
|
|
1629
|
+
function buildDrumVoice(totalBeats, meters) {
|
|
1566
1630
|
const tokens = [];
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (
|
|
1570
|
-
|
|
1631
|
+
meters.forEach((m, i) => {
|
|
1632
|
+
const bpb = barLen(m);
|
|
1633
|
+
if (i > 0) {
|
|
1634
|
+
if (tokens.length > 0 && tokens[tokens.length - 1] !== "|") tokens.push("|");
|
|
1635
|
+
if (bpb !== barLen(meters[i - 1])) tokens.push(`[M:${ratioMeter(bpb)}]`);
|
|
1636
|
+
}
|
|
1637
|
+
const end = meters[i + 1]?.beat ?? Math.max(1, Math.ceil(totalBeats));
|
|
1638
|
+
const n = Math.ceil(end - m.beat - 1e-6);
|
|
1639
|
+
for (let beat = 0; beat < n; beat++) {
|
|
1640
|
+
tokens.push(m.drums?.length ? DRUM_ABC[m.drums[beat % m.drums.length]].note : "z");
|
|
1641
|
+
if ((beat + 1) % bpb === 0) tokens.push("|");
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1571
1644
|
if (tokens.length === 0 || tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
1572
1645
|
else tokens[tokens.length - 1] = "|]";
|
|
1573
1646
|
return tokens.join(" ");
|
|
@@ -1578,16 +1651,25 @@ function lyricLookup(doc) {
|
|
|
1578
1651
|
return doc.parts[p]?.rows[r]?.notes[i]?.lyric;
|
|
1579
1652
|
};
|
|
1580
1653
|
}
|
|
1581
|
-
function resolveDrumTaal(
|
|
1582
|
-
const id =
|
|
1654
|
+
function resolveDrumTaal(taal) {
|
|
1655
|
+
const id = taal?.id;
|
|
1583
1656
|
return id !== void 0 && id in TAALS ? TAALS[id] : { numBeats: 4, sam: 0 };
|
|
1584
1657
|
}
|
|
1585
1658
|
function docToAbc(doc, opts = {}) {
|
|
1586
1659
|
const schedule = buildSchedule(doc, { tonic: opts.tonic, baseOctave: opts.baseOctave, lay: false });
|
|
1587
1660
|
const bpm = opts.bpm ?? doc.meta.bpm ?? 90;
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
|
|
1661
|
+
const meters = schedule.tempo.map((seg) => ({
|
|
1662
|
+
beat: seg.beat,
|
|
1663
|
+
beatsPerBar: opts.beatsPerBar ?? seg.taal?.numBeats ?? 4,
|
|
1664
|
+
drums: opts.drums && seg.taal !== null ? drumPattern(resolveDrumTaal(seg.taal)) : void 0
|
|
1665
|
+
}));
|
|
1666
|
+
return buildAbc(schedule, lyricLookup(doc), {
|
|
1667
|
+
bpm,
|
|
1668
|
+
lyrics: opts.lyrics !== false,
|
|
1669
|
+
beatsPerBar: meters[0].beatsPerBar,
|
|
1670
|
+
drums: opts.drums ? meters[0].drums ?? [] : void 0,
|
|
1671
|
+
meters
|
|
1672
|
+
});
|
|
1591
1673
|
}
|
|
1592
1674
|
async function abcToSvg(abc, opts = {}, notePaths) {
|
|
1593
1675
|
if (typeof document === "undefined") {
|
|
@@ -1666,7 +1748,7 @@ function buildTaalAbc(taal, opts = {}) {
|
|
|
1666
1748
|
const voices = Array.from({ length: n }, (_, i) => src[i % src.length]);
|
|
1667
1749
|
const bpm = Math.round(opts.bpm ?? 90);
|
|
1668
1750
|
const head = ["X:1", `M:${ratioMeter(n)}`, "L:1/4", `Q:1/4=${bpm}`, ...percMapLines(voices), "K:C"];
|
|
1669
|
-
const lines = [...head, "V:1 clef=perc stem=up", buildDrumVoice(n, n, voices)];
|
|
1751
|
+
const lines = [...head, "V:1 clef=perc stem=up", buildDrumVoice(n, [{ beat: 0, beatsPerBar: n, drums: voices }])];
|
|
1670
1752
|
if (opts.bols?.length) {
|
|
1671
1753
|
const w = opts.bols.slice(0, n).map((b) => b.trim() ? abcText(b).replace(/\s+/g, "~") : "*");
|
|
1672
1754
|
while (w.length < n) w.push("*");
|
|
@@ -2057,6 +2139,7 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
|
|
|
2057
2139
|
exports.DEFAULT_NOTATOR = DEFAULT_NOTATOR;
|
|
2058
2140
|
exports.DEFAULT_WATERMARK = DEFAULT_WATERMARK;
|
|
2059
2141
|
exports.DIGITS = DIGITS;
|
|
2142
|
+
exports.FREE_TAAL_LABELS = FREE_TAAL_LABELS;
|
|
2060
2143
|
exports.LABELS = LABELS;
|
|
2061
2144
|
exports.LAY_LABELS = LAY_LABELS;
|
|
2062
2145
|
exports.MARKS = MARKS;
|