@ugfoundation/swaralipi-js 0.4.0 → 0.6.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 (47) hide show
  1. package/README.md +8 -8
  2. package/dist/{batch-V4X3GN4N.js → batch-RUZGM2NR.js} +4 -4
  3. package/dist/{batch-V4X3GN4N.js.map → batch-RUZGM2NR.js.map} +1 -1
  4. package/dist/{chunk-6QW7MSJO.js → chunk-GHWURC4U.js} +98 -6
  5. package/dist/chunk-GHWURC4U.js.map +1 -0
  6. package/dist/{chunk-RTKWFJFI.js → chunk-ULK7NVKH.js} +8 -6
  7. package/dist/chunk-ULK7NVKH.js.map +1 -0
  8. package/dist/{chunk-ZS43BDP7.js → chunk-ZNVTOY7J.js} +7 -6
  9. package/dist/chunk-ZNVTOY7J.js.map +1 -0
  10. package/dist/cli.cjs +11 -8
  11. package/dist/cli.cjs.map +1 -1
  12. package/dist/cli.js +2 -2
  13. package/dist/convert/index.cjs +10 -7
  14. package/dist/convert/index.cjs.map +1 -1
  15. package/dist/convert/index.d.cts +4 -3
  16. package/dist/convert/index.d.ts +4 -3
  17. package/dist/convert/index.js +2 -2
  18. package/dist/index.cjs +6 -4
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/player/index.cjs +222 -23
  24. package/dist/player/index.cjs.map +1 -1
  25. package/dist/player/index.d.cts +16 -4
  26. package/dist/player/index.d.ts +16 -4
  27. package/dist/player/index.js +120 -22
  28. package/dist/player/index.js.map +1 -1
  29. package/dist/{schedule-CAns78TD.d.ts → schedule-8WFUBJqB.d.ts} +82 -7
  30. package/dist/{schedule-D3JyEhuO.d.cts → schedule-CjPcD-Hf.d.cts} +82 -7
  31. package/dist/{schema-D3wyNRqM.d.ts → schema-D_86x4oV.d.cts} +21 -6
  32. package/dist/{schema-D3wyNRqM.d.cts → schema-D_86x4oV.d.ts} +21 -6
  33. package/dist/spec/index.cjs +6 -4
  34. package/dist/spec/index.cjs.map +1 -1
  35. package/dist/spec/index.d.cts +3 -3
  36. package/dist/spec/index.d.ts +3 -3
  37. package/dist/spec/index.js +1 -1
  38. package/dist/viewer/index.cjs +253 -172
  39. package/dist/viewer/index.cjs.map +1 -1
  40. package/dist/viewer/index.d.cts +16 -23
  41. package/dist/viewer/index.d.ts +16 -23
  42. package/dist/viewer/index.js +88 -47
  43. package/dist/viewer/index.js.map +1 -1
  44. package/package.json +1 -1
  45. package/dist/chunk-6QW7MSJO.js.map +0 -1
  46. package/dist/chunk-RTKWFJFI.js.map +0 -1
  47. package/dist/chunk-ZS43BDP7.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/spec/schema.ts","../src/spec/taals.ts","../src/spec/index.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * swaralipi-js JSON spec — Zod schema + inferred types.\n\n/** Octave registers */\nexport const octaveSchema = z.enum([\n 'ati-mandra', // very low\n 'mandra', // low (dot below)\n 'madhyam', // middle\n 'taar', // high (ref/dot above)\n 'ati-taar', // very high\n]);\nexport type Octave = z.infer<typeof octaveSchema>;\n\n/**\n * The seven shuddha svara letters:\n * `d` = ধৈবত (dha), `n` = নিষাদ (ni). Komal/tivra are flags, not letters.\n */\nexport const svaraLetterSchema = z.enum(['s', 'r', 'g', 'm', 'p', 'd', 'n']);\nexport type SvaraLetter = z.infer<typeof svaraLetterSchema>;\n\n/** A pitch as `[octave, letter]` */\nexport const svaraSchema = z.tuple([octaveSchema, svaraLetterSchema]);\nexport type Svara = z.infer<typeof svaraSchema>;\n\n/** Microtone variants below komal (legend: অল্পকোমল / অতিকোমল). */\nexport const microtoneSchema = z.enum(['alpakomal', 'atikomal']);\nexport type Microtone = z.infer<typeof microtoneSchema>;\n\n/**\n * Barline weights. Swarabitan distinguishes the light দাঁড়ি (।) that\n * separates vibhags *within* a taal cycle from the দণ্ড (danda, I) strokes:\n * - `dari` — । vibhag separator inside the cycle\n * - `single` — I end of one taal cycle (ফেরা)\n * - `double` — II section (kali) boundary / return-to-asthayi marker\n * - `quad` — IIII end of the song\n */\nexport const barSchema = z.enum(['dari', 'single', 'double', 'quad']);\nexport type Bar = z.infer<typeof barSchema>;\n\n/**\n * A repeat-bracket edge. Brackets span a range of notes; the opening note\n * carries `{ ..., edge: 'open' }` and the closing note `{ ..., edge: 'close' }`.\n * kind: `repeat` = {} গুন্ডবন্ধনী, `skip` = () বক্রবন্ধনী (omit on repeat),\n * `alt` = [] সরল বন্ধনী (changed notes on repeat).\n */\nexport const bracketSchema = z.object({\n kind: z.enum(['repeat', 'skip', 'alt']),\n edge: z.enum(['open', 'close']),\n});\nexport type Bracket = z.infer<typeof bracketSchema>;\n\n/**\n * A single token in a row. A token is one of: a pitched note (`svara`),\n * a rest (`rest`), an আ-কার sustain (`sustain`), a barline (`bar`), or a\n * purely raw/undecoded cell (`raw`) — possibly carrying annotations\n * (`above`/`below`) or a lyric syllable on an otherwise empty column.\n * The `.refine` enforces that a note carries at least one of these so\n * truly empty tokens can't slip through.\n *\n * `above`/`below` make `Note` recursive, so the type is hand-written and\n * the schema is typed `z.ZodType<Note>` via `z.lazy` (all fields are\n * optional, hence input ≡ output and the annotation is sound).\n */\nexport interface Note {\n /** Pitch. Omitted for rests, sustains, and barlines. */\n svara?: Svara;\n /** কোমল (flat). */\n komal?: boolean;\n /** তীব্র / কড়ি (sharp — applies to ma). */\n tivra?: boolean;\n /** Sub-komal microtone (legend: অল্পকোমল / অতিকোমল). */\n microtone?: Microtone;\n /** Duration in মাত্রা (beats): 1 (default), 0.5, 0.25, 2, … */\n durationMatra?: number;\n /**\n * আ-কার: no new syllable attack. Alone (no `svara`) it holds the previous\n * note's pitch and vowel; combined with `svara` it is a melisma — the\n * pitch moves while the previous syllable's vowel continues (Swarabitan\n * writes these with a leading hyphen, e.g. `-sfa`).\n */\n sustain?: boolean;\n /** বিরাম: a rest of `durationMatra` beats. */\n rest?: boolean;\n /** কণ স্বর: a grace note attached before this note. */\n kan?: Svara;\n /** মীড় (glide/slur) endpoint. */\n meend?: 'start' | 'end';\n /** Barline. A bar token usually has no `svara`. */\n bar?: Bar;\n /** Repeat-bracket edges opening/closing on this token. */\n brackets?: Bracket[];\n /** Lyric syllable aligned under this note */\n lyric?: string;\n /** Lossless passthrough of the source cell (e.g. raw Swarabitan code). */\n raw?: string;\n /**\n * Annotation notes rendered above this column: alt-on-repeat passages\n * (legend: পরিবর্তিত স্বর in [ ] শিরোদেশে), stop marks, beat numbers.\n */\n above?: Note[];\n /** Annotation notes rendered below this column. */\n below?: Note[];\n}\n\nexport const noteSchema: z.ZodType<Note> = z.lazy(() =>\n z\n .object({\n svara: svaraSchema.optional(),\n komal: z.boolean().optional(),\n tivra: z.boolean().optional(),\n microtone: microtoneSchema.optional(),\n durationMatra: z.number().positive().optional(),\n sustain: z.boolean().optional(),\n rest: z.boolean().optional(),\n kan: svaraSchema.optional(),\n meend: z.enum(['start', 'end']).optional(),\n bar: barSchema.optional(),\n brackets: z.array(bracketSchema).optional(),\n lyric: z.string().optional(),\n raw: z.string().optional(),\n above: z.array(noteSchema).optional(),\n below: z.array(noteSchema).optional(),\n })\n .refine(\n (n) =>\n n.svara !== undefined ||\n n.rest === true ||\n n.sustain === true ||\n n.bar !== undefined ||\n n.raw !== undefined ||\n (n.brackets !== undefined && n.brackets.length > 0) ||\n (n.above !== undefined && n.above.length > 0) ||\n (n.below !== undefined && n.below.length > 0) ||\n n.lyric !== undefined,\n {\n message:\n 'note must carry one of: svara, rest, sustain, bar, raw, lyric, brackets, or a non-empty above/below',\n },\n ),\n);\n\n/** A written line of the song: a sequence of note tokens. */\nexport const rowSchema = z.object({\n /** Optional line label (e.g. a tuk/section marker). */\n label: z.string().optional(),\n notes: z.array(noteSchema),\n});\nexport type Row = z.infer<typeof rowSchema>;\n\n/** A structural section: আস্থায়ী / অন্তরা / সঞ্চারী / আভোগ, or free text. */\nexport const partSchema = z.object({\n name: z.string().optional(),\n rows: z.array(rowSchema),\n});\nexport type Part = z.infer<typeof partSchema>;\n\n/**\n * Taal definition. `numBeats` is the cycle length in মাত্রা; `bhaags` (if\n * present) must sum to `numBeats`; `sam`/`khaali` are 0-based beat indices.\n */\nexport const taalSchema = z\n .object({\n /** Player sample key, e.g. `dadra`, `teentaal`. */\n id: z.string(),\n /**\n * Which tabla loop variation to play, e.g. `pro` (CDN dir suffix). Optional\n * and additive — omitted means the taal's default variation, so existing\n * docs are unaffected. The player honors it but never exposes a variation\n * picker; selecting a non-default variation is the consumer's premium policy.\n */\n variation: z.string().optional(),\n numBeats: z.number().int().positive(),\n /** Vibhag groupings; if given, they sum to numBeats. */\n bhaags: z.array(z.number().int().positive()).optional(),\n /** Sam (cycle start) beat index, 0-based. */\n sam: z.number().int().nonnegative().optional(),\n /** Khaali (wave) beat indices, 0-based. */\n khaali: z.array(z.number().int().nonnegative()).optional(),\n })\n .refine((t) => t.bhaags === undefined || t.bhaags.reduce((a, b) => a + b, 0) === t.numBeats, {\n message: 'bhaags must sum to numBeats',\n });\nexport type Taal = z.infer<typeof taalSchema>;\n\n/** Document-level metadata. */\nexport const metaSchema = z.object({\n title: z.string(),\n /** Cross-reference to a gitabitan.io song content id (filename, immutable). */\n songId: z.string().optional(),\n /** Source NLTR node/song id. */\n nltrId: z.number().int().optional(),\n taal: taalSchema.optional(),\n /**\n * The taal name exactly as printed in the source (e.g. `তালমুক্ত`,\n * `ষষ্ঠী(২/৪)`). Always set by the NLTR converter; `taal` is set only\n * when the name resolves to a known definition.\n */\n taalName: z.string().optional(),\n /** Base pitch for playback, e.g. `C`, `C#`, or the player's `cs` form. */\n tonic: z.string().optional(),\n /** Default playback tempo in matra/min. Player-only — the viewer ignores it.\n * The player adopts it on `load()` (precedence: constructor `bpm` → `setBpm`\n * tweak → this → 90). `null` is the converter's blank placeholder = unset. */\n bpm: z.number().positive().nullable().optional(),\n raag: z.string().optional(),\n /** আবর্তন: number of taal cycles, when the source states it. */\n avartan: z.number().int().positive().optional(),\n /** স্বরলিপিকার (notation author). */\n notationBy: z.string().optional(),\n /** Provenance note or URL. */\n source: z.string().optional(),\n});\nexport type Meta = z.infer<typeof metaSchema>;\n\n/** A complete swaralipi document. */\nexport const swaralipiSchema = z.object({\n /** Spec version this document targets (see SPEC_VERSION in ./index). */\n version: z.string(),\n meta: metaSchema,\n parts: z.array(partSchema),\n});\nexport type Swaralipi = z.infer<typeof swaralipiSchema>;\n","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.\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} as const satisfies Record<string, Taal>;\n\nexport type TaalId = keyof typeof TAALS;\n","/**\n * @ugfoundation/swaralipi-js/spec\n *\n * The swaralipi JSON spec: Zod schemas, inferred TS types, and parse helpers.\n */\nimport { swaralipiSchema, type Swaralipi } from './schema.js';\n\n/** Current spec version. Documents declare the version they target. */\nexport const SPEC_VERSION = '0.4.0';\n\nexport {\n octaveSchema,\n svaraLetterSchema,\n svaraSchema,\n microtoneSchema,\n barSchema,\n bracketSchema,\n noteSchema,\n rowSchema,\n partSchema,\n taalSchema,\n metaSchema,\n swaralipiSchema,\n} from './schema.js';\n\nexport type {\n Octave,\n SvaraLetter,\n Svara,\n Microtone,\n Bar,\n Bracket,\n Note,\n Row,\n Part,\n Taal,\n Meta,\n Swaralipi,\n} from './schema.js';\n\nexport { TAALS, type TaalId } from './taals.js';\n\n/** Parse + validate unknown data into a `Swaralipi`. Throws `ZodError` on failure. */\nexport function parseSwaralipi(data: unknown): Swaralipi {\n return swaralipiSchema.parse(data);\n}\n\n/** Non-throwing variant: returns Zod's `{ success, data | error }` result. */\nexport function safeParseSwaralipi(data: unknown) {\n return swaralipiSchema.safeParse(data);\n}\n"]}
1
+ {"version":3,"sources":["../src/spec/schema.ts","../src/spec/taals.ts","../src/spec/index.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * swaralipi-js JSON spec — Zod schema + inferred types.\n\n/** Octave registers */\nexport const octaveSchema = z.enum([\n 'ati-mandra', // very low\n 'mandra', // low (dot below)\n 'madhyam', // middle\n 'taar', // high (ref/dot above)\n 'ati-taar', // very high\n]);\nexport type Octave = z.infer<typeof octaveSchema>;\n\n/**\n * The seven shuddha svara letters:\n * `d` = ধৈবত (dha), `n` = নিষাদ (ni). Komal/tivra are flags, not letters.\n */\nexport const svaraLetterSchema = z.enum(['s', 'r', 'g', 'm', 'p', 'd', 'n']);\nexport type SvaraLetter = z.infer<typeof svaraLetterSchema>;\n\n/** A pitch as `[octave, letter]` */\nexport const svaraSchema = z.tuple([octaveSchema, svaraLetterSchema]);\nexport type Svara = z.infer<typeof svaraSchema>;\n\n/** Microtone variants below komal (legend: অল্পকোমল / অতিকোমল). */\nexport const microtoneSchema = z.enum(['alpakomal', 'atikomal']);\nexport type Microtone = z.infer<typeof microtoneSchema>;\n\n/**\n * Barline weights. Swarabitan distinguishes the light দাঁড়ি (।) that\n * separates vibhags *within* a taal cycle from the দণ্ড (danda, I) strokes:\n * - `dari` — । vibhag separator inside the cycle\n * - `single` — I end of one taal cycle (ফেরা)\n * - `double` — II section (kali) boundary / return-to-asthayi marker\n * - `quad` — IIII end of the song\n */\nexport const barSchema = z.enum(['dari', 'single', 'double', 'quad']);\nexport type Bar = z.infer<typeof barSchema>;\n\n/**\n * A repeat-bracket edge. Brackets span a range of notes; the opening note\n * carries `{ ..., edge: 'open' }` and the closing note `{ ..., edge: 'close' }`.\n * kind: `repeat` = {} গুন্ডবন্ধনী, `skip` = () বক্রবন্ধনী (omit on repeat),\n * `alt` = [] সরল বন্ধনী (changed notes on repeat).\n */\nexport const bracketSchema = z.object({\n kind: z.enum(['repeat', 'skip', 'alt']),\n edge: z.enum(['open', 'close']),\n});\nexport type Bracket = z.infer<typeof bracketSchema>;\n\n/**\n * A single token in a row. A token is one of: a pitched note (`svara`),\n * a rest (`rest`), an আ-কার sustain (`sustain`), a barline (`bar`), or a\n * purely raw/undecoded cell (`raw`) — possibly carrying annotations\n * (`above`/`below`) or a lyric syllable on an otherwise empty column.\n * The `.refine` enforces that a note carries at least one of these so\n * truly empty tokens can't slip through.\n *\n * `above`/`below` make `Note` recursive, so the type is hand-written and\n * the schema is typed `z.ZodType<Note>` via `z.lazy` (all fields are\n * optional, hence input ≡ output and the annotation is sound).\n */\nexport interface Note {\n /** Pitch. Omitted for rests, sustains, and barlines. */\n svara?: Svara;\n /** কোমল (flat). */\n komal?: boolean;\n /** তীব্র / কড়ি (sharp — applies to ma). */\n tivra?: boolean;\n /** Sub-komal microtone (legend: অল্পকোমল / অতিকোমল). */\n microtone?: Microtone;\n /** Duration in মাত্রা (beats): 1 (default), 0.5, 0.25, 2, … */\n durationMatra?: number;\n /**\n * আ-কার: no new syllable attack. Alone (no `svara`) it holds the previous\n * note's pitch and vowel; combined with `svara` it is a melisma — the\n * pitch moves while the previous syllable's vowel continues (Swarabitan\n * writes these with a leading hyphen, e.g. `-sfa`).\n */\n sustain?: boolean;\n /** বিরাম: a rest of `durationMatra` beats. */\n rest?: boolean;\n /** কণ স্বর: a grace note attached before this note. */\n kan?: Svara;\n /**\n * রেশ: an after-touch grace — the legend's mirror of কণ, written small to\n * the *right* of the note (\"আসল স্বরের পরে … অন্য স্বরের ঈষৎ রেশ লাগে;\n * তখন সেই স্বর ক্ষুদ্র অক্ষরে দক্ষিণ পার্শ্বে লিখিত হয়, যথা— রা˰স\").\n * Swarabitan writes it as a trailing uppercase letter (`mpaM`, `raS`).\n */\n resh?: Svara;\n /** মীড় (glide/slur) endpoint. */\n meend?: 'start' | 'end';\n /**\n * অর্ধমাত্রা (ঃ): this note closes a cell whose matra unit is a *half* —\n * `সঃ` = ½ matra, `সরঃ` = two ¼s, `রাঃ` = 1½ (legend p. ১৩৫).\n * Explicit rather than derived, because durations alone cannot distinguish\n * two half-matra cells (`সঃ গঃ`) from one matra split in two (`সরা`); this\n * flag is what closes the cell and prints the ঃ.\n */\n halfMatra?: boolean;\n /** Barline. A bar token usually has no `svara`. */\n bar?: Bar;\n /** Repeat-bracket edges opening/closing on this token. */\n brackets?: Bracket[];\n /** Lyric syllable aligned under this note */\n lyric?: string;\n /** Lossless passthrough of the source cell (e.g. raw Swarabitan code). */\n raw?: string;\n /**\n * Annotation notes rendered above this column: alt-on-repeat passages\n * (legend: পরিবর্তিত স্বর in [ ] শিরোদেশে), stop marks, beat numbers.\n */\n above?: Note[];\n /** Annotation notes rendered below this column. */\n below?: Note[];\n}\n\nexport const noteSchema: z.ZodType<Note> = z.lazy(() =>\n z\n .object({\n svara: svaraSchema.optional(),\n komal: z.boolean().optional(),\n tivra: z.boolean().optional(),\n microtone: microtoneSchema.optional(),\n durationMatra: z.number().positive().optional(),\n sustain: z.boolean().optional(),\n rest: z.boolean().optional(),\n kan: svaraSchema.optional(),\n resh: svaraSchema.optional(),\n meend: z.enum(['start', 'end']).optional(),\n halfMatra: z.boolean().optional(),\n bar: barSchema.optional(),\n brackets: z.array(bracketSchema).optional(),\n lyric: z.string().optional(),\n raw: z.string().optional(),\n above: z.array(noteSchema).optional(),\n below: z.array(noteSchema).optional(),\n })\n .refine(\n (n) =>\n n.svara !== undefined ||\n n.rest === true ||\n n.sustain === true ||\n n.bar !== undefined ||\n n.raw !== undefined ||\n (n.brackets !== undefined && n.brackets.length > 0) ||\n (n.above !== undefined && n.above.length > 0) ||\n (n.below !== undefined && n.below.length > 0) ||\n n.lyric !== undefined,\n {\n message:\n 'note must carry one of: svara, rest, sustain, bar, raw, lyric, brackets, or a non-empty above/below',\n },\n ),\n);\n\n/** A written line of the song: a sequence of note tokens. */\nexport const rowSchema = z.object({\n /** Optional line label (e.g. a tuk/section marker). */\n label: z.string().optional(),\n notes: z.array(noteSchema),\n});\nexport type Row = z.infer<typeof rowSchema>;\n\n/** A structural section: আস্থায়ী / অন্তরা / সঞ্চারী / আভোগ, or free text. */\nexport const partSchema = z.object({\n name: z.string().optional(),\n rows: z.array(rowSchema),\n});\nexport type Part = z.infer<typeof partSchema>;\n\n/**\n * Taal definition. `numBeats` is the cycle length in মাত্রা; `bhaags` (if\n * present) must sum to `numBeats`; `sam`/`khaali` are 0-based beat indices.\n */\nexport const taalSchema = z\n .object({\n /** Player sample key, e.g. `dadra`, `teentaal`. */\n id: z.string(),\n /**\n * Which tabla loop variation to play, e.g. `pro` (CDN dir suffix). Optional\n * and additive — omitted means the taal's default variation, so existing\n * docs are unaffected. The player honors it but never exposes a variation\n * picker; selecting a non-default variation is the consumer's premium policy.\n */\n variation: z.string().optional(),\n numBeats: z.number().int().positive(),\n /** Vibhag groupings; if given, they sum to numBeats. */\n bhaags: z.array(z.number().int().positive()).optional(),\n /** Sam (cycle start) beat index, 0-based. */\n sam: z.number().int().nonnegative().optional(),\n /** Khaali (wave) beat indices, 0-based. */\n khaali: z.array(z.number().int().nonnegative()).optional(),\n })\n .refine((t) => t.bhaags === undefined || t.bhaags.reduce((a, b) => a + b, 0) === t.numBeats, {\n message: 'bhaags must sum to numBeats',\n });\nexport type Taal = z.infer<typeof taalSchema>;\n\n/** Document-level metadata. */\nexport const metaSchema = z.object({\n title: z.string(),\n /** Cross-reference to a gitabitan.io song content id (filename, immutable). */\n songId: z.string().optional(),\n /** Source NLTR node/song id. */\n nltrId: z.number().int().optional(),\n taal: taalSchema.optional(),\n /**\n * The taal name as printed in the source (e.g. `ষষ্ঠী(২/৪)`), except\n * free rhythm which is normalised to `মুক্তছন্দ`. Always set by the NLTR\n * converter; `taal` is set only when the name resolves to a definition.\n */\n taalName: z.string().optional(),\n /** Base pitch for playback, e.g. `C`, `C#`, or the player's `cs` form. */\n tonic: z.string().optional(),\n /** Default playback tempo in matra/min. Player-only — the viewer ignores it.\n * The player adopts it on `load()` (precedence: constructor `bpm` → `setBpm`\n * tweak → this → 90). `null` is the converter's blank placeholder = unset. */\n bpm: z.number().positive().nullable().optional(),\n raag: z.string().optional(),\n /** আবর্তন: number of taal cycles, when the source states it. */\n avartan: z.number().int().positive().optional(),\n /** স্বরলিপিকার (notation author). */\n notationBy: z.string().optional(),\n /** Provenance note or URL. */\n source: z.string().optional(),\n});\nexport type Meta = z.infer<typeof metaSchema>;\n\n/** A complete swaralipi document. */\nexport const swaralipiSchema = z.object({\n /** Spec version this document targets (see SPEC_VERSION in ./index). */\n version: z.string(),\n meta: metaSchema,\n parts: z.array(partSchema),\n});\nexport type Swaralipi = z.infer<typeof swaralipiSchema>;\n","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.\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} as const satisfies Record<string, Taal>;\n\nexport type TaalId = keyof typeof TAALS;\n","/**\n * @ugfoundation/swaralipi-js/spec\n *\n * The swaralipi JSON spec: Zod schemas, inferred TS types, and parse helpers.\n */\nimport { swaralipiSchema, type Swaralipi } from './schema.js';\n\n/** Current spec version. Documents declare the version they target. */\nexport const SPEC_VERSION = '0.6.0';\n\nexport {\n octaveSchema,\n svaraLetterSchema,\n svaraSchema,\n microtoneSchema,\n barSchema,\n bracketSchema,\n noteSchema,\n rowSchema,\n partSchema,\n taalSchema,\n metaSchema,\n swaralipiSchema,\n} from './schema.js';\n\nexport type {\n Octave,\n SvaraLetter,\n Svara,\n Microtone,\n Bar,\n Bracket,\n Note,\n Row,\n Part,\n Taal,\n Meta,\n Swaralipi,\n} from './schema.js';\n\nexport { TAALS, type TaalId } from './taals.js';\n\n/** Parse + validate unknown data into a `Swaralipi`. Throws `ZodError` on failure. */\nexport function parseSwaralipi(data: unknown): Swaralipi {\n return swaralipiSchema.parse(data);\n}\n\n/** Non-throwing variant: returns Zod's `{ success, data | error }` result. */\nexport function safeParseSwaralipi(data: unknown) {\n return swaralipiSchema.safeParse(data);\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { SPEC_VERSION, parseSwaralipi, safeParseSwaralipi } from './spec/index.cjs';
2
- export { B as Bar, b as Bracket, M as Meta, c as Microtone, N as Note, O as Octave, P as Part, R as Row, d as Svara, a as SvaraLetter, S as Swaralipi, T as Taal, e as barSchema, f as bracketSchema, m as metaSchema, g as microtoneSchema, n as noteSchema, o as octaveSchema, p as partSchema, r as rowSchema, s as svaraLetterSchema, h as svaraSchema, i as swaralipiSchema, t as taalSchema } from './schema-D3wyNRqM.cjs';
2
+ export { B as Bar, b as Bracket, M as Meta, c as Microtone, N as Note, O as Octave, P as Part, R as Row, d as Svara, a as SvaraLetter, S as Swaralipi, T as Taal, e as barSchema, f as bracketSchema, m as metaSchema, g as microtoneSchema, n as noteSchema, o as octaveSchema, p as partSchema, r as rowSchema, s as svaraLetterSchema, h as svaraSchema, i as swaralipiSchema, t as taalSchema } from './schema-D_86x4oV.cjs';
3
3
  export { a as TAALS, T as TaalId } from './taals-CjzBuZ35.cjs';
4
4
  import 'zod';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { SPEC_VERSION, parseSwaralipi, safeParseSwaralipi } from './spec/index.js';
2
- export { B as Bar, b as Bracket, M as Meta, c as Microtone, N as Note, O as Octave, P as Part, R as Row, d as Svara, a as SvaraLetter, S as Swaralipi, T as Taal, e as barSchema, f as bracketSchema, m as metaSchema, g as microtoneSchema, n as noteSchema, o as octaveSchema, p as partSchema, r as rowSchema, s as svaraLetterSchema, h as svaraSchema, i as swaralipiSchema, t as taalSchema } from './schema-D3wyNRqM.js';
2
+ export { B as Bar, b as Bracket, M as Meta, c as Microtone, N as Note, O as Octave, P as Part, R as Row, d as Svara, a as SvaraLetter, S as Swaralipi, T as Taal, e as barSchema, f as bracketSchema, m as metaSchema, g as microtoneSchema, n as noteSchema, o as octaveSchema, p as partSchema, r as rowSchema, s as svaraLetterSchema, h as svaraSchema, i as swaralipiSchema, t as taalSchema } from './schema-D_86x4oV.js';
3
3
  export { a as TAALS, T as TaalId } from './taals-CjzBuZ35.js';
4
4
  import 'zod';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { SPEC_VERSION, barSchema, bracketSchema, metaSchema, microtoneSchema, noteSchema, octaveSchema, parseSwaralipi, partSchema, rowSchema, safeParseSwaralipi, svaraLetterSchema, svaraSchema, swaralipiSchema, taalSchema } from './chunk-RTKWFJFI.js';
1
+ export { SPEC_VERSION, barSchema, bracketSchema, metaSchema, microtoneSchema, noteSchema, octaveSchema, parseSwaralipi, partSchema, rowSchema, safeParseSwaralipi, svaraLetterSchema, svaraSchema, swaralipiSchema, taalSchema } from './chunk-ULK7NVKH.js';
2
2
  export { TAALS } from './chunk-6ETPFHZ6.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -96,9 +96,50 @@ function midiToSampleName(midi) {
96
96
  function sampleFileName(midi) {
97
97
  return midiToSampleName(midi).toLowerCase().replace("#", "s");
98
98
  }
99
+ function sCurve(x, steepness = 8) {
100
+ const t = Math.min(Math.max(x, 0), 1);
101
+ const logistic = (u) => 1 / (1 + Math.exp(-steepness * (u - 0.5)));
102
+ const lo = logistic(0);
103
+ return (logistic(t) - lo) / (logistic(1) - lo);
104
+ }
99
105
 
100
106
  // src/player/schedule.ts
101
- var isSilent = (n) => n.svara === void 0 && n.rest !== true && n.sustain !== true;
107
+ var UNDECODED_CELL = /[srgmpqnvtduka]/i;
108
+ var isSilent = (n) => n.svara === void 0 && n.rest !== true && n.sustain !== true && !(n.raw !== void 0 && UNDECODED_CELL.test(n.raw));
109
+ var ARC_RAW_RE = /^[wWxXyYjJeEzZ$=_<>`\\]+$/;
110
+ var isArcRaw = (raw) => raw !== void 0 && ARC_RAW_RE.test(raw);
111
+ function meendContinuations(notes) {
112
+ const out = /* @__PURE__ */ new Set();
113
+ let pending;
114
+ notes.forEach((note, i) => {
115
+ let marks = note.meend === "start" ? "w" : note.meend === "end" ? "x" : "";
116
+ for (const b of note.below ?? []) if (isArcRaw(b.raw)) marks += b.raw;
117
+ let marked = false;
118
+ for (const ch of marks) {
119
+ if (ch === "x" || ch === "X") {
120
+ if (pending !== void 0) for (const k of [...pending, i]) out.add(k);
121
+ pending = void 0;
122
+ marked = true;
123
+ } else if (ch === "w" || ch === "W") {
124
+ pending = [];
125
+ marked = true;
126
+ }
127
+ }
128
+ if (!marked && pending !== void 0) pending.push(i);
129
+ });
130
+ return out;
131
+ }
132
+ function glideTargets(events, i) {
133
+ const out = [];
134
+ for (let j = i + 1; j < events.length; j++) {
135
+ const ev = events[j];
136
+ if (ev.kind === "rest") break;
137
+ if (ev.midi === null) continue;
138
+ if (ev.glide !== true) break;
139
+ out.push(ev);
140
+ }
141
+ return out;
142
+ }
102
143
  var SAM_TICK = /^\d+[fF]$/;
103
144
  function buildSchedule(doc, opts = {}) {
104
145
  const tonicPc = parseTonic(opts.tonic ?? doc.meta.tonic);
@@ -109,6 +150,7 @@ function buildSchedule(doc, opts = {}) {
109
150
  let samBeat;
110
151
  doc.parts.forEach((part, p) => {
111
152
  part.rows.forEach((row, r) => {
153
+ const glides = meendContinuations(row.notes);
112
154
  row.notes.forEach((note, i) => {
113
155
  if (samBeat === void 0 && note.above !== void 0) {
114
156
  for (const a of note.above) {
@@ -128,7 +170,8 @@ function buildSchedule(doc, opts = {}) {
128
170
  midi: svaraToMidi(note, tonicPc, baseOctave),
129
171
  kan: kanToMidi(note.kan, tonicPc, baseOctave),
130
172
  path,
131
- kind: note.sustain === true ? "sustain" : "note"
173
+ kind: note.sustain === true ? "sustain" : "note",
174
+ ...glides.has(i) && lastPitched >= 0 ? { glide: true } : {}
132
175
  });
133
176
  lastPitched = events.length - 1;
134
177
  } else if (note.sustain === true) {
@@ -147,7 +190,7 @@ function buildSchedule(doc, opts = {}) {
147
190
  }
148
191
 
149
192
  // src/player/instruments.ts
150
- var DEFAULT_SOUNDS_BASE = "https://cdn.daserve.in/swaralipijs-instruments/";
193
+ var DEFAULT_SOUNDS_BASE = "https://bitoron.pages.dev/audio/instruments/";
151
194
  var SALAMANDER_BASE = "https://tonejs.github.io/audio/salamander/";
152
195
  var TONEJS_INSTRUMENTS_BASE = "https://nbrosowsky.github.io/tonejs-instruments/samples/";
153
196
  function keyMap(keys) {
@@ -385,6 +428,55 @@ function createSampler(Tone, def, soundsBaseUrl, range) {
385
428
  const urls = range ? subsetUrls(def.urls, range.lo, range.hi) : def.urls;
386
429
  return new Tone.Sampler({ urls, baseUrl: normalizeBase(base), release: def.release ?? 1 });
387
430
  }
431
+ var MEEND_HOLD = 0.5;
432
+ var GLIDE_STEPS = 24;
433
+ function nearestSampleKey(urls, midi) {
434
+ let best = "";
435
+ let bestDiff = Infinity;
436
+ for (const key of Object.keys(urls)) {
437
+ const diff = Math.abs(noteToMidi(key) - midi);
438
+ if (diff < bestDiff) {
439
+ bestDiff = diff;
440
+ best = key;
441
+ }
442
+ }
443
+ return best;
444
+ }
445
+ function createBuffers(Tone, def, soundsBaseUrl, range) {
446
+ const base = def.local ? `${normalizeBase(soundsBaseUrl ?? "")}${def.baseUrl}` : def.baseUrl;
447
+ const urls = range ? subsetUrls(def.urls, range.lo, range.hi) : def.urls;
448
+ return { buffers: new Tone.ToneAudioBuffers({ urls, baseUrl: normalizeBase(base) }), urls };
449
+ }
450
+ function triggerGlide(Tone, buffers, urls, dest, legs, endSec, release = 1, hold = MEEND_HOLD) {
451
+ const key = nearestSampleKey(urls, legs[0].midi);
452
+ if (key === "" || !buffers.has(key)) return;
453
+ const sampleMidi = noteToMidi(key);
454
+ const rate = (midi) => Math.pow(2, (midi - sampleMidi) / 12);
455
+ const source = new Tone.ToneBufferSource({
456
+ url: buffers.get(key),
457
+ fadeOut: release,
458
+ curve: "exponential",
459
+ // Bind to the destination's context explicitly. `Tone.Offline` restores the
460
+ // global context the moment it starts rendering, so a node built inside a
461
+ // transport callback would otherwise land on the *online* context and the
462
+ // offline export would render the glide as silence.
463
+ context: dest.context
464
+ }).connect(dest);
465
+ if (!dest.context.isOffline) source.onended = () => source.dispose();
466
+ const rates = source.playbackRate;
467
+ rates.setValueAtTime(rate(legs[0].midi), legs[0].at);
468
+ for (let k = 1; k < legs.length; k++) {
469
+ const from = legs[k - 1];
470
+ const to = legs[k];
471
+ const start = from.at + (to.at - from.at) * hold;
472
+ const values = [];
473
+ for (let n = 0; n <= GLIDE_STEPS; n++) {
474
+ values.push(rate(from.midi + (to.midi - from.midi) * sCurve(n / GLIDE_STEPS)));
475
+ }
476
+ rates.setValueCurveAtTime(values, start, Math.max(to.at - start, 0.02));
477
+ }
478
+ source.start(legs[0].at, 0, Math.max(endSec - legs[0].at, 0.05));
479
+ }
388
480
  function createTanpura(Tone, base, stems = TANPURA_PCS) {
389
481
  const urls = {};
390
482
  for (const s of stems) urls[s] = `${s}.mp3`;
@@ -406,6 +498,13 @@ function midiRange(schedule) {
406
498
  }
407
499
  return lo <= hi ? { lo, hi } : void 0;
408
500
  }
501
+ async function loadedOrWarn(Tone) {
502
+ try {
503
+ await Tone.loaded();
504
+ } catch (err) {
505
+ console.warn("[swaralipi] some audio samples failed to load; continuing without them", err);
506
+ }
507
+ }
409
508
  var SwaralipiPlayer = class {
410
509
  #opts;
411
510
  /** Per-song setBpm tweak: beats meta.bpm but is cleared by the next load(). */
@@ -421,6 +520,8 @@ var SwaralipiPlayer = class {
421
520
  /** Cached melody samplers keyed by instrument id, with the MIDI range each
422
521
  * was loaded for (rebuilt when a song needs a wider range). */
423
522
  #samplers = /* @__PURE__ */ new Map();
523
+ /** Raw sample buffers for the মীড় glide voice, keyed like #samplers. */
524
+ #glide = /* @__PURE__ */ new Map();
424
525
  #tanpura;
425
526
  #tanpuraKey = "";
426
527
  #metronome;
@@ -672,6 +773,8 @@ var SwaralipiPlayer = class {
672
773
  this.#halt();
673
774
  for (const { sampler } of this.#samplers.values()) sampler.dispose();
674
775
  this.#samplers.clear();
776
+ for (const { buffers } of this.#glide.values()) buffers.dispose();
777
+ this.#glide.clear();
675
778
  this.#tanpura?.dispose();
676
779
  this.#metronome?.dispose();
677
780
  this.#tabla?.dispose();
@@ -774,30 +877,54 @@ var SwaralipiPlayer = class {
774
877
  baseOctave: this.#opts.baseOctave
775
878
  });
776
879
  const range = midiRange(schedule);
777
- const melody = this.#buildMelody(Tone, this.#effectiveInstrument(), base, range);
880
+ const id = this.#effectiveInstrument();
881
+ const def = this.#resolveDef(id) ?? INSTRUMENTS.harmonium;
882
+ const melody = this.#buildMelody(Tone, id, base, range);
883
+ const glide = schedule.events.some((ev) => ev.glide === true) ? this.#buildGlide(Tone, id, base, range) : void 0;
778
884
  this.#buildRhythm(Tone, transport, base, spb);
779
885
  this.#buildTanpura(Tone, transport, base, spb);
780
- await Tone.loaded();
886
+ await loadedOrWarn(Tone);
781
887
  if (this.#tabla?.buffer?.loaded) {
782
888
  this.#tabla.loopEnd = quantizeLoopEnd(this.#tabla.buffer.duration, spb);
783
889
  }
784
890
  const draw = Tone.getDraw();
785
891
  const shift = this.#opts.rhythm !== "none" ? schedule.pickupBeats * spb : 0;
786
- for (const ev of schedule.events) {
892
+ schedule.events.forEach((ev, i) => {
787
893
  const at = ev.beat * spb + shift;
788
- if (ev.midi !== null) {
894
+ if (ev.midi !== null && ev.glide !== true) {
789
895
  const name = midiToSampleName(ev.midi);
790
896
  const dur = Math.max(ev.durationBeats * spb, 0.05);
791
897
  if (ev.kan !== null) {
792
898
  const kanName = midiToSampleName(ev.kan);
793
899
  transport.scheduleOnce((time) => melody.triggerAttackRelease(kanName, spb * 0.4, time), Math.max(at - spb * 0.12, 0));
794
900
  }
795
- transport.scheduleOnce((time) => melody.triggerAttackRelease(name, dur, time), at);
901
+ const targets = glide !== void 0 ? glideTargets(schedule.events, i) : [];
902
+ if (targets.length > 0 && glide !== void 0) {
903
+ const last = targets[targets.length - 1];
904
+ const legs = [
905
+ { midi: ev.midi, at: 0 },
906
+ ...targets.map((t) => ({ midi: t.midi, at: (t.beat - ev.beat) * spb }))
907
+ ];
908
+ const end = (last.beat + last.durationBeats - ev.beat) * spb;
909
+ transport.scheduleOnce((time) => {
910
+ triggerGlide(
911
+ Tone,
912
+ glide.buffers,
913
+ glide.urls,
914
+ this.#ensureMaster(Tone),
915
+ legs.map((l) => ({ midi: l.midi, at: time + l.at })),
916
+ time + end,
917
+ def.release ?? 1
918
+ );
919
+ }, at);
920
+ } else {
921
+ transport.scheduleOnce((time) => melody.triggerAttackRelease(name, dur, time), at);
922
+ }
796
923
  }
797
924
  transport.scheduleOnce((time) => {
798
925
  draw.schedule(() => this.#emitHighlight(ev.path), time);
799
926
  }, at);
800
- }
927
+ });
801
928
  const endAt = schedule.totalBeats * spb + shift + spb;
802
929
  transport.scheduleOnce((time) => {
803
930
  draw.schedule(() => this.#emitHighlight(null), time);
@@ -821,7 +948,7 @@ var SwaralipiPlayer = class {
821
948
  transport.bpm.value = bpm;
822
949
  const spb = 60 / bpm;
823
950
  this.#buildRhythm(Tone, transport, base, spb);
824
- await Tone.loaded();
951
+ await loadedOrWarn(Tone);
825
952
  if (this.#tabla?.buffer?.loaded) {
826
953
  this.#tabla.loopEnd = quantizeLoopEnd(this.#tabla.buffer.duration, spb);
827
954
  }
@@ -853,6 +980,19 @@ var SwaralipiPlayer = class {
853
980
  this.#samplers.set(id, { sampler, lo: range?.lo ?? -Infinity, hi: range?.hi ?? Infinity });
854
981
  return sampler;
855
982
  }
983
+ /** Build (or reuse) the মীড় glide buffers for `id` — the same samples the
984
+ * Sampler uses, kept as raw buffers so a note can be started once and its
985
+ * playbackRate automated. Cached and range-widened exactly like #buildMelody. */
986
+ #buildGlide(Tone, id, base, range) {
987
+ const cached = this.#glide.get(id);
988
+ if (cached && (range === void 0 || cached.lo <= range.lo && cached.hi >= range.hi)) return cached;
989
+ cached?.buffers.dispose();
990
+ const def = this.#resolveDef(id) ?? INSTRUMENTS.harmonium;
991
+ const built = createBuffers(Tone, def, base, range);
992
+ const entry = { ...built, lo: range?.lo ?? -Infinity, hi: range?.hi ?? Infinity };
993
+ this.#glide.set(id, entry);
994
+ return entry;
995
+ }
856
996
  #buildRhythm(Tone, transport, base, spb) {
857
997
  this.#tabla?.dispose();
858
998
  this.#tabla = void 0;
@@ -981,17 +1121,39 @@ async function exportAudio(doc, options = {}) {
981
1121
  const master = new Tone.Gain(1).toDestination();
982
1122
  if (withMelody) {
983
1123
  const sampler = createSampler(Tone, def, base, range).connect(master);
984
- for (const ev of schedule.events) {
985
- if (ev.midi === null) continue;
1124
+ const glide = schedule.events.some((ev) => ev.glide === true) ? createBuffers(Tone, def, base, range) : void 0;
1125
+ schedule.events.forEach((ev, i) => {
1126
+ if (ev.midi === null || ev.glide === true) return;
986
1127
  const at = ev.beat * spb + shift;
987
1128
  const dur = Math.max(ev.durationBeats * spb, 0.05);
988
1129
  if (ev.kan !== null) {
989
- const name2 = midiToSampleName(ev.kan);
990
- transport.scheduleOnce((t) => sampler.triggerAttackRelease(name2, spb * 0.4, t), Math.max(at - spb * 0.12, 0));
1130
+ const name = midiToSampleName(ev.kan);
1131
+ transport.scheduleOnce((t) => sampler.triggerAttackRelease(name, spb * 0.4, t), Math.max(at - spb * 0.12, 0));
991
1132
  }
992
- const name = midiToSampleName(ev.midi);
993
- transport.scheduleOnce((t) => sampler.triggerAttackRelease(name, dur, t), at);
994
- }
1133
+ const targets = glide !== void 0 ? glideTargets(schedule.events, i) : [];
1134
+ if (targets.length > 0 && glide !== void 0) {
1135
+ const last = targets[targets.length - 1];
1136
+ const legs = [
1137
+ { midi: ev.midi, at: 0 },
1138
+ ...targets.map((t) => ({ midi: t.midi, at: (t.beat - ev.beat) * spb }))
1139
+ ];
1140
+ const end = (last.beat + last.durationBeats - ev.beat) * spb;
1141
+ transport.scheduleOnce((t) => {
1142
+ triggerGlide(
1143
+ Tone,
1144
+ glide.buffers,
1145
+ glide.urls,
1146
+ master,
1147
+ legs.map((l) => ({ midi: l.midi, at: t + l.at })),
1148
+ t + end,
1149
+ def.release ?? 1
1150
+ );
1151
+ }, at);
1152
+ } else {
1153
+ const name = midiToSampleName(ev.midi);
1154
+ transport.scheduleOnce((t) => sampler.triggerAttackRelease(name, dur, t), at);
1155
+ }
1156
+ });
995
1157
  }
996
1158
  let tabla;
997
1159
  if (rhythm === "drums" && base) {
@@ -1163,6 +1325,17 @@ function utf8(s) {
1163
1325
  function u32be(n) {
1164
1326
  return [n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255];
1165
1327
  }
1328
+ var BEND_RANGE = 12;
1329
+ var BEND_STEPS = 16;
1330
+ function bendEvent(tick, semitones) {
1331
+ const raw = Math.round(8192 + semitones / BEND_RANGE * 8192);
1332
+ const value = Math.min(Math.max(raw, 0), 16383);
1333
+ return { tick, on: false, status: 224, note: value & 127, vel: value >> 7 & 127, ch: 0 };
1334
+ }
1335
+ function bendRangeSetup() {
1336
+ const cc = (num, val) => ({ tick: 0, on: false, status: 176, note: num, vel: val, ch: 0 });
1337
+ return [cc(101, 0), cc(100, 0), cc(6, BEND_RANGE), cc(38, 0), cc(101, 127), cc(100, 127)];
1338
+ }
1166
1339
  var DRUM_NOTE = {
1167
1340
  kick: 36,
1168
1341
  snare: 38,
@@ -1173,11 +1346,12 @@ var DRUM_NOTE = {
1173
1346
  tom3: 41
1174
1347
  };
1175
1348
  function trackChunk(raw, head) {
1176
- raw.sort((a, b) => a.tick - b.tick || Number(a.on) - Number(b.on));
1349
+ const rank = (e) => e.status !== void 0 ? 1 : e.on ? 2 : 0;
1350
+ raw.sort((a, b) => a.tick - b.tick || rank(a) - rank(b));
1177
1351
  const track = [...head];
1178
1352
  let last = 0;
1179
1353
  for (const e of raw) {
1180
- const status = (e.on ? 144 : 128) | e.ch & 15;
1354
+ const status = e.status !== void 0 ? e.status | e.ch & 15 : (e.on ? 144 : 128) | e.ch & 15;
1181
1355
  track.push(...vlq(e.tick - last), status, e.note & 127, e.vel & 127);
1182
1356
  last = e.tick;
1183
1357
  }
@@ -1186,10 +1360,14 @@ function trackChunk(raw, head) {
1186
1360
  }
1187
1361
  function encodeMidi(schedule, opts) {
1188
1362
  const melody = [];
1189
- for (const ev of schedule.events) {
1190
- if (ev.midi === null) continue;
1363
+ const hasGlide = schedule.events.some((ev) => ev.glide === true);
1364
+ if (hasGlide) melody.push(...bendRangeSetup());
1365
+ schedule.events.forEach((ev, i) => {
1366
+ if (ev.midi === null || ev.glide === true) return;
1191
1367
  const start = Math.round(ev.beat * PPQ);
1192
- const end = Math.max(Math.round((ev.beat + ev.durationBeats) * PPQ), start + 1);
1368
+ const targets = glideTargets(schedule.events, i);
1369
+ const last = targets[targets.length - 1] ?? ev;
1370
+ const end = Math.max(Math.round((last.beat + last.durationBeats) * PPQ), start + 1);
1193
1371
  if (ev.kan !== null) {
1194
1372
  const kOn = Math.max(start - Math.round(0.12 * PPQ), 0);
1195
1373
  melody.push({ tick: kOn, on: true, note: ev.kan, vel: 70, ch: 0 });
@@ -1197,7 +1375,19 @@ function encodeMidi(schedule, opts) {
1197
1375
  }
1198
1376
  melody.push({ tick: start, on: true, note: ev.midi, vel: 100, ch: 0 });
1199
1377
  melody.push({ tick: end, on: false, note: ev.midi, vel: 0, ch: 0 });
1200
- }
1378
+ let from = ev;
1379
+ for (const to of targets) {
1380
+ const glideStart = from.beat + (to.beat - from.beat) * MEEND_HOLD;
1381
+ for (let n = 0; n <= BEND_STEPS; n++) {
1382
+ const u = n / BEND_STEPS;
1383
+ const beat = glideStart + (to.beat - glideStart) * u;
1384
+ const semis = from.midi - ev.midi + (to.midi - from.midi) * sCurve(u);
1385
+ melody.push(bendEvent(Math.round(beat * PPQ), semis));
1386
+ }
1387
+ from = to;
1388
+ }
1389
+ if (targets.length > 0) melody.push(bendEvent(end, 0));
1390
+ });
1201
1391
  const usPerBeat = Math.round(6e7 / opts.bpm);
1202
1392
  const head = [];
1203
1393
  if (opts.name) head.push(...vlq(0), 255, 3, ...vlq(utf8(opts.name).length), ...utf8(opts.name));
@@ -1247,9 +1437,11 @@ function exportMidi(doc, options = {}) {
1247
1437
  };
1248
1438
  }
1249
1439
 
1440
+ exports.ARC_RAW_RE = ARC_RAW_RE;
1250
1441
  exports.DEFAULT_SOUNDS_BASE = DEFAULT_SOUNDS_BASE;
1251
1442
  exports.DRUM_KIT_BASE = DRUM_KIT_BASE;
1252
1443
  exports.INSTRUMENTS = INSTRUMENTS;
1444
+ exports.MEEND_HOLD = MEEND_HOLD;
1253
1445
  exports.OCTAVE_OFFSET = OCTAVE_OFFSET;
1254
1446
  exports.SALAMANDER_BASE = SALAMANDER_BASE;
1255
1447
  exports.SAMPLED_TAALS = SAMPLED_TAALS;
@@ -1259,19 +1451,25 @@ exports.TABLA_BPMS = TABLA_BPMS;
1259
1451
  exports.TABLA_VARIATIONS = TABLA_VARIATIONS;
1260
1452
  exports.TONEJS_INSTRUMENTS_BASE = TONEJS_INSTRUMENTS_BASE;
1261
1453
  exports.buildSchedule = buildSchedule;
1454
+ exports.createBuffers = createBuffers;
1262
1455
  exports.drumPattern = drumPattern;
1263
1456
  exports.encodeMidi = encodeMidi;
1264
1457
  exports.encodeMp3 = encodeMp3;
1265
1458
  exports.encodeWav = encodeWav;
1266
1459
  exports.exportAudio = exportAudio;
1267
1460
  exports.exportMidi = exportMidi;
1461
+ exports.glideTargets = glideTargets;
1268
1462
  exports.instrumentSampleUrl = instrumentSampleUrl;
1463
+ exports.isArcRaw = isArcRaw;
1464
+ exports.meendContinuations = meendContinuations;
1269
1465
  exports.midiToSampleName = midiToSampleName;
1466
+ exports.nearestSampleKey = nearestSampleKey;
1270
1467
  exports.nearestTablaBpm = nearestTablaBpm;
1271
1468
  exports.normalizeBase = normalizeBase;
1272
1469
  exports.parseTonic = parseTonic;
1273
1470
  exports.probeUrl = probeUrl;
1274
1471
  exports.quantizeLoopEnd = quantizeLoopEnd;
1472
+ exports.sCurve = sCurve;
1275
1473
  exports.sampleFileName = sampleFileName;
1276
1474
  exports.santoorUrls = santoorUrls;
1277
1475
  exports.subsetUrls = subsetUrls;
@@ -1279,5 +1477,6 @@ exports.svaraSemitone = svaraSemitone;
1279
1477
  exports.svaraToMidi = svaraToMidi;
1280
1478
  exports.tablaBpms = tablaBpms;
1281
1479
  exports.tablaUrl = tablaUrl;
1480
+ exports.triggerGlide = triggerGlide;
1282
1481
  //# sourceMappingURL=index.cjs.map
1283
1482
  //# sourceMappingURL=index.cjs.map