@dawcore/components 0.0.23 → 0.0.24

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
@@ -10,11 +10,16 @@ Framework-agnostic Web Components for multi-track audio editing. Drop `<daw-edit
10
10
  - **Drag interactions** — Move clips, trim boundaries, split at playhead
11
11
  - **Keyboard shortcuts** — Play/pause, split, undo/redo via `<daw-keyboard-shortcuts>`
12
12
  - **Undo/redo** — Full transaction-based undo with Cmd/Ctrl+Z and Cmd/Ctrl+Shift+Z
13
- - **File drop** — Drag audio files onto the editor to add tracks
13
+ - **File drop** — Drag audio files onto the editor to add tracks (enable with the `file-drop` attribute)
14
14
  - **Recording** — Live mic recording with waveform preview, pause/resume, cancelable clip creation
15
15
  - **Pre-computed peaks** — Instant waveform rendering from `.dat` files before audio decodes
16
- - **MIDI tracks** — Programmatic MIDI clips render as piano-roll via `<daw-piano-roll>`. Playback via Tone.js adapter (native MIDI synth deferred). See [MIDI Tracks](#midi-tracks).
16
+ - **MIDI tracks** — Declarative or programmatic MIDI clips render as piano-roll. Playback via the Tone.js adapter. See [MIDI Tracks](#midi-tracks).
17
+ - **MIDI file loading** — `editor.loadMidi(urlOrFile)` parses a `.mid` file into piano-roll tracks via the optional `@dawcore/midi` peer
18
+ - **Spectrogram rendering** — `<daw-track render-mode="spectrogram">` renders FFT spectrograms via `@dawcore/spectrogram`
17
19
  - **Track controls** — Volume, pan, mute, solo per track via `<daw-track-controls>`
20
+ - **Effects chains** — Per-track and master effect chains with built-in `native-*` effects, WAM 2.0 plugins via `@dawcore/wam`, and in-browser-compiled Faust DSP via `@dawcore/faust`. See [Effects](#effects).
21
+ - **Effect GUIs and persistence** — Mount plugin GUIs into your own panels; snapshot and restore whole chains. See [Effect GUIs](#effect-guis) and [Effects Persistence](#effects-persistence).
22
+ - **Offline export** — `editor.exportAudio()` renders the session (clips, mix, all effect chains) to an `AudioBuffer`. See [Offline Export](#offline-export).
18
23
  - **Transport access** — Tempo, metronome, count-in, meter, effects via `@dawcore/transport`
19
24
  - **CSS theming** — Dark mode by default, fully customizable via CSS custom properties
20
25
  - **Native Web Audio** — Uses `@dawcore/transport` for playback scheduling. No Tone.js dependency.
@@ -25,21 +30,27 @@ Framework-agnostic Web Components for multi-track audio editing. Drop `<daw-edit
25
30
  npm install @dawcore/components
26
31
  ```
27
32
 
28
- Peer dependencies:
33
+ Required peer dependencies:
34
+
29
35
  ```bash
30
36
  npm install @waveform-playlist/core @waveform-playlist/engine
31
37
  ```
32
38
 
33
39
  Audio backend (choose one — see [Choosing an Audio Backend](#choosing-an-audio-backend)):
40
+
34
41
  ```bash
35
42
  npm install @dawcore/transport # Native Web Audio (recommended)
36
43
  # or
37
44
  npm install @waveform-playlist/playout tone # Tone.js
38
45
  ```
39
46
 
40
- Optional (for recording):
47
+ Optional peer dependencies — each is dynamic-imported on first use, so you only install (and ship) what you use:
48
+
41
49
  ```bash
42
- npm install @waveform-playlist/worklets
50
+ npm install @waveform-playlist/worklets # recording
51
+ npm install @dawcore/midi # editor.loadMidi()
52
+ npm install @dawcore/wam # WAM 2.0 plugins (addWamPlugin) + generic effect GUIs
53
+ npm install @dawcore/faust @dawcore/wam # in-browser Faust compilation (addFaustEffect)
43
54
  ```
44
55
 
45
56
  ## Quick Start
@@ -93,7 +104,7 @@ adapter.transport.setCountIn(true);
93
104
 
94
105
  ### Tone.js (effects, MIDI synths)
95
106
 
96
- Uses Tone.js for audio processing. Single tempo/meter only. **Required for MIDI playback** — the native adapter has no MIDI synth yet, so MIDI clips render as piano-roll but are silent.
107
+ Uses Tone.js for audio processing. Single tempo/meter only. **Required for MIDI playback** — the native adapter has no MIDI synth, so MIDI clips render as piano-roll but play silently on it.
97
108
 
98
109
  ```bash
99
110
  npm install @waveform-playlist/playout tone
@@ -128,7 +139,7 @@ For multiple clips per track with independent positioning:
128
139
 
129
140
  ## MIDI Tracks
130
141
 
