@dawcore/transport 0.0.10 → 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.d.mts CHANGED
@@ -142,6 +142,10 @@ declare class TempoMap {
142
142
  private _ppqn;
143
143
  private _entries;
144
144
  constructor(ppqn?: number, initialBpm?: number);
145
+ /** A non-finite or non-positive BPM silently corrupts the secondsAtTick
146
+ * cache (Infinity, or non-monotonic values that break the binary search
147
+ * in secondsToTicks) — reject it at the boundary instead. */
148
+ private static _validateBpm;
145
149
  getTempo(atTick?: Tick): number;
146
150
  setTempo(bpm: number, atTick?: Tick, options?: SetTempoOptions): void;
147
151
  ticksToSeconds(ticks: Tick): number;
@@ -299,17 +303,18 @@ interface ClipEvent extends SchedulerEvent {
299
303
  trackId: string;
300
304
  clipId: string;
301
305
  audioBuffer: AudioBuffer;
302
- /** Clip position on timeline (integer samples) */
306
+ /** Clip position on timeline (integer samples, at the TIMELINE sample rate) */
303
307
  startSample: Sample;
304
- /** Offset into audioBuffer (integer samples) */
308
+ /** Offset into audioBuffer (integer samples, at the BUFFER's own sample
309
+ * rate — they index the buffer, not the timeline) */
305
310
  offsetSamples: Sample;
306
- /** Duration to play (integer samples) */
311
+ /** Duration to play (integer samples, at the BUFFER's own sample rate) */
307
312
  durationSamples: Sample;
308
313
  /** Clip gain multiplier */
309
314
  gain: number;
310
- /** Fade in duration (integer samples) */
315
+ /** Fade in duration (integer samples, at the TIMELINE sample rate) */
311
316
  fadeInDurationSamples: Sample;
312
- /** Fade out duration (integer samples) */
317
+ /** Fade out duration (integer samples, at the TIMELINE sample rate) */
313
318
  fadeOutDurationSamples: Sample;
314
319
  }
315
320
  declare class ClipPlayer implements SchedulerListener<ClipEvent> {
package/dist/index.d.ts CHANGED
@@ -142,6 +142,10 @@ declare class TempoMap {
142
142
  private _ppqn;
143
143
  private _entries;
144
144
  constructor(ppqn?: number, initialBpm?: number);
145
+ /** A non-finite or non-positive BPM silently corrupts the secondsAtTick
146
+ * cache (Infinity, or non-monotonic values that break the binary search
147
+ * in secondsToTicks) — reject it at the boundary instead. */
148
+ private static _validateBpm;
145
149
  getTempo(atTick?: Tick): number;
146
150
  setTempo(bpm: number, atTick?: Tick, options?: SetTempoOptions): void;
147
151
  ticksToSeconds(ticks: Tick): number;
@@ -299,17 +303,18 @@ interface ClipEvent extends SchedulerEvent {
299
303
  trackId: string;
300
304
  clipId: string;
301
305
  audioBuffer: AudioBuffer;
302
- /** Clip position on timeline (integer samples) */
306
+ /** Clip position on timeline (integer samples, at the TIMELINE sample rate) */
303
307
  startSample: Sample;
304
- /** Offset into audioBuffer (integer samples) */
308
+ /** Offset into audioBuffer (integer samples, at the BUFFER's own sample
309
+ * rate — they index the buffer, not the timeline) */
305
310
  offsetSamples: Sample;
306
- /** Duration to play (integer samples) */
311
+ /** Duration to play (integer samples, at the BUFFER's own sample rate) */
307
312
  durationSamples: Sample;
308
313
  /** Clip gain multiplier */
309
314
  gain: number;
310
- /** Fade in duration (integer samples) */
315
+ /** Fade in duration (integer samples, at the TIMELINE sample rate) */
311
316
  fadeInDurationSamples: Sample;
312
- /** Fade out duration (integer samples) */
317
+ /** Fade out duration (integer samples, at the TIMELINE sample rate) */
313
318
  fadeOutDurationSamples: Sample;
314
319
  }
315
320
  declare class ClipPlayer implements SchedulerListener<ClipEvent> {
package/dist/index.js CHANGED
@@ -261,15 +261,27 @@ function curveNormalizedAt(x, slope) {
261
261
  const p = Math.max(CURVE_EPSILON, Math.min(1 - CURVE_EPSILON, slope));
262
262
  return p * p / (1 - p * 2) * (Math.pow((1 - p) / p, 2 * x) - 1);
263
263
  }
264
- var TempoMap = class {
264
+ var TempoMap = class _TempoMap {
265
265
  constructor(ppqn = 960, initialBpm = 120) {
266
+ _TempoMap._validateBpm(initialBpm);
266
267
  this._ppqn = ppqn;
267
268
  this._entries = [{ tick: 0, bpm: initialBpm, interpolation: "step", secondsAtTick: 0 }];
268
269
  }
270
+ /** A non-finite or non-positive BPM silently corrupts the secondsAtTick
271
+ * cache (Infinity, or non-monotonic values that break the binary search
272
+ * in secondsToTicks) — reject it at the boundary instead. */
273
+ static _validateBpm(bpm) {
274
+ if (!Number.isFinite(bpm) || bpm <= 0) {
275
+ throw new Error(
276
+ "[waveform-playlist] TempoMap: bpm must be a finite positive number, got " + bpm
277
+ );
278
+ }
279
+ }
269
280
  getTempo(atTick = 0) {
270
281
  return this._getTempoAt(atTick);
271
282
  }
272
283
  setTempo(bpm, atTick = 0, options) {
284
+ _TempoMap._validateBpm(bpm);
273
285
  const interpolation = options?.interpolation ?? "step";
274
286
  if (typeof interpolation === "object" && interpolation.type === "curve") {
275
287
  const s = interpolation.slope;
@@ -507,6 +519,7 @@ function isPowerOf2(n) {
507
519
  var MeterMap = class {
508
520
  constructor(ppqn, numerator = 4, denominator = 4) {
509
521
  this._ppqn = ppqn;
522
+ this._validateMeter(numerator, denominator);
510
523
  this._entries = [{ tick: 0, numerator, denominator, barAtTick: 0 }];
511
524
  }
512
525
  get ppqn() {
@@ -800,11 +813,19 @@ var ClipPlayer = class {
800
813
  const clipTick = clip.startTick !== void 0 ? clip.startTick : this._sampleTimeline.samplesToTicks(clip.startSample);
801
814
  if (clipTick < fromTick) continue;
802
815
  if (clipTick >= toTick) continue;
803
- const fadeInDurationSamples = clip.fadeIn ? clip.fadeIn.duration ?? 0 : 0;
804
- const fadeOutDurationSamples = clip.fadeOut ? clip.fadeOut.duration ?? 0 : 0;
816
+ const timelineRate = this._sampleTimeline.sampleRate;
817
+ const bufferRate = clip.audioBuffer.sampleRate;
818
+ const fadeInDurationSamples = clip.fadeIn ? Math.round((clip.fadeIn.duration ?? 0) * timelineRate) : 0;
819
+ const fadeOutDurationSamples = clip.fadeOut ? Math.round((clip.fadeOut.duration ?? 0) * timelineRate) : 0;
805
820
  let durationSamples = clip.durationSamples;
806
- if (this._loopEnabled && clip.startSample + durationSamples > this._loopEndSamples) {
807
- durationSamples = this._loopEndSamples - clip.startSample;
821
+ if (this._loopEnabled) {
822
+ const durationTimelineSamples = Math.round(
823
+ clip.durationSamples / bufferRate * timelineRate
824
+ );
825
+ const allowedTimelineSamples = this._loopEndSamples - clip.startSample;
826
+ if (durationTimelineSamples > allowedTimelineSamples) {
827
+ durationSamples = Math.round(allowedTimelineSamples / timelineRate * bufferRate);
828
+ }
808
829
  }
809
830
  events.push({
810
831
  trackId,
@@ -830,9 +851,9 @@ var ClipPlayer = class {
830
851
  );
831
852
  return;
832
853
  }
833
- const sampleRate = this._sampleTimeline.sampleRate;
834
- const offsetSeconds = event.offsetSamples / sampleRate;
835
- const durationSeconds = event.durationSamples / sampleRate;
854
+ const bufferRate = event.audioBuffer.sampleRate;
855
+ const offsetSeconds = event.offsetSamples / bufferRate;
856
+ const durationSeconds = event.durationSamples / bufferRate;
836
857
  if (offsetSeconds >= event.audioBuffer.duration) {
837
858
  console.warn(
838
859
  "[waveform-playlist] ClipPlayer.consume: offset (" + offsetSeconds + "s) exceeds audioBuffer.duration (" + event.audioBuffer.duration + 's) for clipId "' + event.clipId + '" \u2014 clip will not play'
@@ -845,8 +866,9 @@ var ClipPlayer = class {
845
866
  const when = this._toAudioTime(transportSeconds);
846
867
  const gainNode = this._audioContext.createGain();
847
868
  gainNode.gain.value = event.gain;
848
- let fadeIn = event.fadeInDurationSamples / sampleRate;
849
- let fadeOut = event.fadeOutDurationSamples / sampleRate;
869
+ const timelineRate = this._sampleTimeline.sampleRate;
870
+ let fadeIn = event.fadeInDurationSamples / timelineRate;
871
+ let fadeOut = event.fadeOutDurationSamples / timelineRate;
850
872
  if (fadeIn + fadeOut > durationSeconds) {
851
873
  const ratio = durationSeconds / (fadeIn + fadeOut);
852
874
  fadeIn *= ratio;
@@ -886,16 +908,21 @@ var ClipPlayer = class {
886
908
  if (!clip.audioBuffer) continue;
887
909
  const clipTick = clip.startTick !== void 0 ? clip.startTick : this._sampleTimeline.samplesToTicks(clip.startSample);
888
910
  if (clipTick >= newTick) continue;
889
- const clipEndSample = clip.startSample + clip.durationSamples;
911
+ const timelineRate = this._sampleTimeline.sampleRate;
912
+ const bufferRate = clip.audioBuffer.sampleRate;
913
+ const bufToTimeline = (n) => Math.round(n / bufferRate * timelineRate);
914
+ const timelineToBuf = (n) => Math.round(n / timelineRate * bufferRate);
915
+ const clipEndSample = clip.startSample + bufToTimeline(clip.durationSamples);
890
916
  if (clipEndSample <= newSample) continue;
891
917
  const offsetIntoClipSamples = newSample - clip.startSample;
892
- const offsetSamples = clip.offsetSamples + offsetIntoClipSamples;
893
- let durationSamples = clipEndSample - newSample;
894
- if (this._loopEnabled && newSample + durationSamples > this._loopEndSamples) {
895
- durationSamples = this._loopEndSamples - newSample;
918
+ const offsetSamples = clip.offsetSamples + timelineToBuf(offsetIntoClipSamples);
919
+ let remainingTimelineSamples = clipEndSample - newSample;
920
+ if (this._loopEnabled && newSample + remainingTimelineSamples > this._loopEndSamples) {
921
+ remainingTimelineSamples = this._loopEndSamples - newSample;
896
922
  }
897
- if (durationSamples <= 0) continue;
898
- const fadeOutDurationSamples = clip.fadeOut ? clip.fadeOut.duration ?? 0 : 0;
923
+ if (remainingTimelineSamples <= 0) continue;
924
+ const durationSamples = timelineToBuf(remainingTimelineSamples);
925
+ const fadeOutDurationSamples = clip.fadeOut ? Math.round((clip.fadeOut.duration ?? 0) * timelineRate) : 0;
899
926
  this.consume({
900
927
  trackId,
901
928
  clipId: clip.id,