@waveform-playlist/browser 13.0.0 → 13.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.mts CHANGED
@@ -6,11 +6,11 @@ export { EffectsFunction, TrackEffectsFunction } from '@waveform-playlist/playou
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode, RefObject } from 'react';
8
8
  import { PlaylistEngine, EngineState } from '@waveform-playlist/engine';
9
- import { ClipTrack, AnnotationData, AnnotationAction, PeakData, Fade, MidiNoteData, WaveformDataObject, TrackEffectsFunction as TrackEffectsFunction$2, RenderMode, SpectrogramConfig, ColorMapValue, KeyboardShortcut, AnnotationActionOptions, RenderAnnotationItemProps, TrackSpectrogramOverrides } from '@waveform-playlist/core';
9
+ import { PeakData, Fade, MidiNoteData, ClipTrack, AnnotationData, AnnotationAction, WaveformDataObject, TrackEffectsFunction as TrackEffectsFunction$2, RenderMode, SpectrogramConfig, ColorMapValue, KeyboardShortcut, AnnotationActionOptions, RenderAnnotationItemProps, TrackSpectrogramOverrides } from '@waveform-playlist/core';
10
10
  export { AnnotationData, AudioClip, ClipTrack, Fade, getShortcutLabel } from '@waveform-playlist/core';
11
11
  import { WaveformPlaylistTheme, TimeFormat, RenderPlayheadFunction, TrackMenuItem, SnapTo } from '@waveform-playlist/ui-components';
12
12
  export { TimeFormat } from '@waveform-playlist/ui-components';
13
- import { FadeConfig, MediaElementPlayout } from '@waveform-playlist/media-element-playout';
13
+ import { MediaElementPlayout, FadeConfig } from '@waveform-playlist/media-element-playout';
14
14
  import * as _dnd_kit_abstract from '@dnd-kit/abstract';
15
15
  import { DragStartEvent, DragMoveEvent, DragEndEvent, PluginDescriptor, Modifier, DragDropManager, DragOperation, Plugins } from '@dnd-kit/abstract';
16
16
  import { PointerSensor } from '@dnd-kit/dom';
