@dawcore/components 0.0.19 → 0.0.21

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 CHANGED
@@ -168,7 +168,7 @@ A clip is treated as MIDI iff `clip.midiNotes != null`. MIDI clips skip audio fe
168
168
 
169
169
  **Theming:** the piano-roll honors `--daw-piano-roll-note-color` (default `#2a7070`), `--daw-piano-roll-selected-note-color` (default `#3d9e9e`), and `--daw-piano-roll-background` (default `#1a1a2e`).
170
170
 
171
- See `examples/dawcore-tone/midi.html` for a runnable demo (C major scale, PolySynth playback).
171
+ See `examples/dawcore-tone/midi.html` for a runnable demo (C major scale, PolySynth playback). For SoundFont sample playback, pass `createToneAdapter({ soundFontCache })` and see `examples/dawcore-tone/soundfont.html`.
172
172
 
173
173
  ## Pre-Computed Peaks
174
174
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as lit from 'lit';
2
2
  import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost } from 'lit';
3
- import { MidiNoteData, SpectrogramConfig, FadeType, Peaks, Bits, PeakData, MeterEntry, SnapTo, ColorMapValue, ClipTrack, KeyboardShortcut } from '@waveform-playlist/core';
3
+ import { MidiNoteData, RenderMode, SpectrogramConfig, Peaks, Bits, FadeType, PeakData, MeterEntry, SnapTo, ColorMapValue, ClipTrack, KeyboardShortcut } from '@waveform-playlist/core';
4
4
  import WaveformData from 'waveform-data';
5
5
  import { PlayoutAdapter, PlaylistEngine } from '@waveform-playlist/engine';
6
6
  import { MidiLoadOptions, MidiLoadResult } from '@dawcore/midi';
@@ -46,113 +46,6 @@ declare global {
46
46
  }
47
47
  }
48
48
 
