@waveform-playlist/browser 7.1.3 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import { Analyser } from 'tone';
2
- import { BaseContext } from 'tone';
3
2
  import { default as default_2 } from 'react';
4
3
  import { default as default_3 } from 'waveform-data';
4
+ import { DragCancelEvent } from '@dnd-kit/core';
5
5
  import { DragEndEvent } from '@dnd-kit/core';
6
6
  import { DragMoveEvent } from '@dnd-kit/core';
7
7
  import { DragStartEvent } from '@dnd-kit/core';
8
- import { Fade as Fade_2 } from '@waveform-playlist/core';
8
+ import { EngineState } from '@waveform-playlist/engine';
9
9
  import { Gain } from 'tone';
10
10
  import { InputNode } from 'tone';
11
11
  import { MediaElementPlayout } from '@waveform-playlist/media-element-playout';
12
12
  import { Modifier } from '@dnd-kit/core';
13
13
  import { MutableRefObject } from 'react';
14
+ import { PlaylistEngine } from '@waveform-playlist/engine';
14
15
  import { Provider } from 'react';
15
16
  import react__default from 'react';
16
17
  import { ReactNode } from 'react';
@@ -19,7 +20,6 @@ import { SensorDescriptor } from '@dnd-kit/core';
19
20
  import { SensorOptions } from '@dnd-kit/core';
20
21
  import * as Tone from 'tone';
21
22
  import { ToneAudioNode } from 'tone';
22
- import { Track } from '@waveform-playlist/core';
23
23
  import { Volume } from 'tone';
24
24
 
