@dawcore/components 0.0.1

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Naomi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,541 @@
1
+ import * as lit from 'lit';
2
+ import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost } from 'lit';
3
+ import { Peaks, Bits, FadeType, PeakData, ClipTrack } from '@waveform-playlist/core';
4
+ import { PlaylistEngine } from '@waveform-playlist/engine';
5
+
6
+ declare class DawClipElement extends LitElement {
7
+ src: string;
8
+ peaksSrc: string;
9
+ start: number;
10
+ duration: number;
11
+ offset: number;
12
+ gain: number;
13
+ name: string;
14
+ color: string;
15
+ fadeIn: number;
16
+ fadeOut: number;
17
+ fadeType: string;
18
+ readonly clipId: `${string}-${string}-${string}-${string}-${string}`;
19
+ createRenderRoot(): this;
20
+ }
21
+ declare global {
22
+ interface HTMLElementTagNameMap {
23
+ 'daw-clip': DawClipElement;
24
+ }
25
+ }
26
+
27
+ declare class DawTrackElement extends LitElement {
28
+ src: string;
29
+ name: string;
30
+ volume: number;
31
+ pan: number;
32
+ muted: boolean;
33
+ soloed: boolean;
34
+ readonly trackId: `${string}-${string}-${string}-${string}-${string}`;
35
+ createRenderRoot(): this;
36
+ connectedCallback(): void;
37
+ private _hasRendered;
38
+ updated(changed: PropertyValues): void;
39
+ }
40
+ declare global {
41
+ interface HTMLElementTagNameMap {
42
+ 'daw-track': DawTrackElement;
43
+ }
44
+ }
45
+
46
+ declare class DawWaveformElement extends LitElement {
47
+ private _peaks;
48
+ private _dirtyPixels;
49
+ private _drawScheduled;
50
+ private _rafId;
51
+ /** Chunk indices visible in the last draw pass — used to detect new chunks on scroll. */
52
+ private _drawnChunks;
53
+ set peaks(value: Peaks);
54
+ get peaks(): Peaks;
55
+ /**
56
+ * Replace the internal peaks reference without marking all dirty.
57
+ * Use with updatePeaks() for incremental recording updates where
58
+ * appendPeaks() returns a new array but only the tail changed.
59
+ */
60
+ setPeaksQuiet(value: Peaks): void;
61
+ get bits(): Bits;
62
+ length: number;
63
+ waveHeight: number;
64
+ barWidth: number;
65
+ barGap: number;
66
+ /** Visible viewport start in pixels (relative to timeline origin). */
67
+ visibleStart: number;
68
+ /** Visible viewport end in pixels (relative to timeline origin). */
69
+ visibleEnd: number;
70
+ /** This element's left offset on the timeline (for viewport intersection). */
71
+ originX: number;
72
+ static styles: lit.CSSResult;
73
+ private _getVisibleChunkIndices;
74
+ /**
75
+ * Mark a range of peak indices as dirty for incremental redraw.
76
+ * The caller must have already updated the underlying peaks array.
77
+ * Does NOT trigger a Lit re-render — bypasses Lit entirely.
78
+ */
79
+ updatePeaks(startIndex: number, endIndex: number): void;
80
+ private _markAllDirty;
81
+ private _scheduleDraw;
82
+ private _drawDirty;
83
+ private _drawChunk;
84
+ connectedCallback(): void;
85
+ disconnectedCallback(): void;
86
+ render(): lit.TemplateResult<1>;
87
+ /** Mark peaks dirty only for chunks that weren't drawn in the previous frame. */
88
+ private _markNewChunksDirty;
89
+ updated(changedProperties: Map<string, unknown>): void;
90
+ }
91
+ declare global {
92
+ interface HTMLElementTagNameMap {
93
+ 'daw-waveform': DawWaveformElement;
94
+ }
95
+ }
96
+
97
+ declare class DawPlayheadElement extends LitElement {
98
+ private _animation;
99
+ private _line;
100
+ static styles: lit.CSSResult;
101
+ render(): lit.TemplateResult<1>;
102
+ firstUpdated(): void;
103
+ startAnimation(getTime: () => number, sampleRate: number, samplesPerPixel: number): void;
104
+ stopAnimation(time: number, sampleRate: number, samplesPerPixel: number): void;
105
+ }
106
+ declare global {
107
+ interface HTMLElementTagNameMap {
108
+ 'daw-playhead': DawPlayheadElement;
109
+ }
110
+ }
111
+
112
+ declare class DawTransportElement extends LitElement {
113
+ for: string;
114
+ get target(): HTMLElement | null;
115
+ createRenderRoot(): this;
116
+ }
117
+ declare global {
118
+ interface HTMLElementTagNameMap {
119
+ 'daw-transport': DawTransportElement;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Base class for transport button elements.
125
+ * Finds target daw-editor via closest <daw-transport>.
126
+ */
127
+ declare class DawTransportButton extends LitElement {
128
+ protected get target(): any;
129
+ static styles: lit.CSSResultGroup;
130
+ }
131
+
132
+ declare class DawPlayButtonElement extends DawTransportButton {
133
+ render(): lit.TemplateResult<1>;
134
+ private _onClick;
135
+ }
136
+ declare global {
137
+ interface HTMLElementTagNameMap {
138
+ 'daw-play-button': DawPlayButtonElement;
139
+ }
140
+ }
141
+
142
+ declare class DawPauseButtonElement extends DawTransportButton {
143
+ render(): lit.TemplateResult<1>;
144
+ private _onClick;
145
+ }
146
+ declare global {
147
+ interface HTMLElementTagNameMap {
148
+ 'daw-pause-button': DawPauseButtonElement;
149
+ }
150
+ }
151
+
152
+ declare class DawStopButtonElement extends DawTransportButton {
153
+ render(): lit.TemplateResult<1>;
154
+ private _onClick;
155
+ }
156
+ declare global {
157
+ interface HTMLElementTagNameMap {
158
+ 'daw-stop-button': DawStopButtonElement;
159
+ }
160
+ }
161
+
162
+ interface TrackDescriptor {
163
+ name: string;
164
+ src: string;
165
+ volume: number;
166
+ pan: number;
167
+ muted: boolean;
168
+ soloed: boolean;
169
+ clips: ClipDescriptor[];
170
+ }
171
+ interface ClipDescriptor {
172
+ src: string;
173
+ start: number;
174
+ duration: number;
175
+ offset: number;
176
+ gain: number;
177
+ name: string;
178
+ fadeIn: number;
179
+ fadeOut: number;
180
+ fadeType: FadeType;
181
+ }
182
+
183
+ /**
184
+ * Peak generation pipeline: AudioBuffer → web worker → WaveformData → PeakData.
185
+ *
186
+ * Manages worker lifecycle, WaveformData caching per AudioBuffer (WeakMap),
187
+ * inflight dedup, and peak extraction via resample() for any zoom level
188
+ * coarser than the base scale.
189
+ *
190
+ * The base scale determines the finest zoom level that can be rendered without
191
+ * regenerating. Resampling only works to coarser (larger) scales. Set baseScale
192
+ * to the finest zoom level the user might need.
193
+ */
194
+
195
+ declare class PeakPipeline {
196
+ private _worker;
197
+ private _cache;
198
+ private _inflight;
199
+ /**
200
+ * Generate PeakData for a clip from its AudioBuffer.
201
+ * Uses cached WaveformData when available; otherwise generates via worker.
202
+ * The worker generates at `scale` (= samplesPerPixel) for exact rendering.
203
+ */
204
+ generatePeaks(audioBuffer: AudioBuffer, samplesPerPixel: number, isMono: boolean): Promise<PeakData>;
205
+ /**
206
+ * Re-extract peaks for all clips at a new zoom level using cached WaveformData.
207
+ * Only works for zoom levels coarser than (or equal to) the cached base scale.
208
+ * Returns a new Map of clipId → PeakData. Clips without cached data or where
209
+ * the target scale is finer than the cached base are skipped.
210
+ */
211
+ reextractPeaks(clipBuffers: ReadonlyMap<string, AudioBuffer>, samplesPerPixel: number, isMono: boolean): Map<string, PeakData>;
212
+ terminate(): void;
213
+ private _getWaveformData;
214
+ }
215
+
216
+ declare class DawTrackControlsElement extends LitElement {
217
+ /** Track ID — set by the editor to link controls to a track row. */
218
+ trackId: string | null;
219
+ trackName: string;
220
+ volume: number;
221
+ pan: number;
222
+ muted: boolean;
223
+ soloed: boolean;
224
+ static styles: lit.CSSResult;
225
+ private _onVolumeInput;
226
+ private _onPanInput;
227
+ private _onMuteClick;
228
+ private _onSoloClick;
229
+ private _onRemoveClick;
230
+ private _dispatchControl;
231
+ render(): lit.TemplateResult<1>;
232
+ }
233
+ declare global {
234
+ interface HTMLElementTagNameMap {
235
+ 'daw-track-controls': DawTrackControlsElement;
236
+ }
237
+ }
238
+
239
+ interface RecordingOptions {
240
+ trackId?: string;
241
+ bits?: 8 | 16;
242
+ startSample?: number;
243
+ }
244
+ interface RecordingSession {
245
+ readonly trackId: string;
246
+ readonly stream: MediaStream;
247
+ readonly source: {
248
+ disconnect(): void;
249
+ connect(dest: unknown): void;
250
+ };
251
+ readonly workletNode: {
252
+ port: MessagePort;
253
+ disconnect(): void;
254
+ };
255
+ readonly chunks: Float32Array[][];
256
+ totalSamples: number;
257
+ readonly peaks: (Int8Array | Int16Array)[];
258
+ readonly startSample: number;
259
+ readonly channelCount: number;
260
+ readonly bits: Bits;
261
+ isFirstMessage: boolean;
262
+ /** Stored so it can be removed on stop/cleanup — not just when stream ends. */
263
+ readonly _onTrackEnded: (() => void) | null;
264
+ readonly _audioTrack: MediaStreamTrack | null;
265
+ }
266
+ /** Readonly view of a recording session for external consumers. */
267
+ type ReadonlyRecordingSession = Readonly<Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack'>> & {
268
+ readonly chunks: ReadonlyArray<ReadonlyArray<Float32Array>>;
269
+ readonly peaks: ReadonlyArray<Int8Array | Int16Array>;
270
+ };
271
+ /** Narrow interface for the host editor. */
272
+ interface RecordingHost extends ReactiveControllerHost {
273
+ readonly samplesPerPixel: number;
274
+ readonly effectiveSampleRate: number;
275
+ readonly _selectedTrackId: string | null;
276
+ readonly _currentTime: number;
277
+ readonly shadowRoot: ShadowRoot | null;
278
+ resolveAudioContextSampleRate(rate: number): void;
279
+ _addRecordedClip?(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number): void;
280
+ dispatchEvent(event: Event): boolean;
281
+ }
282
+ declare class RecordingController implements ReactiveController {
283
+ private _host;
284
+ private _sessions;
285
+ private _workletLoaded;
286
+ constructor(host: RecordingHost & HTMLElement);
287
+ hostConnected(): void;
288
+ hostDisconnected(): void;
289
+ get isRecording(): boolean;
290
+ getSession(trackId: string): ReadonlyRecordingSession | undefined;
291
+ startRecording(stream: MediaStream, options?: RecordingOptions): Promise<void>;
292
+ stopRecording(trackId?: string): void;
293
+ private _onWorkletMessage;
294
+ private _createClipFromRecording;
295
+ private _removeTrackEndedListener;
296
+ private _cleanupSession;
297
+ }
298
+
299
+ interface DawSelectionDetail {
300
+ start: number;
301
+ end: number;
302
+ }
303
+ interface DawSeekDetail {
304
+ time: number;
305
+ }
306
+ interface DawTrackSelectDetail {
307
+ trackId: string;
308
+ }
309
+ interface DawTrackConnectedDetail {
310
+ trackId: string;
311
+ element: DawTrackElement;
312
+ }
313
+ interface DawTrackIdDetail {
314
+ trackId: string;
315
+ }
316
+ interface DawTrackErrorDetail {
317
+ trackId: string;
318
+ error: unknown;
319
+ }
320
+ interface DawFilesLoadErrorDetail {
321
+ file: File;
322
+ error: unknown;
323
+ }
324
+ interface DawErrorDetail {
325
+ operation: string;
326
+ error: unknown;
327
+ }
328
+ interface DawTrackControlDetail {
329
+ trackId: string;
330
+ prop: string;
331
+ value: number | boolean;
332
+ }
333
+ interface DawTrackRemoveDetail {
334
+ trackId: string;
335
+ }
336
+ interface DawRecordingStartDetail {
337
+ trackId: string;
338
+ stream: MediaStream;
339
+ }
340
+ interface DawRecordingCompleteDetail {
341
+ trackId: string;
342
+ audioBuffer: AudioBuffer;
343
+ startSample: number;
344
+ durationSamples: number;
345
+ }
346
+ interface DawRecordingErrorDetail {
347
+ trackId: string;
348
+ error: unknown;
349
+ }
350
+ interface DawEventMap {
351
+ 'daw-selection': CustomEvent<DawSelectionDetail>;
352
+ 'daw-seek': CustomEvent<DawSeekDetail>;
353
+ 'daw-track-select': CustomEvent<DawTrackSelectDetail>;
354
+ 'daw-track-connected': CustomEvent<DawTrackConnectedDetail>;
355
+ 'daw-track-update': CustomEvent<DawTrackIdDetail>;
356
+ 'daw-track-ready': CustomEvent<DawTrackIdDetail>;
357
+ 'daw-track-error': CustomEvent<DawTrackErrorDetail>;
358
+ 'daw-files-load-error': CustomEvent<DawFilesLoadErrorDetail>;
359
+ 'daw-play': CustomEvent<void>;
360
+ 'daw-pause': CustomEvent<void>;
361
+ 'daw-stop': CustomEvent<void>;
362
+ 'daw-error': CustomEvent<DawErrorDetail>;
363
+ 'daw-track-control': CustomEvent<DawTrackControlDetail>;
364
+ 'daw-track-remove': CustomEvent<DawTrackRemoveDetail>;
365
+ 'daw-recording-start': CustomEvent<DawRecordingStartDetail>;
366
+ 'daw-recording-complete': CustomEvent<DawRecordingCompleteDetail>;
367
+ 'daw-recording-error': CustomEvent<DawRecordingErrorDetail>;
368
+ }
369
+ type DawEvent<K extends keyof DawEventMap> = DawEventMap[K];
370
+ interface LoadFilesResult {
371
+ loaded: string[];
372
+ failed: Array<{
373
+ file: File;
374
+ error: unknown;
375
+ }>;
376
+ }
377
+
378
+ declare class DawEditorElement extends LitElement {
379
+ samplesPerPixel: number;
380
+ waveHeight: number;
381
+ timescale: boolean;
382
+ mono: boolean;
383
+ barWidth: number;
384
+ barGap: number;
385
+ fileDrop: boolean;
386
+ /** Initial sample rate hint. Overridden by decoded audio buffer's actual rate. */
387
+ sampleRate: number;
388
+ /** Resolved sample rate — falls back to sampleRate property until first audio decode. */
389
+ _resolvedSampleRate: number | null;
390
+ _tracks: Map<string, TrackDescriptor>;
391
+ _engineTracks: Map<string, ClipTrack>;
392
+ _peaksData: Map<string, PeakData>;
393
+ _isPlaying: boolean;
394
+ private _duration;
395
+ _selectedTrackId: string | null;
396
+ _dragOver: boolean;
397
+ _selectionStartTime: number;
398
+ _selectionEndTime: number;
399
+ _currentTime: number;
400
+ _engine: PlaylistEngine | null;
401
+ private _enginePromise;
402
+ private _audioInitialized;
403
+ _audioCache: Map<string, Promise<AudioBuffer>>;
404
+ _clipBuffers: Map<string, AudioBuffer>;
405
+ _peakPipeline: PeakPipeline;
406
+ private _trackElements;
407
+ private _childObserver;
408
+ private _audioResume;
409
+ eagerResume?: string;
410
+ private _recordingController;
411
+ private _pointer;
412
+ private _viewport;
413
+ static styles: lit.CSSResult[];
414
+ get effectiveSampleRate(): number;
415
+ resolveAudioContextSampleRate(rate: number): void;
416
+ private get _totalWidth();
417
+ _setSelectedTrackId(trackId: string | null): void;
418
+ get tracks(): TrackDescriptor[];
419
+ get selectedTrackId(): string | null;
420
+ get selection(): {
421
+ start: number;
422
+ end: number;
423
+ } | null;
424
+ setSelection(start: number, end: number): void;
425
+ connectedCallback(): void;
426
+ disconnectedCallback(): void;
427
+ willUpdate(changedProperties: Map<string, unknown>): void;
428
+ private _onTrackConnected;
429
+ private _onTrackRemoved;
430
+ private _onTrackUpdate;
431
+ private static _CONTROL_PROPS;
432
+ private _onTrackControl;
433
+ private _onTrackRemoveRequest;
434
+ private _readTrackDescriptor;
435
+ private _loadTrack;
436
+ _fetchAndDecode(src: string): Promise<AudioBuffer>;
437
+ _recomputeDuration(): void;
438
+ _ensureEngine(): Promise<PlaylistEngine>;
439
+ private _buildEngine;
440
+ private _disposeEngine;
441
+ private _onDragOver;
442
+ private _onDragLeave;
443
+ private _onDrop;
444
+ loadFiles(files: FileList | File[]): Promise<LoadFilesResult>;
445
+ play(): Promise<void>;
446
+ pause(): void;
447
+ stop(): void;
448
+ seekTo(time: number): void;
449
+ recordingStream: MediaStream | null;
450
+ get isRecording(): boolean;
451
+ stopRecording(): void;
452
+ _addRecordedClip(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number): void;
453
+ startRecording(stream?: MediaStream, options?: RecordingOptions): Promise<void>;
454
+ private _renderRecordingPreview;
455
+ _startPlayhead(): void;
456
+ _stopPlayhead(): void;
457
+ private _getPlayhead;
458
+ private _getOrderedTracks;
459
+ render(): lit.TemplateResult<1>;
460
+ }
461
+ declare global {
462
+ interface HTMLElementTagNameMap {
463
+ 'daw-editor': DawEditorElement;
464
+ }
465
+ }
466
+
467
+ declare class DawRulerElement extends LitElement {
468
+ samplesPerPixel: number;
469
+ sampleRate: number;
470
+ duration: number;
471
+ rulerHeight: number;
472
+ private _tickData;
473
+ static styles: lit.CSSResult;
474
+ willUpdate(): void;
475
+ render(): lit.TemplateResult<1>;
476
+ updated(): void;
477
+ private _drawTicks;
478
+ }
479
+ declare global {
480
+ interface HTMLElementTagNameMap {
481
+ 'daw-ruler': DawRulerElement;
482
+ }
483
+ }
484
+
485
+ declare class DawSelectionElement extends LitElement {
486
+ startPx: number;
487
+ endPx: number;
488
+ static styles: lit.CSSResult;
489
+ render(): lit.TemplateResult<1>;
490
+ }
491
+ declare global {
492
+ interface HTMLElementTagNameMap {
493
+ 'daw-selection': DawSelectionElement;
494
+ }
495
+ }
496
+
497
+ declare class DawRecordButtonElement extends DawTransportButton {
498
+ private _isRecording;
499
+ private _targetRef;
500
+ private _onStart;
501
+ private _onComplete;
502
+ private _onError;
503
+ static styles: lit.CSSResultGroup[];
504
+ connectedCallback(): void;
505
+ disconnectedCallback(): void;
506
+ private _listenToTarget;
507
+ private _cleanupListeners;
508
+ render(): lit.TemplateResult<1>;
509
+ private _onClick;
510
+ }
511
+ declare global {
512
+ interface HTMLElementTagNameMap {
513
+ 'daw-record-button': DawRecordButtonElement;
514
+ }
515
+ }
516
+
517
+ declare class AudioResumeController implements ReactiveController {
518
+ private _host;
519
+ private _target;
520
+ private _attached;
521
+ private _generation;
522
+ /** CSS selector, or 'document'. When undefined, controller is inert. */
523
+ target?: string;
524
+ constructor(host: ReactiveControllerHost & HTMLElement);
525
+ hostConnected(): void;
526
+ hostDisconnected(): void;
527
+ private _onGesture;
528
+ private _resolveTarget;
529
+ private _removeListeners;
530
+ }
531
+
532
+ /** Narrow engine contract for pointer interactions. */
533
+ interface PointerEngineContract {
534
+ setSelection(start: number, end: number): void;
535
+ stop(): void;
536
+ play(time: number): void;
537
+ seek(time: number): void;
538
+ selectTrack(trackId: string | null): void;
539
+ }
540
+
541
+ export { AudioResumeController, type ClipDescriptor, DawClipElement, 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 TrackDescriptor };