wavesurfer.js 7.0.0-alpha.22 → 7.0.0-alpha.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-plugin.d.ts +6 -5
- package/dist/base-plugin.js +4 -2
- 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/envelope.min.js +1 -0
- package/dist/plugins/minimap.d.ts +6 -4
- package/dist/plugins/minimap.js +18 -7
- package/dist/plugins/minimap.min.js +1 -0
- package/dist/plugins/multitrack.d.ts +1 -2
- package/dist/plugins/multitrack.js +15 -17
- package/dist/plugins/multitrack.min.js +1 -0
- package/dist/plugins/regions.d.ts +5 -2
- package/dist/plugins/regions.js +26 -10
- package/dist/plugins/regions.min.js +1 -0
- package/dist/plugins/timeline.d.ts +4 -2
- package/dist/plugins/timeline.js +13 -4
- package/dist/plugins/timeline.min.js +1 -0
- package/dist/renderer.d.ts +3 -1
- package/dist/renderer.js +26 -13
- package/dist/{index.d.ts → wavesurfer.d.ts} +18 -35
- package/dist/{index.js → wavesurfer.js} +49 -102
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +20 -5
- package/dist/wavesurfer.Minimap.min.js +0 -1
- package/dist/wavesurfer.Multitrack.min.js +0 -1
- package/dist/wavesurfer.Regions.min.js +0 -1
- package/dist/wavesurfer.Timeline.min.js +0 -1
package/dist/base-plugin.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import EventEmitter, { type GeneralEventTypes } from './event-emitter.js';
|
|
2
|
-
import WaveSurfer, { type WaveSurferPluginParams } from './
|
|
2
|
+
import WaveSurfer, { type WaveSurferPluginParams } from './wavesurfer.js';
|
|
3
3
|
export declare class BasePlugin<EventTypes extends GeneralEventTypes, Options> extends EventEmitter<EventTypes> {
|
|
4
|
-
protected wavesurfer
|
|
5
|
-
protected container
|
|
6
|
-
protected wrapper
|
|
4
|
+
protected wavesurfer?: WaveSurfer;
|
|
5
|
+
protected container?: HTMLElement;
|
|
6
|
+
protected wrapper?: HTMLElement;
|
|
7
7
|
protected subscriptions: (() => void)[];
|
|
8
8
|
protected options: Options;
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(options: Options);
|
|
10
|
+
init(params: WaveSurferPluginParams): void;
|
|
10
11
|
destroy(): void;
|
|
11
12
|
}
|
|
12
13
|
export default BasePlugin;
|
package/dist/base-plugin.js
CHANGED
|
@@ -5,12 +5,14 @@ export class BasePlugin extends EventEmitter {
|
|
|
5
5
|
wrapper;
|
|
6
6
|
subscriptions = [];
|
|
7
7
|
options;
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(options) {
|
|
9
9
|
super();
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
init(params) {
|
|
10
13
|
this.wavesurfer = params.wavesurfer;
|
|
11
14
|
this.container = params.container;
|
|
12
15
|
this.wrapper = params.wrapper;
|
|
13
|
-
this.options = options;
|
|
14
16
|
}
|
|
15
17
|
destroy() {
|
|
16
18
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
package/dist/player.d.ts
CHANGED
|
@@ -1,27 +1,44 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6
|
-
|
|
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.
|
|
18
|
+
this.onceMediaEvent('play', () => {
|
|
17
19
|
this.hasPlayedOnce = true;
|
|
18
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
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 '../
|
|
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(
|
|
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;
|
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) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Envelope=e():t.Envelope=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={284:(t,e,i)=>{i.d(e,{Z:()=>o});var s=i(139);class n extends s.Z{wavesurfer;container;wrapper;subscriptions=[];options;constructor(t){super(),this.options=t}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}}const o=n},139:(t,e,i)=>{i.d(e,{Z:()=>s});const s=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(t,e){const i=new CustomEvent(String(t),{detail:e});this.eventTarget.dispatchEvent(i)}on(t,e,i){const s=t=>{t instanceof CustomEvent&&e(t.detail)},n=String(t);return this.eventTarget.addEventListener(n,s,{once:i}),()=>this.eventTarget.removeEventListener(n,s)}once(t,e){return this.on(t,e,!0)}}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s](o,o.exports,i),o.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var s={};return(()=>{i.d(s,{default:()=>o});var t=i(284);const e={startTime:0,endTime:0,fadeInEnd:0,fadeOutStart:0,lineWidth:4,lineColor:"rgba(0, 0, 255, 0.5)",dragPointSize:10,dragPointFill:"rgba(255, 255, 255, 0.8)",dragPointStroke:"rgba(255, 255, 255, 0.8)"};class n extends t.Z{options;svg=null;audioContext=null;gainNode=null;volume=1;isFadingIn=!1;isFadingOut=!1;constructor(t){super(t),this.options=Object.assign({},e,t),this.options.lineColor=this.options.lineColor||e.lineColor,this.options.dragPointFill=this.options.dragPointFill||e.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||e.dragPointStroke,this.volume=this.options.volume??1}static create(t){return new n(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");let e;this.subscriptions.push(this.wavesurfer.once("decode",(({duration:t})=>{this.options.startTime=this.options.startTime||0,this.options.endTime=this.options.endTime||t,this.options.fadeInEnd=this.options.fadeInEnd||this.options.startTime,this.options.fadeOutStart=this.options.fadeOutStart||this.options.endTime,this.initWebAudio(),this.initSvg(),this.initFadeEffects()}))),this.subscriptions.push(this.wavesurfer.on("zoom",(()=>{e&&clearTimeout(e),e=setTimeout((()=>{this.svg?.remove(),this.initSvg()}),100)})))}makeDraggable(t,e){t.addEventListener("mousedown",(t=>{let i=t.clientX,s=t.clientY;const n=this.wavesurfer?.options.interact||!0;let o;this.wavesurfer?.toggleInteractive(!1);const r=t=>{const n=t.clientX-i,o=t.clientY-s;i=t.clientX,s=t.clientY,e(n,o)},a=()=>{document.removeEventListener("mousemove",r),document.removeEventListener("mouseup",a),o&&clearTimeout(o),o=setTimeout((()=>{this.wavesurfer?.toggleInteractive(n)}),100)};document.addEventListener("mousemove",r),document.addEventListener("mouseup",a),t.preventDefault(),t.stopPropagation()}))}renderPolyline(){if(!this.svg||!this.wrapper||!this.wavesurfer)return;const t=this.svg.querySelector("polyline"),e=t.points,i=e.getItem(1).y,s=this.wrapper.clientWidth,n=this.wavesurfer.getDuration();e.getItem(0).x=this.options.startTime/n*s,e.getItem(3).x=this.options.endTime/n*s;const o=this.svg.querySelector("line");o.setAttribute("x1",e.getItem(1).x.toString()),o.setAttribute("x2",e.getItem(2).x.toString()),o.setAttribute("y1",i.toString()),o.setAttribute("y2",i.toString());const r=this.svg.querySelectorAll("circle");for(let e=0;e<r.length;e++){const s=r[e],n=t.points.getItem(e+1);s.setAttribute("cx",n.x.toString()),s.setAttribute("cy",i.toString())}}initSvg(){if(!this.wrapper||!this.wavesurfer)return;const t=this.wrapper.clientWidth,e=this.wrapper.clientHeight,i=this.wavesurfer.getDuration(),s=document.createElementNS("http://www.w3.org/2000/svg","svg");s.setAttribute("width","100%"),s.setAttribute("height","100%"),s.setAttribute("viewBox",`0 0 ${t} ${e}`),s.setAttribute("preserveAspectRatio","none"),s.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),this.svg=s;const n=document.createElementNS("http://www.w3.org/2000/svg","polyline");n.setAttribute("points","0,0 0,0 0,0 0,0"),n.setAttribute("stroke",this.options.lineColor),n.setAttribute("stroke-width",this.options.lineWidth),n.setAttribute("fill","none"),n.setAttribute("style","pointer-events: none"),s.appendChild(n);const o=document.createElementNS("http://www.w3.org/2000/svg","line");o.setAttribute("stroke","none"),o.setAttribute("stroke-width",(3*this.options.lineWidth).toString()),o.setAttribute("style","cursor: ns-resize; pointer-events: all;"),s.appendChild(o);const r=n.points,a=this.options.dragPointSize/2,u=e-this.volume*e+a;r.getItem(0).x=this.options.startTime/i*t,r.getItem(0).y=e,r.getItem(1).x=this.options.fadeInEnd/i*t,r.getItem(1).y=u,r.getItem(2).x=this.options.fadeOutStart/i*t,r.getItem(2).y=u,r.getItem(3).x=this.options.endTime/i*t,r.getItem(3).y=e,[1,2].forEach((()=>{const t=document.createElementNS("http://www.w3.org/2000/svg","circle");t.setAttribute("r",(this.options.dragPointSize/2).toString()),t.setAttribute("fill",this.options.dragPointFill),t.setAttribute("stroke",this.options.dragPointStroke||this.options.dragPointFill),t.setAttribute("stroke-width","2"),t.setAttribute("style","cursor: ew-resize; pointer-events: all;"),s.appendChild(t)})),this.wrapper.appendChild(s),this.renderPolyline();const h=t=>{const i=r.getItem(1).y+t-a;if(i<-.5||i>e)return;r.getItem(1).y=i+a,r.getItem(2).y=i+a,this.renderPolyline();const s=Math.min(1,Math.max(0,(e-i)/e));this.onVolumeChange(s),this.renderPolyline()},d=(e,s,o)=>{const r=n.points.getItem(o),a=r.x+e,u=a/t*i;1===o&&u>this.options.fadeOutStart||u<this.options.startTime||2===o&&u<this.options.fadeInEnd||u>this.options.endTime||(r.x=a,1===o?(this.options.fadeInEnd=u,this.emit("fade-in-change",{time:u})):2===o&&(this.options.fadeOutStart=u,this.emit("fade-out-change",{time:u})),s>1||s<-1?h(s):this.renderPolyline())};this.makeDraggable(o,((t,e)=>h(e)));const l=s.querySelectorAll("circle");for(let t=0;t<l.length;t++){const e=t+1;this.makeDraggable(l[t],((t,i)=>d(t,i,e)))}}destroy(){this.svg?.remove(),super.destroy()}initWebAudio(){const t=this.wavesurfer?.getMediaElement();if(!t)return null;this.volume=this.options.volume??t.volume;const e=new window.AudioContext;this.gainNode=e.createGain(),this.gainNode.gain.value=this.volume,e.createMediaElementSource(t).connect(this.gainNode),this.gainNode.connect(e.destination),this.audioContext=e}naturalVolume(t){return 1e-4+.9999*Math.pow(t,3)}onVolumeChange(t){t=this.naturalVolume(t),this.volume=t,this.emit("volume-change",{volume:t}),this.gainNode&&(this.gainNode.gain.value=t)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(({currentTime:t})=>{if(!this.audioContext||!this.gainNode)return;if(!this.wavesurfer?.isPlaying())return;if("suspended"===this.audioContext.state&&this.audioContext.resume(),!this.isFadingIn&&t>=this.options.startTime&&t<=this.options.fadeInEnd)return this.isFadingIn=!0,this.gainNode.gain.setValueAtTime(0,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(this.volume,this.audioContext.currentTime+(this.options.fadeInEnd-t));if(!this.isFadingOut&&t>=this.options.fadeOutStart&&t<=this.options.endTime)return this.isFadingOut=!0,void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.endTime-t));let e=!1;this.isFadingIn&&(t<this.options.startTime||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,e=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.endTime)&&(this.isFadingOut=!1,e=!0),e&&(this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.gainNode.gain.value=this.volume)}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t){this.options.startTime=t,this.renderPolyline()}setEndTime(t){this.options.endTime=t,this.renderPolyline()}}const o=n})(),s.default})()));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
import { type WaveSurferPluginParams, type WaveSurferOptions } from '../
|
|
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;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Minimap=e():t.Minimap=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var s in i)t.o(i,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:i[s]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>p});const i=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(t,e){const i=new CustomEvent(String(t),{detail:e});this.eventTarget.dispatchEvent(i)}on(t,e,i){const s=t=>{t instanceof CustomEvent&&e(t.detail)},r=String(t);return this.eventTarget.addEventListener(r,s,{once:i}),()=>this.eventTarget.removeEventListener(r,s)}once(t,e){return this.on(t,e,!0)}},s=class extends i{wavesurfer;container;wrapper;subscriptions=[];options;constructor(t){super(),this.options=t}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}},r=class extends i{options;container;scrollContainer;wrapper;canvasWrapper;progressWrapper;timeout=null;isScrolling=!1;audioData=null;resizeObserver=null;constructor(t){super(),this.options=t;let e=null;if("string"==typeof this.options.container?e=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(e=this.options.container),!e)throw new Error("Container not found");const i=document.createElement("div"),s=i.attachShadow({mode:"open"});s.innerHTML=`\n <style>\n :host {\n user-select: none;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n ${this.options.hideScrollbar?"scrollbar-color: transparent;":""}\n }\n :host ::-webkit-scrollbar {\n display: ${this.options.hideScrollbar?"none":"auto"};\n }\n :host .wrapper {\n position: relative;\n min-width: 100%;\n z-index: 2;\n }\n :host .canvases {\n position: relative;\n height: ${this.options.height}px;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n height: ${this.options.height}px;\n image-rendering: pixelated;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: ${this.options.height}px;\n overflow: hidden;\n box-sizing: border-box;\n border-right-style: solid;\n border-right-width: ${this.options.cursorWidth}px;\n border-right-color: ${this.options.cursorColor||this.options.progressColor};\n }\n </style>\n\n <div class="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress"></div>\n </div>\n </div>\n `,this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),e.appendChild(i),this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",{relativeX:i})})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.audioData&&this.zoom(this.audioData,this.options.minPxPerSec)),100)})),this.resizeObserver.observe(this.scrollContainer)}getContainer(){return this.scrollContainer}getWrapper(){return this.wrapper}destroy(){this.container.remove(),this.resizeObserver?.disconnect()}delay(t,e=10){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}async renderPeaks(t,e,i,s){const r=null!=this.options.barWidth?this.options.barWidth*s:1,n=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?r/2:0,o=this.options.barRadius??0,a=t[0],h=a.length,d=Math.floor(e/(r+n))/h,l=i/2,c=1===t.length,p=c?a:t[1],u=c&&p.some((t=>t<0)),m=(t,i)=>{let c=0,m=0,g=0;const v=document.createElement("canvas");v.width=Math.round(e*(i-t)/h),v.height=this.options.height,v.style.width=`${Math.floor(v.width/s)}px`,v.style.left=`${Math.floor(t*e/s/h)}px`,this.canvasWrapper.appendChild(v);const y=v.getContext("2d",{desynchronized:!0});y.beginPath(),y.fillStyle=this.options.waveColor,y.roundRect||(y.roundRect=y.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*d);if(i>c){const t=Math.round(m*l),e=Math.round(g*l);y.roundRect(c*(r+n),l-t,r,t+e||1,o),c=i,m=0,g=0}const s=u?a[e]:Math.abs(a[e]),h=u?p[e]:Math.abs(p[e]);s>m&&(m=s),(u?h<-g:h>g)&&(g=h<0?-h:h)}y.fill(),y.closePath();const f=v.cloneNode();this.progressWrapper.appendChild(f);const b=f.getContext("2d",{desynchronized:!0});b.drawImage(v,0,0),b.globalCompositeOperation="source-in",b.fillStyle=this.options.progressColor,b.fillRect(0,0,v.width,v.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:g,scrollWidth:v,clientWidth:y}=this.scrollContainer,f=h/v,b=Math.floor(g*f),w=Math.ceil(Math.min(v,g+y)*f);m(b,w);const C=w-b;for(let t=w;t<h;t+=C)await this.delay((()=>{m(t,Math.min(h,t+C))}));for(let t=b-1;t>=0;t-=C)await this.delay((()=>{m(Math.max(0,t-C),t)}))}render(t){const e=window.devicePixelRatio||1,i=this.options.fillParent?this.scrollContainer.clientWidth*e:0,s=Math.max(1,t.duration*this.options.minPxPerSec),r=s>i?s:i,{height:n}=this.options;this.isScrolling=r>i,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.wrapper.style.width=`${Math.floor(r/e)}px`;const o=[t.getChannelData(0)];t.numberOfChannels>1&&o.push(t.getChannelData(1)),this.renderPeaks(o,r,n,e),this.audioData=t}zoom(t,e){const i=this.progressWrapper.clientWidth;this.options.minPxPerSec=e,this.render(t);const s=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=s-i}renderProgress(t,e=!1){if(this.progressWrapper.style.width=100*t+"%",this.isScrolling){const t=this.scrollContainer.clientWidth,i=t/2,s=this.progressWrapper.clientWidth,r=e?i:t;s>this.scrollContainer.scrollLeft+r&&(this.scrollContainer.scrollLeft=s-i)}}},n=class extends i{media;subscriptions=[];isExternalMedia=!1;hasPlayedOnce=!1;constructor(t){super(),t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),this.subscriptions.push(this.onceMediaEvent("play",(()=>{this.hasPlayedOnce=!0}))),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}onceMediaEvent(t,e){return this.onMediaEvent(t,e,{once:!0})}loadUrl(t){this.media.src=t}destroy(){this.media.pause(),this.subscriptions.forEach((t=>t())),this.isExternalMedia||this.media.remove()}play(){this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}seekTo(t){!this.hasPlayedOnce&&navigator.userAgent.match(/(iPad|iPhone|iPod)/g)&&this.media.play()?.then?.((()=>{setTimeout((()=>this.media.pause()),10)})),this.media.currentTime=t}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}},o=class extends i{unsubscribe=()=>{};start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}},a={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoCenter:!0};class h extends n{options;fetcher;decoder;renderer;timer;plugins=[];decodedData=null;canPlay=!1;static create(t){return new h(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.options=Object.assign({},a,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.decoder=new class{audioCtx=null;initAudioContext(t){this.audioCtx=new AudioContext({latencyHint:"playback",sampleRate:t})}constructor(){try{this.initAudioContext(3e3)}catch(t){this.initAudioContext(8e3)}}async decode(t){if(!this.audioCtx)throw new Error("AudioContext is not initialized");return this.audioCtx.decodeAudioData(t)}destroy(){this.audioCtx?.close(),this.audioCtx=null}},this.timer=new o,this.renderer=new r({container:this.options.container,height:this.options.height,waveColor:this.options.waveColor,progressColor:this.options.progressColor,cursorColor:this.options.cursorColor,cursorWidth:this.options.cursorWidth,minPxPerSec:this.options.minPxPerSec,fillParent:this.options.fillParent,barWidth:this.options.barWidth,barGap:this.options.barGap,barRadius:this.options.barRadius,hideScrollbar:this.options.hideScrollbar}),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent(),this.initPlugins();const e=this.options.url||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.options.autoCenter&&this.isPlaying()),this.emit("timeupdate",{currentTime:t})})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.onMediaEvent("canplay",(()=>{this.canPlay=!0,this.emit("canplay",{duration:this.getDuration()})})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",{currentTime:this.getCurrentTime()})})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(({relativeX:t})=>{if(this.options.interact){const e=this.getDuration()*t;this.seekTo(e),this.emit("seekClick",{currentTime:this.getCurrentTime()})}})))}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",{currentTime:t})})))}initReadyEvent(){const t=()=>{this.decodedData&&this.canPlay&&this.emit("ready",{duration:this.getDuration()})};this.subscriptions.push(this.on("decode",t),this.on("canplay",t))}initPlugins(){this.options.plugins?.length&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init({wavesurfer:this,container:this.renderer.getContainer(),wrapper:this.renderer.getWrapper()}),this.plugins.push(t),t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t))})),t}async load(t,e,i){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),null==e){const e=await this.fetcher.load(t),i=await this.decoder.decode(e);this.decodedData=i}else i||(i=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getDuration())))}))||0),this.decodedData={duration:i,numberOfChannels:e.length,sampleRate:e[0].length/i,getChannelData:t=>e[t]};this.renderer.render(this.decodedData),this.emit("decode",{duration:this.decodedData.duration})}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(this.decodedData,t),this.emit("zoom",{minPxPerSec:t})}getDecodedData(){return this.decodedData}getDuration(){return this.decodedData?.duration||this.getMediaElement()?.duration||0}toggleInteractive(t){this.options.interact=t}destroy(){this.emit("destroy"),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.decoder.destroy(),this.renderer.destroy(),super.destroy()}}const d=h,l={height:50,overlayColor:"rgba(100, 100, 100, 0.1)"};class c extends s{options;minimapWrapper;miniWavesurfer=null;overlay;constructor(t){super(t),this.options=Object.assign({},l,t),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay()}static create(t){return new c(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.container?.insertAdjacentElement("afterend",this.minimapWrapper),this.subscriptions.push(this.wavesurfer.on("decode",(()=>{this.initMinimap()})))}initMinimapWrapper(){const t=document.createElement("div");return t.style.position="relative",t}initOverlay(){const t=document.createElement("div");return t.setAttribute("style","position: absolute; z-index: 2; left: 0; top: 0; bottom: 0;"),t.style.backgroundColor=this.options.overlayColor,this.minimapWrapper.appendChild(t),t}initMinimap(){if(!this.wavesurfer||!this.wrapper)return;const t=this.wavesurfer.getDecodedData(),e=this.wavesurfer.getMediaElement();if(!t||!e)return;this.miniWavesurfer=d.create({...this.options,container:this.minimapWrapper,minPxPerSec:1,fillParent:!0,media:e,url:e.src,peaks:[t.getChannelData(0)],duration:t.duration});const i=Math.round(this.minimapWrapper.clientWidth/this.wrapper.clientWidth*100);this.overlay.style.width=`${i}%`,this.wavesurfer.on("timeupdate",(({currentTime:e})=>{const s=Math.max(0,Math.min(e/t.duration*100-i/2,100-i)).toFixed(2);this.overlay.style.left=`${s}%`}))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}}const p=c;return e.default})()));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WaveSurferOptions } from '../
|
|
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,10 +171,10 @@ class MultiTrack extends EventEmitter {
|
|
|
171
171
|
initTimeline() {
|
|
172
172
|
if (this.timeline)
|
|
173
173
|
this.timeline.destroy();
|
|
174
|
-
this.timeline = this.wavesurfers[0].registerPlugin(TimelinePlugin
|
|
174
|
+
this.timeline = this.wavesurfers[0].registerPlugin(TimelinePlugin.create({
|
|
175
175
|
duration: this.maxDuration,
|
|
176
176
|
container: this.rendering.containers[0].parentElement,
|
|
177
|
-
});
|
|
177
|
+
}));
|
|
178
178
|
}
|
|
179
179
|
updatePosition(time, autoCenter = false) {
|
|
180
180
|
const precisionSeconds = 0.3;
|
|
@@ -214,19 +214,17 @@ class MultiTrack extends EventEmitter {
|
|
|
214
214
|
}
|
|
215
215
|
onDrag(index, delta) {
|
|
216
216
|
this.setIsDragging();
|
|
217
|
-
const newTime = this.tracks[index].startPosition + delta * this.maxDuration;
|
|
218
|
-
this.onMove(index, newTime);
|
|
219
|
-
}
|
|
220
|
-
onMove(index, newStartPosition) {
|
|
221
217
|
const track = this.tracks[index];
|
|
222
218
|
if (!track.draggable)
|
|
223
219
|
return;
|
|
220
|
+
const newStartPosition = track.startPosition + delta * this.maxDuration;
|
|
224
221
|
const mainIndex = this.tracks.findIndex((item) => item.url && !item.draggable);
|
|
225
222
|
const mainTrack = this.tracks[mainIndex];
|
|
226
223
|
const minStart = (mainTrack ? mainTrack.startPosition : 0) - this.durations[index];
|
|
227
224
|
const maxStart = mainTrack ? mainTrack.startPosition + this.durations[mainIndex] : this.maxDuration;
|
|
228
225
|
if (newStartPosition >= minStart && newStartPosition <= maxStart) {
|
|
229
226
|
track.startPosition = newStartPosition;
|
|
227
|
+
this.initDurations(this.durations);
|
|
230
228
|
this.rendering.setContainerOffsets();
|
|
231
229
|
this.updatePosition(this.currentTime);
|
|
232
230
|
this.emit('start-position-change', { id: track.id, startPosition: newStartPosition });
|