@waveform-playlist/browser 9.1.1 → 9.2.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
@@ -1112,7 +1112,7 @@ export declare const PlaylistVisualization: default_2.FC<PlaylistVisualizationPr
1112
1112
 
1113
1113
  export declare interface PlaylistVisualizationProps {
1114
1114
  renderTrackControls?: (trackIndex: number) => ReactNode;
1115
- renderTimestamp?: (timeMs: number, pixelPosition: number) => ReactNode;
1115
+ renderTick?: (label: string, pixelPosition: number) => ReactNode;
1116
1116
  /** Custom playhead render function. Receives position (pixels) and color from theme. */
1117
1117
  renderPlayhead?: RenderPlayheadFunction;
1118
1118
  annotationControls?: AnnotationAction[];
@@ -1189,6 +1189,43 @@ export declare const SkipForwardButton: default_2.FC<{
1189
1189
  className?: string;
1190
1190
  }>;
1191
1191
 
1192
+ declare type SnapTo = 'bar' | 'beat' | 'off';
1193
+
1194
+ declare interface SnapToGridBeatsOptions {
1195
+ mode: 'beats';
1196
+ snapTo: SnapTo;
1197
+ bpm: number;
1198
+ timeSignature: [number, number];
1199
+ samplesPerPixel: number;
1200
+ sampleRate: number;
1201
+ }
1202
+
1203
+ /**
1204
+ * dnd-kit modifier that quantizes clip drag movement to a grid.
1205
+ *
1206
+ * Two modes:
1207
+ * - "beats": Snaps to beat/bar grid using PPQN tick space for exact musical timing.
1208
+ * - "temporal": Snaps to a sample-based grid derived from timescale markers.
1209
+ *
1210
+ * Designed to compose with ClipCollisionModifier — snap first,
1211
+ * then collision constrains the snapped position.
1212
+ */
1213
+ export declare class SnapToGridModifier extends Modifier<DragDropManager<any, any>, SnapToGridOptions> {
1214
+ apply(operation: DragOperation): {
1215
+ x: number;
1216
+ y: number;
1217
+ };
1218
+ static configure: (options: SnapToGridBeatsOptions | SnapToGridTemporalOptions) => PluginDescriptor<any, any, typeof SnapToGridModifier>;
1219
+ }
1220
+
1221
+ declare type SnapToGridOptions = SnapToGridBeatsOptions | SnapToGridTemporalOptions;
1222
+
1223
+ declare interface SnapToGridTemporalOptions {
1224
+ mode: 'temporal';
1225
+ gridSamples: number;
1226
+ samplesPerPixel: number;
1227
+ }
1228
+
1192
1229
  /**
1193
1230
  * Caches parsed SoundFont2 data and AudioBuffers for efficient playback.
1194
1231
  *
@@ -1227,33 +1264,9 @@ declare class SoundFontCache {
1227
1264
  * @returns SoundFontSample or null if no sample found for this note
1228
1265
  */
1229
1266
  getAudioBuffer(midiNote: number, bankNumber?: number, presetNumber?: number): SoundFontSample | null;
1230
- /**
1231
- * Extract loop points and volume envelope data from per-zone generators.
1232
- *
1233
- * Loop points are stored as absolute indices into the SF2 sample pool.
1234
- * We convert to AudioBuffer-relative seconds by subtracting header.start
1235
- * and dividing by sampleRate.
1236
- *
1237
- * Volume envelope times are in SF2 timecents; sustain is centibels attenuation.
1238
- */
1239
- private extractLoopAndEnvelope;
1240
- /**
1241
- * Calculate playback rate for a MIDI note using the SF2 generator chain.
1242
- *
1243
- * SF2 root key resolution priority:
1244
- * 1. OverridingRootKey generator (per-zone, most specific)
1245
- * 2. sample.header.originalPitch (sample header)
1246
- * 3. MIDI note 60 (middle C fallback)
1247
- *
1248
- * Tuning adjustments:
1249
- * - CoarseTune generator (semitones, additive)
1250
- * - FineTune generator (cents, additive)
1251
- * - sample.header.pitchCorrection (cents, additive)
1252
- */
1253
- private calculatePlaybackRate;
1254
1267
  /**
1255
1268
  * Convert Int16Array sample data to an AudioBuffer.
1256
- * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
1269
+ * Uses the extracted int16ToFloat32 for the conversion, then copies into an AudioBuffer.
1257
1270
  */
1258
1271
  private int16ToAudioBuffer;
1259
1272
  /**
@@ -1632,10 +1645,19 @@ declare interface UseAnnotationKeyboardControlsOptions {
1632
1645
  * { src: 'audio/drums.mp3', name: 'Drums' },
1633
1646
  * ]);
1634
1647
  *
1635
- * // Progressive loading (tracks appear as they load)
1636
- * const { tracks, loading, loadedCount, totalCount } = useAudioTracks(
1637
- * [{ src: 'audio/vocals.mp3' }, { src: 'audio/drums.mp3' }],
1638
- * { progressive: true }
1648
+ * // Immediate rendering with deferred engine build (recommended for multi-track)
1649
+ * const { tracks, loading } = useAudioTracks(
1650
+ * [
1651
+ * { src: 'audio/vocals.mp3', name: 'Vocals', duration: 30 },
1652
+ * { src: 'audio/drums.mp3', name: 'Drums', duration: 30 },
1653
+ * ],
1654
+ * { immediate: true }
1655
+ * );
1656
+ * // All tracks render instantly as placeholders, peaks fill in as files load
1657
+ * return (
1658
+ * <WaveformPlaylistProvider tracks={tracks} deferEngineRebuild={loading}>
1659
+ * ...
1660
+ * </WaveformPlaylistProvider>
1639
1661
  * );
1640
1662
  *
1641
1663
  * // Pre-loaded AudioBuffer (skip fetch/decode)
@@ -1647,11 +1669,6 @@ declare interface UseAnnotationKeyboardControlsOptions {
1647
1669
  * const { tracks } = useAudioTracks([
1648
1670
  * { waveformData: preloadedPeaks, name: 'Peaks Only' }, // Renders immediately
1649
1671
  * ]);
1650
- *
1651
- * if (loading) return <div>Loading {loadedCount}/{totalCount}...</div>;
1652
- * if (error) return <div>Error: {error}</div>;
1653
- *
1654
- * return <WaveformPlaylistProvider tracks={tracks}>...</WaveformPlaylistProvider>;
1655
1672
  * ```
1656
1673
  */
1657
1674
  export declare function useAudioTracks(configs: AudioTrackConfig[], options?: UseAudioTracksOptions): {
@@ -1667,10 +1684,16 @@ export declare function useAudioTracks(configs: AudioTrackConfig[], options?: Us
1667
1684
  */
1668
1685
  declare interface UseAudioTracksOptions {
1669
1686
  /**
1670
- * When true, tracks are added to the playlist progressively as they load,
1671
- * rather than waiting for all tracks to finish loading.
1672
- * Default: false (wait for all tracks)
1687
+ * When true, all tracks render immediately as placeholders with clip geometry
1688
+ * from the config. Audio fills in progressively as files decode, and peaks
1689
+ * render as each buffer becomes available. Use with `deferEngineRebuild={loading}`
1690
+ * on the provider for a single engine build when all tracks are ready.
1691
+ *
1692
+ * Requires `duration` or `waveformData` in each config so clip dimensions are known upfront.
1693
+ * Default: false
1673
1694
  */
1695
+ immediate?: boolean;
1696
+ /** @deprecated Use `immediate` instead. */
1674
1697
  progressive?: boolean;
1675
1698
  }
1676
1699
 
@@ -1712,7 +1735,7 @@ declare interface UseAudioTracksOptions {
1712
1735
  * );
1713
1736
  * ```
1714
1737
  */
1715
- export declare function useClipDragHandlers({ tracks, onTracksChange, samplesPerPixel, sampleRate, engineRef, isDraggingRef, }: UseClipDragHandlersOptions): {
1738
+ export declare function useClipDragHandlers({ tracks, onTracksChange, samplesPerPixel, sampleRate, engineRef, isDraggingRef, snapSamplePosition, }: UseClipDragHandlersOptions): {
1716
1739
  onDragStart: (event: Parameters<DragStartEvent>[0]) => void;
1717
1740
  onDragMove: (event: Parameters<DragMoveEvent>[0]) => void;
1718
1741
  onDragEnd: (event: Parameters<DragEndEvent>[0]) => void;
@@ -1728,6 +1751,9 @@ declare interface UseClipDragHandlersOptions {
1728
1751
  * skips engine rebuilds so engine keeps original clip positions. On drag end,
1729
1752
  * engine.trimClip() commits the final delta. Obtain from usePlaylistData(). */
1730
1753
  isDraggingRef: default_2.MutableRefObject<boolean>;
1754
+ /** Optional function that snaps a sample position to the nearest grid position.
1755
+ * Used for boundary trim snapping (move snapping is handled by the SnapToGridModifier). */
1756
+ snapSamplePosition?: (samplePosition: number) => number;
1731
1757
  }
1732
1758
 
1733
1759
  /**
@@ -2190,6 +2216,10 @@ declare interface WaveformPlaylistProviderProps {
2190
2216
  /** SoundFont cache for sample-based MIDI playback. When provided, MIDI clips
2191
2217
  * use SoundFont samples instead of PolySynth synthesis. */
2192
2218
  soundFontCache?: SoundFontCache;
2219
+ /** When true, tracks render visually but the engine build is deferred.
2220
+ * Use this during progressive loading to avoid rebuilding the engine for
2221
+ * each track — flip to false when all tracks are ready for a single build. */
2222
+ deferEngineRebuild?: boolean;
2193
2223
  children: ReactNode;
2194
2224
  }
2195
2225
 
@@ -2250,6 +2280,10 @@ declare interface WaveformPlaylistTheme {
2250
2280
 
2251
2281
  export declare interface WaveformProps {
2252
2282
  renderTrackControls?: (trackIndex: number) => ReactNode;
2283
+ /** Custom render function for timescale tick labels. `label` is a formatted string
2284
+ * (bar/beat notation like "2.3" in beats mode, or "m:ss" in temporal mode). */
2285
+ renderTick?: (label: string, pixelPosition: number) => ReactNode;
2286
+ /** @deprecated Use `renderTick` instead. */
2253
2287
  renderTimestamp?: (timeMs: number, pixelPosition: number) => ReactNode;
2254
2288
  /** Custom playhead render function. Receives position (pixels) and color from theme. */
2255
2289
  renderPlayhead?: RenderPlayheadFunction;