@ugfoundation/swaralipi-js 0.3.1 → 0.3.3

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 (45) hide show
  1. package/dist/{batch-TVEIT2OJ.js → batch-6YAUMVHC.js} +5 -5
  2. package/dist/{batch-TVEIT2OJ.js.map → batch-6YAUMVHC.js.map} +1 -1
  3. package/dist/{chunk-V5ZSUJCA.js → chunk-32LOBRC2.js} +3 -3
  4. package/dist/{chunk-V5ZSUJCA.js.map → chunk-32LOBRC2.js.map} +1 -1
  5. package/dist/{chunk-HDN3YZ6K.js → chunk-RTCRSOVW.js} +3 -3
  6. package/dist/{chunk-HDN3YZ6K.js.map → chunk-RTCRSOVW.js.map} +1 -1
  7. package/dist/{chunk-WBSIKYWB.js → chunk-UBJZUKQD.js} +6 -6
  8. package/dist/chunk-UBJZUKQD.js.map +1 -0
  9. package/dist/{chunk-H6HC44PZ.js → chunk-YE7UVYRY.js} +5 -3
  10. package/dist/chunk-YE7UVYRY.js.map +1 -0
  11. package/dist/cli.cjs +6 -4
  12. package/dist/cli.cjs.map +1 -1
  13. package/dist/cli.js +3 -3
  14. package/dist/convert/index.cjs +6 -4
  15. package/dist/convert/index.cjs.map +1 -1
  16. package/dist/convert/index.js +3 -3
  17. package/dist/index.cjs +4 -2
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +2 -2
  22. package/dist/player/index.cjs +70 -11
  23. package/dist/player/index.cjs.map +1 -1
  24. package/dist/player/index.d.cts +17 -4
  25. package/dist/player/index.d.ts +17 -4
  26. package/dist/player/index.js +70 -13
  27. package/dist/player/index.js.map +1 -1
  28. package/dist/{schedule-BhiP_maG.d.ts → schedule-BaE3JGHe.d.ts} +1 -1
  29. package/dist/{schedule-Dsb2paUE.d.cts → schedule-CHXz3V6T.d.cts} +1 -1
  30. package/dist/spec/index.cjs +4 -2
  31. package/dist/spec/index.cjs.map +1 -1
  32. package/dist/spec/index.d.cts +2 -2
  33. package/dist/spec/index.d.ts +2 -2
  34. package/dist/spec/index.js +2 -2
  35. package/dist/{taals-FmEulsjY.d.cts → taals-DxJ8wRtF.d.cts} +8 -1
  36. package/dist/{taals-FmEulsjY.d.ts → taals-DxJ8wRtF.d.ts} +8 -1
  37. package/dist/viewer/index.cjs +28 -2
  38. package/dist/viewer/index.cjs.map +1 -1
  39. package/dist/viewer/index.d.cts +36 -4
  40. package/dist/viewer/index.d.ts +36 -4
  41. package/dist/viewer/index.js +25 -3
  42. package/dist/viewer/index.js.map +1 -1
  43. package/package.json +1 -1
  44. package/dist/chunk-H6HC44PZ.js.map +0 -1
  45. package/dist/chunk-WBSIKYWB.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { S as Swaralipi, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.cjs';
2
- import { T as TaalId } from '../taals-FmEulsjY.cjs';
3
- import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-Dsb2paUE.cjs';
4
- export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-Dsb2paUE.cjs';
1
+ import { S as Swaralipi, T as Taal, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.cjs';
2
+ import { T as TaalId } from '../taals-DxJ8wRtF.cjs';
3
+ import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-CHXz3V6T.cjs';
4
+ export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-CHXz3V6T.cjs';
5
5
  import 'zod';
6
6
  import 'tone';
7
7
 
@@ -48,6 +48,10 @@ interface PlayerOptions {
48
48
  onLimit?: () => void;
49
49
  /** Called with the active note's `part.row.note` path, or null when idle. */
50
50
  onHighlight?: (path: string | null) => void;
51
+ /** Taal mode only (`loadTaal`): called on the draw thread once per matra with
52
+ * the beat index within the cycle (0-based, `< numBeats`), so a consumer can
53
+ * highlight the active theka bol / drum-staff note. Never fired in doc mode. */
54
+ onBeat?: (beatInCycle: number) => void;
51
55
  /** Called on every state transition. */
52
56
  onState?: (state: PlayerState) => void;
53
57
  }
@@ -82,6 +86,15 @@ declare class SwaralipiPlayer {
82
86
  get usesTabla(): boolean;
83
87
  /** Set the document to play. Stops any current playback. */
84
88
  load(doc: Swaralipi): void;
89
+ /**
90
+ * Taal mode: loop one avartan of a taal's theka (rhythm only, no melody) —
91
+ * for a standalone theka player. `play()` loops indefinitely (tabla loop /
92
+ * drum sequence) and fires `onBeat(beatInCycle)` once per matra on the draw
93
+ * thread so the consumer can highlight the active bol / drum-staff note. The
94
+ * taal's `id` should be a sampled taal (see `SAMPLED_TAALS`) for tabla audio;
95
+ * `variation` picks a tabla loop variation. Stops any current playback.
96
+ */
97
+ loadTaal(taal: Taal): void;
85
98
  /** Start (or resume) playback. Must be triggered from a user gesture so the
86
99
  * browser permits `AudioContext` resume. */
87
100
  play(): Promise<void>;
@@ -1,7 +1,7 @@
1
- import { S as Swaralipi, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.js';
2
- import { T as TaalId } from '../taals-FmEulsjY.js';
3
- import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-BhiP_maG.js';
4
- export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-BhiP_maG.js';
1
+ import { S as Swaralipi, T as Taal, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.js';
2
+ import { T as TaalId } from '../taals-DxJ8wRtF.js';
3
+ import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-BaE3JGHe.js';
4
+ export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-BaE3JGHe.js';
5
5
  import 'zod';
6
6
  import 'tone';
7
7
 
@@ -48,6 +48,10 @@ interface PlayerOptions {
48
48
  onLimit?: () => void;
49
49
  /** Called with the active note's `part.row.note` path, or null when idle. */
50
50
  onHighlight?: (path: string | null) => void;
51
+ /** Taal mode only (`loadTaal`): called on the draw thread once per matra with
52
+ * the beat index within the cycle (0-based, `< numBeats`), so a consumer can
53
+ * highlight the active theka bol / drum-staff note. Never fired in doc mode. */
54
+ onBeat?: (beatInCycle: number) => void;
51
55
  /** Called on every state transition. */
52
56
  onState?: (state: PlayerState) => void;
53
57
  }
@@ -82,6 +86,15 @@ declare class SwaralipiPlayer {
82
86
  get usesTabla(): boolean;
83
87
  /** Set the document to play. Stops any current playback. */
84
88
  load(doc: Swaralipi): void;
89
+ /**
90
+ * Taal mode: loop one avartan of a taal's theka (rhythm only, no melody) —
91
+ * for a standalone theka player. `play()` loops indefinitely (tabla loop /
92
+ * drum sequence) and fires `onBeat(beatInCycle)` once per matra on the draw
93
+ * thread so the consumer can highlight the active bol / drum-staff note. The
94
+ * taal's `id` should be a sampled taal (see `SAMPLED_TAALS`) for tabla audio;
95
+ * `variation` picks a tabla loop variation. Stops any current playback.
96
+ */
97
+ loadTaal(taal: Taal): void;
85
98
  /** Start (or resume) playback. Must be triggered from a user gesture so the
86
99
  * browser permits `AudioContext` resume. */
87
100
  play(): Promise<void>;
@@ -1,6 +1,6 @@
1
- import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, instrumentSampleUrl, probeUrl, normalizeBase, nearestTablaBpm, tablaBpms, buildSchedule, quantizeLoopEnd, midiToSampleName, createSampler, tablaUrl, createDrumKit, drumPattern, createMetronome, TANPURA_PCS, createTanpura, parseTonic } from '../chunk-V5ZSUJCA.js';
2
- export { DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, INSTRUMENTS, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_VARIATIONS, TONEJS_INSTRUMENTS_BASE, buildSchedule, drumPattern, instrumentSampleUrl, midiToSampleName, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sampleFileName, santoorUrls, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaUrl } from '../chunk-V5ZSUJCA.js';
3
- import { TAALS } from '../chunk-H6HC44PZ.js';
1
+ import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, instrumentSampleUrl, probeUrl, normalizeBase, nearestTablaBpm, tablaBpms, buildSchedule, quantizeLoopEnd, midiToSampleName, createSampler, tablaUrl, createDrumKit, drumPattern, createMetronome, TANPURA_PCS, createTanpura, parseTonic } from '../chunk-32LOBRC2.js';
2
+ export { DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, INSTRUMENTS, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_VARIATIONS, TONEJS_INSTRUMENTS_BASE, buildSchedule, drumPattern, instrumentSampleUrl, midiToSampleName, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sampleFileName, santoorUrls, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaUrl } from '../chunk-32LOBRC2.js';
3
+ import { TAALS } from '../chunk-YE7UVYRY.js';
4
4
 
5
5
  // src/player/player.ts
6
6
  var STOP_FADE_SEC = 0.5;
@@ -22,6 +22,9 @@ var SwaralipiPlayer = class {
22
22
  /** Per-song setBpm tweak: beats meta.bpm but is cleared by the next load(). */
23
23
  #bpmTweak;
24
24
  #doc;
25
+ /** Taal mode (`loadTaal`): loops one avartan of this taal's theka with no
26
+ * melody. Mutually exclusive with `#doc`. */
27
+ #taal;
25
28
  #state = "idle";
26
29
  #tone;
27
30
  /** Master bus everything routes through, so stop() can fade it out. */
@@ -50,6 +53,7 @@ var SwaralipiPlayer = class {
50
53
  tanpura: options.tanpura ?? true,
51
54
  previewLimitSec: options.previewLimitSec ?? Infinity,
52
55
  onHighlight: options.onHighlight,
56
+ onBeat: options.onBeat,
53
57
  onState: options.onState,
54
58
  onLimit: options.onLimit
55
59
  };
@@ -89,6 +93,23 @@ var SwaralipiPlayer = class {
89
93
  load(doc) {
90
94
  this.#halt();
91
95
  this.#doc = doc;
96
+ this.#taal = void 0;
97
+ this.#taalOverride = void 0;
98
+ this.#bpmTweak = void 0;
99
+ this.#setState("ready");
100
+ }
101
+ /**
102
+ * Taal mode: loop one avartan of a taal's theka (rhythm only, no melody) —
103
+ * for a standalone theka player. `play()` loops indefinitely (tabla loop /
104
+ * drum sequence) and fires `onBeat(beatInCycle)` once per matra on the draw
105
+ * thread so the consumer can highlight the active bol / drum-staff note. The
106
+ * taal's `id` should be a sampled taal (see `SAMPLED_TAALS`) for tabla audio;
107
+ * `variation` picks a tabla loop variation. Stops any current playback.
108
+ */
109
+ loadTaal(taal) {
110
+ this.#halt();
111
+ this.#taal = taal;
112
+ this.#doc = void 0;
92
113
  this.#taalOverride = void 0;
93
114
  this.#bpmTweak = void 0;
94
115
  this.#setState("ready");
@@ -96,7 +117,7 @@ var SwaralipiPlayer = class {
96
117
  /** Start (or resume) playback. Must be triggered from a user gesture so the
97
118
  * browser permits `AudioContext` resume. */
98
119
  async play() {
99
- if (this.#doc === void 0 || this.#state === "playing") return;
120
+ if (this.#doc === void 0 && this.#taal === void 0 || this.#state === "playing") return;
100
121
  const Tone = await this.#ensureTone();
101
122
  await Tone.start();
102
123
  if (this.#state === "paused") {
@@ -144,7 +165,7 @@ var SwaralipiPlayer = class {
144
165
  this.#master.gain.cancelScheduledValues(now);
145
166
  this.#master.gain.rampTo(0, STOP_FADE_SEC, now);
146
167
  this.#emitHighlight(null);
147
- this.#setState(this.#doc ? "ready" : "idle");
168
+ this.#setState(this.#doc || this.#taal ? "ready" : "idle");
148
169
  this.#cancelFadeTimer();
149
170
  this.#fadeTimer = setTimeout(() => this.#halt(), STOP_FADE_SEC * 1e3 + 60);
150
171
  return;
@@ -166,7 +187,7 @@ var SwaralipiPlayer = class {
166
187
  this.#restoreMasterGain();
167
188
  this.#scheduleIds = [];
168
189
  this.#emitHighlight(null);
169
- if (this.#state !== "idle") this.#setState(this.#doc ? "ready" : "idle");
190
+ if (this.#state !== "idle") this.#setState(this.#doc || this.#taal ? "ready" : "idle");
170
191
  }
171
192
  #cancelFadeTimer() {
172
193
  if (this.#fadeTimer !== void 0) {
@@ -280,15 +301,16 @@ var SwaralipiPlayer = class {
280
301
  #emitHighlight(path) {
281
302
  this.#opts.onHighlight?.(path);
282
303
  }
283
- /** The tabla variation to play: the doc's own (in auto mode), else none — an
284
- * explicit setTaal override plays the taal's default variation. */
304
+ /** The tabla variation to play: taal-mode taal's, the doc's own (auto mode),
305
+ * else none — an explicit setTaal override plays the taal's default variation. */
285
306
  #variation() {
307
+ if (this.#taal !== void 0) return this.#taal.variation;
286
308
  return this.#taalAuto ? this.#doc?.meta.taal?.variation : void 0;
287
309
  }
288
- /** Resolve the active taal: an explicit override, else the doc's. */
310
+ /** Resolve the active taal id: taal mode's taal, an explicit override, else
311
+ * the doc's — but only when it names a sampled taal. */
289
312
  #taalId() {
290
- if (!this.#taalAuto && this.#taalOverride !== void 0) return this.#taalOverride;
291
- const id = this.#doc?.meta.taal?.id;
313
+ const id = this.#taal?.id ?? (!this.#taalAuto && this.#taalOverride !== void 0 ? this.#taalOverride : this.#doc?.meta.taal?.id);
292
314
  return id !== void 0 && id in TAALS ? id : void 0;
293
315
  }
294
316
  /** Resolve an instrument id to its def (custom overrides built-ins; the
@@ -345,6 +367,7 @@ var SwaralipiPlayer = class {
345
367
  /** Build the melody instrument(s)/rhythm/drone for the current settings and
346
368
  * wait for their buffers, then schedule everything on the Transport. */
347
369
  async #arrange(Tone) {
370
+ if (this.#taal !== void 0) return this.#arrangeTaal(Tone);
348
371
  if (this.#doc === void 0) return;
349
372
  const base = this.#opts.soundsBaseUrl;
350
373
  const transport = Tone.getTransport();
@@ -390,6 +413,40 @@ var SwaralipiPlayer = class {
390
413
  }, schedule.totalBeats * spb);
391
414
  transport.scheduleOnce(() => this.stop(), endAt);
392
415
  }
416
+ /** Taal mode: loop one avartan forever (tabla loop / drum sequence via
417
+ * #buildRhythm), firing onBeat + a `String(beatInCycle)` highlight each matra
418
+ * on the draw thread. No melody, no tanpura, no auto-stop — it plays until
419
+ * stop(). */
420
+ async #arrangeTaal(Tone) {
421
+ if (this.#taal === void 0) return;
422
+ const base = this.#opts.soundsBaseUrl;
423
+ const transport = Tone.getTransport();
424
+ transport.cancel(0);
425
+ transport.position = 0;
426
+ this.#scheduleIds = [];
427
+ this.#ensureMaster(Tone);
428
+ this.#restoreMasterGain();
429
+ const bpm = this.#effectiveBpm();
430
+ transport.bpm.value = bpm;
431
+ const spb = 60 / bpm;
432
+ this.#buildRhythm(Tone, transport, base, spb);
433
+ await Tone.loaded();
434
+ if (this.#tabla?.buffer?.loaded) {
435
+ this.#tabla.loopEnd = quantizeLoopEnd(this.#tabla.buffer.duration, spb);
436
+ }
437
+ const draw = Tone.getDraw();
438
+ const n = Math.max(1, this.#taal.numBeats);
439
+ let beat = 0;
440
+ const id = transport.scheduleRepeat((time) => {
441
+ const b = beat % n;
442
+ draw.schedule(() => {
443
+ this.#emitHighlight(String(b));
444
+ this.#opts.onBeat?.(b);
445
+ }, time);
446
+ beat += 1;
447
+ }, spb, 0);
448
+ this.#scheduleIds.push(id);
449
+ }
393
450
  /** Build (or reuse) the melody sampler for `id`, loading only the samples
394
451
  * covering `range`. A cached sampler is reused while it still covers the
395
452
  * range; otherwise it's rebuilt (so changing songs/tonic widens it lazily). */
@@ -420,7 +477,7 @@ var SwaralipiPlayer = class {
420
477
  if (this.#opts.rhythm === "drums") {
421
478
  this.#drums ??= createDrumKit(Tone).connect(master);
422
479
  const drums = this.#drums;
423
- const taal = taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 };
480
+ const taal = this.#taal ?? (taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 });
424
481
  const pattern = drumPattern(taal);
425
482
  let beat2 = 0;
426
483
  const id2 = transport.scheduleRepeat((time) => {
@@ -434,7 +491,7 @@ var SwaralipiPlayer = class {
434
491
  if (this.#opts.rhythm === "none" || base === void 0) return;
435
492
  this.#metronome ??= createMetronome(Tone, base).connect(master);
436
493
  const metro = this.#metronome;
437
- const cycle = taalId !== void 0 ? TAALS[taalId].numBeats : 4;
494
+ const cycle = this.#taal?.numBeats ?? (taalId !== void 0 ? TAALS[taalId].numBeats : 4);
438
495
  let beat = 0;
439
496
  const id = transport.scheduleRepeat((time) => {
440
497
  metro.volume.setValueAtTime(beat % cycle === 0 ? 0 : -8, time);