wavesurfer.js 7.0.0-alpha.36 → 7.0.0-alpha.38
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 +12 -4
- package/dist/base-plugin.d.ts +6 -1
- package/dist/decoder.d.ts +8 -8
- package/dist/decoder.js +38 -44
- package/dist/event-emitter.d.ts +16 -10
- package/dist/event-emitter.js +38 -16
- package/dist/player.d.ts +0 -1
- package/dist/player.js +0 -11
- package/dist/plugins/envelope.d.ts +5 -11
- package/dist/plugins/envelope.js +6 -5
- package/dist/plugins/envelope.min.js +1 -1
- package/dist/plugins/minimap.d.ts +5 -4
- package/dist/plugins/minimap.js +2 -1
- package/dist/plugins/minimap.min.js +1 -1
- package/dist/plugins/multitrack.d.ts +17 -17
- package/dist/plugins/multitrack.js +5 -4
- package/dist/plugins/multitrack.min.js +1 -1
- package/dist/plugins/record.d.ts +2 -2
- package/dist/plugins/record.min.js +1 -1
- package/dist/plugins/regions.d.ts +13 -27
- package/dist/plugins/regions.js +26 -19
- package/dist/plugins/regions.min.js +1 -1
- package/dist/plugins/timeline.d.ts +5 -5
- package/dist/plugins/timeline.js +2 -1
- package/dist/plugins/timeline.min.js +1 -1
- package/dist/renderer.d.ts +1 -3
- package/dist/renderer.js +1 -1
- package/dist/timer.d.ts +1 -1
- package/dist/wavesurfer.d.ts +26 -44
- package/dist/wavesurfer.js +21 -21
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ Import like this:
|
|
|
15
15
|
import WaveSurfer from 'wavesurfer.js'
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
To import a plugin, e.g. the Timeline plugin:
|
|
19
|
+
```
|
|
20
|
+
import Timeline from 'wavesurfer.js/dist/plugins/timeline'
|
|
21
|
+
```
|
|
22
|
+
|
|
18
23
|
TypeScript types are included in the package, so there's no need to install `@types/wavesurfer.js`.
|
|
19
24
|
|
|
20
25
|
## Why upgrade to wavesurfer.ts?
|
|
@@ -34,23 +39,26 @@ The "official" plugins have been completely rewritten and enhanced:
|
|
|
34
39
|
* [Envelope](https://wavesurfer-ts.pages.dev/tutorial/#/examples/envelope.js) – a graphical interface to add fade-in and -out effects and control volume
|
|
35
40
|
* [Record](https://wavesurfer-ts.pages.dev/tutorial/#/examples/record.js) – records audio from the microphone and renders a waveform
|
|
36
41
|
|
|
42
|
+
## Documentation
|
|
43
|
+
See the documentation on wavesurfer.js [methods](https://wavesurfer-ts.pages.dev/docs/classes/wavesurfer.WaveSurfer), [options](https://wavesurfer-ts.pages.dev/docs/types/wavesurfer.WaveSurferOptions) and [events](https://wavesurfer-ts.pages.dev/docs/types/wavesurfer.WaveSurferEvents) on our website.
|
|
44
|
+
|
|
37
45
|
## Migrating from v6 and lower
|
|
38
46
|
|
|
39
47
|
Most options, events, and methods are similar to those in previous versions.
|
|
40
48
|
|
|
41
49
|
### Notable differences
|
|
42
50
|
* The `backend` option is removed – HTML5 audio (or video) is the only playback mechanism. However, you can still connect wavesurfer to Web Audio via `MediaElementSourceNode`. See this [example](https://wavesurfer-ts.pages.dev/tutorial/#/examples/webaudio.js).
|
|
43
|
-
* The Markers plugin is removed – use the Regions plugin with `startTime` equal to `endTime
|
|
44
|
-
*
|
|
51
|
+
* The Markers plugin is removed – use the Regions plugin with `startTime` equal to `endTime`.
|
|
52
|
+
* No Microphone plugn – superseded by the new Record plugin with more features.
|
|
53
|
+
* No Spectrogram, Cursor and Playhead plugins yet – to be done.
|
|
45
54
|
|
|
46
55
|
### Removed methods
|
|
47
56
|
* `getFilters`, `setFilter` – as there's no Web Audio "backend"
|
|
48
57
|
* `cancelAjax` – ajax is replaced by `fetch`
|
|
49
58
|
* `loadBlob` – use `URL.createObjectURL()` to convert a blob to a URL and call `load(url)` instead
|
|
50
|
-
* `un`, `unAll` – the `on` method now returns an unsubscribe function. E.g., `const unsubscribe = wavesurfer.on('ready', () => ...)`
|
|
51
59
|
* `skipForward`, `skipBackward`, `setPlayEnd` – can be implemented using `setTime(time)`
|
|
52
60
|
* `exportPCM` is renamed to `getDecodedData` and doesn't take any params
|
|
53
|
-
* `toggleMute` is now called `
|
|
61
|
+
* `toggleMute` is now called `setMuted(true | false)`
|
|
54
62
|
* `setHeight`, `setWaveColor`, `setCursorColor`, etc. – use `setOptions` with the corresponding params instead. E.g., `wavesurfer.setOptions({ height: 300, waveColor: '#abc' })`
|
|
55
63
|
|
|
56
64
|
See the complete [documentation of the new API](https://wavesurfer-ts.pages.dev/docs/classes/wavesurfer.WaveSurfer).
|
package/dist/base-plugin.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import EventEmitter, { type GeneralEventTypes } from './event-emitter.js';
|
|
2
|
-
import
|
|
2
|
+
import type WaveSurfer from './wavesurfer.js';
|
|
3
3
|
export type GenericPlugin = BasePlugin<GeneralEventTypes, unknown>;
|
|
4
|
+
export type WaveSurferPluginParams = {
|
|
5
|
+
wavesurfer: WaveSurfer;
|
|
6
|
+
container: HTMLElement;
|
|
7
|
+
wrapper: HTMLElement;
|
|
8
|
+
};
|
|
4
9
|
export declare class BasePlugin<EventTypes extends GeneralEventTypes, Options> extends EventEmitter<EventTypes> {
|
|
5
10
|
protected wavesurfer?: WaveSurfer;
|
|
6
11
|
protected container?: HTMLElement;
|
package/dist/decoder.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
1
|
+
/** Decode an array buffer into an audio buffer */
|
|
2
|
+
declare function decode(audioData: ArrayBuffer): Promise<AudioBuffer>;
|
|
3
|
+
/** Create an audio buffer from pre-decoded audio data */
|
|
4
|
+
declare function createBuffer(channelData: Float32Array[] | Array<number[]>, duration: number): AudioBuffer;
|
|
5
|
+
declare const Decoder: {
|
|
6
|
+
decode: typeof decode;
|
|
7
|
+
createBuffer: typeof createBuffer;
|
|
8
|
+
};
|
|
9
9
|
export default Decoder;
|
package/dist/decoder.js
CHANGED
|
@@ -1,50 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/** Decode an array buffer into an audio buffer */
|
|
2
|
+
async function decode(audioData) {
|
|
3
|
+
const DEFAULT_SAMPLE_RATE = 8000; // All user agents are required to support a range of 8000Hz to 96000Hz
|
|
4
|
+
const MIN_SAMPLE_RATE = 3000; // Chrome, Safari can do 3kHz
|
|
5
|
+
let audioCtx;
|
|
6
|
+
try {
|
|
7
|
+
audioCtx = new AudioContext({
|
|
8
|
+
sampleRate: MIN_SAMPLE_RATE,
|
|
6
9
|
});
|
|
7
10
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const FALLBACK_SAMPLE_RATE = 8000; // Firefox
|
|
13
|
-
try {
|
|
14
|
-
this.initAudioContext(DEFAULT_SAMPLE_RATE);
|
|
15
|
-
}
|
|
16
|
-
catch (e) {
|
|
17
|
-
this.initAudioContext(FALLBACK_SAMPLE_RATE);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
async decode(audioData) {
|
|
21
|
-
if (!this.audioCtx) {
|
|
22
|
-
throw new Error('AudioContext is not initialized');
|
|
23
|
-
}
|
|
24
|
-
return this.audioCtx.decodeAudioData(audioData);
|
|
25
|
-
}
|
|
26
|
-
createBuffer(channelData, duration) {
|
|
27
|
-
// If a single array of numbers is passed, make it an array of arrays
|
|
28
|
-
if (typeof channelData[0] === 'number')
|
|
29
|
-
channelData = [channelData];
|
|
30
|
-
// Normalize to -1..1
|
|
31
|
-
if (channelData[0].some((n) => n > 1 || n < -1)) {
|
|
32
|
-
const max = Math.max(...channelData[0]);
|
|
33
|
-
channelData = channelData.map((channel) => channel.map((n) => n / max));
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
length: channelData[0].length,
|
|
37
|
-
duration,
|
|
38
|
-
numberOfChannels: channelData.length,
|
|
39
|
-
sampleRate: channelData[0].length / duration,
|
|
40
|
-
getChannelData: (i) => channelData?.[i],
|
|
41
|
-
copyFromChannel: AudioBuffer.prototype.copyFromChannel,
|
|
42
|
-
copyToChannel: AudioBuffer.prototype.copyToChannel,
|
|
43
|
-
};
|
|
11
|
+
catch (_) {
|
|
12
|
+
audioCtx = new AudioContext({
|
|
13
|
+
sampleRate: DEFAULT_SAMPLE_RATE,
|
|
14
|
+
});
|
|
44
15
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
16
|
+
const decode = audioCtx.decodeAudioData(audioData);
|
|
17
|
+
decode.finally(() => audioCtx.close());
|
|
18
|
+
return decode;
|
|
19
|
+
}
|
|
20
|
+
/** Create an audio buffer from pre-decoded audio data */
|
|
21
|
+
function createBuffer(channelData, duration) {
|
|
22
|
+
// If a single array of numbers is passed, make it an array of arrays
|
|
23
|
+
if (typeof channelData[0] === 'number')
|
|
24
|
+
channelData = [channelData];
|
|
25
|
+
// Normalize to -1..1
|
|
26
|
+
if (channelData[0].some((n) => n > 1 || n < -1)) {
|
|
27
|
+
const max = Math.max(...channelData[0]);
|
|
28
|
+
channelData = channelData.map((channel) => channel.map((n) => n / max));
|
|
48
29
|
}
|
|
30
|
+
return {
|
|
31
|
+
length: channelData[0].length,
|
|
32
|
+
duration,
|
|
33
|
+
numberOfChannels: channelData.length,
|
|
34
|
+
sampleRate: channelData[0].length / duration,
|
|
35
|
+
getChannelData: (i) => channelData?.[i],
|
|
36
|
+
copyFromChannel: AudioBuffer.prototype.copyFromChannel,
|
|
37
|
+
copyToChannel: AudioBuffer.prototype.copyToChannel,
|
|
38
|
+
};
|
|
49
39
|
}
|
|
40
|
+
const Decoder = {
|
|
41
|
+
decode,
|
|
42
|
+
createBuffer,
|
|
43
|
+
};
|
|
50
44
|
export default Decoder;
|
package/dist/event-emitter.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
export
|
|
2
|
-
[
|
|
3
|
-
}
|
|
1
|
+
export type GeneralEventTypes = {
|
|
2
|
+
[EventName: string]: any[];
|
|
3
|
+
};
|
|
4
|
+
type EventListener<EventTypes extends GeneralEventTypes, EventName extends keyof EventTypes> = (...args: EventTypes[EventName]) => void;
|
|
5
|
+
/** A simple event emitter that can be used to listen to and emit events. */
|
|
4
6
|
declare class EventEmitter<EventTypes extends GeneralEventTypes> {
|
|
5
|
-
private
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/** Subscribe to an event
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
|
|
7
|
+
private listeners;
|
|
8
|
+
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
9
|
+
on<EventName extends keyof EventTypes>(eventName: EventName, listener: EventListener<EventTypes, EventName>): () => void;
|
|
10
|
+
/** Subscribe to an event only once */
|
|
11
|
+
once<EventName extends keyof EventTypes>(eventName: EventName, listener: EventListener<EventTypes, EventName>): () => void;
|
|
12
|
+
/** Unsubscribe from an event */
|
|
13
|
+
un<EventName extends keyof EventTypes>(eventName: EventName, listener: EventListener<EventTypes, EventName>): void;
|
|
14
|
+
/** Clear all events */
|
|
15
|
+
unAll(): void;
|
|
16
|
+
/** Emit an event */
|
|
17
|
+
protected emit<EventName extends keyof EventTypes>(eventName: EventName, ...args: EventTypes[EventName]): void;
|
|
12
18
|
}
|
|
13
19
|
export default EventEmitter;
|
package/dist/event-emitter.js
CHANGED
|
@@ -1,25 +1,47 @@
|
|
|
1
|
+
/** A simple event emitter that can be used to listen to and emit events. */
|
|
1
2
|
class EventEmitter {
|
|
2
3
|
constructor() {
|
|
3
|
-
this.
|
|
4
|
+
this.listeners = {};
|
|
4
5
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.
|
|
6
|
+
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
7
|
+
on(eventName, listener) {
|
|
8
|
+
if (!this.listeners[eventName]) {
|
|
9
|
+
this.listeners[eventName] = new Set();
|
|
10
|
+
}
|
|
11
|
+
this.listeners[eventName].add(listener);
|
|
12
|
+
return () => this.un(eventName, listener);
|
|
8
13
|
}
|
|
9
|
-
/** Subscribe to an event
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
/** Subscribe to an event only once */
|
|
15
|
+
once(eventName, listener) {
|
|
16
|
+
// The actual subscription
|
|
17
|
+
const unsubscribe = this.on(eventName, listener);
|
|
18
|
+
// Another subscription that will unsubscribe the actual subscription and itself after the first event
|
|
19
|
+
const unsubscribeOnce = this.on(eventName, () => {
|
|
20
|
+
unsubscribe();
|
|
21
|
+
unsubscribeOnce();
|
|
22
|
+
});
|
|
23
|
+
return unsubscribe;
|
|
24
|
+
}
|
|
25
|
+
/** Unsubscribe from an event */
|
|
26
|
+
un(eventName, listener) {
|
|
27
|
+
if (this.listeners[eventName]) {
|
|
28
|
+
if (listener) {
|
|
29
|
+
this.listeners[eventName].delete(listener);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
delete this.listeners[eventName];
|
|
14
33
|
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Clear all events */
|
|
37
|
+
unAll() {
|
|
38
|
+
this.listeners = {};
|
|
19
39
|
}
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
/** Emit an event */
|
|
41
|
+
emit(eventName, ...args) {
|
|
42
|
+
if (this.listeners[eventName]) {
|
|
43
|
+
this.listeners[eventName].forEach((listener) => listener(...args));
|
|
44
|
+
}
|
|
23
45
|
}
|
|
24
46
|
}
|
|
25
47
|
export default EventEmitter;
|
package/dist/player.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ declare class Player<T extends GeneralEventTypes> extends EventEmitter<T> {
|
|
|
8
8
|
protected media: HTMLMediaElement;
|
|
9
9
|
protected subscriptions: Array<() => void>;
|
|
10
10
|
private isExternalMedia;
|
|
11
|
-
private hasPlayedOnce;
|
|
12
11
|
constructor(options: PlayerOptions);
|
|
13
12
|
protected onMediaEvent(event: keyof HTMLMediaElementEventMap, callback: () => void, options?: AddEventListenerOptions): () => void;
|
|
14
13
|
protected onceMediaEvent(event: keyof HTMLMediaElementEventMap, callback: () => void): () => void;
|
package/dist/player.js
CHANGED
|
@@ -4,7 +4,6 @@ class Player extends EventEmitter {
|
|
|
4
4
|
super();
|
|
5
5
|
this.subscriptions = [];
|
|
6
6
|
this.isExternalMedia = false;
|
|
7
|
-
this.hasPlayedOnce = false;
|
|
8
7
|
if (options.media) {
|
|
9
8
|
this.media = options.media;
|
|
10
9
|
this.isExternalMedia = true;
|
|
@@ -12,11 +11,6 @@ class Player extends EventEmitter {
|
|
|
12
11
|
else {
|
|
13
12
|
this.media = document.createElement('audio');
|
|
14
13
|
}
|
|
15
|
-
this.subscriptions.push(
|
|
16
|
-
// Track the first play() call
|
|
17
|
-
this.onceMediaEvent('play', () => {
|
|
18
|
-
this.hasPlayedOnce = true;
|
|
19
|
-
}));
|
|
20
14
|
// Autoplay
|
|
21
15
|
if (options.autoplay) {
|
|
22
16
|
this.media.autoplay = true;
|
|
@@ -57,11 +51,6 @@ class Player extends EventEmitter {
|
|
|
57
51
|
}
|
|
58
52
|
/** Jumpt to a specific time in the audio (in seconds) */
|
|
59
53
|
setTime(time) {
|
|
60
|
-
if (!this.hasPlayedOnce) {
|
|
61
|
-
this.media.play()?.then?.(() => {
|
|
62
|
-
setTimeout(() => this.media.pause(), 10);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
54
|
this.media.currentTime = time;
|
|
66
55
|
}
|
|
67
56
|
/** Get the duration of the audio in seconds */
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
import type { WaveSurferPluginParams } from '../wavesurfer.js';
|
|
1
|
+
import BasePlugin, { type WaveSurferPluginParams } from '../base-plugin.js';
|
|
3
2
|
export type EnvelopePluginOptions = {
|
|
4
3
|
fadeInStart?: number;
|
|
5
4
|
fadeInEnd?: number;
|
|
@@ -24,15 +23,9 @@ declare const defaultOptions: {
|
|
|
24
23
|
dragPointStroke: string;
|
|
25
24
|
};
|
|
26
25
|
export type EnvelopePluginEvents = {
|
|
27
|
-
'fade-in-change':
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'fade-out-change': {
|
|
31
|
-
time: number;
|
|
32
|
-
};
|
|
33
|
-
'volume-change': {
|
|
34
|
-
volume: number;
|
|
35
|
-
};
|
|
26
|
+
'fade-in-change': [time: number];
|
|
27
|
+
'fade-out-change': [time: number];
|
|
28
|
+
'volume-change': [volume: number];
|
|
36
29
|
};
|
|
37
30
|
declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePluginOptions> {
|
|
38
31
|
protected options: EnvelopePluginOptions & typeof defaultOptions;
|
|
@@ -44,6 +37,7 @@ declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePl
|
|
|
44
37
|
private isFadingOut;
|
|
45
38
|
constructor(options: EnvelopePluginOptions);
|
|
46
39
|
static create(options: EnvelopePluginOptions): EnvelopePlugin;
|
|
40
|
+
/** Called by wavesurfer, don't call manually */
|
|
47
41
|
init(params: WaveSurferPluginParams): void;
|
|
48
42
|
private makeDraggable;
|
|
49
43
|
private renderPolyline;
|
package/dist/plugins/envelope.js
CHANGED
|
@@ -28,12 +28,13 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
28
28
|
static create(options) {
|
|
29
29
|
return new EnvelopePlugin(options);
|
|
30
30
|
}
|
|
31
|
+
/** Called by wavesurfer, don't call manually */
|
|
31
32
|
init(params) {
|
|
32
33
|
super.init(params);
|
|
33
34
|
if (!this.wavesurfer) {
|
|
34
35
|
throw Error('WaveSurfer is not initialized');
|
|
35
36
|
}
|
|
36
|
-
this.subscriptions.push(this.wavesurfer.once('decode', (
|
|
37
|
+
this.subscriptions.push(this.wavesurfer.once('decode', (duration) => {
|
|
37
38
|
this.options.fadeInStart = this.options.fadeInStart || 0;
|
|
38
39
|
this.options.fadeOutEnd = this.options.fadeOutEnd || duration;
|
|
39
40
|
this.options.fadeInEnd = this.options.fadeInEnd || this.options.fadeInStart;
|
|
@@ -183,11 +184,11 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
183
184
|
point.x = newX;
|
|
184
185
|
if (index === 1) {
|
|
185
186
|
this.options.fadeInEnd = newTime;
|
|
186
|
-
this.emit('fade-in-change',
|
|
187
|
+
this.emit('fade-in-change', newTime);
|
|
187
188
|
}
|
|
188
189
|
else if (index === 2) {
|
|
189
190
|
this.options.fadeOutStart = newTime;
|
|
190
|
-
this.emit('fade-out-change',
|
|
191
|
+
this.emit('fade-out-change', newTime);
|
|
191
192
|
}
|
|
192
193
|
// Also allow dragging points vertically
|
|
193
194
|
if (dy > 1 || dy < -1) {
|
|
@@ -237,7 +238,7 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
237
238
|
onVolumeChange(volume) {
|
|
238
239
|
volume = this.naturalVolume(volume);
|
|
239
240
|
this.volume = volume;
|
|
240
|
-
this.emit('volume-change',
|
|
241
|
+
this.emit('volume-change', volume);
|
|
241
242
|
if (!this.gainNode)
|
|
242
243
|
return;
|
|
243
244
|
this.gainNode.gain.value = volume;
|
|
@@ -245,7 +246,7 @@ class EnvelopePlugin extends BasePlugin {
|
|
|
245
246
|
initFadeEffects() {
|
|
246
247
|
if (!this.audioContext || !this.wavesurfer)
|
|
247
248
|
return;
|
|
248
|
-
const unsub = this.wavesurfer.on('timeupdate', (
|
|
249
|
+
const unsub = this.wavesurfer.on('timeupdate', (currentTime) => {
|
|
249
250
|
if (!this.audioContext || !this.gainNode)
|
|
250
251
|
return;
|
|
251
252
|
if (!this.wavesurfer?.isPlaying())
|
|
@@ -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.Envelope=e():t.Envelope=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={284:(t,e,i)=>{i.d(e,{Z:()=>o});var
|
|
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{constructor(t){super(),this.subscriptions=[],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{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),s=this.on(t,(()=>{i(),s()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}}},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={fadeInStart:0,fadeOutEnd: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{constructor(t){super(t),this.svg=null,this.audioContext=null,this.gainNode=null,this.volume=1,this.isFadingIn=!1,this.isFadingOut=!1,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",(t=>{this.options.fadeInStart=this.options.fadeInStart||0,this.options.fadeOutEnd=this.options.fadeOutEnd||t,this.options.fadeInEnd=this.options.fadeInEnd||this.options.fadeInStart,this.options.fadeOutStart=this.options.fadeOutStart||this.options.fadeOutEnd,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?.toggleInteraction(!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?.toggleInteraction(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.fadeInStart/n*s,e.getItem(3).x=this.options.fadeOutEnd/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,h=e-this.volume*e+a;r.getItem(0).x=this.options.fadeInStart/i*t,r.getItem(0).y=e,r.getItem(1).x=this.options.fadeInEnd/i*t,r.getItem(1).y=h,r.getItem(2).x=this.options.fadeOutStart/i*t,r.getItem(2).y=h,r.getItem(3).x=this.options.fadeOutEnd/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 u=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,h=a/t*i;1===o&&h>this.options.fadeOutStart||h<this.options.fadeInStart||2===o&&h<this.options.fadeInEnd||h>this.options.fadeOutEnd||(r.x=a,1===o?(this.options.fadeInEnd=h,this.emit("fade-in-change",h)):2===o&&(this.options.fadeOutStart=h,this.emit("fade-out-change",h)),s>1||s<-1?u(s):this.renderPolyline())};this.makeDraggable(o,((t,e)=>u(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",t),this.gainNode&&(this.gainNode.gain.value=t)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(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.fadeInStart&&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.fadeOutEnd)return this.isFadingOut=!0,void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.fadeOutEnd-t));let e=!1;this.isFadingIn&&(t<this.options.fadeInStart||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,e=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.fadeOutEnd)&&(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.fadeInStart=t,this.renderPolyline()}setEndTime(t){this.options.fadeOutEnd=t,this.renderPolyline()}}const o=n})(),s.default})()));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import BasePlugin from '../base-plugin.js';
|
|
2
|
-
import { type
|
|
1
|
+
import BasePlugin, { type WaveSurferPluginParams } from '../base-plugin.js';
|
|
2
|
+
import { type WaveSurferOptions } from '../wavesurfer.js';
|
|
3
3
|
export type MinimapPluginOptions = {
|
|
4
4
|
overlayColor?: string;
|
|
5
5
|
insertPosition?: InsertPosition;
|
|
@@ -10,8 +10,8 @@ declare const defaultOptions: {
|
|
|
10
10
|
insertPosition: string;
|
|
11
11
|
};
|
|
12
12
|
export type MinimapPluginEvents = {
|
|
13
|
-
ready:
|
|
14
|
-
interaction:
|
|
13
|
+
ready: [];
|
|
14
|
+
interaction: [];
|
|
15
15
|
};
|
|
16
16
|
declare class MinimapPlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions> {
|
|
17
17
|
protected options: MinimapPluginOptions & typeof defaultOptions;
|
|
@@ -20,6 +20,7 @@ declare class MinimapPlugin extends BasePlugin<MinimapPluginEvents, MinimapPlugi
|
|
|
20
20
|
private overlay;
|
|
21
21
|
constructor(options: MinimapPluginOptions);
|
|
22
22
|
static create(options: MinimapPluginOptions): MinimapPlugin;
|
|
23
|
+
/** Called by wavesurfer, don't call manually */
|
|
23
24
|
init(params: WaveSurferPluginParams): void;
|
|
24
25
|
private initMinimapWrapper;
|
|
25
26
|
private initOverlay;
|
package/dist/plugins/minimap.js
CHANGED
|
@@ -16,6 +16,7 @@ class MinimapPlugin extends BasePlugin {
|
|
|
16
16
|
static create(options) {
|
|
17
17
|
return new MinimapPlugin(options);
|
|
18
18
|
}
|
|
19
|
+
/** Called by wavesurfer, don't call manually */
|
|
19
20
|
init(params) {
|
|
20
21
|
super.init(params);
|
|
21
22
|
if (!this.wavesurfer) {
|
|
@@ -68,7 +69,7 @@ class MinimapPlugin extends BasePlugin {
|
|
|
68
69
|
});
|
|
69
70
|
const overlayWidth = Math.round((this.minimapWrapper.clientWidth / this.wrapper.clientWidth) * 100);
|
|
70
71
|
this.overlay.style.width = `${overlayWidth}%`;
|
|
71
|
-
this.subscriptions.push(this.wavesurfer.on('timeupdate', (
|
|
72
|
+
this.subscriptions.push(this.wavesurfer.on('timeupdate', (currentTime) => {
|
|
72
73
|
const offset = Math.max(0, Math.min((currentTime / data.duration) * 100 - overlayWidth / 2, 100 - overlayWidth)).toFixed(2);
|
|
73
74
|
this.overlay.style.left = `${offset}%`;
|
|
74
75
|
}));
|
|
@@ -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.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:()=>u});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)}},s=class extends i{constructor(t){super(),this.subscriptions=[],this.options=t}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}};class n extends i{constructor(t,e){super(),this.options={height:0},this.timeout=null,this.isScrolling=!1,this.channelData=null,this.duration=null,this.resizeObserver=null,this.options={...e};let i=null;if("string"==typeof t.container?i=document.querySelector(t.container):t.container instanceof HTMLElement&&(i=t.container),!i)throw new Error("Container not found");const[s,n]=this.initHtml();i.appendChild(s),this.container=s,this.scrollContainer=n.querySelector(".scroll"),this.wrapper=n.querySelector(".wrapper"),this.canvasWrapper=n.querySelector(".canvases"),this.progressWrapper=n.querySelector(".progress"),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.reRender()),100)})),this.resizeObserver.observe(this.scrollContainer)}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"});return e.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 .noScrollbar {\n scrollbar-color: transparent;\n scrollbar-width: none;\n }\n :host .noScrollbar::-webkit-scrollbar {\n display: none;\n -webkit-appearance: none;\n }\n :host .wrapper {\n position: relative;\n overflow: visible;\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 image-rendering: pixelated;\n height: ${this.options.height}px;\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: 100%;\n overflow: hidden;\n box-sizing: border-box;\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 `,[t,e]}setOptions(t){this.options=t,this.reRender()}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,o=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?r/2:0,a=this.options.barRadius??0,h=t[0],l=h.length,d=Math.floor(e/(r+o))/l,c=i/2,p=1===t.length,u=p?h:t[1],m=p&&u.some((t=>t<0)),g=(t,i)=>{let n=0,p=0,g=0;const y=document.createElement("canvas");y.width=Math.round(e*(i-t)/l),y.height=this.options.height,y.style.width=`${Math.floor(y.width/s)}px`,y.style.height=`${this.options.height}px`,y.style.left=`${Math.floor(t*e/s/l)}px`,this.canvasWrapper.appendChild(y);const v=y.getContext("2d",{desynchronized:!0});v.beginPath(),v.fillStyle=this.options.waveColor??"",v.roundRect||(v.roundRect=v.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*d);if(i>n){const t=Math.round(p*c),e=Math.round(g*c);v.roundRect(n*(r+o),c-t,r,t+e||1,a),n=i,p=0,g=0}const s=m?h[e]:Math.abs(h[e]),l=m?u[e]:Math.abs(u[e]);s>p&&(p=s),(m?l<-g:l>g)&&(g=l<0?-l:l)}v.fill(),v.closePath();const f=y.cloneNode();this.progressWrapper.appendChild(f);const b=f.getContext("2d",{desynchronized:!0});y.width>0&&y.height>0&&b.drawImage(y,0,0),b.globalCompositeOperation="source-in",b.fillStyle=this.options.progressColor??"",b.fillRect(0,0,y.width,y.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:y,scrollWidth:v,clientWidth:f}=this.scrollContainer,b=l/v;let w=Math.min(n.MAX_CANVAS_WIDTH,f);w-=w%((r+o)/s);const C=Math.floor(Math.abs(y)*b),W=Math.ceil(C+w*b);g(C,W);const x=W-C;for(let t=W;t<l;t+=x)await this.delay((()=>{g(t,Math.min(l,t+x))}));for(let t=C-1;t>=0;t-=x)await this.delay((()=>{g(Math.max(0,t-x),t)}))}render(t,e){const i=window.devicePixelRatio||1,s=this.scrollContainer.clientWidth,n=Math.ceil(e*(this.options.minPxPerSec||0));this.isScrolling=n>s;const r=this.options.fillParent&&!this.isScrolling,o=(r?s:n)*i,{height:a}=this.options;this.wrapper.style.width=r?"100%":`${n}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.progressWrapper.style.borderRightStyle="solid",this.progressWrapper.style.borderRightColor=`${this.options.cursorColor||this.options.progressColor}`,this.progressWrapper.style.borderRightWidth=`${this.options.cursorWidth}px`,this.canvasWrapper.style.height=`${this.options.height}px`,this.renderPeaks(t,o,a,i),this.channelData=t,this.duration=e}reRender(){if(!this.channelData||!this.duration)return;const t=this.progressWrapper.clientWidth;this.render(this.channelData,this.duration);const e=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=e-t}zoom(t){this.options.minPxPerSec=t,this.reRender()}renderProgress(t,e=!1){if(!isNaN(t)&&(this.progressWrapper.style.width=100*t+"%",this.isScrolling&&this.options.autoCenter)){const{clientWidth:i,scrollLeft:s,scrollWidth:n}=this.scrollContainer,r=n*t,o=i/2,a=o/20;(r>s+(e?o:i)||r<s)&&(r-(s+o)>=a&&r<s+i?this.scrollContainer.scrollLeft+=a:this.scrollContainer.scrollLeft=r-o)}}}n.MAX_CANVAS_WIDTH=4e3;const r=n,o=class extends i{constructor(t){super(),this.subscriptions=[],this.isExternalMedia=!1,this.hasPlayedOnce=!1,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(){return this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}setTime(t){this.hasPlayedOnce||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}setSinkId(t){return this.media.setSinkId(t)}},a=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()}},h={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoCenter:!0};class l extends o{static create(t){return new l(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.canPlay=!1,this.options=Object.assign({},h,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},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)}}async decode(t){if(!this.audioCtx)throw new Error("AudioContext is not initialized");return this.audioCtx.decodeAudioData(t)}createBuffer(t,e){if("number"==typeof t[0]&&(t=[t]),t[0].some((t=>t>1||t<-1))){const e=Math.max(...t[0]);t=t.map((t=>t.map((t=>t/e))))}return{length:t[0].length,duration:e,numberOfChannels:t.length,sampleRate:t[0].length/e,getChannelData:e=>t?.[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}destroy(){this.audioCtx?.close(),this.audioCtx=null}},this.timer=new a,this.renderer=new r({container:this.options.container},this.options),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)}setOptions(t){this.options={...this.options,...t},this.renderer.setOptions(this.options)}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),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.getCurrentTime()>=this.getDuration()&&this.emit("finish")})),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})=>{this.options.interact&&(this.seekTo(t),this.emit("interaction"))})))}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}getActivePlugins(){return this.plugins}async load(t,e,i){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),this.emit("loading",{url: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=this.decoder.createBuffer(e,i);this.renderAudio(),this.emit("decode",{duration:this.getDuration()}),this.emit("redraw")}renderAudio(){if(!this.decodedData)return;const t=[this.decodedData.getChannelData(0)];this.decodedData.numberOfChannels>1&&t.push(this.decodedData.getChannelData(1)),this.renderer.render(t,this.decodedData.duration)}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",{minPxPerSec:t})}getDecodedData(){return this.decodedData}getDuration(){const t=super.getDuration();return t>0&&t<1/0?t:this.decodedData?.duration||0}toggleInteraction(t){this.options.interact=t}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}playPause(){return this.isPlaying()?(this.pause(),Promise.resolve()):this.play()}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}destroy(){this.emit("destroy"),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.decoder.destroy(),this.renderer.destroy(),super.destroy()}}const d=l,c={height:50,overlayColor:"rgba(100, 100, 100, 0.1)",insertPosition:"afterend"};class p extends s{constructor(t){super(t),this.miniWavesurfer=null,this.options=Object.assign({},c,t),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay()}static create(t){return new p(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");if(this.options.container){let t=null;"string"==typeof this.options.container?t=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(t=this.options.container),t?.appendChild(this.minimapWrapper)}else this.container?.insertAdjacentElement(this.options.insertPosition,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,peaks:[t.getChannelData(0)],duration:t.duration});const i=Math.round(this.minimapWrapper.clientWidth/this.wrapper.clientWidth*100);this.overlay.style.width=`${i}%`,this.subscriptions.push(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}%`}))),this.subscriptions.push(this.miniWavesurfer.on("interaction",(()=>{this.wavesurfer&&this.miniWavesurfer&&this.wavesurfer.setTime(this.miniWavesurfer.getCurrentTime()),this.container&&(this.container.scrollLeft=this.minimapWrapper.scrollLeft/this.minimapWrapper.scrollWidth*this.container.scrollWidth),this.emit("interaction")})),this.miniWavesurfer.on("ready",(()=>{this.emit("ready")})))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}}const u=p;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.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:()=>m});const i=class{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),s=this.on(t,(()=>{i(),s()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}},s=class extends i{constructor(t){super(),this.subscriptions=[],this.options=t}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}},n={decode:async function(t){let e;try{e=new AudioContext({sampleRate:3e3})}catch(t){e=new AudioContext({sampleRate:8e3})}const i=e.decodeAudioData(t);return i.finally((()=>e.close())),i},createBuffer:function(t,e){if("number"==typeof t[0]&&(t=[t]),t[0].some((t=>t>1||t<-1))){const e=Math.max(...t[0]);t=t.map((t=>t.map((t=>t/e))))}return{length:t[0].length,duration:e,numberOfChannels:t.length,sampleRate:t[0].length/e,getChannelData:e=>t?.[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};class r extends i{constructor(t,e){super(),this.options={height:0},this.timeout=null,this.isScrolling=!1,this.channelData=null,this.duration=null,this.resizeObserver=null,this.options={...e};let i=null;if("string"==typeof t.container?i=document.querySelector(t.container):t.container instanceof HTMLElement&&(i=t.container),!i)throw new Error("Container not found");const[s,n]=this.initHtml();i.appendChild(s),this.container=s,this.scrollContainer=n.querySelector(".scroll"),this.wrapper=n.querySelector(".wrapper"),this.canvasWrapper=n.querySelector(".canvases"),this.progressWrapper=n.querySelector(".progress"),this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",i)})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.reRender()),100)})),this.resizeObserver.observe(this.scrollContainer)}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"});return e.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 .noScrollbar {\n scrollbar-color: transparent;\n scrollbar-width: none;\n }\n :host .noScrollbar::-webkit-scrollbar {\n display: none;\n -webkit-appearance: none;\n }\n :host .wrapper {\n position: relative;\n overflow: visible;\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 image-rendering: pixelated;\n height: ${this.options.height}px;\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: 100%;\n overflow: hidden;\n box-sizing: border-box;\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 `,[t,e]}setOptions(t){this.options=t,this.reRender()}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 n=null!=this.options.barWidth?this.options.barWidth*s:1,o=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?n/2:0,a=this.options.barRadius??0,h=t[0],l=h.length,d=Math.floor(e/(n+o))/l,c=i/2,p=1===t.length,u=p?h:t[1],m=p&&u.some((t=>t<0)),g=(t,i)=>{let r=0,p=0,g=0;const y=document.createElement("canvas");y.width=Math.round(e*(i-t)/l),y.height=this.options.height,y.style.width=`${Math.floor(y.width/s)}px`,y.style.height=`${this.options.height}px`,y.style.left=`${Math.floor(t*e/s/l)}px`,this.canvasWrapper.appendChild(y);const f=y.getContext("2d",{desynchronized:!0});f.beginPath(),f.fillStyle=this.options.waveColor??"",f.roundRect||(f.roundRect=f.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*d);if(i>r){const t=Math.round(p*c),e=Math.round(g*c);f.roundRect(r*(n+o),c-t,n,t+e||1,a),r=i,p=0,g=0}const s=m?h[e]:Math.abs(h[e]),l=m?u[e]:Math.abs(u[e]);s>p&&(p=s),(m?l<-g:l>g)&&(g=l<0?-l:l)}f.fill(),f.closePath();const v=y.cloneNode();this.progressWrapper.appendChild(v);const b=v.getContext("2d",{desynchronized:!0});y.width>0&&y.height>0&&b.drawImage(y,0,0),b.globalCompositeOperation="source-in",b.fillStyle=this.options.progressColor??"",b.fillRect(0,0,y.width,y.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:y,scrollWidth:f,clientWidth:v}=this.scrollContainer,b=l/f;let w=Math.min(r.MAX_CANVAS_WIDTH,v);w-=w%((n+o)/s);const C=Math.floor(Math.abs(y)*b),W=Math.ceil(C+w*b);g(C,W);const M=W-C;for(let t=W;t<l;t+=M)await this.delay((()=>{g(t,Math.min(l,t+M))}));for(let t=C-1;t>=0;t-=M)await this.delay((()=>{g(Math.max(0,t-M),t)}))}render(t,e){const i=window.devicePixelRatio||1,s=this.scrollContainer.clientWidth,n=Math.ceil(e*(this.options.minPxPerSec||0));this.isScrolling=n>s;const r=this.options.fillParent&&!this.isScrolling,o=(r?s:n)*i,{height:a}=this.options;this.wrapper.style.width=r?"100%":`${n}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.progressWrapper.style.borderRightStyle="solid",this.progressWrapper.style.borderRightColor=`${this.options.cursorColor||this.options.progressColor}`,this.progressWrapper.style.borderRightWidth=`${this.options.cursorWidth}px`,this.canvasWrapper.style.height=`${this.options.height}px`,this.renderPeaks(t,o,a,i),this.channelData=t,this.duration=e}reRender(){if(!this.channelData||!this.duration)return;const t=this.progressWrapper.clientWidth;this.render(this.channelData,this.duration);const e=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=e-t}zoom(t){this.options.minPxPerSec=t,this.reRender()}renderProgress(t,e=!1){if(!isNaN(t)&&(this.progressWrapper.style.width=100*t+"%",this.isScrolling&&this.options.autoCenter)){const{clientWidth:i,scrollLeft:s,scrollWidth:n}=this.scrollContainer,r=n*t,o=i/2,a=o/20;(r>s+(e?o:i)||r<s)&&(r-(s+o)>=a&&r<s+i?this.scrollContainer.scrollLeft+=a:this.scrollContainer.scrollLeft=r-o)}}}r.MAX_CANVAS_WIDTH=4e3;const o=r,a=class extends i{constructor(t){super(),this.subscriptions=[],this.isExternalMedia=!1,t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),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(){return this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}setTime(t){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}setSinkId(t){return this.media.setSinkId(t)}},h=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()}},l={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoCenter:!0};class d extends a{static create(t){return new d(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.canPlay=!1,this.options=Object.assign({},l,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.timer=new h,this.renderer=new o({container:this.options.container},this.options),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)}setOptions(t){this.options={...this.options,...t},this.renderer.setOptions(this.options)}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",t)})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop(),this.getCurrentTime()>=this.getDuration()&&this.emit("finish")})),this.onMediaEvent("canplay",(()=>{this.canPlay=!0,this.emit("canplay",this.getDuration())})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(t=>{this.options.interact&&(this.canPlay&&this.seekTo(t),this.emit("interaction"))})))}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",t),this.emit("audioprocess",t)})))}initReadyEvent(){const t=()=>{this.decodedData&&this.canPlay&&this.emit("ready",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}getActivePlugins(){return this.plugins}async load(t,e,i){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),this.emit("load",t),e)i||(i=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getMediaElement().duration)))}))||0),this.decodedData=n.createBuffer(e,i);else{const e=await this.fetcher.load(t);this.decodedData=await n.decode(e)}this.renderAudio(),this.emit("decode",this.getDuration()),this.emit("redraw")}renderAudio(){if(!this.decodedData)return;const t=[this.decodedData.getChannelData(0)];this.decodedData.numberOfChannels>1&&t.push(this.decodedData.getChannelData(1)),this.renderer.render(t,this.decodedData.duration)}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}getDuration(){const t=super.getDuration();return t>0&&t<1/0?t:this.decodedData?.duration||0}toggleInteraction(t){this.options.interact=t}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}playPause(){return this.isPlaying()?(this.pause(),Promise.resolve()):this.play()}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}destroy(){this.emit("destroy"),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.renderer.destroy(),super.destroy()}}const c=d,p={height:50,overlayColor:"rgba(100, 100, 100, 0.1)",insertPosition:"afterend"};class u extends s{constructor(t){super(t),this.miniWavesurfer=null,this.options=Object.assign({},p,t),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay()}static create(t){return new u(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");if(this.options.container){let t=null;"string"==typeof this.options.container?t=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(t=this.options.container),t?.appendChild(this.minimapWrapper)}else this.container?.insertAdjacentElement(this.options.insertPosition,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=c.create({...this.options,container:this.minimapWrapper,minPxPerSec:1,fillParent:!0,media:e,peaks:[t.getChannelData(0)],duration:t.duration});const i=Math.round(this.minimapWrapper.clientWidth/this.wrapper.clientWidth*100);this.overlay.style.width=`${i}%`,this.subscriptions.push(this.wavesurfer.on("timeupdate",(e=>{const s=Math.max(0,Math.min(e/t.duration*100-i/2,100-i)).toFixed(2);this.overlay.style.left=`${s}%`}))),this.subscriptions.push(this.miniWavesurfer.on("interaction",(()=>{this.wavesurfer&&this.miniWavesurfer&&this.wavesurfer.setTime(this.miniWavesurfer.getCurrentTime()),this.container&&(this.container.scrollLeft=this.minimapWrapper.scrollLeft/this.minimapWrapper.scrollWidth*this.container.scrollWidth),this.emit("interaction")})),this.miniWavesurfer.on("ready",(()=>{this.emit("ready")})))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}}const m=u;return e.default})()));
|
|
@@ -36,38 +36,38 @@ export type MultitrackOptions = {
|
|
|
36
36
|
envelopeOptions?: EnvelopePluginOptions;
|
|
37
37
|
};
|
|
38
38
|
export type MultitrackEvents = {
|
|
39
|
-
canplay:
|
|
40
|
-
'start-position-change': {
|
|
39
|
+
canplay: [];
|
|
40
|
+
'start-position-change': [{
|
|
41
41
|
id: TrackId;
|
|
42
42
|
startPosition: number;
|
|
43
|
-
};
|
|
44
|
-
'start-cue-change': {
|
|
43
|
+
}];
|
|
44
|
+
'start-cue-change': [{
|
|
45
45
|
id: TrackId;
|
|
46
46
|
startCue: number;
|
|
47
|
-
};
|
|
48
|
-
'end-cue-change': {
|
|
47
|
+
}];
|
|
48
|
+
'end-cue-change': [{
|
|
49
49
|
id: TrackId;
|
|
50
50
|
endCue: number;
|
|
51
|
-
};
|
|
52
|
-
'fade-in-change': {
|
|
51
|
+
}];
|
|
52
|
+
'fade-in-change': [{
|
|
53
53
|
id: TrackId;
|
|
54
54
|
fadeInEnd: number;
|
|
55
|
-
};
|
|
56
|
-
'fade-out-change': {
|
|
55
|
+
}];
|
|
56
|
+
'fade-out-change': [{
|
|
57
57
|
id: TrackId;
|
|
58
58
|
fadeOutStart: number;
|
|
59
|
-
};
|
|
60
|
-
'volume-change': {
|
|
59
|
+
}];
|
|
60
|
+
'volume-change': [{
|
|
61
61
|
id: TrackId;
|
|
62
62
|
volume: number;
|
|
63
|
-
};
|
|
64
|
-
'intro-end-change': {
|
|
63
|
+
}];
|
|
64
|
+
'intro-end-change': [{
|
|
65
65
|
id: TrackId;
|
|
66
66
|
endTime: number;
|
|
67
|
-
};
|
|
68
|
-
drop: {
|
|
67
|
+
}];
|
|
68
|
+
drop: [{
|
|
69
69
|
id: TrackId;
|
|
70
|
-
};
|
|
70
|
+
}];
|
|
71
71
|
};
|
|
72
72
|
export type MultitrackTracks = Array<TrackOptions>;
|
|
73
73
|
declare class MultiTrack extends EventEmitter<MultitrackEvents> {
|