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