wavesurfer.js 7.0.0-alpha.22 → 7.0.0-alpha.24

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.
@@ -1,12 +1,13 @@
1
1
  import EventEmitter, { type GeneralEventTypes } from './event-emitter.js';
2
- import WaveSurfer, { type WaveSurferPluginParams } from './index.js';
2
+ import WaveSurfer, { type WaveSurferPluginParams } from './wavesurfer.js';
3
3
  export declare class BasePlugin<EventTypes extends GeneralEventTypes, Options> extends EventEmitter<EventTypes> {
4
- protected wavesurfer: WaveSurfer;
5
- protected container: HTMLElement;
6
- protected wrapper: HTMLElement;
4
+ protected wavesurfer?: WaveSurfer;
5
+ protected container?: HTMLElement;
6
+ protected wrapper?: HTMLElement;
7
7
  protected subscriptions: (() => void)[];
8
8
  protected options: Options;
9
- constructor(params: WaveSurferPluginParams, options: Options);
9
+ constructor(options: Options);
10
+ init(params: WaveSurferPluginParams): void;
10
11
  destroy(): void;
11
12
  }
12
13
  export default BasePlugin;
@@ -5,12 +5,14 @@ export class BasePlugin extends EventEmitter {
5
5
  wrapper;
6
6
  subscriptions = [];
7
7
  options;
8
- constructor(params, options) {
8
+ constructor(options) {
9
9
  super();
10
+ this.options = options;
11
+ }
12
+ init(params) {
10
13
  this.wavesurfer = params.wavesurfer;
11
14
  this.container = params.container;
12
15
  this.wrapper = params.wrapper;
13
- this.options = options;
14
16
  }
15
17
  destroy() {
16
18
  this.subscriptions.forEach((unsubscribe) => unsubscribe());
package/dist/player.d.ts CHANGED
@@ -1,27 +1,44 @@
1
- declare class Player {
1
+ import EventEmitter, { type GeneralEventTypes } from './event-emitter.js';
2
+ type PlayerOptions = {
3
+ media?: HTMLMediaElement;
4
+ autoplay?: boolean;
5
+ playbackRate?: number;
6
+ };
7
+ declare class Player<T extends GeneralEventTypes> extends EventEmitter<T> {
2
8
  protected media: HTMLMediaElement;
9
+ protected subscriptions: Array<() => void>;
3
10
  private isExternalMedia;
4
11
  private hasPlayedOnce;
5
- private subscriptions;
6
- constructor({ media, autoplay }: {
7
- media?: HTMLMediaElement;
8
- autoplay?: boolean;
9
- });
10
- on(event: keyof HTMLMediaElementEventMap, callback: () => void, options?: AddEventListenerOptions): () => void;
12
+ constructor(options: PlayerOptions);
13
+ protected onMediaEvent(event: keyof HTMLMediaElementEventMap, callback: () => void, options?: AddEventListenerOptions): () => void;
14
+ protected onceMediaEvent(event: keyof HTMLMediaElementEventMap, callback: () => void): () => void;
15
+ protected loadUrl(src: string): void;
11
16
  destroy(): void;
12
- loadUrl(src: string): void;
13
- getCurrentTime(): number;
17
+ /** Start playing the audio */
14
18
  play(): void;
19
+ /** Pause the audio */
15
20
  pause(): void;
21
+ /** Check if the audio is playing */
16
22
  isPlaying(): boolean;
23
+ /** Skip to a time position in seconds */
17
24
  seekTo(time: number): void;
25
+ /** Get the duration of the audio in seconds */
18
26
  getDuration(): number;
27
+ /** Get the current audio position in seconds */
28
+ getCurrentTime(): number;
29
+ /** Get the audio volume */
19
30
  getVolume(): number;
31
+ /** Set the audio volume */
20
32
  setVolume(volume: number): void;
33
+ /** Get the audio muted state */
21
34
  getMuted(): boolean;
35
+ /** Mute or unmute the audio */
22
36
  setMuted(muted: boolean): void;
37
+ /** Get the playback speed */
23
38
  getPlaybackRate(): number;
39
+ /** Set the playback speed, pass an optional false to NOT preserve the pitch */
24
40
  setPlaybackRate(rate: number, preservePitch?: boolean): void;
41
+ /** Get the HTML media element */
25
42
  getMediaElement(): HTMLMediaElement;
26
43
  }
27
44
  export default Player;
package/dist/player.js CHANGED
@@ -1,11 +1,13 @@
1
- class Player {
1
+ import EventEmitter from './event-emitter.js';
2
+ class Player extends EventEmitter {
2
3
  media;
4
+ subscriptions = [];
3
5
  isExternalMedia = false;
4
6
  hasPlayedOnce = false;
5
- subscriptions = [];
6
- constructor({ media, autoplay }) {
7
- if (media) {
8
- this.media = media;
7
+ constructor(options) {
8
+ super();
9
+ if (options.media) {
10
+ this.media = options.media;
9
11
  this.isExternalMedia = true;
10
12
  }
11
13
  else {
@@ -13,42 +15,48 @@ class Player {
13
15
  }
14
16
  this.subscriptions.push(
15
17
  // Track the first play() call
16
- this.on('play', () => {
18
+ this.onceMediaEvent('play', () => {
17
19
  this.hasPlayedOnce = true;
18
- }, { once: true }));
20
+ }));
19
21
  // Autoplay
20
- if (autoplay) {
22
+ if (options.autoplay) {
21
23
  this.media.autoplay = true;
22
24
  }
25
+ // Speed
26
+ if (options.playbackRate != null) {
27
+ this.media.playbackRate = options.playbackRate;
28
+ }
23
29
  }
24
- on(event, callback, options) {
30
+ onMediaEvent(event, callback, options) {
25
31
  this.media.addEventListener(event, callback, options);
26
32
  return () => this.media.removeEventListener(event, callback);
27
33
  }
34
+ onceMediaEvent(event, callback) {
35
+ return this.onMediaEvent(event, callback, { once: true });
36
+ }
37
+ loadUrl(src) {
38
+ this.media.src = src;
39
+ }
28
40
  destroy() {
29
- this.subscriptions.forEach((unsubscribe) => {
30
- unsubscribe();
31
- });
32
41
  this.media.pause();
42
+ this.subscriptions.forEach((unsubscribe) => unsubscribe());
33
43
  if (!this.isExternalMedia) {
34
44
  this.media.remove();
35
45
  }
36
46
  }
37
- loadUrl(src) {
38
- this.media.src = src;
39
- }
40
- getCurrentTime() {
41
- return this.media.currentTime;
42
- }
47
+ /** Start playing the audio */
43
48
  play() {
44
49
  this.media.play();
45
50
  }
51
+ /** Pause the audio */
46
52
  pause() {
47
53
  this.media.pause();
48
54
  }
55
+ /** Check if the audio is playing */
49
56
  isPlaying() {
50
57
  return this.media.currentTime > 0 && !this.media.paused && !this.media.ended;
51
58
  }
59
+ /** Skip to a time position in seconds */
52
60
  seekTo(time) {
53
61
  // iOS Safari requires a play() call before seeking
54
62
  if (!this.hasPlayedOnce && navigator.userAgent.match(/(iPad|iPhone|iPod)/g)) {
@@ -58,24 +66,35 @@ class Player {
58
66
  }
59
67
  this.media.currentTime = time;
60
68
  }
69
+ /** Get the duration of the audio in seconds */
61
70
  getDuration() {
62
71
  return this.media.duration;
63
72
  }
73
+ /** Get the current audio position in seconds */
74
+ getCurrentTime() {
75
+ return this.media.currentTime;
76
+ }
77
+ /** Get the audio volume */
64
78
  getVolume() {
65
79
  return this.media.volume;
66
80
  }
81
+ /** Set the audio volume */
67
82
  setVolume(volume) {
68
83
  this.media.volume = volume;
69
84
  }
85
+ /** Get the audio muted state */
70
86
  getMuted() {
71
87
  return this.media.muted;
72
88
  }
89
+ /** Mute or unmute the audio */
73
90
  setMuted(muted) {
74
91
  this.media.muted = muted;
75
92
  }
93
+ /** Get the playback speed */
76
94
  getPlaybackRate() {
77
95
  return this.media.playbackRate;
78
96
  }
97
+ /** Set the playback speed, pass an optional false to NOT preserve the pitch */
79
98
  setPlaybackRate(rate, preservePitch) {
80
99
  // preservePitch is true by default in most browsers
81
100
  if (preservePitch != null) {
@@ -83,6 +102,7 @@ class Player {
83
102
  }
84
103
  this.media.playbackRate = rate;
85
104
  }
105
+ /** Get the HTML media element */
86
106
  getMediaElement() {
87
107
  return this.media;
88
108
  }
@@ -1,5 +1,5 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import type { WaveSurferPluginParams } from '../index.js';
2
+ import type { WaveSurferPluginParams } from '../wavesurfer.js';
3
3
  export type EnvelopePluginOptions = {
4
4
  startTime?: number;
5
5
  endTime?: number;
@@ -42,7 +42,9 @@ declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePl
42
42
  private volume;
43
43
  private isFadingIn;
44
44
  private isFadingOut;
45
- constructor(params: WaveSurferPluginParams, options: EnvelopePluginOptions);
45
+ constructor(options: EnvelopePluginOptions);
46
+ static create(options: EnvelopePluginOptions): EnvelopePlugin;
47
+ init(params: WaveSurferPluginParams): void;
46
48
  private makeDraggable;
47
49
  private renderPolyline;
48
50
  private initSvg;
@@ -18,13 +18,22 @@ class EnvelopePlugin extends BasePlugin {
18
18
  volume = 1;
19
19
  isFadingIn = false;
20
20
  isFadingOut = false;
21
- constructor(params, options) {
22
- super(params, options);
21
+ constructor(options) {
22
+ super(options);
23
23
  this.options = Object.assign({}, defaultOptions, options);
24
24
  this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
25
25
  this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
26
26
  this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
27
27
  this.volume = this.options.volume ?? 1;
28
+ }
29
+ static create(options) {
30
+ return new EnvelopePlugin(options);
31
+ }
32
+ init(params) {
33
+ super.init(params);
34
+ if (!this.wavesurfer) {
35
+ throw Error('WaveSurfer is not initialized');
36
+ }
28
37
  this.subscriptions.push(this.wavesurfer.once('decode', ({ duration }) => {
29
38
  this.options.startTime = this.options.startTime || 0;
30
39
  this.options.endTime = this.options.endTime || duration;
@@ -48,10 +57,10 @@ class EnvelopePlugin extends BasePlugin {
48
57
  draggable.addEventListener('mousedown', (e) => {
49
58
  let x = e.clientX;
50
59
  let y = e.clientY;
51
- const wasInteractive = this.wavesurfer.options.interactive;
60
+ const wasInteractive = this.wavesurfer?.options.interact || true;
52
61
  let delay;
53
62
  // Make the wavesurfer ignore clicks when we're dragging
54
- this.wavesurfer.toggleInteractive(false);
63
+ this.wavesurfer?.toggleInteractive(false);
55
64
  const move = (e) => {
56
65
  const dx = e.clientX - x;
57
66
  const dy = e.clientY - y;
@@ -66,7 +75,7 @@ class EnvelopePlugin extends BasePlugin {
66
75
  if (delay)
67
76
  clearTimeout(delay);
68
77
  delay = setTimeout(() => {
69
- this.wavesurfer.toggleInteractive(wasInteractive);
78
+ this.wavesurfer?.toggleInteractive(wasInteractive);
70
79
  }, 100);
71
80
  };
72
81
  document.addEventListener('mousemove', move);
@@ -76,7 +85,7 @@ class EnvelopePlugin extends BasePlugin {
76
85
  });
77
86
  }
78
87
  renderPolyline() {
79
- if (!this.svg)
88
+ if (!this.svg || !this.wrapper || !this.wavesurfer)
80
89
  return;
81
90
  const polyline = this.svg.querySelector('polyline');
82
91
  const points = polyline.points;
@@ -99,6 +108,8 @@ class EnvelopePlugin extends BasePlugin {
99
108
  }
100
109
  }
101
110
  initSvg() {
111
+ if (!this.wrapper || !this.wavesurfer)
112
+ return;
102
113
  const width = this.wrapper.clientWidth;
103
114
  const height = this.wrapper.clientHeight;
104
115
  const duration = this.wavesurfer.getDuration();
@@ -180,7 +191,7 @@ class EnvelopePlugin extends BasePlugin {
180
191
  this.emit('fade-out-change', { time: newTime });
181
192
  }
182
193
  // Also allow dragging points vertically
183
- if (dy > 1) {
194
+ if (dy > 1 || dy < -1) {
184
195
  onDragY(dy);
185
196
  }
186
197
  else {
@@ -201,7 +212,7 @@ class EnvelopePlugin extends BasePlugin {
201
212
  super.destroy();
202
213
  }
203
214
  initWebAudio() {
204
- const audio = this.wavesurfer.getMediaElement();
215
+ const audio = this.wavesurfer?.getMediaElement();
205
216
  if (!audio)
206
217
  return null;
207
218
  this.volume = this.options.volume ?? audio.volume;
@@ -233,12 +244,12 @@ class EnvelopePlugin extends BasePlugin {
233
244
  this.gainNode.gain.value = volume;
234
245
  }
235
246
  initFadeEffects() {
236
- if (!this.audioContext)
247
+ if (!this.audioContext || !this.wavesurfer)
237
248
  return;
238
249
  const unsub = this.wavesurfer.on('timeupdate', ({ currentTime }) => {
239
250
  if (!this.audioContext || !this.gainNode)
240
251
  return;
241
- if (!this.wavesurfer.isPlaying())
252
+ if (!this.wavesurfer?.isPlaying())
242
253
  return;
243
254
  if (this.audioContext.state === 'suspended') {
244
255
  this.audioContext.resume();
@@ -281,7 +292,6 @@ class EnvelopePlugin extends BasePlugin {
281
292
  }
282
293
  setStartTime(time) {
283
294
  this.options.startTime = time;
284
- console.log(time);
285
295
  this.renderPolyline();
286
296
  }
287
297
  setEndTime(time) {
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Envelope=e():t.Envelope=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={284:(t,e,i)=>{i.d(e,{Z:()=>o});var s=i(139);class n extends s.Z{wavesurfer;container;wrapper;subscriptions=[];options;constructor(t){super(),this.options=t}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}}const o=n},139:(t,e,i)=>{i.d(e,{Z:()=>s});const s=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(t,e){const i=new CustomEvent(String(t),{detail:e});this.eventTarget.dispatchEvent(i)}on(t,e,i){const s=t=>{t instanceof CustomEvent&&e(t.detail)},n=String(t);return this.eventTarget.addEventListener(n,s,{once:i}),()=>this.eventTarget.removeEventListener(n,s)}once(t,e){return this.on(t,e,!0)}}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s](o,o.exports,i),o.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var s={};return(()=>{i.d(s,{default:()=>o});var t=i(284);const e={startTime:0,endTime:0,fadeInEnd:0,fadeOutStart:0,lineWidth:4,lineColor:"rgba(0, 0, 255, 0.5)",dragPointSize:10,dragPointFill:"rgba(255, 255, 255, 0.8)",dragPointStroke:"rgba(255, 255, 255, 0.8)"};class n extends t.Z{options;svg=null;audioContext=null;gainNode=null;volume=1;isFadingIn=!1;isFadingOut=!1;constructor(t){super(t),this.options=Object.assign({},e,t),this.options.lineColor=this.options.lineColor||e.lineColor,this.options.dragPointFill=this.options.dragPointFill||e.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||e.dragPointStroke,this.volume=this.options.volume??1}static create(t){return new n(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");let e;this.subscriptions.push(this.wavesurfer.once("decode",(({duration:t})=>{this.options.startTime=this.options.startTime||0,this.options.endTime=this.options.endTime||t,this.options.fadeInEnd=this.options.fadeInEnd||this.options.startTime,this.options.fadeOutStart=this.options.fadeOutStart||this.options.endTime,this.initWebAudio(),this.initSvg(),this.initFadeEffects()}))),this.subscriptions.push(this.wavesurfer.on("zoom",(()=>{e&&clearTimeout(e),e=setTimeout((()=>{this.svg?.remove(),this.initSvg()}),100)})))}makeDraggable(t,e){t.addEventListener("mousedown",(t=>{let i=t.clientX,s=t.clientY;const n=this.wavesurfer?.options.interact||!0;let o;this.wavesurfer?.toggleInteractive(!1);const r=t=>{const n=t.clientX-i,o=t.clientY-s;i=t.clientX,s=t.clientY,e(n,o)},a=()=>{document.removeEventListener("mousemove",r),document.removeEventListener("mouseup",a),o&&clearTimeout(o),o=setTimeout((()=>{this.wavesurfer?.toggleInteractive(n)}),100)};document.addEventListener("mousemove",r),document.addEventListener("mouseup",a),t.preventDefault(),t.stopPropagation()}))}renderPolyline(){if(!this.svg||!this.wrapper||!this.wavesurfer)return;const t=this.svg.querySelector("polyline"),e=t.points,i=e.getItem(1).y,s=this.wrapper.clientWidth,n=this.wavesurfer.getDuration();e.getItem(0).x=this.options.startTime/n*s,e.getItem(3).x=this.options.endTime/n*s;const o=this.svg.querySelector("line");o.setAttribute("x1",e.getItem(1).x.toString()),o.setAttribute("x2",e.getItem(2).x.toString()),o.setAttribute("y1",i.toString()),o.setAttribute("y2",i.toString());const r=this.svg.querySelectorAll("circle");for(let e=0;e<r.length;e++){const s=r[e],n=t.points.getItem(e+1);s.setAttribute("cx",n.x.toString()),s.setAttribute("cy",i.toString())}}initSvg(){if(!this.wrapper||!this.wavesurfer)return;const t=this.wrapper.clientWidth,e=this.wrapper.clientHeight,i=this.wavesurfer.getDuration(),s=document.createElementNS("http://www.w3.org/2000/svg","svg");s.setAttribute("width","100%"),s.setAttribute("height","100%"),s.setAttribute("viewBox",`0 0 ${t} ${e}`),s.setAttribute("preserveAspectRatio","none"),s.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),this.svg=s;const n=document.createElementNS("http://www.w3.org/2000/svg","polyline");n.setAttribute("points","0,0 0,0 0,0 0,0"),n.setAttribute("stroke",this.options.lineColor),n.setAttribute("stroke-width",this.options.lineWidth),n.setAttribute("fill","none"),n.setAttribute("style","pointer-events: none"),s.appendChild(n);const o=document.createElementNS("http://www.w3.org/2000/svg","line");o.setAttribute("stroke","none"),o.setAttribute("stroke-width",(3*this.options.lineWidth).toString()),o.setAttribute("style","cursor: ns-resize; pointer-events: all;"),s.appendChild(o);const r=n.points,a=this.options.dragPointSize/2,u=e-this.volume*e+a;r.getItem(0).x=this.options.startTime/i*t,r.getItem(0).y=e,r.getItem(1).x=this.options.fadeInEnd/i*t,r.getItem(1).y=u,r.getItem(2).x=this.options.fadeOutStart/i*t,r.getItem(2).y=u,r.getItem(3).x=this.options.endTime/i*t,r.getItem(3).y=e,[1,2].forEach((()=>{const t=document.createElementNS("http://www.w3.org/2000/svg","circle");t.setAttribute("r",(this.options.dragPointSize/2).toString()),t.setAttribute("fill",this.options.dragPointFill),t.setAttribute("stroke",this.options.dragPointStroke||this.options.dragPointFill),t.setAttribute("stroke-width","2"),t.setAttribute("style","cursor: ew-resize; pointer-events: all;"),s.appendChild(t)})),this.wrapper.appendChild(s),this.renderPolyline();const h=t=>{const i=r.getItem(1).y+t-a;if(i<-.5||i>e)return;r.getItem(1).y=i+a,r.getItem(2).y=i+a,this.renderPolyline();const s=Math.min(1,Math.max(0,(e-i)/e));this.onVolumeChange(s),this.renderPolyline()},d=(e,s,o)=>{const r=n.points.getItem(o),a=r.x+e,u=a/t*i;1===o&&u>this.options.fadeOutStart||u<this.options.startTime||2===o&&u<this.options.fadeInEnd||u>this.options.endTime||(r.x=a,1===o?(this.options.fadeInEnd=u,this.emit("fade-in-change",{time:u})):2===o&&(this.options.fadeOutStart=u,this.emit("fade-out-change",{time:u})),s>1||s<-1?h(s):this.renderPolyline())};this.makeDraggable(o,((t,e)=>h(e)));const l=s.querySelectorAll("circle");for(let t=0;t<l.length;t++){const e=t+1;this.makeDraggable(l[t],((t,i)=>d(t,i,e)))}}destroy(){this.svg?.remove(),super.destroy()}initWebAudio(){const t=this.wavesurfer?.getMediaElement();if(!t)return null;this.volume=this.options.volume??t.volume;const e=new window.AudioContext;this.gainNode=e.createGain(),this.gainNode.gain.value=this.volume,e.createMediaElementSource(t).connect(this.gainNode),this.gainNode.connect(e.destination),this.audioContext=e}naturalVolume(t){return 1e-4+.9999*Math.pow(t,3)}onVolumeChange(t){t=this.naturalVolume(t),this.volume=t,this.emit("volume-change",{volume:t}),this.gainNode&&(this.gainNode.gain.value=t)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(({currentTime:t})=>{if(!this.audioContext||!this.gainNode)return;if(!this.wavesurfer?.isPlaying())return;if("suspended"===this.audioContext.state&&this.audioContext.resume(),!this.isFadingIn&&t>=this.options.startTime&&t<=this.options.fadeInEnd)return this.isFadingIn=!0,this.gainNode.gain.setValueAtTime(0,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(this.volume,this.audioContext.currentTime+(this.options.fadeInEnd-t));if(!this.isFadingOut&&t>=this.options.fadeOutStart&&t<=this.options.endTime)return this.isFadingOut=!0,void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.endTime-t));let e=!1;this.isFadingIn&&(t<this.options.startTime||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,e=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.endTime)&&(this.isFadingOut=!1,e=!0),e&&(this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.gainNode.gain.value=this.volume)}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t){this.options.startTime=t,this.renderPolyline()}setEndTime(t){this.options.endTime=t,this.renderPolyline()}}const o=n})(),s.default})()));
@@ -1,5 +1,5 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import { type WaveSurferPluginParams, type WaveSurferOptions } from '../index.js';
2
+ import { type WaveSurferPluginParams, type WaveSurferOptions } from '../wavesurfer.js';
3
3
  export type MinimapPluginOptions = {
4
4
  overlayColor?: string;
5
5
  } & WaveSurferOptions;
@@ -10,16 +10,18 @@ declare const defaultOptions: {
10
10
  export type MinimapPluginEvents = {
11
11
  ready: void;
12
12
  };
13
- declare class TimelinePlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions> {
13
+ declare class MinimapPlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions> {
14
14
  protected options: MinimapPluginOptions & typeof defaultOptions;
15
15
  private minimapWrapper;
16
16
  private miniWavesurfer;
17
17
  private overlay;
18
- constructor(params: WaveSurferPluginParams, options: MinimapPluginOptions);
18
+ constructor(options: MinimapPluginOptions);
19
+ static create(options: MinimapPluginOptions): MinimapPlugin;
20
+ init(params: WaveSurferPluginParams): void;
19
21
  private initMinimapWrapper;
20
22
  private initOverlay;
21
23
  private initMinimap;
22
24
  /** Unmount */
23
25
  destroy(): void;
24
26
  }
25
- export default TimelinePlugin;
27
+ export default MinimapPlugin;
@@ -1,19 +1,29 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import WaveSurfer from '../index.js';
2
+ import WaveSurfer from '../wavesurfer.js';
3
3
  const defaultOptions = {
4
4
  height: 50,
5
5
  overlayColor: 'rgba(100, 100, 100, 0.1)',
6
6
  };
7
- class TimelinePlugin extends BasePlugin {
7
+ class MinimapPlugin extends BasePlugin {
8
8
  options;
9
9
  minimapWrapper;
10
10
  miniWavesurfer = null;
11
11
  overlay;
12
- constructor(params, options) {
13
- super(params, options);
12
+ constructor(options) {
13
+ super(options);
14
14
  this.options = Object.assign({}, defaultOptions, options);
15
15
  this.minimapWrapper = this.initMinimapWrapper();
16
16
  this.overlay = this.initOverlay();
17
+ }
18
+ static create(options) {
19
+ return new MinimapPlugin(options);
20
+ }
21
+ init(params) {
22
+ super.init(params);
23
+ if (!this.wavesurfer) {
24
+ throw Error('WaveSurfer is not initialized');
25
+ }
26
+ this.container?.insertAdjacentElement('afterend', this.minimapWrapper);
17
27
  this.subscriptions.push(this.wavesurfer.on('decode', () => {
18
28
  this.initMinimap();
19
29
  }));
@@ -21,7 +31,6 @@ class TimelinePlugin extends BasePlugin {
21
31
  initMinimapWrapper() {
22
32
  const div = document.createElement('div');
23
33
  div.style.position = 'relative';
24
- this.container.insertAdjacentElement('afterend', div);
25
34
  return div;
26
35
  }
27
36
  initOverlay() {
@@ -32,6 +41,8 @@ class TimelinePlugin extends BasePlugin {
32
41
  return div;
33
42
  }
34
43
  initMinimap() {
44
+ if (!this.wavesurfer || !this.wrapper)
45
+ return;
35
46
  const data = this.wavesurfer.getDecodedData();
36
47
  const media = this.wavesurfer.getMediaElement();
37
48
  if (!data || !media)
@@ -49,7 +60,7 @@ class TimelinePlugin extends BasePlugin {
49
60
  const overlayWidth = Math.round((this.minimapWrapper.clientWidth / this.wrapper.clientWidth) * 100);
50
61
  this.overlay.style.width = `${overlayWidth}%`;
51
62
  this.wavesurfer.on('timeupdate', ({ currentTime }) => {
52
- const offset = Math.max(0, Math.min((currentTime / this.wavesurfer.getDuration()) * 100 - overlayWidth / 2, 100 - overlayWidth)).toFixed(2);
63
+ const offset = Math.max(0, Math.min((currentTime / data.duration) * 100 - overlayWidth / 2, 100 - overlayWidth)).toFixed(2);
53
64
  this.overlay.style.left = `${offset}%`;
54
65
  });
55
66
  }
@@ -60,4 +71,4 @@ class TimelinePlugin extends BasePlugin {
60
71
  super.destroy();
61
72
  }
62
73
  }
63
- export default TimelinePlugin;
74
+ export default MinimapPlugin;
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Minimap=e():t.Minimap=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var s in i)t.o(i,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:i[s]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>p});const i=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(t,e){const i=new CustomEvent(String(t),{detail:e});this.eventTarget.dispatchEvent(i)}on(t,e,i){const s=t=>{t instanceof CustomEvent&&e(t.detail)},r=String(t);return this.eventTarget.addEventListener(r,s,{once:i}),()=>this.eventTarget.removeEventListener(r,s)}once(t,e){return this.on(t,e,!0)}},s=class extends i{wavesurfer;container;wrapper;subscriptions=[];options;constructor(t){super(),this.options=t}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}},r=class extends i{options;container;scrollContainer;wrapper;canvasWrapper;progressWrapper;timeout=null;isScrolling=!1;audioData=null;resizeObserver=null;constructor(t){super(),this.options=t;let e=null;if("string"==typeof this.options.container?e=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(e=this.options.container),!e)throw new Error("Container not found");const i=document.createElement("div"),s=i.attachShadow({mode:"open"});s.innerHTML=`\n <style>\n :host {\n user-select: none;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n ${this.options.hideScrollbar?"scrollbar-color: transparent;":""}\n }\n :host ::-webkit-scrollbar {\n display: ${this.options.hideScrollbar?"none":"auto"};\n }\n :host .wrapper {\n position: relative;\n min-width: 100%;\n z-index: 2;\n }\n :host .canvases {\n position: relative;\n height: ${this.options.height}px;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n height: ${this.options.height}px;\n image-rendering: pixelated;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: ${this.options.height}px;\n overflow: hidden;\n box-sizing: border-box;\n border-right-style: solid;\n border-right-width: ${this.options.cursorWidth}px;\n border-right-color: ${this.options.cursorColor||this.options.progressColor};\n }\n </style>\n\n <div class="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress"></div>\n </div>\n </div>\n `,this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),e.appendChild(i),this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",{relativeX:i})})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.audioData&&this.zoom(this.audioData,this.options.minPxPerSec)),100)})),this.resizeObserver.observe(this.scrollContainer)}getContainer(){return this.scrollContainer}getWrapper(){return this.wrapper}destroy(){this.container.remove(),this.resizeObserver?.disconnect()}delay(t,e=10){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}async renderPeaks(t,e,i,s){const r=null!=this.options.barWidth?this.options.barWidth*s:1,n=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?r/2:0,o=this.options.barRadius??0,a=t[0],h=a.length,d=Math.floor(e/(r+n))/h,l=i/2,c=1===t.length,p=c?a:t[1],u=c&&p.some((t=>t<0)),m=(t,i)=>{let c=0,m=0,g=0;const v=document.createElement("canvas");v.width=Math.round(e*(i-t)/h),v.height=this.options.height,v.style.width=`${Math.floor(v.width/s)}px`,v.style.left=`${Math.floor(t*e/s/h)}px`,this.canvasWrapper.appendChild(v);const y=v.getContext("2d",{desynchronized:!0});y.beginPath(),y.fillStyle=this.options.waveColor,y.roundRect||(y.roundRect=y.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*d);if(i>c){const t=Math.round(m*l),e=Math.round(g*l);y.roundRect(c*(r+n),l-t,r,t+e||1,o),c=i,m=0,g=0}const s=u?a[e]:Math.abs(a[e]),h=u?p[e]:Math.abs(p[e]);s>m&&(m=s),(u?h<-g:h>g)&&(g=h<0?-h:h)}y.fill(),y.closePath();const f=v.cloneNode();this.progressWrapper.appendChild(f);const b=f.getContext("2d",{desynchronized:!0});b.drawImage(v,0,0),b.globalCompositeOperation="source-in",b.fillStyle=this.options.progressColor,b.fillRect(0,0,v.width,v.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:g,scrollWidth:v,clientWidth:y}=this.scrollContainer,f=h/v,b=Math.floor(g*f),w=Math.ceil(Math.min(v,g+y)*f);m(b,w);const C=w-b;for(let t=w;t<h;t+=C)await this.delay((()=>{m(t,Math.min(h,t+C))}));for(let t=b-1;t>=0;t-=C)await this.delay((()=>{m(Math.max(0,t-C),t)}))}render(t){const e=window.devicePixelRatio||1,i=this.options.fillParent?this.scrollContainer.clientWidth*e:0,s=Math.max(1,t.duration*this.options.minPxPerSec),r=s>i?s:i,{height:n}=this.options;this.isScrolling=r>i,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.wrapper.style.width=`${Math.floor(r/e)}px`;const o=[t.getChannelData(0)];t.numberOfChannels>1&&o.push(t.getChannelData(1)),this.renderPeaks(o,r,n,e),this.audioData=t}zoom(t,e){const i=this.progressWrapper.clientWidth;this.options.minPxPerSec=e,this.render(t);const s=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=s-i}renderProgress(t,e=!1){if(this.progressWrapper.style.width=100*t+"%",this.isScrolling){const t=this.scrollContainer.clientWidth,i=t/2,s=this.progressWrapper.clientWidth,r=e?i:t;s>this.scrollContainer.scrollLeft+r&&(this.scrollContainer.scrollLeft=s-i)}}},n=class extends i{media;subscriptions=[];isExternalMedia=!1;hasPlayedOnce=!1;constructor(t){super(),t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),this.subscriptions.push(this.onceMediaEvent("play",(()=>{this.hasPlayedOnce=!0}))),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}onceMediaEvent(t,e){return this.onMediaEvent(t,e,{once:!0})}loadUrl(t){this.media.src=t}destroy(){this.media.pause(),this.subscriptions.forEach((t=>t())),this.isExternalMedia||this.media.remove()}play(){this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}seekTo(t){!this.hasPlayedOnce&&navigator.userAgent.match(/(iPad|iPhone|iPod)/g)&&this.media.play()?.then?.((()=>{setTimeout((()=>this.media.pause()),10)})),this.media.currentTime=t}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}},o=class extends i{unsubscribe=()=>{};start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}},a={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoCenter:!0};class h extends n{options;fetcher;decoder;renderer;timer;plugins=[];decodedData=null;canPlay=!1;static create(t){return new h(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.options=Object.assign({},a,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.decoder=new class{audioCtx=null;initAudioContext(t){this.audioCtx=new AudioContext({latencyHint:"playback",sampleRate:t})}constructor(){try{this.initAudioContext(3e3)}catch(t){this.initAudioContext(8e3)}}async decode(t){if(!this.audioCtx)throw new Error("AudioContext is not initialized");return this.audioCtx.decodeAudioData(t)}destroy(){this.audioCtx?.close(),this.audioCtx=null}},this.timer=new o,this.renderer=new r({container:this.options.container,height:this.options.height,waveColor:this.options.waveColor,progressColor:this.options.progressColor,cursorColor:this.options.cursorColor,cursorWidth:this.options.cursorWidth,minPxPerSec:this.options.minPxPerSec,fillParent:this.options.fillParent,barWidth:this.options.barWidth,barGap:this.options.barGap,barRadius:this.options.barRadius,hideScrollbar:this.options.hideScrollbar}),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent(),this.initPlugins();const e=this.options.url||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.options.autoCenter&&this.isPlaying()),this.emit("timeupdate",{currentTime:t})})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.onMediaEvent("canplay",(()=>{this.canPlay=!0,this.emit("canplay",{duration:this.getDuration()})})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",{currentTime:this.getCurrentTime()})})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(({relativeX:t})=>{if(this.options.interact){const e=this.getDuration()*t;this.seekTo(e),this.emit("seekClick",{currentTime:this.getCurrentTime()})}})))}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",{currentTime:t})})))}initReadyEvent(){const t=()=>{this.decodedData&&this.canPlay&&this.emit("ready",{duration:this.getDuration()})};this.subscriptions.push(this.on("decode",t),this.on("canplay",t))}initPlugins(){this.options.plugins?.length&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init({wavesurfer:this,container:this.renderer.getContainer(),wrapper:this.renderer.getWrapper()}),this.plugins.push(t),t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t))})),t}async load(t,e,i){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),null==e){const e=await this.fetcher.load(t),i=await this.decoder.decode(e);this.decodedData=i}else i||(i=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getDuration())))}))||0),this.decodedData={duration:i,numberOfChannels:e.length,sampleRate:e[0].length/i,getChannelData:t=>e[t]};this.renderer.render(this.decodedData),this.emit("decode",{duration:this.decodedData.duration})}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(this.decodedData,t),this.emit("zoom",{minPxPerSec:t})}getDecodedData(){return this.decodedData}getDuration(){return this.decodedData?.duration||this.getMediaElement()?.duration||0}toggleInteractive(t){this.options.interact=t}destroy(){this.emit("destroy"),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.decoder.destroy(),this.renderer.destroy(),super.destroy()}}const d=h,l={height:50,overlayColor:"rgba(100, 100, 100, 0.1)"};class c extends s{options;minimapWrapper;miniWavesurfer=null;overlay;constructor(t){super(t),this.options=Object.assign({},l,t),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay()}static create(t){return new c(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.container?.insertAdjacentElement("afterend",this.minimapWrapper),this.subscriptions.push(this.wavesurfer.on("decode",(()=>{this.initMinimap()})))}initMinimapWrapper(){const t=document.createElement("div");return t.style.position="relative",t}initOverlay(){const t=document.createElement("div");return t.setAttribute("style","position: absolute; z-index: 2; left: 0; top: 0; bottom: 0;"),t.style.backgroundColor=this.options.overlayColor,this.minimapWrapper.appendChild(t),t}initMinimap(){if(!this.wavesurfer||!this.wrapper)return;const t=this.wavesurfer.getDecodedData(),e=this.wavesurfer.getMediaElement();if(!t||!e)return;this.miniWavesurfer=d.create({...this.options,container:this.minimapWrapper,minPxPerSec:1,fillParent:!0,media:e,url:e.src,peaks:[t.getChannelData(0)],duration:t.duration});const i=Math.round(this.minimapWrapper.clientWidth/this.wrapper.clientWidth*100);this.overlay.style.width=`${i}%`,this.wavesurfer.on("timeupdate",(({currentTime:e})=>{const s=Math.max(0,Math.min(e/t.duration*100-i/2,100-i)).toFixed(2);this.overlay.style.left=`${s}%`}))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}}const p=c;return e.default})()));
@@ -1,4 +1,4 @@
1
- import { type WaveSurferOptions } from '../index.js';
1
+ import { type WaveSurferOptions } from '../wavesurfer.js';
2
2
  import { type EnvelopePluginOptions } from './envelope.js';
3
3
  import EventEmitter from '../event-emitter.js';
4
4
  export type TrackId = string | number;
@@ -95,7 +95,6 @@ declare class MultiTrack extends EventEmitter<MultitrackEvents> {
95
95
  private updatePosition;
96
96
  private setIsDragging;
97
97
  private onDrag;
98
- private onMove;
99
98
  private findCurrentTracks;
100
99
  private startSync;
101
100
  play(): void;
@@ -1,4 +1,4 @@
1
- import WaveSurfer from '../index.js';
1
+ import WaveSurfer from '../wavesurfer.js';
2
2
  import RegionsPlugin from './regions.js';
3
3
  import TimelinePlugin from './timeline.js';
4
4
  import EnvelopePlugin from './envelope.js';
@@ -39,6 +39,11 @@ class MultiTrack extends EventEmitter {
39
39
  const drag = initDragging(container, (delta) => this.onDrag(index, delta), options.rightButtonDrag);
40
40
  this.wavesurfers[index].once('destroy', () => drag?.destroy());
41
41
  });
42
+ this.rendering.addClickHandler((position) => {
43
+ if (this.isDragging)
44
+ return;
45
+ this.seekTo(position * this.maxDuration);
46
+ });
42
47
  this.emit('canplay');
43
48
  });
44
49
  }
@@ -48,11 +53,6 @@ class MultiTrack extends EventEmitter {
48
53
  return Math.max(max, track.startPosition + durations[index]);
49
54
  }, 0);
50
55
  this.rendering.setMainWidth(durations, this.maxDuration);
51
- this.rendering.addClickHandler((position) => {
52
- if (this.isDragging)
53
- return;
54
- this.seekTo(position * this.maxDuration);
55
- });
56
56
  }
57
57
  initAudio(track) {
58
58
  const audio = new Audio(track.url);
@@ -77,14 +77,14 @@ class MultiTrack extends EventEmitter {
77
77
  peaks: track.peaks,
78
78
  cursorColor: 'transparent',
79
79
  cursorWidth: 0,
80
- interactive: false,
80
+ interact: false,
81
81
  });
82
82
  // Regions and markers
83
- const wsRegions = ws.registerPlugin(RegionsPlugin, {
83
+ const wsRegions = ws.registerPlugin(RegionsPlugin.create({
84
84
  draggable: false,
85
85
  resizable: true,
86
86
  dragSelection: false,
87
- });
87
+ }));
88
88
  this.subscriptions.push(ws.once('decode', () => {
89
89
  // Start and end cues
90
90
  if (track.startCue != null || track.endCue != null) {
@@ -133,14 +133,14 @@ class MultiTrack extends EventEmitter {
133
133
  }
134
134
  }));
135
135
  // Envelope
136
- const envelope = ws.registerPlugin(EnvelopePlugin, {
136
+ const envelope = ws.registerPlugin(EnvelopePlugin.create({
137
137
  ...this.options.envelopeOptions,
138
138
  startTime: track.startCue,
139
139
  endTime: track.endCue,
140
140
  fadeInEnd: track.fadeInEnd,
141
141
  fadeOutStart: track.fadeOutStart,
142
142
  volume: track.volume,
143
- });
143
+ }));
144
144
  this.subscriptions.push(envelope.on('volume-change', ({ volume }) => {
145
145
  this.setIsDragging();
146
146
  this.emit('volume-change', { id: track.id, volume });
@@ -171,10 +171,10 @@ class MultiTrack extends EventEmitter {
171
171
  initTimeline() {
172
172
  if (this.timeline)
173
173
  this.timeline.destroy();
174
- this.timeline = this.wavesurfers[0].registerPlugin(TimelinePlugin, {
174
+ this.timeline = this.wavesurfers[0].registerPlugin(TimelinePlugin.create({
175
175
  duration: this.maxDuration,
176
176
  container: this.rendering.containers[0].parentElement,
177
- });
177
+ }));
178
178
  }
179
179
  updatePosition(time, autoCenter = false) {
180
180
  const precisionSeconds = 0.3;
@@ -214,19 +214,17 @@ class MultiTrack extends EventEmitter {
214
214
  }
215
215
  onDrag(index, delta) {
216
216
  this.setIsDragging();
217
- const newTime = this.tracks[index].startPosition + delta * this.maxDuration;
218
- this.onMove(index, newTime);
219
- }
220
- onMove(index, newStartPosition) {
221
217
  const track = this.tracks[index];
222
218
  if (!track.draggable)
223
219
  return;
220
+ const newStartPosition = track.startPosition + delta * this.maxDuration;
224
221
  const mainIndex = this.tracks.findIndex((item) => item.url && !item.draggable);
225
222
  const mainTrack = this.tracks[mainIndex];
226
223
  const minStart = (mainTrack ? mainTrack.startPosition : 0) - this.durations[index];
227
224
  const maxStart = mainTrack ? mainTrack.startPosition + this.durations[mainIndex] : this.maxDuration;
228
225
  if (newStartPosition >= minStart && newStartPosition <= maxStart) {
229
226
  track.startPosition = newStartPosition;
227
+ this.initDurations(this.durations);
230
228
  this.rendering.setContainerOffsets();
231
229
  this.updatePosition(this.currentTime);
232
230
  this.emit('start-position-change', { id: track.id, startPosition: newStartPosition });