wavesurfer.js 7.0.0-alpha.21 → 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.
- package/dist/base-plugin.d.ts +6 -5
- package/dist/base-plugin.js +4 -2
- package/dist/index.d.ts +18 -35
- package/dist/index.js +48 -102
- package/dist/player.d.ts +26 -9
- package/dist/player.js +38 -18
- package/dist/plugins/envelope.d.ts +4 -2
- package/dist/plugins/envelope.js +21 -11
- package/dist/plugins/minimap.d.ts +6 -4
- package/dist/plugins/minimap.js +18 -7
- package/dist/plugins/multitrack.d.ts +1 -2
- package/dist/plugins/multitrack.js +19 -19
- package/dist/plugins/regions.d.ts +5 -2
- package/dist/plugins/regions.js +26 -10
- package/dist/plugins/timeline.d.ts +4 -2
- package/dist/plugins/timeline.js +13 -4
- package/dist/renderer.d.ts +3 -1
- package/dist/renderer.js +26 -13
- package/dist/wavesurfer.Minimap.min.js +1 -1
- package/dist/wavesurfer.Multitrack.min.js +1 -1
- package/dist/wavesurfer.Regions.min.js +1 -1
- package/dist/wavesurfer.Timeline.min.js +1 -1
- package/dist/wavesurfer.d.ts +121 -0
- package/dist/wavesurfer.js +185 -0
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +3 -3
package/dist/plugins/envelope.js
CHANGED
|
@@ -18,13 +18,22 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
18
18
|
volume = 1;
|
|
19
19
|
isFadingIn = false;
|
|
20
20
|
isFadingOut = false;
|
|
21
|
-
constructor(
|
|
22
|
-
super(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
import { type WaveSurferPluginParams, type WaveSurferOptions } from '../
|
|
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
|
|
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(
|
|
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
|
|
27
|
+
export default MinimapPlugin;
|
package/dist/plugins/minimap.js
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
import WaveSurfer from '../
|
|
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
|
|
7
|
+
class MinimapPlugin extends BasePlugin {
|
|
8
8
|
options;
|
|
9
9
|
minimapWrapper;
|
|
10
10
|
miniWavesurfer = null;
|
|
11
11
|
overlay;
|
|
12
|
-
constructor(
|
|
13
|
-
super(
|
|
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 /
|
|
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
|
|
74
|
+
export default MinimapPlugin;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WaveSurferOptions } from '../
|
|
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 '../
|
|
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
|
-
|
|
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,16 +171,18 @@ 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;
|
|
181
|
-
this.currentTime = time;
|
|
182
|
-
this.rendering.updateCursor(time / this.maxDuration, autoCenter);
|
|
183
181
|
const isPaused = !this.isPlaying();
|
|
182
|
+
if (time !== this.currentTime) {
|
|
183
|
+
this.currentTime = time;
|
|
184
|
+
this.rendering.updateCursor(time / this.maxDuration, autoCenter);
|
|
185
|
+
}
|
|
184
186
|
// Update the current time of each audio
|
|
185
187
|
this.tracks.forEach((track, index) => {
|
|
186
188
|
const audio = this.audios[index];
|
|
@@ -212,19 +214,17 @@ class MultiTrack extends EventEmitter {
|
|
|
212
214
|
}
|
|
213
215
|
onDrag(index, delta) {
|
|
214
216
|
this.setIsDragging();
|
|
215
|
-
const newTime = this.tracks[index].startPosition + delta * this.maxDuration;
|
|
216
|
-
this.onMove(index, newTime);
|
|
217
|
-
}
|
|
218
|
-
onMove(index, newStartPosition) {
|
|
219
217
|
const track = this.tracks[index];
|
|
220
218
|
if (!track.draggable)
|
|
221
219
|
return;
|
|
220
|
+
const newStartPosition = track.startPosition + delta * this.maxDuration;
|
|
222
221
|
const mainIndex = this.tracks.findIndex((item) => item.url && !item.draggable);
|
|
223
222
|
const mainTrack = this.tracks[mainIndex];
|
|
224
223
|
const minStart = (mainTrack ? mainTrack.startPosition : 0) - this.durations[index];
|
|
225
224
|
const maxStart = mainTrack ? mainTrack.startPosition + this.durations[mainIndex] : this.maxDuration;
|
|
226
225
|
if (newStartPosition >= minStart && newStartPosition <= maxStart) {
|
|
227
226
|
track.startPosition = newStartPosition;
|
|
227
|
+
this.initDurations(this.durations);
|
|
228
228
|
this.rendering.setContainerOffsets();
|
|
229
229
|
this.updatePosition(this.currentTime);
|
|
230
230
|
this.emit('start-position-change', { id: track.id, startPosition: newStartPosition });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
import type { WaveSurferPluginParams } from '../
|
|
2
|
+
import type { WaveSurferPluginParams } from '../wavesurfer.js';
|
|
3
3
|
export type RegionsPluginOptions = {
|
|
4
4
|
dragSelection?: boolean;
|
|
5
5
|
draggable?: boolean;
|
|
@@ -32,8 +32,11 @@ declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPlugi
|
|
|
32
32
|
private modifiedRegion;
|
|
33
33
|
private isResizingLeft;
|
|
34
34
|
private isMoving;
|
|
35
|
+
private wasInteractive;
|
|
35
36
|
/** Create an instance of RegionsPlugin */
|
|
36
|
-
constructor(
|
|
37
|
+
constructor(options?: RegionsPluginOptions);
|
|
38
|
+
static create(options?: RegionsPluginOptions): RegionsPlugin;
|
|
39
|
+
init(params: WaveSurferPluginParams): void;
|
|
37
40
|
/** Unmount */
|
|
38
41
|
destroy(): void;
|
|
39
42
|
private initRegionsContainer;
|
package/dist/plugins/regions.js
CHANGED
|
@@ -23,13 +23,23 @@ class RegionsPlugin extends BasePlugin {
|
|
|
23
23
|
modifiedRegion = null;
|
|
24
24
|
isResizingLeft = false;
|
|
25
25
|
isMoving = false;
|
|
26
|
+
wasInteractive = true;
|
|
26
27
|
/** Create an instance of RegionsPlugin */
|
|
27
|
-
constructor(
|
|
28
|
-
super(
|
|
28
|
+
constructor(options) {
|
|
29
|
+
super(options || {});
|
|
29
30
|
this.options = Object.assign({}, defaultOptions, options);
|
|
30
31
|
this.regionsContainer = this.initRegionsContainer();
|
|
32
|
+
}
|
|
33
|
+
static create(options) {
|
|
34
|
+
return new RegionsPlugin(options);
|
|
35
|
+
}
|
|
36
|
+
init(params) {
|
|
37
|
+
super.init(params);
|
|
38
|
+
if (!this.wavesurfer || !this.wrapper) {
|
|
39
|
+
throw Error('WaveSurfer is not initialized');
|
|
40
|
+
}
|
|
31
41
|
this.subscriptions.push(this.wavesurfer.once('decode', () => {
|
|
32
|
-
this.wrapper
|
|
42
|
+
this.wrapper?.appendChild(this.regionsContainer);
|
|
33
43
|
}));
|
|
34
44
|
this.wrapper.addEventListener('mousedown', this.handleMouseDown);
|
|
35
45
|
document.addEventListener('mousemove', this.handleMouseMove);
|
|
@@ -37,7 +47,7 @@ class RegionsPlugin extends BasePlugin {
|
|
|
37
47
|
}
|
|
38
48
|
/** Unmount */
|
|
39
49
|
destroy() {
|
|
40
|
-
this.wrapper
|
|
50
|
+
this.wrapper?.removeEventListener('mousedown', this.handleMouseDown);
|
|
41
51
|
document.removeEventListener('mousemove', this.handleMouseMove);
|
|
42
52
|
document.removeEventListener('mouseup', this.handleMouseUp, true);
|
|
43
53
|
this.regionsContainer.remove();
|
|
@@ -56,10 +66,14 @@ class RegionsPlugin extends BasePlugin {
|
|
|
56
66
|
}
|
|
57
67
|
handleMouseDown = (e) => {
|
|
58
68
|
if (this.options.draggable || this.options.resizable || this.options.dragSelection) {
|
|
69
|
+
if (!this.wrapper)
|
|
70
|
+
return;
|
|
59
71
|
this.dragStart = e.clientX - this.wrapper.getBoundingClientRect().left;
|
|
60
72
|
}
|
|
61
73
|
};
|
|
62
74
|
handleMouseMove = (e) => {
|
|
75
|
+
if (!this.wrapper)
|
|
76
|
+
return;
|
|
63
77
|
const box = this.wrapper.getBoundingClientRect();
|
|
64
78
|
const { width } = box;
|
|
65
79
|
const dragEnd = e.clientX - box.left;
|
|
@@ -76,7 +90,8 @@ class RegionsPlugin extends BasePlugin {
|
|
|
76
90
|
const dragEnd = e.clientX - this.regionsContainer.getBoundingClientRect().left;
|
|
77
91
|
if (dragEnd - this.dragStart >= MIN_WIDTH) {
|
|
78
92
|
if (!this.createdRegion) {
|
|
79
|
-
this.
|
|
93
|
+
this.wasInteractive = this.wavesurfer?.options.interact || true;
|
|
94
|
+
this.wavesurfer?.toggleInteractive(false);
|
|
80
95
|
this.createdRegion = this.createRegion(this.dragStart / width, dragEnd / width);
|
|
81
96
|
}
|
|
82
97
|
else {
|
|
@@ -93,7 +108,7 @@ class RegionsPlugin extends BasePlugin {
|
|
|
93
108
|
this.modifiedRegion = null;
|
|
94
109
|
this.isMoving = false;
|
|
95
110
|
this.dragStart = NaN;
|
|
96
|
-
this.
|
|
111
|
+
this.wavesurfer?.toggleInteractive(this.wasInteractive);
|
|
97
112
|
};
|
|
98
113
|
createRegionElement(start, end, title = '') {
|
|
99
114
|
const noWidth = start === end;
|
|
@@ -181,7 +196,7 @@ class RegionsPlugin extends BasePlugin {
|
|
|
181
196
|
return div;
|
|
182
197
|
}
|
|
183
198
|
createRegion(start, end, title = '') {
|
|
184
|
-
const duration = this.wavesurfer
|
|
199
|
+
const duration = this.wavesurfer?.getDuration() || 0;
|
|
185
200
|
return {
|
|
186
201
|
element: this.createRegionElement(start, end, title),
|
|
187
202
|
start,
|
|
@@ -196,16 +211,17 @@ class RegionsPlugin extends BasePlugin {
|
|
|
196
211
|
this.emit('region-created', { region });
|
|
197
212
|
}
|
|
198
213
|
updateRegion(region, start, end) {
|
|
214
|
+
const duration = this.wavesurfer?.getDuration() || 0;
|
|
199
215
|
if (start != null) {
|
|
200
216
|
region.start = start;
|
|
201
217
|
region.element.style.left = `${region.start * 100}%`;
|
|
202
218
|
region.element.style.width = `${(region.end - region.start) * 100}%`;
|
|
203
|
-
region.startTime = start *
|
|
219
|
+
region.startTime = start * duration;
|
|
204
220
|
}
|
|
205
221
|
if (end != null) {
|
|
206
222
|
region.end = end;
|
|
207
223
|
region.element.style.width = `${(region.end - region.start) * 100}%`;
|
|
208
|
-
region.endTime = end *
|
|
224
|
+
region.endTime = end * duration;
|
|
209
225
|
}
|
|
210
226
|
this.emit('region-updated', { region });
|
|
211
227
|
}
|
|
@@ -214,7 +230,7 @@ class RegionsPlugin extends BasePlugin {
|
|
|
214
230
|
}
|
|
215
231
|
/** Create a region at a given start and end time, with an optional title */
|
|
216
232
|
add(startTime, endTime, title = '', color = '') {
|
|
217
|
-
const duration = this.wavesurfer
|
|
233
|
+
const duration = this.wavesurfer?.getDuration() || 0;
|
|
218
234
|
const start = startTime / duration;
|
|
219
235
|
const end = endTime / duration;
|
|
220
236
|
const region = this.createRegion(start, end, title);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
import type { WaveSurferPluginParams } from '../
|
|
2
|
+
import type { WaveSurferPluginParams } from '../wavesurfer.js';
|
|
3
3
|
export type TimelinePluginOptions = {
|
|
4
4
|
/** The height of the timeline in pixels, defaults to 20 */
|
|
5
5
|
height?: number;
|
|
@@ -23,7 +23,9 @@ export type TimelinePluginEvents = {
|
|
|
23
23
|
declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
|
|
24
24
|
private timelineWrapper;
|
|
25
25
|
protected options: TimelinePluginOptions & typeof defaultOptions;
|
|
26
|
-
constructor(
|
|
26
|
+
constructor(options: TimelinePluginOptions);
|
|
27
|
+
static create(options: TimelinePluginOptions): TimelinePlugin;
|
|
28
|
+
init(params: WaveSurferPluginParams): void;
|
|
27
29
|
/** Unmount */
|
|
28
30
|
destroy(): void;
|
|
29
31
|
private initTimelineWrapper;
|
package/dist/plugins/timeline.js
CHANGED
|
@@ -5,12 +5,21 @@ const defaultOptions = {
|
|
|
5
5
|
class TimelinePlugin extends BasePlugin {
|
|
6
6
|
timelineWrapper;
|
|
7
7
|
options;
|
|
8
|
-
constructor(
|
|
9
|
-
super(
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super(options);
|
|
10
10
|
this.options = Object.assign({}, defaultOptions, options);
|
|
11
|
-
this.container = this.options.container ?? this.wrapper;
|
|
12
11
|
this.timelineWrapper = this.initTimelineWrapper();
|
|
13
|
-
|
|
12
|
+
}
|
|
13
|
+
static create(options) {
|
|
14
|
+
return new TimelinePlugin(options);
|
|
15
|
+
}
|
|
16
|
+
init(params) {
|
|
17
|
+
super.init(params);
|
|
18
|
+
if (!this.wavesurfer || !this.wrapper) {
|
|
19
|
+
throw Error('WaveSurfer is not initialized');
|
|
20
|
+
}
|
|
21
|
+
const container = this.options.container ?? this.wrapper;
|
|
22
|
+
container.appendChild(this.timelineWrapper);
|
|
14
23
|
if (this.options.duration) {
|
|
15
24
|
this.initTimeline(this.options.duration);
|
|
16
25
|
}
|
package/dist/renderer.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ type RendererOptions = {
|
|
|
11
11
|
barWidth?: number;
|
|
12
12
|
barGap?: number;
|
|
13
13
|
barRadius?: number;
|
|
14
|
-
|
|
14
|
+
hideScrollbar?: boolean;
|
|
15
15
|
};
|
|
16
16
|
type RendererEvents = {
|
|
17
17
|
click: {
|
|
@@ -27,6 +27,8 @@ declare class Renderer extends EventEmitter<RendererEvents> {
|
|
|
27
27
|
private progressWrapper;
|
|
28
28
|
private timeout;
|
|
29
29
|
private isScrolling;
|
|
30
|
+
private audioData;
|
|
31
|
+
private resizeObserver;
|
|
30
32
|
constructor(options: RendererOptions);
|
|
31
33
|
getContainer(): HTMLElement;
|
|
32
34
|
getWrapper(): HTMLElement;
|
package/dist/renderer.js
CHANGED
|
@@ -8,6 +8,8 @@ class Renderer extends EventEmitter {
|
|
|
8
8
|
progressWrapper;
|
|
9
9
|
timeout = null;
|
|
10
10
|
isScrolling = false;
|
|
11
|
+
audioData = null;
|
|
12
|
+
resizeObserver = null;
|
|
11
13
|
constructor(options) {
|
|
12
14
|
super();
|
|
13
15
|
this.options = options;
|
|
@@ -33,10 +35,10 @@ class Renderer extends EventEmitter {
|
|
|
33
35
|
overflow-y: hidden;
|
|
34
36
|
width: 100%;
|
|
35
37
|
position: relative;
|
|
36
|
-
${this.options.
|
|
38
|
+
${this.options.hideScrollbar ? 'scrollbar-color: transparent;' : ''}
|
|
37
39
|
}
|
|
38
40
|
:host ::-webkit-scrollbar {
|
|
39
|
-
display: ${this.options.
|
|
41
|
+
display: ${this.options.hideScrollbar ? 'none' : 'auto'};
|
|
40
42
|
}
|
|
41
43
|
:host .wrapper {
|
|
42
44
|
position: relative;
|
|
@@ -89,6 +91,11 @@ class Renderer extends EventEmitter {
|
|
|
89
91
|
const relativeX = x / rect.width;
|
|
90
92
|
this.emit('click', { relativeX });
|
|
91
93
|
});
|
|
94
|
+
// Re-render the waveform on container resize
|
|
95
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
96
|
+
this.delay(() => this.audioData && this.zoom(this.audioData, this.options.minPxPerSec), 100);
|
|
97
|
+
});
|
|
98
|
+
this.resizeObserver.observe(this.scrollContainer);
|
|
92
99
|
}
|
|
93
100
|
getContainer() {
|
|
94
101
|
return this.scrollContainer;
|
|
@@ -98,6 +105,7 @@ class Renderer extends EventEmitter {
|
|
|
98
105
|
}
|
|
99
106
|
destroy() {
|
|
100
107
|
this.container.remove();
|
|
108
|
+
this.resizeObserver?.disconnect();
|
|
101
109
|
}
|
|
102
110
|
delay(fn, delayMs = 10) {
|
|
103
111
|
if (this.timeout) {
|
|
@@ -178,11 +186,12 @@ class Renderer extends EventEmitter {
|
|
|
178
186
|
// Clear the canvas
|
|
179
187
|
this.canvasWrapper.innerHTML = '';
|
|
180
188
|
this.progressWrapper.innerHTML = '';
|
|
181
|
-
//
|
|
189
|
+
// Determine the currently visible part of the waveform
|
|
182
190
|
const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
|
|
183
191
|
const scale = len / scrollWidth;
|
|
184
192
|
const start = Math.floor(scrollLeft * scale);
|
|
185
193
|
const end = Math.ceil(Math.min(scrollWidth, scrollLeft + clientWidth) * scale);
|
|
194
|
+
// Draw the visible portion of the waveform
|
|
186
195
|
draw(start, end);
|
|
187
196
|
// Draw the rest of the waveform with a timeout for better performance
|
|
188
197
|
const step = end - start;
|
|
@@ -198,30 +207,34 @@ class Renderer extends EventEmitter {
|
|
|
198
207
|
}
|
|
199
208
|
}
|
|
200
209
|
render(audioData) {
|
|
201
|
-
// Determine the width of the
|
|
210
|
+
// Determine the width of the waveform
|
|
202
211
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
203
212
|
const parentWidth = this.options.fillParent ? this.scrollContainer.clientWidth * pixelRatio : 0;
|
|
204
|
-
const scrollWidth = audioData.duration * this.options.minPxPerSec;
|
|
205
|
-
|
|
206
|
-
const width = Math.max(1, this.isScrolling ? scrollWidth : parentWidth);
|
|
213
|
+
const scrollWidth = Math.max(1, audioData.duration * this.options.minPxPerSec);
|
|
214
|
+
const width = scrollWidth > parentWidth ? scrollWidth : parentWidth;
|
|
207
215
|
const { height } = this.options;
|
|
208
|
-
//
|
|
209
|
-
|
|
216
|
+
// Set the width of the container
|
|
217
|
+
this.isScrolling = width > parentWidth;
|
|
210
218
|
this.scrollContainer.style.overflowX = this.isScrolling ? 'auto' : 'hidden';
|
|
211
219
|
this.wrapper.style.width = `${Math.floor(width / pixelRatio)}px`;
|
|
212
|
-
//
|
|
213
|
-
const newCursortPosition = this.progressWrapper.clientWidth;
|
|
214
|
-
this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
|
|
215
|
-
// First two channels are used
|
|
220
|
+
// Only the first two channels are used
|
|
216
221
|
const channelData = [audioData.getChannelData(0)];
|
|
217
222
|
if (audioData.numberOfChannels > 1) {
|
|
218
223
|
channelData.push(audioData.getChannelData(1));
|
|
219
224
|
}
|
|
225
|
+
// Render the waveform
|
|
220
226
|
this.renderPeaks(channelData, width, height, pixelRatio);
|
|
227
|
+
this.audioData = audioData;
|
|
221
228
|
}
|
|
222
229
|
zoom(audioData, minPxPerSec) {
|
|
230
|
+
// Remember the current cursor position
|
|
231
|
+
const oldCursorPosition = this.progressWrapper.clientWidth;
|
|
232
|
+
// Set the new zoom level and re-render the waveform
|
|
223
233
|
this.options.minPxPerSec = minPxPerSec;
|
|
224
234
|
this.render(audioData);
|
|
235
|
+
// Adjust the scroll position so that the cursor stays in the same place
|
|
236
|
+
const newCursortPosition = this.progressWrapper.clientWidth;
|
|
237
|
+
this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
|
|
225
238
|
}
|
|
226
239
|
renderProgress(progress, autoCenter = false) {
|
|
227
240
|
this.progressWrapper.style.width = `${progress * 100}%`;
|