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

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import EventEmitter, { type GeneralEventTypes } from './event-emitter.js';
1
+ import Player from './player.js';
2
+ import { type GeneralEventTypes } from './event-emitter.js';
2
3
  import BasePlugin from './base-plugin.js';
3
4
  export type WaveSurferOptions = {
4
5
  /** HTML element or CSS selector */
@@ -34,9 +35,15 @@ export type WaveSurferOptions = {
34
35
  /** Play the audio on load */
35
36
  autoplay?: boolean;
36
37
  /** Is the waveform clickable? */
37
- interactive?: boolean;
38
+ interact?: boolean;
38
39
  /** Hide scrollbar **/
39
- noScrollbar?: boolean;
40
+ hideScrollbar?: boolean;
41
+ /** Audio rate */
42
+ audioRate?: number;
43
+ /** Keep scroll to the center of the waveform during playback */
44
+ autoCenter?: boolean;
45
+ /** Initialize plugins */
46
+ plugins: BasePlugin<GeneralEventTypes, unknown>[];
40
47
  };
41
48
  declare const defaultOptions: {
42
49
  height: number;
@@ -45,7 +52,8 @@ declare const defaultOptions: {
45
52
  cursorWidth: number;
46
53
  minPxPerSec: number;
47
54
  fillParent: boolean;
48
- interactive: boolean;
55
+ interact: boolean;
56
+ autoCenter: boolean;
49
57
  };
50
58
  export type WaveSurferEvents = {
51
59
  decode: {
@@ -78,16 +86,15 @@ export type WaveSurferPluginParams = {
78
86
  container: HTMLElement;
79
87
  wrapper: HTMLElement;
80
88
  };
81
- declare class WaveSurfer extends EventEmitter<WaveSurferEvents> {
89
+ declare class WaveSurfer extends Player<WaveSurferEvents> {
82
90
  options: WaveSurferOptions & typeof defaultOptions;
83
91
  private fetcher;
84
92
  private decoder;
85
93
  private renderer;
86
- private player;
87
94
  private timer;
88
95
  private plugins;
89
- private subscriptions;
90
96
  private decodedData;
97
+ private canPlay;
91
98
  /** Create a new WaveSurfer instance */
92
99
  static create(options: WaveSurferOptions): WaveSurfer;
93
100
  /** Create a new WaveSurfer instance */
@@ -96,40 +103,16 @@ declare class WaveSurfer extends EventEmitter<WaveSurferEvents> {
96
103
  private initRendererEvents;
97
104
  private initTimerEvents;
98
105
  private initReadyEvent;
106
+ private initPlugins;
107
+ /** Register a wavesurfer.js plugin */
108
+ registerPlugin(plugin: BasePlugin<GeneralEventTypes, unknown>): void;
99
109
  /** Load an audio file by URL, with optional pre-decoded audio data */
100
110
  load(url: string, channelData?: Float32Array[], duration?: number): Promise<void>;
101
111
  /** Zoom in or out */
102
112
  zoom(minPxPerSec: number): void;
103
- /** Start playing the audio */
104
- play(): void;
105
- /** Pause the audio */
106
- pause(): void;
107
- /** Skip to a time position in seconds */
108
- seekTo(time: number): void;
109
- /** Check if the audio is playing */
110
- isPlaying(): boolean;
111
- /** Get the duration of the audio in seconds */
112
- getDuration(): number;
113
- /** Get the current audio position in seconds */
114
- getCurrentTime(): number;
115
- /** Get the audio volume */
116
- getVolume(): number;
117
- /** Set the audio volume */
118
- setVolume(volume: number): void;
119
- /** Get the audio muted state */
120
- getMuted(): boolean;
121
- /** Mute or unmute the audio */
122
- setMuted(muted: boolean): void;
123
- /** Get playback rate */
124
- getPlaybackRate(): number;
125
- /** Set playback rate, with an optional parameter to NOT preserve the pitch if false */
126
- setPlaybackRate(rate: number, preservePitch?: boolean): void;
127
- /** Register and initialize a plugin */
128
- registerPlugin<T extends BasePlugin<GeneralEventTypes, Options>, Options>(CustomPlugin: new (params: WaveSurferPluginParams, options: Options) => T, options: Options): T;
129
113
  /** Get the decoded audio data */
130
114
  getDecodedData(): AudioBuffer | null;
131
- /** Get the raw media element */
132
- getMediaElement(): HTMLMediaElement | null;
115
+ getDuration(): number;
133
116
  /** Toggle if the waveform should react to clicks */
134
117
  toggleInteractive(isInteractive: boolean): void;
135
118
  /** Unmount wavesurfer */
package/dist/index.js CHANGED
@@ -2,7 +2,6 @@ import Fetcher from './fetcher.js';
2
2
  import Decoder from './decoder.js';
3
3
  import Renderer from './renderer.js';
4
4
  import Player from './player.js';
5
- import EventEmitter from './event-emitter.js';
6
5
  import Timer from './timer.js';
7
6
  const defaultOptions = {
8
7
  height: 128,
@@ -11,33 +10,33 @@ const defaultOptions = {
11
10
  cursorWidth: 1,
12
11
  minPxPerSec: 0,
13
12
  fillParent: true,
14
- interactive: true,
13
+ interact: true,
14
+ autoCenter: true,
15
15
  };
16
- class WaveSurfer extends EventEmitter {
16
+ class WaveSurfer extends Player {
17
17
  options;
18
18
  fetcher;
19
19
  decoder;
20
20
  renderer;
21
- player;
22
21
  timer;
23
22
  plugins = [];
24
- subscriptions = [];
25
23
  decodedData = null;
24
+ canPlay = false;
26
25
  /** Create a new WaveSurfer instance */
27
26
  static create(options) {
28
27
  return new WaveSurfer(options);
29
28
  }
30
29
  /** Create a new WaveSurfer instance */
31
30
  constructor(options) {
32
- super();
31
+ super({
32
+ media: options.media,
33
+ autoplay: options.autoplay,
34
+ playbackRate: options.audioRate,
35
+ });
33
36
  this.options = Object.assign({}, defaultOptions, options);
34
37
  this.fetcher = new Fetcher();
35
38
  this.decoder = new Decoder();
36
39
  this.timer = new Timer();
37
- this.player = new Player({
38
- media: this.options.media,
39
- autoplay: this.options.autoplay,
40
- });
41
40
  this.renderer = new Renderer({
42
41
  container: this.options.container,
43
42
  height: this.options.height,
@@ -50,38 +49,40 @@ class WaveSurfer extends EventEmitter {
50
49
  barWidth: this.options.barWidth,
51
50
  barGap: this.options.barGap,
52
51
  barRadius: this.options.barRadius,
53
- noScrollbar: this.options.noScrollbar,
52
+ hideScrollbar: this.options.hideScrollbar,
54
53
  });
55
54
  this.initPlayerEvents();
56
55
  this.initRendererEvents();
57
56
  this.initTimerEvents();
58
57
  this.initReadyEvent();
58
+ this.initPlugins();
59
59
  const url = this.options.url || this.options.media?.src;
60
60
  if (url) {
61
61
  this.load(url, this.options.peaks, this.options.duration);
62
62
  }
63
63
  }
64
64
  initPlayerEvents() {
65
- this.subscriptions.push(this.player.on('timeupdate', () => {
65
+ this.subscriptions.push(this.onMediaEvent('timeupdate', () => {
66
66
  const currentTime = this.getCurrentTime();
67
- this.renderer.renderProgress(currentTime / this.getDuration(), this.isPlaying());
67
+ this.renderer.renderProgress(currentTime / this.getDuration(), this.options.autoCenter && this.isPlaying());
68
68
  this.emit('timeupdate', { currentTime });
69
- }), this.player.on('play', () => {
69
+ }), this.onMediaEvent('play', () => {
70
70
  this.emit('play');
71
71
  this.timer.start();
72
- }), this.player.on('pause', () => {
72
+ }), this.onMediaEvent('pause', () => {
73
73
  this.emit('pause');
74
74
  this.timer.stop();
75
- }), this.player.on('canplay', () => {
75
+ }), this.onMediaEvent('canplay', () => {
76
+ this.canPlay = true;
76
77
  this.emit('canplay', { duration: this.getDuration() });
77
- }), this.player.on('seeking', () => {
78
+ }), this.onMediaEvent('seeking', () => {
78
79
  this.emit('seeking', { currentTime: this.getCurrentTime() });
79
80
  }));
80
81
  }
81
82
  initRendererEvents() {
82
83
  // Seek on click
83
84
  this.subscriptions.push(this.renderer.on('click', ({ relativeX }) => {
84
- if (this.options.interactive) {
85
+ if (this.options.interact) {
85
86
  const time = this.getDuration() * relativeX;
86
87
  this.seekTo(time);
87
88
  this.emit('seekClick', { currentTime: this.getCurrentTime() });
@@ -97,27 +98,37 @@ class WaveSurfer extends EventEmitter {
97
98
  }));
98
99
  }
99
100
  initReadyEvent() {
100
- let isDecoded = false;
101
- let isPlayable = false;
102
101
  const emitReady = () => {
103
- if (isDecoded && isPlayable) {
102
+ if (this.decodedData && this.canPlay) {
104
103
  this.emit('ready', { duration: this.getDuration() });
105
104
  }
106
105
  };
107
- this.subscriptions.push(this.on('decode', () => {
108
- isDecoded = true;
109
- emitReady();
110
- }), this.on('canplay', () => {
111
- isPlayable = true;
112
- emitReady();
113
- }), this.player.on('waiting', () => {
114
- isPlayable = false;
115
- isDecoded = false;
116
- }));
106
+ this.subscriptions.push(this.on('decode', emitReady), this.on('canplay', emitReady));
107
+ }
108
+ initPlugins() {
109
+ if (!this.options.plugins?.length)
110
+ return;
111
+ this.options.plugins.forEach((plugin) => {
112
+ this.registerPlugin(plugin);
113
+ });
114
+ }
115
+ /** Register a wavesurfer.js plugin */
116
+ registerPlugin(plugin) {
117
+ plugin.init({
118
+ wavesurfer: this,
119
+ container: this.renderer.getContainer(),
120
+ wrapper: this.renderer.getWrapper(),
121
+ });
122
+ this.plugins.push(plugin);
123
+ plugin.once('destroy', () => {
124
+ this.plugins = this.plugins.filter((p) => p !== plugin);
125
+ });
117
126
  }
118
127
  /** Load an audio file by URL, with optional pre-decoded audio data */
119
128
  async load(url, channelData, duration) {
120
- this.player.loadUrl(url);
129
+ this.decodedData = null;
130
+ this.canPlay = false;
131
+ this.loadUrl(url);
121
132
  // Fetch and decode the audio of no pre-computed audio data is provided
122
133
  if (channelData == null) {
123
134
  const audio = await this.fetcher.load(url);
@@ -128,9 +139,7 @@ class WaveSurfer extends EventEmitter {
128
139
  if (!duration) {
129
140
  duration =
130
141
  (await new Promise((resolve) => {
131
- this.player.on('canplay', () => resolve(this.getDuration()), {
132
- once: true,
133
- });
142
+ this.onceMediaEvent('loadedmetadata', () => resolve(this.getDuration()));
134
143
  })) || 0;
135
144
  }
136
145
  this.decodedData = {
@@ -151,88 +160,25 @@ class WaveSurfer extends EventEmitter {
151
160
  this.renderer.zoom(this.decodedData, minPxPerSec);
152
161
  this.emit('zoom', { minPxPerSec });
153
162
  }
154
- /** Start playing the audio */
155
- play() {
156
- this.player.play();
157
- }
158
- /** Pause the audio */
159
- pause() {
160
- this.player.pause();
161
- }
162
- /** Skip to a time position in seconds */
163
- seekTo(time) {
164
- this.player.seekTo(time);
165
- }
166
- /** Check if the audio is playing */
167
- isPlaying() {
168
- return this.player.isPlaying();
169
- }
170
- /** Get the duration of the audio in seconds */
171
- getDuration() {
172
- return this.player.getDuration() || this.decodedData?.duration || 0;
173
- }
174
- /** Get the current audio position in seconds */
175
- getCurrentTime() {
176
- return this.player.getCurrentTime();
177
- }
178
- /** Get the audio volume */
179
- getVolume() {
180
- return this.player.getVolume();
181
- }
182
- /** Set the audio volume */
183
- setVolume(volume) {
184
- this.player.setVolume(volume);
185
- }
186
- /** Get the audio muted state */
187
- getMuted() {
188
- return this.player.getMuted();
189
- }
190
- /** Mute or unmute the audio */
191
- setMuted(muted) {
192
- this.player.setMuted(muted);
193
- }
194
- /** Get playback rate */
195
- getPlaybackRate() {
196
- return this.player.getPlaybackRate();
197
- }
198
- /** Set playback rate, with an optional parameter to NOT preserve the pitch if false */
199
- setPlaybackRate(rate, preservePitch) {
200
- this.player.setPlaybackRate(rate, preservePitch);
201
- }
202
- /** Register and initialize a plugin */
203
- registerPlugin(CustomPlugin, options) {
204
- const plugin = new CustomPlugin({
205
- wavesurfer: this,
206
- container: this.renderer.getContainer(),
207
- wrapper: this.renderer.getWrapper(),
208
- }, options);
209
- this.plugins.push(plugin);
210
- plugin.once('destroy', () => {
211
- this.plugins = this.plugins.filter((p) => p !== plugin);
212
- });
213
- return plugin;
214
- }
215
163
  /** Get the decoded audio data */
216
164
  getDecodedData() {
217
165
  return this.decodedData;
218
166
  }
219
- /** Get the raw media element */
220
- getMediaElement() {
221
- return this.player.getMediaElement();
167
+ getDuration() {
168
+ return this.decodedData?.duration || this.getMediaElement()?.duration || 0;
222
169
  }
223
170
  /** Toggle if the waveform should react to clicks */
224
171
  toggleInteractive(isInteractive) {
225
- this.options.interactive = isInteractive;
172
+ this.options.interact = isInteractive;
226
173
  }
227
174
  /** Unmount wavesurfer */
228
175
  destroy() {
229
176
  this.emit('destroy');
230
- this.subscriptions.forEach((unsubscribe) => unsubscribe());
231
177
  this.plugins.forEach((plugin) => plugin.destroy());
232
178
  this.timer.destroy();
233
- this.player.destroy();
234
179
  this.decoder.destroy();
235
180
  this.renderer.destroy();
181
+ super.destroy();
236
182
  }
237
183
  }
238
184
  export default WaveSurfer;
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;