@dawcore/components 0.0.1 → 0.0.2
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 +186 -9
- package/dist/index.d.ts +186 -9
- package/dist/index.js +868 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +890 -105
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.d.mts
CHANGED
|
@@ -130,6 +130,12 @@ declare class DawTransportButton extends LitElement {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
declare class DawPlayButtonElement extends DawTransportButton {
|
|
133
|
+
private _isRecording;
|
|
134
|
+
private _targetRef;
|
|
135
|
+
private _onRecStart;
|
|
136
|
+
private _onRecEnd;
|
|
137
|
+
connectedCallback(): void;
|
|
138
|
+
disconnectedCallback(): void;
|
|
133
139
|
render(): lit.TemplateResult<1>;
|
|
134
140
|
private _onClick;
|
|
135
141
|
}
|
|
@@ -140,6 +146,14 @@ declare global {
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
declare class DawPauseButtonElement extends DawTransportButton {
|
|
149
|
+
private _isPaused;
|
|
150
|
+
private _isRecording;
|
|
151
|
+
private _targetRef;
|
|
152
|
+
private _onRecStart;
|
|
153
|
+
private _onRecEnd;
|
|
154
|
+
static styles: lit.CSSResultGroup[];
|
|
155
|
+
connectedCallback(): void;
|
|
156
|
+
disconnectedCallback(): void;
|
|
143
157
|
render(): lit.TemplateResult<1>;
|
|
144
158
|
private _onClick;
|
|
145
159
|
}
|
|
@@ -196,19 +210,25 @@ declare class PeakPipeline {
|
|
|
196
210
|
private _worker;
|
|
197
211
|
private _cache;
|
|
198
212
|
private _inflight;
|
|
213
|
+
private _baseScale;
|
|
214
|
+
private _bits;
|
|
215
|
+
constructor(baseScale?: number, bits?: 8 | 16);
|
|
199
216
|
/**
|
|
200
217
|
* Generate PeakData for a clip from its AudioBuffer.
|
|
201
218
|
* Uses cached WaveformData when available; otherwise generates via worker.
|
|
202
|
-
*
|
|
219
|
+
* Worker generates at baseScale (default 128); extractPeaks resamples to the requested zoom.
|
|
203
220
|
*/
|
|
204
|
-
generatePeaks(audioBuffer: AudioBuffer, samplesPerPixel: number, isMono: boolean): Promise<PeakData>;
|
|
221
|
+
generatePeaks(audioBuffer: AudioBuffer, samplesPerPixel: number, isMono: boolean, offsetSamples?: number, durationSamples?: number): Promise<PeakData>;
|
|
205
222
|
/**
|
|
206
223
|
* Re-extract peaks for all clips at a new zoom level using cached WaveformData.
|
|
207
224
|
* Only works for zoom levels coarser than (or equal to) the cached base scale.
|
|
208
225
|
* Returns a new Map of clipId → PeakData. Clips without cached data or where
|
|
209
226
|
* the target scale is finer than the cached base are skipped.
|
|
210
227
|
*/
|
|
211
|
-
reextractPeaks(clipBuffers: ReadonlyMap<string, AudioBuffer>, samplesPerPixel: number, isMono: boolean
|
|
228
|
+
reextractPeaks(clipBuffers: ReadonlyMap<string, AudioBuffer>, samplesPerPixel: number, isMono: boolean, clipOffsets?: ReadonlyMap<string, {
|
|
229
|
+
offsetSamples: number;
|
|
230
|
+
durationSamples: number;
|
|
231
|
+
}>): Map<string, PeakData>;
|
|
212
232
|
terminate(): void;
|
|
213
233
|
private _getWaveformData;
|
|
214
234
|
}
|
|
@@ -240,6 +260,8 @@ interface RecordingOptions {
|
|
|
240
260
|
trackId?: string;
|
|
241
261
|
bits?: 8 | 16;
|
|
242
262
|
startSample?: number;
|
|
263
|
+
/** Start playback during recording so user hears existing tracks. */
|
|
264
|
+
overdub?: boolean;
|
|
243
265
|
}
|
|
244
266
|
interface RecordingSession {
|
|
245
267
|
readonly trackId: string;
|
|
@@ -259,12 +281,16 @@ interface RecordingSession {
|
|
|
259
281
|
readonly channelCount: number;
|
|
260
282
|
readonly bits: Bits;
|
|
261
283
|
isFirstMessage: boolean;
|
|
284
|
+
/** Latency samples to skip in live preview (outputLatency + lookAhead). */
|
|
285
|
+
readonly latencySamples: number;
|
|
286
|
+
readonly wasOverdub: boolean;
|
|
262
287
|
/** Stored so it can be removed on stop/cleanup — not just when stream ends. */
|
|
263
288
|
readonly _onTrackEnded: (() => void) | null;
|
|
264
289
|
readonly _audioTrack: MediaStreamTrack | null;
|
|
265
290
|
}
|
|
266
291
|
/** Readonly view of a recording session for external consumers. */
|
|
267
|
-
type ReadonlyRecordingSession = Readonly<Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack'>> & {
|
|
292
|
+
type ReadonlyRecordingSession = Readonly<Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack' | 'latencySamples'>> & {
|
|
293
|
+
readonly latencySamples: number;
|
|
268
294
|
readonly chunks: ReadonlyArray<ReadonlyArray<Float32Array>>;
|
|
269
295
|
readonly peaks: ReadonlyArray<Int8Array | Int16Array>;
|
|
270
296
|
};
|
|
@@ -276,7 +302,9 @@ interface RecordingHost extends ReactiveControllerHost {
|
|
|
276
302
|
readonly _currentTime: number;
|
|
277
303
|
readonly shadowRoot: ShadowRoot | null;
|
|
278
304
|
resolveAudioContextSampleRate(rate: number): void;
|
|
279
|
-
_addRecordedClip?(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number): void;
|
|
305
|
+
_addRecordedClip?(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number, offsetSamples?: number): void;
|
|
306
|
+
play?(startTime?: number): Promise<void>;
|
|
307
|
+
stop?(): void;
|
|
280
308
|
dispatchEvent(event: Event): boolean;
|
|
281
309
|
}
|
|
282
310
|
declare class RecordingController implements ReactiveController {
|
|
@@ -289,6 +317,8 @@ declare class RecordingController implements ReactiveController {
|
|
|
289
317
|
get isRecording(): boolean;
|
|
290
318
|
getSession(trackId: string): ReadonlyRecordingSession | undefined;
|
|
291
319
|
startRecording(stream: MediaStream, options?: RecordingOptions): Promise<void>;
|
|
320
|
+
pauseRecording(trackId?: string): void;
|
|
321
|
+
resumeRecording(trackId?: string): void;
|
|
292
322
|
stopRecording(trackId?: string): void;
|
|
293
323
|
private _onWorkletMessage;
|
|
294
324
|
private _createClipFromRecording;
|
|
@@ -296,6 +326,85 @@ declare class RecordingController implements ReactiveController {
|
|
|
296
326
|
private _cleanupSession;
|
|
297
327
|
}
|
|
298
328
|
|
|
329
|
+
/** Snapshot of a clip's bounds for trim constraint computation. */
|
|
330
|
+
interface ClipBounds {
|
|
331
|
+
readonly offsetSamples: number;
|
|
332
|
+
readonly durationSamples: number;
|
|
333
|
+
readonly startSample: number;
|
|
334
|
+
readonly sourceDurationSamples: number;
|
|
335
|
+
}
|
|
336
|
+
/** Narrow engine contract for clip move/trim interactions. */
|
|
337
|
+
interface ClipEngineContract {
|
|
338
|
+
moveClip(trackId: string, clipId: string, deltaSamples: number, skipAdapter?: boolean): void;
|
|
339
|
+
trimClip(trackId: string, clipId: string, boundary: 'left' | 'right', deltaSamples: number, skipAdapter?: boolean): void;
|
|
340
|
+
updateTrack(trackId: string): void;
|
|
341
|
+
/** Get a clip's full bounds for trim constraint computation. */
|
|
342
|
+
getClipBounds(trackId: string, clipId: string): ClipBounds | null;
|
|
343
|
+
/** Constrain a trim delta using the engine's collision/bounds logic. */
|
|
344
|
+
constrainTrimDelta(trackId: string, clipId: string, boundary: 'left' | 'right', deltaSamples: number): number;
|
|
345
|
+
}
|
|
346
|
+
/** Peak data returned by reextractClipPeaks for imperative waveform updates. */
|
|
347
|
+
interface ClipPeakSlice {
|
|
348
|
+
data: ArrayLike<number>[];
|
|
349
|
+
length: number;
|
|
350
|
+
}
|
|
351
|
+
/** Host interface required by ClipPointerHandler. */
|
|
352
|
+
interface ClipPointerHost {
|
|
353
|
+
readonly samplesPerPixel: number;
|
|
354
|
+
readonly effectiveSampleRate: number;
|
|
355
|
+
readonly interactiveClips: boolean;
|
|
356
|
+
readonly engine: ClipEngineContract | null;
|
|
357
|
+
readonly shadowRoot: ShadowRoot | null;
|
|
358
|
+
dispatchEvent(event: Event): boolean;
|
|
359
|
+
/** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */
|
|
360
|
+
reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number): ClipPeakSlice | null;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Handles pointer interactions for clip move and trim drag operations.
|
|
364
|
+
* Converts pixel deltas to sample deltas and delegates to the engine.
|
|
365
|
+
*
|
|
366
|
+
* Move: sends incremental deltas per-frame with skipAdapter=true (engine shifts
|
|
367
|
+
* startSample additively without touching audio adapter). Adapter synced once
|
|
368
|
+
* via updateTrack() at drag end.
|
|
369
|
+
* Trim: updates clip container CSS imperatively during drag for visual feedback,
|
|
370
|
+
* then applies cumulative delta to engine once at drag end.
|
|
371
|
+
*/
|
|
372
|
+
declare class ClipPointerHandler {
|
|
373
|
+
private _host;
|
|
374
|
+
private _mode;
|
|
375
|
+
private _clipId;
|
|
376
|
+
private _trackId;
|
|
377
|
+
private _startPx;
|
|
378
|
+
private _isDragging;
|
|
379
|
+
private _lastDeltaPx;
|
|
380
|
+
private _cumulativeDeltaSamples;
|
|
381
|
+
private _clipContainer;
|
|
382
|
+
private _boundaryEl;
|
|
383
|
+
private _originalLeft;
|
|
384
|
+
private _originalWidth;
|
|
385
|
+
private _originalOffsetSamples;
|
|
386
|
+
private _originalDurationSamples;
|
|
387
|
+
constructor(host: ClipPointerHost);
|
|
388
|
+
/** Returns true if a drag interaction is currently in progress. */
|
|
389
|
+
get isActive(): boolean;
|
|
390
|
+
/**
|
|
391
|
+
* Attempts to handle a pointerdown event on the given target element.
|
|
392
|
+
* Returns true if the target is a recognized clip interaction element.
|
|
393
|
+
*/
|
|
394
|
+
tryHandle(target: Element, e: PointerEvent): boolean;
|
|
395
|
+
private _beginDrag;
|
|
396
|
+
/** Processes pointermove events during an active drag. */
|
|
397
|
+
onPointerMove(e: PointerEvent): void;
|
|
398
|
+
/** Processes pointerup events to finalize and dispatch result events. */
|
|
399
|
+
onPointerUp(_e: PointerEvent): void;
|
|
400
|
+
/** Re-extract peaks from cache and set on waveform elements during trim drag.
|
|
401
|
+
* Returns true if peaks were successfully updated. */
|
|
402
|
+
private _updateWaveformPeaks;
|
|
403
|
+
/** Restore clip container CSS to original values after trim visual preview. */
|
|
404
|
+
private _restoreTrimVisual;
|
|
405
|
+
private _reset;
|
|
406
|
+
}
|
|
407
|
+
|
|
299
408
|
interface DawSelectionDetail {
|
|
300
409
|
start: number;
|
|
301
410
|
end: number;
|
|
@@ -342,11 +451,33 @@ interface DawRecordingCompleteDetail {
|
|
|
342
451
|
audioBuffer: AudioBuffer;
|
|
343
452
|
startSample: number;
|
|
344
453
|
durationSamples: number;
|
|
454
|
+
offsetSamples: number;
|
|
345
455
|
}
|
|
346
456
|
interface DawRecordingErrorDetail {
|
|
347
457
|
trackId: string;
|
|
348
458
|
error: unknown;
|
|
349
459
|
}
|
|
460
|
+
interface DawClipMoveDetail {
|
|
461
|
+
readonly trackId: string;
|
|
462
|
+
readonly clipId: string;
|
|
463
|
+
/** Requested cumulative delta. May exceed actual applied movement due to
|
|
464
|
+
* collision constraints. Query engine state for actual clip positions. */
|
|
465
|
+
readonly deltaSamples: number;
|
|
466
|
+
}
|
|
467
|
+
interface DawClipTrimDetail {
|
|
468
|
+
readonly trackId: string;
|
|
469
|
+
readonly clipId: string;
|
|
470
|
+
readonly boundary: 'left' | 'right';
|
|
471
|
+
/** Constrained cumulative delta applied by the engine. Already clamped
|
|
472
|
+
* by collision and boundary constraints during drag. */
|
|
473
|
+
readonly deltaSamples: number;
|
|
474
|
+
}
|
|
475
|
+
interface DawClipSplitDetail {
|
|
476
|
+
readonly trackId: string;
|
|
477
|
+
readonly originalClipId: string;
|
|
478
|
+
readonly leftClipId: string;
|
|
479
|
+
readonly rightClipId: string;
|
|
480
|
+
}
|
|
350
481
|
interface DawEventMap {
|
|
351
482
|
'daw-selection': CustomEvent<DawSelectionDetail>;
|
|
352
483
|
'daw-seek': CustomEvent<DawSeekDetail>;
|
|
@@ -365,6 +496,9 @@ interface DawEventMap {
|
|
|
365
496
|
'daw-recording-start': CustomEvent<DawRecordingStartDetail>;
|
|
366
497
|
'daw-recording-complete': CustomEvent<DawRecordingCompleteDetail>;
|
|
367
498
|
'daw-recording-error': CustomEvent<DawRecordingErrorDetail>;
|
|
499
|
+
'daw-clip-move': CustomEvent<DawClipMoveDetail>;
|
|
500
|
+
'daw-clip-trim': CustomEvent<DawClipTrimDetail>;
|
|
501
|
+
'daw-clip-split': CustomEvent<DawClipSplitDetail>;
|
|
368
502
|
}
|
|
369
503
|
type DawEvent<K extends keyof DawEventMap> = DawEventMap[K];
|
|
370
504
|
interface LoadFilesResult {
|
|
@@ -383,6 +517,9 @@ declare class DawEditorElement extends LitElement {
|
|
|
383
517
|
barWidth: number;
|
|
384
518
|
barGap: number;
|
|
385
519
|
fileDrop: boolean;
|
|
520
|
+
clipHeaders: boolean;
|
|
521
|
+
clipHeaderHeight: number;
|
|
522
|
+
interactiveClips: boolean;
|
|
386
523
|
/** Initial sample rate hint. Overridden by decoded audio buffer's actual rate. */
|
|
387
524
|
sampleRate: number;
|
|
388
525
|
/** Resolved sample rate — falls back to sampleRate property until first audio decode. */
|
|
@@ -399,15 +536,26 @@ declare class DawEditorElement extends LitElement {
|
|
|
399
536
|
_currentTime: number;
|
|
400
537
|
_engine: PlaylistEngine | null;
|
|
401
538
|
private _enginePromise;
|
|
402
|
-
private _audioInitialized;
|
|
403
539
|
_audioCache: Map<string, Promise<AudioBuffer>>;
|
|
404
540
|
_clipBuffers: Map<string, AudioBuffer>;
|
|
541
|
+
_clipOffsets: Map<string, {
|
|
542
|
+
offsetSamples: number;
|
|
543
|
+
durationSamples: number;
|
|
544
|
+
}>;
|
|
405
545
|
_peakPipeline: PeakPipeline;
|
|
406
546
|
private _trackElements;
|
|
407
547
|
private _childObserver;
|
|
408
548
|
private _audioResume;
|
|
409
549
|
eagerResume?: string;
|
|
410
550
|
private _recordingController;
|
|
551
|
+
private _clipPointer;
|
|
552
|
+
get _clipHandler(): ClipPointerHandler | null;
|
|
553
|
+
get engine(): PlaylistEngine | null;
|
|
554
|
+
/** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */
|
|
555
|
+
reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number): {
|
|
556
|
+
data: Peaks[];
|
|
557
|
+
length: number;
|
|
558
|
+
} | null;
|
|
411
559
|
private _pointer;
|
|
412
560
|
private _viewport;
|
|
413
561
|
static styles: lit.CSSResult[];
|
|
@@ -442,14 +590,20 @@ declare class DawEditorElement extends LitElement {
|
|
|
442
590
|
private _onDragLeave;
|
|
443
591
|
private _onDrop;
|
|
444
592
|
loadFiles(files: FileList | File[]): Promise<LoadFilesResult>;
|
|
445
|
-
play(): Promise<void>;
|
|
593
|
+
play(startTime?: number): Promise<void>;
|
|
446
594
|
pause(): void;
|
|
447
595
|
stop(): void;
|
|
448
596
|
seekTo(time: number): void;
|
|
597
|
+
/** Split the clip under the playhead on the selected track. */
|
|
598
|
+
splitAtPlayhead(): boolean;
|
|
599
|
+
private _onKeyDown;
|
|
449
600
|
recordingStream: MediaStream | null;
|
|
601
|
+
get currentTime(): number;
|
|
450
602
|
get isRecording(): boolean;
|
|
603
|
+
pauseRecording(): void;
|
|
604
|
+
resumeRecording(): void;
|
|
451
605
|
stopRecording(): void;
|
|
452
|
-
_addRecordedClip(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number): void;
|
|
606
|
+
_addRecordedClip(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number, offsetSamples?: number): void;
|
|
453
607
|
startRecording(stream?: MediaStream, options?: RecordingOptions): Promise<void>;
|
|
454
608
|
private _renderRecordingPreview;
|
|
455
609
|
_startPlayhead(): void;
|
|
@@ -538,4 +692,27 @@ interface PointerEngineContract {
|
|
|
538
692
|
selectTrack(trackId: string | null): void;
|
|
539
693
|
}
|
|
540
694
|
|
|
541
|
-
|
|
695
|
+
/** Narrow engine contract for split operations. */
|
|
696
|
+
interface SplitEngineContract {
|
|
697
|
+
getState(): {
|
|
698
|
+
selectedTrackId: string | null;
|
|
699
|
+
tracks: ClipTrack[];
|
|
700
|
+
};
|
|
701
|
+
splitClip(trackId: string, clipId: string, atSample: number): void;
|
|
702
|
+
}
|
|
703
|
+
/** Host interface for splitAtPlayhead. */
|
|
704
|
+
interface SplitHost {
|
|
705
|
+
readonly effectiveSampleRate: number;
|
|
706
|
+
readonly currentTime: number;
|
|
707
|
+
readonly engine: SplitEngineContract | null;
|
|
708
|
+
dispatchEvent(event: Event): boolean;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Splits the clip under the playhead on the selected track.
|
|
712
|
+
*
|
|
713
|
+
* Returns true if the split occurred and dispatched a daw-clip-split event.
|
|
714
|
+
* Returns false for any guard failure or engine no-op.
|
|
715
|
+
*/
|
|
716
|
+
declare function splitAtPlayhead(host: SplitHost): boolean;
|
|
717
|
+
|
|
718
|
+
export { AudioResumeController, type ClipDescriptor, type ClipEngineContract, ClipPointerHandler, type ClipPointerHost, DawClipElement, type DawClipMoveDetail, type DawClipSplitDetail, type DawClipTrimDetail, DawEditorElement, type DawErrorDetail, type DawEvent, type DawEventMap, type DawFilesLoadErrorDetail, DawPauseButtonElement, DawPlayButtonElement, DawPlayheadElement, DawRecordButtonElement, type DawRecordingCompleteDetail, type DawRecordingErrorDetail, type DawRecordingStartDetail, DawRulerElement, type DawSeekDetail, type DawSelectionDetail, DawSelectionElement, DawStopButtonElement, type DawTrackConnectedDetail, type DawTrackControlDetail, DawTrackControlsElement, DawTrackElement, type DawTrackErrorDetail, type DawTrackIdDetail, type DawTrackRemoveDetail, type DawTrackSelectDetail, DawTransportButton, DawTransportElement, DawWaveformElement, type LoadFilesResult, type PointerEngineContract, RecordingController, type RecordingOptions, type RecordingSession, type SplitEngineContract, type SplitHost, type TrackDescriptor, splitAtPlayhead };
|
package/dist/index.d.ts
CHANGED
|
@@ -130,6 +130,12 @@ declare class DawTransportButton extends LitElement {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
declare class DawPlayButtonElement extends DawTransportButton {
|
|
133
|
+
private _isRecording;
|
|
134
|
+
private _targetRef;
|
|
135
|
+
private _onRecStart;
|
|
136
|
+
private _onRecEnd;
|
|
137
|
+
connectedCallback(): void;
|
|
138
|
+
disconnectedCallback(): void;
|
|
133
139
|
render(): lit.TemplateResult<1>;
|
|
134
140
|
private _onClick;
|
|
135
141
|
}
|
|
@@ -140,6 +146,14 @@ declare global {
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
declare class DawPauseButtonElement extends DawTransportButton {
|
|
149
|
+
private _isPaused;
|
|
150
|
+
private _isRecording;
|
|
151
|
+
private _targetRef;
|
|
152
|
+
private _onRecStart;
|
|
153
|
+
private _onRecEnd;
|
|
154
|
+
static styles: lit.CSSResultGroup[];
|
|
155
|
+
connectedCallback(): void;
|
|
156
|
+
disconnectedCallback(): void;
|
|
143
157
|
render(): lit.TemplateResult<1>;
|
|
144
158
|
private _onClick;
|
|
145
159
|
}
|
|
@@ -196,19 +210,25 @@ declare class PeakPipeline {
|
|
|
196
210
|
private _worker;
|
|
197
211
|
private _cache;
|
|
198
212
|
private _inflight;
|
|
213
|
+
private _baseScale;
|
|
214
|
+
private _bits;
|
|
215
|
+
constructor(baseScale?: number, bits?: 8 | 16);
|
|
199
216
|
/**
|
|
200
217
|
* Generate PeakData for a clip from its AudioBuffer.
|
|
201
218
|
* Uses cached WaveformData when available; otherwise generates via worker.
|
|
202
|
-
*
|
|
219
|
+
* Worker generates at baseScale (default 128); extractPeaks resamples to the requested zoom.
|
|
203
220
|
*/
|
|
204
|
-
generatePeaks(audioBuffer: AudioBuffer, samplesPerPixel: number, isMono: boolean): Promise<PeakData>;
|
|
221
|
+
generatePeaks(audioBuffer: AudioBuffer, samplesPerPixel: number, isMono: boolean, offsetSamples?: number, durationSamples?: number): Promise<PeakData>;
|
|
205
222
|
/**
|
|
206
223
|
* Re-extract peaks for all clips at a new zoom level using cached WaveformData.
|
|
207
224
|
* Only works for zoom levels coarser than (or equal to) the cached base scale.
|
|
208
225
|
* Returns a new Map of clipId → PeakData. Clips without cached data or where
|
|
209
226
|
* the target scale is finer than the cached base are skipped.
|
|
210
227
|
*/
|
|
211
|
-
reextractPeaks(clipBuffers: ReadonlyMap<string, AudioBuffer>, samplesPerPixel: number, isMono: boolean
|
|
228
|
+
reextractPeaks(clipBuffers: ReadonlyMap<string, AudioBuffer>, samplesPerPixel: number, isMono: boolean, clipOffsets?: ReadonlyMap<string, {
|
|
229
|
+
offsetSamples: number;
|
|
230
|
+
durationSamples: number;
|
|
231
|
+
}>): Map<string, PeakData>;
|
|
212
232
|
terminate(): void;
|
|
213
233
|
private _getWaveformData;
|
|
214
234
|
}
|
|
@@ -240,6 +260,8 @@ interface RecordingOptions {
|
|
|
240
260
|
trackId?: string;
|
|
241
261
|
bits?: 8 | 16;
|
|
242
262
|
startSample?: number;
|
|
263
|
+
/** Start playback during recording so user hears existing tracks. */
|
|
264
|
+
overdub?: boolean;
|
|
243
265
|
}
|
|
244
266
|
interface RecordingSession {
|
|
245
267
|
readonly trackId: string;
|
|
@@ -259,12 +281,16 @@ interface RecordingSession {
|
|
|
259
281
|
readonly channelCount: number;
|
|
260
282
|
readonly bits: Bits;
|
|
261
283
|
isFirstMessage: boolean;
|
|
284
|
+
/** Latency samples to skip in live preview (outputLatency + lookAhead). */
|
|
285
|
+
readonly latencySamples: number;
|
|
286
|
+
readonly wasOverdub: boolean;
|
|
262
287
|
/** Stored so it can be removed on stop/cleanup — not just when stream ends. */
|
|
263
288
|
readonly _onTrackEnded: (() => void) | null;
|
|
264
289
|
readonly _audioTrack: MediaStreamTrack | null;
|
|
265
290
|
}
|
|
266
291
|
/** Readonly view of a recording session for external consumers. */
|
|
267
|
-
type ReadonlyRecordingSession = Readonly<Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack'>> & {
|
|
292
|
+
type ReadonlyRecordingSession = Readonly<Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack' | 'latencySamples'>> & {
|
|
293
|
+
readonly latencySamples: number;
|
|
268
294
|
readonly chunks: ReadonlyArray<ReadonlyArray<Float32Array>>;
|
|
269
295
|
readonly peaks: ReadonlyArray<Int8Array | Int16Array>;
|
|
270
296
|
};
|
|
@@ -276,7 +302,9 @@ interface RecordingHost extends ReactiveControllerHost {
|
|
|
276
302
|
readonly _currentTime: number;
|
|
277
303
|
readonly shadowRoot: ShadowRoot | null;
|
|
278
304
|
resolveAudioContextSampleRate(rate: number): void;
|
|
279
|
-
_addRecordedClip?(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number): void;
|
|
305
|
+
_addRecordedClip?(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number, offsetSamples?: number): void;
|
|
306
|
+
play?(startTime?: number): Promise<void>;
|
|
307
|
+
stop?(): void;
|
|
280
308
|
dispatchEvent(event: Event): boolean;
|
|
281
309
|
}
|
|
282
310
|
declare class RecordingController implements ReactiveController {
|
|
@@ -289,6 +317,8 @@ declare class RecordingController implements ReactiveController {
|
|
|
289
317
|
get isRecording(): boolean;
|
|
290
318
|
getSession(trackId: string): ReadonlyRecordingSession | undefined;
|
|
291
319
|
startRecording(stream: MediaStream, options?: RecordingOptions): Promise<void>;
|
|
320
|
+
pauseRecording(trackId?: string): void;
|
|
321
|
+
resumeRecording(trackId?: string): void;
|
|
292
322
|
stopRecording(trackId?: string): void;
|
|
293
323
|
private _onWorkletMessage;
|
|
294
324
|
private _createClipFromRecording;
|
|
@@ -296,6 +326,85 @@ declare class RecordingController implements ReactiveController {
|
|
|
296
326
|
private _cleanupSession;
|
|
297
327
|
}
|
|
298
328
|
|
|
329
|
+
/** Snapshot of a clip's bounds for trim constraint computation. */
|
|
330
|
+
interface ClipBounds {
|
|
331
|
+
readonly offsetSamples: number;
|
|
332
|
+
readonly durationSamples: number;
|
|
333
|
+
readonly startSample: number;
|
|
334
|
+
readonly sourceDurationSamples: number;
|
|
335
|
+
}
|
|
336
|
+
/** Narrow engine contract for clip move/trim interactions. */
|
|
337
|
+
interface ClipEngineContract {
|
|
338
|
+
moveClip(trackId: string, clipId: string, deltaSamples: number, skipAdapter?: boolean): void;
|
|
339
|
+
trimClip(trackId: string, clipId: string, boundary: 'left' | 'right', deltaSamples: number, skipAdapter?: boolean): void;
|
|
340
|
+
updateTrack(trackId: string): void;
|
|
341
|
+
/** Get a clip's full bounds for trim constraint computation. */
|
|
342
|
+
getClipBounds(trackId: string, clipId: string): ClipBounds | null;
|
|
343
|
+
/** Constrain a trim delta using the engine's collision/bounds logic. */
|
|
344
|
+
constrainTrimDelta(trackId: string, clipId: string, boundary: 'left' | 'right', deltaSamples: number): number;
|
|
345
|
+
}
|
|
346
|
+
/** Peak data returned by reextractClipPeaks for imperative waveform updates. */
|
|
347
|
+
interface ClipPeakSlice {
|
|
348
|
+
data: ArrayLike<number>[];
|
|
349
|
+
length: number;
|
|
350
|
+
}
|
|
351
|
+
/** Host interface required by ClipPointerHandler. */
|
|
352
|
+
interface ClipPointerHost {
|
|
353
|
+
readonly samplesPerPixel: number;
|
|
354
|
+
readonly effectiveSampleRate: number;
|
|
355
|
+
readonly interactiveClips: boolean;
|
|
356
|
+
readonly engine: ClipEngineContract | null;
|
|
357
|
+
readonly shadowRoot: ShadowRoot | null;
|
|
358
|
+
dispatchEvent(event: Event): boolean;
|
|
359
|
+
/** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */
|
|
360
|
+
reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number): ClipPeakSlice | null;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Handles pointer interactions for clip move and trim drag operations.
|
|
364
|
+
* Converts pixel deltas to sample deltas and delegates to the engine.
|
|
365
|
+
*
|
|
366
|
+
* Move: sends incremental deltas per-frame with skipAdapter=true (engine shifts
|
|
367
|
+
* startSample additively without touching audio adapter). Adapter synced once
|
|
368
|
+
* via updateTrack() at drag end.
|
|
369
|
+
* Trim: updates clip container CSS imperatively during drag for visual feedback,
|
|
370
|
+
* then applies cumulative delta to engine once at drag end.
|
|
371
|
+
*/
|
|
372
|
+
declare class ClipPointerHandler {
|
|
373
|
+
private _host;
|
|
374
|
+
private _mode;
|
|
375
|
+
private _clipId;
|
|
376
|
+
private _trackId;
|
|
377
|
+
private _startPx;
|
|
378
|
+
private _isDragging;
|
|
379
|
+
private _lastDeltaPx;
|
|
380
|
+
private _cumulativeDeltaSamples;
|
|
381
|
+
private _clipContainer;
|
|
382
|
+
private _boundaryEl;
|
|
383
|
+
private _originalLeft;
|
|
384
|
+
private _originalWidth;
|
|
385
|
+
private _originalOffsetSamples;
|
|
386
|
+
private _originalDurationSamples;
|
|
387
|
+
constructor(host: ClipPointerHost);
|
|
388
|
+
/** Returns true if a drag interaction is currently in progress. */
|
|
389
|
+
get isActive(): boolean;
|
|
390
|
+
/**
|
|
391
|
+
* Attempts to handle a pointerdown event on the given target element.
|
|
392
|
+
* Returns true if the target is a recognized clip interaction element.
|
|
393
|
+
*/
|
|
394
|
+
tryHandle(target: Element, e: PointerEvent): boolean;
|
|
395
|
+
private _beginDrag;
|
|
396
|
+
/** Processes pointermove events during an active drag. */
|
|
397
|
+
onPointerMove(e: PointerEvent): void;
|
|
398
|
+
/** Processes pointerup events to finalize and dispatch result events. */
|
|
399
|
+
onPointerUp(_e: PointerEvent): void;
|
|
400
|
+
/** Re-extract peaks from cache and set on waveform elements during trim drag.
|
|
401
|
+
* Returns true if peaks were successfully updated. */
|
|
402
|
+
private _updateWaveformPeaks;
|
|
403
|
+
/** Restore clip container CSS to original values after trim visual preview. */
|
|
404
|
+
private _restoreTrimVisual;
|
|
405
|
+
private _reset;
|
|
406
|
+
}
|
|
407
|
+
|
|
299
408
|
interface DawSelectionDetail {
|
|
300
409
|
start: number;
|
|
301
410
|
end: number;
|
|
@@ -342,11 +451,33 @@ interface DawRecordingCompleteDetail {
|
|
|
342
451
|
audioBuffer: AudioBuffer;
|
|
343
452
|
startSample: number;
|
|
344
453
|
durationSamples: number;
|
|
454
|
+
offsetSamples: number;
|
|
345
455
|
}
|
|
346
456
|
interface DawRecordingErrorDetail {
|
|
347
457
|
trackId: string;
|
|
348
458
|
error: unknown;
|
|
349
459
|
}
|
|
460
|
+
interface DawClipMoveDetail {
|
|
461
|
+
readonly trackId: string;
|
|
462
|
+
readonly clipId: string;
|
|
463
|
+
/** Requested cumulative delta. May exceed actual applied movement due to
|
|
464
|
+
* collision constraints. Query engine state for actual clip positions. */
|
|
465
|
+
readonly deltaSamples: number;
|
|
466
|
+
}
|
|
467
|
+
interface DawClipTrimDetail {
|
|
468
|
+
readonly trackId: string;
|
|
469
|
+
readonly clipId: string;
|
|
470
|
+
readonly boundary: 'left' | 'right';
|
|
471
|
+
/** Constrained cumulative delta applied by the engine. Already clamped
|
|
472
|
+
* by collision and boundary constraints during drag. */
|
|
473
|
+
readonly deltaSamples: number;
|
|
474
|
+
}
|
|
475
|
+
interface DawClipSplitDetail {
|
|
476
|
+
readonly trackId: string;
|
|
477
|
+
readonly originalClipId: string;
|
|
478
|
+
readonly leftClipId: string;
|
|
479
|
+
readonly rightClipId: string;
|
|
480
|
+
}
|
|
350
481
|
interface DawEventMap {
|
|
351
482
|
'daw-selection': CustomEvent<DawSelectionDetail>;
|
|
352
483
|
'daw-seek': CustomEvent<DawSeekDetail>;
|
|
@@ -365,6 +496,9 @@ interface DawEventMap {
|
|
|
365
496
|
'daw-recording-start': CustomEvent<DawRecordingStartDetail>;
|
|
366
497
|
'daw-recording-complete': CustomEvent<DawRecordingCompleteDetail>;
|
|
367
498
|
'daw-recording-error': CustomEvent<DawRecordingErrorDetail>;
|
|
499
|
+
'daw-clip-move': CustomEvent<DawClipMoveDetail>;
|
|
500
|
+
'daw-clip-trim': CustomEvent<DawClipTrimDetail>;
|
|
501
|
+
'daw-clip-split': CustomEvent<DawClipSplitDetail>;
|
|
368
502
|
}
|
|
369
503
|
type DawEvent<K extends keyof DawEventMap> = DawEventMap[K];
|
|
370
504
|
interface LoadFilesResult {
|
|
@@ -383,6 +517,9 @@ declare class DawEditorElement extends LitElement {
|
|
|
383
517
|
barWidth: number;
|
|
384
518
|
barGap: number;
|
|
385
519
|
fileDrop: boolean;
|
|
520
|
+
clipHeaders: boolean;
|
|
521
|
+
clipHeaderHeight: number;
|
|
522
|
+
interactiveClips: boolean;
|
|
386
523
|
/** Initial sample rate hint. Overridden by decoded audio buffer's actual rate. */
|
|
387
524
|
sampleRate: number;
|
|
388
525
|
/** Resolved sample rate — falls back to sampleRate property until first audio decode. */
|
|
@@ -399,15 +536,26 @@ declare class DawEditorElement extends LitElement {
|
|
|
399
536
|
_currentTime: number;
|
|
400
537
|
_engine: PlaylistEngine | null;
|
|
401
538
|
private _enginePromise;
|
|
402
|
-
private _audioInitialized;
|
|
403
539
|
_audioCache: Map<string, Promise<AudioBuffer>>;
|
|
404
540
|
_clipBuffers: Map<string, AudioBuffer>;
|
|
541
|
+
_clipOffsets: Map<string, {
|
|
542
|
+
offsetSamples: number;
|
|
543
|
+
durationSamples: number;
|
|
544
|
+
}>;
|
|
405
545
|
_peakPipeline: PeakPipeline;
|
|
406
546
|
private _trackElements;
|
|
407
547
|
private _childObserver;
|
|
408
548
|
private _audioResume;
|
|
409
549
|
eagerResume?: string;
|
|
410
550
|
private _recordingController;
|
|
551
|
+
private _clipPointer;
|
|
552
|
+
get _clipHandler(): ClipPointerHandler | null;
|
|
553
|
+
get engine(): PlaylistEngine | null;
|
|
554
|
+
/** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */
|
|
555
|
+
reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number): {
|
|
556
|
+
data: Peaks[];
|
|
557
|
+
length: number;
|
|
558
|
+
} | null;
|
|
411
559
|
private _pointer;
|
|
412
560
|
private _viewport;
|
|
413
561
|
static styles: lit.CSSResult[];
|
|
@@ -442,14 +590,20 @@ declare class DawEditorElement extends LitElement {
|
|
|
442
590
|
private _onDragLeave;
|
|
443
591
|
private _onDrop;
|
|
444
592
|
loadFiles(files: FileList | File[]): Promise<LoadFilesResult>;
|
|
445
|
-
play(): Promise<void>;
|
|
593
|
+
play(startTime?: number): Promise<void>;
|
|
446
594
|
pause(): void;
|
|
447
595
|
stop(): void;
|
|
448
596
|
seekTo(time: number): void;
|
|
597
|
+
/** Split the clip under the playhead on the selected track. */
|
|
598
|
+
splitAtPlayhead(): boolean;
|
|
599
|
+
private _onKeyDown;
|
|
449
600
|
recordingStream: MediaStream | null;
|
|
601
|
+
get currentTime(): number;
|
|
450
602
|
get isRecording(): boolean;
|
|
603
|
+
pauseRecording(): void;
|
|
604
|
+
resumeRecording(): void;
|
|
451
605
|
stopRecording(): void;
|
|
452
|
-
_addRecordedClip(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number): void;
|
|
606
|
+
_addRecordedClip(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number, offsetSamples?: number): void;
|
|
453
607
|
startRecording(stream?: MediaStream, options?: RecordingOptions): Promise<void>;
|
|
454
608
|
private _renderRecordingPreview;
|
|
455
609
|
_startPlayhead(): void;
|
|
@@ -538,4 +692,27 @@ interface PointerEngineContract {
|
|
|
538
692
|
selectTrack(trackId: string | null): void;
|
|
539
693
|
}
|
|
540
694
|
|
|
541
|
-
|
|
695
|
+
/** Narrow engine contract for split operations. */
|
|
696
|
+
interface SplitEngineContract {
|
|
697
|
+
getState(): {
|
|
698
|
+
selectedTrackId: string | null;
|
|
699
|
+
tracks: ClipTrack[];
|
|
700
|
+
};
|
|
701
|
+
splitClip(trackId: string, clipId: string, atSample: number): void;
|
|
702
|
+
}
|
|
703
|
+
/** Host interface for splitAtPlayhead. */
|
|
704
|
+
interface SplitHost {
|
|
705
|
+
readonly effectiveSampleRate: number;
|
|
706
|
+
readonly currentTime: number;
|
|
707
|
+
readonly engine: SplitEngineContract | null;
|
|
708
|
+
dispatchEvent(event: Event): boolean;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Splits the clip under the playhead on the selected track.
|
|
712
|
+
*
|
|
713
|
+
* Returns true if the split occurred and dispatched a daw-clip-split event.
|
|
714
|
+
* Returns false for any guard failure or engine no-op.
|
|
715
|
+
*/
|
|
716
|
+
declare function splitAtPlayhead(host: SplitHost): boolean;
|
|
717
|
+
|
|
718
|
+
export { AudioResumeController, type ClipDescriptor, type ClipEngineContract, ClipPointerHandler, type ClipPointerHost, DawClipElement, type DawClipMoveDetail, type DawClipSplitDetail, type DawClipTrimDetail, DawEditorElement, type DawErrorDetail, type DawEvent, type DawEventMap, type DawFilesLoadErrorDetail, DawPauseButtonElement, DawPlayButtonElement, DawPlayheadElement, DawRecordButtonElement, type DawRecordingCompleteDetail, type DawRecordingErrorDetail, type DawRecordingStartDetail, DawRulerElement, type DawSeekDetail, type DawSelectionDetail, DawSelectionElement, DawStopButtonElement, type DawTrackConnectedDetail, type DawTrackControlDetail, DawTrackControlsElement, DawTrackElement, type DawTrackErrorDetail, type DawTrackIdDetail, type DawTrackRemoveDetail, type DawTrackSelectDetail, DawTransportButton, DawTransportElement, DawWaveformElement, type LoadFilesResult, type PointerEngineContract, RecordingController, type RecordingOptions, type RecordingSession, type SplitEngineContract, type SplitHost, type TrackDescriptor, splitAtPlayhead };
|