@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.
- package/README.md +8 -8
- package/dist/{batch-V4X3GN4N.js → batch-RUZGM2NR.js} +4 -4
- package/dist/{batch-V4X3GN4N.js.map → batch-RUZGM2NR.js.map} +1 -1
- package/dist/{chunk-6QW7MSJO.js → chunk-GHWURC4U.js} +98 -6
- package/dist/chunk-GHWURC4U.js.map +1 -0
- package/dist/{chunk-RTKWFJFI.js → chunk-ULK7NVKH.js} +8 -6
- package/dist/chunk-ULK7NVKH.js.map +1 -0
- package/dist/{chunk-ZS43BDP7.js → chunk-ZNVTOY7J.js} +7 -6
- package/dist/chunk-ZNVTOY7J.js.map +1 -0
- package/dist/cli.cjs +11 -8
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/convert/index.cjs +10 -7
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.d.cts +4 -3
- package/dist/convert/index.d.ts +4 -3
- package/dist/convert/index.js +2 -2
- package/dist/index.cjs +6 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/player/index.cjs +222 -23
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +16 -4
- package/dist/player/index.d.ts +16 -4
- package/dist/player/index.js +120 -22
- package/dist/player/index.js.map +1 -1
- package/dist/{schedule-CAns78TD.d.ts → schedule-8WFUBJqB.d.ts} +82 -7
- package/dist/{schedule-D3JyEhuO.d.cts → schedule-CjPcD-Hf.d.cts} +82 -7
- package/dist/{schema-D3wyNRqM.d.ts → schema-D_86x4oV.d.cts} +21 -6
- package/dist/{schema-D3wyNRqM.d.cts → schema-D_86x4oV.d.ts} +21 -6
- package/dist/spec/index.cjs +6 -4
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +3 -3
- package/dist/spec/index.d.ts +3 -3
- package/dist/spec/index.js +1 -1
- package/dist/viewer/index.cjs +253 -172
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +16 -23
- package/dist/viewer/index.d.ts +16 -23
- package/dist/viewer/index.js +88 -47
- package/dist/viewer/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6QW7MSJO.js.map +0 -1
- package/dist/chunk-RTKWFJFI.js.map +0 -1
- package/dist/chunk-ZS43BDP7.js.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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
|
package/dist/player/index.cjs
CHANGED
|
@@ -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
|
|
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://
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
985
|
-
|
|
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
|
|
990
|
-
transport.scheduleOnce((t) => sampler.triggerAttackRelease(
|
|
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
|
|
993
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1190
|
-
|
|
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
|
|
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
|