@waveform-playlist/browser 9.0.4 → 9.1.1

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
@@ -203,6 +203,16 @@ export declare interface AudioClip {
203
203
  * Load with: `const waveformData = await loadWaveformData('/path/to/peaks.dat')`
204
204
  */
205
205
  waveformData?: WaveformDataObject;
206
+ /**
207
+ * MIDI note data — when present, this clip plays MIDI instead of audio.
208
+ * The playout adapter uses this field to detect MIDI clips and route them
209
+ * to MidiToneTrack (PolySynth) instead of ToneTrack (AudioBufferSourceNode).
210
+ */
211
+ midiNotes?: MidiNoteData[];
212
+ /** MIDI channel (0-indexed). Channel 9 = GM percussion. */
213
+ midiChannel?: number;
214
+ /** MIDI program number (0-127). GM instrument number for SoundFont playback. */
215
+ midiProgram?: number;
206
216
  }
207
217
 
208
218
  /**
@@ -294,6 +304,9 @@ declare interface ClipPeaks {
294
304
  durationSamples: number;
295
305
  fadeIn?: Fade;
296
306
  fadeOut?: Fade;
307
+ midiNotes?: MidiNoteData[];
308
+ sampleRate?: number;
309
+ offsetSamples?: number;
297
310
  }
298
311
 
299
312
  /**
@@ -861,6 +874,26 @@ export declare interface MediaElementWaveformProps {
861
874
  className?: string;
862
875
  }
863
876
 
877
+ /**
878
+ * MIDI note data for clips that play MIDI instead of audio.
879
+ * When present on an AudioClip, the clip is treated as a MIDI clip
880
+ * by the playout adapter.
881
+ */
882
+ declare interface MidiNoteData {
883
+ /** MIDI note number (0-127) */
884
+ midi: number;
885
+ /** Note name in scientific pitch notation ("C4", "G#3") */
886
+ name: string;
887
+ /** Start time in seconds, relative to clip start */
888
+ time: number;
889
+ /** Duration in seconds */
890
+ duration: number;
891
+ /** Velocity (0-1 normalized) */
892
+ velocity: number;
893
+ /** MIDI channel (0-indexed). Channel 9 = GM percussion. Enables per-note routing in flattened tracks. */
894
+ channel?: number;
895
+ }
896
+
864
897
  /**
865
898
  * DragDropProvider plugins customizer that disables the Feedback plugin's drop animation.
866
899
  *
@@ -941,8 +974,8 @@ declare interface PlayheadProps {
941
974
  samplesPerPixel: number;
942
975
  /** Sample rate - for converting time to pixels */
943
976
  sampleRate: number;
944
- /** Controls offset in pixels */
945
- controlsOffset: number;
977
+ /** Controls offset in pixels (deprecated, always 0 — controls are now outside scroll area) */
978
+ controlsOffset?: number;
946
979
  /** Function to get current audio context time - required for smooth animation */
947
980
  getAudioContextTime?: () => number;
948
981
  /** Returns current playback time (auto-wraps at loop boundaries). Preferred over manual elapsed calculation. */
@@ -1122,7 +1155,7 @@ declare interface RenderAnnotationItemProps {
1122
1155
  }
1123
1156
 
1124
1157
  /** Render mode for a track's visualization */
1125
- declare type RenderMode = 'waveform' | 'spectrogram' | 'both';
1158
+ declare type RenderMode = 'waveform' | 'spectrogram' | 'both' | 'piano-roll';
1126
1159
 