49
- type TrackRenderMode = 'waveform' | 'piano-roll' | 'spectrogram';
50
- interface TrackDescriptor {
51
- name: string;
52
- src: string;
53
- volume: number;
54
- pan: number;
55
- muted: boolean;
56
- soloed: boolean;
57
- renderMode: TrackRenderMode;
58
- spectrogramConfig?: SpectrogramConfig | null;
59
- clips: ClipDescriptor[];
60
- }
61
- /**
62
- * Common fields shared by all clip descriptors regardless of source.
63
- */
64
- interface BaseClipDescriptor {
65
- src: string;
66
- peaksSrc: string;
67
- start: number;
68
- duration: number;
69
- offset: number;
70
- gain: number;
71
- name: string;
72
- fadeIn: number;
73
- fadeOut: number;
74
- fadeType: FadeType;
75
- midiNotes: MidiNoteData[] | null;
76
- midiChannel: number | null;
77
- midiProgram: number | null;
78
- }
79
- /**
80
- * A clip descriptor sourced from a `<daw-clip>` DOM element. `clipId` is
81
- * always set — `<daw-clip>.clipId` is a `crypto.randomUUID()` generated at
82
- * construction. Engine `clip.id` is aligned with this id in `_loadTrack`
83
- * and `_loadAndAppendClip` so DOM and engine reference the same clip.
84
- */
85
- interface DomClipDescriptor extends BaseClipDescriptor {
86
- kind: 'dom';
87
- clipId: string;
88
- }
89
- /**
90
- * A clip descriptor synthesized from a non-DOM source — file drops, the
91
- * `<daw-track src>` shorthand fallback, or recording-clip insertion. No
92
- * `clipId` because there's no DOM element to align with; the engine
93
- * generates its own id at clip-creation time.
94
- */
95
- interface DropClipDescriptor extends BaseClipDescriptor {
96
- kind: 'drop';
97
- }
98
- type ClipDescriptor = DomClipDescriptor | DropClipDescriptor;
99
- /**
100
- * Type predicate for the `'dom'` discriminator. Use to narrow a
101
- * `ClipDescriptor` to `DomClipDescriptor` (which has `clipId`) without
102
- * inline `c.kind === 'dom'` repetition.
103
- */
104
- declare function isDomClip(desc: ClipDescriptor): desc is DomClipDescriptor;
105
- /**
106
- * Public input shape for `editor.addTrack(config)`. All fields optional —
107
- * defaults match the declarative `<daw-track>` defaults.
108
- */
109
- interface TrackConfig {
110
- name?: string;
111
- volume?: number;
112
- pan?: number;
113
- muted?: boolean;
114
- soloed?: boolean;
115
- renderMode?: TrackRenderMode;
116
- spectrogramConfig?: SpectrogramConfig | null;
117
- clips?: ClipConfig[];
118
- /**
119
- * Convenience: creates a single piano-roll `<daw-clip>` child with these
120
- * notes and sets `render-mode="piano-roll"` on the track. Equivalent to
121
- * passing `{ renderMode: 'piano-roll', clips: [{ midiNotes, midiChannel, midiProgram }] }`.
122
- * An explicit `renderMode` takes precedence over the inferred `'piano-roll'`.
123
- *
124
- * Creation-only — ignored by `updateTrack`. To modify notes after the
125
- * track is built, use `editor.updateClip(trackId, clipId, { midiNotes })` or
126
- * mutate the `<daw-clip>` element's `midiNotes` property directly.
127
- */
128
- midi?: {
129
- notes: MidiNoteData[];
130
- channel?: number;
131
- program?: number;
132
- };
133
- }
134
- /**
135
- * Public input shape for clips passed via `TrackConfig.clips` or
136
- * `editor.addClip(trackId, config)`. `src` is optional to support MIDI clips
137
- * with no audio source. Other fields default to the matching `<daw-clip>`
138
- * attribute defaults.
139
- */
140
- interface ClipConfig {
141
- src?: string;
142
- peaksSrc?: string;
143
- start?: number;
144
- duration?: number;
145
- offset?: number;
146
- gain?: number;
147
- name?: string;
148
- fadeIn?: number;
149
- fadeOut?: number;
150
- fadeType?: FadeType;
151
- midiNotes?: MidiNoteData[];
152
- midiChannel?: number;
153
- midiProgram?: number;
154
- }
155
-
156
49
  declare class DawTrackElement extends LitElement {
157
50
  src: string;
158
51
  name: string;
@@ -160,7 +53,9 @@ declare class DawTrackElement extends LitElement {
160
53
  pan: number;
161
54
  muted: boolean;
162
55
  soloed: boolean;
163
- renderMode: TrackRenderMode;
56
+ get renderMode(): RenderMode;
57
+ set renderMode(value: RenderMode);
58
+ private _renderMode;
164
59
  spectrogramConfig: SpectrogramConfig | null;
165
60
  readonly trackId: `${string}-${string}-${string}-${string}-${string}`;
166
61
  createRenderRoot(): this;
@@ -360,6 +255,113 @@ declare global {
360
255
  }
361
256
  }
362
257
 
258
+ type TrackRenderMode = RenderMode;
259
+ interface TrackDescriptor {
260
+ name: string;
261
+ src: string;
262
+ volume: number;
263
+ pan: number;
264
+ muted: boolean;
265
+ soloed: boolean;
266
+ renderMode: TrackRenderMode;
267
+ spectrogramConfig?: SpectrogramConfig | null;
268
+ clips: ClipDescriptor[];
269
+ }
270
+ /**
271
+ * Common fields shared by all clip descriptors regardless of source.
272
+ */
273
+ interface BaseClipDescriptor {
274
+ src: string;
275
+ peaksSrc: string;
276
+ start: number;
277
+ duration: number;
278
+ offset: number;
279
+ gain: number;
280
+ name: string;
281
+ fadeIn: number;
282
+ fadeOut: number;
283
+ fadeType: FadeType;
284
+ midiNotes: MidiNoteData[] | null;
285
+ midiChannel: number | null;
286
+ midiProgram: number | null;
287
+ }
288
+ /**
289
+ * A clip descriptor sourced from a `<daw-clip>` DOM element. `clipId` is
290
+ * always set — `<daw-clip>.clipId` is a `crypto.randomUUID()` generated at
291
+ * construction. Engine `clip.id` is aligned with this id in `_loadTrack`
292
+ * and `_loadAndAppendClip` so DOM and engine reference the same clip.
293
+ */
294
+ interface DomClipDescriptor extends BaseClipDescriptor {
295
+ kind: 'dom';
296
+ clipId: string;
297
+ }
298
+ /**
299
+ * A clip descriptor synthesized from a non-DOM source — file drops, the
300
+ * `<daw-track src>` shorthand fallback, or recording-clip insertion. No
301
+ * `clipId` because there's no DOM element to align with; the engine
302
+ * generates its own id at clip-creation time.
303
+ */
304
+ interface DropClipDescriptor extends BaseClipDescriptor {
305
+ kind: 'drop';
306
+ }
307
+ type ClipDescriptor = DomClipDescriptor | DropClipDescriptor;
308
+ /**
309
+ * Type predicate for the `'dom'` discriminator. Use to narrow a
310
+ * `ClipDescriptor` to `DomClipDescriptor` (which has `clipId`) without
311
+ * inline `c.kind === 'dom'` repetition.
312
+ */
313
+ declare function isDomClip(desc: ClipDescriptor): desc is DomClipDescriptor;
314
+ /**
315
+ * Public input shape for `editor.addTrack(config)`. All fields optional —
316
+ * defaults match the declarative `<daw-track>` defaults.
317
+ */
318
+ interface TrackConfig {
319
+ name?: string;
320
+ volume?: number;
321
+ pan?: number;
322
+ muted?: boolean;
323
+ soloed?: boolean;
324
+ renderMode?: TrackRenderMode;
325
+ spectrogramConfig?: SpectrogramConfig | null;
326
+ clips?: ClipConfig[];
327
+ /**
328
+ * Convenience: creates a single piano-roll `<daw-clip>` child with these
329
+ * notes and sets `render-mode="piano-roll"` on the track. Equivalent to
330
+ * passing `{ renderMode: 'piano-roll', clips: [{ midiNotes, midiChannel, midiProgram }] }`.
331
+ * An explicit `renderMode` takes precedence over the inferred `'piano-roll'`.
332
+ *
333
+ * Creation-only — ignored by `updateTrack`. To modify notes after the
334
+ * track is built, use `editor.updateClip(trackId, clipId, { midiNotes })` or
335
+ * mutate the `<daw-clip>` element's `midiNotes` property directly.
336
+ */
337
+ midi?: {
338
+ notes: MidiNoteData[];
339
+ channel?: number;
340
+ program?: number;
341
+ };
342
+ }
343
+ /**
344
+ * Public input shape for clips passed via `TrackConfig.clips` or
345
+ * `editor.addClip(trackId, config)`. `src` is optional to support MIDI clips
346
+ * with no audio source. Other fields default to the matching `<daw-clip>`
347
+ * attribute defaults.
348
+ */
349
+ interface ClipConfig {
350
+ src?: string;
351
+ peaksSrc?: string;
352
+ start?: number;
353
+ duration?: number;
354
+ offset?: number;
355
+ gain?: number;
356
+ name?: string;
357
+ fadeIn?: number;
358
+ fadeOut?: number;
359
+ fadeType?: FadeType;
360
+ midiNotes?: MidiNoteData[];
361
+ midiChannel?: number;
362
+ midiProgram?: number;
363
+ }
364
+
363
365
  /**
364
366
  * Peak generation pipeline: AudioBuffer → web worker → WaveformData → PeakData.
365
367
  *
@@ -425,6 +427,30 @@ declare class PeakPipeline {
425
427
  private _getWaveformData;
426
428
  }
427
429
 
430
+ declare class DawRulerElement extends LitElement {
431
+ samplesPerPixel: number;
432
+ sampleRate: number;
433
+ duration: number;
434
+ rulerHeight: number;
435
+ scaleMode: 'temporal' | 'beats';
436
+ ticksPerPixel: number;
437
+ meterEntries: MeterEntry[];
438
+ ppqn: number;
439
+ totalWidth: number;
440
+ private _tickData;
441
+ private _musicalTickData;
442
+ static styles: lit.CSSResult;
443
+ willUpdate(): void;
444
+ render(): lit.TemplateResult<1>;
445
+ updated(): void;
446
+ private _drawTicks;
447
+ }
448
+ declare global {
449
+ interface HTMLElementTagNameMap {
450
+ 'daw-ruler': DawRulerElement;
451
+ }
452
+ }
453
+
428
454
  declare class DawTrackControlsElement extends LitElement {
429
455
  /** Track ID — set by the editor to link controls to a track row. */
430
456
  trackId: string | null;
@@ -434,6 +460,7 @@ declare class DawTrackControlsElement extends LitElement {
434
460
  muted: boolean;
435
461
  soloed: boolean;
436
462
  static styles: lit.CSSResult;
463
+ protected firstUpdated(): void;
437
464
  private _onVolumeInput;
438
465
  private _onPanInput;
439
466
  private _onMuteClick;
@@ -792,6 +819,12 @@ interface DawClipSplitDetail {
792
819
  }
793
820
  interface DawSpectrogramReadyDetail {
794
821
  trackId: string;
822
+ generation: number;
823
+ }
824
+ interface DawSpectrogramErrorDetail {
825
+ trackId: string;
826
+ generation: number;
827
+ error: Error;
795
828
  }
796
829
  interface DawEventMap {
797
830
  'daw-selection': CustomEvent<DawSelectionDetail>;
@@ -821,6 +854,7 @@ interface DawEventMap {
821
854
  'daw-clip-trim': CustomEvent<DawClipTrimDetail>;
822
855
  'daw-clip-split': CustomEvent<DawClipSplitDetail>;
823
856
  'daw-spectrogram-ready': CustomEvent<DawSpectrogramReadyDetail>;
857
+ 'daw-spectrogram-error': CustomEvent<DawSpectrogramErrorDetail>;
824
858
  }
825
859
  type DawEvent<K extends keyof DawEventMap> = DawEventMap[K];
826
860
  interface LoadFilesResult {
@@ -906,9 +940,10 @@ declare class DawEditorElement extends LitElement implements MidiLoaderHost {
906
940
  /** Called by <daw-spectrogram> on chunk unmount / element disconnect. */
907
941
  _spectrogramUnregisterCanvas(canvasId: string): void;
908
942
  /**
909
- * Push a clip's decoded audio into the spectrogram controller. No-op
910
- * unless the track is in spectrogram render-mode and the controller
911
- * already exists (it bootstraps from canvas registration).
943
+ * Forward a clip's AudioBuffer to the spectrogram controller if the parent
944
+ * track is in spectrogram render-mode. Eagerly creates the controller via
945
+ * `_ensureSpectrogramController` so the audio data is queued for the first
946
+ * render — even if no canvases have been registered yet.
912
947
  */
913
948
  private _maybeRegisterSpectrogramClipAudio;
914
949
  scaleMode: 'temporal' | 'beats';
@@ -982,6 +1017,7 @@ declare class DawEditorElement extends LitElement implements MidiLoaderHost {
982
1017
  isMidiClip(trackId: string, clipId: string): boolean;
983
1018
  private _pointer;
984
1019
  private _viewport;
1020
+ private _scrollSync;
985
1021
  static styles: lit.CSSResult[];
986
1022
  get effectiveSampleRate(): number;
987
1023
  resolveAudioContextSampleRate(rate: number): void;
@@ -1014,7 +1050,10 @@ declare class DawEditorElement extends LitElement implements MidiLoaderHost {
1014
1050
  * `_selectedTrackId`, etc.) — most of which don't affect the spectrogram
1015
1051
  * viewport. Skip the cross-controller call when nothing changed.
1016
1052
  *
1017
- * The orchestrator dedupes too, but this avoids the call entirely.
1053
+ * The orchestrator dedupes identical viewports too, so removing this cache
1054
+ * wouldn't change observable behavior — but it would push a fresh
1055
+ * `setViewport` call (with object allocation) into every Lit reactive
1056
+ * update for properties unrelated to the viewport.
1018
1057
  */
1019
1058
  private _lastSpectrogramViewport;
1020
1059
  protected updated(_changed: Map<string, unknown>): void;
@@ -1212,6 +1251,10 @@ declare class DawEditorElement extends LitElement implements MidiLoaderHost {
1212
1251
  _startPlayhead(): void;
1213
1252
  _stopPlayhead(): void;
1214
1253
  private _getPlayhead;
1254
+ /** True when the controls column should be rendered (and its selector is valid). */
1255
+ private get _showControls();
1256
+ /** True when the ruler header band should be rendered (and its selector is valid). */
1257
+ private get _showRuler();
1215
1258
  private _getOrderedTracks;
1216
1259
  render(): lit.TemplateResult<1>;
1217
1260
  }
@@ -1221,30 +1264,6 @@ declare global {
1221
1264
  }
1222
1265
  }
1223
1266
 
1224
- declare class DawRulerElement extends LitElement {
1225
- samplesPerPixel: number;
1226
- sampleRate: number;
1227
- duration: number;
1228
- rulerHeight: number;
1229
- scaleMode: 'temporal' | 'beats';
1230
- ticksPerPixel: number;
1231
- meterEntries: MeterEntry[];
1232
- ppqn: number;
1233
- totalWidth: number;
1234
- private _tickData;
1235
- private _musicalTickData;
1236
- static styles: lit.CSSResult;
1237
- willUpdate(): void;
1238
- render(): lit.TemplateResult<1>;
1239
- updated(): void;
1240
- private _drawTicks;
1241
- }
1242
- declare global {
1243
- interface HTMLElementTagNameMap {
1244
- 'daw-ruler': DawRulerElement;
1245
- }
1246
- }
1247
-
1248
1267
  declare class DawSelectionElement extends LitElement {
1249
1268
  startPx: number;
1250
1269
  endPx: number;
@@ -1349,6 +1368,7 @@ declare class DawSpectrogramElement extends LitElement {
1349
1368
  static styles: lit.CSSResult;
1350
1369
  private _canvases;
1351
1370
  private _registeredCanvasIds;
1371
+ private _warnedNoHost;
1352
1372
  /**
1353
1373
  * Walk up to the editor host. `closest('daw-editor')` does NOT cross
1354
1374
  * shadow boundaries — and this element lives inside the editor's shadow