wavesurfer.js 7.0.0-alpha.36 → 7.0.0-alpha.37
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 +7 -4
- package/dist/base-plugin.d.ts +6 -1
- package/dist/event-emitter.d.ts +16 -10
- package/dist/event-emitter.js +38 -16
- 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 +25 -42
- package/dist/wavesurfer.js +10 -9
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,23 +34,26 @@ The "official" plugins have been completely rewritten and enhanced:
|
|
|
34
34
|
* [Envelope](https://wavesurfer-ts.pages.dev/tutorial/#/examples/envelope.js) – a graphical interface to add fade-in and -out effects and control volume
|
|
35
35
|
* [Record](https://wavesurfer-ts.pages.dev/tutorial/#/examples/record.js) – records audio from the microphone and renders a waveform
|
|
36
36
|
|
|
37
|
+
## Documentation
|
|
38
|
+
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.
|
|
39
|
+
|
|
37
40
|
## Migrating from v6 and lower
|
|
38
41
|
|
|
39
42
|
Most options, events, and methods are similar to those in previous versions.
|
|
40
43
|
|
|
41
44
|
### Notable differences
|
|
42
45
|
* 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
|
-
*
|
|
46
|
+
* The Markers plugin is removed – use the Regions plugin with `startTime` equal to `endTime`.
|
|
47
|
+
* No Microphone plugn – superseded by the new Record plugin with more features.
|
|
48
|
+
* No Spectrogram, Cursor and Playhead plugins yet – to be done.
|
|
45
49
|
|
|
46
50
|
### Removed methods
|
|
47
51
|
* `getFilters`, `setFilter` – as there's no Web Audio "backend"
|
|
48
52
|
* `cancelAjax` – ajax is replaced by `fetch`
|
|
49
53
|
* `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
54
|
* `skipForward`, `skipBackward`, `setPlayEnd` – can be implemented using `setTime(time)`
|
|
52
55
|
* `exportPCM` is renamed to `getDecodedData` and doesn't take any params
|
|
53
|
-
* `toggleMute` is now called `
|
|
56
|
+
* `toggleMute` is now called `setMuted(true | false)`
|
|
54
57
|
* `setHeight`, `setWaveColor`, `setCursorColor`, etc. – use `setOptions` with the corresponding params instead. E.g., `wavesurfer.setOptions({ height: 300, waveColor: '#abc' })`
|
|
55
58
|
|
|
56
59
|
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/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;
|
|
@@ -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:()=>u});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()))}};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",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 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>n){const t=Math.round(p*c),e=Math.round(g*c);f.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)}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(n.MAX_CANVAS_WIDTH,v);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",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.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("loading",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",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.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",(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})()));
|
|
@@ -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> {
|
|
@@ -77,7 +77,8 @@ class MultiTrack extends EventEmitter {
|
|
|
77
77
|
interact: false,
|
|
78
78
|
});
|
|
79
79
|
// Regions and markers
|
|
80
|
-
const wsRegions =
|
|
80
|
+
const wsRegions = RegionsPlugin.create();
|
|
81
|
+
ws.registerPlugin(wsRegions);
|
|
81
82
|
this.subscriptions.push(ws.once('decode', () => {
|
|
82
83
|
// Start and end cues
|
|
83
84
|
if (track.startCue != null || track.endCue != null) {
|
|
@@ -148,13 +149,13 @@ class MultiTrack extends EventEmitter {
|
|
|
148
149
|
fadeOutEnd: track.endCue,
|
|
149
150
|
volume: track.volume,
|
|
150
151
|
}));
|
|
151
|
-
this.subscriptions.push(envelope.on('volume-change', (
|
|
152
|
+
this.subscriptions.push(envelope.on('volume-change', (volume) => {
|
|
152
153
|
this.setIsDragging();
|
|
153
154
|
this.emit('volume-change', { id: track.id, volume });
|
|
154
|
-
}), envelope.on('fade-in-change', (
|
|
155
|
+
}), envelope.on('fade-in-change', (time) => {
|
|
155
156
|
this.setIsDragging();
|
|
156
157
|
this.emit('fade-in-change', { id: track.id, fadeInEnd: time });
|
|
157
|
-
}), envelope.on('fade-out-change', (
|
|
158
|
+
}), envelope.on('fade-out-change', (time) => {
|
|
158
159
|
this.setIsDragging();
|
|
159
160
|
this.emit('fade-out-change', { id: track.id, fadeOutStart: time });
|
|
160
161
|
}), this.on('start-cue-change', ({ id, startCue }) => {
|