wavesurfer.js 7.0.0-alpha.5 → 7.0.0-alpha.7

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/README.md CHANGED
@@ -4,6 +4,11 @@ An experimental rewrite of [wavesufer.js](https://github.com/wavesurfer-js/waves
4
4
 
5
5
  <img alt="screenshot" src="https://user-images.githubusercontent.com/381895/225539680-fc724acd-8657-458e-a558-ff1c6758ba30.png" width="800" />
6
6
 
7
+ Try it out:
8
+ ```
9
+ npm install --save wavesurfer.js@alpha
10
+ ```
11
+
7
12
  ## Goals
8
13
 
9
14
  * TypeScript API
@@ -1,10 +1,11 @@
1
1
  import EventEmitter, { type GeneralEventTypes } from './event-emitter.js';
2
2
  import type { WaveSurfer, WaveSurferPluginParams } from './index.js';
3
- export declare class BasePlugin<EventTypes extends GeneralEventTypes> extends EventEmitter<EventTypes> {
3
+ export declare class BasePlugin<EventTypes extends GeneralEventTypes, Options> extends EventEmitter<EventTypes> {
4
4
  protected wavesurfer: WaveSurfer;
5
5
  protected container: HTMLElement;
6
6
  protected subscriptions: (() => void)[];
7
- constructor(params: WaveSurferPluginParams);
7
+ protected options: Options;
8
+ constructor(params: WaveSurferPluginParams, options: Options);
8
9
  destroy(): void;
9
10
  }
10
11
  export default BasePlugin;
@@ -1,10 +1,11 @@
1
1
  import EventEmitter from './event-emitter.js';
2
2
  export class BasePlugin extends EventEmitter {
3
- constructor(params) {
3
+ constructor(params, options) {
4
4
  super();
5
5
  this.subscriptions = [];
6
6
  this.wavesurfer = params.wavesurfer;
7
7
  this.container = params.container;
8
+ this.options = options;
8
9
  }
9
10
  destroy() {
10
11
  this.subscriptions.forEach((unsubscribe) => unsubscribe());
package/dist/decoder.d.ts CHANGED
@@ -2,10 +2,7 @@ declare class Decoder {
2
2
  audioCtx: AudioContext | null;
3
3
  private initAudioContext;
4
4
  constructor();
5
- decode(audioData: ArrayBuffer): Promise<{
6
- duration: number;
7
- channelData: Float32Array[];
8
- }>;
5
+ decode(audioData: ArrayBuffer): Promise<AudioBuffer>;
9
6
  destroy(): void;
10
7
  }
11
8
  export default Decoder;
package/dist/decoder.js CHANGED
@@ -31,15 +31,7 @@ class Decoder {
31
31
  if (!this.audioCtx) {
32
32
  throw new Error('AudioContext is not initialized');
33
33
  }
34
- const buffer = yield this.audioCtx.decodeAudioData(audioData);
35
- const channelData = [buffer.getChannelData(0)];
36
- if (buffer.numberOfChannels > 1) {
37
- channelData.push(buffer.getChannelData(1));
38
- }
39
- return {
40
- duration: buffer.duration,
41
- channelData,
42
- };
34
+ return this.audioCtx.decodeAudioData(audioData);
43
35
  });
44
36
  }
45
37
  destroy() {
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@ export type WaveSurferOptions = {
9
9
  waveColor?: string;
10
10
  /** The color of the progress mask */
11
11
  progressColor?: string;
12
+ /** The color of the playpack cursort */
13
+ cursorColor?: string;
12
14
  /** If set, the waveform will be rendered in bars like so: ▁ ▂ ▇ ▃ ▅ ▂ */
13
15
  barWidth?: number;
14
16
  /** Spacing between bars in pixels */
@@ -17,16 +19,20 @@ export type WaveSurferOptions = {
17
19
  barRadius?: number;
18
20
  /** Minimum pixels per second of audio (zoom) */
19
21
  minPxPerSec?: number;
22
+ /** Stretch the waveform to fill the container, true by default */
23
+ fillParent?: boolean;
20
24
  /** Audio URL */
21
25
  url?: string;
22
26
  /** Pre-computed audio data */
23
- channelData?: Float32Array[];
27
+ peaks?: Float32Array[];
24
28
  /** Pre-computed duration */
25
29
  duration?: number;
26
30
  /** Use an existing media element instead of creating one */
27
31
  media?: HTMLMediaElement;
28
32
  /** Play the audio on load */
29
33
  autoplay?: boolean;
34
+ /** Is the waveform clickable? */
35
+ interactive?: boolean;
30
36
  };
31
37
  export type WaveSurferEvents = {
32
38
  decode: {
@@ -35,14 +41,21 @@ export type WaveSurferEvents = {
35
41
  canplay: {
36
42
  duration: number;
37
43
  };
44
+ ready: {
45
+ duration: number;
46
+ };
38
47
  play: void;
39
48
  pause: void;
40
- audioprocess: {
49
+ timeupdate: {
41
50
  currentTime: number;
42
51
  };
43
- seek: {
44
- time: number;
52
+ seeking: {
53
+ currentTime: number;
54
+ };
55
+ seekClick: {
56
+ currentTime: number;
45
57
  };
58
+ destroy: void;
46
59
  };
47
60
  export type WaveSurferPluginParams = {
48
61
  wavesurfer: WaveSurfer;
@@ -57,8 +70,7 @@ export declare class WaveSurfer extends EventEmitter<WaveSurferEvents> {
57
70
  private timer;
58
71
  private plugins;
59
72
  private subscriptions;
60
- private channelData;
61
- private duration;
73
+ private decodedData;
62
74
  /** Create a new WaveSurfer instance */
63
75
  static create(options: WaveSurferOptions): WaveSurfer;
64
76
  /** Create a new WaveSurfer instance */
@@ -66,8 +78,7 @@ export declare class WaveSurfer extends EventEmitter<WaveSurferEvents> {
66
78
  private initPlayerEvents;
67
79
  private initRendererEvents;
68
80
  private initTimerEvents;
69
- /** Unmount wavesurfer */
70
- destroy(): void;
81
+ private initReadyEvent;
71
82
  /** Load an audio file by URL, with optional pre-decoded audio data */
72
83
  load(url: string, channelData?: Float32Array[], duration?: number): Promise<void>;
73
84
  /** Zoom in or out */
@@ -97,8 +108,10 @@ export declare class WaveSurfer extends EventEmitter<WaveSurferEvents> {
97
108
  /** Set playback rate, with an optional parameter to NOT preserve the pitch if false */
98
109
  setPlaybackRate(rate: number, preservePitch?: boolean): void;
99
110
  /** Register and initialize a plugin */
100
- registerPlugin<T extends BasePlugin<GeneralEventTypes>>(CustomPlugin: new (params: WaveSurferPluginParams) => T): T;
111
+ registerPlugin<T extends BasePlugin<GeneralEventTypes, unknown>>(CustomPlugin: new (params: unknown, options: unknown) => T, options?: unknown): T;
101
112
  /** Get the decoded audio data */
102
- getAudioData(): Float32Array[] | null;
113
+ getDecodedData(): AudioBuffer | null;
114
+ /** Unmount wavesurfer */
115
+ destroy(): void;
103
116
  }
104
117
  export default WaveSurfer;
package/dist/index.js CHANGED
@@ -18,6 +18,8 @@ const defaultOptions = {
18
18
  waveColor: '#999',
19
19
  progressColor: '#555',
20
20
  minPxPerSec: 0,
21
+ fillParent: true,
22
+ interactive: true,
21
23
  };
22
24
  export class WaveSurfer extends EventEmitter {
23
25
  /** Create a new WaveSurfer instance */
@@ -30,8 +32,7 @@ export class WaveSurfer extends EventEmitter {
30
32
  super();
31
33
  this.plugins = [];
32
34
  this.subscriptions = [];
33
- this.channelData = null;
34
- this.duration = 0;
35
+ this.decodedData = null;
35
36
  this.options = Object.assign({}, defaultOptions, options);
36
37
  this.fetcher = new Fetcher();
37
38
  this.decoder = new Decoder();
@@ -45,7 +46,9 @@ export class WaveSurfer extends EventEmitter {
45
46
  height: this.options.height,
46
47
  waveColor: this.options.waveColor,
47
48
  progressColor: this.options.progressColor,
49
+ cursorColor: this.options.cursorColor,
48
50
  minPxPerSec: this.options.minPxPerSec,
51
+ fillParent: this.options.fillParent,
49
52
  barWidth: this.options.barWidth,
50
53
  barGap: this.options.barGap,
51
54
  barRadius: this.options.barRadius,
@@ -53,16 +56,17 @@ export class WaveSurfer extends EventEmitter {
53
56
  this.initPlayerEvents();
54
57
  this.initRendererEvents();
55
58
  this.initTimerEvents();
59
+ this.initReadyEvent();
56
60
  const url = this.options.url || ((_a = this.options.media) === null || _a === void 0 ? void 0 : _a.src);
57
61
  if (url) {
58
- this.load(url, this.options.channelData, this.options.duration);
62
+ this.load(url, this.options.peaks, this.options.duration);
59
63
  }
60
64
  }
61
65
  initPlayerEvents() {
62
66
  this.subscriptions.push(this.player.on('timeupdate', () => {
63
67
  const currentTime = this.getCurrentTime();
64
- this.renderer.renderProgress(currentTime / this.duration, this.isPlaying());
65
- this.emit('audioprocess', { currentTime });
68
+ this.renderer.renderProgress(currentTime / this.getDuration(), this.isPlaying());
69
+ this.emit('timeupdate', { currentTime });
66
70
  }), this.player.on('play', () => {
67
71
  this.emit('play');
68
72
  this.timer.start();
@@ -72,56 +76,80 @@ export class WaveSurfer extends EventEmitter {
72
76
  }), this.player.on('canplay', () => {
73
77
  this.emit('canplay', { duration: this.getDuration() });
74
78
  }), this.player.on('seeking', () => {
75
- this.emit('seek', { time: this.getCurrentTime() });
79
+ this.emit('seeking', { currentTime: this.getCurrentTime() });
76
80
  }));
77
81
  }
78
82
  initRendererEvents() {
79
83
  // Seek on click
80
84
  this.subscriptions.push(this.renderer.on('click', ({ relativeX }) => {
81
- const time = this.getDuration() * relativeX;
82
- this.seekTo(time);
85
+ if (this.options.interactive) {
86
+ const time = this.getDuration() * relativeX;
87
+ this.seekTo(time);
88
+ this.emit('seekClick', { currentTime: this.getCurrentTime() });
89
+ }
83
90
  }));
84
91
  }
85
92
  initTimerEvents() {
86
93
  // The timer fires every 16ms for a smooth progress animation
87
94
  this.subscriptions.push(this.timer.on('tick', () => {
88
95
  const currentTime = this.getCurrentTime();
89
- this.renderer.renderProgress(currentTime / this.duration, true);
90
- this.emit('audioprocess', { currentTime });
96
+ this.renderer.renderProgress(currentTime / this.getDuration(), true);
97
+ this.emit('timeupdate', { currentTime });
91
98
  }));
92
99
  }
93
- /** Unmount wavesurfer */
94
- destroy() {
95
- this.subscriptions.forEach((unsubscribe) => unsubscribe());
96
- this.plugins.forEach((plugin) => plugin.destroy());
97
- this.timer.destroy();
98
- this.player.destroy();
99
- this.decoder.destroy();
100
- this.renderer.destroy();
100
+ initReadyEvent() {
101
+ let isDecoded = false;
102
+ let isPlayable = false;
103
+ const emitReady = () => {
104
+ if (isDecoded && isPlayable) {
105
+ this.emit('ready', { duration: this.getDuration() });
106
+ }
107
+ };
108
+ this.subscriptions.push(this.on('decode', () => {
109
+ isDecoded = true;
110
+ emitReady();
111
+ }), this.on('canplay', () => {
112
+ isPlayable = true;
113
+ emitReady();
114
+ }), this.player.on('waiting', () => {
115
+ isPlayable = false;
116
+ isDecoded = false;
117
+ }));
101
118
  }
102
119
  /** Load an audio file by URL, with optional pre-decoded audio data */
103
120
  load(url, channelData, duration) {
104
121
  return __awaiter(this, void 0, void 0, function* () {
105
122
  this.player.loadUrl(url);
106
123
  // Fetch and decode the audio of no pre-computed audio data is provided
107
- if (channelData == null || duration == null) {
124
+ if (channelData == null) {
108
125
  const audio = yield this.fetcher.load(url);
109
126
  const data = yield this.decoder.decode(audio);
110
- channelData = data.channelData;
111
- duration = data.duration;
127
+ this.decodedData = data;
128
+ }
129
+ else {
130
+ if (!duration) {
131
+ duration =
132
+ (yield new Promise((resolve) => {
133
+ this.player.on('canplay', () => resolve(this.getDuration()), { once: true });
134
+ })) || 0;
135
+ }
136
+ this.decodedData = {
137
+ duration,
138
+ numberOfChannels: channelData.length,
139
+ sampleRate: channelData[0].length / duration,
140
+ getChannelData: (i) => channelData[i],
141
+ };
112
142
  }
113
- this.channelData = channelData;
114
- this.duration = duration;
115
- this.renderer.render(this.channelData, this.duration);
116
- this.emit('decode', { duration: this.duration });
143
+ this.renderer.render(this.decodedData);
144
+ this.emit('decode', { duration: this.decodedData.duration });
117
145
  });
118
146
  }
119
147
  /** Zoom in or out */
120
148
  zoom(minPxPerSec) {
121
- if (this.channelData == null || this.duration == null) {
149
+ if (!this.decodedData) {
122
150
  throw new Error('No audio loaded');
123
151
  }
124
- this.renderer.zoom(this.channelData, this.duration, minPxPerSec);
152
+ this.renderer.zoom(this.decodedData, minPxPerSec);
125
153
  }
126
154
  /** Start playing the audio */
127
155
  play() {
@@ -141,7 +169,8 @@ export class WaveSurfer extends EventEmitter {
141
169
  }
142
170
  /** Get the duration of the audio in seconds */
143
171
  getDuration() {
144
- return this.duration;
172
+ var _a;
173
+ return this.player.getDuration() || ((_a = this.decodedData) === null || _a === void 0 ? void 0 : _a.duration) || 0;
145
174
  }
146
175
  /** Get the current audio position in seconds */
147
176
  getCurrentTime() {
@@ -172,17 +201,27 @@ export class WaveSurfer extends EventEmitter {
172
201
  this.player.setPlaybackRate(rate, preservePitch);
173
202
  }
174
203
  /** Register and initialize a plugin */
175
- registerPlugin(CustomPlugin) {
204
+ registerPlugin(CustomPlugin, options) {
176
205
  const plugin = new CustomPlugin({
177
206
  wavesurfer: this,
178
207
  container: this.renderer.getContainer(),
179
- });
208
+ }, options);
180
209
  this.plugins.push(plugin);
181
210
  return plugin;
182
211
  }
183
212
  /** Get the decoded audio data */
184
- getAudioData() {
185
- return this.channelData;
213
+ getDecodedData() {
214
+ return this.decodedData;
215
+ }
216
+ /** Unmount wavesurfer */
217
+ destroy() {
218
+ this.emit('destroy');
219
+ this.subscriptions.forEach((unsubscribe) => unsubscribe());
220
+ this.plugins.forEach((plugin) => plugin.destroy());
221
+ this.timer.destroy();
222
+ this.player.destroy();
223
+ this.decoder.destroy();
224
+ this.renderer.destroy();
186
225
  }
187
226
  }
188
227
  export default WaveSurfer;
package/dist/player.d.ts CHANGED
@@ -6,7 +6,7 @@ declare class Player {
6
6
  media?: HTMLMediaElement;
7
7
  autoplay?: boolean;
8
8
  });
9
- on(event: keyof HTMLMediaElementEventMap, callback: () => void): () => void;
9
+ on(event: keyof HTMLMediaElementEventMap, callback: () => void, options?: AddEventListenerOptions): () => void;
10
10
  destroy(): void;
11
11
  loadUrl(src: string): void;
12
12
  getCurrentTime(): number;
@@ -14,6 +14,7 @@ declare class Player {
14
14
  pause(): void;
15
15
  isPlaying(): boolean;
16
16
  seekTo(time: number): void;
17
+ getDuration(): number;
17
18
  getVolume(): number;
18
19
  setVolume(volume: number): void;
19
20
  getMuted(): boolean;
package/dist/player.js CHANGED
@@ -19,22 +19,18 @@ class Player {
19
19
  this.media.autoplay = true;
20
20
  }
21
21
  }
22
- on(event, callback) {
23
- var _a;
24
- (_a = this.media) === null || _a === void 0 ? void 0 : _a.addEventListener(event, callback);
25
- return () => { var _a; return (_a = this.media) === null || _a === void 0 ? void 0 : _a.removeEventListener(event, callback); };
22
+ on(event, callback, options) {
23
+ this.media.addEventListener(event, callback, options);
24
+ return () => this.media.removeEventListener(event, callback);
26
25
  }
27
26
  destroy() {
28
- var _a, _b;
29
- (_a = this.media) === null || _a === void 0 ? void 0 : _a.pause();
27
+ this.media.pause();
30
28
  if (!this.isExternalMedia) {
31
- (_b = this.media) === null || _b === void 0 ? void 0 : _b.remove();
29
+ this.media.remove();
32
30
  }
33
31
  }
34
32
  loadUrl(src) {
35
- if (this.media) {
36
- this.media.src = src;
37
- }
33
+ this.media.src = src;
38
34
  }
39
35
  getCurrentTime() {
40
36
  return this.media.currentTime;
@@ -61,6 +57,9 @@ class Player {
61
57
  }
62
58
  }
63
59
  }
60
+ getDuration() {
61
+ return this.media.duration;
62
+ }
64
63
  getVolume() {
65
64
  return this.media.volume;
66
65
  }
@@ -1,5 +1,10 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
2
  import { WaveSurferPluginParams } from '../index.js';
3
+ type RegionsPluginOptions = {
4
+ dragSelection?: boolean;
5
+ draggable?: boolean;
6
+ resizable?: boolean;
7
+ };
3
8
  type Region = {
4
9
  startTime: number;
5
10
  endTime: number;
@@ -19,7 +24,7 @@ type RegionsPluginEvents = {
19
24
  region: Region;
20
25
  };
21
26
  };
22
- declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents> {
27
+ declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
23
28
  private dragStart;
24
29
  private wrapper;
25
30
  private regions;
@@ -28,8 +33,8 @@ declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents> {
28
33
  private isResizingLeft;
29
34
  private isMoving;
30
35
  /** Create an instance of RegionsPlugin */
31
- constructor(params: WaveSurferPluginParams);
32
- /** Unmounts the regions */
36
+ constructor(params: WaveSurferPluginParams, options: RegionsPluginOptions);
37
+ /** Unmount */
33
38
  destroy(): void;
34
39
  private initWrapper;
35
40
  private handleMouseDown;
@@ -1,5 +1,10 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
2
  const MIN_WIDTH = 10;
3
+ const defaultOptions = {
4
+ dragSelection: true,
5
+ draggable: true,
6
+ resizable: true,
7
+ };
3
8
  const style = (element, styles) => {
4
9
  for (const key in styles) {
5
10
  element.style[key] = styles[key] || '';
@@ -12,8 +17,8 @@ const el = (tagName, css) => {
12
17
  };
13
18
  class RegionsPlugin extends BasePlugin {
14
19
  /** Create an instance of RegionsPlugin */
15
- constructor(params) {
16
- super(params);
20
+ constructor(params, options) {
21
+ super(params, options);
17
22
  this.dragStart = NaN;
18
23
  this.regions = [];
19
24
  this.createdRegion = null;
@@ -21,7 +26,9 @@ class RegionsPlugin extends BasePlugin {
21
26
  this.isResizingLeft = false;
22
27
  this.isMoving = false;
23
28
  this.handleMouseDown = (e) => {
24
- this.dragStart = e.clientX - this.container.getBoundingClientRect().left;
29
+ if (this.options.draggable || this.options.resizable || this.options.dragSelection) {
30
+ this.dragStart = e.clientX - this.container.getBoundingClientRect().left;
31
+ }
25
32
  };
26
33
  this.handleMouseMove = (e) => {
27
34
  const dragEnd = e.clientX - this.container.getBoundingClientRect().left;
@@ -57,17 +64,17 @@ class RegionsPlugin extends BasePlugin {
57
64
  this.dragStart = NaN;
58
65
  this.container.style.pointerEvents = '';
59
66
  };
67
+ this.options = Object.assign(Object.assign({}, defaultOptions), options);
60
68
  this.wrapper = this.initWrapper();
61
- const unsubscribe = this.wavesurfer.on('decode', () => {
69
+ const unsubscribe = this.wavesurfer.once('decode', () => {
62
70
  this.container.appendChild(this.wrapper);
63
- unsubscribe();
64
71
  });
65
72
  this.subscriptions.push(unsubscribe);
66
73
  this.container.addEventListener('mousedown', this.handleMouseDown);
67
74
  document.addEventListener('mousemove', this.handleMouseMove);
68
75
  document.addEventListener('mouseup', this.handleMouseUp);
69
76
  }
70
- /** Unmounts the regions */
77
+ /** Unmount */
71
78
  destroy() {
72
79
  this.container.removeEventListener('mousedown', this.handleMouseDown);
73
80
  document.removeEventListener('mousemove', this.handleMouseMove);
@@ -87,25 +94,29 @@ class RegionsPlugin extends BasePlugin {
87
94
  });
88
95
  }
89
96
  createRegionElement(start, end, title = '') {
97
+ const noWidth = start === end;
90
98
  const div = el('div', {
91
99
  position: 'absolute',
92
100
  left: `${start}px`,
93
101
  width: `${end - start}px`,
94
102
  height: '100%',
95
- backgroundColor: 'rgba(0, 0, 0, 0.1)',
103
+ backgroundColor: noWidth ? '' : 'rgba(0, 0, 0, 0.1)',
104
+ borderRadius: '2px',
105
+ boxSizing: 'border-box',
106
+ borderLeft: '2px solid rgba(0, 0, 0, 0.5)',
107
+ borderRight: noWidth ? '' : '2px solid rgba(0, 0, 0, 0.5)',
96
108
  transition: 'background-color 0.2s ease',
97
- cursor: 'move',
109
+ cursor: this.options.draggable ? 'move' : '',
98
110
  pointerEvents: 'all',
111
+ padding: '0.2em',
99
112
  });
100
- div.title = title;
113
+ div.textContent = title;
101
114
  const leftHandle = el('div', {
102
115
  position: 'absolute',
103
116
  left: '0',
104
117
  width: '6px',
105
118
  height: '100%',
106
- borderLeft: '2px solid rgba(0, 0, 0, 0.5)',
107
- borderRadius: '2px 0 0 2px',
108
- cursor: 'ew-resize',
119
+ cursor: this.options.resizable ? 'ew-resize' : '',
109
120
  pointerEvents: 'all',
110
121
  });
111
122
  div.appendChild(leftHandle);
@@ -114,23 +125,27 @@ class RegionsPlugin extends BasePlugin {
114
125
  left: '',
115
126
  right: '0',
116
127
  borderLeft: '',
117
- borderRight: '2px solid rgba(0, 0, 0, 0.5)',
118
- borderRadius: '0 2px 2px 0',
119
128
  });
120
129
  div.appendChild(rightHandle);
121
130
  leftHandle.addEventListener('mousedown', (e) => {
131
+ if (!this.options.resizable)
132
+ return;
122
133
  e.stopPropagation();
123
134
  this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
124
135
  this.isResizingLeft = true;
125
136
  this.isMoving = false;
126
137
  });
127
138
  rightHandle.addEventListener('mousedown', (e) => {
139
+ if (!this.options.resizable)
140
+ return;
128
141
  e.stopPropagation();
129
142
  this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
130
143
  this.isResizingLeft = false;
131
144
  this.isMoving = false;
132
145
  });
133
146
  div.addEventListener('mousedown', () => {
147
+ if (!this.options.draggable)
148
+ return;
134
149
  this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
135
150
  this.isMoving = true;
136
151
  });
@@ -146,6 +161,8 @@ class RegionsPlugin extends BasePlugin {
146
161
  createRegion(start, end, title = '') {
147
162
  const duration = this.wavesurfer.getDuration();
148
163
  const width = this.wrapper.clientWidth;
164
+ start = Math.max(0, Math.min(start, width - 1));
165
+ end = Math.max(0, Math.min(end, width - 1));
149
166
  return {
150
167
  element: this.createRegionElement(start, end, title),
151
168
  start,
@@ -160,12 +177,15 @@ class RegionsPlugin extends BasePlugin {
160
177
  this.emit('region-created', { region });
161
178
  }
162
179
  updateRegion(region, start, end) {
180
+ const width = this.wrapper.clientWidth;
163
181
  if (start != null) {
164
- region.start = start !== null && start !== void 0 ? start : region.start;
182
+ start = Math.max(0, Math.min(start, width));
183
+ region.start = start;
165
184
  region.element.style.left = `${region.start}px`;
166
185
  region.startTime = (start / this.wrapper.clientWidth) * this.wavesurfer.getDuration();
167
186
  }
168
187
  if (end != null) {
188
+ end = Math.max(0, Math.min(end, width));
169
189
  region.end = end;
170
190
  region.element.style.width = `${region.end - region.start}px`;
171
191
  region.endTime = (end / this.wrapper.clientWidth) * this.wavesurfer.getDuration();
@@ -187,7 +207,7 @@ class RegionsPlugin extends BasePlugin {
187
207
  }
188
208
  /** Set the background color of a region */
189
209
  setRegionColor(region, color) {
190
- region.element.style.backgroundColor = color;
210
+ region.element.style[region.startTime === region.endTime ? 'borderColor' : 'backgroundColor'] = color;
191
211
  }
192
212
  }
193
213
  export default RegionsPlugin;
@@ -0,0 +1,28 @@
1
+ import BasePlugin from '../base-plugin.js';
2
+ import { WaveSurferPluginParams } from '../index.js';
3
+ type TimelinePluginOptions = {
4
+ height?: number;
5
+ timeInterval?: number;
6
+ primaryLabelInterval?: number;
7
+ secondaryLabelInterval?: number;
8
+ };
9
+ declare const defaultOptions: {
10
+ height: number;
11
+ };
12
+ type TimelinePluginEvents = {
13
+ ready: void;
14
+ };
15
+ declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
16
+ private wrapper;
17
+ protected options: TimelinePluginOptions & typeof defaultOptions;
18
+ constructor(params: WaveSurferPluginParams, options: TimelinePluginOptions);
19
+ /** Unmount */
20
+ destroy(): void;
21
+ private initWrapper;
22
+ private formatTime;
23
+ private defaultTimeInterval;
24
+ defaultPrimaryLabelInterval(pxPerSec: number): number;
25
+ defaultSecondaryLabelInterval(pxPerSec: number): number;
26
+ private initTimeline;
27
+ }
28
+ export default TimelinePlugin;
@@ -0,0 +1,117 @@
1
+ import BasePlugin from '../base-plugin.js';
2
+ const defaultOptions = {
3
+ height: 20,
4
+ };
5
+ class TimelinePlugin extends BasePlugin {
6
+ constructor(params, options) {
7
+ super(params, options);
8
+ this.options = Object.assign({}, defaultOptions, options);
9
+ this.wrapper = this.initWrapper();
10
+ this.container.appendChild(this.wrapper);
11
+ this.subscriptions.push(this.wavesurfer.on('decode', ({ duration }) => {
12
+ this.initTimeline(duration);
13
+ }));
14
+ }
15
+ /** Unmount */
16
+ destroy() {
17
+ this.wrapper.remove();
18
+ super.destroy();
19
+ }
20
+ initWrapper() {
21
+ return document.createElement('div');
22
+ }
23
+ formatTime(seconds) {
24
+ if (seconds / 60 > 1) {
25
+ // calculate minutes and seconds from seconds count
26
+ const minutes = Math.round(seconds / 60);
27
+ seconds = Math.round(seconds % 60);
28
+ const paddedSeconds = `${seconds < 10 ? '0' : ''}${seconds}`;
29
+ return `${minutes}:${paddedSeconds}`;
30
+ }
31
+ const rounded = Math.round(seconds * 1000) / 1000;
32
+ return `${rounded}`;
33
+ }
34
+ // Return how many seconds should be between each notch
35
+ defaultTimeInterval(pxPerSec) {
36
+ if (pxPerSec >= 25) {
37
+ return 1;
38
+ }
39
+ else if (pxPerSec * 5 >= 25) {
40
+ return 5;
41
+ }
42
+ else if (pxPerSec * 15 >= 25) {
43
+ return 15;
44
+ }
45
+ return Math.ceil(0.5 / pxPerSec) * 60;
46
+ }
47
+ // Return the cadence of notches that get labels in the primary color.
48
+ defaultPrimaryLabelInterval(pxPerSec) {
49
+ if (pxPerSec >= 25) {
50
+ return 10;
51
+ }
52
+ else if (pxPerSec * 5 >= 25) {
53
+ return 6;
54
+ }
55
+ else if (pxPerSec * 15 >= 25) {
56
+ return 4;
57
+ }
58
+ return 4;
59
+ }
60
+ // Return the cadence of notches that get labels in the secondary color.
61
+ defaultSecondaryLabelInterval(pxPerSec) {
62
+ if (pxPerSec >= 25) {
63
+ return 5;
64
+ }
65
+ else if (pxPerSec * 5 >= 25) {
66
+ return 2;
67
+ }
68
+ else if (pxPerSec * 15 >= 25) {
69
+ return 2;
70
+ }
71
+ return 2;
72
+ }
73
+ initTimeline(duration) {
74
+ var _a, _b, _c;
75
+ const width = Math.round(this.wrapper.scrollWidth * devicePixelRatio);
76
+ const pxPerSec = width / duration;
77
+ const timeInterval = (_a = this.options.timeInterval) !== null && _a !== void 0 ? _a : this.defaultTimeInterval(pxPerSec);
78
+ const primaryLabelInterval = (_b = this.options.primaryLabelInterval) !== null && _b !== void 0 ? _b : this.defaultPrimaryLabelInterval(pxPerSec);
79
+ const secondaryLabelInterval = (_c = this.options.secondaryLabelInterval) !== null && _c !== void 0 ? _c : this.defaultSecondaryLabelInterval(pxPerSec);
80
+ const timeline = document.createElement('div');
81
+ timeline.setAttribute('style', `
82
+ height: ${this.options.height}px;
83
+ overflow: hidden;
84
+ display: flex;
85
+ justify-content: space-between;
86
+ align-items: flex-end;
87
+ font-size: ${this.options.height / 2}px;
88
+ `);
89
+ const notchEl = document.createElement('div');
90
+ notchEl.setAttribute('style', `
91
+ width: 1px;
92
+ height: 50%;
93
+ display: flex;
94
+ flex-direction: column;
95
+ justify-content: flex-end;
96
+ overflow: visible;
97
+ border-left: 1px solid currentColor;
98
+ opacity: 0.25;
99
+ `);
100
+ for (let i = 0; i < duration; i += timeInterval) {
101
+ const notch = notchEl.cloneNode();
102
+ const isPrimary = i % primaryLabelInterval === 0;
103
+ const isSecondary = i % secondaryLabelInterval === 0;
104
+ if (isPrimary || isSecondary) {
105
+ notch.style.height = '100%';
106
+ notch.style.textIndent = '3px';
107
+ notch.textContent = this.formatTime(i);
108
+ if (isPrimary)
109
+ notch.style.opacity = '1';
110
+ }
111
+ timeline.appendChild(notch);
112
+ }
113
+ this.wrapper.appendChild(timeline);
114
+ this.emit('ready');
115
+ }
116
+ }
117
+ export default TimelinePlugin;
@@ -4,7 +4,9 @@ type RendererOptions = {
4
4
  height: number;
5
5
  waveColor: string;
6
6
  progressColor: string;
7
+ cursorColor?: string;
7
8
  minPxPerSec: number;
9
+ fillParent: boolean;
8
10
  barWidth?: number;
9
11
  barGap?: number;
10
12
  barRadius?: number;
@@ -29,8 +31,8 @@ declare class Renderer extends EventEmitter<RendererEvents> {
29
31
  private delay;
30
32
  private renderPeaks;
31
33
  private createProgressMask;
32
- render(channelData: Float32Array[], duration: number): void;
33
- zoom(channelData: Float32Array[], duration: number, minPxPerSec: number): void;
34
+ render(audioData: AudioBuffer): void;
35
+ zoom(audioData: AudioBuffer, minPxPerSec: number): void;
34
36
  renderProgress(progress: number, autoCenter?: boolean): void;
35
37
  }
36
38
  export default Renderer;
package/dist/renderer.js CHANGED
@@ -32,21 +32,19 @@ class Renderer extends EventEmitter {
32
32
  }
33
33
  :host .scroll {
34
34
  overflow-x: auto;
35
- overflow-y: visible;
35
+ overflow-y: hidden;
36
36
  width: 100%;
37
- height: ${this.options.height}px;
38
37
  position: relative;
39
38
  }
40
39
  :host .wrapper {
41
40
  position: relative;
42
41
  width: fit-content;
43
42
  min-width: 100%;
44
- height: 100%;
45
43
  z-index: 2;
46
44
  }
47
45
  :host canvas {
48
46
  display: block;
49
- height: 100%;
47
+ height: ${this.options.height}px;
50
48
  min-width: 100%;
51
49
  image-rendering: pixelated;
52
50
  }
@@ -55,7 +53,6 @@ class Renderer extends EventEmitter {
55
53
  z-index: 2;
56
54
  top: 0;
57
55
  left: 0;
58
- height: 100%;
59
56
  pointer-events: none;
60
57
  clip-path: inset(100%);
61
58
  }
@@ -65,7 +62,7 @@ class Renderer extends EventEmitter {
65
62
  top: 0;
66
63
  left: 0;
67
64
  height: 100%;
68
- border-right: 1px solid ${this.options.progressColor};
65
+ border-right: 1px solid ${this.options.cursorColor || this.options.progressColor};
69
66
  margin-left: -1px;
70
67
  }
71
68
  </style>
@@ -129,6 +126,10 @@ class Renderer extends EventEmitter {
129
126
  let prevLeft = 0;
130
127
  let prevRight = 0;
131
128
  ctx.beginPath();
129
+ ctx.fillStyle = this.options.waveColor;
130
+ // Firefox shim until 2023.04.11
131
+ if (!ctx.roundRect)
132
+ ctx.roundRect = ctx.fillRect;
132
133
  for (let i = start; i < end; i++) {
133
134
  const barIndex = Math.round(i * barIndexScale);
134
135
  if (barIndex > prevX) {
@@ -150,7 +151,6 @@ class Renderer extends EventEmitter {
150
151
  prevRight = rightValue < 0 ? -rightValue : rightValue;
151
152
  }
152
153
  }
153
- ctx.fillStyle = this.options.waveColor;
154
154
  ctx.fill();
155
155
  ctx.closePath();
156
156
  };
@@ -197,24 +197,29 @@ class Renderer extends EventEmitter {
197
197
  // This rectangle acts as a mask thanks to the composition method
198
198
  progressCtx.fillRect(0, 0, this.progressCanvas.width, this.progressCanvas.height);
199
199
  }
200
- render(channelData, duration) {
200
+ render(audioData) {
201
201
  // Determine the width of the canvas
202
202
  const { devicePixelRatio } = window;
203
- const parentWidth = this.scrollContainer.clientWidth * devicePixelRatio;
204
- const scrollWidth = duration * this.options.minPxPerSec;
205
- const scrollParent = scrollWidth > parentWidth;
206
- const width = scrollParent ? scrollWidth : parentWidth;
203
+ const parentWidth = this.options.fillParent ? this.scrollContainer.clientWidth * devicePixelRatio : 0;
204
+ const scrollWidth = audioData.duration * this.options.minPxPerSec;
205
+ const isScrolling = scrollWidth > parentWidth;
206
+ const width = isScrolling ? scrollWidth : parentWidth;
207
207
  const { height } = this.options;
208
208
  this.mainCanvas.width = width;
209
209
  this.mainCanvas.height = height;
210
- this.mainCanvas.style.width = scrollParent ? Math.round(scrollWidth / devicePixelRatio) + 'px' : '100%';
210
+ this.mainCanvas.style.width = Math.floor(scrollWidth / devicePixelRatio) + 'px';
211
+ // First two channels are used
212
+ const channelData = [audioData.getChannelData(0)];
213
+ if (audioData.numberOfChannels > 1) {
214
+ channelData.push(audioData.getChannelData(1));
215
+ }
211
216
  this.renderPeaks(channelData, width, height);
212
217
  }
213
- zoom(channelData, duration, minPxPerSec) {
218
+ zoom(audioData, minPxPerSec) {
214
219
  // Remember the current cursor position
215
220
  const oldCursorPosition = this.cursor.getBoundingClientRect().left;
216
221
  this.options.minPxPerSec = minPxPerSec;
217
- this.render(channelData, duration);
222
+ this.render(audioData);
218
223
  // Adjust the scroll position so that the cursor stays in the same place
219
224
  const newCursortPosition = this.cursor.getBoundingClientRect().left;
220
225
  this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Regions=t():e.Regions=t()}(WaveSurfer,(()=>(()=>{"use strict";var e={d:(t,i)=>{for(var n in i)e.o(i,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:i[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>r});const i=class{constructor(){this.eventTarget=new EventTarget}emit(e,t){const i=new CustomEvent(String(e),{detail:t});this.eventTarget.dispatchEvent(i)}on(e,t,i){const n=e=>{e instanceof CustomEvent&&t(e.detail)},s=String(e);return this.eventTarget.addEventListener(s,n,{once:i}),()=>this.eventTarget.removeEventListener(s,n)}once(e,t){return this.on(e,t,!0)}},n=class extends i{constructor(e){super(),this.subscriptions=[],this.wavesurfer=e.wavesurfer,this.container=e.container}destroy(){this.subscriptions.forEach((e=>e()))}},s=(e,t)=>{for(const i in t)e.style[i]=t[i]||""},o=(e,t)=>{const i=document.createElement(e);return s(i,t),i},r=class extends n{constructor(e){super(e),this.dragStart=NaN,this.regions=[],this.createdRegion=null,this.modifiedRegion=null,this.isResizingLeft=!1,this.isMoving=!1,this.handleMouseDown=e=>{this.dragStart=e.clientX-this.container.getBoundingClientRect().left},this.handleMouseMove=e=>{const t=e.clientX-this.container.getBoundingClientRect().left;if(this.modifiedRegion&&this.isMoving)return this.moveRegion(this.modifiedRegion,t-this.dragStart),void(this.dragStart=t);if(this.modifiedRegion)this.updateRegion(this.modifiedRegion,this.isResizingLeft?t:void 0,this.isResizingLeft?void 0:t);else if(!isNaN(this.dragStart)){const t=e.clientX-this.wrapper.getBoundingClientRect().left;t-this.dragStart>=10&&(this.createdRegion?this.updateRegion(this.createdRegion,this.dragStart,t):(this.container.style.pointerEvents="none",this.createdRegion=this.createRegion(this.dragStart,t)))}},this.handleMouseUp=()=>{this.createdRegion&&(this.addRegion(this.createdRegion),this.createdRegion=null),this.modifiedRegion=null,this.isMoving=!1,this.dragStart=NaN,this.container.style.pointerEvents=""},this.wrapper=this.initWrapper();const t=this.wavesurfer.on("decode",(()=>{this.container.appendChild(this.wrapper),t()}));this.subscriptions.push(t),this.container.addEventListener("mousedown",this.handleMouseDown),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp)}destroy(){this.container.removeEventListener("mousedown",this.handleMouseDown),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp,!0),this.wrapper.remove(),super.destroy()}initWrapper(){return o("div",{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",zIndex:"3",pointerEvents:"none"})}createRegionElement(e,t,i=""){const n=o("div",{position:"absolute",left:`${e}px`,width:t-e+"px",height:"100%",backgroundColor:"rgba(0, 0, 0, 0.1)",transition:"background-color 0.2s ease",cursor:"move",pointerEvents:"all"});n.title=i;const r=o("div",{position:"absolute",left:"0",width:"6px",height:"100%",borderLeft:"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"2px 0 0 2px",cursor:"ew-resize",pointerEvents:"all"});n.appendChild(r);const d=r.cloneNode();return s(d,{left:"",right:"0",borderLeft:"",borderRight:"2px solid rgba(0, 0, 0, 0.5)",borderRadius:"0 2px 2px 0"}),n.appendChild(d),r.addEventListener("mousedown",(e=>{e.stopPropagation(),this.modifiedRegion=this.regions.find((e=>e.element===n))||null,this.isResizingLeft=!0,this.isMoving=!1})),d.addEventListener("mousedown",(e=>{e.stopPropagation(),this.modifiedRegion=this.regions.find((e=>e.element===n))||null,this.isResizingLeft=!1,this.isMoving=!1})),n.addEventListener("mousedown",(()=>{this.modifiedRegion=this.regions.find((e=>e.element===n))||null,this.isMoving=!0})),n.addEventListener("click",(()=>{const e=this.regions.find((e=>e.element===n));e&&this.emit("region-clicked",{region:e})})),this.wrapper.appendChild(n),n}createRegion(e,t,i=""){const n=this.wavesurfer.getDuration(),s=this.wrapper.clientWidth;return{element:this.createRegionElement(e,t,i),start:e,end:t,startTime:e/s*n,endTime:t/s*n,title:i}}addRegion(e){this.regions.push(e),this.emit("region-created",{region:e})}updateRegion(e,t,i){null!=t&&(e.start=null!=t?t:e.start,e.element.style.left=`${e.start}px`,e.startTime=t/this.wrapper.clientWidth*this.wavesurfer.getDuration()),null!=i&&(e.end=i,e.element.style.width=e.end-e.start+"px",e.endTime=i/this.wrapper.clientWidth*this.wavesurfer.getDuration()),this.emit("region-updated",{region:e})}moveRegion(e,t){this.updateRegion(e,e.start+t,e.end+t)}add(e,t,i=""){const n=this.wavesurfer.getDuration(),s=this.wrapper.clientWidth,o=e/n*s,r=t/n*s,d=this.createRegion(o,r,i);return this.addRegion(d),d}setRegionColor(e,t){e.element.style.backgroundColor=t}};return t.default})()));
1
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Regions=t():e.Regions=t()}(WaveSurfer,(()=>(()=>{"use strict";var e={d:(t,i)=>{for(var n in i)e.o(i,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:i[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>a});const i=class{constructor(){this.eventTarget=new EventTarget}emit(e,t){const i=new CustomEvent(String(e),{detail:t});this.eventTarget.dispatchEvent(i)}on(e,t,i){const n=e=>{e instanceof CustomEvent&&t(e.detail)},s=String(e);return this.eventTarget.addEventListener(s,n,{once:i}),()=>this.eventTarget.removeEventListener(s,n)}once(e,t){return this.on(e,t,!0)}},n=class extends i{constructor(e,t){super(),this.subscriptions=[],this.wavesurfer=e.wavesurfer,this.container=e.container,this.options=t}destroy(){this.subscriptions.forEach((e=>e()))}},s={dragSelection:!0,draggable:!0,resizable:!0},o=(e,t)=>{for(const i in t)e.style[i]=t[i]||""},r=(e,t)=>{const i=document.createElement(e);return o(i,t),i},a=class extends n{constructor(e,t){super(e,t),this.dragStart=NaN,this.regions=[],this.createdRegion=null,this.modifiedRegion=null,this.isResizingLeft=!1,this.isMoving=!1,this.handleMouseDown=e=>{(this.options.draggable||this.options.resizable||this.options.dragSelection)&&(this.dragStart=e.clientX-this.container.getBoundingClientRect().left)},this.handleMouseMove=e=>{const t=e.clientX-this.container.getBoundingClientRect().left;if(this.modifiedRegion&&this.isMoving)return this.moveRegion(this.modifiedRegion,t-this.dragStart),void(this.dragStart=t);if(this.modifiedRegion)this.updateRegion(this.modifiedRegion,this.isResizingLeft?t:void 0,this.isResizingLeft?void 0:t);else if(!isNaN(this.dragStart)){const t=e.clientX-this.wrapper.getBoundingClientRect().left;t-this.dragStart>=10&&(this.createdRegion?this.updateRegion(this.createdRegion,this.dragStart,t):(this.container.style.pointerEvents="none",this.createdRegion=this.createRegion(this.dragStart,t)))}},this.handleMouseUp=()=>{this.createdRegion&&(this.addRegion(this.createdRegion),this.createdRegion=null),this.modifiedRegion=null,this.isMoving=!1,this.dragStart=NaN,this.container.style.pointerEvents=""},this.options=Object.assign(Object.assign({},s),t),this.wrapper=this.initWrapper();const i=this.wavesurfer.once("decode",(()=>{this.container.appendChild(this.wrapper)}));this.subscriptions.push(i),this.container.addEventListener("mousedown",this.handleMouseDown),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp)}destroy(){this.container.removeEventListener("mousedown",this.handleMouseDown),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp,!0),this.wrapper.remove(),super.destroy()}initWrapper(){return r("div",{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",zIndex:"3",pointerEvents:"none"})}createRegionElement(e,t,i=""){const n=e===t,s=r("div",{position:"absolute",left:`${e}px`,width:t-e+"px",height:"100%",backgroundColor:n?"":"rgba(0, 0, 0, 0.1)",borderRadius:"2px",boxSizing:"border-box",borderLeft:"2px solid rgba(0, 0, 0, 0.5)",borderRight:n?"":"2px solid rgba(0, 0, 0, 0.5)",transition:"background-color 0.2s ease",cursor:this.options.draggable?"move":"",pointerEvents:"all",padding:"0.2em"});s.textContent=i;const a=r("div",{position:"absolute",left:"0",width:"6px",height:"100%",cursor:this.options.resizable?"ew-resize":"",pointerEvents:"all"});s.appendChild(a);const d=a.cloneNode();return o(d,{left:"",right:"0",borderLeft:""}),s.appendChild(d),a.addEventListener("mousedown",(e=>{this.options.resizable&&(e.stopPropagation(),this.modifiedRegion=this.regions.find((e=>e.element===s))||null,this.isResizingLeft=!0,this.isMoving=!1)})),d.addEventListener("mousedown",(e=>{this.options.resizable&&(e.stopPropagation(),this.modifiedRegion=this.regions.find((e=>e.element===s))||null,this.isResizingLeft=!1,this.isMoving=!1)})),s.addEventListener("mousedown",(()=>{this.options.draggable&&(this.modifiedRegion=this.regions.find((e=>e.element===s))||null,this.isMoving=!0)})),s.addEventListener("click",(()=>{const e=this.regions.find((e=>e.element===s));e&&this.emit("region-clicked",{region:e})})),this.wrapper.appendChild(s),s}createRegion(e,t,i=""){const n=this.wavesurfer.getDuration(),s=this.wrapper.clientWidth;return e=Math.max(0,Math.min(e,s-1)),t=Math.max(0,Math.min(t,s-1)),{element:this.createRegionElement(e,t,i),start:e,end:t,startTime:e/s*n,endTime:t/s*n,title:i}}addRegion(e){this.regions.push(e),this.emit("region-created",{region:e})}updateRegion(e,t,i){const n=this.wrapper.clientWidth;null!=t&&(t=Math.max(0,Math.min(t,n)),e.start=t,e.element.style.left=`${e.start}px`,e.startTime=t/this.wrapper.clientWidth*this.wavesurfer.getDuration()),null!=i&&(i=Math.max(0,Math.min(i,n)),e.end=i,e.element.style.width=e.end-e.start+"px",e.endTime=i/this.wrapper.clientWidth*this.wavesurfer.getDuration()),this.emit("region-updated",{region:e})}moveRegion(e,t){this.updateRegion(e,e.start+t,e.end+t)}add(e,t,i=""){const n=this.wavesurfer.getDuration(),s=this.wrapper.clientWidth,o=e/n*s,r=t/n*s,a=this.createRegion(o,r,i);return this.addRegion(a),a}setRegionColor(e,t){e.element.style[e.startTime===e.endTime?"borderColor":"backgroundColor"]=t}};return t.default})()));
@@ -1 +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.WaveSurfer=e():t.WaveSurfer=e()}(this,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>a});const i=class{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 n=t=>{t instanceof CustomEvent&&e(t.detail)},s=String(t);return this.eventTarget.addEventListener(s,n,{once:i}),()=>this.eventTarget.removeEventListener(s,n)}once(t,e){return this.on(t,e,!0)}};const n=class extends i{constructor(t){super(),this.timeout=null,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"),n=i.attachShadow({mode:"open"});n.innerHTML=`\n <style>\n :host {\n user-select: none;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: visible;\n width: 100%;\n height: ${this.options.height}px;\n position: relative;\n }\n :host .wrapper {\n position: relative;\n width: fit-content;\n min-width: 100%;\n height: 100%;\n z-index: 2;\n }\n :host canvas {\n display: block;\n height: 100%;\n min-width: 100%;\n image-rendering: pixelated;\n }\n :host .progress {\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n height: 100%;\n pointer-events: none;\n clip-path: inset(100%);\n }\n :host .cursor {\n position: absolute;\n z-index: 3;\n top: 0;\n left: 0;\n height: 100%;\n border-right: 1px solid ${this.options.progressColor};\n margin-left: -1px;\n }\n </style>\n\n <div class="scroll">\n <div class="wrapper">\n <canvas></canvas>\n <canvas class="progress"></canvas>\n <div class="cursor"></div>\n </div>\n </div>\n `,this.container=i,this.scrollContainer=n.querySelector(".scroll"),this.mainCanvas=n.querySelector("canvas"),this.ctx=this.mainCanvas.getContext("2d",{desynchronized:!0}),this.progressCanvas=n.querySelector(".progress"),this.cursor=n.querySelector(".cursor"),e.appendChild(i),this.mainCanvas.addEventListener("click",(t=>{const e=this.mainCanvas.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",{relativeX:i})}))}getContainer(){return this.scrollContainer.querySelector(".wrapper")}destroy(){this.container.remove()}delay(t,e=100){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}renderPeaks(t,e,i){var n,s,r,o,a;return s=this,r=void 0,a=function*(){const{devicePixelRatio:s}=window,{ctx:r}=this,o=null!=this.options.barWidth?this.options.barWidth*s:1,a=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?o/2:0,h=null!==(n=this.options.barRadius)&&void 0!==n?n:0,l=t[0],c=l.length,d=Math.floor(e/(o+a))/c,u=i/2,p=1===t.length,m=p?l:t[1],v=p&&m.some((t=>t<0)),y=(t,e)=>{let i=0,n=0,s=0;r.beginPath();for(let c=t;c<e;c++){const t=Math.round(c*d);if(t>i){const e=Math.round(n*u),l=Math.round(s*u);r.roundRect(i*(o+a),u-e,o,e+l||1,h),i=t,n=0,s=0}const e=v?l[c]:Math.abs(l[c]),p=v?m[c]:Math.abs(m[c]);e>n&&(n=e),(v?p<-s:p>s)&&(s=p<0?-p:p)}r.fillStyle=this.options.waveColor,r.fill(),r.closePath()};r.clearRect(0,0,e,i);const{scrollLeft:g,scrollWidth:f,clientWidth:C}=this.scrollContainer,w=c/f,b=Math.floor(g*w),x=Math.ceil((g+C)*w);y(b,x),this.createProgressMask(),b>0&&(yield this.delay((()=>{y(0,b)}))),x<c&&(yield this.delay((()=>{y(x,c)}))),this.delay((()=>{this.createProgressMask()}))},new((o=void 0)||(o=Promise))((function(t,e){function i(t){try{h(a.next(t))}catch(t){e(t)}}function n(t){try{h(a.throw(t))}catch(t){e(t)}}function h(e){var s;e.done?t(e.value):(s=e.value,s instanceof o?s:new o((function(t){t(s)}))).then(i,n)}h((a=a.apply(s,r||[])).next())}))}createProgressMask(){const t=this.progressCanvas.getContext("2d",{desynchronized:!0});this.progressCanvas.width=this.mainCanvas.width,this.progressCanvas.height=this.mainCanvas.height,this.progressCanvas.style.width=this.mainCanvas.style.width,this.progressCanvas.style.height=this.mainCanvas.style.height,t.drawImage(this.mainCanvas,0,0),t.globalCompositeOperation="source-in",t.fillStyle=this.options.progressColor,t.fillRect(0,0,this.progressCanvas.width,this.progressCanvas.height)}render(t,e){const{devicePixelRatio:i}=window,n=this.scrollContainer.clientWidth*i,s=e*this.options.minPxPerSec,r=s>n,o=r?s:n,{height:a}=this.options;this.mainCanvas.width=o,this.mainCanvas.height=a,this.mainCanvas.style.width=r?Math.round(s/i)+"px":"100%",this.renderPeaks(t,o,a)}zoom(t,e,i){const n=this.cursor.getBoundingClientRect().left;this.options.minPxPerSec=i,this.render(t,e);const s=this.cursor.getBoundingClientRect().left;this.scrollContainer.scrollLeft+=s-n}renderProgress(t,e=!1){if(this.progressCanvas.style.clipPath=`inset(0 ${100-100*t}% 0 0)`,this.cursor.style.left=100*t+"%",e){const e=this.container.clientWidth/2,i=this.mainCanvas.clientWidth;i*t>=e&&(this.scrollContainer.scrollLeft=i*t-e)}}},s=class extends i{constructor(){super(...arguments),this.unsubscribe=()=>{}}start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}};const r={height:128,waveColor:"#999",progressColor:"#555",minPxPerSec:0};class o extends i{static create(t){return new o(t)}constructor(t){var e;super(),this.plugins=[],this.subscriptions=[],this.channelData=null,this.duration=0,this.options=Object.assign({},r,t),this.fetcher=new class{load(t){return e=this,i=void 0,s=function*(){return fetch(t).then((t=>t.arrayBuffer()))},new((n=void 0)||(n=Promise))((function(t,r){function o(t){try{h(s.next(t))}catch(t){r(t)}}function a(t){try{h(s.throw(t))}catch(t){r(t)}}function h(e){var i;e.done?t(e.value):(i=e.value,i instanceof n?i:new n((function(t){t(i)}))).then(o,a)}h((s=s.apply(e,i||[])).next())}));var e,i,n,s}},this.decoder=new class{initAudioContext(t){this.audioCtx=new AudioContext({latencyHint:"playback",sampleRate:t})}constructor(){this.audioCtx=null;try{this.initAudioContext(3e3)}catch(t){this.initAudioContext(8e3)}}decode(t){return e=this,i=void 0,s=function*(){if(!this.audioCtx)throw new Error("AudioContext is not initialized");const e=yield this.audioCtx.decodeAudioData(t),i=[e.getChannelData(0)];return e.numberOfChannels>1&&i.push(e.getChannelData(1)),{duration:e.duration,channelData:i}},new((n=void 0)||(n=Promise))((function(t,r){function o(t){try{h(s.next(t))}catch(t){r(t)}}function a(t){try{h(s.throw(t))}catch(t){r(t)}}function h(e){var i;e.done?t(e.value):(i=e.value,i instanceof n?i:new n((function(t){t(i)}))).then(o,a)}h((s=s.apply(e,i||[])).next())}));var e,i,n,s}destroy(){var t;null===(t=this.audioCtx)||void 0===t||t.close(),this.audioCtx=null}},this.timer=new s,this.player=new class{constructor({media:t,autoplay:e}){this.isExternalMedia=!1,this.hasPlayedOnce=!1,t?(this.media=t,this.isExternalMedia=!0):this.media=document.createElement("audio");const i=this.on("play",(()=>{this.hasPlayedOnce=!0,i()}));e&&(this.media.autoplay=!0)}on(t,e){var i;return null===(i=this.media)||void 0===i||i.addEventListener(t,e),()=>{var i;return null===(i=this.media)||void 0===i?void 0:i.removeEventListener(t,e)}}destroy(){var t,e;null===(t=this.media)||void 0===t||t.pause(),this.isExternalMedia||null===(e=this.media)||void 0===e||e.remove()}loadUrl(t){this.media&&(this.media.src=t)}getCurrentTime(){return this.media.currentTime}play(){this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}seekTo(t){if(this.media){const{hasPlayedOnce:e}=this;e||this.media.play(),this.media.currentTime=t,e||this.media.pause()}}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}}({media:this.options.media,autoplay:this.options.autoplay}),this.renderer=new n({container:this.options.container,height:this.options.height,waveColor:this.options.waveColor,progressColor:this.options.progressColor,minPxPerSec:this.options.minPxPerSec,barWidth:this.options.barWidth,barGap:this.options.barGap,barRadius:this.options.barRadius}),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents();const i=this.options.url||(null===(e=this.options.media)||void 0===e?void 0:e.src);i&&this.load(i,this.options.channelData,this.options.duration)}initPlayerEvents(){this.subscriptions.push(this.player.on("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.duration,this.isPlaying()),this.emit("audioprocess",{currentTime:t})})),this.player.on("play",(()=>{this.emit("play"),this.timer.start()})),this.player.on("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.player.on("canplay",(()=>{this.emit("canplay",{duration:this.getDuration()})})),this.player.on("seeking",(()=>{this.emit("seek",{time:this.getCurrentTime()})})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(({relativeX:t})=>{const e=this.getDuration()*t;this.seekTo(e)})))}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.duration,!0),this.emit("audioprocess",{currentTime:t})})))}destroy(){this.subscriptions.forEach((t=>t())),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.player.destroy(),this.decoder.destroy(),this.renderer.destroy()}load(t,e,i){return n=this,s=void 0,o=function*(){if(this.player.loadUrl(t),null==e||null==i){const n=yield this.fetcher.load(t),s=yield this.decoder.decode(n);e=s.channelData,i=s.duration}this.channelData=e,this.duration=i,this.renderer.render(this.channelData,this.duration),this.emit("decode",{duration:this.duration})},new((r=void 0)||(r=Promise))((function(t,e){function i(t){try{h(o.next(t))}catch(t){e(t)}}function a(t){try{h(o.throw(t))}catch(t){e(t)}}function h(e){var n;e.done?t(e.value):(n=e.value,n instanceof r?n:new r((function(t){t(n)}))).then(i,a)}h((o=o.apply(n,s||[])).next())}));var n,s,r,o}zoom(t){if(null==this.channelData||null==this.duration)throw new Error("No audio loaded");this.renderer.zoom(this.channelData,this.duration,t)}play(){this.player.play()}pause(){this.player.pause()}seekTo(t){this.player.seekTo(t)}isPlaying(){return this.player.isPlaying()}getDuration(){return this.duration}getCurrentTime(){return this.player.getCurrentTime()}getVolume(){return this.player.getVolume()}setVolume(t){this.player.setVolume(t)}getMuted(){return this.player.getMuted()}setMuted(t){this.player.setMuted(t)}getPlaybackRate(){return this.player.getPlaybackRate()}setPlaybackRate(t,e){this.player.setPlaybackRate(t,e)}registerPlugin(t){const e=new t({wavesurfer:this,container:this.renderer.getContainer()});return this.plugins.push(e),e}getAudioData(){return this.channelData}}const a=o;return e.default})()));
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.WaveSurfer=e():t.WaveSurfer=e()}(this,(()=>(()=>{"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:()=>a});const i=class{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)}};const s=class extends i{constructor(t){super(),this.timeout=null,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 }\n :host .wrapper {\n position: relative;\n width: fit-content;\n min-width: 100%;\n z-index: 2;\n }\n :host canvas {\n display: block;\n height: ${this.options.height}px;\n min-width: 100%;\n image-rendering: pixelated;\n }\n :host .progress {\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n pointer-events: none;\n clip-path: inset(100%);\n }\n :host .cursor {\n position: absolute;\n z-index: 3;\n top: 0;\n left: 0;\n height: 100%;\n border-right: 1px solid ${this.options.cursorColor||this.options.progressColor};\n margin-left: -1px;\n }\n </style>\n\n <div class="scroll">\n <div class="wrapper">\n <canvas></canvas>\n <canvas class="progress"></canvas>\n <div class="cursor"></div>\n </div>\n </div>\n `,this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.mainCanvas=s.querySelector("canvas"),this.ctx=this.mainCanvas.getContext("2d",{desynchronized:!0}),this.progressCanvas=s.querySelector(".progress"),this.cursor=s.querySelector(".cursor"),e.appendChild(i),this.mainCanvas.addEventListener("click",(t=>{const e=this.mainCanvas.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",{relativeX:i})}))}getContainer(){return this.scrollContainer.querySelector(".wrapper")}destroy(){this.container.remove()}delay(t,e=100){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}renderPeaks(t,e,i){var s,n,o,r,a;return n=this,o=void 0,a=function*(){const{devicePixelRatio:n}=window,{ctx:o}=this,r=null!=this.options.barWidth?this.options.barWidth*n:1,a=null!=this.options.barGap?this.options.barGap*n:this.options.barWidth?r/2:0,h=null!==(s=this.options.barRadius)&&void 0!==s?s:0,l=t[0],d=l.length,c=Math.floor(e/(r+a))/d,u=i/2,p=1===t.length,m=p?l:t[1],y=p&&m.some((t=>t<0)),v=(t,e)=>{let i=0,s=0,n=0;o.beginPath(),o.fillStyle=this.options.waveColor,o.roundRect||(o.roundRect=o.fillRect);for(let d=t;d<e;d++){const t=Math.round(d*c);if(t>i){const e=Math.round(s*u),l=Math.round(n*u);o.roundRect(i*(r+a),u-e,r,e+l||1,h),i=t,s=0,n=0}const e=y?l[d]:Math.abs(l[d]),p=y?m[d]:Math.abs(m[d]);e>s&&(s=e),(y?p<-n:p>n)&&(n=p<0?-p:p)}o.fill(),o.closePath()};o.clearRect(0,0,e,i);const{scrollLeft:g,scrollWidth:f,clientWidth:C}=this.scrollContainer,w=d/f,b=Math.floor(g*w),P=Math.ceil((g+C)*w);v(b,P),this.createProgressMask(),b>0&&(yield this.delay((()=>{v(0,b)}))),P<d&&(yield this.delay((()=>{v(P,d)}))),this.delay((()=>{this.createProgressMask()}))},new((r=void 0)||(r=Promise))((function(t,e){function i(t){try{h(a.next(t))}catch(t){e(t)}}function s(t){try{h(a.throw(t))}catch(t){e(t)}}function h(e){var n;e.done?t(e.value):(n=e.value,n instanceof r?n:new r((function(t){t(n)}))).then(i,s)}h((a=a.apply(n,o||[])).next())}))}createProgressMask(){const t=this.progressCanvas.getContext("2d",{desynchronized:!0});this.progressCanvas.width=this.mainCanvas.width,this.progressCanvas.height=this.mainCanvas.height,this.progressCanvas.style.width=this.mainCanvas.style.width,this.progressCanvas.style.height=this.mainCanvas.style.height,t.drawImage(this.mainCanvas,0,0),t.globalCompositeOperation="source-in",t.fillStyle=this.options.progressColor,t.fillRect(0,0,this.progressCanvas.width,this.progressCanvas.height)}render(t){const{devicePixelRatio:e}=window,i=this.options.fillParent?this.scrollContainer.clientWidth*e:0,s=t.duration*this.options.minPxPerSec,n=s>i?s:i,{height:o}=this.options;this.mainCanvas.width=n,this.mainCanvas.height=o,this.mainCanvas.style.width=Math.floor(s/e)+"px";const r=[t.getChannelData(0)];t.numberOfChannels>1&&r.push(t.getChannelData(1)),this.renderPeaks(r,n,o)}zoom(t,e){const i=this.cursor.getBoundingClientRect().left;this.options.minPxPerSec=e,this.render(t);const s=this.cursor.getBoundingClientRect().left;this.scrollContainer.scrollLeft+=s-i}renderProgress(t,e=!1){if(this.progressCanvas.style.clipPath=`inset(0 ${100-100*t}% 0 0)`,this.cursor.style.left=100*t+"%",e){const e=this.container.clientWidth/2,i=this.mainCanvas.clientWidth;i*t>=e&&(this.scrollContainer.scrollLeft=i*t-e)}}},n=class extends i{constructor(){super(...arguments),this.unsubscribe=()=>{}}start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}};const o={height:128,waveColor:"#999",progressColor:"#555",minPxPerSec:0,fillParent:!0,interactive:!0};class r extends i{static create(t){return new r(t)}constructor(t){var e;super(),this.plugins=[],this.subscriptions=[],this.decodedData=null,this.options=Object.assign({},o,t),this.fetcher=new class{load(t){return e=this,i=void 0,n=function*(){return fetch(t).then((t=>t.arrayBuffer()))},new((s=void 0)||(s=Promise))((function(t,o){function r(t){try{h(n.next(t))}catch(t){o(t)}}function a(t){try{h(n.throw(t))}catch(t){o(t)}}function h(e){var i;e.done?t(e.value):(i=e.value,i instanceof s?i:new s((function(t){t(i)}))).then(r,a)}h((n=n.apply(e,i||[])).next())}));var e,i,s,n}},this.decoder=new class{initAudioContext(t){this.audioCtx=new AudioContext({latencyHint:"playback",sampleRate:t})}constructor(){this.audioCtx=null;try{this.initAudioContext(3e3)}catch(t){this.initAudioContext(8e3)}}decode(t){return e=this,i=void 0,n=function*(){if(!this.audioCtx)throw new Error("AudioContext is not initialized");return this.audioCtx.decodeAudioData(t)},new((s=void 0)||(s=Promise))((function(t,o){function r(t){try{h(n.next(t))}catch(t){o(t)}}function a(t){try{h(n.throw(t))}catch(t){o(t)}}function h(e){var i;e.done?t(e.value):(i=e.value,i instanceof s?i:new s((function(t){t(i)}))).then(r,a)}h((n=n.apply(e,i||[])).next())}));var e,i,s,n}destroy(){var t;null===(t=this.audioCtx)||void 0===t||t.close(),this.audioCtx=null}},this.timer=new n,this.player=new class{constructor({media:t,autoplay:e}){this.isExternalMedia=!1,this.hasPlayedOnce=!1,t?(this.media=t,this.isExternalMedia=!0):this.media=document.createElement("audio");const i=this.on("play",(()=>{this.hasPlayedOnce=!0,i()}));e&&(this.media.autoplay=!0)}on(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}destroy(){this.media.pause(),this.isExternalMedia||this.media.remove()}loadUrl(t){this.media.src=t}getCurrentTime(){return this.media.currentTime}play(){this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}seekTo(t){if(this.media){const{hasPlayedOnce:e}=this;e||this.media.play(),this.media.currentTime=t,e||this.media.pause()}}getDuration(){return this.media.duration}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}}({media:this.options.media,autoplay:this.options.autoplay}),this.renderer=new s({container:this.options.container,height:this.options.height,waveColor:this.options.waveColor,progressColor:this.options.progressColor,cursorColor:this.options.cursorColor,minPxPerSec:this.options.minPxPerSec,fillParent:this.options.fillParent,barWidth:this.options.barWidth,barGap:this.options.barGap,barRadius:this.options.barRadius}),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent();const i=this.options.url||(null===(e=this.options.media)||void 0===e?void 0:e.src);i&&this.load(i,this.options.peaks,this.options.duration)}initPlayerEvents(){this.subscriptions.push(this.player.on("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",{currentTime:t})})),this.player.on("play",(()=>{this.emit("play"),this.timer.start()})),this.player.on("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.player.on("canplay",(()=>{this.emit("canplay",{duration:this.getDuration()})})),this.player.on("seeking",(()=>{this.emit("seeking",{currentTime:this.getCurrentTime()})})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(({relativeX:t})=>{if(this.options.interactive){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(){let t=!1,e=!1;const i=()=>{t&&e&&this.emit("ready",{duration:this.getDuration()})};this.subscriptions.push(this.on("decode",(()=>{t=!0,i()})),this.on("canplay",(()=>{e=!0,i()})),this.player.on("waiting",(()=>{e=!1,t=!1})))}load(t,e,i){return s=this,n=void 0,r=function*(){if(this.player.loadUrl(t),null==e){const e=yield this.fetcher.load(t),i=yield this.decoder.decode(e);this.decodedData=i}else i||(i=(yield new Promise((t=>{this.player.on("canplay",(()=>t(this.getDuration())),{once:!0})})))||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})},new((o=void 0)||(o=Promise))((function(t,e){function i(t){try{h(r.next(t))}catch(t){e(t)}}function a(t){try{h(r.throw(t))}catch(t){e(t)}}function h(e){var s;e.done?t(e.value):(s=e.value,s instanceof o?s:new o((function(t){t(s)}))).then(i,a)}h((r=r.apply(s,n||[])).next())}));var s,n,o,r}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(this.decodedData,t)}play(){this.player.play()}pause(){this.player.pause()}seekTo(t){this.player.seekTo(t)}isPlaying(){return this.player.isPlaying()}getDuration(){var t;return this.player.getDuration()||(null===(t=this.decodedData)||void 0===t?void 0:t.duration)||0}getCurrentTime(){return this.player.getCurrentTime()}getVolume(){return this.player.getVolume()}setVolume(t){this.player.setVolume(t)}getMuted(){return this.player.getMuted()}setMuted(t){this.player.setMuted(t)}getPlaybackRate(){return this.player.getPlaybackRate()}setPlaybackRate(t,e){this.player.setPlaybackRate(t,e)}registerPlugin(t,e){const i=new t({wavesurfer:this,container:this.renderer.getContainer()},e);return this.plugins.push(i),i}getDecodedData(){return this.decodedData}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.player.destroy(),this.decoder.destroy(),this.renderer.destroy()}}const a=r;return e.default})()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wavesurfer.js",
3
- "version": "7.0.0-alpha.5",
3
+ "version": "7.0.0-alpha.7",
4
4
  "description": "wavesurfer.js is an audio library that draws waveforms and plays audio in the browser.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",