@dawcore/transport 0.0.2 → 0.0.3
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/README.md +8 -4
- package/dist/index.d.mts +110 -69
- package/dist/index.d.ts +110 -69
- package/dist/index.js +203 -96
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +203 -96
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Native Web Audio transport for multi-track audio scheduling, looping, tempo, and
|
|
|
10
10
|
- **Built-in metronome** — Beat-grid click scheduling with accent on beat 1. Just another scheduler listener.
|
|
11
11
|
- **Per-track signal chain** — Native GainNode (volume) → StereoPannerNode → GainNode (mute) → effects hook → master output.
|
|
12
12
|
- **Effects plugin hook** — `connectTrackOutput(trackId, node)` inserts any `AudioNode` chain (Tone.js effects, WAM plugins, native nodes).
|
|
13
|
+
- **Type-safe coordinates** — Branded `Tick` and `Sample` types prevent accidentally passing seconds where ticks or samples are expected. Zero runtime cost.
|
|
13
14
|
- **PlayoutAdapter bridge** — `NativePlayoutAdapter` implements the `PlayoutAdapter` interface from `@waveform-playlist/engine`.
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
@@ -145,14 +146,17 @@ new Transport(audioContext: AudioContext, options?: TransportOptions)
|
|
|
145
146
|
- `setMasterVolume(volume)`
|
|
146
147
|
|
|
147
148
|
**Loop:**
|
|
148
|
-
- `setLoop(enabled,
|
|
149
|
+
- `setLoop(enabled, startTick: Tick, endTick: Tick)` — Set loop region in ticks (primary API)
|
|
150
|
+
- `setLoopSeconds(enabled, start, end)` — Set loop region in seconds (convenience)
|
|
151
|
+
- `setLoopSamples(enabled, startSample: Sample, endSample: Sample)` — Set loop region in samples (convenience)
|
|
149
152
|
|
|
150
153
|
**Tempo & Meter:**
|
|
151
154
|
- `setTempo(bpm, atTick?)` / `getTempo(atTick?)`
|
|
152
155
|
- `clearTempos()` — remove all tempo entries
|
|
153
|
-
- `setMeter(numerator, denominator, atTick
|
|
154
|
-
- `removeMeter(atTick)` / `clearMeters()`
|
|
155
|
-
- `barToTick(bar)` / `tickToBar(tick)`
|
|
156
|
+
- `setMeter(numerator, denominator, atTick?: Tick)` / `getMeter(atTick?: Tick)`
|
|
157
|
+
- `removeMeter(atTick: Tick)` / `clearMeters()`
|
|
158
|
+
- `barToTick(bar): Tick` / `tickToBar(tick: Tick)`
|
|
159
|
+
- `timeToTick(seconds): Tick` / `tickToTime(tick: Tick)`
|
|
156
160
|
|
|
157
161
|
**Metronome:**
|
|
158
162
|
- `setMetronomeEnabled(enabled)`
|
package/dist/index.d.mts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import { ClipTrack } from '@waveform-playlist/core';
|
|
2
2
|
import { PlayoutAdapter } from '@waveform-playlist/engine';
|
|
3
3
|
|
|
4
|
+
/** Branded type for tick positions — prevents accidentally passing seconds where ticks are expected. */
|
|
5
|
+
declare const __tick: unique symbol;
|
|
6
|
+
type Tick = number & {
|
|
7
|
+
readonly [__tick]: never;
|
|
8
|
+
};
|
|
9
|
+
/** Branded type for sample counts — prevents accidentally passing seconds where samples are expected. */
|
|
10
|
+
declare const __sample: unique symbol;
|
|
11
|
+
type Sample = number & {
|
|
12
|
+
readonly [__sample]: never;
|
|
13
|
+
};
|
|
4
14
|
interface SchedulerEvent {
|
|
5
|
-
/**
|
|
6
|
-
|
|
15
|
+
/** Tick position (integer) on the timeline */
|
|
16
|
+
tick: Tick;
|
|
7
17
|
}
|
|
8
18
|
interface SchedulerListener<T extends SchedulerEvent> {
|
|
9
|
-
/** Generate events in the
|
|
10
|
-
generate(
|
|
19
|
+
/** Generate events in the tick window [fromTick, toTick) */
|
|
20
|
+
generate(fromTick: Tick, toTick: Tick): T[];
|
|
11
21
|
/** Realize an event (create audio nodes, start sources) */
|
|
12
22
|
consume(event: T): void;
|
|
13
|
-
/** Position jumped (loop/seek) — stop
|
|
14
|
-
|
|
23
|
+
/** Position jumped (loop/seek) — listeners may stop and re-schedule as appropriate
|
|
24
|
+
* (ClipPlayer stops sources and creates mid-clip restarts; MetronomePlayer is a no-op
|
|
25
|
+
* since clicks are short one-shots that finish naturally) */
|
|
26
|
+
onPositionJump(newTick: Tick): void;
|
|
15
27
|
/** Stop all active audio immediately */
|
|
16
28
|
silence(): void;
|
|
17
29
|
}
|
|
@@ -37,7 +49,7 @@ interface MeterSignature {
|
|
|
37
49
|
/** Storage entry for MeterMap */
|
|
38
50
|
interface MeterEntry {
|
|
39
51
|
/** Tick position where this meter starts */
|
|
40
|
-
tick:
|
|
52
|
+
tick: Tick;
|
|
41
53
|
/** Time signature numerator (e.g., 6 in 6/8) */
|
|
42
54
|
numerator: number;
|
|
43
55
|
/** Time signature denominator (e.g., 8 in 6/8) */
|
|
@@ -47,7 +59,7 @@ interface MeterEntry {
|
|
|
47
59
|
}
|
|
48
60
|
interface TempoEntry {
|
|
49
61
|
/** Tick position where this tempo starts */
|
|
50
|
-
tick:
|
|
62
|
+
tick: Tick;
|
|
51
63
|
/** Beats per minute */
|
|
52
64
|
bpm: number;
|
|
53
65
|
/** Cached cumulative seconds up to this tick (for O(log n) lookup). Derived — do not set manually. */
|
|
@@ -58,8 +70,10 @@ interface TransportPosition {
|
|
|
58
70
|
bar: number;
|
|
59
71
|
/** 1-indexed beat within bar */
|
|
60
72
|
beat: number;
|
|
61
|
-
/** Sub-beat tick (0 to ppqn-1)
|
|
62
|
-
|
|
73
|
+
/** Sub-beat tick remainder (0 to ppqn-1). Named subTick to avoid
|
|
74
|
+
* collision with SchedulerEvent.tick (absolute timeline position).
|
|
75
|
+
* Not branded Tick — a remainder within a beat, not an absolute position. */
|
|
76
|
+
subTick: number;
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
declare class Clock {
|
|
@@ -81,10 +95,29 @@ declare class Clock {
|
|
|
81
95
|
isRunning(): boolean;
|
|
82
96
|
}
|
|
83
97
|
|
|
98
|
+
declare class TempoMap {
|
|
99
|
+
private _ppqn;
|
|
100
|
+
private _entries;
|
|
101
|
+
constructor(ppqn?: number, initialBpm?: number);
|
|
102
|
+
getTempo(atTick?: Tick): number;
|
|
103
|
+
setTempo(bpm: number, atTick?: Tick): void;
|
|
104
|
+
ticksToSeconds(ticks: Tick): number;
|
|
105
|
+
secondsToTicks(seconds: number): Tick;
|
|
106
|
+
beatsToSeconds(beats: number): number;
|
|
107
|
+
secondsToBeats(seconds: number): number;
|
|
108
|
+
clearTempos(): void;
|
|
109
|
+
private _ticksToSecondsInternal;
|
|
110
|
+
private _entryAt;
|
|
111
|
+
private _recomputeCache;
|
|
112
|
+
}
|
|
113
|
+
|
|
84
114
|
interface SchedulerOptions {
|
|
85
115
|
lookahead?: number;
|
|
86
|
-
/** Called when the scheduler wraps at loopEnd
|
|
87
|
-
|
|
116
|
+
/** Called when the scheduler wraps at loopEnd.
|
|
117
|
+
* Receives loopStart, loopEnd, and the currentTimeSeconds snapshot from
|
|
118
|
+
* advance() so the Transport can compute the correct clock seek target
|
|
119
|
+
* without re-reading the live AudioContext.currentTime. */
|
|
120
|
+
onLoop?: (loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => void;
|
|
88
121
|
}
|
|
89
122
|
declare class Scheduler<T extends SchedulerEvent> {
|
|
90
123
|
private _lookahead;
|
|
@@ -94,12 +127,18 @@ declare class Scheduler<T extends SchedulerEvent> {
|
|
|
94
127
|
private _loopStart;
|
|
95
128
|
private _loopEnd;
|
|
96
129
|
private _onLoop;
|
|
97
|
-
|
|
130
|
+
private _tempoMap;
|
|
131
|
+
constructor(tempoMap: TempoMap, options?: SchedulerOptions);
|
|
98
132
|
addListener(listener: SchedulerListener<T>): void;
|
|
99
133
|
removeListener(listener: SchedulerListener<T>): void;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
134
|
+
/** Primary API — ticks as source of truth */
|
|
135
|
+
setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void;
|
|
136
|
+
/** Convenience — converts seconds to ticks via TempoMap */
|
|
137
|
+
setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void;
|
|
138
|
+
/** Reset scheduling cursor. Takes seconds (from Clock), converts to ticks. */
|
|
139
|
+
reset(timeSeconds: number): void;
|
|
140
|
+
/** Advance the scheduling window. Takes seconds (from Clock), converts to ticks. */
|
|
141
|
+
advance(currentTimeSeconds: number): void;
|
|
103
142
|
private _generateAndConsume;
|
|
104
143
|
}
|
|
105
144
|
|
|
@@ -115,26 +154,14 @@ declare class Timer {
|
|
|
115
154
|
|
|
116
155
|
declare class SampleTimeline {
|
|
117
156
|
private _sampleRate;
|
|
157
|
+
private _tempoMap;
|
|
118
158
|
constructor(sampleRate: number);
|
|
119
159
|
get sampleRate(): number;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
private _ppqn;
|
|
126
|
-
private _entries;
|
|
127
|
-
constructor(ppqn?: number, initialBpm?: number);
|
|
128
|
-
getTempo(atTick?: number): number;
|
|
129
|
-
setTempo(bpm: number, atTick?: number): void;
|
|
130
|
-
ticksToSeconds(ticks: number): number;
|
|
131
|
-
secondsToTicks(seconds: number): number;
|
|
132
|
-
beatsToSeconds(beats: number): number;
|
|
133
|
-
secondsToBeats(seconds: number): number;
|
|
134
|
-
clearTempos(): void;
|
|
135
|
-
private _ticksToSecondsInternal;
|
|
136
|
-
private _entryAt;
|
|
137
|
-
private _recomputeCache;
|
|
160
|
+
setTempoMap(tempoMap: TempoMap): void;
|
|
161
|
+
samplesToSeconds(samples: Sample): number;
|
|
162
|
+
secondsToSamples(seconds: number): Sample;
|
|
163
|
+
ticksToSamples(ticks: Tick): Sample;
|
|
164
|
+
samplesToTicks(samples: Sample): Tick;
|
|
138
165
|
}
|
|
139
166
|
|
|
140
167
|
declare class MeterMap {
|
|
@@ -142,17 +169,17 @@ declare class MeterMap {
|
|
|
142
169
|
private _entries;
|
|
143
170
|
constructor(ppqn: number, numerator?: number, denominator?: number);
|
|
144
171
|
get ppqn(): number;
|
|
145
|
-
getMeter(atTick?:
|
|
146
|
-
setMeter(numerator: number, denominator: number, atTick?:
|
|
147
|
-
removeMeter(atTick:
|
|
172
|
+
getMeter(atTick?: Tick): MeterSignature;
|
|
173
|
+
setMeter(numerator: number, denominator: number, atTick?: Tick): void;
|
|
174
|
+
removeMeter(atTick: Tick): void;
|
|
148
175
|
clearMeters(): void;
|
|
149
|
-
ticksPerBeat(atTick?:
|
|
150
|
-
ticksPerBar(atTick?:
|
|
151
|
-
barToTick(bar: number):
|
|
152
|
-
tickToBar(tick:
|
|
153
|
-
isBarBoundary(tick:
|
|
176
|
+
ticksPerBeat(atTick?: Tick): number;
|
|
177
|
+
ticksPerBar(atTick?: Tick): number;
|
|
178
|
+
barToTick(bar: number): Tick;
|
|
179
|
+
tickToBar(tick: Tick): number;
|
|
180
|
+
isBarBoundary(tick: Tick): boolean;
|
|
154
181
|
/** Internal: get the full entry at a tick (for MetronomePlayer beat grid anchoring) */
|
|
155
|
-
getEntryAt(tick:
|
|
182
|
+
getEntryAt(tick: Tick): MeterEntry;
|
|
156
183
|
private _entryAt;
|
|
157
184
|
private _ticksPerBarForEntry;
|
|
158
185
|
private _snapToBarBoundary;
|
|
@@ -199,33 +226,40 @@ interface ClipEvent extends SchedulerEvent {
|
|
|
199
226
|
trackId: string;
|
|
200
227
|
clipId: string;
|
|
201
228
|
audioBuffer: AudioBuffer;
|
|
202
|
-
/**
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
|
|
229
|
+
/** Clip position on timeline (integer samples) */
|
|
230
|
+
startSample: Sample;
|
|
231
|
+
/** Offset into audioBuffer (integer samples) */
|
|
232
|
+
offsetSamples: Sample;
|
|
233
|
+
/** Duration to play (integer samples) */
|
|
234
|
+
durationSamples: Sample;
|
|
206
235
|
/** Clip gain multiplier */
|
|
207
236
|
gain: number;
|
|
208
|
-
/** Fade in duration
|
|
209
|
-
|
|
210
|
-
/** Fade out duration
|
|
211
|
-
|
|
237
|
+
/** Fade in duration (integer samples) */
|
|
238
|
+
fadeInDurationSamples: Sample;
|
|
239
|
+
/** Fade out duration (integer samples) */
|
|
240
|
+
fadeOutDurationSamples: Sample;
|
|
212
241
|
}
|
|
213
242
|
declare class ClipPlayer implements SchedulerListener<ClipEvent> {
|
|
214
243
|
private _audioContext;
|
|
215
244
|
private _sampleTimeline;
|
|
245
|
+
private _tempoMap;
|
|
216
246
|
private _toAudioTime;
|
|
217
247
|
private _tracks;
|
|
218
248
|
private _trackNodes;
|
|
219
249
|
private _activeSources;
|
|
220
250
|
private _loopEnabled;
|
|
221
|
-
private
|
|
222
|
-
constructor(audioContext: AudioContext, sampleTimeline: SampleTimeline, toAudioTime: (transportTime: number) => number);
|
|
251
|
+
private _loopEndSamples;
|
|
252
|
+
constructor(audioContext: AudioContext, sampleTimeline: SampleTimeline, tempoMap: TempoMap, toAudioTime: (transportTime: number) => number);
|
|
223
253
|
setTracks(tracks: ClipTrack[], trackNodes: Map<string, TrackNode>): void;
|
|
224
|
-
|
|
254
|
+
/** Set loop region using ticks. startTick is unused — loop clamping only needs
|
|
255
|
+
* the end boundary; mid-clip restart at loopStart is handled by onPositionJump. */
|
|
256
|
+
setLoop(enabled: boolean, _startTick: Tick, endTick: Tick): void;
|
|
257
|
+
/** Set loop region using samples directly */
|
|
258
|
+
setLoopSamples(enabled: boolean, _startSample: Sample, endSample: Sample): void;
|
|
225
259
|
updateTrack(trackId: string, track: ClipTrack): void;
|
|
226
|
-
generate(
|
|
260
|
+
generate(fromTick: Tick, toTick: Tick): ClipEvent[];
|
|
227
261
|
consume(event: ClipEvent): void;
|
|
228
|
-
onPositionJump(
|
|
262
|
+
onPositionJump(newTick: Tick): void;
|
|
229
263
|
silence(): void;
|
|
230
264
|
private _silenceTrack;
|
|
231
265
|
}
|
|
@@ -247,9 +281,9 @@ declare class MetronomePlayer implements SchedulerListener<MetronomeEvent> {
|
|
|
247
281
|
constructor(audioContext: AudioContext, tempoMap: TempoMap, meterMap: MeterMap, destination: AudioNode, toAudioTime: (transportTime: number) => number);
|
|
248
282
|
setEnabled(enabled: boolean): void;
|
|
249
283
|
setClickSounds(accent: AudioBuffer, normal: AudioBuffer): void;
|
|
250
|
-
generate(
|
|
284
|
+
generate(fromTick: Tick, toTick: Tick): MetronomeEvent[];
|
|
251
285
|
consume(event: MetronomeEvent): void;
|
|
252
|
-
onPositionJump(
|
|
286
|
+
onPositionJump(_newTick: Tick): void;
|
|
253
287
|
silence(): void;
|
|
254
288
|
}
|
|
255
289
|
|
|
@@ -279,6 +313,8 @@ declare class Transport {
|
|
|
279
313
|
private _mutedTrackIds;
|
|
280
314
|
private _playing;
|
|
281
315
|
private _endTime;
|
|
316
|
+
private _loopEnabled;
|
|
317
|
+
private _loopStartSeconds;
|
|
282
318
|
private _listeners;
|
|
283
319
|
constructor(audioContext: AudioContext, options?: TransportOptions);
|
|
284
320
|
get audioContext(): AudioContext;
|
|
@@ -297,20 +333,25 @@ declare class Transport {
|
|
|
297
333
|
setTrackMute(trackId: string, muted: boolean): void;
|
|
298
334
|
setTrackSolo(trackId: string, soloed: boolean): void;
|
|
299
335
|
setMasterVolume(volume: number): void;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
336
|
+
/** Primary loop API — ticks as source of truth */
|
|
337
|
+
setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void;
|
|
338
|
+
/** Convenience — converts seconds to ticks */
|
|
339
|
+
setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void;
|
|
340
|
+
/** Convenience — sets loop in samples */
|
|
341
|
+
setLoopSamples(enabled: boolean, startSample: Sample, endSample: Sample): void;
|
|
342
|
+
setTempo(bpm: number, atTick?: Tick): void;
|
|
343
|
+
getTempo(atTick?: Tick): number;
|
|
344
|
+
setMeter(numerator: number, denominator: number, atTick?: Tick): void;
|
|
345
|
+
getMeter(atTick?: Tick): MeterSignature;
|
|
346
|
+
removeMeter(atTick: Tick): void;
|
|
306
347
|
clearMeters(): void;
|
|
307
348
|
clearTempos(): void;
|
|
308
|
-
barToTick(bar: number):
|
|
309
|
-
tickToBar(tick:
|
|
349
|
+
barToTick(bar: number): Tick;
|
|
350
|
+
tickToBar(tick: Tick): number;
|
|
310
351
|
/** Convert transport time (seconds) to tick position, using the tempo map. */
|
|
311
|
-
timeToTick(seconds: number):
|
|
352
|
+
timeToTick(seconds: number): Tick;
|
|
312
353
|
/** Convert tick position to transport time (seconds), using the tempo map. */
|
|
313
|
-
tickToTime(tick:
|
|
354
|
+
tickToTime(tick: Tick): number;
|
|
314
355
|
setMetronomeEnabled(enabled: boolean): void;
|
|
315
356
|
setMetronomeClickSounds(accent: AudioBuffer, normal: AudioBuffer): void;
|
|
316
357
|
connectTrackOutput(trackId: string, node: AudioNode): void;
|
|
@@ -350,4 +391,4 @@ declare class NativePlayoutAdapter implements PlayoutAdapter {
|
|
|
350
391
|
dispose(): void;
|
|
351
392
|
}
|
|
352
393
|
|
|
353
|
-
export { type ClipEvent, ClipPlayer, Clock, MasterNode, type MeterEntry, MeterMap, type MeterSignature, type MetronomeEvent, MetronomePlayer, NativePlayoutAdapter, SampleTimeline, Scheduler, type SchedulerEvent, type SchedulerListener, type SchedulerOptions, type TempoEntry, TempoMap, Timer, TrackNode, Transport, type TransportEvents, type TransportOptions, type TransportPosition };
|
|
394
|
+
export { type ClipEvent, ClipPlayer, Clock, MasterNode, type MeterEntry, MeterMap, type MeterSignature, type MetronomeEvent, MetronomePlayer, NativePlayoutAdapter, type Sample, SampleTimeline, Scheduler, type SchedulerEvent, type SchedulerListener, type SchedulerOptions, type TempoEntry, TempoMap, type Tick, Timer, TrackNode, Transport, type TransportEvents, type TransportOptions, type TransportPosition };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import { ClipTrack } from '@waveform-playlist/core';
|
|
2
2
|
import { PlayoutAdapter } from '@waveform-playlist/engine';
|
|
3
3
|
|
|
4
|
+
/** Branded type for tick positions — prevents accidentally passing seconds where ticks are expected. */
|
|
5
|
+
declare const __tick: unique symbol;
|
|
6
|
+
type Tick = number & {
|
|
7
|
+
readonly [__tick]: never;
|
|
8
|
+
};
|
|
9
|
+
/** Branded type for sample counts — prevents accidentally passing seconds where samples are expected. */
|
|
10
|
+
declare const __sample: unique symbol;
|
|
11
|
+
type Sample = number & {
|
|
12
|
+
readonly [__sample]: never;
|
|
13
|
+
};
|
|
4
14
|
interface SchedulerEvent {
|
|
5
|
-
/**
|
|
6
|
-
|
|
15
|
+
/** Tick position (integer) on the timeline */
|
|
16
|
+
tick: Tick;
|
|
7
17
|
}
|
|
8
18
|
interface SchedulerListener<T extends SchedulerEvent> {
|
|
9
|
-
/** Generate events in the
|
|
10
|
-
generate(
|
|
19
|
+
/** Generate events in the tick window [fromTick, toTick) */
|
|
20
|
+
generate(fromTick: Tick, toTick: Tick): T[];
|
|
11
21
|
/** Realize an event (create audio nodes, start sources) */
|
|
12
22
|
consume(event: T): void;
|
|
13
|
-
/** Position jumped (loop/seek) — stop
|
|
14
|
-
|
|
23
|
+
/** Position jumped (loop/seek) — listeners may stop and re-schedule as appropriate
|
|
24
|
+
* (ClipPlayer stops sources and creates mid-clip restarts; MetronomePlayer is a no-op
|
|
25
|
+
* since clicks are short one-shots that finish naturally) */
|
|
26
|
+
onPositionJump(newTick: Tick): void;
|
|
15
27
|
/** Stop all active audio immediately */
|
|
16
28
|
silence(): void;
|
|
17
29
|
}
|
|
@@ -37,7 +49,7 @@ interface MeterSignature {
|
|
|
37
49
|
/** Storage entry for MeterMap */
|
|
38
50
|
interface MeterEntry {
|
|
39
51
|
/** Tick position where this meter starts */
|
|
40
|
-
tick:
|
|
52
|
+
tick: Tick;
|
|
41
53
|
/** Time signature numerator (e.g., 6 in 6/8) */
|
|
42
54
|
numerator: number;
|
|
43
55
|
/** Time signature denominator (e.g., 8 in 6/8) */
|
|
@@ -47,7 +59,7 @@ interface MeterEntry {
|
|
|
47
59
|
}
|
|
48
60
|
interface TempoEntry {
|
|
49
61
|
/** Tick position where this tempo starts */
|
|
50
|
-
tick:
|
|
62
|
+
tick: Tick;
|
|
51
63
|
/** Beats per minute */
|
|
52
64
|
bpm: number;
|
|
53
65
|
/** Cached cumulative seconds up to this tick (for O(log n) lookup). Derived — do not set manually. */
|
|
@@ -58,8 +70,10 @@ interface TransportPosition {
|
|
|
58
70
|
bar: number;
|
|
59
71
|
/** 1-indexed beat within bar */
|
|
60
72
|
beat: number;
|
|
61
|
-
/** Sub-beat tick (0 to ppqn-1)
|
|
62
|
-
|
|
73
|
+
/** Sub-beat tick remainder (0 to ppqn-1). Named subTick to avoid
|
|
74
|
+
* collision with SchedulerEvent.tick (absolute timeline position).
|
|
75
|
+
* Not branded Tick — a remainder within a beat, not an absolute position. */
|
|
76
|
+
subTick: number;
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
declare class Clock {
|
|
@@ -81,10 +95,29 @@ declare class Clock {
|
|
|
81
95
|
isRunning(): boolean;
|
|
82
96
|
}
|
|
83
97
|
|
|
98
|
+
declare class TempoMap {
|
|
99
|
+
private _ppqn;
|
|
100
|
+
private _entries;
|
|
101
|
+
constructor(ppqn?: number, initialBpm?: number);
|
|
102
|
+
getTempo(atTick?: Tick): number;
|
|
103
|
+
setTempo(bpm: number, atTick?: Tick): void;
|
|
104
|
+
ticksToSeconds(ticks: Tick): number;
|
|
105
|
+
secondsToTicks(seconds: number): Tick;
|
|
106
|
+
beatsToSeconds(beats: number): number;
|
|
107
|
+
secondsToBeats(seconds: number): number;
|
|
108
|
+
clearTempos(): void;
|
|
109
|
+
private _ticksToSecondsInternal;
|
|
110
|
+
private _entryAt;
|
|
111
|
+
private _recomputeCache;
|
|
112
|
+
}
|
|
113
|
+
|
|
84
114
|
interface SchedulerOptions {
|
|
85
115
|
lookahead?: number;
|
|
86
|
-
/** Called when the scheduler wraps at loopEnd
|
|
87
|
-
|
|
116
|
+
/** Called when the scheduler wraps at loopEnd.
|
|
117
|
+
* Receives loopStart, loopEnd, and the currentTimeSeconds snapshot from
|
|
118
|
+
* advance() so the Transport can compute the correct clock seek target
|
|
119
|
+
* without re-reading the live AudioContext.currentTime. */
|
|
120
|
+
onLoop?: (loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => void;
|
|
88
121
|
}
|
|
89
122
|
declare class Scheduler<T extends SchedulerEvent> {
|
|
90
123
|
private _lookahead;
|
|
@@ -94,12 +127,18 @@ declare class Scheduler<T extends SchedulerEvent> {
|
|
|
94
127
|
private _loopStart;
|
|
95
128
|
private _loopEnd;
|
|
96
129
|
private _onLoop;
|
|
97
|
-
|
|
130
|
+
private _tempoMap;
|
|
131
|
+
constructor(tempoMap: TempoMap, options?: SchedulerOptions);
|
|
98
132
|
addListener(listener: SchedulerListener<T>): void;
|
|
99
133
|
removeListener(listener: SchedulerListener<T>): void;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
134
|
+
/** Primary API — ticks as source of truth */
|
|
135
|
+
setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void;
|
|
136
|
+
/** Convenience — converts seconds to ticks via TempoMap */
|
|
137
|
+
setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void;
|
|
138
|
+
/** Reset scheduling cursor. Takes seconds (from Clock), converts to ticks. */
|
|
139
|
+
reset(timeSeconds: number): void;
|
|
140
|
+
/** Advance the scheduling window. Takes seconds (from Clock), converts to ticks. */
|
|
141
|
+
advance(currentTimeSeconds: number): void;
|
|
103
142
|
private _generateAndConsume;
|
|
104
143
|
}
|
|
105
144
|
|
|
@@ -115,26 +154,14 @@ declare class Timer {
|
|
|
115
154
|
|
|
116
155
|
declare class SampleTimeline {
|
|
117
156
|
private _sampleRate;
|
|
157
|
+
private _tempoMap;
|
|
118
158
|
constructor(sampleRate: number);
|
|
119
159
|
get sampleRate(): number;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
private _ppqn;
|
|
126
|
-
private _entries;
|
|
127
|
-
constructor(ppqn?: number, initialBpm?: number);
|
|
128
|
-
getTempo(atTick?: number): number;
|
|
129
|
-
setTempo(bpm: number, atTick?: number): void;
|
|
130
|
-
ticksToSeconds(ticks: number): number;
|
|
131
|
-
secondsToTicks(seconds: number): number;
|
|
132
|
-
beatsToSeconds(beats: number): number;
|
|
133
|
-
secondsToBeats(seconds: number): number;
|
|
134
|
-
clearTempos(): void;
|
|
135
|
-
private _ticksToSecondsInternal;
|
|
136
|
-
private _entryAt;
|
|
137
|
-
private _recomputeCache;
|
|
160
|
+
setTempoMap(tempoMap: TempoMap): void;
|
|
161
|
+
samplesToSeconds(samples: Sample): number;
|
|
162
|
+
secondsToSamples(seconds: number): Sample;
|
|
163
|
+
ticksToSamples(ticks: Tick): Sample;
|
|
164
|
+
samplesToTicks(samples: Sample): Tick;
|
|
138
165
|
}
|
|
139
166
|
|
|
140
167
|
declare class MeterMap {
|
|
@@ -142,17 +169,17 @@ declare class MeterMap {
|
|
|
142
169
|
private _entries;
|
|
143
170
|
constructor(ppqn: number, numerator?: number, denominator?: number);
|
|
144
171
|
get ppqn(): number;
|
|
145
|
-
getMeter(atTick?:
|
|
146
|
-
setMeter(numerator: number, denominator: number, atTick?:
|
|
147
|
-
removeMeter(atTick:
|
|
172
|
+
getMeter(atTick?: Tick): MeterSignature;
|
|
173
|
+
setMeter(numerator: number, denominator: number, atTick?: Tick): void;
|
|
174
|
+
removeMeter(atTick: Tick): void;
|
|
148
175
|
clearMeters(): void;
|
|
149
|
-
ticksPerBeat(atTick?:
|
|
150
|
-
ticksPerBar(atTick?:
|
|
151
|
-
barToTick(bar: number):
|
|
152
|
-
tickToBar(tick:
|
|
153
|
-
isBarBoundary(tick:
|
|
176
|
+
ticksPerBeat(atTick?: Tick): number;
|
|
177
|
+
ticksPerBar(atTick?: Tick): number;
|
|
178
|
+
barToTick(bar: number): Tick;
|
|
179
|
+
tickToBar(tick: Tick): number;
|
|
180
|
+
isBarBoundary(tick: Tick): boolean;
|
|
154
181
|
/** Internal: get the full entry at a tick (for MetronomePlayer beat grid anchoring) */
|
|
155
|
-
getEntryAt(tick:
|
|
182
|
+
getEntryAt(tick: Tick): MeterEntry;
|
|
156
183
|
private _entryAt;
|
|
157
184
|
private _ticksPerBarForEntry;
|
|
158
185
|
private _snapToBarBoundary;
|
|
@@ -199,33 +226,40 @@ interface ClipEvent extends SchedulerEvent {
|
|
|
199
226
|
trackId: string;
|
|
200
227
|
clipId: string;
|
|
201
228
|
audioBuffer: AudioBuffer;
|
|
202
|
-
/**
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
|
|
229
|
+
/** Clip position on timeline (integer samples) */
|
|
230
|
+
startSample: Sample;
|
|
231
|
+
/** Offset into audioBuffer (integer samples) */
|
|
232
|
+
offsetSamples: Sample;
|
|
233
|
+
/** Duration to play (integer samples) */
|
|
234
|
+
durationSamples: Sample;
|
|
206
235
|
/** Clip gain multiplier */
|
|
207
236
|
gain: number;
|
|
208
|
-
/** Fade in duration
|
|
209
|
-
|
|
210
|
-
/** Fade out duration
|
|
211
|
-
|
|
237
|
+
/** Fade in duration (integer samples) */
|
|
238
|
+
fadeInDurationSamples: Sample;
|
|
239
|
+
/** Fade out duration (integer samples) */
|
|
240
|
+
fadeOutDurationSamples: Sample;
|
|
212
241
|
}
|
|
213
242
|
declare class ClipPlayer implements SchedulerListener<ClipEvent> {
|
|
214
243
|
private _audioContext;
|
|
215
244
|
private _sampleTimeline;
|
|
245
|
+
private _tempoMap;
|
|
216
246
|
private _toAudioTime;
|
|
217
247
|
private _tracks;
|
|
218
248
|
private _trackNodes;
|
|
219
249
|
private _activeSources;
|
|
220
250
|
private _loopEnabled;
|
|
221
|
-
private
|
|
222
|
-
constructor(audioContext: AudioContext, sampleTimeline: SampleTimeline, toAudioTime: (transportTime: number) => number);
|
|
251
|
+
private _loopEndSamples;
|
|
252
|
+
constructor(audioContext: AudioContext, sampleTimeline: SampleTimeline, tempoMap: TempoMap, toAudioTime: (transportTime: number) => number);
|
|
223
253
|
setTracks(tracks: ClipTrack[], trackNodes: Map<string, TrackNode>): void;
|
|
224
|
-
|
|
254
|
+
/** Set loop region using ticks. startTick is unused — loop clamping only needs
|
|
255
|
+
* the end boundary; mid-clip restart at loopStart is handled by onPositionJump. */
|
|
256
|
+
setLoop(enabled: boolean, _startTick: Tick, endTick: Tick): void;
|
|
257
|
+
/** Set loop region using samples directly */
|
|
258
|
+
setLoopSamples(enabled: boolean, _startSample: Sample, endSample: Sample): void;
|
|
225
259
|
updateTrack(trackId: string, track: ClipTrack): void;
|
|
226
|
-
generate(
|
|
260
|
+
generate(fromTick: Tick, toTick: Tick): ClipEvent[];
|
|
227
261
|
consume(event: ClipEvent): void;
|
|
228
|
-
onPositionJump(
|
|
262
|
+
onPositionJump(newTick: Tick): void;
|
|
229
263
|
silence(): void;
|
|
230
264
|
private _silenceTrack;
|
|
231
265
|
}
|
|
@@ -247,9 +281,9 @@ declare class MetronomePlayer implements SchedulerListener<MetronomeEvent> {
|
|
|
247
281
|
constructor(audioContext: AudioContext, tempoMap: TempoMap, meterMap: MeterMap, destination: AudioNode, toAudioTime: (transportTime: number) => number);
|
|
248
282
|
setEnabled(enabled: boolean): void;
|
|
249
283
|
setClickSounds(accent: AudioBuffer, normal: AudioBuffer): void;
|
|
250
|
-
generate(
|
|
284
|
+
generate(fromTick: Tick, toTick: Tick): MetronomeEvent[];
|
|
251
285
|
consume(event: MetronomeEvent): void;
|
|
252
|
-
onPositionJump(
|
|
286
|
+
onPositionJump(_newTick: Tick): void;
|
|
253
287
|
silence(): void;
|
|
254
288
|
}
|
|
255
289
|
|
|
@@ -279,6 +313,8 @@ declare class Transport {
|
|
|
279
313
|
private _mutedTrackIds;
|
|
280
314
|
private _playing;
|
|
281
315
|
private _endTime;
|
|
316
|
+
private _loopEnabled;
|
|
317
|
+
private _loopStartSeconds;
|
|
282
318
|
private _listeners;
|
|
283
319
|
constructor(audioContext: AudioContext, options?: TransportOptions);
|
|
284
320
|
get audioContext(): AudioContext;
|
|
@@ -297,20 +333,25 @@ declare class Transport {
|
|
|
297
333
|
setTrackMute(trackId: string, muted: boolean): void;
|
|
298
334
|
setTrackSolo(trackId: string, soloed: boolean): void;
|
|
299
335
|
setMasterVolume(volume: number): void;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
336
|
+
/** Primary loop API — ticks as source of truth */
|
|
337
|
+
setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void;
|
|
338
|
+
/** Convenience — converts seconds to ticks */
|
|
339
|
+
setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void;
|
|
340
|
+
/** Convenience — sets loop in samples */
|
|
341
|
+
setLoopSamples(enabled: boolean, startSample: Sample, endSample: Sample): void;
|
|
342
|
+
setTempo(bpm: number, atTick?: Tick): void;
|
|
343
|
+
getTempo(atTick?: Tick): number;
|
|
344
|
+
setMeter(numerator: number, denominator: number, atTick?: Tick): void;
|
|
345
|
+
getMeter(atTick?: Tick): MeterSignature;
|
|
346
|
+
removeMeter(atTick: Tick): void;
|
|
306
347
|
clearMeters(): void;
|
|
307
348
|
clearTempos(): void;
|
|
308
|
-
barToTick(bar: number):
|
|
309
|
-
tickToBar(tick:
|
|
349
|
+
barToTick(bar: number): Tick;
|
|
350
|
+
tickToBar(tick: Tick): number;
|
|
310
351
|
/** Convert transport time (seconds) to tick position, using the tempo map. */
|
|
311
|
-
timeToTick(seconds: number):
|
|
352
|
+
timeToTick(seconds: number): Tick;
|
|
312
353
|
/** Convert tick position to transport time (seconds), using the tempo map. */
|
|
313
|
-
tickToTime(tick:
|
|
354
|
+
tickToTime(tick: Tick): number;
|
|
314
355
|
setMetronomeEnabled(enabled: boolean): void;
|
|
315
356
|
setMetronomeClickSounds(accent: AudioBuffer, normal: AudioBuffer): void;
|
|
316
357
|
connectTrackOutput(trackId: string, node: AudioNode): void;
|
|
@@ -350,4 +391,4 @@ declare class NativePlayoutAdapter implements PlayoutAdapter {
|
|
|
350
391
|
dispose(): void;
|
|
351
392
|
}
|
|
352
393
|
|
|
353
|
-
export { type ClipEvent, ClipPlayer, Clock, MasterNode, type MeterEntry, MeterMap, type MeterSignature, type MetronomeEvent, MetronomePlayer, NativePlayoutAdapter, SampleTimeline, Scheduler, type SchedulerEvent, type SchedulerListener, type SchedulerOptions, type TempoEntry, TempoMap, Timer, TrackNode, Transport, type TransportEvents, type TransportOptions, type TransportPosition };
|
|
394
|
+
export { type ClipEvent, ClipPlayer, Clock, MasterNode, type MeterEntry, MeterMap, type MeterSignature, type MetronomeEvent, MetronomePlayer, NativePlayoutAdapter, type Sample, SampleTimeline, Scheduler, type SchedulerEvent, type SchedulerListener, type SchedulerOptions, type TempoEntry, TempoMap, type Tick, Timer, TrackNode, Transport, type TransportEvents, type TransportOptions, type TransportPosition };
|