@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.d.mts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.js +53 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +53 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -8
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> {
|
|
@@ -467,6 +472,9 @@ declare class Transport {
|
|
|
467
472
|
isCountingIn(): boolean;
|
|
468
473
|
connectTrackOutput(trackId: string, node: AudioNode): void;
|
|
469
474
|
disconnectTrackOutput(trackId: string): void;
|
|
475
|
+
/** The master output AudioNode. Connect your own nodes (analyzers, recorders, etc.)
|
|
476
|
+
* in parallel. The transport already routes this to audioContext.destination. */
|
|
477
|
+
get masterOutputNode(): AudioNode;
|
|
470
478
|
on<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void;
|
|
471
479
|
off<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void;
|
|
472
480
|
dispose(): void;
|
|
@@ -515,6 +523,7 @@ declare class NativePlayoutAdapter implements PlayoutAdapter {
|
|
|
515
523
|
setMeter(numerator: number, denominator: number, atTick?: number): void;
|
|
516
524
|
ticksToSeconds(tick: number): number;
|
|
517
525
|
secondsToTicks(seconds: number): number;
|
|
526
|
+
get masterOutputNode(): AudioNode;
|
|
518
527
|
dispose(): void;
|
|
519
528
|
}
|
|
520
529
|
|
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> {
|
|
@@ -467,6 +472,9 @@ declare class Transport {
|
|
|
467
472
|
isCountingIn(): boolean;
|
|
468
473
|
connectTrackOutput(trackId: string, node: AudioNode): void;
|
|
469
474
|
disconnectTrackOutput(trackId: string): void;
|
|
475
|
+
/** The master output AudioNode. Connect your own nodes (analyzers, recorders, etc.)
|
|
476
|
+
* in parallel. The transport already routes this to audioContext.destination. */
|
|
477
|
+
get masterOutputNode(): AudioNode;
|
|
470
478
|
on<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void;
|
|
471
479
|
off<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void;
|
|
472
480
|
dispose(): void;
|
|
@@ -515,6 +523,7 @@ declare class NativePlayoutAdapter implements PlayoutAdapter {
|
|
|
515
523
|
setMeter(numerator: number, denominator: number, atTick?: number): void;
|
|
516
524
|
ticksToSeconds(tick: number): number;
|
|
517
525
|
secondsToTicks(seconds: number): number;
|
|
526
|
+
get masterOutputNode(): AudioNode;
|
|
518
527
|
dispose(): void;
|
|
519
528
|
}
|
|
520
529
|
|
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() {
|
|
@@ -691,7 +704,7 @@ var MasterNode = class {
|
|
|
691
704
|
try {
|
|
692
705
|
this._gainNode.disconnect();
|
|
693
706
|
} catch (err) {
|
|
694
|
-
console.warn("[waveform-playlist] MasterNode.dispose: error disconnecting:"
|
|
707
|
+
console.warn("[waveform-playlist] MasterNode.dispose: error disconnecting: " + String(err));
|
|
695
708
|
}
|
|
696
709
|
}
|
|
697
710
|
};
|
|
@@ -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
|
|
804
|
-
const
|
|
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
|
|
807
|
-
|
|
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
|
|
834
|
-
const offsetSeconds = event.offsetSamples /
|
|
835
|
-
const durationSeconds = event.durationSamples /
|
|
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
|
-
|
|
849
|
-
let
|
|
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
|
|
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
|
|
894
|
-
if (this._loopEnabled && newSample +
|
|
895
|
-
|
|
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 (
|
|
898
|
-
const
|
|
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,
|
|
@@ -1612,6 +1639,11 @@ var _Transport = class _Transport {
|
|
|
1612
1639
|
}
|
|
1613
1640
|
trackNode.disconnectEffects();
|
|
1614
1641
|
}
|
|
1642
|
+
/** The master output AudioNode. Connect your own nodes (analyzers, recorders, etc.)
|
|
1643
|
+
* in parallel. The transport already routes this to audioContext.destination. */
|
|
1644
|
+
get masterOutputNode() {
|
|
1645
|
+
return this._masterNode.output;
|
|
1646
|
+
}
|
|
1615
1647
|
// --- Events ---
|
|
1616
1648
|
on(event, cb) {
|
|
1617
1649
|
if (!this._listeners.has(event)) {
|
|
@@ -1924,6 +1956,9 @@ var NativePlayoutAdapter = class {
|
|
|
1924
1956
|
secondsToTicks(seconds) {
|
|
1925
1957
|
return this._transport.timeToTick(seconds);
|
|
1926
1958
|
}
|
|
1959
|
+
get masterOutputNode() {
|
|
1960
|
+
return this._transport.masterOutputNode;
|
|
1961
|
+
}
|
|
1927
1962
|
dispose() {
|
|
1928
1963
|
this._transport.dispose();
|
|
1929
1964
|
}
|