@ugfoundation/swaralipi-js 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +7 -1
  2. package/dist/{batch-RAM2S4QO.js → batch-BS2AIZOR.js} +6 -5
  3. package/dist/batch-BS2AIZOR.js.map +1 -0
  4. package/dist/{chunk-3EWAKXLM.js → chunk-3ZBNANDF.js} +3 -3
  5. package/dist/{chunk-3EWAKXLM.js.map → chunk-3ZBNANDF.js.map} +1 -1
  6. package/dist/{chunk-LRIAUFKF.js → chunk-5N6HGPFH.js} +8 -168
  7. package/dist/chunk-5N6HGPFH.js.map +1 -0
  8. package/dist/chunk-BSZ3WQUP.js +164 -0
  9. package/dist/chunk-BSZ3WQUP.js.map +1 -0
  10. package/dist/{chunk-SR7NFEGL.js → chunk-KOHSKHXI.js} +5 -3
  11. package/dist/chunk-KOHSKHXI.js.map +1 -0
  12. package/dist/{chunk-CF3JWIHP.js → chunk-ZNJTMQIX.js} +228 -64
  13. package/dist/chunk-ZNJTMQIX.js.map +1 -0
  14. package/dist/cli.cjs +6 -4
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.js +3 -3
  17. package/dist/convert/index.cjs +6 -4
  18. package/dist/convert/index.cjs.map +1 -1
  19. package/dist/convert/index.js +4 -3
  20. package/dist/index.cjs +4 -2
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +2 -2
  25. package/dist/{lay-DypMIIuf.d.ts → lay-CWlEhImP.d.ts} +8 -1
  26. package/dist/{lay-CvZeVon3.d.cts → lay-CXhC-HRO.d.cts} +8 -1
  27. package/dist/player/index.cjs +473 -123
  28. package/dist/player/index.cjs.map +1 -1
  29. package/dist/player/index.d.cts +14 -4
  30. package/dist/player/index.d.ts +14 -4
  31. package/dist/player/index.js +87 -62
  32. package/dist/player/index.js.map +1 -1
  33. package/dist/{schedule-DjcqM6A4.d.ts → schedule-ClyON98C.d.ts} +23 -11
  34. package/dist/{schedule-C4sP7uFu.d.cts → schedule-V-fgepvP.d.cts} +23 -11
  35. package/dist/spec/index.cjs +4 -2
  36. package/dist/spec/index.cjs.map +1 -1
  37. package/dist/spec/index.d.cts +2 -2
  38. package/dist/spec/index.d.ts +2 -2
  39. package/dist/spec/index.js +2 -2
  40. package/dist/viewer/index.cjs +369 -62
  41. package/dist/viewer/index.cjs.map +1 -1
  42. package/dist/viewer/index.d.cts +2 -2
  43. package/dist/viewer/index.d.ts +2 -2
  44. package/dist/viewer/index.js +3 -2
  45. package/dist/viewer/index.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/batch-RAM2S4QO.js.map +0 -1
  48. package/dist/chunk-CF3JWIHP.js.map +0 -1
  49. package/dist/chunk-LRIAUFKF.js.map +0 -1
  50. package/dist/chunk-SR7NFEGL.js.map +0 -1
