@waveform-playlist/browser 10.1.1 → 10.1.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.
@@ -0,0 +1,1824 @@
1
+ import { Analyser, ToneAudioNode, InputNode } from 'tone';
2
+ import * as tone from 'tone';
3
+ export { tone as Tone };
4
+ import { EffectsFunction, SoundFontCache, TrackEffectsFunction as TrackEffectsFunction$1 } from '@waveform-playlist/playout';
5
+ export { EffectsFunction, TrackEffectsFunction } from '@waveform-playlist/playout';
6
+ import * as React$1 from 'react';
7
+ import React__default, { ReactNode, RefObject } from 'react';
8
+ import { PlaylistEngine, EngineState } from '@waveform-playlist/engine';
9
+ import { ClipTrack, AnnotationData, AnnotationAction, PeakData, Fade, MidiNoteData, WaveformDataObject, TrackEffectsFunction as TrackEffectsFunction$2, RenderMode, SpectrogramConfig, ColorMapValue, AnnotationActionOptions, RenderAnnotationItemProps, TrackSpectrogramOverrides } from '@waveform-playlist/core';
10
+ export { AnnotationData, AudioClip, ClipTrack, Fade } from '@waveform-playlist/core';
11
+ import { WaveformPlaylistTheme, TimeFormat, RenderPlayheadFunction, TrackMenuItem, SnapTo } from '@waveform-playlist/ui-components';
12
+ export { TimeFormat } from '@waveform-playlist/ui-components';
13
+ import { FadeConfig, MediaElementPlayout } from '@waveform-playlist/media-element-playout';
14
+ import * as _dnd_kit_abstract from '@dnd-kit/abstract';
15
+ import { DragStartEvent, DragMoveEvent, DragEndEvent, PluginDescriptor, Modifier, DragDropManager, DragOperation, Plugins } from '@dnd-kit/abstract';
16
+ import { PointerSensor } from '@dnd-kit/dom';
17
+ import WaveformData from 'waveform-data';
18
+
19
+ interface ClipPeaks {
20
+ clipId: string;
21
+ trackName: string;
22
+ peaks: PeakData;
23
+ startSample: number;
24
+ durationSamples: number;
25
+ fadeIn?: Fade;
26
+ fadeOut?: Fade;
27
+ midiNotes?: MidiNoteData[];
28
+ sampleRate?: number;
29
+ offsetSamples?: number;
30
+ }
31
+ type TrackClipPeaks = ClipPeaks[];
32
+ interface WaveformTrack {
33
+ src: string | AudioBuffer;
34
+ name?: string;
35
+ effects?: TrackEffectsFunction$1;
36
+ }
37
+ interface TrackState$1 {
38
+ name: string;
39
+ muted: boolean;
40
+ soloed: boolean;
41
+ volume: number;
42
+ pan: number;
43
+ }
44
+ interface PlaybackAnimationContextValue {
45
+ isPlaying: boolean;
46
+ currentTime: number;
47
+ currentTimeRef: React__default.RefObject<number>;
48
+ playbackStartTimeRef: React__default.RefObject<number>;
49
+ audioStartPositionRef: React__default.RefObject<number>;
50
+ /** Returns current playback time from engine (auto-wraps at loop boundaries). */
51
+ getPlaybackTime: () => number;
52
+ }
53
+ interface PlaylistStateContextValue {
54
+ continuousPlay: boolean;
55
+ linkEndpoints: boolean;
56
+ annotationsEditable: boolean;
57
+ isAutomaticScroll: boolean;
58
+ isLoopEnabled: boolean;
59
+ annotations: AnnotationData[];
60
+ activeAnnotationId: string | null;
61
+ selectionStart: number;
62
+ selectionEnd: number;
63
+ selectedTrackId: string | null;
64
+ loopStart: number;
65
+ loopEnd: number;
66
+ /** Whether playback continues past the end of loaded audio */
67
+ indefinitePlayback: boolean;
68
+ }
69
+ interface PlaylistControlsContextValue {
70
+ play: (startTime?: number, playDuration?: number) => Promise<void>;
71
+ pause: () => void;
72
+ stop: () => void;
73
+ seekTo: (time: number) => void;
74
+ setCurrentTime: (time: number) => void;
75
+ setTrackMute: (trackIndex: number, muted: boolean) => void;
76
+ setTrackSolo: (trackIndex: number, soloed: boolean) => void;
77
+ setTrackVolume: (trackIndex: number, volume: number) => void;
78
+ setTrackPan: (trackIndex: number, pan: number) => void;
79
+ setSelection: (start: number, end: number) => void;
80
+ setSelectedTrackId: (trackId: string | null) => void;
81
+ setTimeFormat: (format: TimeFormat) => void;
82
+ formatTime: (seconds: number) => string;
83
+ zoomIn: () => void;
84
+ zoomOut: () => void;
85
+ setMasterVolume: (volume: number) => void;
86
+ setAutomaticScroll: (enabled: boolean) => void;
87
+ setScrollContainer: (element: HTMLDivElement | null) => void;
88
+ scrollContainerRef: React__default.RefObject<HTMLDivElement | null>;
89
+ setContinuousPlay: (enabled: boolean) => void;
90
+ setLinkEndpoints: (enabled: boolean) => void;
91
+ setAnnotationsEditable: (enabled: boolean) => void;
92
+ setAnnotations: React__default.Dispatch<React__default.SetStateAction<AnnotationData[]>>;
93
+ setActiveAnnotationId: (id: string | null) => void;
94
+ setLoopEnabled: (enabled: boolean) => void;
95
+ setLoopRegion: (start: number, end: number) => void;
96
+ setLoopRegionFromSelection: () => void;
97
+ clearLoopRegion: () => void;
98
+ }
99
+ interface PlaylistDataContextValue {
100
+ duration: number;
101
+ audioBuffers: AudioBuffer[];
102
+ peaksDataArray: TrackClipPeaks[];
103
+ trackStates: TrackState$1[];
104
+ tracks: ClipTrack[];
105
+ sampleRate: number;
106
+ waveHeight: number;
107
+ timeScaleHeight: number;
108
+ minimumPlaylistHeight: number;
109
+ controls: {
110
+ show: boolean;
111
+ width: number;
112
+ };
113
+ playoutRef: React__default.RefObject<PlaylistEngine | null>;
114
+ samplesPerPixel: number;
115
+ timeFormat: TimeFormat;
116
+ masterVolume: number;
117
+ canZoomIn: boolean;
118
+ canZoomOut: boolean;
119
+ barWidth: number;
120
+ barGap: number;
121
+ /** Width in pixels of progress bars. Defaults to barWidth + barGap (fills gaps). */
122
+ progressBarWidth: number;
123
+ /** Whether the playlist has finished loading all tracks */
124
+ isReady: boolean;
125
+ /** Whether tracks are rendered in mono mode */
126
+ mono: boolean;
127
+ /** Ref set by useClipDragHandlers during boundary trim drags.
128
+ * When true, loadAudio skips engine rebuild — visual updates flow via React state only. */
129
+ isDraggingRef: React__default.MutableRefObject<boolean>;
130
+ onTracksChange: ((tracks: ClipTrack[]) => void) | undefined;
131
+ }
132
+ interface WaveformPlaylistProviderProps {
133
+ tracks: ClipTrack[];
134
+ timescale?: boolean;
135
+ mono?: boolean;
136
+ waveHeight?: number;
137
+ samplesPerPixel?: number;
138
+ zoomLevels?: number[];
139
+ automaticScroll?: boolean;
140
+ theme?: Partial<WaveformPlaylistTheme>;
141
+ controls?: {
142
+ show: boolean;
143
+ width: number;
144
+ };
145
+ annotationList?: {
146
+ annotations?: AnnotationData[];
147
+ editable?: boolean;
148
+ isContinuousPlay?: boolean;
149
+ linkEndpoints?: boolean;
150
+ controls?: AnnotationAction[];
151
+ };
152
+ effects?: EffectsFunction;
153
+ onReady?: () => void;
154
+ /** @deprecated Use onAnnotationsChange instead */
155
+ onAnnotationUpdate?: (annotations: AnnotationData[]) => void;
156
+ /** Callback when annotations are changed (drag, edit, etc.) */
157
+ onAnnotationsChange?: (annotations: AnnotationData[]) => void;
158
+ /** Width in pixels of waveform bars. Default: 1 */
159
+ barWidth?: number;
160
+ /** Spacing in pixels between waveform bars. Default: 0 */
161
+ barGap?: number;
162
+ /** Width in pixels of progress bars. Default: barWidth + barGap (fills gaps). */
163
+ progressBarWidth?: number;
164
+ /** Callback when engine clip operations (move, trim, split) change tracks.
165
+ * The provider calls this so the parent can update its tracks state without
166
+ * triggering a full engine rebuild.
167
+ *
168
+ * **Important:** The parent must pass the received `tracks` reference back as
169
+ * the `tracks` prop (i.e. `setState(tracks)`). The provider uses reference
170
+ * identity (`tracks === engineTracksRef.current`) to detect engine-originated
171
+ * updates and skip the expensive `loadAudio` rebuild. */
172
+ onTracksChange?: (tracks: ClipTrack[]) => void;
173
+ /** SoundFont cache for sample-based MIDI playback. When provided, MIDI clips
174
+ * use SoundFont samples instead of PolySynth synthesis. */
175
+ soundFontCache?: SoundFontCache;
176
+ /** When true, tracks render visually but the engine build is deferred.
177
+ * Use this during progressive loading to avoid rebuilding the engine for
178
+ * each track — flip to false when all tracks are ready for a single build. */
179
+ deferEngineRebuild?: boolean;
180
+ /** Disable automatic stop when the cursor reaches the end of the longest
181
+ * track. Useful for DAW-style recording beyond existing audio. */
182
+ indefinitePlayback?: boolean;
183
+ children: ReactNode;
184
+ }
185
+ declare const WaveformPlaylistProvider: React__default.FC<WaveformPlaylistProviderProps>;
186
+ declare const usePlaybackAnimation: () => PlaybackAnimationContextValue;
187
+ declare const usePlaylistState: () => PlaylistStateContextValue;
188
+ declare const usePlaylistControls: () => PlaylistControlsContextValue;
189
+ declare const usePlaylistData: () => PlaylistDataContextValue;
190
+
191
+ interface MediaElementTrackConfig {
192
+ /** Audio source URL or Blob URL */
193
+ source: string;
194
+ /** Pre-computed waveform data (required for visualization) */
195
+ waveformData: WaveformDataObject;
196
+ /** Track name for display */
197
+ name?: string;
198
+ /** Fade in configuration (requires audioContext on provider) */
199
+ fadeIn?: FadeConfig;
200
+ /** Fade out configuration (requires audioContext on provider) */
201
+ fadeOut?: FadeConfig;
202
+ }
203
+ interface MediaElementAnimationContextValue {
204
+ isPlaying: boolean;
205
+ currentTime: number;
206
+ currentTimeRef: React__default.RefObject<number>;
207
+ }
208
+ interface MediaElementStateContextValue {
209
+ continuousPlay: boolean;
210
+ annotations: AnnotationData[];
211
+ activeAnnotationId: string | null;
212
+ playbackRate: number;
213
+ isAutomaticScroll: boolean;
214
+ }
215
+ interface MediaElementControlsContextValue {
216
+ play: (startTime?: number) => void;
217
+ pause: () => void;
218
+ stop: () => void;
219
+ seekTo: (time: number) => void;
220
+ setPlaybackRate: (rate: number) => void;
221
+ setContinuousPlay: (enabled: boolean) => void;
222
+ setAnnotations: React__default.Dispatch<React__default.SetStateAction<AnnotationData[]>>;
223
+ setActiveAnnotationId: (id: string | null) => void;
224
+ setAutomaticScroll: (enabled: boolean) => void;
225
+ setScrollContainer: (element: HTMLDivElement | null) => void;
226
+ scrollContainerRef: React__default.RefObject<HTMLDivElement | null>;
227
+ }
228
+ interface MediaElementDataContextValue {
229
+ duration: number;
230
+ peaksDataArray: TrackClipPeaks[];
231
+ sampleRate: number;
232
+ waveHeight: number;
233
+ timeScaleHeight: number;
234
+ samplesPerPixel: number;
235
+ playoutRef: React__default.RefObject<MediaElementPlayout | null>;
236
+ controls: {
237
+ show: boolean;
238
+ width: number;
239
+ };
240
+ barWidth: number;
241
+ barGap: number;
242
+ progressBarWidth: number;
243
+ fadeIn?: FadeConfig;
244
+ fadeOut?: FadeConfig;
245
+ }
246
+ interface MediaElementPlaylistProviderProps {
247
+ /** Single track configuration with source URL and waveform data */
248
+ track: MediaElementTrackConfig;
249
+ /** Initial samples per pixel (zoom level) */
250
+ samplesPerPixel?: number;
251
+ /** Height of each waveform track */
252
+ waveHeight?: number;
253
+ /** Show timescale */
254
+ timescale?: boolean;
255
+ /** Initial playback rate (0.5 to 2.0) */
256
+ playbackRate?: number;
257
+ /** Enable automatic scroll to keep playhead centered */
258
+ automaticScroll?: boolean;
259
+ /** Theme configuration */
260
+ theme?: Partial<WaveformPlaylistTheme>;
261
+ /** Track controls configuration */
262
+ controls?: {
263
+ show: boolean;
264
+ width: number;
265
+ };
266
+ /** Annotations */
267
+ annotationList?: {
268
+ annotations?: AnnotationData[];
269
+ isContinuousPlay?: boolean;
270
+ };
271
+ /** Width of waveform bars */
272
+ barWidth?: number;
273
+ /** Gap between waveform bars */
274
+ barGap?: number;
275
+ /** Width of progress bars */
276
+ progressBarWidth?: number;
277
+ /** Callback when annotations are changed (drag, edit, etc.) */
278
+ onAnnotationsChange?: (annotations: AnnotationData[]) => void;
279
+ /**
280
+ * AudioContext for Web Audio routing (fades, effects).
281
+ * When provided, audio routes through Web Audio nodes:
282
+ * HTMLAudioElement → MediaElementSourceNode → fadeGain → volumeGain → destination
283
+ *
284
+ * Without this, playback uses HTMLAudioElement directly (no fades or effects).
285
+ * Each provider instance should use its own AudioContext or share one —
286
+ * createMediaElementSource() is called once per audio element.
287
+ */
288
+ audioContext?: AudioContext;
289
+ /** Callback when audio is ready */
290
+ onReady?: () => void;
291
+ children: ReactNode;
292
+ }
293
+ /**
294
+ * MediaElementPlaylistProvider
295
+ *
296
+ * A simplified playlist provider for single-track playback using HTMLAudioElement.
297
+ * Key features:
298
+ * - Pitch-preserving playback rate (0.5x - 2.0x)
299
+ * - Pre-computed peaks visualization (no AudioBuffer needed)
300
+ * - Simpler API than full WaveformPlaylistProvider
301
+ *
302
+ * Use this for:
303
+ * - Language learning apps (speed control)
304
+ * - Podcast players
305
+ * - Single-track audio viewers
306
+ *
307
+ * For multi-track editing, use WaveformPlaylistProvider instead.
308
+ */
309
+ declare const MediaElementPlaylistProvider: React__default.FC<MediaElementPlaylistProviderProps>;
310
+ declare const useMediaElementAnimation: () => MediaElementAnimationContextValue;
311
+ declare const useMediaElementState: () => MediaElementStateContextValue;
312
+ declare const useMediaElementControls: () => MediaElementControlsContextValue;
313
+ declare const useMediaElementData: () => MediaElementDataContextValue;
314
+
315
+ interface TimeFormatControls {
316
+ timeFormat: TimeFormat;
317
+ setTimeFormat: (format: TimeFormat) => void;
318
+ formatTime: (seconds: number) => string;
319
+ parseTime: (timeString: string) => number;
320
+ }
321
+ /**
322
+ * Hook to manage time format state
323
+ *
324
+ * @example
325
+ * ```tsx
326
+ * const { timeFormat, setTimeFormat, formatTime, parseTime } = useTimeFormat();
327
+ *
328
+ * <TimeFormatSelect
329
+ * value={timeFormat}
330
+ * onChange={setTimeFormat}
331
+ * />
332
+ * <span>{formatTime(currentTime)}</span>
333
+ * <input onChange={(e) => seekTo(parseTime(e.target.value))} />
334
+ * ```
335
+ */
336
+ declare function useTimeFormat(): TimeFormatControls;
337
+
338
+ interface ZoomControls {
339
+ samplesPerPixel: number;
340
+ zoomIn: () => void;
341
+ zoomOut: () => void;
342
+ canZoomIn: boolean;
343
+ canZoomOut: boolean;
344
+ }
345
+ interface UseZoomControlsProps {
346
+ engineRef: RefObject<PlaylistEngine | null>;
347
+ initialSamplesPerPixel: number;
348
+ }
349
+ /**
350
+ * Hook for managing zoom controls via PlaylistEngine delegation.
351
+ *
352
+ * zoomIn/zoomOut delegate to the engine. State is mirrored back from
353
+ * the engine via onEngineState(), which the provider's statechange
354
+ * handler calls on every engine event.
355
+ *
356
+ * samplesPerPixel updates use startTransition so React treats them as
357
+ * non-urgent — during playback, animation RAF callbacks interleave
358
+ * with the zoom re-render instead of being blocked.
359
+ */
360
+ declare function useZoomControls({ engineRef, initialSamplesPerPixel, }: UseZoomControlsProps): ZoomControls & {
361
+ onEngineState: (state: EngineState) => void;
362
+ };
363
+
364
+ interface UseMasterVolumeProps {
365
+ engineRef: RefObject<PlaylistEngine | null>;
366
+ initialVolume?: number;
367
+ }
368
+ interface MasterVolumeControls {
369
+ masterVolume: number;
370
+ setMasterVolume: (volume: number) => void;
371
+ /** Ref holding the current masterVolume for seeding a fresh engine. */
372
+ masterVolumeRef: React.RefObject<number>;
373
+ }
374
+ /**
375
+ * Hook for managing master volume via PlaylistEngine delegation.
376
+ *
377
+ * setMasterVolume delegates to the engine. State is mirrored back from
378
+ * the engine via onEngineState(), which the provider's statechange
379
+ * handler calls on every engine event.
380
+ */
381
+ declare function useMasterVolume({ engineRef, initialVolume, }: UseMasterVolumeProps): MasterVolumeControls & {
382
+ onEngineState: (state: EngineState) => void;
383
+ };
384
+
385
+ /**
386
+ * Hook for master effects with frequency analyzer
387
+ * Returns the analyser ref and the effects function to pass to WaveformPlaylistProvider
388
+ *
389
+ * For more advanced effects (reverb, delay, filters, etc.), use useDynamicEffects instead.
390
+ */
391
+ declare const useMasterAnalyser: (fftSize?: number) => {
392
+ analyserRef: React$1.MutableRefObject<Analyser | null>;
393
+ masterEffects: EffectsFunction;
394
+ };
395
+
396
+ /**
397
+ * Configuration for a single audio track to load
398
+ *
399
+ * Audio can be provided in three ways:
400
+ * 1. `src` - URL to fetch and decode (standard loading)
401
+ * 2. `audioBuffer` - Pre-loaded AudioBuffer (skip fetch/decode)
402
+ * 3. `waveformData` only - Peaks-first rendering (audio loads later)
403
+ *
404
+ * For peaks-first rendering, just provide `waveformData` - the sample rate
405
+ * and duration are derived from the waveform data automatically.
406
+ */
407
+ interface AudioTrackConfig {
408
+ /** URL to audio file - used if audioBuffer not provided */
409
+ src?: string;
410
+ /** Pre-loaded AudioBuffer - skips fetch/decode if provided */
411
+ audioBuffer?: AudioBuffer;
412
+ name?: string;
413
+ muted?: boolean;
414
+ soloed?: boolean;
415
+ volume?: number;
416
+ pan?: number;
417
+ color?: string;
418
+ effects?: TrackEffectsFunction$2;
419
+ startTime?: number;
420
+ duration?: number;
421
+ offset?: number;
422
+ fadeIn?: Fade;
423
+ fadeOut?: Fade;
424
+ waveformData?: WaveformDataObject;
425
+ /** Visualization render mode: 'waveform' | 'spectrogram' | 'both'. Default: 'waveform' */
426
+ renderMode?: RenderMode;
427
+ /** Spectrogram configuration (FFT size, window, frequency scale, etc.) */
428
+ spectrogramConfig?: SpectrogramConfig;
429
+ /** Spectrogram color map name or custom color array */
430
+ spectrogramColorMap?: ColorMapValue;
431
+ }
432
+ /**
433
+ * Options for useAudioTracks hook
434
+ */
435
+ interface UseAudioTracksOptions {
436
+ /**
437
+ * When true, all tracks render immediately as placeholders with clip geometry
438
+ * from the config. Audio fills in progressively as files decode, and peaks
439
+ * render as each buffer becomes available. Use with `deferEngineRebuild={loading}`
440
+ * on the provider for a single engine build when all tracks are ready.
441
+ *
442
+ * Requires `duration` or `waveformData` in each config so clip dimensions are known upfront.
443
+ * Default: false
444
+ */
445
+ immediate?: boolean;
446
+ /** @deprecated Use `immediate` instead. */
447
+ progressive?: boolean;
448
+ }
449
+ /**
450
+ * Hook to load audio from URLs and convert to ClipTrack format
451
+ *
452
+ * This hook fetches audio files, decodes them, and creates ClipTrack objects
453
+ * with a single clip per track. Supports custom positioning for multi-clip arrangements.
454
+ *
455
+ * @param configs - Array of audio track configurations
456
+ * @param options - Optional configuration for loading behavior
457
+ * @returns Object with tracks array, loading state, and progress info
458
+ *
459
+ * @example
460
+ * ```typescript
461
+ * // Basic usage (clips positioned at start)
462
+ * const { tracks, loading, error } = useAudioTracks([
463
+ * { src: 'audio/vocals.mp3', name: 'Vocals' },
464
+ * { src: 'audio/drums.mp3', name: 'Drums' },
465
+ * ]);
466
+ *
467
+ * // Immediate rendering with deferred engine build (recommended for multi-track)
468
+ * const { tracks, loading } = useAudioTracks(
469
+ * [
470
+ * { src: 'audio/vocals.mp3', name: 'Vocals', duration: 30 },
471
+ * { src: 'audio/drums.mp3', name: 'Drums', duration: 30 },
472
+ * ],
473
+ * { immediate: true }
474
+ * );
475
+ * // All tracks render instantly as placeholders, peaks fill in as files load
476
+ * return (
477
+ * <WaveformPlaylistProvider tracks={tracks} deferEngineRebuild={loading}>
478
+ * ...
479
+ * </WaveformPlaylistProvider>
480
+ * );
481
+ *
482
+ * // Pre-loaded AudioBuffer (skip fetch/decode)
483
+ * const { tracks } = useAudioTracks([
484
+ * { audioBuffer: myPreloadedBuffer, name: 'Pre-loaded' },
485
+ * ]);
486
+ *
487
+ * // Peaks-first rendering (instant visual, audio loads later)
488
+ * const { tracks } = useAudioTracks([
489
+ * { waveformData: preloadedPeaks, name: 'Peaks Only' }, // Renders immediately
490
+ * ]);
491
+ * ```
492
+ */
493
+ declare function useAudioTracks(configs: AudioTrackConfig[], options?: UseAudioTracksOptions): {
494
+ tracks: ClipTrack[];
495
+ loading: boolean;
496
+ error: string | null;
497
+ loadedCount: number;
498
+ totalCount: number;
499
+ };
500
+
501
+ interface UseClipDragHandlersOptions {
502
+ tracks: ClipTrack[];
503
+ onTracksChange: (tracks: ClipTrack[]) => void;
504
+ samplesPerPixel: number;
505
+ sampleRate: number;
506
+ engineRef: React__default.RefObject<PlaylistEngine | null>;
507
+ /** Ref toggled during boundary trim drags. When true, the provider's loadAudio
508
+ * skips engine rebuilds so engine keeps original clip positions. On drag end,
509
+ * engine.trimClip() commits the final delta. Obtain from usePlaylistData(). */
510
+ isDraggingRef: React__default.MutableRefObject<boolean>;
511
+ /** Optional function that snaps a sample position to the nearest grid position.
512
+ * Used for boundary trim snapping (move snapping is handled by the SnapToGridModifier). */
513
+ snapSamplePosition?: (samplePosition: number) => number;
514
+ }
515
+ /**
516
+ * Custom hook for handling clip drag operations (movement and trimming)
517
+ *
518
+ * Provides drag handlers for use with @dnd-kit/react DragDropProvider.
519
+ * Handles both clip movement (dragging entire clips) and boundary trimming (adjusting clip edges).
520
+ *
521
+ * Collision detection for clip moves is handled by `ClipCollisionModifier` (passed to DragDropProvider).
522
+ *
523
+ * **Move:** `onDragEnd` delegates to `engine.moveClip()` in one shot.
524
+ *
525
+ * **Trim:** `onDragMove` updates React state per-frame via `onTracksChange` for smooth
526
+ * visual feedback (using cumulative deltas from the original clip snapshot). `isDraggingRef`
527
+ * prevents loadAudio from rebuilding the engine during the drag, so the engine keeps the
528
+ * original clip positions. On drag end, `engine.trimClip()` commits the final delta.
529
+ *
530
+ * @example
531
+ * ```tsx
532
+ * const { onDragStart, onDragMove, onDragEnd } = useClipDragHandlers({
533
+ * tracks,
534
+ * onTracksChange: setTracks,
535
+ * samplesPerPixel,
536
+ * sampleRate,
537
+ * engineRef: playoutRef,
538
+ * isDraggingRef,
539
+ * });
540
+ *
541
+ * return (
542
+ * <DragDropProvider
543
+ * onDragStart={onDragStart}
544
+ * onDragMove={onDragMove}
545
+ * onDragEnd={onDragEnd}
546
+ * modifiers={[RestrictToHorizontalAxis, ClipCollisionModifier.configure({ tracks, samplesPerPixel })]}
547
+ * >
548
+ * <Waveform showClipHeaders={true} />
549
+ * </DragDropProvider>
550
+ * );
551
+ * ```
552
+ */
553
+ declare function useClipDragHandlers({ tracks, onTracksChange, samplesPerPixel, sampleRate, engineRef, isDraggingRef, snapSamplePosition, }: UseClipDragHandlersOptions): {
554
+ onDragStart: (event: Parameters<DragStartEvent>[0]) => void;
555
+ onDragMove: (event: Parameters<DragMoveEvent>[0]) => void;
556
+ onDragEnd: (event: Parameters<DragEndEvent>[0]) => void;
557
+ };
558
+
559
+ interface UseAnnotationDragHandlersOptions {
560
+ annotations: AnnotationData[];
561
+ onAnnotationsChange: (annotations: AnnotationData[]) => void;
562
+ samplesPerPixel: number;
563
+ sampleRate: number;
564
+ duration: number;
565
+ linkEndpoints: boolean;
566
+ }
567
+ /**
568
+ * Custom hook for handling annotation drag operations (boundary trimming)
569
+ *
570
+ * Provides drag handlers for use with @dnd-kit/react DragDropProvider.
571
+ * Handles annotation boundary resizing with linked endpoints support.
572
+ *
573
+ * @example
574
+ * ```tsx
575
+ * const { onDragStart, onDragMove, onDragEnd } = useAnnotationDragHandlers({
576
+ * annotations,
577
+ * onAnnotationsChange: setAnnotations,
578
+ * samplesPerPixel,
579
+ * sampleRate,
580
+ * duration,
581
+ * linkEndpoints,
582
+ * });
583
+ *
584
+ * return (
585
+ * <DragDropProvider
586
+ * onDragStart={onDragStart}
587
+ * onDragMove={onDragMove}
588
+ * onDragEnd={onDragEnd}
589
+ * modifiers={[RestrictToHorizontalAxis]}
590
+ * >
591
+ * {renderAnnotations()}
592
+ * </DragDropProvider>
593
+ * );
594
+ * ```
595
+ */
596
+ declare function useAnnotationDragHandlers({ annotations, onAnnotationsChange, samplesPerPixel, sampleRate, duration, linkEndpoints, }: UseAnnotationDragHandlersOptions): {
597
+ onDragStart: (event: Parameters<DragStartEvent>[0]) => void;
598
+ onDragMove: (event: Parameters<DragMoveEvent>[0]) => void;
599
+ onDragEnd: (event: Parameters<DragEndEvent>[0]) => void;
600
+ };
601
+
602
+ /**
603
+ * Hook for configuring @dnd-kit sensors for clip dragging
604
+ *
605
+ * Provides consistent drag activation behavior across all examples.
606
+ * Always overrides PointerSensor defaults with custom activation constraints:
607
+ * - Default mode: distance-based activation (1px) for all pointer types
608
+ * - Touch-optimized mode: delay-based activation for touch (250ms),
609
+ * distance-based for mouse/pen
610
+ */
611
+
612
+ interface DragSensorOptions {
613
+ /**
614
+ * Enable mobile-optimized touch handling with delay-based activation.
615
+ * When true, touch events get delay-based activation while mouse/pen get distance-based.
616
+ * When false (default), all pointer types use distance-based activation (1px).
617
+ */
618
+ touchOptimized?: boolean;
619
+ /**
620
+ * Delay in milliseconds before touch drag activates (only when touchOptimized is true).
621
+ * Default: 250ms - long enough to distinguish from scroll intent
622
+ */
623
+ touchDelay?: number;
624
+ /**
625
+ * Distance tolerance during touch delay (only when touchOptimized is true).
626
+ * If finger moves more than this during delay, drag is cancelled.
627
+ * Default: 5px - allows slight finger movement
628
+ */
629
+ touchTolerance?: number;
630
+ /**
631
+ * Distance in pixels before mouse drag activates.
632
+ * Default: 1px for immediate feedback on desktop
633
+ */
634
+ mouseDistance?: number;
635
+ }
636
+ /**
637
+ * Returns configured sensors for @dnd-kit drag operations
638
+ *
639
+ * @param options - Configuration options for drag sensors
640
+ * @returns Array of sensor constructors/descriptors for DragDropProvider's sensors prop
641
+ *
642
+ * @example
643
+ * // Desktop-optimized (default — 1px distance activation for all pointer types)
644
+ * const sensors = useDragSensors();
645
+ *
646
+ * @example
647
+ * // Mobile-optimized with custom touch delay
648
+ * const sensors = useDragSensors({ touchOptimized: true, touchDelay: 300 });
649
+ */
650
+ declare function useDragSensors(options?: DragSensorOptions): (typeof PointerSensor | PluginDescriptor<any, any, any>)[];
651
+
652
+ interface UseClipSplittingOptions {
653
+ tracks: ClipTrack[];
654
+ sampleRate: number;
655
+ samplesPerPixel: number;
656
+ engineRef: React__default.RefObject<PlaylistEngine | null>;
657
+ }
658
+ interface UseClipSplittingResult {
659
+ splitClipAtPlayhead: () => boolean;
660
+ splitClipAt: (trackIndex: number, clipIndex: number, splitTime: number) => boolean;
661
+ }
662
+ /**
663
+ * Hook for splitting clips at the playhead or at a specific time
664
+ *
665
+ * Splitting delegates to `engine.splitClip()` — the engine handles clip creation,
666
+ * adapter sync, and emits statechange. The provider's statechange handler propagates
667
+ * the updated tracks to the parent via `onTracksChange`.
668
+ *
669
+ * @param options - Configuration options
670
+ * @returns Object with split functions
671
+ *
672
+ * @example
673
+ * ```tsx
674
+ * const { splitClipAtPlayhead } = useClipSplitting({
675
+ * tracks,
676
+ * sampleRate,
677
+ * samplesPerPixel,
678
+ * engineRef: playoutRef,
679
+ * });
680
+ *
681
+ * // In keyboard handler
682
+ * const handleKeyPress = (e: KeyboardEvent) => {
683
+ * if (e.key === 's' || e.key === 'S') {
684
+ * splitClipAtPlayhead();
685
+ * }
686
+ * };
687
+ * ```
688
+ */
689
+ declare const useClipSplitting: (options: UseClipSplittingOptions) => UseClipSplittingResult;
690
+
691
+ interface KeyboardShortcut {
692
+ key: string;
693
+ ctrlKey?: boolean;
694
+ shiftKey?: boolean;
695
+ metaKey?: boolean;
696
+ altKey?: boolean;
697
+ action: () => void;
698
+ description?: string;
699
+ preventDefault?: boolean;
700
+ }
701
+ interface UseKeyboardShortcutsOptions {
702
+ shortcuts: KeyboardShortcut[];
703
+ enabled?: boolean;
704
+ }
705
+ /**
706
+ * Hook for managing keyboard shortcuts
707
+ *
708
+ * @param options - Configuration options
709
+ *
710
+ * @example
711
+ * ```tsx
712
+ * useKeyboardShortcuts({
713
+ * shortcuts: [
714
+ * {
715
+ * key: ' ',
716
+ * action: togglePlayPause,
717
+ * description: 'Play/Pause',
718
+ * preventDefault: true,
719
+ * },
720
+ * {
721
+ * key: 's',
722
+ * action: splitClipAtPlayhead,
723
+ * description: 'Split clip at playhead',
724
+ * preventDefault: true,
725
+ * },
726
+ * ],
727
+ * });
728
+ * ```
729
+ */
730
+ declare const useKeyboardShortcuts: (options: UseKeyboardShortcutsOptions) => void;
731
+ /**
732
+ * Get a human-readable string representation of a keyboard shortcut
733
+ *
734
+ * @param shortcut - The keyboard shortcut
735
+ * @returns Human-readable string (e.g., "Cmd+Shift+S")
736
+ */
737
+ declare const getShortcutLabel: (shortcut: KeyboardShortcut) => string;
738
+
739
+ interface UsePlaybackShortcutsOptions {
740
+ /**
741
+ * Enable the shortcuts. Defaults to true.
742
+ */
743
+ enabled?: boolean;
744
+ /**
745
+ * Additional shortcuts to include alongside the default playback shortcuts.
746
+ */
747
+ additionalShortcuts?: KeyboardShortcut[];
748
+ /**
749
+ * Override default shortcuts. If provided, only these shortcuts will be used.
750
+ */
751
+ shortcuts?: KeyboardShortcut[];
752
+ }
753
+ interface UsePlaybackShortcutsReturn {
754
+ /** Rewind to the beginning (time = 0) */
755
+ rewindToStart: () => void;
756
+ /** Toggle play/pause */
757
+ togglePlayPause: () => void;
758
+ /** Stop playback and return to start position */
759
+ stopPlayback: () => void;
760
+ /** The list of active keyboard shortcuts */
761
+ shortcuts: KeyboardShortcut[];
762
+ }
763
+ /**
764
+ * Hook that provides common playback keyboard shortcuts for the playlist.
765
+ *
766
+ * Default shortcuts:
767
+ * - `Space` - Toggle play/pause
768
+ * - `Escape` - Stop playback
769
+ * - `0` - Rewind to start (seek to time 0)
770
+ *
771
+ * @example
772
+ * ```tsx
773
+ * // Basic usage - enables default shortcuts
774
+ * usePlaybackShortcuts();
775
+ *
776
+ * // With additional custom shortcuts
777
+ * usePlaybackShortcuts({
778
+ * additionalShortcuts: [
779
+ * { key: 's', action: splitClipAtPlayhead, description: 'Split clip' },
780
+ * ],
781
+ * });
782
+ *
783
+ * // Completely override shortcuts
784
+ * usePlaybackShortcuts({
785
+ * shortcuts: [
786
+ * { key: 'Home', action: rewindToStart, description: 'Go to start' },
787
+ * ],
788
+ * });
789
+ * ```
790
+ */
791
+ declare const usePlaybackShortcuts: (options?: UsePlaybackShortcutsOptions) => UsePlaybackShortcutsReturn;
792
+
793
+ interface UseAnnotationKeyboardControlsOptions {
794
+ annotations: AnnotationData[];
795
+ activeAnnotationId: string | null;
796
+ onAnnotationsChange: (annotations: AnnotationData[]) => void;
797
+ /** Callback to set the active annotation ID for selection */
798
+ onActiveAnnotationChange?: (id: string | null) => void;
799
+ duration: number;
800
+ linkEndpoints: boolean;
801
+ /** Whether continuous play is enabled (affects playback duration) */
802
+ continuousPlay?: boolean;
803
+ enabled?: boolean;
804
+ /** Optional: scroll container ref for auto-scrolling to annotation */
805
+ scrollContainerRef?: React.RefObject<HTMLDivElement | null>;
806
+ /** Optional: samples per pixel for scroll position calculation */
807
+ samplesPerPixel?: number;
808
+ /** Optional: sample rate for scroll position calculation */
809
+ sampleRate?: number;
810
+ /** Optional: callback to start playback at a time with optional duration */
811
+ onPlay?: (startTime: number, duration?: number) => void;
812
+ }
813
+ /**
814
+ * Hook for keyboard-based annotation navigation and boundary editing
815
+ *
816
+ * Navigation Shortcuts:
817
+ * - ArrowUp / ArrowLeft = Select previous annotation
818
+ * - ArrowDown / ArrowRight = Select next annotation
819
+ * - Home = Select first annotation
820
+ * - End = Select last annotation
821
+ * - Escape = Deselect annotation
822
+ * - Enter = Play selected annotation
823
+ *
824
+ * Boundary Editing Shortcuts (requires active annotation):
825
+ * - [ = Move start boundary earlier (left)
826
+ * - ] = Move start boundary later (right)
827
+ * - Shift+[ = Move end boundary earlier (left)
828
+ * - Shift+] = Move end boundary later (right)
829
+ *
830
+ * Respects linkEndpoints and continuousPlay settings.
831
+ *
832
+ * @example
833
+ * ```tsx
834
+ * useAnnotationKeyboardControls({
835
+ * annotations,
836
+ * activeAnnotationId,
837
+ * onAnnotationsChange: setAnnotations,
838
+ * onActiveAnnotationChange: setActiveAnnotationId,
839
+ * duration,
840
+ * linkEndpoints,
841
+ * });
842
+ * ```
843
+ */
844
+ declare function useAnnotationKeyboardControls({ annotations, activeAnnotationId, onAnnotationsChange, onActiveAnnotationChange, duration, linkEndpoints, continuousPlay, enabled, scrollContainerRef, samplesPerPixel, sampleRate, onPlay, }: UseAnnotationKeyboardControlsOptions): {
845
+ moveStartBoundary: (delta: number) => void;
846
+ moveEndBoundary: (delta: number) => void;
847
+ selectPrevious: () => void;
848
+ selectNext: () => void;
849
+ selectFirst: () => void;
850
+ selectLast: () => void;
851
+ clearSelection: () => void;
852
+ scrollToAnnotation: (annotationId: string) => void;
853
+ playActiveAnnotation: () => void;
854
+ };
855
+
856
+ /**
857
+ * Effect definitions for all available Tone.js effects
858
+ * Each effect has parameters with min/max/default values for UI controls
859
+ */
860
+ type ParameterType = 'number' | 'select' | 'boolean';
861
+ interface EffectParameter {
862
+ name: string;
863
+ label: string;
864
+ type: ParameterType;
865
+ min?: number;
866
+ max?: number;
867
+ step?: number;
868
+ default: number | string | boolean;
869
+ unit?: string;
870
+ options?: {
871
+ value: string | number;
872
+ label: string;
873
+ }[];
874
+ }
875
+ interface EffectDefinition {
876
+ id: string;
877
+ name: string;
878
+ category: 'delay' | 'reverb' | 'modulation' | 'distortion' | 'filter' | 'dynamics' | 'spatial';
879
+ description: string;
880
+ parameters: EffectParameter[];
881
+ }
882
+ declare const effectDefinitions: EffectDefinition[];
883
+ declare const getEffectDefinition: (id: string) => EffectDefinition | undefined;
884
+ declare const getEffectsByCategory: (category: EffectDefinition["category"]) => EffectDefinition[];
885
+ declare const effectCategories: {
886
+ id: EffectDefinition['category'];
887
+ name: string;
888
+ }[];
889
+
890
+ interface ActiveEffect {
891
+ instanceId: string;
892
+ effectId: string;
893
+ definition: EffectDefinition;
894
+ params: Record<string, number | string | boolean>;
895
+ bypassed: boolean;
896
+ }
897
+ interface UseDynamicEffectsReturn {
898
+ activeEffects: ActiveEffect[];
899
+ availableEffects: EffectDefinition[];
900
+ addEffect: (effectId: string) => void;
901
+ removeEffect: (instanceId: string) => void;
902
+ updateParameter: (instanceId: string, paramName: string, value: number | string | boolean) => void;
903
+ toggleBypass: (instanceId: string) => void;
904
+ reorderEffects: (fromIndex: number, toIndex: number) => void;
905
+ clearAllEffects: () => void;
906
+ masterEffects: EffectsFunction;
907
+ /**
908
+ * Creates a fresh effects function for offline rendering.
909
+ * This creates new effect instances that work in the offline AudioContext.
910
+ */
911
+ createOfflineEffectsFunction: () => EffectsFunction | undefined;
912
+ analyserRef: React.RefObject<Analyser | null>;
913
+ }
914
+ /**
915
+ * Hook for managing a dynamic chain of audio effects with real-time parameter updates
916
+ */
917
+ declare function useDynamicEffects(fftSize?: number): UseDynamicEffectsReturn;
918
+
919
+ interface TrackActiveEffect {
920
+ instanceId: string;
921
+ effectId: string;
922
+ definition: EffectDefinition;
923
+ params: Record<string, number | string | boolean>;
924
+ bypassed: boolean;
925
+ }
926
+ interface TrackEffectsState {
927
+ trackId: string;
928
+ activeEffects: TrackActiveEffect[];
929
+ }
930
+ interface UseTrackDynamicEffectsReturn {
931
+ trackEffectsState: Map<string, TrackActiveEffect[]>;
932
+ addEffectToTrack: (trackId: string, effectId: string) => void;
933
+ removeEffectFromTrack: (trackId: string, instanceId: string) => void;
934
+ updateTrackEffectParameter: (trackId: string, instanceId: string, paramName: string, value: number | string | boolean) => void;
935
+ toggleBypass: (trackId: string, instanceId: string) => void;
936
+ clearTrackEffects: (trackId: string) => void;
937
+ getTrackEffectsFunction: (trackId: string) => TrackEffectsFunction$1 | undefined;
938
+ /**
939
+ * Creates a fresh effects function for a track for offline rendering.
940
+ * This creates new effect instances that work in the offline AudioContext.
941
+ */
942
+ createOfflineTrackEffectsFunction: (trackId: string) => TrackEffectsFunction$1 | undefined;
943
+ availableEffects: EffectDefinition[];
944
+ }
945
+ /**
946
+ * Hook for managing dynamic effects per track with real-time parameter updates
947
+ */
948
+ declare function useTrackDynamicEffects(): UseTrackDynamicEffectsReturn;
949
+
950
+ /**
951
+ * WAV file encoder
952
+ * Converts AudioBuffer to WAV format Blob
953
+ */
954
+ interface WavEncoderOptions {
955
+ /** Bit depth: 16 or 32. Default: 16 */
956
+ bitDepth?: 16 | 32;
957
+ }
958
+
959
+ /** Function type for per-track effects (same as in @waveform-playlist/core) */
960
+ type TrackEffectsFunction = (graphEnd: unknown, destination: unknown, isOffline: boolean) => void | (() => void);
961
+ interface ExportOptions extends WavEncoderOptions {
962
+ /** Filename for download (without extension) */
963
+ filename?: string;
964
+ /** Export mode: 'master' for stereo mix, 'individual' for single track */
965
+ mode?: 'master' | 'individual';
966
+ /** Track index for individual export (only used when mode is 'individual') */
967
+ trackIndex?: number;
968
+ /** Whether to trigger automatic download */
969
+ autoDownload?: boolean;
970
+ /** Whether to apply effects (fades, etc.) - defaults to true */
971
+ applyEffects?: boolean;
972
+ /**
973
+ * Optional Tone.js effects function for master effects. When provided, export will use Tone.Offline
974
+ * to render through the effects chain. The function receives isOffline=true.
975
+ */
976
+ effectsFunction?: EffectsFunction;
977
+ /**
978
+ * Optional function to create offline track effects.
979
+ * Takes a trackId and returns a TrackEffectsFunction for offline rendering.
980
+ * This is used instead of track.effects to avoid AudioContext mismatch issues.
981
+ */
982
+ createOfflineTrackEffects?: (trackId: string) => TrackEffectsFunction | undefined;
983
+ /** Progress callback (0-1) */
984
+ onProgress?: (progress: number) => void;
985
+ }
986
+ interface ExportResult {
987
+ /** The rendered audio buffer */
988
+ audioBuffer: AudioBuffer;
989
+ /** The WAV file as a Blob */
990
+ blob: Blob;
991
+ /** Duration in seconds */
992
+ duration: number;
993
+ }
994
+ interface UseExportWavReturn {
995
+ /** Export the playlist to WAV */
996
+ exportWav: (tracks: ClipTrack[], trackStates: TrackState[], options?: ExportOptions) => Promise<ExportResult>;
997
+ /** Whether export is in progress */
998
+ isExporting: boolean;
999
+ /** Export progress (0-1) */
1000
+ progress: number;
1001
+ /** Error message if export failed */
1002
+ error: string | null;
1003
+ }
1004
+ interface TrackState {
1005
+ muted: boolean;
1006
+ soloed: boolean;
1007
+ volume: number;
1008
+ pan: number;
1009
+ }
1010
+ /**
1011
+ * Hook for exporting the waveform playlist to WAV format
1012
+ * Uses OfflineAudioContext for fast, non-real-time rendering
1013
+ */
1014
+ declare function useExportWav(): UseExportWavReturn;
1015
+
1016
+ /**
1017
+ * useDynamicTracks — imperative hook for runtime track additions.
1018
+ *
1019
+ * Complements `useAudioTracks` (declarative, configs-driven) with an
1020
+ * imperative `addTracks()` API for dynamic loading (drag-and-drop, file pickers).
1021
+ *
1022
+ * Placeholder tracks appear instantly while audio decodes in parallel.
1023
+ */
1024
+
1025
+ /** A source that can be decoded into a track. */
1026
+ type TrackSource = File | Blob | string | {
1027
+ src: string;
1028
+ name?: string;
1029
+ };
1030
+ /** Info about a track that failed to load. */
1031
+ interface TrackLoadError {
1032
+ /** Display name of the source that failed. */
1033
+ name: string;
1034
+ /** The underlying error. */
1035
+ error: Error;
1036
+ }
1037
+ interface UseDynamicTracksReturn {
1038
+ /**
1039
+ * Current tracks array (placeholders + loaded). Pass to WaveformPlaylistProvider.
1040
+ * Placeholder tracks have `clips: []` and names ending with " (loading...)".
1041
+ */
1042
+ tracks: ClipTrack[];
1043
+ /** Add one or more sources — creates placeholder tracks immediately. */
1044
+ addTracks: (sources: TrackSource[]) => void;
1045
+ /** Remove a track by its id. Aborts in-flight fetch/decode if still loading. */
1046
+ removeTrack: (trackId: string) => void;
1047
+ /** Number of sources currently decoding. */
1048
+ loadingCount: number;
1049
+ /** True when any source is still decoding. */
1050
+ isLoading: boolean;
1051
+ /** Tracks that failed to load (removed from `tracks` automatically). */
1052
+ errors: TrackLoadError[];
1053
+ }
1054
+ declare function useDynamicTracks(): UseDynamicTracksReturn;
1055
+
1056
+ /**
1057
+ * Hook for monitoring master output levels
1058
+ *
1059
+ * Connects an AudioWorklet meter processor to the Destination node for
1060
+ * real-time output level monitoring. Computes sample-accurate peak and
1061
+ * RMS via the meter worklet — no transient is missed.
1062
+ *
1063
+ * IMPORTANT: Uses getGlobalContext() from playout to ensure the meter
1064
+ * is created on the same AudioContext as the audio engine. Tone.js's
1065
+ * getContext()/getDestination() return the DEFAULT context, which is
1066
+ * replaced when getGlobalContext() calls setContext() on first audio init.
1067
+ */
1068
+ interface UseOutputMeterOptions {
1069
+ /**
1070
+ * Number of channels to meter.
1071
+ * Default: 2 (stereo output)
1072
+ */
1073
+ channelCount?: number;
1074
+ /**
1075
+ * How often to update the levels (in Hz).
1076
+ * Default: 60 (60fps)
1077
+ */
1078
+ updateRate?: number;
1079
+ /**
1080
+ * Whether audio is currently playing. When this transitions to false,
1081
+ * all levels (current, peak, RMS) and smoothed state are reset to zero.
1082
+ * Without this, the browser's tail-time optimization stops calling the
1083
+ * worklet's process() when no audio flows, leaving the last non-zero
1084
+ * levels frozen in state.
1085
+ * Default: false
1086
+ */
1087
+ isPlaying?: boolean;
1088
+ }
1089
+ interface UseOutputMeterReturn {
1090
+ /** Per-channel peak output levels (0-1) */
1091
+ levels: number[];
1092
+ /** Per-channel held peak levels (0-1) */
1093
+ peakLevels: number[];
1094
+ /** Per-channel RMS output levels (0-1) */
1095
+ rmsLevels: number[];
1096
+ /** Reset all held peak levels to 0 */
1097
+ resetPeak: () => void;
1098
+ /** Error from meter setup (worklet load failure, context issues, etc.) */
1099
+ error: Error | null;
1100
+ }
1101
+ declare function useOutputMeter(options?: UseOutputMeterOptions): UseOutputMeterReturn;
1102
+
1103
+ /**
1104
+ * Factory for creating Tone.js effect instances from effect definitions
1105
+ */
1106
+
1107
+ interface EffectInstance {
1108
+ effect: ToneAudioNode;
1109
+ id: string;
1110
+ instanceId: string;
1111
+ dispose: () => void;
1112
+ setParameter: (name: string, value: number | string | boolean) => void;
1113
+ getParameter: (name: string) => number | string | boolean | undefined;
1114
+ connect: (destination: InputNode) => void;
1115
+ disconnect: () => void;
1116
+ }
1117
+ /**
1118
+ * Create an effect instance from a definition with initial parameter values
1119
+ */
1120
+ declare function createEffectInstance(definition: EffectDefinition, initialParams?: Record<string, number | string | boolean>): EffectInstance;
1121
+ /**
1122
+ * Create a chain of effects connected in series
1123
+ */
1124
+ declare function createEffectChain(effects: EffectInstance[]): {
1125
+ input: ToneAudioNode;
1126
+ output: ToneAudioNode;
1127
+ dispose: () => void;
1128
+ };
1129
+
1130
+ declare const PlayButton: React__default.FC<{
1131
+ className?: string;
1132
+ }>;
1133
+ declare const PauseButton: React__default.FC<{
1134
+ className?: string;
1135
+ }>;
1136
+ declare const StopButton: React__default.FC<{
1137
+ className?: string;
1138
+ }>;
1139
+ declare const RewindButton: React__default.FC<{
1140
+ className?: string;
1141
+ }>;
1142
+ declare const FastForwardButton: React__default.FC<{
1143
+ className?: string;
1144
+ }>;
1145
+ declare const SkipBackwardButton: React__default.FC<{
1146
+ skipAmount?: number;
1147
+ className?: string;
1148
+ }>;
1149
+ declare const SkipForwardButton: React__default.FC<{
1150
+ skipAmount?: number;
1151
+ className?: string;
1152
+ }>;
1153
+ declare const LoopButton: React__default.FC<{
1154
+ className?: string;
1155
+ }>;
1156
+ declare const SetLoopRegionButton: React__default.FC<{
1157
+ className?: string;
1158
+ }>;
1159
+ interface ClearAllButtonProps {
1160
+ onClearAll: () => void;
1161
+ label?: string;
1162
+ className?: string;
1163
+ }
1164
+ declare const ClearAllButton: React__default.FC<ClearAllButtonProps>;
1165
+
1166
+ declare const ZoomInButton: React__default.FC<{
1167
+ className?: string;
1168
+ disabled?: boolean;
1169
+ }>;
1170
+ declare const ZoomOutButton: React__default.FC<{
1171
+ className?: string;
1172
+ disabled?: boolean;
1173
+ }>;
1174
+
1175
+ /**
1176
+ * Master volume control that uses the playlist context
1177
+ */
1178
+ declare const MasterVolumeControl: React__default.FC<{
1179
+ className?: string;
1180
+ }>;
1181
+ /**
1182
+ * Time format selector that uses the playlist context
1183
+ */
1184
+ declare const TimeFormatSelect: React__default.FC<{
1185
+ className?: string;
1186
+ }>;
1187
+ /**
1188
+ * Audio position display that uses the playlist context.
1189
+ * Uses requestAnimationFrame for smooth 60fps updates during playback.
1190
+ * Direct DOM manipulation avoids React re-renders.
1191
+ */
1192
+ declare const AudioPosition: React__default.FC<{
1193
+ className?: string;
1194
+ }>;
1195
+ /**
1196
+ * Selection time inputs that use the playlist context
1197
+ */
1198
+ declare const SelectionTimeInputs: React__default.FC<{
1199
+ className?: string;
1200
+ }>;
1201
+ /**
1202
+ * Automatic scroll checkbox that uses the playlist context
1203
+ * Uses split contexts to avoid re-rendering during animation
1204
+ */
1205
+ declare const AutomaticScrollCheckbox: React__default.FC<{
1206
+ className?: string;
1207
+ }>;
1208
+
1209
+ /**
1210
+ * Continuous play checkbox that uses the playlist context.
1211
+ * Must be used within <AnnotationProvider>.
1212
+ */
1213
+ declare const ContinuousPlayCheckbox: React__default.FC<{
1214
+ className?: string;
1215
+ }>;
1216
+ /**
1217
+ * Link endpoints checkbox that uses the playlist context.
1218
+ * Must be used within <AnnotationProvider>.
1219
+ */
1220
+ declare const LinkEndpointsCheckbox: React__default.FC<{
1221
+ className?: string;
1222
+ }>;
1223
+ /**
1224
+ * Editable annotations checkbox that uses the playlist context.
1225
+ * Must be used within <AnnotationProvider>.
1226
+ */
1227
+ declare const EditableCheckbox: React__default.FC<{
1228
+ className?: string;
1229
+ }>;
1230
+ /**
1231
+ * Download annotations button that uses the playlist context.
1232
+ * Must be used within <AnnotationProvider>.
1233
+ */
1234
+ declare const DownloadAnnotationsButton: React__default.FC<{
1235
+ filename?: string;
1236
+ className?: string;
1237
+ }>;
1238
+
1239
+ interface ExportWavButtonProps {
1240
+ /** Button label */
1241
+ label?: string;
1242
+ /** Filename for the downloaded file (without extension) */
1243
+ filename?: string;
1244
+ /** Export mode: 'master' for stereo mix, 'individual' for single track */
1245
+ mode?: 'master' | 'individual';
1246
+ /** Track index for individual export */
1247
+ trackIndex?: number;
1248
+ /** Bit depth: 16 or 32 */
1249
+ bitDepth?: 16 | 32;
1250
+ /** Whether to apply effects (fades, etc.) - defaults to true */
1251
+ applyEffects?: boolean;
1252
+ /**
1253
+ * Optional Tone.js effects function for master effects. When provided, export will use Tone.Offline
1254
+ * to render through the effects chain. The function receives isOffline=true.
1255
+ */
1256
+ effectsFunction?: EffectsFunction;
1257
+ /**
1258
+ * Optional function to create offline track effects.
1259
+ * Takes a trackId and returns a TrackEffectsFunction for offline rendering.
1260
+ */
1261
+ createOfflineTrackEffects?: (trackId: string) => TrackEffectsFunction | undefined;
1262
+ /** CSS class name */
1263
+ className?: string;
1264
+ /** Callback when export completes */
1265
+ onExportComplete?: (blob: Blob) => void;
1266
+ /** Callback when export fails */
1267
+ onExportError?: (error: Error) => void;
1268
+ }
1269
+ declare const ExportWavButton: React__default.FC<ExportWavButtonProps>;
1270
+
1271
+ /**
1272
+ * Shared annotation types used across Waveform components
1273
+ */
1274
+
1275
+ /**
1276
+ * Custom function to generate the label shown on annotation boxes in the waveform.
1277
+ * Receives the annotation data and its index in the list, returns a string label.
1278
+ * Default behavior: displays annotation.id
1279
+ */
1280
+ type GetAnnotationBoxLabelFn = (annotation: AnnotationData, index: number) => string;
1281
+ /**
1282
+ * Callback when annotations are updated (e.g., boundaries dragged).
1283
+ * Called with the full updated annotations array.
1284
+ */
1285
+ type OnAnnotationUpdateFn = (annotations: AnnotationData[]) => void;
1286
+
1287
+ interface WaveformProps {
1288
+ renderTrackControls?: (trackIndex: number) => ReactNode;
1289
+ /** Custom render function for timescale tick labels. `label` is a formatted string
1290
+ * (bar/beat notation like "2.3" in beats mode, or "m:ss" in temporal mode). */
1291
+ renderTick?: (label: string, pixelPosition: number) => ReactNode;
1292
+ /** @deprecated Use `renderTick` instead. */
1293
+ renderTimestamp?: (timeMs: number, pixelPosition: number) => ReactNode;
1294
+ /** Custom playhead render function. Receives position (pixels) and color from theme. */
1295
+ renderPlayhead?: RenderPlayheadFunction;
1296
+ annotationControls?: AnnotationAction[];
1297
+ annotationListConfig?: AnnotationActionOptions;
1298
+ annotationTextHeight?: number;
1299
+ /**
1300
+ * Custom render function for annotation items in the text list.
1301
+ * Use this to completely customize how each annotation is displayed.
1302
+ */
1303
+ renderAnnotationItem?: (props: RenderAnnotationItemProps) => ReactNode;
1304
+ /**
1305
+ * Custom function to generate the label shown on annotation boxes in the waveform.
1306
+ * Receives the annotation data and its index, returns a string label.
1307
+ * Default: annotation.id
1308
+ */
1309
+ getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;
1310
+ /** Where to position the active annotation when auto-scrolling: 'center', 'start', 'end', or 'nearest'. Defaults to 'center'. */
1311
+ scrollActivePosition?: ScrollLogicalPosition;
1312
+ /** Which scrollable containers to scroll: 'nearest' (only the annotation list) or 'all' (including viewport). Defaults to 'nearest'. */
1313
+ scrollActiveContainer?: 'nearest' | 'all';
1314
+ className?: string;
1315
+ showClipHeaders?: boolean;
1316
+ interactiveClips?: boolean;
1317
+ showFades?: boolean;
1318
+ /**
1319
+ * Enable mobile-optimized touch interactions.
1320
+ * When true, increases touch target sizes for clip boundaries.
1321
+ * Use with useDragSensors({ touchOptimized: true }) for best results.
1322
+ */
1323
+ touchOptimized?: boolean;
1324
+ /** Callback when a track's close button is clicked. Only renders close button when provided. */
1325
+ onRemoveTrack?: (trackIndex: number) => void;
1326
+ recordingState?: {
1327
+ isRecording: boolean;
1328
+ trackId: string;
1329
+ startSample: number;
1330
+ durationSamples: number;
1331
+ peaks: (Int8Array | Int16Array)[];
1332
+ bits: 8 | 16;
1333
+ };
1334
+ }
1335
+ /**
1336
+ * Waveform visualization component that uses the playlist context.
1337
+ *
1338
+ * Composes PlaylistVisualization (waveform + tracks) and
1339
+ * PlaylistAnnotationList (annotation text list below the waveform).
1340
+ */
1341
+ declare const Waveform: React__default.FC<WaveformProps>;
1342
+
1343
+ interface MediaElementWaveformProps {
1344
+ /** Height in pixels for the annotation text list */
1345
+ annotationTextHeight?: number;
1346
+ /** Custom function to generate the label shown on annotation boxes */
1347
+ getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;
1348
+ /**
1349
+ * Custom render function for annotation items in the text list.
1350
+ * When provided, completely replaces the default annotation item rendering.
1351
+ * Use this to customize the appearance of each annotation (e.g., add furigana).
1352
+ */
1353
+ renderAnnotationItem?: (props: RenderAnnotationItemProps) => React__default.ReactNode;
1354
+ /** Whether annotation boundaries can be edited by dragging. Defaults to false. */
1355
+ editable?: boolean;
1356
+ /** Whether dragging one annotation boundary also moves the adjacent annotation's boundary. Defaults to false. */
1357
+ linkEndpoints?: boolean;
1358
+ /**
1359
+ * Callback when annotations are updated (e.g., boundaries dragged).
1360
+ * Called with the full updated annotations array.
1361
+ */
1362
+ onAnnotationUpdate?: OnAnnotationUpdateFn;
1363
+ /** Where to position the active annotation when auto-scrolling: 'center', 'start', 'end', or 'nearest'. Defaults to 'center'. */
1364
+ scrollActivePosition?: ScrollLogicalPosition;
1365
+ /** Which scrollable containers to scroll: 'nearest' (only the annotation list) or 'all' (including viewport). Defaults to 'nearest'. */
1366
+ scrollActiveContainer?: 'nearest' | 'all';
1367
+ /** Custom playhead render function. Receives position, color, and animation refs for smooth 60fps animation. */
1368
+ renderPlayhead?: RenderPlayheadFunction;
1369
+ /** Show fade in/out overlays on the waveform. Defaults to false. */
1370
+ showFades?: boolean;
1371
+ className?: string;
1372
+ }
1373
+ /**
1374
+ * Simplified Waveform component for MediaElementPlaylistProvider
1375
+ *
1376
+ * This is a stripped-down version of Waveform that works with the
1377
+ * MediaElement context. It supports:
1378
+ * - Single track visualization
1379
+ * - Click to seek
1380
+ * - Annotation display and click-to-play
1381
+ * - Playhead animation
1382
+ *
1383
+ * For multi-track editing, use the full Waveform with WaveformPlaylistProvider.
1384
+ */
1385
+ declare const MediaElementWaveform: React__default.FC<MediaElementWaveformProps>;
1386
+
1387
+ interface MediaElementPlaylistProps {
1388
+ /** Custom function to generate the label shown on annotation boxes */
1389
+ getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;
1390
+ /** Whether annotation boundaries can be edited by dragging. Defaults to false. */
1391
+ editable?: boolean;
1392
+ /** Whether dragging one annotation boundary also moves the adjacent annotation's boundary. Defaults to false. */
1393
+ linkEndpoints?: boolean;
1394
+ /**
1395
+ * Callback when annotations are updated (e.g., boundaries dragged).
1396
+ * Called with the full updated annotations array.
1397
+ */
1398
+ onAnnotationUpdate?: OnAnnotationUpdateFn;
1399
+ /** Custom playhead render function. Receives position, color, and animation refs for smooth 60fps animation. */
1400
+ renderPlayhead?: RenderPlayheadFunction;
1401
+ /** Show fade in/out overlays on the waveform. Defaults to false. */
1402
+ showFades?: boolean;
1403
+ className?: string;
1404
+ }
1405
+ /**
1406
+ * Standalone waveform + annotation boxes component for MediaElementPlaylistProvider.
1407
+ *
1408
+ * Renders the waveform visualization, annotation boxes, selection, and playhead.
1409
+ * Does NOT render the annotation text list — use `MediaElementAnnotationList` for that.
1410
+ *
1411
+ * Must be used inside a `MediaElementPlaylistProvider`.
1412
+ *
1413
+ * This component can be placed independently in consumer layouts, allowing the
1414
+ * waveform and annotation list to be positioned separately (e.g., in different
1415
+ * panels or with custom elements between them).
1416
+ */
1417
+ declare const MediaElementPlaylist: React__default.FC<MediaElementPlaylistProps>;
1418
+
1419
+ interface MediaElementAnnotationListProps {
1420
+ /** Height in pixels for the annotation text list */
1421
+ height?: number;
1422
+ /**
1423
+ * Custom render function for annotation items in the text list.
1424
+ * When provided, completely replaces the default annotation item rendering.
1425
+ */
1426
+ renderAnnotationItem?: (props: RenderAnnotationItemProps) => React__default.ReactNode;
1427
+ /**
1428
+ * Callback when annotations are updated (e.g., text edited).
1429
+ * Called with the full updated annotations array.
1430
+ */
1431
+ onAnnotationUpdate?: OnAnnotationUpdateFn;
1432
+ /** Whether annotation text can be edited. Defaults to false. */
1433
+ editable?: boolean;
1434
+ /**
1435
+ * Action controls to show on each annotation item (e.g., delete, split).
1436
+ * Only rendered when `editable` is true.
1437
+ */
1438
+ controls?: AnnotationAction[];
1439
+ /**
1440
+ * Override annotation list config. Falls back to context values
1441
+ * `{ linkEndpoints: false, continuousPlay }` if not provided.
1442
+ */
1443
+ annotationListConfig?: AnnotationActionOptions;
1444
+ /** Where to position the active annotation when auto-scrolling. Defaults to 'center'. */
1445
+ scrollActivePosition?: ScrollLogicalPosition;
1446
+ /** Which scrollable containers to scroll: 'nearest' or 'all'. Defaults to 'nearest'. */
1447
+ scrollActiveContainer?: 'nearest' | 'all';
1448
+ }
1449
+ /**
1450
+ * Standalone annotation text list component for MediaElementPlaylistProvider.
1451
+ *
1452
+ * Requires @waveform-playlist/annotations with AnnotationProvider.
1453
+ * Throws if used without `<AnnotationProvider>` wrapping the component tree.
1454
+ */
1455
+ declare const MediaElementAnnotationList: React__default.FC<MediaElementAnnotationListProps>;
1456
+
1457
+ interface PlaylistVisualizationProps {
1458
+ renderTrackControls?: (trackIndex: number) => ReactNode;
1459
+ renderTick?: (label: string, pixelPosition: number) => ReactNode;
1460
+ /** Custom playhead render function. Receives position (pixels) and color from theme. */
1461
+ renderPlayhead?: RenderPlayheadFunction;
1462
+ annotationControls?: AnnotationAction[];
1463
+ /**
1464
+ * Custom function to generate the label shown on annotation boxes in the waveform.
1465
+ * Receives the annotation data and its index, returns a string label.
1466
+ * Default: annotation.id
1467
+ */
1468
+ getAnnotationBoxLabel?: GetAnnotationBoxLabelFn;
1469
+ className?: string;
1470
+ showClipHeaders?: boolean;
1471
+ interactiveClips?: boolean;
1472
+ showFades?: boolean;
1473
+ /**
1474
+ * Enable mobile-optimized touch interactions.
1475
+ * When true, increases touch target sizes for clip boundaries.
1476
+ * Use with useDragSensors({ touchOptimized: true }) for best results.
1477
+ */
1478
+ touchOptimized?: boolean;
1479
+ /** Callback when a track's close button is clicked. Only renders close button when provided. */
1480
+ onRemoveTrack?: (trackIndex: number) => void;
1481
+ recordingState?: {
1482
+ isRecording: boolean;
1483
+ trackId: string;
1484
+ startSample: number;
1485
+ durationSamples: number;
1486
+ peaks: (Int8Array | Int16Array)[];
1487
+ bits: 8 | 16;
1488
+ };
1489
+ }
1490
+ /**
1491
+ * Standalone playlist visualization component (WebAudio version).
1492
+ *
1493
+ * Renders the waveform tracks, timescale, annotations boxes, selection,
1494
+ * playhead, loop regions, and track controls — everything that lives
1495
+ * inside <Playlist> plus wrapping providers.
1496
+ *
1497
+ * Does NOT render AnnotationText (the annotation list below the waveform).
1498
+ * Pair with PlaylistAnnotationList for a full annotation editing UI.
1499
+ */
1500
+ declare const PlaylistVisualization: React__default.FC<PlaylistVisualizationProps>;
1501
+
1502
+ interface PlaylistAnnotationListProps {
1503
+ /** Height in pixels for the annotation text list */
1504
+ height?: number;
1505
+ /**
1506
+ * Custom render function for annotation items in the text list.
1507
+ * When provided, completely replaces the default annotation item rendering.
1508
+ */
1509
+ renderAnnotationItem?: (props: RenderAnnotationItemProps) => React__default.ReactNode;
1510
+ /**
1511
+ * Callback when annotations are updated (e.g., text edited).
1512
+ * Called with the full updated annotations array.
1513
+ */
1514
+ onAnnotationUpdate?: OnAnnotationUpdateFn;
1515
+ /**
1516
+ * Action controls to show on each annotation item (e.g., delete, split).
1517
+ * Only rendered when `annotationsEditable` is true in context.
1518
+ */
1519
+ controls?: AnnotationAction[];
1520
+ /**
1521
+ * Override annotation list config. Falls back to context values
1522
+ * `{ linkEndpoints, continuousPlay }` if not provided.
1523
+ */
1524
+ annotationListConfig?: AnnotationActionOptions;
1525
+ /** Where to position the active annotation when auto-scrolling. Defaults to 'center'. */
1526
+ scrollActivePosition?: ScrollLogicalPosition;
1527
+ /** Which scrollable containers to scroll: 'nearest' or 'all'. Defaults to 'nearest'. */
1528
+ scrollActiveContainer?: 'nearest' | 'all';
1529
+ }
1530
+ /**
1531
+ * Standalone annotation text list component for WaveformPlaylistProvider (WebAudio).
1532
+ *
1533
+ * Requires @waveform-playlist/annotations with AnnotationProvider.
1534
+ * Throws if used without `<AnnotationProvider>` wrapping the component tree.
1535
+ */
1536
+ declare const PlaylistAnnotationList: React__default.FC<PlaylistAnnotationListProps>;
1537
+
1538
+ interface KeyboardShortcutsProps {
1539
+ /** Enable default playback shortcuts (Space, Escape, 0). Defaults to false. */
1540
+ playback?: boolean;
1541
+ /** Enable clip splitting shortcut ('s' key). Defaults to false. */
1542
+ clipSplitting?: boolean;
1543
+ /** Enable annotation keyboard controls (arrow nav, boundary editing). Defaults to false. */
1544
+ annotations?: boolean;
1545
+ /** Additional shortcuts appended to the defaults. */
1546
+ additionalShortcuts?: KeyboardShortcut[];
1547
+ }
1548
+ /**
1549
+ * Self-closing component that sets up keyboard shortcuts for the playlist.
1550
+ * Must be rendered inside a WaveformPlaylistProvider.
1551
+ *
1552
+ * @example
1553
+ * ```tsx
1554
+ * <WaveformPlaylistProvider tracks={tracks} {...}>
1555
+ * <KeyboardShortcuts playback clipSplitting />
1556
+ * <Waveform />
1557
+ * </WaveformPlaylistProvider>
1558
+ * ```
1559
+ */
1560
+ declare const KeyboardShortcuts: React__default.FC<KeyboardShortcutsProps>;
1561
+
1562
+ /**
1563
+ * Props the browser package passes to the AnnotationText component.
1564
+ * Mirrors what PlaylistAnnotationList and MediaElementAnnotationList actually use.
1565
+ */
1566
+ interface AnnotationTextIntegrationProps {
1567
+ annotations: AnnotationData[];
1568
+ activeAnnotationId?: string;
1569
+ shouldScrollToActive?: boolean;
1570
+ scrollActivePosition?: ScrollLogicalPosition;
1571
+ scrollActiveContainer?: 'nearest' | 'all';
1572
+ editable?: boolean;
1573
+ controls?: AnnotationAction[];
1574
+ annotationListConfig?: AnnotationActionOptions;
1575
+ height?: number;
1576
+ onAnnotationUpdate?: (updatedAnnotations: AnnotationData[]) => void;
1577
+ renderAnnotationItem?: (props: RenderAnnotationItemProps) => React.ReactNode;
1578
+ }
1579
+ /**
1580
+ * Props the browser package passes to the AnnotationBox component.
1581
+ * Mirrors what PlaylistVisualization and MediaElementPlaylist actually use.
1582
+ */
1583
+ interface AnnotationBoxIntegrationProps {
1584
+ annotationId: string;
1585
+ annotationIndex: number;
1586
+ startPosition: number;
1587
+ endPosition: number;
1588
+ label?: string;
1589
+ color?: string;
1590
+ isActive?: boolean;
1591
+ onClick?: () => void;
1592
+ editable?: boolean;
1593
+ }
1594
+ /**
1595
+ * Props the browser package passes to the AnnotationBoxesWrapper component.
1596
+ * Mirrors what PlaylistVisualization and MediaElementPlaylist actually use.
1597
+ */
1598
+ interface AnnotationBoxesWrapperIntegrationProps {
1599
+ children?: React.ReactNode;
1600
+ height?: number;
1601
+ width?: number;
1602
+ }
1603
+ /**
1604
+ * Interface for annotation integration provided by @waveform-playlist/annotations.
1605
+ *
1606
+ * The browser package defines what it needs, and the optional annotations package
1607
+ * provides it via <AnnotationProvider>.
1608
+ */
1609
+ interface AnnotationIntegration {
1610
+ parseAeneas: (data: unknown) => AnnotationData;
1611
+ serializeAeneas: (annotation: AnnotationData) => unknown;
1612
+ AnnotationText: React.ComponentType<AnnotationTextIntegrationProps>;
1613
+ AnnotationBox: React.ComponentType<AnnotationBoxIntegrationProps>;
1614
+ AnnotationBoxesWrapper: React.ComponentType<AnnotationBoxesWrapperIntegrationProps>;
1615
+ ContinuousPlayCheckbox: React.ComponentType<{
1616
+ checked: boolean;
1617
+ onChange: (checked: boolean) => void;
1618
+ className?: string;
1619
+ }>;
1620
+ LinkEndpointsCheckbox: React.ComponentType<{
1621
+ checked: boolean;
1622
+ onChange: (checked: boolean) => void;
1623
+ className?: string;
1624
+ }>;
1625
+ EditableCheckbox: React.ComponentType<{
1626
+ checked: boolean;
1627
+ onChange: (checked: boolean) => void;
1628
+ className?: string;
1629
+ }>;
1630
+ DownloadAnnotationsButton: React.ComponentType<{
1631
+ annotations: AnnotationData[];
1632
+ filename?: string;
1633
+ className?: string;
1634
+ }>;
1635
+ }
1636
+ declare const AnnotationIntegrationProvider: React$1.Provider<AnnotationIntegration | null>;
1637
+ /**
1638
+ * Hook to access annotation integration provided by @waveform-playlist/annotations.
1639
+ * Throws if used without <AnnotationProvider> wrapping the component tree.
1640
+ *
1641
+ * Follows the Kent C. Dodds pattern:
1642
+ * https://kentcdodds.com/blog/how-to-use-react-context-effectively
1643
+ */
1644
+ declare function useAnnotationIntegration(): AnnotationIntegration;
1645
+
1646
+ interface SpectrogramIntegration {
1647
+ trackSpectrogramOverrides: Map<string, TrackSpectrogramOverrides>;
1648
+ spectrogramWorkerApi: SpectrogramWorkerApi | null;
1649
+ spectrogramConfig?: SpectrogramConfig;
1650
+ spectrogramColorMap?: ColorMapValue;
1651
+ setTrackRenderMode: (trackId: string, mode: RenderMode) => void;
1652
+ setTrackSpectrogramConfig: (trackId: string, config: SpectrogramConfig, colorMap?: ColorMapValue) => void;
1653
+ registerSpectrogramCanvases: (clipId: string, channelIndex: number, canvasIds: string[], canvasWidths: number[]) => void;
1654
+ unregisterSpectrogramCanvases: (clipId: string, channelIndex: number) => void;
1655
+ /** Render spectrogram menu items for a track's context menu */
1656
+ renderMenuItems?: (props: {
1657
+ renderMode: string;
1658
+ onRenderModeChange: (mode: RenderMode) => void;
1659
+ onOpenSettings: () => void;
1660
+ onClose?: () => void;
1661
+ }) => TrackMenuItem[];
1662
+ /** Settings modal component provided by the spectrogram package */
1663
+ SettingsModal?: React.ComponentType<{
1664
+ open: boolean;
1665
+ onClose: () => void;
1666
+ config: SpectrogramConfig;
1667
+ colorMap: ColorMapValue;
1668
+ onApply: (config: SpectrogramConfig, colorMap: ColorMapValue) => void;
1669
+ }>;
1670
+ /** Get color lookup table for a color map name */
1671
+ getColorMap: (name: ColorMapValue) => Uint8Array;
1672
+ /** Get frequency scale function for a scale name */
1673
+ getFrequencyScale: (name: string) => (f: number, minF: number, maxF: number) => number;
1674
+ }
1675
+ /** Minimal type for the worker API surface used by browser components */
1676
+ interface SpectrogramWorkerApi {
1677
+ registerCanvas: (canvasId: string, canvas: OffscreenCanvas) => void;
1678
+ unregisterCanvas: (canvasId: string) => void;
1679
+ }
1680
+ declare const SpectrogramIntegrationProvider: React$1.Provider<SpectrogramIntegration | null>;
1681
+ /**
1682
+ * Hook to access spectrogram integration provided by @waveform-playlist/spectrogram.
1683
+ * Throws if used without <SpectrogramProvider> wrapping the component tree.
1684
+ *
1685
+ * Follows the Kent C. Dodds pattern:
1686
+ * https://kentcdodds.com/blog/how-to-use-react-context-effectively
1687
+ */
1688
+ declare function useSpectrogramIntegration(): SpectrogramIntegration;
1689
+
1690
+ interface ClipInteractionProviderProps {
1691
+ /** Enable snap-to-grid for clip moves and boundary trims. When true,
1692
+ * auto-detects beats snapping from BeatsAndBarsProvider context
1693
+ * (if present with scaleMode="beats" and snapTo!="off"), otherwise
1694
+ * falls back to timescale-based snapping. Default: false. */
1695
+ snap?: boolean;
1696
+ touchOptimized?: boolean;
1697
+ children: React__default.ReactNode;
1698
+ }
1699
+ declare const ClipInteractionProvider: React__default.FC<ClipInteractionProviderProps>;
1700
+
1701
+ declare function useClipInteractionEnabled(): boolean;
1702
+
1703
+ interface ClipCollisionOptions {
1704
+ tracks: ClipTrack[];
1705
+ samplesPerPixel: number;
1706
+ }
1707
+ /**
1708
+ * Modifier that constrains clip drag movement to prevent overlaps.
1709
+ *
1710
+ * For clip move operations: constrains horizontal transform to valid positions
1711
+ * using the engine's collision detection.
1712
+ *
1713
+ * For boundary trim operations: returns zero transform because visual feedback
1714
+ * comes from React state updates resizing the clip, not from CSS translate.
1715
+ */
1716
+ declare class ClipCollisionModifier extends Modifier<DragDropManager<any, any>, ClipCollisionOptions> {
1717
+ apply(operation: DragOperation): {
1718
+ x: number;
1719
+ y: number;
1720
+ };
1721
+ static configure: (options: ClipCollisionOptions) => _dnd_kit_abstract.PluginDescriptor<any, any, typeof ClipCollisionModifier>;
1722
+ }
1723
+
1724
+ interface SnapToGridBeatsOptions {
1725
+ mode: 'beats';
1726
+ snapTo: SnapTo;
1727
+ bpm: number;
1728
+ timeSignature: [number, number];
1729
+ samplesPerPixel: number;
1730
+ sampleRate: number;
1731
+ }
1732
+ interface SnapToGridTimescaleOptions {
1733
+ mode: 'timescale';
1734
+ gridSamples: number;
1735
+ samplesPerPixel: number;
1736
+ }
1737
+ type SnapToGridOptions = SnapToGridBeatsOptions | SnapToGridTimescaleOptions;
1738
+ /**
1739
+ * dnd-kit modifier that quantizes clip drag movement to a grid.
1740
+ *
1741
+ * Two modes:
1742
+ * - "beats": Snaps to beat/bar grid using PPQN tick space for exact musical timing.
1743
+ * - "timescale": Snaps to a sample-based grid derived from timescale markers.
1744
+ *
1745
+ * Designed to compose with ClipCollisionModifier — snap first,
1746
+ * then collision constrains the snapped position.
1747
+ */
1748
+ declare class SnapToGridModifier extends Modifier<DragDropManager<any, any>, SnapToGridOptions> {
1749
+ apply(operation: DragOperation): {
1750
+ x: number;
1751
+ y: number;
1752
+ };
1753
+ static configure: (options: SnapToGridBeatsOptions | SnapToGridTimescaleOptions) => _dnd_kit_abstract.PluginDescriptor<any, any, typeof SnapToGridModifier>;
1754
+ }
1755
+
1756
+ /**
1757
+ * DragDropProvider plugins customizer that disables the Feedback plugin's drop animation.
1758
+ *
1759
+ * Without this, the Feedback plugin animates the dragged element back to its original
1760
+ * position on drop, causing a visual snap-back before React re-renders at the new position.
1761
+ *
1762
+ * Usage:
1763
+ * ```tsx
1764
+ * <DragDropProvider plugins={noDropAnimationPlugins} ...>
1765
+ * ```
1766
+ */
1767
+ declare const noDropAnimationPlugins: (defaults: Plugins) => Plugins;
1768
+
1769
+ /**
1770
+ * Waveform Data Loader
1771
+ *
1772
+ * Utilities for loading pre-computed waveform data in waveform-data.js format.
1773
+ * Supports both binary (.dat) and JSON formats from BBC's audiowaveform tool.
1774
+ */
1775
+
1776
+ /**
1777
+ * Load waveform data from a .dat or .json file
1778
+ *
1779
+ * @param src - URL to waveform data file (.dat or .json)
1780
+ * @returns WaveformData instance
1781
+ */
1782
+ declare function loadWaveformData(src: string): Promise<WaveformData>;
1783
+ /**
1784
+ * Convert WaveformData to our internal Peaks format
1785
+ *
1786
+ * @param waveformData - WaveformData instance from waveform-data.js
1787
+ * @param channelIndex - Channel index (0 for mono/left, 1 for right)
1788
+ * @returns Peaks data with alternating min/max values, preserving original bit depth
1789
+ */
1790
+ declare function waveformDataToPeaks(waveformData: WaveformData, channelIndex?: number): {
1791
+ data: Int8Array | Int16Array;
1792
+ bits: 8 | 16;
1793
+ length: number;
1794
+ sampleRate: number;
1795
+ };
1796
+ /**
1797
+ * Load waveform data file and convert to Peaks format in one step
1798
+ *
1799
+ * @param src - URL to waveform data file (.dat or .json)
1800
+ * @param channelIndex - Channel index (default: 0)
1801
+ * @returns Peaks data ready for rendering
1802
+ */
1803
+ declare function loadPeaksFromWaveformData(src: string, channelIndex?: number): Promise<{
1804
+ data: Int8Array | Int16Array;
1805
+ bits: 8 | 16;
1806
+ length: number;
1807
+ sampleRate: number;
1808
+ }>;
1809
+ /**
1810
+ * Get metadata from waveform data file without converting to peaks
1811
+ *
1812
+ * @param src - URL to waveform data file
1813
+ * @returns Metadata (sample rate, channels, duration, bits, etc.)
1814
+ */
1815
+ declare function getWaveformDataMetadata(src: string): Promise<{
1816
+ sampleRate: number;
1817
+ channels: number;
1818
+ duration: number;
1819
+ samplesPerPixel: number;
1820
+ length: number;
1821
+ bits: 8 | 16;
1822
+ }>;
1823
+
1824
+ export { type ActiveEffect, type AnnotationIntegration, AnnotationIntegrationProvider, AudioPosition, type AudioTrackConfig, AutomaticScrollCheckbox, ClearAllButton, type ClearAllButtonProps, ClipCollisionModifier, ClipInteractionProvider, type ClipInteractionProviderProps, ContinuousPlayCheckbox, DownloadAnnotationsButton, EditableCheckbox, type EffectDefinition, type EffectInstance, type EffectParameter, type ExportOptions, type ExportResult, ExportWavButton, type ExportWavButtonProps, FastForwardButton, type GetAnnotationBoxLabelFn, KeyboardShortcuts, type KeyboardShortcutsProps, LinkEndpointsCheckbox, LoopButton, MasterVolumeControl, type MasterVolumeControls, type MediaElementAnimationContextValue, MediaElementAnnotationList, type MediaElementAnnotationListProps, type MediaElementControlsContextValue, type MediaElementDataContextValue, MediaElementPlaylist, type MediaElementPlaylistProps, MediaElementPlaylistProvider, type MediaElementStateContextValue, type MediaElementTrackConfig, MediaElementWaveform, type MediaElementWaveformProps, type OnAnnotationUpdateFn, type ParameterType, PauseButton, PlayButton, PlaylistAnnotationList, type PlaylistAnnotationListProps, PlaylistVisualization, type PlaylistVisualizationProps, RewindButton, SelectionTimeInputs, SetLoopRegionButton, SkipBackwardButton, SkipForwardButton, SnapToGridModifier, type SpectrogramIntegration, SpectrogramIntegrationProvider, StopButton, type TimeFormatControls, TimeFormatSelect, type TrackActiveEffect, type TrackEffectsState, type TrackLoadError, type TrackSource, type TrackState$1 as TrackState, type UseDynamicEffectsReturn, type UseDynamicTracksReturn, type UseExportWavReturn, type UseOutputMeterOptions, type UseOutputMeterReturn, type UsePlaybackShortcutsOptions, type UsePlaybackShortcutsReturn, type UseTrackDynamicEffectsReturn, Waveform, WaveformPlaylistProvider, type WaveformProps, type WaveformTrack, type ZoomControls, ZoomInButton, ZoomOutButton, createEffectChain, createEffectInstance, effectCategories, effectDefinitions, getEffectDefinition, getEffectsByCategory, getShortcutLabel, getWaveformDataMetadata, loadPeaksFromWaveformData, loadWaveformData, noDropAnimationPlugins, useAnnotationDragHandlers, useAnnotationIntegration, useAnnotationKeyboardControls, useAudioTracks, useClipDragHandlers, useClipInteractionEnabled, useClipSplitting, useDragSensors, useDynamicEffects, useDynamicTracks, useExportWav, useKeyboardShortcuts, useMasterAnalyser, useMasterVolume, useMediaElementAnimation, useMediaElementControls, useMediaElementData, useMediaElementState, useOutputMeter, usePlaybackAnimation, usePlaybackShortcuts, usePlaylistControls, usePlaylistData, usePlaylistState, useSpectrogramIntegration, useTimeFormat, useTrackDynamicEffects, useZoomControls, waveformDataToPeaks };