131
- Programmatic MIDI clips render as piano-roll. Playback requires the Tone.js adapter (the native adapter has no MIDI synth yet). Use the `editor.addTrack({ midi })` sugar for the simplest path:
142
+ Programmatic MIDI clips render as piano-roll. Playback requires the Tone.js adapter (the native adapter has no MIDI synth). Use the `editor.addTrack({ midi })` sugar for the simplest path:
132
143
 
133
144
  ```javascript
134
145
  import { createToneAdapter } from '@waveform-playlist/playout';
@@ -164,7 +175,7 @@ This expands to a `<daw-track render-mode="piano-roll">` containing a `<daw-clip
164
175
  </script>
165
176
  ```
166
177
 
167
- A clip is treated as MIDI iff `clip.midiNotes != null`. MIDI clips skip audio fetch + decode + peak generation. Trim handles and split-at-playhead are inert on MIDI clips for now (note slicing is a follow-up). Move drag works.
178
+ A clip is treated as MIDI iff `clip.midiNotes != null`. MIDI clips skip audio fetch + decode + peak generation. Move drag works on MIDI clips; trim handles and split-at-playhead are inert on them.
168
179
 
169
180
  **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
181
 
@@ -184,6 +195,24 @@ For instant waveform rendering before audio finishes decoding:
184
195
 
185
196
  The `.dat` file renders the waveform immediately. Audio decodes in the background for playback.
186
197
 
198
+ ## Effects Persistence
199
+
200
+ `getEffectsState()` / `setEffectsState(entries)` on `<daw-editor>` (master chain) and `<daw-track>` snapshot and restore effect chains. Persist the returned array however you like (localStorage, server, project file):
201
+
202
+ ```js
203
+ const saved = await editor.getEffectsState();
204
+ // [
205
+ // { kind: 'native', type: 'native-delay', params: {...}, bypassed: false },
206
+ // { kind: 'wam', url: 'https://…/index.js', bypassed: false, state: {…} },
207
+ // ]
208
+ localStorage.setItem('master-fx', JSON.stringify(saved));
209
+
210
+ // later / next session
211
+ await editor.setEffectsState(JSON.parse(localStorage.getItem('master-fx')));
212
+ ```
213
+
214
+ WAM entries carry the plugin's own `getState()` snapshot, reapplied on restore. Faust entries (added with `addFaustEffect`) persist their DSP source instead of a URL — `{ kind: 'wam', faustDsp, faustName, bypassed, state }` — and are recompiled in the browser on restore. If a saved plugin URL is unreachable (or a saved Faust source no longer compiles), the restore **continues**: the entry becomes a bypassed passthrough placeholder at its saved position (a `daw-effect-error` event fires with `{effectId, url?, source?, message}`), and its saved state is retained so re-serializing round-trips it for a later retry.
215
+
187
216
  ## Transport Access
188
217
 
189
218
  Transport-specific APIs are on the `NativePlayoutAdapter` reference:
@@ -202,6 +231,88 @@ adapter.transport.on('countIn', ({ beat, totalBeats }) => {
202
231
  adapter.transport.connectTrackOutput('track-id', reverbNode);
203
232
  ```
204
233
 