@@ -45,7 +45,8 @@ interface TrackState$1 {
45
45
  interface FrameData {
46
46
  /** Raw engine time (for state/logic — NOT for visual positioning). */
47
47
  readonly time: number;
48
- /** time - outputLatency (for DOM positioning — matches speaker output). */
48
+ /** Visually-aligned time for DOM positioning: engine.getAudibleTime() while
49
+ * playing (matches speaker output), raw time when resting. */
49
50
  readonly visualTime: number;
50
51
  readonly sampleRate: number;
51
52
  readonly samplesPerPixel: number;
@@ -55,8 +56,8 @@ interface PlaybackAnimationContextValue {
55
56
  currentTime: number;
56
57
  currentTimeRef: React__default.RefObject<number>;
57
58
  /**
58
- * Visually-aligned playback time (raw engine time minus `outputLatency` and
59
- * `engine.lookAhead`). Kept current by the animation loop during playback
59
+ * Visually-aligned playback time (engine.getAudibleTime() while playing;
60
+ * raw resting time otherwise). Kept current by the animation loop during playback
60
61
  * and by pause/seek/stop paths when stopped. Read from this for any visual
61
62
  * positioning that should match the audible output.
62
63
  */
package/dist/index.d.ts CHANGED
@@ -6,11 +6,11 @@ export { EffectsFunction, TrackEffectsFunction } from '@waveform-playlist/playou
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode, RefObject } from 'react';
8
8
  import { PlaylistEngine, EngineState } from '@waveform-playlist/engine';
9
- import { ClipTrack, AnnotationData, AnnotationAction, PeakData, Fade, MidiNoteData, WaveformDataObject, TrackEffectsFunction as TrackEffectsFunction$2, RenderMode, SpectrogramConfig, ColorMapValue, KeyboardShortcut, AnnotationActionOptions, RenderAnnotationItemProps, TrackSpectrogramOverrides } from '@waveform-playlist/core';
9
+ import { PeakData, Fade, MidiNoteData, ClipTrack, AnnotationData, AnnotationAction, WaveformDataObject, TrackEffectsFunction as TrackEffectsFunction$2, RenderMode, SpectrogramConfig, ColorMapValue, KeyboardShortcut, AnnotationActionOptions, RenderAnnotationItemProps, TrackSpectrogramOverrides } from '@waveform-playlist/core';
10
10
  export { AnnotationData, AudioClip, ClipTrack, Fade, getShortcutLabel } from '@waveform-playlist/core';
11
11
  import { WaveformPlaylistTheme, TimeFormat, RenderPlayheadFunction, TrackMenuItem, SnapTo } from '@waveform-playlist/ui-components';
12
12
  export { TimeFormat } from '@waveform-playlist/ui-components';
13
- import { FadeConfig, MediaElementPlayout } from '@waveform-playlist/media-element-playout';
13
+ import { MediaElementPlayout, FadeConfig } from '@waveform-playlist/media-element-playout';
14
14
  import * as _dnd_kit_abstract from '@dnd-kit/abstract';
15
15
  import { DragStartEvent, DragMoveEvent, DragEndEvent, PluginDescriptor, Modifier, DragDropManager, DragOperation, Plugins } from '@dnd-kit/abstract';
16
16
  import { PointerSensor } from '@dnd-kit/dom';
@@ -45,7 +45,8 @@ interface TrackState$1 {
45
45
  interface FrameData {
46
46
  /** Raw engine time (for state/logic — NOT for visual positioning). */
47
47
  readonly time: number;
48
- /** time - outputLatency (for DOM positioning — matches speaker output). */
48
+ /** Visually-aligned time for DOM positioning: engine.getAudibleTime() while
49
+ * playing (matches speaker output), raw time when resting. */
49
50
  readonly visualTime: number;
50
51
  readonly sampleRate: number;
51
52
  readonly samplesPerPixel: number;
@@ -55,8 +56,8 @@ interface PlaybackAnimationContextValue {
55
56
  currentTime: number;
56
57
  currentTimeRef: React__default.RefObject<number>;
57
58
  /**
58
- * Visually-aligned playback time (raw engine time minus `outputLatency` and
59
- * `engine.lookAhead`). Kept current by the animation loop during playback
59
+ * Visually-aligned playback time (engine.getAudibleTime() while playing;
60
+ * raw resting time otherwise). Kept current by the animation loop during playback
60
61
  * and by pause/seek/stop paths when stopped. Read from this for any visual
61
62
  * positioning that should match the audible output.
62
63
  */
package/dist/index.js CHANGED
@@ -163,7 +163,7 @@ var Tone2 = __toESM(require("tone"));
163
163
  // src/WaveformPlaylistContext.tsx
164
164
  var import_react24 = require("react");
165
165
  var import_styled_components = require("styled-components");
166
- var import_playout5 = require("@waveform-playlist/playout");
166
+ var import_playout6 = require("@waveform-playlist/playout");
167
167
  var import_engine3 = require("@waveform-playlist/engine");
168
168
  var import_ui_components2 = require("@waveform-playlist/ui-components");
169
169
  var import_tone4 = require("tone");
@@ -331,6 +331,13 @@ function extractPeaksFromWaveformDataFull(waveformData, samplesPerPixel, isMono,
331
331
  };
332
332
  }
333
333
 
334
+ // src/soundFontSync.ts
335
+ var import_playout = require("@waveform-playlist/playout");
336
+ function syncSoundFontCacheToAdapter(adapter, cache) {
337
+ if (!(0, import_playout.isToneAdapter)(adapter)) return;
338
+ adapter.setSoundFontCache(cache);
339
+ }
340
+
334
341
  // src/hooks/useTimeFormat.ts
335
342
  var import_react = require("react");
336
343
  var import_ui_components = require("@waveform-playlist/ui-components");
@@ -1035,13 +1042,13 @@ function useClipDragHandlers({
1035
1042
 
1036
1043
  // src/hooks/useAnnotationDragHandlers.ts
1037
1044
  var import_react11 = __toESM(require("react"));
1038
- var import_playout = require("@waveform-playlist/playout");
1045
+ var import_playout2 = require("@waveform-playlist/playout");
1039
1046
  var LINK_THRESHOLD = 0.01;
1040
1047
  function useAnnotationDragHandlers({
1041
1048
  annotations,
1042
1049
  onAnnotationsChange,
1043
1050
  samplesPerPixel,
1044
- sampleRate = (0, import_playout.getGlobalAudioContext)().sampleRate,
1051
+ sampleRate = (0, import_playout2.getGlobalAudioContext)().sampleRate,
1045
1052
  duration,
1046
1053
  linkEndpoints
1047
1054
  }) {
@@ -2742,7 +2749,7 @@ function useTrackDynamicEffects() {
2742
2749
  // src/hooks/useExportWav.ts
2743
2750
  var import_react19 = require("react");
2744
2751
  var import_core4 = require("@waveform-playlist/core");
2745
- var import_playout2 = require("@waveform-playlist/playout");
2752
+ var import_playout3 = require("@waveform-playlist/playout");
2746
2753
 
2747
2754
  // src/utils/wavEncoder.ts
2748
2755
  function encodeWav(audioBuffer, options = {}) {
@@ -2841,7 +2848,7 @@ function useExportWav() {
2841
2848
  if (mode === "individual" && (trackIndex === void 0 || trackIndex < 0 || trackIndex >= tracks.length)) {
2842
2849
  throw new Error("Invalid track index for individual export");
2843
2850
  }
2844
- const sampleRate = (0, import_playout2.getGlobalAudioContext)().sampleRate;
2851
+ const sampleRate = (0, import_playout3.getGlobalAudioContext)().sampleRate;
2845
2852
  let totalDurationSamples = 0;
2846
2853
  for (const track of tracks) {
2847
2854
  for (const clip of track.clips) {
@@ -2956,7 +2963,7 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
2956
2963
  player.connect(fadeGain);
2957
2964
  fadeGain.connect(trackVolume);
2958
2965
  if (applyEffects) {
2959
- const audioParam = (0, import_playout2.getUnderlyingAudioParam)(fadeGain.gain);
2966
+ const audioParam = (0, import_playout3.getUnderlyingAudioParam)(fadeGain.gain);
2960
2967
  if (audioParam) {
2961
2968
  applyClipFades(audioParam, clipGain, startTime, clipDuration, fadeIn, fadeOut);
2962
2969
  } else if (fadeIn || fadeOut) {
@@ -3400,7 +3407,7 @@ function useWaveformDataCache(tracks, baseScale) {
3400
3407
  // src/hooks/useDynamicTracks.ts
3401
3408
  var import_react22 = require("react");
3402
3409
  var import_core5 = require("@waveform-playlist/core");
3403
- var import_playout3 = require("@waveform-playlist/playout");
3410
+ var import_playout4 = require("@waveform-playlist/playout");
3404
3411
  function getSourceName(source) {
3405
3412
  var _a, _b, _c, _d, _e;
3406
3413
  if (source instanceof File) {
@@ -3451,7 +3458,7 @@ function useDynamicTracks() {
3451
3458
  }, []);
3452
3459
  const addTracks = (0, import_react22.useCallback)((sources) => {
3453
3460
  if (sources.length === 0) return;
3454
- const audioContext = (0, import_playout3.getGlobalAudioContext)();
3461
+ const audioContext = (0, import_playout4.getGlobalAudioContext)();
3455
3462
  const placeholders = sources.map((source) => ({
3456
3463
  track: (0, import_core5.createTrack)({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
3457
3464
  source
@@ -3522,7 +3529,7 @@ function useDynamicTracks() {
3522
3529
 
3523
3530
  // src/hooks/useOutputMeter.ts
3524
3531
  var import_react23 = require("react");
3525
- var import_playout4 = require("@waveform-playlist/playout");
3532
+ var import_playout5 = require("@waveform-playlist/playout");
3526
3533
  var import_core6 = require("@waveform-playlist/core");
3527
3534
  var import_worklets = require("@waveform-playlist/worklets");
3528
3535
  var PEAK_DECAY = 0.98;
@@ -3550,7 +3557,7 @@ function useOutputMeter(options = {}) {
3550
3557
  (0, import_react23.useEffect)(() => {
3551
3558
  let isMounted = true;
3552
3559
  const setup = () => __async(null, null, function* () {
3553
- const context = (0, import_playout4.getGlobalContext)();
3560
+ const context = (0, import_playout5.getGlobalContext)();
3554
3561
  const rawCtx = context.rawContext;
3555
3562
  yield rawCtx.audioWorklet.addModule(import_worklets.meterProcessorUrl);
3556
3563
  if (!isMounted) return;
@@ -3599,7 +3606,7 @@ function useOutputMeter(options = {}) {
3599
3606
  isMounted = false;
3600
3607
  if (workletNodeRef.current) {
3601
3608
  try {
3602
- const context = (0, import_playout4.getGlobalContext)();
3609
+ const context = (0, import_playout5.getGlobalContext)();
3603
3610
  context.destination.chain();
3604
3611
  } catch (err) {
3605
3612
  console.warn("[waveform-playlist] Failed to restore destination chain:", String(err));
@@ -3687,6 +3694,7 @@ var WaveformPlaylistProvider = ({
3687
3694
  const [annotationsEditable, setAnnotationsEditable] = (0, import_react24.useState)((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3688
3695
  const [isReady, setIsReady] = (0, import_react24.useState)(false);
3689
3696
  const engineRef = (0, import_react24.useRef)(null);
3697
+ const adapterRef = (0, import_react24.useRef)(null);
3690
3698
  const audioInitializedRef = (0, import_react24.useRef)(false);
3691
3699
  const isPlayingRef = (0, import_react24.useRef)(false);
3692
3700
  isPlayingRef.current = isPlaying;
@@ -3715,9 +3723,9 @@ var WaveformPlaylistProvider = ({
3715
3723
  if (typeof AudioContext === "undefined") return sampleRateProp != null ? sampleRateProp : 48e3;
3716
3724
  try {
3717
3725
  if (sampleRateProp !== void 0) {
3718
- return (0, import_playout5.configureGlobalContext)({ sampleRate: sampleRateProp });
3726
+ return (0, import_playout6.configureGlobalContext)({ sampleRate: sampleRateProp });
3719
3727
  }
3720
- return (0, import_playout5.getGlobalAudioContext)().sampleRate;
3728
+ return (0, import_playout6.getGlobalAudioContext)().sampleRate;
3721
3729
  } catch (err) {
3722
3730
  console.warn(
3723
3731
  "[waveform-playlist] Failed to configure AudioContext: " + String(err) + " \u2014 falling back to " + (sampleRateProp != null ? sampleRateProp : 48e3) + " Hz"
@@ -3915,6 +3923,7 @@ var WaveformPlaylistProvider = ({
3915
3923
  if (engineRef.current) {
3916
3924
  engineRef.current.dispose();
3917
3925
  engineRef.current = null;
3926
+ adapterRef.current = null;
3918
3927
  }
3919
3928
  prevTracksRef.current = tracks;
3920
3929
  return;
@@ -3968,7 +3977,8 @@ var WaveformPlaylistProvider = ({
3968
3977
  lastTracksVersionRef.current = 0;
3969
3978
  engineTracksRef.current = null;
3970
3979
  audioInitializedRef.current = false;
3971
- const adapter = (0, import_playout5.createToneAdapter)({ effects, soundFontCache: soundFontCacheRef.current });
3980
+ const adapter = (0, import_playout6.createToneAdapter)({ effects, soundFontCache: soundFontCacheRef.current });
3981
+ adapterRef.current = adapter;
3972
3982
  const engine = new import_engine3.PlaylistEngine({
3973
3983
  adapter,
3974
3984
  samplesPerPixel: samplesPerPixelRef.current,
@@ -4037,6 +4047,7 @@ var WaveformPlaylistProvider = ({
4037
4047
  stopAnimationFrameLoop();
4038
4048
  if (engineRef.current) {
4039
4049
  engineRef.current.dispose();
4050
+ adapterRef.current = null;
4040
4051
  }
4041
4052
  };
4042
4053
  }, [
@@ -4047,6 +4058,9 @@ var WaveformPlaylistProvider = ({
4047
4058
  // isPlaying is intentionally excluded — read from isPlayingRef inside the
4048
4059
  // effect body. Including it causes a full engine+playout rebuild on every
4049
4060
  // play/pause/stop, destroying and recreating all audio Players.
4061
+ // soundFontCache is deliberately excluded — late cache changes are forwarded
4062
+ // to the live adapter by the sync effect below; only adapter creation reads it
4063
+ // (via soundFontCacheRef).
4050
4064
  onReady,
4051
4065
  effects,
4052
4066
  stopAnimationFrameLoop,
@@ -4064,9 +4078,11 @@ var WaveformPlaylistProvider = ({
4064
4078
  loopEndRef,
4065
4079
  isLoopEnabledRef,
4066
4080
  stableZoomLevels,
4067
- soundFontCache,
4068
4081
  deferEngineRebuild
4069
4082
  ]);
4083
+ (0, import_react24.useEffect)(() => {
4084
+ syncSoundFontCacheToAdapter(adapterRef.current, soundFontCache);
4085
+ }, [soundFontCache]);
4070
4086
  (0, import_react24.useEffect)(() => {
4071
4087
  if (tracks.length === 0) return;
4072
4088
  const allTrackPeaks = tracks.map((track) => {
@@ -4164,12 +4180,7 @@ var WaveformPlaylistProvider = ({
4164
4180
  return ((_b2 = audioStartPositionRef.current) != null ? _b2 : 0) + elapsed;
4165
4181
  }, []);
4166
4182
  const toVisualTime = (0, import_react24.useCallback)((rawTime) => {
4167
- var _a2, _b2;
4168
- const audioCtx = (0, import_playout5.getGlobalAudioContext)();
4169
- const latency = "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
4170
- const lookAhead = (_b2 = (_a2 = engineRef.current) == null ? void 0 : _a2.lookAhead) != null ? _b2 : 0;
4171
- const visual = rawTime - latency - lookAhead;
4172
- return Number.isFinite(visual) ? Math.max(0, visual) : 0;
4183
+ return Number.isFinite(rawTime) ? Math.max(0, rawTime) : 0;
4173
4184
  }, []);
4174
4185
  const setCurrentTimeRefs = (0, import_react24.useCallback)(
4175
4186
  (rawTime) => {
@@ -4189,14 +4200,10 @@ var WaveformPlaylistProvider = ({
4189
4200
  frameCallbacksRef.current.delete(id);
4190
4201
  }, []);
4191
4202
  const startAnimationLoop = (0, import_react24.useCallback)(() => {
4192
- const audioCtx = (0, import_playout5.getGlobalAudioContext)();
4193
4203
  const updateTime = () => {
4194
- var _a2, _b2;
4195
4204
  const time = getPlaybackTime();
4196
4205
  currentTimeRef.current = time;
4197
- const latency = "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
4198
- const lookAhead = (_b2 = (_a2 = engineRef.current) == null ? void 0 : _a2.lookAhead) != null ? _b2 : 0;
4199
- const visualRaw = time - latency - lookAhead;
4206
+ const visualRaw = engineRef.current ? engineRef.current.getAudibleTime() : time;
4200
4207
  const visualTime = Number.isFinite(visualRaw) ? Math.max(0, visualRaw) : 0;
4201
4208
  visualTimeRef.current = visualTime;
4202
4209
  const sr = sampleRateRef.current;
@@ -5434,7 +5441,7 @@ function useClipInteractionEnabled() {
5434
5441
  var import_react33 = require("react");
5435
5442
  var import_react_dom = require("react-dom");
5436
5443
  var import_styled_components6 = __toESM(require("styled-components"));
5437
- var import_playout6 = require("@waveform-playlist/playout");
5444
+ var import_playout7 = require("@waveform-playlist/playout");
5438
5445
  var import_ui_components9 = require("@waveform-playlist/ui-components");
5439
5446
  var import_core8 = require("@waveform-playlist/core");
5440
5447
 
@@ -5726,7 +5733,7 @@ var CustomPlayhead = ({ renderPlayhead, color, samplesPerPixel, sampleRate }) =>
5726
5733
  samplesPerPixel,
5727
5734
  sampleRate,
5728
5735
  controlsOffset: 0,
5729
- getAudioContextTime: () => (0, import_playout6.getGlobalAudioContext)().currentTime,
5736
+ getAudioContextTime: () => (0, import_playout7.getGlobalAudioContext)().currentTime,
5730
5737
  getPlaybackTime
5731
5738
  });
5732
5739
  };
@@ -6193,7 +6200,7 @@ var PlaylistVisualization = ({
6193
6200
  );
6194
6201
  }),
6195
6202
  (recordingState == null ? void 0 : recordingState.isRecording) && recordingState.trackId === track.id && ((_d2 = recordingState.peaks[0]) == null ? void 0 : _d2.length) > 0 && (() => {
6196
- const audioCtx = (0, import_playout6.getGlobalAudioContext)();
6203
+ const audioCtx = (0, import_playout7.getGlobalAudioContext)();
6197
6204
  const outputLatency = "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
6198
6205
  const lookAhead = getLookAhead();
6199
6206
  const latencyOffsetSamples = (0, import_core8.audibleLatencySamples)(