@ugfoundation/swaralipi-js 0.7.4 → 0.7.6

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.
@@ -489,6 +489,7 @@ function createTanpura(Tone, base, stems = TANPURA_PCS) {
489
489
 
490
490
  // src/player/player.ts
491
491
  var STOP_FADE_SEC = 0.5;
492
+ var MEDIA_LEAD_SEC = 0.25;
492
493
  function midiRange(schedule) {
493
494
  let lo = Infinity;
494
495
  let hi = -Infinity;
@@ -502,6 +503,9 @@ function midiRange(schedule) {
502
503
  }
503
504
  return lo <= hi ? { lo, hi } : void 0;
504
505
  }
506
+ function playDurationSec(schedule, spb, rhythm) {
507
+ return schedule.totalBeats * spb + (rhythm !== "none" ? schedule.pickupBeats * spb : 0) + spb;
508
+ }
505
509
  async function loadedOrWarn(Tone) {
506
510
  try {
507
511
  await Tone.loaded();
@@ -548,6 +552,7 @@ var SwaralipiPlayer = class {
548
552
  tanpura: options.tanpura ?? true,
549
553
  previewLimitSec: options.previewLimitSec ?? Infinity,
550
554
  onHighlight: options.onHighlight,
555
+ onEvent: options.onEvent,
551
556
  onBeat: options.onBeat,
552
557
  onState: options.onState,
553
558
  onLimit: options.onLimit
@@ -581,6 +586,27 @@ var SwaralipiPlayer = class {
581
586
  const id = this.#taalId();
582
587
  return this.#opts.rhythm === "tabla" && id !== void 0 && SAMPLED_TAALS.has(id) && !!this.#opts.soundsBaseUrl;
583
588
  }
589
+ /**
590
+ * Length of one play-through in seconds, for a progress bar (the OS media
591
+ * session's, typically). Derived from the loaded notation on every read and
592
+ * never stored, because every input can move: re-engrave the document, drag
593
+ * the tempo, switch the rhythm off, and this changes with it. `Infinity` in
594
+ * taal mode (one avartan looping, no end — the value the Media Session spec
595
+ * reserves for exactly that), 0 with nothing loaded, and clamped by a preview
596
+ * cap when one is set, since that is when playback will really stop.
597
+ */
598
+ get durationSec() {
599
+ if (this.#taal !== void 0) return Infinity;
600
+ if (this.#doc === void 0) return 0;
601
+ const schedule = buildSchedule(this.#doc, { tonic: this.#opts.tonic, baseOctave: this.#opts.baseOctave });
602
+ const full = playDurationSec(schedule, 60 / this.#effectiveBpm(), this.#opts.rhythm);
603
+ return Math.min(full, this.#opts.previewLimitSec);
604
+ }
605
+ /** Seconds played so far, off the Transport's own clock — so it holds while
606
+ * paused, and reads 0 before the first play and after stop(). */
607
+ get positionSec() {
608
+ return this.#tone?.getTransport().seconds ?? 0;
609
+ }
584
610
  /* ----------------------------------------------------------------- */
585
611
  /* Loading + transport control */
586
612
  /* ----------------------------------------------------------------- */
@@ -893,6 +919,7 @@ var SwaralipiPlayer = class {
893
919
  }
894
920
  const draw = Tone.getDraw();
895
921
  const shift = this.#opts.rhythm !== "none" ? schedule.pickupBeats * spb : 0;
922
+ const mediaLead = Math.min(MEDIA_LEAD_SEC, spb);
896
923
  schedule.events.forEach((ev, i) => {
897
924
  const at = ev.beat * spb + shift;
898
925
  if (ev.midi !== null && ev.glide !== true) {
@@ -926,12 +953,14 @@ var SwaralipiPlayer = class {
926
953
  transport.scheduleOnce((time) => melody.triggerAttackRelease(name, dur, time), at);
927
954
  }
928
955
  }
929
- transport.scheduleOnce((time) => {
930
- draw.schedule(() => this.#emitHighlight(ev.path), time);
931
- }, at);
956
+ if (this.#opts.onEvent !== void 0) {
957
+ transport.scheduleOnce(() => this.#opts.onEvent?.(ev.path), Math.max(at - mediaLead, 0));
958
+ }
959
+ transport.scheduleOnce((time) => draw.schedule(() => this.#emitHighlight(ev.path), time), at);
932
960
  });
933
- const endAt = schedule.totalBeats * spb + shift + spb;
961
+ const endAt = playDurationSec(schedule, spb, this.#opts.rhythm);
934
962
  transport.scheduleOnce((time) => {
963
+ this.#opts.onEvent?.(null);
935
964
  draw.schedule(() => this.#emitHighlight(null), time);
936
965
  }, schedule.totalBeats * spb + shift);
937
966
  transport.scheduleOnce(() => this.stop(), endAt);