@dawcore/transport 0.0.11 → 0.0.12

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
@@ -212,7 +212,7 @@ var SampleTimeline = class {
212
212
  "[waveform-playlist] SampleTimeline: tempoMap not set \u2014 call setTempoMap() first"
213
213
  );
214
214
  }
215
- return this._tempoMap.secondsToTicks(samples / this._sampleRate);
215
+ return Math.round(this._tempoMap.secondsToTicks(samples / this._sampleRate));
216
216
  }
217
217
  };
218
218
 
@@ -243,6 +243,11 @@ var TempoMap = class _TempoMap {
243
243
  getTempo(atTick = 0) {
244
244
  return this._getTempoAt(atTick);
245
245
  }
246
+ /** Number of tempo entries in the map. Always >= 1 — the tick-0 entry is
247
+ * permanent. Used by Transport.setTempo to detect multi-entry maps. */
248
+ get entryCount() {
249
+ return this._entries.length;
250
+ }
246
251
  setTempo(bpm, atTick = 0, options) {
247
252
  _TempoMap._validateBpm(bpm);
248
253
  const interpolation = options?.interpolation ?? "step";
@@ -321,6 +326,18 @@ var TempoMap = class _TempoMap {
321
326
  const first = this._entries[0];
322
327
  this._entries = [{ tick: 0, bpm: first.bpm, interpolation: "step", secondsAtTick: 0 }];
323
328
  }
329
+ /** Remove the tempo entry at exactly `atTick`, if one exists. The tick-0
330
+ * entry is permanent (a map must always have a tempo) — removing it is a
331
+ * no-op, matching removeMeter's treatment of the initial meter. The
332
+ * seconds cache is recomputed from the removal point, the same partial
333
+ * update setTempo uses. */
334
+ removeTempo(atTick) {
335
+ if (atTick === 0) return;
336
+ const i = this._entries.findIndex((e) => e.tick === atTick);
337
+ if (i === -1) return;
338
+ this._entries = [...this._entries.slice(0, i), ...this._entries.slice(i + 1)];
339
+ this._recomputeCache(i);
340
+ }
324
341
  /** Get the interpolated BPM at a tick position */
325
342
  _getTempoAt(atTick) {
326
343
  const entryIndex = this._entryIndexAt(atTick);
@@ -646,6 +663,11 @@ var MeterMap = class {
646
663
  "[waveform-playlist] MeterMap: denominator must be a power of 2 (1-32), got " + denominator
647
664
  );
648
665
  }
666
+ if (!Number.isInteger(this._ppqn * 4 / denominator)) {
667
+ throw new Error(
668
+ "[waveform-playlist] MeterMap: ppqn (" + this._ppqn + ") * 4 is not divisible by denominator (" + denominator + ") \u2014 bar boundaries would fall on fractional ticks"
669
+ );
670
+ }
649
671
  }
650
672
  };
651
673
 
@@ -1489,12 +1511,22 @@ var _Transport = class _Transport {
1489
1511
  this._emit("loop");
1490
1512
  }
1491
1513
  // --- Tempo ---
1514
+ /** Returns true when the tempo was applied, false when a defaulted (no
1515
+ * atTick) write was refused because the tempo map has multiple entries —
1516
+ * pass an explicit atTick to modify a multi-entry map (#407). */
1492
1517
  setTempo(bpm, atTick, options) {
1518
+ if (atTick === void 0 && this._tempoMap.entryCount > 1) {
1519
+ console.warn(
1520
+ "[waveform-playlist] Transport.setTempo: refusing defaulted tick-0 write of " + bpm + " BPM \u2014 the tempo map has " + this._tempoMap.entryCount + " entries. Pass an explicit atTick to modify a multi-entry tempo map."
1521
+ );
1522
+ return false;
1523
+ }
1493
1524
  this._tempoMap.setTempo(bpm, atTick, options);
1494
1525
  if (this._loopEnabled) {
1495
1526
  this._loopStartSeconds = this._tempoMap.ticksToSeconds(this._loopStartTick);
1496
1527
  }
1497
1528
  this._emit("tempochange", { bpm, atTick: atTick ?? 0 });
1529
+ return true;
1498
1530
  }
1499
1531
  getTempo(atTick) {
1500
1532
  return this._tempoMap.getTempo(atTick);
@@ -1532,6 +1564,17 @@ var _Transport = class _Transport {
1532
1564
  }
1533
1565
  this._emit("tempochange", { bpm: this._tempoMap.getTempo(), atTick: 0 });
1534
1566
  }
1567
+ /** Remove the tempo entry at exactly `atTick` (tick 0 is permanent — a
1568
+ * no-op, like removeMeter's initial entry). Mirrors setTempo's loop-cache
1569
+ * invalidation and event; the emitted bpm is the tempo now in force at
1570
+ * the removed position. */
1571
+ removeTempo(atTick) {
1572
+ this._tempoMap.removeTempo(atTick);
1573
+ if (this._loopEnabled) {
1574
+ this._loopStartSeconds = this._tempoMap.ticksToSeconds(this._loopStartTick);
1575
+ }
1576
+ this._emit("tempochange", { bpm: this._tempoMap.getTempo(atTick), atTick });
1577
+ }
1535
1578
  barToTick(bar) {
1536
1579
  return this._meterMap.barToTick(bar);
1537
1580
  }
@@ -1904,7 +1947,7 @@ var NativePlayoutAdapter = class {
1904
1947
  return this._transport.isCountingIn();
1905
1948
  }
1906
1949
  setTempo(bpm, atTick) {
1907
- this._transport.setTempo(bpm, atTick !== void 0 ? atTick : void 0);
1950
+ return this._transport.setTempo(bpm, atTick !== void 0 ? atTick : void 0);
1908
1951
  }
1909
1952
  setMeter(numerator, denominator, atTick) {
1910
1953
  this._transport.setMeter(