1127
1160
  /**
1128
1161
  * Type for custom playhead render functions.
@@ -1156,6 +1189,106 @@ export declare const SkipForwardButton: default_2.FC<{
1156
1189
  className?: string;
1157
1190
  }>;
1158
1191
 
1192
+ /**
1193
+ * Caches parsed SoundFont2 data and AudioBuffers for efficient playback.
1194
+ *
1195
+ * AudioBuffers are created lazily on first access and cached by sample index.
1196
+ * Pitch calculation uses the SF2 generator chain:
1197
+ * OverridingRootKey → sample.header.originalPitch → fallback 60
1198
+ *
1199
+ * Audio graph per note:
1200
+ * AudioBufferSourceNode (playbackRate for pitch) → GainNode (velocity) → track chain
1201
+ */
1202
+ declare class SoundFontCache {
1203
+ private sf2;
1204
+ private audioBufferCache;
1205
+ private context;
1206
+ /**
1207
+ * @param context Optional AudioContext for createBuffer(). If omitted, uses
1208
+ * an OfflineAudioContext which doesn't require user gesture — safe to
1209
+ * construct before user interaction (avoids Firefox autoplay warnings).
1210
+ */
1211
+ constructor(context?: BaseAudioContext);
1212
+ /**
1213
+ * Load and parse an SF2 file from a URL.
1214
+ */
1215
+ load(url: string, signal?: AbortSignal): Promise<void>;
1216
+ /**
1217
+ * Load from an already-fetched ArrayBuffer.
1218
+ */
1219
+ loadFromBuffer(data: ArrayBuffer): void;
1220
+ get isLoaded(): boolean;
1221
+ /**
1222
+ * Look up a MIDI note and return the AudioBuffer + playbackRate.
1223
+ *
1224
+ * @param midiNote - MIDI note number (0-127)
1225
+ * @param bankNumber - Bank number (0 for melodic, 128 for percussion/drums)
1226
+ * @param presetNumber - GM program number (0-127)
1227
+ * @returns SoundFontSample or null if no sample found for this note
1228
+ */
1229
+ 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
+ /**
1255
+ * Convert Int16Array sample data to an AudioBuffer.
1256
+ * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
1257
+ */
1258
+ private int16ToAudioBuffer;
1259
+ /**
1260
+ * Clear all cached AudioBuffers and release the parsed SF2.
1261
+ */
1262
+ dispose(): void;
1263
+ }
1264
+
1265
+ /**
1266
+ * Result of looking up a MIDI note in the SoundFont.
1267
+ * Contains the AudioBuffer, playbackRate, loop points, and volume envelope.
1268
+ */
1269
+ declare interface SoundFontSample {
1270
+ /** Cached AudioBuffer for this sample */
1271
+ buffer: AudioBuffer;
1272
+ /** Playback rate to pitch-shift from originalPitch to target note */
1273
+ playbackRate: number;
1274
+ /** Loop mode: 0=no loop, 1=continuous, 3=sustain loop */
1275
+ loopMode: number;
1276
+ /** Loop start in seconds, relative to AudioBuffer start */
1277
+ loopStart: number;
1278
+ /** Loop end in seconds, relative to AudioBuffer start */
1279
+ loopEnd: number;
1280
+ /** Volume envelope attack time in seconds */
1281
+ attackVolEnv: number;
1282
+ /** Volume envelope hold time in seconds */
1283
+ holdVolEnv: number;
1284
+ /** Volume envelope decay time in seconds */
1285
+ decayVolEnv: number;
1286
+ /** Volume envelope sustain level as linear gain 0-1 */
1287
+ sustainVolEnv: number;
1288
+ /** Volume envelope release time in seconds */
1289
+ releaseVolEnv: number;
1290
+ }
1291
+
1159
1292
  /**
1160
1293
  * Configuration for spectrogram computation and rendering.
1161
1294
  */
@@ -1448,7 +1581,7 @@ export declare function useAnnotationIntegration(): AnnotationIntegration;
1448
1581
  * });
1449
1582
  * ```
1450
1583
  */
1451
- export declare function useAnnotationKeyboardControls({ annotations, activeAnnotationId, onAnnotationsChange, onActiveAnnotationChange, duration, linkEndpoints, continuousPlay, enabled, scrollContainerRef, samplesPerPixel, sampleRate, controlsWidth, onPlay, }: UseAnnotationKeyboardControlsOptions): {
1584
+ export declare function useAnnotationKeyboardControls({ annotations, activeAnnotationId, onAnnotationsChange, onActiveAnnotationChange, duration, linkEndpoints, continuousPlay, enabled, scrollContainerRef, samplesPerPixel, sampleRate, onPlay, }: UseAnnotationKeyboardControlsOptions): {
1452
1585
  moveStartBoundary: (delta: number) => void;
1453
1586
  moveEndBoundary: (delta: number) => void;
1454
1587
  selectPrevious: () => void;
@@ -1477,8 +1610,6 @@ declare interface UseAnnotationKeyboardControlsOptions {
1477
1610
  samplesPerPixel?: number;
1478
1611
  /** Optional: sample rate for scroll position calculation */
1479
1612
  sampleRate?: number;
1480
- /** Optional: controls width offset for scroll position calculation */
1481
- controlsWidth?: number;
1482
1613
  /** Optional: callback to start playback at a time with optional duration */
1483
1614
  onPlay?: (startTime: number, duration?: number) => void;
1484
1615
  }
@@ -2056,6 +2187,9 @@ declare interface WaveformPlaylistProviderProps {
2056
2187
  * identity (`tracks === engineTracksRef.current`) to detect engine-originated
2057
2188
  * updates and skip the expensive `loadAudio` rebuild. */
2058
2189
  onTracksChange?: (tracks: ClipTrack[]) => void;
2190
+ /** SoundFont cache for sample-based MIDI playback. When provided, MIDI clips
2191
+ * use SoundFont samples instead of PolySynth synthesis. */
2192
+ soundFontCache?: SoundFontCache;
2059
2193
  children: ReactNode;
2060
2194
  }
2061
2195
 
@@ -2104,6 +2238,10 @@ declare interface WaveformPlaylistTheme {
2104
2238
  annotationResizeHandleColor: string;
2105
2239
  annotationResizeHandleActiveColor: string;
2106
2240
  annotationTextItemHoverBackground: string;
2241
+ playlistBackgroundColor?: string;
2242
+ pianoRollNoteColor: string;
2243
+ pianoRollSelectedNoteColor: string;
2244
+ pianoRollBackgroundColor: string;
2107
2245
  borderRadius: string;
2108
2246
  fontFamily: string;
2109
2247
  fontSize: string;