234
+ ## Effects
235
+
236
+ Per-track and master effect chains (requires `@dawcore/transport` >= 0.0.13). `<daw-track>` owns its track chain; `<daw-editor>` owns the master chain — same API on both:
237
+
238
+ ```javascript
239
+ const track = document.querySelector('daw-track');
240
+
241
+ // Built-in native-* effects: native-gain, native-filter, native-compressor,
242
+ // native-stereo-panner, native-delay
243
+ const filterId = track.addEffect('native-filter', { frequency: 800 });
244
+ const compressorId = editor.addEffect('native-compressor'); // master chain
245
+
246
+ track.setEffectParams(filterId, { frequency: 2000 }); // live, during playback
247
+ track.setEffectBypassed(filterId, true);
248
+ track.moveEffect(filterId, 1);
249
+ track.removeEffect(filterId);
250
+ console.log(track.effects); // [{ id, kind, type, params, bypassed }, ...]
251
+ ```
252
+
253
+ Effect events dispatch from the owning element and bubble to the editor:
254
+
255
+ ```javascript
256
+ editor.addEventListener('daw-effect-add', (e) => console.log(e.detail));
257
+ // also: daw-effect-remove, daw-effect-change, daw-effect-bypass, daw-effect-reorder
258
+ ```
259
+
260
+ Register custom effects with `registerEffect(type, definition)`; inspect built-ins with `getEffectDefinitions()` (exports from `@dawcore/components`).
261
+
262
+ ### WAM Plugins
263
+
264
+ [Web Audio Modules 2.0](https://www.webaudiomodules.com/) plugins load into the same chains via the optional `@dawcore/wam` peer (`npm install @dawcore/wam`):
265
+
266
+ ```javascript
267
+ const wamId = await track.addWamPlugin('https://www.webaudiomodules.com/community/plugins/burns-audio/delay/index.js');
268
+ // WAM entries are ordinary chain entries — bypass/move/remove/events all work
269
+ ```
270
+
271
+ See `examples/dawcore-native/effects.html` for a native-effects demo and `examples/dawcore-wam/` (`pnpm example:dawcore-wam`) for the end-to-end WAM demo: URL paste, community-library picker (`fetchWamLibrary`), GUIs, persistence with reload, and WAV export.
272
+
273
+ ### Faust Effects (compiled in the browser)
274
+
275
+ Write custom DSP in [Faust](https://faust.grame.fr/) and hear it instantly — `addFaustEffect(dspCode, options?)` compiles the source **in the browser** via the optional `@dawcore/faust` peer (`npm install @dawcore/faust @dawcore/wam`) and adds the result to the chain as an ordinary WAM entry:
276
+
277
+ ```javascript
278
+ // Track chains are stereo — duplicate mono filters across both channels.
279
+ const effectId = await track.addFaustEffect(
280
+ `import("stdfaust.lib");
281
+ cutoff = hslider("cutoff", 1000, 20, 20000, 1);
282
+ process = fi.lowpass(2, cutoff), fi.lowpass(2, cutoff);`,
283
+ { name: 'My Lowpass' }
284
+ );
285
+ // Every hslider/vslider/checkbox becomes a WAM parameter + a GUI control.
286
+ ```
287
+
288
+ The Faust compiler (~2.5 MB gzipped WASM) loads lazily on the first call — consumers who never compile Faust load zero compiler bytes. Compile errors keep Faust's line/column diagnostics and leave the chain untouched. Faust entries persist as their DSP source (`{ kind: 'wam', faustDsp, faustName, state }` — no URL) and are **recompiled** on `setEffectsState` restore and offline export. See the "Faust (compile in browser)" section of `examples/dawcore-wam/`.
289
+
290
+ ### Effect GUIs
291
+
292
+ `openEffectGui(effectId, container)` / `closeEffectGui(effectId)` on both elements mount an effect's GUI into a container **you** provide (your own panel, drawer, floating window — dawcore ships no plugin-window UI):
293
+
294
+ ```javascript
295
+ const panel = document.querySelector('#fx-panel');
296
+
297
+ const guiEl = await track.openEffectGui(wamId, panel); // WAM plugins mount their own GUI
298
+ track.closeEffectGui(wamId); // hides — audio keeps processing, element stays cached
299
+ await track.openEffectGui(wamId, panel); // instant reopen, same element
300
+ ```
301
+
302
+ The GUI is created lazily on first open and only destroyed (`destroyGui`) when the effect — or its track — is removed from the chain. Plugins without a GUI, and `native-*` effects, get a generic parameter panel (labeled sliders from the plugin's `getParameterInfo()` or the registry's params metadata) rendered by `@dawcore/wam`; slider edits apply live and dispatch `daw-effect-change` like any other parameter edit.
303
+
304
+ ### Offline Export
305
+
306
+ `editor.exportAudio(options?)` renders the whole session — clips, volume/pan, mute/solo, per-track and master effect chains — on an `OfflineAudioContext` and resolves to an `AudioBuffer` (encode it to WAV/FLAC however you like):
307
+
308
+ ```javascript
309
+ const buffer = await editor.exportAudio();
310
+ // options: { sampleRate?, startTime?, duration?, channels? }
311
+ const intro = await editor.exportAudio({ startTime: 0, duration: 8, channels: 2 });
312
+ ```
313
+
314
+ Chains rebuild from their persisted form: `native-*` effects via the registry, WAM plugins re-instantiated on the offline context with their saved state (worklets are bound to one context), and Faust entries recompiled from their persisted DSP source. Bypass behavior matches live playback; all offline plugin instances are destroyed after rendering.
315
+
205
316
  ## Programmatic File Loading
206
317
 
207
318
  ```javascript
@@ -292,26 +403,39 @@ Core orchestrator. Attributes:
292
403
 
293
404
  | Attribute | Type | Default | Description |
294
405
  |-----------|------|---------|-------------|
295
- | `samples-per-pixel` | number | `1024` | Zoom level |
296
- | `sample-rate` | number | `48000` | AudioContext sample rate hint |
297
- | `wave-height` | number | `100` | Track waveform height in pixels |
406
+ | `samples-per-pixel` | number | `1024` | Zoom level (clamped to the pre-computed peaks floor when one is active) |
407
+ | `wave-height` | number | `128` | Track waveform height in pixels |
298
408
  | `timescale` | boolean | `false` | Show time ruler |
299
409
  | `clip-headers` | boolean | `false` | Show clip name headers |
410
+ | `clip-header-height` | number | `20` | Clip header height in pixels |
300
411
  | `interactive-clips` | boolean | `false` | Enable drag/trim/split |
412
+ | `file-drop` | boolean | `false` | Accept audio files dropped onto the editor |
301
413
  | `mono` | boolean | `false` | Merge stereo to mono display |
302
- | `eager-resume` | boolean | `false` | Resume AudioContext on first user gesture |
414
+ | `bar-width` / `bar-gap` | number | `1` / `0` | Waveform bar rendering style |
415
+ | `indefinite-playback` | boolean | `false` | Keep ruler/timeline filling the viewport with no clips |
416
+ | `scale-mode` | string | `'temporal'` | `'temporal'` (seconds) or `'beats'` (tick-linear grid) |
417
+ | `ticks-per-pixel` | number | `24` | Zoom level in beats mode |
418
+ | `snap-to` | string | `'off'` | Grid snapping in beats mode (`'bar'`, `'beat'`, `'1/2'`…`'1/16'`, `'off'`) |
419
+ | `eager-resume` | string | — | Resume AudioContext on first gesture; bare attribute targets the editor, or pass `"document"` / a CSS selector |
420
+
421
+ JS properties: `adapter` (required `PlayoutAdapter`), `recordingStream`, `bpm`, `ppqn`, `timeSignature`, `meterEntries`, `secondsToTicks` / `ticksToSeconds` (variable-tempo callbacks), `spectrogramConfig`, `spectrogramColorMap`. Read-only: `engine`, `audioContext` (from the adapter), `tracks`, `selection`, `selectedTrackId`, `currentTime`, `canUndo` / `canRedo`, `isRecording`.
303
422
 
304
- JS properties: `audioContext`, `recordingStream`, `engine`.
423
+ Methods:
305
424
 
306
- Methods: `loadFiles(fileList)`, `splitAtPlayhead()`.
425
+ - Playback: `play(startTime?)`, `pause()`, `stop()`, `togglePlayPause()`, `seekTo(time)`
426
+ - Loading: `loadFiles(files)`, `loadMidi(urlOrFile, options?)`, `ready()`
427
+ - Tracks & clips: `addTrack(config)`, `removeTrack(id)`, `updateTrack(id, partial)`, `addClip(trackId, config)`, `removeClip(trackId, clipId)`, `updateClip(trackId, clipId, partial)`
428
+ - Editing: `splitAtPlayhead()`, `undo()`, `redo()`, `setSelection(start, end)`
429
+ - Recording: `startRecording(stream?, options?)`, `stopRecording()`, `pauseRecording()`, `resumeRecording()`, `togglePauseRecording()`
430
+ - Effects & export: the master-chain effects API (see [Effects](#effects)), `getEffectsState()` / `setEffectsState(entries)`, `openEffectGui()` / `closeEffectGui()`, `exportAudio(options?)`
307
431
 
308
432
  ### `<daw-track>`
309
433
 
310
- Declarative track data. Attributes: `src`, `name`, `volume`, `pan`, `muted`, `soloed`, `mono`, `render-mode` (`'waveform' | 'piano-roll'`, default `'waveform'`).
434
+ Declarative track data. Attributes: `src`, `name`, `volume`, `pan`, `muted`, `soloed`, `render-mode` (`'waveform' | 'piano-roll' | 'spectrogram'`, default `'waveform'`). JS property: `spectrogramConfig` (per-track spectrogram override). Also exposes the per-track effects API (see [Effects](#effects)).
311
435
 
312
436
  ### `<daw-clip>`
313
437
 
314
- Declarative clip data. Attributes: `src`, `peaks-src`, `start`, `duration`, `offset`, `gain`, `midi-channel`, `midi-program`. JS-only property: `midiNotes: MidiNoteData[] | null` (note arrays are too large for attributes).
438
+ Declarative clip data. Attributes: `src`, `peaks-src`, `start`, `duration`, `offset`, `gain`, `name`, `color`, `midi-channel`, `midi-program`. JS-only property: `midiNotes: MidiNoteData[] | null` (note arrays are too large for attributes).
315
439
 
316
440
  ### `<daw-piano-roll>`
317
441
 
@@ -356,12 +480,20 @@ editor.addEventListener('daw-recording-complete', (e) => {
356
480
  console.log(e.detail.audioBuffer);
357
481
  });
358
482
 
483
+ // Effects (track events bubble up to the editor)
484
+ editor.addEventListener('daw-effect-add', (e) => console.log(e.detail));
485
+ editor.addEventListener('daw-effect-change', (e) => console.log(e.detail));
486
+ // also: daw-effect-remove, daw-effect-bypass, daw-effect-reorder
487
+
359
488
  // Errors
360
489
  editor.addEventListener('daw-track-error', (e) => console.error(e.detail));
361
490
  editor.addEventListener('daw-error', (e) => console.error(e.detail));
362
491
  editor.addEventListener('daw-files-load-error', (e) => console.error(e.detail));
492
+ editor.addEventListener('daw-effect-error', (e) => console.error(e.detail));
363
493
  ```
364
494
 
495
+ All events and their `detail` payloads are typed in the exported `DawEventMap`.
496
+
365
497
  ## Custom AudioContext
366
498
 
367
499
  Pass a custom `AudioContext` via the adapter:
@@ -374,6 +506,13 @@ editor.adapter = adapter;
374
506
 
375
507
  Set the adapter before tracks load. The provided context is used for decoding, playback, and recording.
376
508
 
509
+ ## Examples & Documentation
510
+
511
+ - [`examples/dawcore-native/`](https://github.com/naomiaro/waveform-playlist/tree/main/examples/dawcore-native) — native transport: basics, multi-clip, metronome, beats grid, beat maps, effects, spectrogram, recording (`pnpm example:dawcore-native`)
512
+ - [`examples/dawcore-tone/`](https://github.com/naomiaro/waveform-playlist/tree/main/examples/dawcore-tone) — Tone.js adapter: MIDI, SoundFont playback (`pnpm example:dawcore-tone`)
513
+ - [`examples/dawcore-wam/`](https://github.com/naomiaro/waveform-playlist/tree/main/examples/dawcore-wam) — WAM plugins end-to-end + in-browser Faust compilation (`pnpm example:dawcore-wam`)
514
+ - Guides: [naomiaro.github.io/waveform-playlist](https://naomiaro.github.io/waveform-playlist/docs/web-components/getting-started)
515
+
377
516
  ## License
378
517
 
379
518
  MIT
package/dist/index.d.mts CHANGED
@@ -46,6 +46,87 @@ declare global {
46
46
  }
47
47
  }
48
48
 
49
+ /** Range/display metadata for one effect parameter. */
50
+ interface EffectParamDef {
51
+ min: number;
52
+ max: number;
53
+ step?: number;
54
+ /** 's', 'ms', 'Hz', 'dB', '%' … */
55
+ unit?: string;
56
+ }
57
+ /**
58
+ * A live, wired effect. `input`/`output` are the chain attachment points
59
+ * (the same node for single-node effects). `applyParams` updates AudioParams
60
+ * in place — the chain never rebuilds for parameter changes.
61
+ */
62
+ interface EffectInstance {
63
+ input: AudioNode;
64
+ output: AudioNode;
65
+ applyParams: (params: Record<string, number>) => void;
66
+ dispose?: () => void;
67
+ /** Serializable snapshot of plugin-internal state (WAM getState). */
68
+ getState?: () => Promise<unknown>;
69
+ /** Builds the plugin's own GUI element (WAM createGui). GUI lifecycle is
70
+ * independent of audio — hiding/destroying a GUI never stops processing. */
71
+ createGui?: () => Promise<HTMLElement>;
72
+ /** Releases a GUI element previously built by `createGui` (WAM destroyGui). */
73
+ destroyGui?: (gui: HTMLElement) => void;
74
+ /** Parameter metadata for the generic fallback panel (WAM getParameterInfo). */
75
+ getParameterInfo?: () => Promise<unknown>;
76
+ }
77
+ /** A registered effect type. `create` must work on any BaseAudioContext so
78
+ * the same definitions serve offline rendering. */
79
+ interface EffectDefinition {
80
+ label: string;
81
+ category: string;
82
+ defaults: Record<string, number>;
83
+ params: Record<string, EffectParamDef>;
84
+ /** Name of the wet/dry param, when the effect has one. Bypass zeroes it
85
+ * (storing the original); effects without one are bypassed by
86
+ * disconnection. */
87
+ wetParam?: string;
88
+ create: (audioContext: BaseAudioContext, params: Record<string, number>) => EffectInstance;
89
+ }
90
+ /** Public, serializable view of one chain entry. */
91
+ interface EffectState {
92
+ id: string;
93
+ kind: string;
94
+ type: string;
95
+ params: Record<string, number>;
96
+ bypassed: boolean;
97
+ url?: string;
98
+ /** Present on Faust entries: the DSP source this effect was compiled from. */
99
+ source?: {
100
+ faust: string;
101
+ };
102
+ label?: string;
103
+ error?: string;
104
+ }
105
+ /** Persisted form of a chain — see README for the consumer contract. */
106
+ type SerializedEffectEntry = {
107
+ kind: 'native';
108
+ type: string;
109
+ params: Record<string, number>;
110
+ bypassed: boolean;
111
+ } | {
112
+ kind: 'wam';
113
+ /** Module URL for url-loaded plugins. Absent for Faust entries. */
114
+ url?: string;
115
+ bypassed: boolean;
116
+ state?: unknown;
117
+ /** Faust DSP source — restore recompiles in-browser via @dawcore/faust
118
+ * instead of fetching a URL. */
119
+ faustDsp?: string;
120
+ /** Name the Faust entry was compiled under (restores the same label). */
121
+ faustName?: string;
122
+ };
123
+ /** Result of creating an effect via the registry. */
124
+ interface CreatedEffect {
125
+ instance: EffectInstance;
126
+ params: Record<string, number>;
127
+ wetParam?: string;
128
+ }
129
+
49
130
  declare class DawTrackElement extends LitElement {
50
131
  src: string;
51
132
  name: string;
@@ -59,6 +140,35 @@ declare class DawTrackElement extends LitElement {
59
140
  spectrogramConfig: SpectrogramConfig | null;
60
141
  readonly trackId: `${string}-${string}-${string}-${string}-${string}`;
61
142
  createRenderRoot(): this;
143
+ addEffect(type: string, params?: Record<string, number>): string;
144
+ /** Load a WAM plugin (via the optional @dawcore/wam peer) into this track's chain. */
145
+ addWamPlugin(url: string, initialState?: unknown): Promise<string>;
146
+ /**
147
+ * Compile Faust DSP source in the browser (via the optional @dawcore/faust
148
+ * peer) and add the resulting WAM to this track's chain. Compile errors
149
+ * keep their Faust line/column diagnostics and leave the chain untouched.
150
+ */
151
+ addFaustEffect(dspCode: string, options?: {
152
+ name?: string;
153
+ }): Promise<string>;
154
+ /** Snapshot this track's chain in its persisted form (see dawcore README). */
155
+ getEffectsState(): Promise<SerializedEffectEntry[]>;
156
+ /** Replace this track's chain with a persisted snapshot. */
157
+ setEffectsState(entries: SerializedEffectEntry[]): Promise<void>;
158
+ /**
159
+ * Open (lazily creating) the GUI for one of this track's effects into a
160
+ * consumer-provided container. Closing hides without interrupting audio;
161
+ * the element is cached for reopen. See <daw-editor>.openEffectGui.
162
+ */
163
+ openEffectGui(effectId: string, container: HTMLElement): Promise<HTMLElement>;
164
+ /** Hide an effect's GUI (cached for reopen — never destroys). */
165
+ closeEffectGui(effectId: string): void;
166
+ removeEffect(effectId: string): void;
167
+ setEffectParams(effectId: string, params: Record<string, number>): void;
168
+ setEffectBypassed(effectId: string, bypassed: boolean): void;
169
+ moveEffect(effectId: string, newIndex: number): void;
170
+ get effects(): EffectState[];
171
+ private _effectsEditor;
62
172
  connectedCallback(): void;
63
173
  private _hasRendered;
64
174
  updated(changed: PropertyValues): void;
@@ -826,6 +936,45 @@ interface DawSpectrogramErrorDetail {
826
936
  generation: number;
827
937
  error: Error;
828
938
  }
939
+ interface DawEffectAddDetail {
940
+ effectId: string;
941
+ kind: string;
942
+ type: string;
943
+ params: Record<string, number>;
944
+ index: number;
945
+ /** Plugin source URL (kind 'wam', url-loaded). */
946
+ url?: string;
947
+ /** Faust DSP source (kind 'wam', compiled in-browser via @dawcore/faust). */
948
+ source?: {
949
+ faust: string;
950
+ };
951
+ }
952
+ interface DawEffectRemoveDetail {
953
+ effectId: string;
954
+ }
955
+ interface DawEffectChangeDetail {
956
+ effectId: string;
957
+ params: Record<string, number>;
958
+ }
959
+ interface DawEffectBypassDetail {
960
+ effectId: string;
961
+ bypassed: boolean;
962
+ }
963
+ interface DawEffectReorderDetail {
964
+ effectId: string;
965
+ fromIndex: number;
966
+ toIndex: number;
967
+ }
968
+ interface DawEffectErrorDetail {
969
+ effectId: string;
970
+ /** Plugin source URL (absent for Faust entries, which have no URL). */
971
+ url?: string;
972
+ /** Faust DSP source of the failed entry, when applicable. */
973
+ source?: {
974
+ faust: string;
975
+ };
976
+ message: string;
977
+ }
829
978
  interface DawEventMap {
830
979
  'daw-selection': CustomEvent<DawSelectionDetail>;
831
980
  'daw-seek': CustomEvent<DawSeekDetail>;
@@ -855,6 +1004,12 @@ interface DawEventMap {
855
1004
  'daw-clip-split': CustomEvent<DawClipSplitDetail>;
856
1005
  'daw-spectrogram-ready': CustomEvent<DawSpectrogramReadyDetail>;
857
1006
  'daw-spectrogram-error': CustomEvent<DawSpectrogramErrorDetail>;
1007
+ 'daw-effect-add': CustomEvent<DawEffectAddDetail>;
1008
+ 'daw-effect-remove': CustomEvent<DawEffectRemoveDetail>;
1009
+ 'daw-effect-change': CustomEvent<DawEffectChangeDetail>;
1010
+ 'daw-effect-bypass': CustomEvent<DawEffectBypassDetail>;
1011
+ 'daw-effect-reorder': CustomEvent<DawEffectReorderDetail>;
1012
+ 'daw-effect-error': CustomEvent<DawEffectErrorDetail>;
858
1013
  }
859
1014
  type DawEvent<K extends keyof DawEventMap> = DawEventMap[K];
860
1015
  interface LoadFilesResult {
@@ -887,6 +1042,42 @@ interface MidiLoaderHost {
887
1042
  querySelectorAll(selector: string): NodeListOf<Element>;
888
1043
  }
889
1044
 
1045
+ interface ExportOptions {
1046
+ /** Default: the host's sample rate. */
1047
+ sampleRate?: number;
1048
+ /** Window start in seconds. Default: 0. */
1049
+ startTime?: number;
1050
+ /** Window length in seconds. Default: host duration minus startTime. */
1051
+ duration?: number;
1052
+ /** Default: 2 (stereo). */
1053
+ channels?: 1 | 2;
1054
+ }
1055
+ interface ExportClip {
1056
+ startSample: number;
1057
+ durationSamples: number;
1058
+ offsetSamples: number;
1059
+ sampleRate: number;
1060
+ gain?: number;
1061
+ audioBuffer?: AudioBuffer;
1062
+ }
1063
+ interface ExportTrack {
1064
+ id: string;
1065
+ volume: number;
1066
+ pan: number;
1067
+ muted: boolean;
1068
+ soloed: boolean;
1069
+ clips: ExportClip[];
1070
+ }
1071
+ /** What the editor provides to the export pipeline. */
1072
+ interface ExportAudioHost {
1073
+ effectiveSampleRate: number;
1074
+ /** Natural session duration in seconds. */
1075
+ duration: number;
1076
+ tracks: ExportTrack[];
1077
+ getMasterEffectsState(): Promise<SerializedEffectEntry[]>;
1078
+ getTrackEffectsState(trackId: string): Promise<SerializedEffectEntry[]>;
1079
+ }
1080
+
890
1081
  declare class DawEditorElement extends LitElement implements MidiLoaderHost {
891
1082
  get samplesPerPixel(): number;
892
1083
  set samplesPerPixel(value: number);
@@ -989,6 +1180,57 @@ declare class DawEditorElement extends LitElement implements MidiLoaderHost {
989
1180
  set adapter(value: PlayoutAdapter | null);
990
1181
  get adapter(): PlayoutAdapter | null;
991
1182
  private _externalAdapter;
1183
+ private _effectsManager;
1184
+ private get _effects();
1185
+ /** Master effects chain — inserted between the master bus and the destination. */
1186
+ addEffect(type: string, params?: Record<string, number>): string;
1187
+ removeEffect(effectId: string): void;
1188
+ setEffectParams(effectId: string, params: Record<string, number>): void;
1189
+ setEffectBypassed(effectId: string, bypassed: boolean): void;
1190
+ moveEffect(effectId: string, newIndex: number): void;
1191
+ get effects(): EffectState[];
1192
+ /** Load a WAM plugin (via the optional @dawcore/wam peer) into the master chain. */
1193
+ addWamPlugin(url: string, initialState?: unknown): Promise<string>;
1194
+ /**
1195
+ * Compile Faust DSP source in the browser (via the optional @dawcore/faust
1196
+ * peer) and add the resulting WAM to the master chain. Compile errors keep
1197
+ * their Faust line/column diagnostics and leave the chain untouched.
1198
+ */
1199
+ addFaustEffect(dspCode: string, options?: {
1200
+ name?: string;
1201
+ }): Promise<string>;
1202
+ /**
1203
+ * Open (lazily creating) the GUI for a master-chain effect into a
1204
+ * consumer-provided container. WAM plugins mount their own GUI; plugins
1205
+ * without one — and native effects — get the generic parameter panel from
1206
+ * @dawcore/wam. The element is cached: closeEffectGui hides it without
1207
+ * interrupting audio, reopening remounts the same element.
1208
+ */
1209
+ openEffectGui(effectId: string, container: HTMLElement): Promise<HTMLElement>;
1210
+ /** Hide a master-chain effect's GUI (cached for reopen — never destroys). */
1211
+ closeEffectGui(effectId: string): void;
1212
+ /** Snapshot the master chain in its persisted form (see dawcore README). */
1213
+ getEffectsState(): Promise<SerializedEffectEntry[]>;
1214
+ /** Replace the master chain with a persisted snapshot. */
1215
+ setEffectsState(entries: SerializedEffectEntry[]): Promise<void>;
1216
+ /**
1217
+ * Render the session offline through all effect chains (per-track +
1218
+ * master), including WAM plugins (re-instantiated on the offline context
1219
+ * with their live state). Returns the rendered AudioBuffer.
1220
+ */
1221
+ exportAudio(options?: ExportOptions): Promise<AudioBuffer>;
1222
+ /** Internal — <daw-track> effects API delegates here (dawcore-internal contract). */
1223
+ _trackAddEffect(trackId: string, target: EventTarget, type: string, params?: Record<string, number>): string;
1224
+ _trackEffectOp(trackId: string, target: EventTarget, op: 'remove' | 'setParams' | 'setBypassed' | 'move', effectId: string, arg?: any): void;
1225
+ _trackEffects(trackId: string): EffectState[];
1226
+ _trackAddWamPlugin(trackId: string, target: EventTarget, url: string, initialState?: unknown): Promise<string>;
1227
+ _trackAddFaustEffect(trackId: string, target: EventTarget, dspCode: string, options?: {
1228
+ name?: string;
1229
+ }): Promise<string>;
1230
+ _trackOpenEffectGui(trackId: string, target: EventTarget, effectId: string, container: HTMLElement): Promise<HTMLElement>;
1231
+ _trackCloseEffectGui(_trackId: string, effectId: string): void;
1232
+ _trackGetEffectsState(trackId: string): Promise<SerializedEffectEntry[]>;
1233
+ _trackSetEffectsState(trackId: string, target: EventTarget, entries: SerializedEffectEntry[]): Promise<void>;
992
1234
  get audioContext(): AudioContext;
993
1235
  _engine: PlaylistEngine | null;
994
1236
  private _warnedMissingTicksToSeconds;
@@ -1496,4 +1738,9 @@ interface SplitHost {
1496
1738
  */
1497
1739
  declare function splitAtPlayhead(host: SplitHost): boolean;
1498
1740
 
1499
- export { AudioResumeController, type ClipConfig, type ClipDescriptor, type ClipEngineContract, ClipPointerHandler, type ClipPointerHost, type DawClipConnectedDetail, DawClipElement, type DawClipErrorDetail, type DawClipIdDetail, type DawClipMoveDetail, type DawClipSplitDetail, type DawClipTrimDetail, type DawClipUpdateDetail, DawEditorElement, type DawErrorDetail, type DawEvent, type DawEventMap, type DawFilesLoadErrorDetail, DawGridElement, DawKeyboardShortcutsElement, DawPauseButtonElement, DawPianoRollElement, DawPlayButtonElement, DawPlayheadElement, DawRecordButtonElement, type DawRecordingCompleteDetail, type DawRecordingErrorDetail, type DawRecordingStartDetail, DawRulerElement, type DawSeekDetail, type DawSelectionDetail, DawSelectionElement, DawSpectrogramElement, DawStopButtonElement, type DawTrackConnectedDetail, type DawTrackControlDetail, DawTrackControlsElement, DawTrackElement, type DawTrackErrorDetail, type DawTrackIdDetail, type DawTrackRemoveDetail, type DawTrackSelectDetail, DawTransportButton, DawTransportElement, DawWaveformElement, type DomClipDescriptor, type DropClipDescriptor, type KeyBinding, type LoadFilesResult, type PlaybackShortcutMap, type PointerEngineContract, RecordingController, type RecordingOptions, type RecordingSession, SpectrogramController, type SplitEngineContract, type SplitHost, type SplittingShortcutMap, type TrackConfig, type TrackDescriptor, type TrackRenderMode, type UndoShortcutMap, type WaveformSegment, isDomClip, splitAtPlayhead };
1741
+ declare function registerEffect(type: string, definition: EffectDefinition): void;
1742
+ /** A copy — mutating the returned map does not affect the registry. */
1743
+ declare function getEffectDefinitions(): Map<string, EffectDefinition>;
1744
+ declare function createEffectInstance(type: string, audioContext: BaseAudioContext, params?: Record<string, number>): CreatedEffect;
1745
+
1746
+ export { AudioResumeController, type ClipConfig, type ClipDescriptor, type ClipEngineContract, ClipPointerHandler, type ClipPointerHost, type DawClipConnectedDetail, DawClipElement, type DawClipErrorDetail, type DawClipIdDetail, type DawClipMoveDetail, type DawClipSplitDetail, type DawClipTrimDetail, type DawClipUpdateDetail, DawEditorElement, type DawEffectAddDetail, type DawEffectBypassDetail, type DawEffectChangeDetail, type DawEffectErrorDetail, type DawEffectRemoveDetail, type DawEffectReorderDetail, type DawErrorDetail, type DawEvent, type DawEventMap, type DawFilesLoadErrorDetail, DawGridElement, DawKeyboardShortcutsElement, DawPauseButtonElement, DawPianoRollElement, DawPlayButtonElement, DawPlayheadElement, DawRecordButtonElement, type DawRecordingCompleteDetail, type DawRecordingErrorDetail, type DawRecordingStartDetail, DawRulerElement, type DawSeekDetail, type DawSelectionDetail, DawSelectionElement, DawSpectrogramElement, DawStopButtonElement, type DawTrackConnectedDetail, type DawTrackControlDetail, DawTrackControlsElement, DawTrackElement, type DawTrackErrorDetail, type DawTrackIdDetail, type DawTrackRemoveDetail, type DawTrackSelectDetail, DawTransportButton, DawTransportElement, DawWaveformElement, type DomClipDescriptor, type DropClipDescriptor, type EffectDefinition, type EffectInstance, type EffectParamDef, type EffectState, type ExportAudioHost, type ExportOptions, type KeyBinding, type LoadFilesResult, type PlaybackShortcutMap, type PointerEngineContract, RecordingController, type RecordingOptions, type RecordingSession, type SerializedEffectEntry, SpectrogramController, type SplitEngineContract, type SplitHost, type SplittingShortcutMap, type TrackConfig, type TrackDescriptor, type TrackRenderMode, type UndoShortcutMap, type WaveformSegment, createEffectInstance, getEffectDefinitions, isDomClip, registerEffect, splitAtPlayhead };