@ugfoundation/swaralipi-js 0.3.2 → 0.3.4

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 (44) hide show
  1. package/dist/{batch-5LFXQ67K.js → batch-Y3HKAL3B.js} +5 -5
  2. package/dist/{batch-5LFXQ67K.js.map → batch-Y3HKAL3B.js.map} +1 -1
  3. package/dist/{chunk-YE7UVYRY.js → chunk-6ETPFHZ6.js} +5 -3
  4. package/dist/chunk-6ETPFHZ6.js.map +1 -0
  5. package/dist/{chunk-32LOBRC2.js → chunk-AIGSKUD3.js} +3 -3
  6. package/dist/{chunk-32LOBRC2.js.map → chunk-AIGSKUD3.js.map} +1 -1
  7. package/dist/{chunk-XQYXZPHK.js → chunk-IG2CIUTP.js} +3 -3
  8. package/dist/{chunk-XQYXZPHK.js.map → chunk-IG2CIUTP.js.map} +1 -1
  9. package/dist/{chunk-QVNFEWMM.js → chunk-KPKD53NA.js} +4 -4
  10. package/dist/{chunk-QVNFEWMM.js.map → chunk-KPKD53NA.js.map} +1 -1
  11. package/dist/cli.cjs +4 -2
  12. package/dist/cli.cjs.map +1 -1
  13. package/dist/cli.js +3 -3
  14. package/dist/convert/index.cjs +4 -2
  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-CHXz3V6T.d.cts → schedule-C2WN7YOK.d.cts} +1 -1
  29. package/dist/{schedule-BaE3JGHe.d.ts → schedule-dXV5cUQI.d.ts} +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-DxJ8wRtF.d.ts → taals-CjzBuZ35.d.cts} +8 -1
  36. package/dist/{taals-DxJ8wRtF.d.cts → taals-CjzBuZ35.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-YE7UVYRY.js.map +0 -1
@@ -17,8 +17,10 @@ var TAALS = {
17
17
  teentaal: { id: "teentaal", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },
18
18
  aratheka: { id: "aratheka", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },
19
19
  // আড়াঠেকা
20
- jhampak: { id: "jhampak", numBeats: 5, bhaags: [3, 2], sam: 0 }
20
+ jhampak: { id: "jhampak", numBeats: 5, bhaags: [3, 2], sam: 0 },
21
21
  // ঝম্পক
22
+ dhamaar: { id: "dhamaar", numBeats: 14, bhaags: [5, 2, 3, 4], sam: 0, khaali: [7] }
23
+ // ধামার
22
24
  };
23
25
 
24
26
  // src/player/pitch.ts
@@ -390,6 +392,9 @@ var SwaralipiPlayer = class {
390
392
  /** Per-song setBpm tweak: beats meta.bpm but is cleared by the next load(). */
391
393
  #bpmTweak;
392
394
  #doc;
395
+ /** Taal mode (`loadTaal`): loops one avartan of this taal's theka with no
396
+ * melody. Mutually exclusive with `#doc`. */
397
+ #taal;
393
398
  #state = "idle";
394
399
  #tone;
395
400
  /** Master bus everything routes through, so stop() can fade it out. */
@@ -418,6 +423,7 @@ var SwaralipiPlayer = class {
418
423
  tanpura: options.tanpura ?? true,
419
424
  previewLimitSec: options.previewLimitSec ?? Infinity,
420
425
  onHighlight: options.onHighlight,
426
+ onBeat: options.onBeat,
421
427
  onState: options.onState,
422
428
  onLimit: options.onLimit
423
429
  };
@@ -457,6 +463,23 @@ var SwaralipiPlayer = class {
457
463
  load(doc) {
458
464
  this.#halt();
459
465
  this.#doc = doc;
466
+ this.#taal = void 0;
467
+ this.#taalOverride = void 0;
468
+ this.#bpmTweak = void 0;
469
+ this.#setState("ready");
470
+ }
471
+ /**
472
+ * Taal mode: loop one avartan of a taal's theka (rhythm only, no melody) —
473
+ * for a standalone theka player. `play()` loops indefinitely (tabla loop /
474
+ * drum sequence) and fires `onBeat(beatInCycle)` once per matra on the draw
475
+ * thread so the consumer can highlight the active bol / drum-staff note. The
476
+ * taal's `id` should be a sampled taal (see `SAMPLED_TAALS`) for tabla audio;
477
+ * `variation` picks a tabla loop variation. Stops any current playback.
478
+ */
479
+ loadTaal(taal) {
480
+ this.#halt();
481
+ this.#taal = taal;
482
+ this.#doc = void 0;
460
483
  this.#taalOverride = void 0;
461
484
  this.#bpmTweak = void 0;
462
485
  this.#setState("ready");
@@ -464,7 +487,7 @@ var SwaralipiPlayer = class {
464
487
  /** Start (or resume) playback. Must be triggered from a user gesture so the
465
488
  * browser permits `AudioContext` resume. */
466
489
  async play() {
467
- if (this.#doc === void 0 || this.#state === "playing") return;
490
+ if (this.#doc === void 0 && this.#taal === void 0 || this.#state === "playing") return;
468
491
  const Tone = await this.#ensureTone();
469
492
  await Tone.start();
470
493
  if (this.#state === "paused") {
@@ -512,7 +535,7 @@ var SwaralipiPlayer = class {
512
535
  this.#master.gain.cancelScheduledValues(now);
513
536
  this.#master.gain.rampTo(0, STOP_FADE_SEC, now);
514
537
  this.#emitHighlight(null);
515
- this.#setState(this.#doc ? "ready" : "idle");
538
+ this.#setState(this.#doc || this.#taal ? "ready" : "idle");
516
539
  this.#cancelFadeTimer();
517
540
  this.#fadeTimer = setTimeout(() => this.#halt(), STOP_FADE_SEC * 1e3 + 60);
518
541
  return;
@@ -534,7 +557,7 @@ var SwaralipiPlayer = class {
534
557
  this.#restoreMasterGain();
535
558
  this.#scheduleIds = [];
536
559
  this.#emitHighlight(null);
537
- if (this.#state !== "idle") this.#setState(this.#doc ? "ready" : "idle");
560
+ if (this.#state !== "idle") this.#setState(this.#doc || this.#taal ? "ready" : "idle");
538
561
  }
539
562
  #cancelFadeTimer() {
540
563
  if (this.#fadeTimer !== void 0) {
@@ -648,15 +671,16 @@ var SwaralipiPlayer = class {
648
671
  #emitHighlight(path) {
649
672
  this.#opts.onHighlight?.(path);
650
673
  }
651
- /** The tabla variation to play: the doc's own (in auto mode), else none — an
652
- * explicit setTaal override plays the taal's default variation. */
674
+ /** The tabla variation to play: taal-mode taal's, the doc's own (auto mode),
675
+ * else none — an explicit setTaal override plays the taal's default variation. */
653
676
  #variation() {
677
+ if (this.#taal !== void 0) return this.#taal.variation;
654
678
  return this.#taalAuto ? this.#doc?.meta.taal?.variation : void 0;
655
679
  }
656
- /** Resolve the active taal: an explicit override, else the doc's. */
680
+ /** Resolve the active taal id: taal mode's taal, an explicit override, else
681
+ * the doc's — but only when it names a sampled taal. */
657
682
  #taalId() {
658
- if (!this.#taalAuto && this.#taalOverride !== void 0) return this.#taalOverride;
659
- const id = this.#doc?.meta.taal?.id;
683
+ const id = this.#taal?.id ?? (!this.#taalAuto && this.#taalOverride !== void 0 ? this.#taalOverride : this.#doc?.meta.taal?.id);
660
684
  return id !== void 0 && id in TAALS ? id : void 0;
661
685
  }
662
686
  /** Resolve an instrument id to its def (custom overrides built-ins; the
@@ -713,6 +737,7 @@ var SwaralipiPlayer = class {
713
737
  /** Build the melody instrument(s)/rhythm/drone for the current settings and
714
738
  * wait for their buffers, then schedule everything on the Transport. */
715
739
  async #arrange(Tone) {
740
+ if (this.#taal !== void 0) return this.#arrangeTaal(Tone);
716
741
  if (this.#doc === void 0) return;
717
742
  const base = this.#opts.soundsBaseUrl;
718
743
  const transport = Tone.getTransport();
@@ -758,6 +783,40 @@ var SwaralipiPlayer = class {
758
783
  }, schedule.totalBeats * spb);
759
784
  transport.scheduleOnce(() => this.stop(), endAt);
760
785
  }
786
+ /** Taal mode: loop one avartan forever (tabla loop / drum sequence via
787
+ * #buildRhythm), firing onBeat + a `String(beatInCycle)` highlight each matra
788
+ * on the draw thread. No melody, no tanpura, no auto-stop — it plays until
789
+ * stop(). */
790
+ async #arrangeTaal(Tone) {
791
+ if (this.#taal === void 0) return;
792
+ const base = this.#opts.soundsBaseUrl;
793
+ const transport = Tone.getTransport();
794
+ transport.cancel(0);
795
+ transport.position = 0;
796
+ this.#scheduleIds = [];
797
+ this.#ensureMaster(Tone);
798
+ this.#restoreMasterGain();
799
+ const bpm = this.#effectiveBpm();
800
+ transport.bpm.value = bpm;
801
+ const spb = 60 / bpm;
802
+ this.#buildRhythm(Tone, transport, base, spb);
803
+ await Tone.loaded();
804
+ if (this.#tabla?.buffer?.loaded) {
805
+ this.#tabla.loopEnd = quantizeLoopEnd(this.#tabla.buffer.duration, spb);
806
+ }
807
+ const draw = Tone.getDraw();
808
+ const n = Math.max(1, this.#taal.numBeats);
809
+ let beat = 0;
810
+ const id = transport.scheduleRepeat((time) => {
811
+ const b = beat % n;
812
+ draw.schedule(() => {
813
+ this.#emitHighlight(String(b));
814
+ this.#opts.onBeat?.(b);
815
+ }, time);
816
+ beat += 1;
817
+ }, spb, 0);
818
+ this.#scheduleIds.push(id);
819
+ }
761
820
  /** Build (or reuse) the melody sampler for `id`, loading only the samples
762
821
  * covering `range`. A cached sampler is reused while it still covers the
763
822
  * range; otherwise it's rebuilt (so changing songs/tonic widens it lazily). */
@@ -788,7 +847,7 @@ var SwaralipiPlayer = class {
788
847
  if (this.#opts.rhythm === "drums") {
789
848
  this.#drums ??= createDrumKit(Tone).connect(master);
790
849
  const drums = this.#drums;
791
- const taal = taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 };
850
+ const taal = this.#taal ?? (taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 });
792
851
  const pattern = drumPattern(taal);
793
852
  let beat2 = 0;
794
853
  const id2 = transport.scheduleRepeat((time) => {
@@ -802,7 +861,7 @@ var SwaralipiPlayer = class {
802
861
  if (this.#opts.rhythm === "none" || base === void 0) return;
803
862
  this.#metronome ??= createMetronome(Tone, base).connect(master);
804
863
  const metro = this.#metronome;
805
- const cycle = taalId !== void 0 ? TAALS[taalId].numBeats : 4;
864
+ const cycle = this.#taal?.numBeats ?? (taalId !== void 0 ? TAALS[taalId].numBeats : 4);
806
865
  let beat = 0;
807
866
  const id = transport.scheduleRepeat((time) => {
808
867
  metro.volume.setValueAtTime(beat % cycle === 0 ? 0 : -8, time);