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