@ugfoundation/swaralipi-js 0.8.2 → 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-6FPBT5DP.js → batch-PYBXQ4OM.js} +5 -5
- package/dist/{batch-6FPBT5DP.js.map → batch-PYBXQ4OM.js.map} +1 -1
- package/dist/{chunk-FULC22MN.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-UMBKIGVQ.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 +150 -70
- 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.0";
|
|
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);
|
|
@@ -439,7 +466,7 @@ function buildSchedule(doc, opts = {}) {
|
|
|
439
466
|
});
|
|
440
467
|
});
|
|
441
468
|
});
|
|
442
|
-
const numBeats =
|
|
469
|
+
const numBeats = tempo[0].taal?.numBeats;
|
|
443
470
|
const pickupMatra = samMatra !== void 0 && numBeats !== void 0 && numBeats > 0 ? (numBeats - samMatra % numBeats) % numBeats : 0;
|
|
444
471
|
tempo[tempo.length - 1].endBeat = cursor;
|
|
445
472
|
return { events, totalBeats: cursor, pickupBeats: pickupMatra / tempo[0].scale, tempo };
|
|
@@ -447,6 +474,7 @@ function buildSchedule(doc, opts = {}) {
|
|
|
447
474
|
|
|
448
475
|
// src/viewer/render.ts
|
|
449
476
|
init_grace();
|
|
477
|
+
init_taals();
|
|
450
478
|
|
|
451
479
|
// src/viewer/glyphs.ts
|
|
452
480
|
init_svara();
|
|
@@ -497,6 +525,11 @@ var LAY_LABELS = {
|
|
|
497
525
|
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
526
|
en: { drut: "Fast (drut)", madhya: "Medium (madhya)", vilambit: "Slow (vilambit)" }
|
|
499
527
|
};
|
|
528
|
+
var FREE_TAAL_LABELS = {
|
|
529
|
+
bn: "\u09AE\u09C1\u0995\u09CD\u09A4\u099B\u09A8\u09CD\u09A6",
|
|
530
|
+
hi: "\u092E\u0941\u0915\u094D\u0924\u091B\u0902\u0926",
|
|
531
|
+
en: "Free rhythm"
|
|
532
|
+
};
|
|
500
533
|
function composeCluster(letter, opts) {
|
|
501
534
|
const classes = [];
|
|
502
535
|
if (opts.lang === "bn") {
|
|
@@ -969,15 +1002,25 @@ function renderToHtml(doc, opts = {}) {
|
|
|
969
1002
|
out.push(`<div class="sw-scroll"><div class="sw-block" style="${tracks}">`);
|
|
970
1003
|
let n = 0;
|
|
971
1004
|
let lay;
|
|
1005
|
+
let taal = doc.meta.taal;
|
|
1006
|
+
let named = !isTaalFerta(doc.meta.taalName);
|
|
972
1007
|
doc.parts.forEach((part, p) => {
|
|
973
1008
|
out.push('<section class="sw-part">');
|
|
974
1009
|
if (part.name !== void 0)
|
|
975
1010
|
out.push(`<h3 class="sw-part-name">${escapeHtml(ctx.tr(part.name))}</h3>`);
|
|
976
1011
|
part.rows.forEach((row, r) => {
|
|
1012
|
+
const marks = [];
|
|
1013
|
+
if (row.taal !== void 0 && (!named || !sameTaal(row.taal, taal))) {
|
|
1014
|
+
taal = row.taal;
|
|
1015
|
+
named = true;
|
|
1016
|
+
const name = row.taalName ?? (taal === null ? FREE_TAAL_LABELS[ctx.lang] : taal.id);
|
|
1017
|
+
marks.push(`<span class="sw-taal" data-taal="${escapeHtml(taal?.id ?? "free")}">${escapeHtml(ctx.tr(name))}</span>`);
|
|
1018
|
+
}
|
|
977
1019
|
if (row.lay !== void 0 && row.lay !== lay) {
|
|
978
1020
|
lay = row.lay;
|
|
979
|
-
|
|
1021
|
+
marks.push(`<span class="sw-lay" data-lay="${lay}">${escapeHtml(LAY_LABELS[ctx.lang][lay])}</span>`);
|
|
980
1022
|
}
|
|
1023
|
+
if (marks.length > 0) out.push(`<div class="sw-change">${marks.join("")}</div>`);
|
|
981
1024
|
out.push(renderRow(row, p, r, layout.offsets[n++], ctx));
|
|
982
1025
|
});
|
|
983
1026
|
out.push("</section>");
|
|
@@ -1071,10 +1114,12 @@ ${FONT_FACES}
|
|
|
1071
1114
|
|
|
1072
1115
|
.sw-part-name { font-size: 0.85em; font-weight: 600; margin: 1em 0 0.2em; opacity: 0.85; }
|
|
1073
1116
|
|
|
1074
|
-
/* \u09B2\u09AF\u09BC
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1117
|
+
/* \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,
|
|
1118
|
+
centred between two systems the way \u09B8\u09CD\u09AC\u09B0\u09AC\u09BF\u09A4\u09BE\u09A8 prints "\u099D\u09BE\u0981\u09AA\u09A4\u09BE\u09B2" or
|
|
1119
|
+
"\u09A6\u09CD\u09B0\u09C1\u09A4 \u09B2\u09AF\u09BC". A full-width band rather than a row label \u2014 the label track is
|
|
1120
|
+
what the notation columns are measured against, and a six-em word in it
|
|
1121
|
+
would shove the whole sheet right. */
|
|
1122
|
+
.sw-change {
|
|
1078
1123
|
grid-column: 1 / -1;
|
|
1079
1124
|
display: flex;
|
|
1080
1125
|
align-items: center;
|
|
@@ -1088,12 +1133,14 @@ ${FONT_FACES}
|
|
|
1088
1133
|
opacity: 0.75;
|
|
1089
1134
|
break-after: avoid;
|
|
1090
1135
|
}
|
|
1091
|
-
.sw-
|
|
1136
|
+
.sw-change::before,
|
|
1137
|
+
.sw-change::after {
|
|
1092
1138
|
content: '';
|
|
1093
1139
|
flex: 1;
|
|
1094
1140
|
height: 1px;
|
|
1095
1141
|
background: var(--sw-rule, rgba(128, 128, 128, 0.35));
|
|
1096
1142
|
}
|
|
1143
|
+
.sw-taal + .sw-lay::before { content: '\xB7'; margin-right: 0.6em; }
|
|
1097
1144
|
|
|
1098
1145
|
/* ---- block grid: ONE column set for the whole sheet ----
|
|
1099
1146
|
* NLTR emits a song as a single html table, which is why its lines align
|
|
@@ -1311,7 +1358,7 @@ var SWARALIPI_PRINT_CSS = `
|
|
|
1311
1358
|
.sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }
|
|
1312
1359
|
.sw-scroll { overflow: visible !important; }
|
|
1313
1360
|
.sw-row { break-inside: avoid; page-break-inside: avoid; }
|
|
1314
|
-
.sw-part-name, .sw-header, .sw-
|
|
1361
|
+
.sw-part-name, .sw-header, .sw-change { break-after: avoid-page; page-break-after: avoid; }
|
|
1315
1362
|
.sw-hl { background: none !important; }
|
|
1316
1363
|
/* Keep each engraved staff system whole across a page break (the abcjs line
|
|
1317
1364
|
is a <div> wrapping the <svg>, so the break rule must sit on the div). */
|
|
@@ -1487,11 +1534,15 @@ function eventPaths(schedule) {
|
|
|
1487
1534
|
}
|
|
1488
1535
|
return paths;
|
|
1489
1536
|
}
|
|
1537
|
+
var barLen = (m) => m.beatsPerBar > 0 ? m.beatsPerBar : 4;
|
|
1490
1538
|
function abcText(s) {
|
|
1491
1539
|
return s.replace(/[\r\n]+/g, " ").trim();
|
|
1492
1540
|
}
|
|
1493
1541
|
function buildAbc(schedule, lyricOf, opts) {
|
|
1494
|
-
const
|
|
1542
|
+
const meters = opts.meters?.length ? opts.meters : [{ beat: 0, beatsPerBar: opts.beatsPerBar, drums: opts.drums }];
|
|
1543
|
+
const withDrums = opts.drums !== void 0 || meters.some((m2) => m2.drums !== void 0);
|
|
1544
|
+
let m = 0;
|
|
1545
|
+
let beatsPerBar = barLen(meters[0]);
|
|
1495
1546
|
const tokens = [];
|
|
1496
1547
|
const words = [];
|
|
1497
1548
|
let barPos = 0;
|
|
@@ -1505,6 +1556,17 @@ function buildAbc(schedule, lyricOf, opts) {
|
|
|
1505
1556
|
}
|
|
1506
1557
|
};
|
|
1507
1558
|
schedule.events.forEach((ev, i) => {
|
|
1559
|
+
while (m + 1 < meters.length && ev.beat >= meters[m + 1].beat - EPS2) {
|
|
1560
|
+
m++;
|
|
1561
|
+
if (barPos > EPS2) {
|
|
1562
|
+
tokens.push("|");
|
|
1563
|
+
words.push("|");
|
|
1564
|
+
}
|
|
1565
|
+
barPos = 0;
|
|
1566
|
+
const next = barLen(meters[m]);
|
|
1567
|
+
if (next !== beatsPerBar) tokens.push(`[M:${ratioMeter(next)}]`);
|
|
1568
|
+
beatsPerBar = next;
|
|
1569
|
+
}
|
|
1508
1570
|
if (ev.midi === null) {
|
|
1509
1571
|
if (ev.kind === "rest") {
|
|
1510
1572
|
tokens.push(`z${abcDuration(ev.durationBeats)}`);
|
|
@@ -1523,16 +1585,16 @@ function buildAbc(schedule, lyricOf, opts) {
|
|
|
1523
1585
|
});
|
|
1524
1586
|
if (tokens.length && tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
1525
1587
|
else if (tokens.length) tokens[tokens.length - 1] = "|]";
|
|
1526
|
-
const meter = ratioMeter(
|
|
1588
|
+
const meter = ratioMeter(barLen(meters[0]));
|
|
1527
1589
|
const head = ["X:1", `M:${meter}`, "L:1/4", `Q:1/4=${Math.round(opts.bpm)}`];
|
|
1528
|
-
if (
|
|
1590
|
+
if (withDrums) head.push(...percMapLines(meters.flatMap((x) => x.drums ?? [])));
|
|
1529
1591
|
head.push("K:C treble");
|
|
1530
1592
|
const melodyBody = tokens.length ? tokens.join(" ") : "z |]";
|
|
1531
1593
|
const lines = [...head];
|
|
1532
|
-
if (
|
|
1594
|
+
if (withDrums) {
|
|
1533
1595
|
lines.push("V:1", melodyBody);
|
|
1534
1596
|
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,
|
|
1597
|
+
lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats, meters));
|
|
1536
1598
|
} else {
|
|
1537
1599
|
lines.push(melodyBody);
|
|
1538
1600
|
if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
|
|
@@ -1561,13 +1623,21 @@ function percMapLines(voices) {
|
|
|
1561
1623
|
}
|
|
1562
1624
|
return lines;
|
|
1563
1625
|
}
|
|
1564
|
-
function buildDrumVoice(totalBeats,
|
|
1565
|
-
const n = Math.max(1, Math.ceil(totalBeats));
|
|
1626
|
+
function buildDrumVoice(totalBeats, meters) {
|
|
1566
1627
|
const tokens = [];
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (
|
|
1570
|
-
|
|
1628
|
+
meters.forEach((m, i) => {
|
|
1629
|
+
const bpb = barLen(m);
|
|
1630
|
+
if (i > 0) {
|
|
1631
|
+
if (tokens.length > 0 && tokens[tokens.length - 1] !== "|") tokens.push("|");
|
|
1632
|
+
if (bpb !== barLen(meters[i - 1])) tokens.push(`[M:${ratioMeter(bpb)}]`);
|
|
1633
|
+
}
|
|
1634
|
+
const end = meters[i + 1]?.beat ?? Math.max(1, Math.ceil(totalBeats));
|
|
1635
|
+
const n = Math.ceil(end - m.beat - 1e-6);
|
|
1636
|
+
for (let beat = 0; beat < n; beat++) {
|
|
1637
|
+
tokens.push(m.drums?.length ? DRUM_ABC[m.drums[beat % m.drums.length]].note : "z");
|
|
1638
|
+
if ((beat + 1) % bpb === 0) tokens.push("|");
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1571
1641
|
if (tokens.length === 0 || tokens[tokens.length - 1] !== "|") tokens.push("|]");
|
|
1572
1642
|
else tokens[tokens.length - 1] = "|]";
|
|
1573
1643
|
return tokens.join(" ");
|
|
@@ -1578,16 +1648,25 @@ function lyricLookup(doc) {
|
|
|
1578
1648
|
return doc.parts[p]?.rows[r]?.notes[i]?.lyric;
|
|
1579
1649
|
};
|
|
1580
1650
|
}
|
|
1581
|
-
function resolveDrumTaal(
|
|
1582
|
-
const id =
|
|
1651
|
+
function resolveDrumTaal(taal) {
|
|
1652
|
+
const id = taal?.id;
|
|
1583
1653
|
return id !== void 0 && id in TAALS ? TAALS[id] : { numBeats: 4, sam: 0 };
|
|
1584
1654
|
}
|
|
1585
1655
|
function docToAbc(doc, opts = {}) {
|
|
1586
1656
|
const schedule = buildSchedule(doc, { tonic: opts.tonic, baseOctave: opts.baseOctave, lay: false });
|
|
1587
1657
|
const bpm = opts.bpm ?? doc.meta.bpm ?? 90;
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
|
|
1658
|
+
const meters = schedule.tempo.map((seg) => ({
|
|
1659
|
+
beat: seg.beat,
|
|
1660
|
+
beatsPerBar: opts.beatsPerBar ?? seg.taal?.numBeats ?? 4,
|
|
1661
|
+
drums: opts.drums && seg.taal !== null ? drumPattern(resolveDrumTaal(seg.taal)) : void 0
|
|
1662
|
+
}));
|
|
1663
|
+
return buildAbc(schedule, lyricLookup(doc), {
|
|
1664
|
+
bpm,
|
|
1665
|
+
lyrics: opts.lyrics !== false,
|
|
1666
|
+
beatsPerBar: meters[0].beatsPerBar,
|
|
1667
|
+
drums: opts.drums ? meters[0].drums ?? [] : void 0,
|
|
1668
|
+
meters
|
|
1669
|
+
});
|
|
1591
1670
|
}
|
|
1592
1671
|
async function abcToSvg(abc, opts = {}, notePaths) {
|
|
1593
1672
|
if (typeof document === "undefined") {
|
|
@@ -1666,7 +1745,7 @@ function buildTaalAbc(taal, opts = {}) {
|
|
|
1666
1745
|
const voices = Array.from({ length: n }, (_, i) => src[i % src.length]);
|
|
1667
1746
|
const bpm = Math.round(opts.bpm ?? 90);
|
|
1668
1747
|
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)];
|
|
1748
|
+
const lines = [...head, "V:1 clef=perc stem=up", buildDrumVoice(n, [{ beat: 0, beatsPerBar: n, drums: voices }])];
|
|
1670
1749
|
if (opts.bols?.length) {
|
|
1671
1750
|
const w = opts.bols.slice(0, n).map((b) => b.trim() ? abcText(b).replace(/\s+/g, "~") : "*");
|
|
1672
1751
|
while (w.length < n) w.push("*");
|
|
@@ -2057,6 +2136,7 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
|
|
|
2057
2136
|
exports.DEFAULT_NOTATOR = DEFAULT_NOTATOR;
|
|
2058
2137
|
exports.DEFAULT_WATERMARK = DEFAULT_WATERMARK;
|
|
2059
2138
|
exports.DIGITS = DIGITS;
|
|
2139
|
+
exports.FREE_TAAL_LABELS = FREE_TAAL_LABELS;
|
|
2060
2140
|
exports.LABELS = LABELS;
|
|
2061
2141
|
exports.LAY_LABELS = LAY_LABELS;
|
|
2062
2142
|
exports.MARKS = MARKS;
|