25
25
  export declare interface ActiveEffect {
@@ -262,16 +262,6 @@ export declare const AutomaticScrollCheckbox: default_2.FC<{
262
262
  */
263
263
  declare type Bits = 8 | 16;
264
264
 
265
- declare interface ClipInfo {
266
- buffer: AudioBuffer;
267
- startTime: number;
268
- duration: number;
269
- offset: number;
270
- fadeIn?: Fade_2;
271
- fadeOut?: Fade_2;
272
- gain: number;
273
- }
274
-
275
265
  declare interface ClipPeaks {
276
266
  clipId: string;
277
267
  trackName: string;
@@ -630,6 +620,8 @@ export declare const MasterVolumeControl: default_2.FC<{
630
620
  export declare interface MasterVolumeControls {
631
621
  masterVolume: number;
632
622
  setMasterVolume: (volume: number) => void;
623
+ /** Ref holding the current masterVolume for seeding a fresh engine. */
624
+ masterVolumeRef: React.RefObject<number>;
633
625
  }
634
626
 
635
627
  export declare interface MediaElementAnimationContextValue {
@@ -890,6 +882,8 @@ declare interface PlaybackAnimationContextValue {
890
882
  currentTimeRef: default_2.RefObject<number>;
891
883
  playbackStartTimeRef: default_2.RefObject<number>;
892
884
  audioStartPositionRef: default_2.RefObject<number>;
885
+ /** Returns current playback time from engine (auto-wraps at loop boundaries). */
886
+ getPlaybackTime: () => number;
893
887
  }
894
888
 
895
889
  export declare const PlayButton: default_2.FC<{
@@ -908,9 +902,9 @@ declare interface PlayheadProps {
908
902
  isPlaying: boolean;
909
903
  /** Ref to current time in seconds - use for smooth animation during playback */
910
904
  currentTimeRef: react__default.RefObject<number>;
911
- /** Audio context start time when playback began - for calculating elapsed time */
905
+ /** Audio context start time when playback began. Fallback when getPlaybackTime is not provided. */
912
906
  playbackStartTimeRef: react__default.RefObject<number>;
913
- /** Audio position when playback started - for calculating current position */
907
+ /** Audio position when playback started. Fallback when getPlaybackTime is not provided. */
914
908
  audioStartPositionRef: react__default.RefObject<number>;
915
909
  /** Samples per pixel - for converting time to pixels */
916
910
  samplesPerPixel: number;
@@ -920,6 +914,8 @@ declare interface PlayheadProps {
920
914
  controlsOffset: number;
921
915
  /** Function to get current audio context time - required for smooth animation */
922
916
  getAudioContextTime?: () => number;
917
+ /** Returns current playback time (auto-wraps at loop boundaries). Preferred over manual elapsed calculation. */
918
+ getPlaybackTime?: () => number;
923
919
  }
924
920
 
925
921
  /**
@@ -1004,7 +1000,7 @@ declare interface PlaylistDataContextValue {
1004
1000
  show: boolean;
1005
1001
  width: number;
1006
1002
  };
1007
- playoutRef: default_2.RefObject<TonePlayout | null>;
1003
+ playoutRef: default_2.RefObject<PlaylistEngine | null>;
1008
1004
  samplesPerPixel: number;
1009
1005
  timeFormat: TimeFormat;
1010
1006
  masterVolume: number;
@@ -1018,6 +1014,9 @@ declare interface PlaylistDataContextValue {
1018
1014
  isReady: boolean;
1019
1015
  /** Whether tracks are rendered in mono mode */
1020
1016
  mono: boolean;
1017
+ /** Ref set by useClipDragHandlers during boundary trim drags.
1018
+ * When true, loadAudio skips engine rebuild — visual updates flow via React state only. */
1019
+ isDraggingRef: default_2.MutableRefObject<boolean>;
1021
1020
  }
1022
1021
 
1023
1022
  declare interface PlaylistStateContextValue {
@@ -1246,88 +1245,6 @@ export declare const TimeFormatSelect: default_2.FC<{
1246
1245
 
1247
1246
  export { Tone }
1248
1247
 
1249
- declare class TonePlayout {
1250
- private tracks;
1251
- private masterVolume;
1252
- private isInitialized;
1253
- private soloedTracks;
1254
- private manualMuteState;
1255
- private effectsCleanup?;
1256
- private onPlaybackCompleteCallback?;
1257
- private activeTracks;
1258
- private playbackSessionId;
1259
- constructor(options?: TonePlayoutOptions);
1260
- private gainToDb;
1261
- init(): Promise<void>;
1262
- addTrack(trackOptions: ToneTrackOptions): ToneTrack;
1263
- /**
1264
- * Apply solo muting after all tracks have been added.
1265
- * Call this after adding all tracks to ensure solo logic is applied correctly.
1266
- */
1267
- applyInitialSoloState(): void;
1268
- removeTrack(trackId: string): void;
1269
- getTrack(trackId: string): ToneTrack | undefined;
1270
- play(when?: number, offset?: number, duration?: number): void;
1271
- pause(): void;
1272
- stop(): void;
1273
- setMasterGain(gain: number): void;
1274
- setSolo(trackId: string, soloed: boolean): void;
1275
- private updateSoloMuting;
1276
- setMute(trackId: string, muted: boolean): void;
1277
- getCurrentTime(): number;
1278
- seekTo(time: number): void;
1279
- dispose(): void;
1280
- get context(): BaseContext;
1281
- get sampleRate(): number;
1282
- setOnPlaybackComplete(callback: () => void): void;
1283
- }
1284
-
1285
- declare interface TonePlayoutOptions {
1286
- tracks?: ToneTrack[];
1287
- masterGain?: number;
1288
- effects?: EffectsFunction;
1289
- }
1290
-
1291
- declare class ToneTrack {
1292
- private clips;
1293
- private volumeNode;
1294
- private panNode;
1295
- private muteGain;
1296
- private track;
1297
- private effectsCleanup?;
1298
- private onStopCallback?;
1299
- private activePlayers;
1300
- constructor(options: ToneTrackOptions);
1301
- /**
1302
- * Schedule fade envelopes for a clip at the given start time
1303
- */
1304
- private scheduleFades;
1305
- private gainToDb;
1306
- setVolume(gain: number): void;
1307
- setPan(pan: number): void;
1308
- setMute(muted: boolean): void;
1309
- setSolo(soloed: boolean): void;
1310
- play(when?: number, offset?: number, duration?: number): void;
1311
- pause(): void;
1312
- stop(when?: number): void;
1313
- dispose(): void;
1314
- get id(): string;
1315
- get duration(): number;
1316
- get buffer(): AudioBuffer;
1317
- get isPlaying(): boolean;
1318
- get muted(): boolean;
1319
- get startTime(): number;
1320
- setOnStopCallback(callback: () => void): void;
1321
- }
1322
-
1323
- declare interface ToneTrackOptions {
1324
- buffer?: AudioBuffer;
1325
- clips?: ClipInfo[];
1326
- track: Track;
1327
- effects?: TrackEffectsFunction;
1328
- destination?: ToneAudioNode;
1329
- }
1330
-
1331
1248
  export declare interface TrackActiveEffect {
1332
1249
  instanceId: string;
1333
1250
  effectId: string;
@@ -1601,13 +1518,22 @@ declare interface UseAudioTracksOptions {
1601
1518
  * Provides drag handlers and collision modifier for use with @dnd-kit/core DndContext.
1602
1519
  * Handles both clip movement (dragging entire clips) and boundary trimming (adjusting clip edges).
1603
1520
  *
1521
+ * **Move:** `onDragEnd` delegates to `engine.moveClip()` in one shot.
1522
+ *
1523
+ * **Trim:** `onDragMove` updates React state per-frame via `onTracksChange` for smooth
1524
+ * visual feedback (using cumulative deltas from the original clip snapshot). `isDraggingRef`
1525
+ * prevents loadAudio from rebuilding the engine during the drag, so the engine keeps the
1526
+ * original clip positions. On drag end, `engine.trimClip()` commits the final delta.
1527
+ *
1604
1528
  * @example
1605
1529
  * ```tsx
1606
- * const { onDragStart, onDragMove, onDragEnd, collisionModifier } = useClipDragHandlers({
1530
+ * const { onDragStart, onDragMove, onDragEnd, onDragCancel, collisionModifier } = useClipDragHandlers({
1607
1531
  * tracks,
1608
1532
  * onTracksChange: setTracks,
1609
1533
  * samplesPerPixel,
1610
1534
  * sampleRate,
1535
+ * engineRef: playoutRef,
1536
+ * isDraggingRef,
1611
1537
  * });
1612
1538
  *
1613
1539
  * return (
@@ -1615,6 +1541,7 @@ declare interface UseAudioTracksOptions {
1615
1541
  * onDragStart={onDragStart}
1616
1542
  * onDragMove={onDragMove}
1617
1543
  * onDragEnd={onDragEnd}
1544
+ * onDragCancel={onDragCancel}
1618
1545
  * modifiers={[restrictToHorizontalAxis, collisionModifier]}
1619
1546
  * >
1620
1547
  * <Waveform showClipHeaders={true} />
@@ -1622,10 +1549,11 @@ declare interface UseAudioTracksOptions {
1622
1549
  * );
1623
1550
  * ```
1624
1551
  */
1625
- export declare function useClipDragHandlers({ tracks, onTracksChange, samplesPerPixel, sampleRate, }: UseClipDragHandlersOptions): {
1552
+ export declare function useClipDragHandlers({ tracks, onTracksChange, samplesPerPixel, sampleRate, engineRef, isDraggingRef, }: UseClipDragHandlersOptions): {
1626
1553
  onDragStart: (event: DragStartEvent) => void;
1627
1554
  onDragMove: (event: DragMoveEvent) => void;
1628
1555
  onDragEnd: (event: DragEndEvent) => void;
1556
+ onDragCancel: (_event: DragCancelEvent) => void;
1629
1557
  collisionModifier: (args: Parameters<Modifier>[0]) => {
1630
1558
  scaleX: number;
1631
1559
  scaleY: number;
@@ -1639,11 +1567,20 @@ declare interface UseClipDragHandlersOptions {
1639
1567
  onTracksChange: (tracks: ClipTrack[]) => void;
1640
1568
  samplesPerPixel: number;
1641
1569
  sampleRate: number;
1570
+ engineRef: default_2.RefObject<PlaylistEngine | null>;
1571
+ /** Ref toggled during boundary trim drags. When true, the provider's loadAudio
1572
+ * skips engine rebuilds so engine keeps original clip positions. On drag end,
1573
+ * engine.trimClip() commits the final delta. Obtain from usePlaylistData(). */
1574
+ isDraggingRef: default_2.MutableRefObject<boolean>;
1642
1575
  }
1643
1576
 
1644
1577
  /**
1645
1578
  * Hook for splitting clips at the playhead or at a specific time
1646
1579
  *
1580
+ * Splitting delegates to `engine.splitClip()` — the engine handles clip creation,
1581
+ * adapter sync, and emits statechange. The provider's statechange handler propagates
1582
+ * the updated tracks to the parent via `onTracksChange`.
1583
+ *
1647
1584
  * @param options - Configuration options
1648
1585
  * @returns Object with split functions
1649
1586
  *
@@ -1651,8 +1588,9 @@ declare interface UseClipDragHandlersOptions {
1651
1588
  * ```tsx
1652
1589
  * const { splitClipAtPlayhead } = useClipSplitting({
1653
1590
  * tracks,
1654
- * onTracksChange: setTracks,
1655
- * currentTime,
1591
+ * sampleRate,
1592
+ * samplesPerPixel,
1593
+ * engineRef: playoutRef,
1656
1594
  * });
1657
1595
  *
1658
1596
  * // In keyboard handler
@@ -1667,9 +1605,9 @@ export declare const useClipSplitting: (options: UseClipSplittingOptions) => Use
1667
1605
 
1668
1606
  declare interface UseClipSplittingOptions {
1669
1607
  tracks: ClipTrack[];
1670
- onTracksChange: (tracks: ClipTrack[]) => void;
1671
1608
  sampleRate: number;
1672
1609
  samplesPerPixel: number;
1610
+ engineRef: default_2.RefObject<PlaylistEngine | null>;
1673
1611
  }
1674
1612
 
1675
1613
  declare interface UseClipSplittingResult {
@@ -1808,27 +1746,19 @@ export declare const useMasterAnalyser: (fftSize?: number) => {
1808
1746
  };
1809
1747
 
1810
1748
  /**
1811
- * Hook for managing master volume control
1749
+ * Hook for managing master volume via PlaylistEngine delegation.
1812
1750
  *
1813
- * @example
1814
- * ```tsx
1815
- * const { masterVolume, setMasterVolume } = useMasterVolume({
1816
- * playoutRef,
1817
- * initialVolume: 1.0,
1818
- * });
1819
- *
1820
- * <MasterVolumeControl
1821
- * volume={masterVolume}
1822
- * onChange={setMasterVolume}
1823
- * />
1824
- * ```
1751
+ * setMasterVolume delegates to the engine. State is mirrored back from
1752
+ * the engine via onEngineState(), which the provider's statechange
1753
+ * handler calls on every engine event.
1825
1754
  */
1826
- export declare function useMasterVolume({ playoutRef, initialVolume, onVolumeChange, }: UseMasterVolumeProps): MasterVolumeControls;
1755
+ export declare function useMasterVolume({ engineRef, initialVolume, }: UseMasterVolumeProps): MasterVolumeControls & {
1756
+ onEngineState: (state: EngineState) => void;
1757
+ };
1827
1758
 
1828
1759
  declare interface UseMasterVolumeProps {
1829
- playoutRef: RefObject<TonePlayout | null>;
1760
+ engineRef: RefObject<PlaylistEngine | null>;
1830
1761
  initialVolume?: number;
1831
- onVolumeChange?: (volume: number) => void;
1832
1762
  }
1833
1763
 
1834
1764
  export declare const useMediaElementAnimation: () => MediaElementAnimationContextValue;
@@ -1950,11 +1880,24 @@ export declare interface UseTrackDynamicEffectsReturn {
1950
1880
  availableEffects: EffectDefinition[];
1951
1881
  }
1952
1882
 
1953
- export declare function useZoomControls({ initialSamplesPerPixel, zoomLevels, }: UseZoomControlsProps): ZoomControls;
1883
+ /**
1884
+ * Hook for managing zoom controls via PlaylistEngine delegation.
1885
+ *
1886
+ * zoomIn/zoomOut delegate to the engine. State is mirrored back from
1887
+ * the engine via onEngineState(), which the provider's statechange
1888
+ * handler calls on every engine event.
1889
+ *
1890
+ * samplesPerPixel updates use startTransition so React treats them as
1891
+ * non-urgent — during playback, animation RAF callbacks interleave
1892
+ * with the zoom re-render instead of being blocked.
1893
+ */
1894
+ export declare function useZoomControls({ engineRef, initialSamplesPerPixel, }: UseZoomControlsProps): ZoomControls & {
1895
+ onEngineState: (state: EngineState) => void;
1896
+ };
1954
1897
 
1955
1898
  declare interface UseZoomControlsProps {
1899
+ engineRef: RefObject<PlaylistEngine | null>;
1956
1900
  initialSamplesPerPixel: number;
1957
- zoomLevels?: number[];
1958
1901
  }
1959
1902
 
1960
1903
  /**
@@ -2087,6 +2030,15 @@ declare interface WaveformPlaylistProviderProps {
2087
2030
  barGap?: number;
2088
2031
  /** Width in pixels of progress bars. Default: barWidth + barGap (fills gaps). */
2089
2032
  progressBarWidth?: number;
2033
+ /** Callback when engine clip operations (move, trim, split) change tracks.
2034
+ * The provider calls this so the parent can update its tracks state without
2035
+ * triggering a full engine rebuild.
2036
+ *
2037
+ * **Important:** The parent must pass the received `tracks` reference back as
2038
+ * the `tracks` prop (i.e. `setState(tracks)`). The provider uses reference
2039
+ * identity (`tracks === engineTracksRef.current`) to detect engine-originated
2040
+ * updates and skip the expensive `loadAudio` rebuild. */
2041
+ onTracksChange?: (tracks: ClipTrack[]) => void;
2090
2042
  children: ReactNode;
2091
2043
  }
2092
2044