@@ -0,0 +1,164 @@
1
+ // src/convert/codec.ts
2
+ var LETTERS = {
3
+ s: { letter: "s" },
4
+ r: { letter: "r" },
5
+ g: { letter: "g" },
6
+ m: { letter: "m" },
7
+ p: { letter: "p" },
8
+ q: { letter: "d" },
9
+ // ধ — shuddha dha
10
+ n: { letter: "n" },
11
+ d: { letter: "d", komal: true },
12
+ // দ
13
+ t: { letter: "g", komal: true },
14
+ // জ্ঞ
15
+ k: { letter: "m", tivra: true },
16
+ // ক্ষ
17
+ u: { letter: "n", komal: true },
18
+ // ণ
19
+ v: { letter: "r", komal: true }
20
+ // ঋ
21
+ };
22
+ var STRUCTURALS = {
23
+ "%": () => ({ svara: ["madhyam", "s"] }),
24
+ // precomposed সা
25
+ "&": () => ({ svara: ["madhyam", "n"] })
26
+ // precomposed নি
27
+ };
28
+ var BAR_RUN_RE = /^(-)?([lLA[\]]+)$/;
29
+ function decodeBarRun(body) {
30
+ const m = body.match(BAR_RUN_RE);
31
+ if (!m || !/[lLA]/.test(m[2])) return void 0;
32
+ const notes = [];
33
+ if (m[1]) notes.push({ sustain: true });
34
+ const run = m[2];
35
+ let i = 0;
36
+ while (i < run.length) {
37
+ const ch = run[i];
38
+ if (ch === "l" || ch === "L") {
39
+ let j = i;
40
+ while (j < run.length && run[j] === ch) j++;
41
+ const strokes = (j - i) * (ch === "L" ? 2 : 1);
42
+ notes.push({ bar: strokes >= 4 ? "quad" : strokes >= 2 ? "double" : "single" });
43
+ i = j;
44
+ } else if (ch === "A") {
45
+ notes.push({ bar: "dari" });
46
+ i++;
47
+ } else {
48
+ notes.push({ brackets: [{ kind: "alt", edge: ch === "[" ? "open" : "close" }] });
49
+ i++;
50
+ }
51
+ }
52
+ return notes;
53
+ }
54
+ var KNOWN_RAW_RE = [
55
+ /^[wxyjEJ$<>=_]+$/,
56
+ // arc & line drawing segments
57
+ /^\|+$/,
58
+ // thin yugal-dari stop mark
59
+ /^[0-9!@#]{1,2}[fF]?:?$/,
60
+ // beat-number labels incl. 10–16 (sam tick = f/F, e.g. 1f / 1F / 2:)
61
+ /^[oOe`+,.;:iI~^*bcBCWXYFHZz'"?]$/
62
+ // dots, ticks, duration marks, legacy forms
63
+ ];
64
+ var OPEN_BRACKETS = {
65
+ "{": "repeat",
66
+ "(": "skip",
67
+ "[": "alt"
68
+ };
69
+ var CLOSE_BRACKETS = {
70
+ "}": "repeat",
71
+ ")": "skip",
72
+ "]": "alt"
73
+ };
74
+ function octaveOf(mark) {
75
+ if (mark === "f" || mark === "F") return "taar";
76
+ if (mark === "h" || mark === "H") return "mandra";
77
+ return "madhyam";
78
+ }
79
+ var BODY_RE = /^([0-9!@][fF]?)?([zj]*)(-)?([i:])?(?:([SRGMPQNDTKUV])([fFhH])?)?((?:[srgmpqndtkuv][fh]?[i:]?)+)?(a)?([fh])?([i:])?(?:([SRGMPQNDTKUV])([fFhH])?)?([0-9!@][fF]?)?(-)?$/;
80
+ function decodeChunk(chunk) {
81
+ const opens = [];
82
+ const closes = [];
83
+ let body = chunk;
84
+ while (body.length > 0) {
85
+ const kind = OPEN_BRACKETS[body[0]];
86
+ if (kind === void 0) break;
87
+ opens.push(kind);
88
+ body = body.slice(1);
89
+ }
90
+ while (body.length > 0) {
91
+ const kind = CLOSE_BRACKETS[body[body.length - 1]];
92
+ if (kind === void 0) break;
93
+ closes.unshift(kind);
94
+ body = body.slice(0, -1);
95
+ }
96
+ const finish = (notes, kind) => {
97
+ if (notes.length === 0) notes.push({});
98
+ const first = notes[0];
99
+ const last = notes[notes.length - 1];
100
+ if (opens.length > 0) first.brackets = [...opens.map((kind_) => ({ kind: kind_, edge: "open" })), ...first.brackets ?? []];
101
+ if (closes.length > 0) last.brackets = [...last.brackets ?? [], ...closes.map((kind_) => ({ kind: kind_, edge: "close" }))];
102
+ return { notes, kind };
103
+ };
104
+ if (body === "") return finish([{}], "decoded");
105
+ const barRun = decodeBarRun(body);
106
+ if (barRun) return finish(barRun, "decoded");
107
+ const structural = STRUCTURALS[body];
108
+ if (structural) return finish([structural()], "decoded");
109
+ if (KNOWN_RAW_RE.some((re) => re.test(body))) return finish([{}], "known");
110
+ const m = body.match(BODY_RE);
111
+ if (m) {
112
+ const [, , , hyphen, , kanLetter, kanOct, unitsStr, aakar, postOct, , trailKan, trailKanOct] = m;
113
+ const attachKan = (note, letter, oct) => {
114
+ const def = LETTERS[letter.toLowerCase()];
115
+ note.kan = [octaveOf(oct?.toLowerCase()), def.letter];
116
+ };
117
+ if (unitsStr) {
118
+ const units = unitsStr.match(/[srgmpqndtkuv][fh]?/g) ?? [];
119
+ const notes = units.map((unit) => {
120
+ const def = LETTERS[unit[0]];
121
+ const note = { svara: [octaveOf(unit[1]), def.letter] };
122
+ if (def.komal) note.komal = true;
123
+ if (def.tivra) note.tivra = true;
124
+ if (units.length > 1) note.durationMatra = 1 / units.length;
125
+ return note;
126
+ });
127
+ const last = notes[notes.length - 1];
128
+ if (postOct && last.svara[0] === "madhyam") {
129
+ last.svara = [octaveOf(postOct), last.svara[1]];
130
+ }
131
+ if (hyphen) notes[0].sustain = true;
132
+ if (kanLetter) attachKan(notes[0], kanLetter, kanOct);
133
+ if (trailKan && last.kan === void 0) attachKan(last, trailKan, trailKanOct);
134
+ return finish(notes, "decoded");
135
+ }
136
+ if (aakar) {
137
+ const note = hyphen ? { sustain: true } : { rest: true };
138
+ if (trailKan) attachKan(note, trailKan, trailKanOct);
139
+ else if (kanLetter) attachKan(note, kanLetter, kanOct);
140
+ return finish([note], "decoded");
141
+ }
142
+ if (hyphen && !kanLetter && !trailKan) {
143
+ return finish([{ sustain: true }], "decoded");
144
+ }
145
+ }
146
+ return finish([{}], "unknown");
147
+ }
148
+ function decodeCell(text) {
149
+ const chunks = text.split(/\s+/).filter((c) => c.length > 0);
150
+ const out = { notes: [], unknownTokens: [], knownRawTokens: [], decoded: false };
151
+ for (const chunk of chunks) {
152
+ const { notes, kind } = decodeChunk(chunk);
153
+ notes[0].raw = chunk;
154
+ if (kind === "decoded") out.decoded = true;
155
+ else if (kind === "known") out.knownRawTokens.push(chunk);
156
+ else out.unknownTokens.push(chunk);
157
+ out.notes.push(...notes);
158
+ }
159
+ return out;
160
+ }
161
+
162
+ export { decodeCell };
163
+ //# sourceMappingURL=chunk-BSZ3WQUP.js.map
164
+ //# sourceMappingURL=chunk-BSZ3WQUP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/convert/codec.ts"],"names":[],"mappings":"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","file":"chunk-BSZ3WQUP.js","sourcesContent":["/**\n * The Swarabitan ASCII codec: decode one grid-cell text into spec `Note`s.\n *\n * NLTR renders notation through the legacy \"Swarabitan\" webfont, which maps\n * ASCII to akarmatrik glyphs. The table below was derived by dumping every\n * glyph of `Swarabitan.woff` (tools/font-dump/) and cross-checking against\n * the Swarabitan legend scans, the rendered reference screenshot of song\n * 4270, and corpus-wide token statistics. Evidence per assignment:\n *\n * Letters (full-size glyphs):\n * s=স r=র g=গ m=ম p=প q=ধ n=ন — the seven shuddha letters\n * d=দ (komal dha) t=জ্ঞ (komal ga) — legend: কোমল র—ঋ, কোমল গ—জ্ঞ,\n * k=ক্ষ (tivra ma) u=ণ (komal ni) — কড়ি ম—ক্ষ, কোমল ধ—দ, কোমল ন—ণ\n * v=ঋ (komal re)\n * Modifiers:\n * f = রেফ above → taar octave (zero-width high combining mark)\n * h = হসন্ত below → mandra octave (low combining mark; corpus nh/uh/dh/ph)\n * leading '-' = no new vowel attack (melisma; lyric row shows ৹)\n * trailing 'a' = the আ-কার matra carrier; k letter-units in one cell\n * share it → k notes of 1/k matra each\n * UPPERCASE letter = small raised form = কণ (kan/grace) prefix; takes its\n * own f/h (corpus: Sfua, NHsa, QHpha); width ratios ~0.65\n * confirm uppercase↔lowercase pairing\n * Structurals (own cells):\n * A = thin full-height bar → দাঁড়ি vibhag separator (fixed columns)\n * l = serif danda I → cycle end; ll / single-glyph L = section\n * %=সা &=নি — precomposed one-matra forms (rare)\n * Known-but-kept-raw marks (drawn pieces the spec doesn't model yet):\n * w x = left/right ends of a long under-arc; E J j y $ < > = _ = arc/line\n * fill segments; | = thin yugal-dari stop mark (mostly annotation rows);\n * digits/!/@ (+f/F sam tick) = taal beat labels; b=× o O e ` + , . i : ;\n * = dots/ticks/duration marks; B C * ^ ~ c = rare precomposed/legacy forms.\n *\n * Decoding never throws: any chunk outside the grammar becomes a raw-only\n * note and is reported as unknown — zero data loss by construction.\n */\nimport type { Note, Octave, Svara, SvaraLetter } from '../spec/index.js';\n\nexport interface CellDecode {\n notes: Note[];\n /** Chunks that failed the grammar (reported, kept as raw notes). */\n unknownTokens: string[];\n /** Chunks understood but intentionally left raw (marks, beat labels …). */\n knownRawTokens: string[];\n /** True if at least one chunk decoded to a semantic note. */\n decoded: boolean;\n}\n\ninterface LetterDef {\n letter: SvaraLetter;\n komal?: true;\n tivra?: true;\n}\n\n/** ASCII → svara letter (+ komal/tivra). The five non-shuddha glyph letters\n * are distinct characters in akarmatrik, so the flags live here. */\nconst LETTERS: Record<string, LetterDef> = {\n s: { letter: 's' },\n r: { letter: 'r' },\n g: { letter: 'g' },\n m: { letter: 'm' },\n p: { letter: 'p' },\n q: { letter: 'd' }, // ধ — shuddha dha\n n: { letter: 'n' },\n d: { letter: 'd', komal: true }, // দ\n t: { letter: 'g', komal: true }, // জ্ঞ\n k: { letter: 'm', tivra: true }, // ক্ষ\n u: { letter: 'n', komal: true }, // ণ\n v: { letter: 'r', komal: true }, // ঋ\n};\n\n/** Standalone structural tokens (bars are handled by the bar-run rule). */\nconst STRUCTURALS: Record<string, () => Note> = {\n '%': () => ({ svara: ['madhyam', 's'] }), // precomposed সা\n '&': () => ({ svara: ['madhyam', 'n'] }), // precomposed নি\n};\n\n/**\n * Bar/bracket runs: `l`(I danda) `ll`/`L`(II) `llll`/`LL`(IIII) `A`(দাঁড়ি),\n * plus composites the corpus writes into one cell — `l[]l` / `L[]L` (the\n * legend's \"I [ ] I\" return-to-asthayi-with-changed-notes marker), `AA`,\n * and `-A` (the previous note's continuation dash sharing the dari's cell).\n */\nconst BAR_RUN_RE = /^(-)?([lLA[\\]]+)$/;\n\nfunction decodeBarRun(body: string): Note[] | undefined {\n const m = body.match(BAR_RUN_RE);\n if (!m || !/[lLA]/.test(m[2]!)) return undefined;\n const notes: Note[] = [];\n if (m[1]) notes.push({ sustain: true });\n const run = m[2]!;\n let i = 0;\n while (i < run.length) {\n const ch = run[i]!;\n if (ch === 'l' || ch === 'L') {\n let j = i;\n while (j < run.length && run[j] === ch) j++;\n const strokes = (j - i) * (ch === 'L' ? 2 : 1);\n notes.push({ bar: strokes >= 4 ? 'quad' : strokes >= 2 ? 'double' : 'single' });\n i = j;\n } else if (ch === 'A') {\n notes.push({ bar: 'dari' });\n i++;\n } else {\n notes.push({ brackets: [{ kind: 'alt', edge: ch === '[' ? 'open' : 'close' }] });\n i++;\n }\n }\n return notes;\n}\n\n/** Marks/labels that are understood but deliberately preserved raw-only. */\nconst KNOWN_RAW_RE = [\n /^[wxyjEJ$<>=_]+$/, // arc & line drawing segments\n /^\\|+$/, // thin yugal-dari stop mark\n /^[0-9!@#]{1,2}[fF]?:?$/, // beat-number labels incl. 10–16 (sam tick = f/F, e.g. 1f / 1F / 2:)\n /^[oOe`+,.;:iI~^*bcBCWXYFHZz'\"?]$/, // dots, ticks, duration marks, legacy forms\n];\n\nconst OPEN_BRACKETS: Record<string, 'repeat' | 'skip' | 'alt'> = {\n '{': 'repeat',\n '(': 'skip',\n '[': 'alt',\n};\nconst CLOSE_BRACKETS: Record<string, 'repeat' | 'skip' | 'alt'> = {\n '}': 'repeat',\n ')': 'skip',\n ']': 'alt',\n};\n\nfunction octaveOf(mark: string | undefined): Octave {\n if (mark === 'f' || mark === 'F') return 'taar';\n if (mark === 'h' || mark === 'H') return 'mandra';\n return 'madhyam';\n}\n\n/**\n * Body grammar of one chunk (brackets already peeled):\n *\n * [beat-digit] ['-'] [KAN] unit* ['a'] [fh] [i:] [KAN] [beat-digit]\n *\n * where unit = letter [fh] and KAN = uppercase letter [fFhH]. Corpus\n * realities beyond the canonical `[-]KAN unit+ a` shape:\n * - octave mark typed after the আ-কার: `saf` ≡ `sfa` (taar sa) — the reph\n * applies to the last unit;\n * - trailing `i` (two stacked rings, ঃ-shaped) or `:` (two lozenges — a\n * different glyph) after the matra: a **matra sign worth ½**, the আ-কার's\n * half-brother (legend `রাঃ` = 1½, `সঃ` = ½; corpus rows fit their taal\n * cycle 114:19 in favour of that reading over \"articulation only\"). The\n * durations below still ignore it — they split the cell evenly among its\n * units, so `qinqi` decodes ⅓⅓⅓ where the sheet says ½ ¼ ¼. The viewer\n * reads the marks off `raw` and draws them right (viewer/render.ts\n * `rawMarks`); playback is what the even split still gets wrong;\n * - trailing KAN (`-aR`): an anticipatory grace drawn at the cell's right\n * edge, leading into the next note — attached to this cell's last note;\n * - the cut mark may also lead or sit between units (`-iqi`, `mpimi`);\n * - leading `z` = blank spacer glyph (empty outline in the font) — skipped.\n * A standalone detached `a` (no hyphen) is a 1-matra rest per the legend\n * (\"হাইফেন-বর্জিত … বিরামের মাত্রা\").\n */\nconst BODY_RE =\n /^([0-9!@][fF]?)?([zj]*)(-)?([i:])?(?:([SRGMPQNDTKUV])([fFhH])?)?((?:[srgmpqndtkuv][fh]?[i:]?)+)?(a)?([fh])?([i:])?(?:([SRGMPQNDTKUV])([fFhH])?)?([0-9!@][fF]?)?(-)?$/;\n\nfunction decodeChunk(chunk: string): { notes: Note[]; kind: 'decoded' | 'known' | 'unknown' } {\n // 1. Peel brackets.\n const opens: ('repeat' | 'skip' | 'alt')[] = [];\n const closes: ('repeat' | 'skip' | 'alt')[] = [];\n let body = chunk;\n while (body.length > 0) {\n const kind = OPEN_BRACKETS[body[0]!];\n if (kind === undefined) break;\n opens.push(kind);\n body = body.slice(1);\n }\n while (body.length > 0) {\n const kind = CLOSE_BRACKETS[body[body.length - 1]!];\n if (kind === undefined) break;\n closes.unshift(kind);\n body = body.slice(0, -1);\n }\n\n const finish = (notes: Note[], kind: 'decoded' | 'known' | 'unknown') => {\n if (notes.length === 0) notes.push({});\n const first = notes[0]!;\n const last = notes[notes.length - 1]!;\n if (opens.length > 0) first.brackets = [...opens.map((kind_) => ({ kind: kind_, edge: 'open' as const })), ...(first.brackets ?? [])];\n if (closes.length > 0) last.brackets = [...(last.brackets ?? []), ...closes.map((kind_) => ({ kind: kind_, edge: 'close' as const }))];\n return { notes, kind };\n };\n\n // 2. Bracket-only chunk → raw carrier for the bracket edges.\n if (body === '') return finish([{}], 'decoded');\n\n // 3. Bar/bracket runs, then the remaining standalone structurals.\n const barRun = decodeBarRun(body);\n if (barRun) return finish(barRun, 'decoded');\n const structural = STRUCTURALS[body];\n if (structural) return finish([structural()], 'decoded');\n\n // 4. Known raw marks.\n if (KNOWN_RAW_RE.some((re) => re.test(body))) return finish([{}], 'known');\n\n // 5. Note grammar.\n const m = body.match(BODY_RE);\n if (m) {\n const [, , , hyphen, , kanLetter, kanOct, unitsStr, aakar, postOct, , trailKan, trailKanOct] = m;\n const attachKan = (note: Note, letter: string, oct: string | undefined) => {\n const def = LETTERS[letter.toLowerCase()]!;\n // NOTE: the spec kan is a plain [octave, letter] tuple, so a komal\n // kan letter (e.g. D = small দ) is projected onto its shuddha\n // letter; the exact glyph stays recoverable from `raw`.\n note.kan = [octaveOf(oct?.toLowerCase()), def.letter] as Svara;\n };\n if (unitsStr) {\n const units = unitsStr.match(/[srgmpqndtkuv][fh]?/g) ?? [];\n const notes: Note[] = units.map((unit) => {\n const def = LETTERS[unit[0]!]!;\n const note: Note = { svara: [octaveOf(unit[1]), def.letter] as Svara };\n if (def.komal) note.komal = true;\n if (def.tivra) note.tivra = true;\n if (units.length > 1) note.durationMatra = 1 / units.length;\n return note;\n });\n const last = notes[notes.length - 1]!;\n // Post-আকার octave mark (`saf`): applies to the last unit unless it\n // already carries its own mark.\n if (postOct && last.svara![0] === 'madhyam') {\n last.svara = [octaveOf(postOct), last.svara![1]] as Svara;\n }\n if (hyphen) notes[0]!.sustain = true;\n if (kanLetter) attachKan(notes[0]!, kanLetter, kanOct);\n if (trailKan && last.kan === undefined) attachKan(last, trailKan, trailKanOct);\n return finish(notes, 'decoded');\n }\n if (aakar) {\n // '-a' = sustain (hold previous pitch, no new attack);\n // bare detached 'a' = one-matra rest (legend: hyphen-less matra sign).\n const note: Note = hyphen ? { sustain: true } : { rest: true };\n if (trailKan) attachKan(note, trailKan, trailKanOct);\n else if (kanLetter) attachKan(note, kanLetter, kanOct);\n return finish([note], 'decoded');\n }\n if (hyphen && !kanLetter && !trailKan) {\n // A bare continuation dash (`-`, `-i`) — the আ-কার is implied.\n return finish([{ sustain: true }], 'decoded');\n }\n // Kan without units, or digit-edges with no note between — keep raw.\n }\n\n return finish([{}], 'unknown');\n}\n\n/**\n * Decode one cell's text. Multi-chunk cells (whitespace-separated) emit\n * notes in order; the first note of each chunk carries that chunk's source\n * text in `raw`, so joining a cell's `raw` values with spaces reproduces\n * the normalized cell text (the lossless property tests rely on this).\n */\nexport function decodeCell(text: string): CellDecode {\n const chunks = text.split(/\\s+/).filter((c) => c.length > 0);\n const out: CellDecode = { notes: [], unknownTokens: [], knownRawTokens: [], decoded: false };\n for (const chunk of chunks) {\n const { notes, kind } = decodeChunk(chunk);\n notes[0]!.raw = chunk;\n if (kind === 'decoded') out.decoded = true;\n else if (kind === 'known') out.knownRawTokens.push(chunk);\n else out.unknownTokens.push(chunk);\n out.notes.push(...notes);\n }\n return out;\n}\n"]}
@@ -19,8 +19,10 @@ var TAALS = {
19
19
  // ঝম্পক
20
20
  dhamaar: { id: "dhamaar", numBeats: 14, bhaags: [5, 2, 3, 4], sam: 0, khaali: [7] },
21
21
  // ধামার
22
- rupakra: { id: "rupakra", numBeats: 8, bhaags: [3, 2, 3], sam: 0 }
22
+ rupakra: { id: "rupakra", numBeats: 8, bhaags: [3, 2, 3], sam: 0 },
23
23
  // রূপকড়া
24
+ khemta: { id: "khemta", numBeats: 12, bhaags: [3, 3, 3, 3], sam: 0, khaali: [6] }
25
+ // খেমটা
24
26
  };
25
27
  var TAAL_FERTA = "\u09A4\u09BE\u09B2\u09AB\u09C7\u09B0\u09A4\u09BE";
26
28
  function isTaalFerta(name) {
@@ -68,5 +70,5 @@ function hasTivra(letter) {
68
70
  }
69
71
 
70
72
  export { LAYS, LAY_SPEEDS, TAALS, TAAL_FERTA, graceNotes, hasKomal, hasTivra, isTaalFerta, printedLay, resolveLaySpeeds, rowLay, sameTaal };
71
- //# sourceMappingURL=chunk-SR7NFEGL.js.map
72
- //# sourceMappingURL=chunk-SR7NFEGL.js.map
73
+ //# sourceMappingURL=chunk-KOHSKHXI.js.map
74
+ //# sourceMappingURL=chunk-KOHSKHXI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/spec/taals.ts","../src/spec/grace.ts","../src/spec/lay.ts","../src/spec/svara.ts"],"names":[],"mappings":"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","file":"chunk-KOHSKHXI.js","sourcesContent":["import type { Taal } from './schema.js';\n\n/**\n * Reference definitions for the taals the player will sample:\n * adachautaal, dadra, ektaal, jhaptaal, kehrwa, rupak, shashthi, teentaal,\n * jhampak, dhamaar, rupakra, khemta.\n *\n * `numBeats` and the `bhaags` given below are standard Hindustani taal theory.\n * `bhaags` is omitted where a single canonical division isn't well-agreed\n * (adachautaal) — we assert only what's uncontested. `sam` is beat 0 for all.\n *\n * Tabla loops live under `tabla/<id>/` by default; per-taal variation and\n * skip-bpm overrides (e.g. shashthi → `tabla/shashthi-pro/`) are in\n * instruments.ts's `TABLA_VARIATIONS`.\n */\nexport const TAALS = {\n dadra: { id: 'dadra', numBeats: 6, bhaags: [3, 3], sam: 0, khaali: [3] },\n kehrwa: { id: 'kehrwa', numBeats: 8, bhaags: [4, 4], sam: 0, khaali: [4] },\n rupak: { id: 'rupak', numBeats: 7, bhaags: [3, 2, 2], sam: 0, khaali: [0] },\n jhaptaal: { id: 'jhaptaal', numBeats: 10, bhaags: [2, 3, 2, 3], sam: 0, khaali: [5] },\n ektaal: { id: 'ektaal', numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] },\n adachautaal: { id: 'adachautaal', numBeats: 14, sam: 0 },\n choutaal: { id: 'choutaal', numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] }, // চৌতাল\n shashthi: { id: 'shashthi', numBeats: 6, bhaags: [2, 4], sam: 0 }, // ষষ্ঠী\n teora: { id: 'teora', numBeats: 7, bhaags: [3, 2, 2], sam: 0 }, // টেওরা\n teentaal: { id: 'teentaal', numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },\n aratheka: { id: 'aratheka', numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] }, // আড়াঠেকা\n jhampak: { id: 'jhampak', numBeats: 5, bhaags: [3, 2], sam: 0 }, // ঝম্পক\n dhamaar: { id: 'dhamaar', numBeats: 14, bhaags: [5, 2, 3, 4], sam: 0, khaali: [7] }, // ধামার\n rupakra: { id: 'rupakra', numBeats: 8, bhaags: [3, 2, 3], sam: 0 }, // রূপকড়া\n khemta: { id: 'khemta', numBeats: 12, bhaags: [3, 3, 3, 3], sam: 0, khaali: [6] }, // খেমটা\n} as const satisfies Record<string, Taal>;\n\nexport type TaalId = keyof typeof TAALS;\n\n/**\n * The name a তালফেরতা song's header prints instead of one taal (Swarabitan /\n * Gitabitan's own word; gitabitan.io's taxonomy spells it তাল-ফেরতা). The\n * taals themselves live on the rows (`row.taal`).\n */\nexport const TAAL_FERTA = 'তালফেরতা';\n\n/** Whether a printed taal name is the তালফেরতা heading (either spelling). */\nexport function isTaalFerta(name: string | undefined): boolean {\n return name !== undefined && name.normalize('NFC').replace(/[\\s-]/g, '') === TAAL_FERTA;\n}\n\n/**\n * Whether two taal marks name the same accompaniment — the তালফেরতা test.\n * `null` (মুক্তছন্দ) and `undefined` (no taal) are only equal to themselves.\n * Compared by value: a row restating the taal in force is not a change.\n */\nexport function sameTaal(a: Taal | null | undefined, b: Taal | null | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return a.id === b.id && a.numBeats === b.numBeats && a.variation === b.variation && String(a.bhaags) === String(b.bhaags);\n}\n","/**\n * কণ / রেশ readers. Zod-free on purpose: the viewer and the player import\n * this directly (the `spec/index.js` barrel would drag zod into their\n * browser bundles — see `taals.ts` for the same rule).\n */\nimport type { Grace, Note, Svara } from './schema.js';\n\n/** Normalise a grace payload to its note list; the legacy tuple is one note. */\nexport function graceNotes(grace: Grace | undefined): Note[] {\n if (grace === undefined) return [];\n return typeof grace[0] === 'string' ? [{ svara: grace as Svara }] : (grace as Note[]);\n}\n","/**\n * লয় (laya) — the tempo tier a passage is sung at.\n *\n * Hindustani names the three tiers বিলম্বিত / মধ্য / দ্রুত (vilambit = slow,\n * madhya = medium, drut = fast), and Rabindrasangeet swaralipi uses the same\n * words: NLTR prints \"বিলম্বিত লয়:\" / \"দ্রুত লয়:\" at the head of the line\n * where the tempo changes (see `printedLay`).\n *\n * `meta.bpm` is the **দ্রুত** tempo — the fast one — so the tiers are\n * multipliers ≤ 1 on it and a document with no লয় marks plays exactly as it\n * did before this field existed.\n *\n * Zod-free on purpose: the viewer, player and creator import this directly\n * (the `spec/index.js` barrel would drag zod into their browser bundles — the\n * same rule as `taals.ts` and `grace.ts`).\n */\nimport type { Note, Row } from './schema.js';\n\nexport type Lay = 'drut' | 'madhya' | 'vilambit';\n\nexport const LAYS: readonly Lay[] = ['drut', 'madhya', 'vilambit'];\n\n/**\n * ### The tempo factors — change them HERE.\n *\n * Fraction of `meta.bpm` each tier is played at. Consumers override per\n * document (`meta.laySpeeds`) or per player/export call (`laySpeeds`), so\n * these are only the fallback.\n */\nexport const LAY_SPEEDS: Record<Lay, number> = {\n drut: 1,\n madhya: 0.75,\n vilambit: 0.5,\n};\n\nexport type LaySpeeds = Partial<Record<Lay, number>>;\n\n/** Resolve the factors actually in force: call option → document → default. */\nexport function resolveLaySpeeds(doc?: LaySpeeds, opt?: LaySpeeds): Record<Lay, number> {\n return { ...LAY_SPEEDS, ...doc, ...opt };\n}\n\n/**\n * The printed লয় mark: the leading lyric-only cells NLTR writes a tempo\n * change as (\"বিলম্বিত\" \"লয়:\"). Reading it means the 1738 already-converted\n * songs change tempo without being reconverted or edited.\n *\n * ponytail: matches the first word only, and only in the leading annotation\n * position where real lyrics never sit — so 'মধ্যমান' (a taal) can't trip it.\n * If a source ever writes the mark elsewhere in the row, give it `row.lay`.\n */\nconst PRINTED: Record<string, Lay> = { দ্রুত: 'drut', মধ্য: 'madhya', বিলম্বিত: 'vilambit' };\n\nexport function printedLay(notes: Note[]): Lay | undefined {\n const first = notes[0];\n if (first === undefined || first.lyric === undefined) return undefined;\n for (const k of Object.keys(first)) if (k !== 'lyric') return undefined;\n return PRINTED[first.lyric.normalize('NFC')];\n}\n\n/** The লয় this row switches to, if any: the explicit field wins over the\n * source's printed mark. `undefined` = carry on at the previous tier. */\nexport function rowLay(row: Row): Lay | undefined {\n return row.lay ?? printedLay(row.notes);\n}\n","/**\n * Which svaras admit which alteration — স and প are অচল (immovable), ম has\n * only its তীব্র (কড়ি) form, and র গ ধ ন only their কোমল form. The notation\n * fonts encode exactly this (no glyph exists for a কোমল প), so a flag the\n * sheet cannot draw must not move the pitch either.\n *\n * Zod-free on purpose: the viewer and the player import this directly (the\n * `spec/index.js` barrel would drag zod into their browser bundles — see\n * `taals.ts` and `grace.ts` for the same rule).\n */\nimport type { SvaraLetter } from './schema.js';\n\nexport function hasKomal(letter: SvaraLetter): boolean {\n return letter === 'r' || letter === 'g' || letter === 'd' || letter === 'n';\n}\n\nexport function hasTivra(letter: SvaraLetter): boolean {\n return letter === 'm';\n}\n"]}
@@ -1,4 +1,5 @@
1
- import { TAALS, hasKomal, hasTivra, resolveLaySpeeds, rowLay, sameTaal, graceNotes } from './chunk-SR7NFEGL.js';
1
+ import { decodeCell } from './chunk-BSZ3WQUP.js';
2
+ import { TAALS, hasKomal, hasTivra, resolveLaySpeeds, rowLay, sameTaal, graceNotes } from './chunk-KOHSKHXI.js';
2
3
 
3
4
  // src/player/pitch.ts
4
5
  var OCTAVE_OFFSET = {
@@ -78,9 +79,144 @@ function sCurve(x, steepness = 8) {
78
79
  return (logistic(t) - lo) / (logistic(1) - lo);
79
80
  }
80
81
 
81
- // src/player/schedule.ts
82
+ // src/player/repeats.ts
82
83
  var UNDECODED_CELL = /[srgmpqnvtduka]/i;
83
84
  var isSilent = (n) => n.svara === void 0 && n.rest !== true && n.sustain !== true && !(n.raw !== void 0 && UNDECODED_CELL.test(n.raw));
85
+ var STOP_MARK = /^\|+$/;
86
+ var EPS = 1e-6;
87
+ var edges = (n, kind, edge) => n.brackets?.filter((b) => b.kind === kind && b.edge === edge).length ?? 0;
88
+ var sounds = (n) => n.svara !== void 0 || n.rest === true || n.sustain === true;
89
+ var matra = (notes) => notes.reduce((sum, n) => sum + (n.durationMatra ?? 1), 0);
90
+ function playOrder(doc) {
91
+ const tokens = [];
92
+ doc.parts.forEach(
93
+ (part, p) => part.rows.forEach(
94
+ (row, r) => row.notes.forEach((note, i) => tokens.push({ p, r, i, note, timed: note.bar === void 0 && !isSilent(note) }))
95
+ )
96
+ );
97
+ const { asthayi, stop, returns } = form(tokens);
98
+ const changed = changedNotes(tokens);
99
+ const out = [];
100
+ const sing = (lo, hi, useChanged) => {
101
+ const repeats = [];
102
+ let owed = 0;
103
+ for (let k = lo; k < hi; ) {
104
+ const t = tokens[k];
105
+ if (edges(t.note, "repeat", "open") > 0 && repeats.at(-1)?.open !== k) repeats.push({ open: k, again: false });
106
+ const again = repeats.at(-1)?.again === true;
107
+ const skipTo = again && edges(t.note, "skip", "open") > 0 ? skipEnd(tokens, k, hi) : -1;
108
+ if (skipTo >= 0) {
109
+ if (edges(tokens[skipTo].note, "repeat", "close") > 0) repeats.pop();
110
+ k = skipTo + 1;
111
+ owed = 0;
112
+ continue;
113
+ }
114
+ if (owed > EPS && t.timed) owed -= matra([t.note]);
115
+ else {
116
+ const sub = useChanged || again ? changed.get(k) : void 0;
117
+ const fits = sub !== void 0 && matra(sub) >= matra([t.note]) - EPS;
118
+ for (const note of fits ? sub : [t.note]) out.push({ p: t.p, r: t.r, i: t.i, note });
119
+ owed = fits ? matra(sub) - matra([t.note]) : 0;
120
+ }
121
+ const loop = edges(t.note, "repeat", "close") > 0 ? repeats.at(-1) : void 0;
122
+ if (loop !== void 0 && !loop.again) {
123
+ loop.again = true;
124
+ k = loop.open;
125
+ owed = 0;
126
+ continue;
127
+ }
128
+ if (loop !== void 0) repeats.pop();
129
+ k++;
130
+ }
131
+ };
132
+ let from = 0;
133
+ for (const ret of returns) {
134
+ sing(from, ret.resume, false);
135
+ sing(asthayi, stop + 1, ret.changed);
136
+ from = ret.resume;
137
+ }
138
+ sing(from, tokens.length, false);
139
+ return out;
140
+ }
141
+ function form(tokens) {
142
+ const first = /* @__PURE__ */ new Map();
143
+ const last = /* @__PURE__ */ new Map();
144
+ tokens.forEach((t, k) => {
145
+ if (!t.timed) return;
146
+ const row = `${t.p}.${t.r}`;
147
+ if (!first.has(row)) first.set(row, k);
148
+ last.set(row, k);
149
+ });
150
+ const opens = [];
151
+ let opening = false;
152
+ tokens.forEach((t, k) => {
153
+ if (t.note.bar !== "double") return;
154
+ const row = `${t.p}.${t.r}`;
155
+ const before = (first.get(row) ?? Infinity) < k;
156
+ const after = (last.get(row) ?? -1) > k;
157
+ opening = before !== after ? after : !opening;
158
+ if (opening) opens.push(k);
159
+ });
160
+ const returns = [];
161
+ for (let k = 0; k < tokens.length; k++) {
162
+ let at = -1;
163
+ let open = false;
164
+ let changed = false;
165
+ let end = false;
166
+ for (; k < tokens.length && !tokens[k].timed; k++) {
167
+ const n = tokens[k].note;
168
+ if (n.bar === "quad") end = true;
169
+ if (n.bar === "double" && !opens.includes(k)) at = k;
170
+ if (edges(n, "alt", "open") > 0) open = true;
171
+ if (open && edges(n, "alt", "close") > 0) {
172
+ at = k;
173
+ changed = true;
174
+ }
175
+ }
176
+ if (at >= 0 && !end) returns.push({ resume: at + 1, changed });
177
+ }
178
+ const top = opens[0];
179
+ const asthayi = top !== void 0 && top < (returns[0]?.resume ?? Infinity) ? top : 0;
180
+ const asthayiEnd = Math.min(returns[0]?.resume ?? Infinity, opens.find((k) => k > asthayi) ?? Infinity, tokens.length);
181
+ const lastSung = tokens.reduce((last2, t, k) => t.timed ? k : last2, -1);
182
+ const stop = tokens.findIndex(
183
+ (t, k) => k >= asthayi && k < asthayiEnd && k < lastSung && t.note.above?.some((a) => STOP_MARK.test(a.raw ?? "")) === true
184
+ );
185
+ if (stop < 0) return { asthayi, stop, returns: [] };
186
+ const atClose = returns[0]?.resume === asthayiEnd && !tokens.slice(stop + 1, asthayiEnd).some((t) => t.timed);
187
+ return { asthayi, stop, returns: atClose ? returns.slice(1) : returns };
188
+ }
189
+ function changedNotes(tokens) {
190
+ const out = /* @__PURE__ */ new Map();
191
+ let row = "";
192
+ let inside = false;
193
+ tokens.forEach((t, k) => {
194
+ if (row !== `${t.p}.${t.r}`) {
195
+ row = `${t.p}.${t.r}`;
196
+ inside = false;
197
+ }
198
+ const notes = [];
199
+ for (const above of t.note.above ?? []) {
200
+ const read = above.raw !== void 0 && !sounds(above) && above.brackets === void 0;
201
+ for (const a of read ? decodeCell(above.raw).notes : [above]) {
202
+ if (edges(a, "alt", "open") > 0) inside = true;
203
+ if (inside && sounds(a)) notes.push(a);
204
+ if (edges(a, "alt", "close") > 0) inside = false;
205
+ }
206
+ }
207
+ if (notes.length > 0) out.set(k, notes);
208
+ });
209
+ return out;
210
+ }
211
+ function skipEnd(tokens, k, hi) {
212
+ for (let depth = 0, j = k; j < hi; j++) {
213
+ depth += edges(tokens[j].note, "skip", "open") - edges(tokens[j].note, "skip", "close");
214
+ if (depth <= 0) return j;
215
+ }
216
+ return -1;
217
+ }
218
+
219
+ // src/player/schedule.ts
84
220
  var ARC_RAW_RE = /^[wWxXyYjJeEzZ$=_<>`\\]+$/;
85
221
  var isArcRaw = (raw) => raw !== void 0 && ARC_RAW_RE.test(raw);
86
222
  var MEEND_MARKS = { start: "w", end: "x", both: "xw" };
@@ -123,26 +259,45 @@ function graceTouches(ev) {
123
259
  ...ev.resh.map((midi, g) => ({ midi, beat: ev.beat + GRACE_STEP * (g + 1) }))
124
260
  ];
125
261
  }
126
- var rowOf = (path) => path.slice(0, path.lastIndexOf("."));
262
+ var rowOf = (path) => path.split(".").slice(0, 2).join(".");
263
+ var noteOf = (path) => Number(path.split(".")[2] ?? 0);
264
+ var byRow = (a, b) => {
265
+ const [ap = 0, ar = 0] = a.split(".").map(Number);
266
+ const [bp = 0, br = 0] = b.split(".").map(Number);
267
+ return ap - bp || ar - br;
268
+ };
127
269
  function scheduleLines(schedule) {
128
270
  const lines = [];
271
+ let note = Infinity;
129
272
  schedule.events.forEach((ev, i) => {
130
- if (lines.at(-1)?.row !== rowOf(ev.path)) lines.push({ row: rowOf(ev.path), first: i, beat: ev.beat, endBeat: 0 });
273
+ const n = noteOf(ev.path);
274
+ if (lines.at(-1)?.row !== rowOf(ev.path) || n < note) lines.push({ row: rowOf(ev.path), first: i, beat: ev.beat, endBeat: 0 });
275
+ note = n;
131
276
  });
132
277
  lines.forEach((line, k) => line.endBeat = lines[k + 1]?.beat ?? schedule.totalBeats);
133
278
  return lines;
134
279
  }
135
- function eventAt(events, path) {
136
- const [p = 0, r = 0, n = 0] = path.split(".").map(Number);
137
- const inRow = (ev) => ev !== void 0 && rowOf(ev.path) === `${p}.${r}`;
138
- let i = events.findIndex((ev) => {
139
- const [ep = 0, er = 0, en = 0] = ev.path.split(".").map(Number);
140
- return ep > p || ep === p && (er > r || er === r && en >= n);
280
+ function lineAt(lines, i) {
281
+ let k = 0;
282
+ while (k + 1 < lines.length && lines[k + 1].first <= i) k++;
283
+ return k;
284
+ }
285
+ function lineFor(lines, path, near) {
286
+ let row;
287
+ for (const line of lines) if (byRow(line.row, rowOf(path)) >= 0 && (row === void 0 || byRow(line.row, row) < 0)) row = line.row;
288
+ let best = -1;
289
+ lines.forEach((line, k) => {
290
+ if (line.row === row && (best < 0 || Math.abs(k - near) < Math.abs(best - near))) best = k;
141
291
  });
142
- if (!inRow(events[i])) {
143
- const last = (i < 0 ? events.length : i) - 1;
144
- if (inRow(events[last])) i = last;
145
- }
292
+ return best;
293
+ }
294
+ function eventAt(events, path, near = -1) {
295
+ const lines = scheduleLines({ events, totalBeats: 0 });
296
+ const k = lineFor(lines, path, near < 0 ? -1 : lineAt(lines, near));
297
+ if (k < 0) return -1;
298
+ const end = lines[k + 1]?.first ?? events.length;
299
+ let i = lines[k].first;
300
+ if (lines[k].row === rowOf(path)) while (i < end - 1 && noteOf(events[i].path) < noteOf(path)) i++;
146
301
  while (i > 0 && events[i].glide === true) i--;
147
302
  return i;
148
303
  }
@@ -154,67 +309,76 @@ function buildSchedule(doc, opts = {}) {
154
309
  let cursor = 0;
155
310
  let lastPitched = -1;
156
311
  let samMatra;
157
- let matra = 0;
312
+ let matra2 = 0;
158
313
  const speeds = resolveLaySpeeds(doc.meta.laySpeeds, opts.laySpeeds);
159
314
  const withLay = opts.lay !== false;
160
315
  const tempo = [{ beat: 0, endBeat: 0, scale: 1, lay: "drut", taal: doc.meta.taal }];
316
+ const rows = /* @__PURE__ */ new Map();
161
317
  let scale = 1;
318
+ let layNow = "drut";
162
319
  let taal = doc.meta.taal;
163
- doc.parts.forEach((part, p) => {
164
- part.rows.forEach((row, r) => {
320
+ doc.parts.forEach(
321
+ (part, p) => part.rows.forEach((row, r) => {
165
322
  const lay = withLay ? rowLay(row) : void 0;
166
- const layChanged = lay !== void 0 && speeds[lay] !== scale;
167
- const nextTaal = row.taal !== void 0 ? row.taal : taal;
168
- if (layChanged || !sameTaal(nextTaal, taal)) {
169
- if (layChanged) scale = speeds[lay];
170
- taal = nextTaal;
171
- const last = tempo[tempo.length - 1];
172
- const seg = { beat: cursor, endBeat: cursor, scale, lay: layChanged ? lay : last.lay, taal };
323
+ if (lay !== void 0 && speeds[lay] !== scale) {
324
+ scale = speeds[lay];
325
+ layNow = lay;
326
+ }
327
+ if (row.taal !== void 0) taal = row.taal;
328
+ rows.set(`${p}.${r}`, { scale, lay: layNow, taal, glides: meendContinuations(row.notes) });
329
+ })
330
+ );
331
+ const visits = opts.repeats === true ? playOrder(doc) : doc.parts.flatMap((part, p) => part.rows.flatMap((row, r) => row.notes.map((note, i) => ({ p, r, i, note }))));
332
+ let rowNow = "";
333
+ for (const { p, r, i, note } of visits) {
334
+ const row = rows.get(`${p}.${r}`);
335
+ if (rowNow !== `${p}.${r}`) {
336
+ rowNow = `${p}.${r}`;
337
+ const last = tempo[tempo.length - 1];
338
+ if (row.scale !== last.scale || !sameTaal(row.taal, last.taal)) {
339
+ const seg = { beat: cursor, endBeat: cursor, scale: row.scale, lay: row.lay, taal: row.taal };
173
340
  if (last.beat === cursor) tempo[tempo.length - 1] = seg;
174
341
  else {
175
342
  last.endBeat = cursor;
176
343
  tempo.push(seg);
177
344
  }
178
345
  }
179
- const glides = meendContinuations(row.notes);
180
- row.notes.forEach((note, i) => {
181
- if (samMatra === void 0 && note.above !== void 0) {
182
- for (const a of note.above) {
183
- if (a.raw !== void 0 && SAM_TICK.test(a.raw)) {
184
- samMatra = matra;
185
- break;
186
- }
187
- }
188
- }
189
- if (note.bar !== void 0 || isSilent(note)) return;
190
- const path = `${p}.${r}.${i}`;
191
- const written = note.durationMatra ?? 1;
192
- const dur = written / scale;
193
- const kan = graceMidis(note.kan, tonicPc, baseOctave);
194
- const resh = graceMidis(note.resh, tonicPc, baseOctave);
195
- if (note.svara !== void 0) {
196
- events.push({
197
- beat: cursor,
198
- durationBeats: dur,
199
- midi: svaraToMidi(note, tonicPc, baseOctave),
200
- kan,
201
- resh,
202
- path,
203
- kind: note.sustain === true ? "sustain" : "note",
204
- ...glides.has(i) && lastPitched >= 0 ? { glide: true } : {}
205
- });
206
- lastPitched = events.length - 1;
207
- } else if (note.sustain === true) {
208
- if (lastPitched >= 0) events[lastPitched].durationBeats += dur;
209
- events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "sustain" });
210
- } else {
211
- events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "rest" });
346
+ }
347
+ if (samMatra === void 0 && note.above !== void 0) {
348
+ for (const a of note.above) {
349
+ if (a.raw !== void 0 && SAM_TICK.test(a.raw)) {
350
+ samMatra = matra2;
351
+ break;
212
352
  }
213
- cursor += dur;
214
- matra += written;
353
+ }
354
+ }
355
+ if (note.bar !== void 0 || isSilent(note)) continue;
356
+ const path = `${p}.${r}.${i}`;
357
+ const written = note.durationMatra ?? 1;
358
+ const dur = written / row.scale;
359
+ const kan = graceMidis(note.kan, tonicPc, baseOctave);
360
+ const resh = graceMidis(note.resh, tonicPc, baseOctave);
361
+ if (note.svara !== void 0) {
362
+ events.push({
363
+ beat: cursor,
364
+ durationBeats: dur,
365
+ midi: svaraToMidi(note, tonicPc, baseOctave),
366
+ kan,
367
+ resh,
368
+ path,
369
+ kind: note.sustain === true ? "sustain" : "note",
370
+ ...row.glides.has(i) && lastPitched >= 0 ? { glide: true } : {}
215
371
  });
216
- });
217
- });
372
+ lastPitched = events.length - 1;
373
+ } else if (note.sustain === true) {
374
+ if (lastPitched >= 0) events[lastPitched].durationBeats += dur;
375
+ events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "sustain" });
376
+ } else {
377
+ events.push({ beat: cursor, durationBeats: dur, midi: null, kan, resh, path, kind: "rest" });
378
+ }
379
+ cursor += dur;
380
+ matra2 += written;
381
+ }
218
382
  const numBeats = tempo[0].taal?.numBeats;
219
383
  const pickupMatra = samMatra !== void 0 && numBeats !== void 0 && numBeats > 0 ? (numBeats - samMatra % numBeats) % numBeats : 0;
220
384
  tempo[tempo.length - 1].endBeat = cursor;
@@ -514,6 +678,6 @@ function createTanpura(Tone, base, stems = TANPURA_PCS) {
514
678
  return new Tone.Players({ urls, baseUrl: `${normalizeBase(base)}tanpura/` });
515
679
  }
516
680
 
517
- export { ARC_RAW_RE, DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, GRACE_STEP, INSTRUMENTS, MEEND_HOLD, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_GRAIN_OVERLAP_SEC, TABLA_GRAIN_SEC, TABLA_VARIATIONS, TANPURA_PCS, TONEJS_INSTRUMENTS_BASE, buildSchedule, createBuffers, createDrumKit, createMetronome, createSampler, createTanpura, drumPattern, eventAt, glideTargets, graceTouches, instrumentSampleUrl, isArcRaw, meendContinuations, midiToSampleName, nearestSampleKey, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sCurve, sampleFileName, santoorUrls, scheduleLines, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaShiftSemitones, tablaUrl, triggerGlide };
518
- //# sourceMappingURL=chunk-CF3JWIHP.js.map
519
- //# sourceMappingURL=chunk-CF3JWIHP.js.map
681
+ export { ARC_RAW_RE, DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, GRACE_STEP, INSTRUMENTS, MEEND_HOLD, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_GRAIN_OVERLAP_SEC, TABLA_GRAIN_SEC, TABLA_VARIATIONS, TANPURA_PCS, TONEJS_INSTRUMENTS_BASE, buildSchedule, byRow, createBuffers, createDrumKit, createMetronome, createSampler, createTanpura, drumPattern, eventAt, glideTargets, graceTouches, instrumentSampleUrl, isArcRaw, lineAt, lineFor, meendContinuations, midiToSampleName, nearestSampleKey, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sCurve, sampleFileName, santoorUrls, scheduleLines, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaShiftSemitones, tablaUrl, triggerGlide };
682
+ //# sourceMappingURL=chunk-ZNJTMQIX.js.map
683
+ //# sourceMappingURL=chunk-ZNJTMQIX.js.map