@waveform-playlist/browser 11.2.0 → 11.3.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.mjs CHANGED
@@ -3759,6 +3759,7 @@ var WaveformPlaylistProvider = ({
3759
3759
  const playbackEndTimeRef = useRef15(null);
3760
3760
  const scrollContainerRef = useRef15(null);
3761
3761
  const isAutomaticScrollRef = useRef15(false);
3762
+ const frameCallbacksRef = useRef15(/* @__PURE__ */ new Map());
3762
3763
  const continuousPlayRef = useRef15((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3763
3764
  const activeAnnotationIdRef = useRef15(null);
3764
3765
  const engineTracksRef = useRef15(null);
@@ -4219,10 +4220,30 @@ var WaveformPlaylistProvider = ({
4219
4220
  const elapsed = getContext2().currentTime - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
4220
4221
  return ((_b2 = audioStartPositionRef.current) != null ? _b2 : 0) + elapsed;
4221
4222
  }, []);
4223
+ const registerFrameCallback = useCallback19((id, cb) => {
4224
+ frameCallbacksRef.current.set(id, cb);
4225
+ }, []);
4226
+ const unregisterFrameCallback = useCallback19((id) => {
4227
+ frameCallbacksRef.current.delete(id);
4228
+ }, []);
4222
4229
  const startAnimationLoop = useCallback19(() => {
4230
+ const audioCtx = getGlobalAudioContext4();
4223
4231
  const updateTime = () => {
4224
4232
  const time = getPlaybackTime();
4225
4233
  currentTimeRef.current = time;
4234
+ const latency = "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
4235
+ const visualTime = Math.max(0, time - latency);
4236
+ const sr = sampleRateRef.current;
4237
+ const spp = samplesPerPixelRef.current;
4238
+ const frameData = {
4239
+ time,
4240
+ visualTime,
4241
+ sampleRate: sr,
4242
+ samplesPerPixel: spp
4243
+ };
4244
+ for (const cb of frameCallbacksRef.current.values()) {
4245
+ cb(frameData);
4246
+ }
4226
4247
  const currentAnnotations = annotationsRef.current;
4227
4248
  if (currentAnnotations.length > 0) {
4228
4249
  const currentAnnotation = currentAnnotations.find(
@@ -4257,10 +4278,9 @@ var WaveformPlaylistProvider = ({
4257
4278
  }
4258
4279
  if (isAutomaticScrollRef.current && scrollContainerRef.current && duration > 0) {
4259
4280
  const container = scrollContainerRef.current;
4260
- const sr = sampleRateRef.current;
4261
- const pixelPosition = time * sr / samplesPerPixelRef.current;
4281
+ const pixelPosition = visualTime * sr / spp;
4262
4282
  const containerWidth = container.clientWidth;
4263
- const targetScrollLeft = Math.max(0, pixelPosition - containerWidth / 2);
4283
+ const targetScrollLeft = Math.round(Math.max(0, pixelPosition - containerWidth / 2));
4264
4284
  container.scrollLeft = targetScrollLeft;
4265
4285
  }
4266
4286
  if (playbackEndTimeRef.current !== null && time >= playbackEndTimeRef.current) {
@@ -4496,7 +4516,9 @@ var WaveformPlaylistProvider = ({
4496
4516
  currentTimeRef,
4497
4517
  playbackStartTimeRef,
4498
4518
  audioStartPositionRef,
4499
- getPlaybackTime
4519
+ getPlaybackTime,
4520
+ registerFrameCallback,
4521
+ unregisterFrameCallback
4500
4522
  }),
4501
4523
  [
4502
4524
  isPlaying,
@@ -4504,7 +4526,9 @@ var WaveformPlaylistProvider = ({
4504
4526
  currentTimeRef,
4505
4527
  playbackStartTimeRef,
4506
4528
  audioStartPositionRef,
4507
- getPlaybackTime
4529
+ getPlaybackTime,
4530
+ registerFrameCallback,
4531
+ unregisterFrameCallback
4508
4532
  ]
4509
4533
  );
4510
4534
  const stateValue = useMemo4(
@@ -5299,32 +5323,19 @@ var PositionDisplay = styled.span`
5299
5323
  var AudioPosition = ({ className }) => {
5300
5324
  var _a;
5301
5325
  const timeRef = useRef17(null);
5302
- const animationFrameRef = useRef17(null);
5303
- const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5326
+ const { isPlaying, currentTimeRef, registerFrameCallback, unregisterFrameCallback } = usePlaybackAnimation();
5304
5327
  const { timeFormat: format } = usePlaylistData();
5305
5328
  useEffect12(() => {
5306
- const updateTime = () => {
5307
- var _a2;
5308
- if (timeRef.current) {
5309
- const time = isPlaying ? getPlaybackTime() : (_a2 = currentTimeRef.current) != null ? _a2 : 0;
5310
- timeRef.current.textContent = formatTime(time, format);
5311
- }
5312
- if (isPlaying) {
5313
- animationFrameRef.current = requestAnimationFrame(updateTime);
5314
- }
5315
- };
5329
+ const id = "audio-position";
5316
5330
  if (isPlaying) {
5317
- animationFrameRef.current = requestAnimationFrame(updateTime);
5318
- } else {
5319
- updateTime();
5331
+ registerFrameCallback(id, ({ time }) => {
5332
+ if (timeRef.current) {
5333
+ timeRef.current.textContent = formatTime(time, format);
5334
+ }
5335
+ });
5320
5336
  }
5321
- return () => {
5322
- if (animationFrameRef.current) {
5323
- cancelAnimationFrame(animationFrameRef.current);
5324
- animationFrameRef.current = null;
5325
- }
5326
- };
5327
- }, [isPlaying, format, currentTimeRef, getPlaybackTime]);
5337
+ return () => unregisterFrameCallback(id);
5338
+ }, [isPlaying, format, registerFrameCallback, unregisterFrameCallback]);
5328
5339
  useEffect12(() => {
5329
5340
  var _a2;
5330
5341
  if (!isPlaying && timeRef.current) {
@@ -5507,33 +5518,20 @@ var PlayheadLine = styled2.div.attrs((props) => ({
5507
5518
  `;
5508
5519
  var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5509
5520
  const playheadRef = useRef18(null);
5510
- const animationFrameRef = useRef18(null);
5511
- const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5521
+ const { isPlaying, currentTimeRef, registerFrameCallback, unregisterFrameCallback } = usePlaybackAnimation();
5512
5522
  const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();
5513
5523
  useEffect13(() => {
5514
- const updatePosition = () => {
5515
- var _a;
5516
- if (playheadRef.current) {
5517
- const time = isPlaying ? getPlaybackTime() : (_a = currentTimeRef.current) != null ? _a : 0;
5518
- const position = time * sampleRate / samplesPerPixel;
5519
- playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;
5520
- }
5521
- if (isPlaying) {
5522
- animationFrameRef.current = requestAnimationFrame(updatePosition);
5523
- }
5524
- };
5524
+ const id = "playhead";
5525
5525
  if (isPlaying) {
5526
- animationFrameRef.current = requestAnimationFrame(updatePosition);
5527
- } else {
5528
- updatePosition();
5526
+ registerFrameCallback(id, ({ visualTime, sampleRate: sr, samplesPerPixel: spp }) => {
5527
+ if (playheadRef.current) {
5528
+ const px = visualTime * sr / spp;
5529
+ playheadRef.current.style.transform = `translate3d(${px}px, 0, 0)`;
5530
+ }
5531
+ });
5529
5532
  }
5530
- return () => {
5531
- if (animationFrameRef.current) {
5532
- cancelAnimationFrame(animationFrameRef.current);
5533
- animationFrameRef.current = null;
5534
- }
5535
- };
5536
- }, [isPlaying, sampleRate, samplesPerPixel, currentTimeRef, getPlaybackTime]);
5533
+ return () => unregisterFrameCallback(id);
5534
+ }, [isPlaying, registerFrameCallback, unregisterFrameCallback]);
5537
5535
  useEffect13(() => {
5538
5536
  var _a;
5539
5537
  if (!isPlaying && playheadRef.current) {
@@ -5546,7 +5544,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5546
5544
  };
5547
5545
 
5548
5546
  // src/components/ChannelWithProgress.tsx
5549
- import { useRef as useRef19, useEffect as useEffect14 } from "react";
5547
+ import { useId, useRef as useRef19, useEffect as useEffect14 } from "react";
5550
5548
  import styled3 from "styled-components";
5551
5549
  import {
5552
5550
  clipPixelWidth as computeClipPixelWidth
@@ -5612,10 +5610,10 @@ var ChannelWithProgress = (_a) => {
5612
5610
  "clipOffsetSeconds"
5613
5611
  ]);
5614
5612
  const progressRef = useRef19(null);
5615
- const animationFrameRef = useRef19(null);
5613
+ const callbackId = useId();
5616
5614
  const theme = useTheme();
5617
5615
  const { waveHeight } = usePlaylistInfo();
5618
- const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5616
+ const { isPlaying, currentTimeRef, registerFrameCallback, unregisterFrameCallback } = usePlaybackAnimation();
5619
5617
  const { samplesPerPixel, sampleRate } = usePlaylistData();
5620
5618
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
5621
5619
  const clipPixelWidth = computeClipPixelWidth(
@@ -5624,46 +5622,32 @@ var ChannelWithProgress = (_a) => {
5624
5622
  samplesPerPixel
5625
5623
  );
5626
5624
  useEffect14(() => {
5627
- const updateProgress = () => {
5628
- var _a2;
5629
- if (progressRef.current) {
5630
- const currentTime = isPlaying ? getPlaybackTime() : (_a2 = currentTimeRef.current) != null ? _a2 : 0;
5631
- const currentSample = currentTime * sampleRate;
5632
- const clipEndSample = clipStartSample + clipDurationSamples;
5633
- let ratio = 0;
5634
- if (currentSample <= clipStartSample) {
5635
- ratio = 0;
5636
- } else if (currentSample >= clipEndSample) {
5637
- ratio = 1;
5638
- } else {
5639
- const playedSamples = currentSample - clipStartSample;
5640
- ratio = playedSamples / clipDurationSamples;
5641
- }
5642
- progressRef.current.style.transform = `scaleX(${ratio})`;
5643
- }
5644
- if (isPlaying) {
5645
- animationFrameRef.current = requestAnimationFrame(updateProgress);
5646
- }
5647
- };
5648
5625
  if (isPlaying) {
5649
- animationFrameRef.current = requestAnimationFrame(updateProgress);
5650
- } else {
5651
- updateProgress();
5626
+ registerFrameCallback(callbackId, ({ visualTime, sampleRate: sr }) => {
5627
+ if (progressRef.current) {
5628
+ const currentSample = visualTime * sr;
5629
+ const clipEndSample = clipStartSample + clipDurationSamples;
5630
+ let ratio = 0;
5631
+ if (currentSample <= clipStartSample) {
5632
+ ratio = 0;
5633
+ } else if (currentSample >= clipEndSample) {
5634
+ ratio = 1;
5635
+ } else {
5636
+ const playedSamples = currentSample - clipStartSample;
5637
+ ratio = playedSamples / clipDurationSamples;
5638
+ }
5639
+ progressRef.current.style.transform = `scaleX(${ratio})`;
5640
+ }
5641
+ });
5652
5642
  }
5653
- return () => {
5654
- if (animationFrameRef.current) {
5655
- cancelAnimationFrame(animationFrameRef.current);
5656
- animationFrameRef.current = null;
5657
- }
5658
- };
5643
+ return () => unregisterFrameCallback(callbackId);
5659
5644
  }, [
5660
5645
  isPlaying,
5661
- sampleRate,
5662
5646
  clipStartSample,
5663
5647
  clipDurationSamples,
5664
- clipPixelWidth,
5665
- currentTimeRef,
5666
- getPlaybackTime
5648
+ callbackId,
5649
+ registerFrameCallback,
5650
+ unregisterFrameCallback
5667
5651
  ]);
5668
5652
  useEffect14(() => {
5669
5653
  var _a2;