@dawcore/transport 0.0.9 → 0.0.11

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.
package/dist/index.mjs CHANGED
@@ -224,15 +224,27 @@ function curveNormalizedAt(x, slope) {
224
224
  const p = Math.max(CURVE_EPSILON, Math.min(1 - CURVE_EPSILON, slope));
225
225
  return p * p / (1 - p * 2) * (Math.pow((1 - p) / p, 2 * x) - 1);
226
226
  }
227
- var TempoMap = class {
227
+ var TempoMap = class _TempoMap {
228
228
  constructor(ppqn = 960, initialBpm = 120) {
229
+ _TempoMap._validateBpm(initialBpm);
229
230
  this._ppqn = ppqn;
230
231
  this._entries = [{ tick: 0, bpm: initialBpm, interpolation: "step", secondsAtTick: 0 }];
231
232
  }
233
+ /** A non-finite or non-positive BPM silently corrupts the secondsAtTick
234
+ * cache (Infinity, or non-monotonic values that break the binary search
235
+ * in secondsToTicks) — reject it at the boundary instead. */
236
+ static _validateBpm(bpm) {
237
+ if (!Number.isFinite(bpm) || bpm <= 0) {
238
+ throw new Error(
239
+ "[waveform-playlist] TempoMap: bpm must be a finite positive number, got " + bpm
240
+ );
241
+ }
242
+ }
232
243
  getTempo(atTick = 0) {
233
244
  return this._getTempoAt(atTick);
234
245
  }
235
246
  setTempo(bpm, atTick = 0, options) {
247
+ _TempoMap._validateBpm(bpm);
236
248
  const interpolation = options?.interpolation ?? "step";
237
249
  if (typeof interpolation === "object" && interpolation.type === "curve") {
238
250
  const s = interpolation.slope;
@@ -470,6 +482,7 @@ function isPowerOf2(n) {
470
482
  var MeterMap = class {
471
483
  constructor(ppqn, numerator = 4, denominator = 4) {
472
484
  this._ppqn = ppqn;
485
+ this._validateMeter(numerator, denominator);
473
486
  this._entries = [{ tick: 0, numerator, denominator, barAtTick: 0 }];
474
487
  }
475
488
  get ppqn() {
@@ -654,7 +667,7 @@ var MasterNode = class {
654
667
  try {
655
668
  this._gainNode.disconnect();
656
669
  } catch (err) {
657
- console.warn("[waveform-playlist] MasterNode.dispose: error disconnecting:", String(err));
670
+ console.warn("[waveform-playlist] MasterNode.dispose: error disconnecting: " + String(err));
658
671
  }
659
672
  }
660
673
  };
@@ -763,11 +776,19 @@ var ClipPlayer = class {
763
776
  const clipTick = clip.startTick !== void 0 ? clip.startTick : this._sampleTimeline.samplesToTicks(clip.startSample);
764
777
  if (clipTick < fromTick) continue;
765
778
  if (clipTick >= toTick) continue;
766
- const fadeInDurationSamples = clip.fadeIn ? clip.fadeIn.duration ?? 0 : 0;
767
- const fadeOutDurationSamples = clip.fadeOut ? clip.fadeOut.duration ?? 0 : 0;
779
+ const timelineRate = this._sampleTimeline.sampleRate;
780
+ const bufferRate = clip.audioBuffer.sampleRate;
781
+ const fadeInDurationSamples = clip.fadeIn ? Math.round((clip.fadeIn.duration ?? 0) * timelineRate) : 0;
782
+ const fadeOutDurationSamples = clip.fadeOut ? Math.round((clip.fadeOut.duration ?? 0) * timelineRate) : 0;
768
783
  let durationSamples = clip.durationSamples;
769
- if (this._loopEnabled && clip.startSample + durationSamples > this._loopEndSamples) {
770
- durationSamples = this._loopEndSamples - clip.startSample;
784
+ if (this._loopEnabled) {
785
+ const durationTimelineSamples = Math.round(
786
+ clip.durationSamples / bufferRate * timelineRate
787
+ );
788
+ const allowedTimelineSamples = this._loopEndSamples - clip.startSample;
789
+ if (durationTimelineSamples > allowedTimelineSamples) {
790
+ durationSamples = Math.round(allowedTimelineSamples / timelineRate * bufferRate);
791
+ }
771
792
  }
772
793
  events.push({
773
794
  trackId,
@@ -793,9 +814,9 @@ var ClipPlayer = class {
793
814
  );
794
815
  return;
795
816
  }
796
- const sampleRate = this._sampleTimeline.sampleRate;
797
- const offsetSeconds = event.offsetSamples / sampleRate;
798
- const durationSeconds = event.durationSamples / sampleRate;
817
+ const bufferRate = event.audioBuffer.sampleRate;
818
+ const offsetSeconds = event.offsetSamples / bufferRate;
819
+ const durationSeconds = event.durationSamples / bufferRate;
799
820
  if (offsetSeconds >= event.audioBuffer.duration) {
800
821
  console.warn(
801
822
  "[waveform-playlist] ClipPlayer.consume: offset (" + offsetSeconds + "s) exceeds audioBuffer.duration (" + event.audioBuffer.duration + 's) for clipId "' + event.clipId + '" \u2014 clip will not play'
@@ -808,8 +829,9 @@ var ClipPlayer = class {
808
829
  const when = this._toAudioTime(transportSeconds);
809
830
  const gainNode = this._audioContext.createGain();
810
831
  gainNode.gain.value = event.gain;
811
- let fadeIn = event.fadeInDurationSamples / sampleRate;
812
- let fadeOut = event.fadeOutDurationSamples / sampleRate;
832
+ const timelineRate = this._sampleTimeline.sampleRate;
833
+ let fadeIn = event.fadeInDurationSamples / timelineRate;
834
+ let fadeOut = event.fadeOutDurationSamples / timelineRate;
813
835
  if (fadeIn + fadeOut > durationSeconds) {
814
836
  const ratio = durationSeconds / (fadeIn + fadeOut);
815
837
  fadeIn *= ratio;
@@ -849,16 +871,21 @@ var ClipPlayer = class {
849
871
  if (!clip.audioBuffer) continue;
850
872
  const clipTick = clip.startTick !== void 0 ? clip.startTick : this._sampleTimeline.samplesToTicks(clip.startSample);
851
873
  if (clipTick >= newTick) continue;
852
- const clipEndSample = clip.startSample + clip.durationSamples;
874
+ const timelineRate = this._sampleTimeline.sampleRate;
875
+ const bufferRate = clip.audioBuffer.sampleRate;
876
+ const bufToTimeline = (n) => Math.round(n / bufferRate * timelineRate);
877
+ const timelineToBuf = (n) => Math.round(n / timelineRate * bufferRate);
878
+ const clipEndSample = clip.startSample + bufToTimeline(clip.durationSamples);
853
879
  if (clipEndSample <= newSample) continue;
854
880
  const offsetIntoClipSamples = newSample - clip.startSample;
855
- const offsetSamples = clip.offsetSamples + offsetIntoClipSamples;
856
- let durationSamples = clipEndSample - newSample;
857
- if (this._loopEnabled && newSample + durationSamples > this._loopEndSamples) {
858
- durationSamples = this._loopEndSamples - newSample;
881
+ const offsetSamples = clip.offsetSamples + timelineToBuf(offsetIntoClipSamples);
882
+ let remainingTimelineSamples = clipEndSample - newSample;
883
+ if (this._loopEnabled && newSample + remainingTimelineSamples > this._loopEndSamples) {
884
+ remainingTimelineSamples = this._loopEndSamples - newSample;
859
885
  }
860
- if (durationSamples <= 0) continue;
861
- const fadeOutDurationSamples = clip.fadeOut ? clip.fadeOut.duration ?? 0 : 0;
886
+ if (remainingTimelineSamples <= 0) continue;
887
+ const durationSamples = timelineToBuf(remainingTimelineSamples);
888
+ const fadeOutDurationSamples = clip.fadeOut ? Math.round((clip.fadeOut.duration ?? 0) * timelineRate) : 0;
862
889
  this.consume({
863
890
  trackId,
864
891
  clipId: clip.id,
@@ -1575,6 +1602,11 @@ var _Transport = class _Transport {
1575
1602
  }
1576
1603
  trackNode.disconnectEffects();
1577
1604
  }
1605
+ /** The master output AudioNode. Connect your own nodes (analyzers, recorders, etc.)
1606
+ * in parallel. The transport already routes this to audioContext.destination. */
1607
+ get masterOutputNode() {
1608
+ return this._masterNode.output;
1609
+ }
1578
1610
  // --- Events ---
1579
1611
  on(event, cb) {
1580
1612
  if (!this._listeners.has(event)) {
@@ -1887,6 +1919,9 @@ var NativePlayoutAdapter = class {
1887
1919
  secondsToTicks(seconds) {
1888
1920
  return this._transport.timeToTick(seconds);
1889
1921
  }
1922
+ get masterOutputNode() {
1923
+ return this._transport.masterOutputNode;
1924
+ }
1890
1925
  dispose() {
1891
1926
  this._transport.dispose();
1892
1927
  }