@ugfoundation/swaralipi-js 0.7.5 → 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();
@@ -582,6 +586,27 @@ var SwaralipiPlayer = class {
582
586
  const id = this.#taalId();
583
587
  return this.#opts.rhythm === "tabla" && id !== void 0 && SAMPLED_TAALS.has(id) && !!this.#opts.soundsBaseUrl;
584
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
+ }
585
610
  /* ----------------------------------------------------------------- */
586
611
  /* Loading + transport control */
587
612
  /* ----------------------------------------------------------------- */
@@ -894,6 +919,7 @@ var SwaralipiPlayer = class {
894
919
  }
895
920
  const draw = Tone.getDraw();
896
921
  const shift = this.#opts.rhythm !== "none" ? schedule.pickupBeats * spb : 0;
922
+ const mediaLead = Math.min(MEDIA_LEAD_SEC, spb);
897
923
  schedule.events.forEach((ev, i) => {
898
924
  const at = ev.beat * spb + shift;
899
925
  if (ev.midi !== null && ev.glide !== true) {
@@ -927,12 +953,12 @@ var SwaralipiPlayer = class {
927
953
  transport.scheduleOnce((time) => melody.triggerAttackRelease(name, dur, time), at);
928
954
  }
929
955
  }
930
- transport.scheduleOnce((time) => {
931
- this.#opts.onEvent?.(ev.path);
932
- draw.schedule(() => this.#emitHighlight(ev.path), time);
933
- }, 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);
934
960
  });
935
- const endAt = schedule.totalBeats * spb + shift + spb;
961
+ const endAt = playDurationSec(schedule, spb, this.#opts.rhythm);
936
962
  transport.scheduleOnce((time) => {
937
963
  this.#opts.onEvent?.(null);
938
964
  draw.schedule(() => this.#emitHighlight(null), time);