wavesurfer.js 7.0.0-beta.11 → 7.0.0-beta.13
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 +22 -6
- package/dist/draggable.js +9 -4
- package/dist/fetcher.d.ts +1 -3
- package/dist/fetcher.js +2 -6
- package/dist/player.d.ts +2 -2
- package/dist/player.js +10 -8
- package/dist/plugins/envelope.d.ts +11 -3
- package/dist/plugins/envelope.js +17 -3
- package/dist/plugins/envelope.min.cjs +1 -1
- package/dist/plugins/hover.d.ts +35 -0
- package/dist/plugins/hover.js +101 -0
- package/dist/plugins/hover.min.cjs +1 -0
- package/dist/plugins/minimap.d.ts +1 -1
- package/dist/plugins/minimap.js +1 -1
- package/dist/plugins/minimap.min.cjs +1 -1
- package/dist/plugins/record.d.ts +1 -1
- package/dist/plugins/record.js +1 -1
- package/dist/plugins/record.min.cjs +1 -1
- package/dist/plugins/regions.d.ts +23 -2
- package/dist/plugins/regions.js +25 -20
- package/dist/plugins/regions.min.cjs +1 -1
- package/dist/plugins/spectrogram.d.ts +5 -1
- package/dist/plugins/spectrogram.js +154 -2
- package/dist/plugins/spectrogram.min.cjs +1 -1
- package/dist/plugins/timeline.d.ts +1 -1
- package/dist/plugins/timeline.js +1 -1
- package/dist/plugins/timeline.min.cjs +1 -1
- package/dist/renderer.js +5 -4
- package/dist/wavesurfer.d.ts +6 -4
- package/dist/wavesurfer.js +7 -5
- package/dist/wavesurfer.min.cjs +1 -1
- package/package.json +7 -5
- package/dist/plugins/spectrogram-fft.d.ts +0 -9
- package/dist/plugins/spectrogram-fft.js +0 -150
|
@@ -13,23 +13,42 @@ export type RegionsPluginEvents = {
|
|
|
13
13
|
'region-double-clicked': [region: Region, e: MouseEvent];
|
|
14
14
|
};
|
|
15
15
|
export type RegionEvents = {
|
|
16
|
+
/** Before the region is removed */
|
|
16
17
|
remove: [];
|
|
18
|
+
/** When the region's parameters are being updated */
|
|
17
19
|
update: [];
|
|
20
|
+
/** When dragging or resizing is finished */
|
|
18
21
|
'update-end': [];
|
|
22
|
+
/** On play */
|
|
19
23
|
play: [];
|
|
24
|
+
/** On mouse click */
|
|
20
25
|
click: [event: MouseEvent];
|
|
26
|
+
/** Double click */
|
|
21
27
|
dblclick: [event: MouseEvent];
|
|
28
|
+
/** Mouse over */
|
|
22
29
|
over: [event: MouseEvent];
|
|
30
|
+
/** Mouse leave */
|
|
23
31
|
leave: [event: MouseEvent];
|
|
24
32
|
};
|
|
25
33
|
export type RegionParams = {
|
|
34
|
+
/** The id of the region, any string */
|
|
26
35
|
id?: string;
|
|
36
|
+
/** The start position of the region (in seconds) */
|
|
27
37
|
start: number;
|
|
38
|
+
/** The end position of the region (in seconds) */
|
|
28
39
|
end?: number;
|
|
40
|
+
/** Allow/dissallow dragging the region */
|
|
29
41
|
drag?: boolean;
|
|
42
|
+
/** Allow/dissallow resizing the region */
|
|
30
43
|
resize?: boolean;
|
|
44
|
+
/** The color of the region (CSS color) */
|
|
31
45
|
color?: string;
|
|
46
|
+
/** Content string or HTML element */
|
|
32
47
|
content?: string | HTMLElement;
|
|
48
|
+
/** Min length when resizing (in seconds) */
|
|
49
|
+
minLength?: number;
|
|
50
|
+
/** Max length when resizing (in seconds) */
|
|
51
|
+
maxLength?: number;
|
|
33
52
|
};
|
|
34
53
|
export declare class Region extends EventEmitter<RegionEvents> {
|
|
35
54
|
private totalDuration;
|
|
@@ -41,13 +60,15 @@ export declare class Region extends EventEmitter<RegionEvents> {
|
|
|
41
60
|
resize: boolean;
|
|
42
61
|
color: string;
|
|
43
62
|
content?: HTMLElement;
|
|
63
|
+
minLength: number;
|
|
64
|
+
maxLength: number;
|
|
44
65
|
constructor(params: RegionParams, totalDuration: number);
|
|
45
66
|
private initElement;
|
|
46
67
|
private renderPosition;
|
|
47
68
|
private initMouseEvents;
|
|
48
69
|
private onStartMoving;
|
|
49
70
|
private onEndMoving;
|
|
50
|
-
_onUpdate(dx: number,
|
|
71
|
+
_onUpdate(dx: number, side?: 'start' | 'end'): void;
|
|
51
72
|
private onMove;
|
|
52
73
|
private onResize;
|
|
53
74
|
private onEndResizing;
|
|
@@ -65,7 +86,7 @@ export declare class Region extends EventEmitter<RegionEvents> {
|
|
|
65
86
|
/** Remove the region */
|
|
66
87
|
remove(): void;
|
|
67
88
|
}
|
|
68
|
-
declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
|
|
89
|
+
export declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
|
|
69
90
|
private regions;
|
|
70
91
|
private regionsContainer;
|
|
71
92
|
/** Create an instance of RegionsPlugin */
|
package/dist/plugins/regions.js
CHANGED
|
@@ -10,12 +10,16 @@ export class Region extends EventEmitter {
|
|
|
10
10
|
constructor(params, totalDuration) {
|
|
11
11
|
super();
|
|
12
12
|
this.totalDuration = totalDuration;
|
|
13
|
+
this.minLength = 0.01;
|
|
14
|
+
this.maxLength = Infinity;
|
|
13
15
|
this.id = params.id || `region-${Math.random().toString(32).slice(2)}`;
|
|
14
16
|
this.start = params.start;
|
|
15
17
|
this.end = params.end ?? params.start;
|
|
16
18
|
this.drag = params.drag ?? true;
|
|
17
19
|
this.resize = params.resize ?? true;
|
|
18
20
|
this.color = params.color ?? 'rgba(0, 0, 0, 0.1)';
|
|
21
|
+
this.minLength = params.minLength ?? this.minLength;
|
|
22
|
+
this.maxLength = params.maxLength ?? this.maxLength;
|
|
19
23
|
this.element = this.initElement(params.content);
|
|
20
24
|
this.renderPosition();
|
|
21
25
|
this.initMouseEvents();
|
|
@@ -34,13 +38,12 @@ export class Region extends EventEmitter {
|
|
|
34
38
|
transition: background-color 0.2s ease;
|
|
35
39
|
cursor: ${this.drag ? 'grab' : 'default'};
|
|
36
40
|
pointer-events: all;
|
|
37
|
-
padding: 0.2em ${isMarker ? 0.2 : 0.4}em;
|
|
38
|
-
pointer-events: all;
|
|
39
41
|
`);
|
|
40
42
|
// Init content
|
|
41
43
|
if (content) {
|
|
42
44
|
if (typeof content === 'string') {
|
|
43
45
|
this.content = document.createElement('div');
|
|
46
|
+
this.content.style.padding = `0.2em ${isMarker ? 0.2 : 0.4}em`;
|
|
44
47
|
this.content.textContent = content;
|
|
45
48
|
}
|
|
46
49
|
else {
|
|
@@ -81,9 +84,9 @@ export class Region extends EventEmitter {
|
|
|
81
84
|
}
|
|
82
85
|
renderPosition() {
|
|
83
86
|
const start = this.start / this.totalDuration;
|
|
84
|
-
const end = this.end / this.totalDuration;
|
|
87
|
+
const end = (this.totalDuration - this.end) / this.totalDuration;
|
|
85
88
|
this.element.style.left = `${start * 100}%`;
|
|
86
|
-
this.element.style.
|
|
89
|
+
this.element.style.right = `${end * 100}%`;
|
|
87
90
|
}
|
|
88
91
|
initMouseEvents() {
|
|
89
92
|
const { element } = this;
|
|
@@ -111,31 +114,33 @@ export class Region extends EventEmitter {
|
|
|
111
114
|
this.element.style.cursor = 'grab';
|
|
112
115
|
this.emit('update-end');
|
|
113
116
|
}
|
|
114
|
-
_onUpdate(dx,
|
|
117
|
+
_onUpdate(dx, side) {
|
|
115
118
|
if (!this.element.parentElement)
|
|
116
119
|
return;
|
|
117
120
|
const deltaSeconds = (dx / this.element.parentElement.clientWidth) * this.totalDuration;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
const newStart = !side || side === 'start' ? this.start + deltaSeconds : this.start;
|
|
122
|
+
const newEnd = !side || side === 'end' ? this.end + deltaSeconds : this.end;
|
|
123
|
+
const length = newEnd - newStart;
|
|
124
|
+
if (newStart > 0 &&
|
|
125
|
+
newEnd < this.totalDuration &&
|
|
126
|
+
newStart < newEnd &&
|
|
127
|
+
length >= this.minLength &&
|
|
128
|
+
length <= this.maxLength) {
|
|
129
|
+
this.start = newStart;
|
|
130
|
+
this.end = newEnd;
|
|
131
|
+
this.renderPosition();
|
|
132
|
+
this.emit('update');
|
|
133
|
+
}
|
|
129
134
|
}
|
|
130
135
|
onMove(dx) {
|
|
131
136
|
if (!this.drag)
|
|
132
137
|
return;
|
|
133
|
-
this._onUpdate(dx
|
|
138
|
+
this._onUpdate(dx);
|
|
134
139
|
}
|
|
135
140
|
onResize(dx, side) {
|
|
136
141
|
if (!this.resize)
|
|
137
142
|
return;
|
|
138
|
-
this._onUpdate(dx,
|
|
143
|
+
this._onUpdate(dx, side);
|
|
139
144
|
}
|
|
140
145
|
onEndResizing() {
|
|
141
146
|
if (!this.resize)
|
|
@@ -182,7 +187,7 @@ export class Region extends EventEmitter {
|
|
|
182
187
|
this.element = null;
|
|
183
188
|
}
|
|
184
189
|
}
|
|
185
|
-
class RegionsPlugin extends BasePlugin {
|
|
190
|
+
export class RegionsPlugin extends BasePlugin {
|
|
186
191
|
/** Create an instance of RegionsPlugin */
|
|
187
192
|
constructor(options) {
|
|
188
193
|
super(options);
|
|
@@ -300,7 +305,7 @@ class RegionsPlugin extends BasePlugin {
|
|
|
300
305
|
if (region) {
|
|
301
306
|
// Update the end position of the region
|
|
302
307
|
// If we're dragging to the left, we need to update the start instead
|
|
303
|
-
region._onUpdate(dx,
|
|
308
|
+
region._onUpdate(dx, x > startX ? 'end' : 'start');
|
|
304
309
|
}
|
|
305
310
|
},
|
|
306
311
|
// On drag start
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Regions=t():e.Regions=t()}(WaveSurfer,(()=>(()=>{"use strict";var e={d:(t,i)=>{for(var n in i)e.o(i,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:i[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>a});const i=class{constructor(){this.listeners={}}on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),()=>this.un(e,t)}once(e,t){const i=this.on(e,t),n=this.on(e,(()=>{i(),n()}));return i}un(e,t){this.listeners[e]&&(t?this.listeners[e].delete(t):delete this.listeners[e])}unAll(){this.listeners={}}emit(e,...t){this.listeners[e]&&this.listeners[e].forEach((e=>e(...t)))}},n=class extends i{constructor(e){super(),this.subscriptions=[],this.options=e}onInit(){}init(e){this.wavesurfer=e,this.onInit()}destroy(){this.subscriptions.forEach((e=>e()))}};function s(e,t,i,n,s=5){let r=()=>{};if(!e)return r;const o=o=>{o.preventDefault(),o.stopPropagation();let a=o.clientX,
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Regions=t():e.Regions=t()}("undefined"!=typeof WaveSurfer?WaveSurfer:this,(()=>(()=>{"use strict";var e={d:(t,i)=>{for(var n in i)e.o(i,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:i[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>a});const i=class{constructor(){this.listeners={}}on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),()=>this.un(e,t)}once(e,t){const i=this.on(e,t),n=this.on(e,(()=>{i(),n()}));return i}un(e,t){this.listeners[e]&&(t?this.listeners[e].delete(t):delete this.listeners[e])}unAll(){this.listeners={}}emit(e,...t){this.listeners[e]&&this.listeners[e].forEach((e=>e(...t)))}},n=class extends i{constructor(e){super(),this.subscriptions=[],this.options=e}onInit(){}init(e){this.wavesurfer=e,this.onInit()}destroy(){this.subscriptions.forEach((e=>e()))}};function s(e,t,i,n,s=5){let r=()=>{};if(!e)return r;const o=o=>{o.preventDefault(),o.stopPropagation();let a=o.clientX,h=o.clientY,l=!1;const d=n=>{n.preventDefault(),n.stopPropagation();const r=n.clientX,o=n.clientY;if(l||Math.abs(r-a)>=s||Math.abs(o-h)>=s){const{left:n,top:s}=e.getBoundingClientRect();l||(l=!0,i?.(a-n,h-s)),t(r-a,o-h,r-n,o-s),a=r,h=o}},c=e=>{l&&(e.preventDefault(),e.stopPropagation())},u=()=>{l&&n?.(),r()},p=e=>e.preventDefault(),g={passive:!1};document.addEventListener("touchmove",p,g),document.addEventListener("pointermove",d),document.addEventListener("pointerup",u),document.addEventListener("pointerleave",u),document.addEventListener("click",c,!0),r=()=>{document.removeEventListener("touchmove",p,g),document.removeEventListener("pointermove",d),document.removeEventListener("pointerup",u),document.removeEventListener("pointerleave",u),setTimeout((()=>{document.removeEventListener("click",c,!0)}),10)}};return e.addEventListener("pointerdown",o),()=>{r(),e.removeEventListener("pointerdown",o)}}class r extends i{constructor(e,t){super(),this.totalDuration=t,this.minLength=.01,this.maxLength=1/0,this.id=e.id||`region-${Math.random().toString(32).slice(2)}`,this.start=e.start,this.end=e.end??e.start,this.drag=e.drag??!0,this.resize=e.resize??!0,this.color=e.color??"rgba(0, 0, 0, 0.1)",this.minLength=e.minLength??this.minLength,this.maxLength=e.maxLength??this.maxLength,this.element=this.initElement(e.content),this.renderPosition(),this.initMouseEvents()}initElement(e){const t=document.createElement("div"),i=this.start===this.end;if(t.setAttribute("part",`${i?"marker":"region"} ${this.id}`),t.setAttribute("style",`\n position: absolute;\n height: 100%;\n background-color: ${i?"none":this.color};\n border-left: ${i?"2px solid "+this.color:"none"};\n border-radius: 2px;\n box-sizing: border-box;\n transition: background-color 0.2s ease;\n cursor: ${this.drag?"grab":"default"};\n pointer-events: all;\n `),e&&("string"==typeof e?(this.content=document.createElement("div"),this.content.style.padding=`0.2em ${i?.2:.4}em`,this.content.textContent=e):this.content=e,this.content.setAttribute("part","region-content"),t.appendChild(this.content)),!i){const e=document.createElement("div");e.setAttribute("data-resize","left"),e.setAttribute("style",`\n position: absolute;\n z-index: 2;\n width: 6px;\n height: 100%;\n top: 0;\n left: 0;\n border-left: 2px solid rgba(0, 0, 0, 0.5);\n border-radius: 2px 0 0 2px;\n cursor: ${this.resize?"ew-resize":"default"};\n word-break: keep-all;\n `),e.setAttribute("part","region-handle region-handle-left");const i=e.cloneNode();i.setAttribute("data-resize","right"),i.style.left="",i.style.right="0",i.style.borderRight=i.style.borderLeft,i.style.borderLeft="",i.style.borderRadius="0 2px 2px 0",i.setAttribute("part","region-handle region-handle-right"),t.appendChild(e),t.appendChild(i)}return t}renderPosition(){const e=this.start/this.totalDuration,t=(this.totalDuration-this.end)/this.totalDuration;this.element.style.left=100*e+"%",this.element.style.right=100*t+"%"}initMouseEvents(){const{element:e}=this;e&&(e.addEventListener("click",(e=>this.emit("click",e))),e.addEventListener("mouseenter",(e=>this.emit("over",e))),e.addEventListener("mouseleave",(e=>this.emit("leave",e))),e.addEventListener("dblclick",(e=>this.emit("dblclick",e))),s(e,(e=>this.onMove(e)),(()=>this.onStartMoving()),(()=>this.onEndMoving())),s(e.querySelector('[data-resize="left"]'),(e=>this.onResize(e,"start")),(()=>null),(()=>this.onEndResizing()),1),s(e.querySelector('[data-resize="right"]'),(e=>this.onResize(e,"end")),(()=>null),(()=>this.onEndResizing()),1))}onStartMoving(){this.drag&&(this.element.style.cursor="grabbing")}onEndMoving(){this.drag&&(this.element.style.cursor="grab",this.emit("update-end"))}_onUpdate(e,t){if(!this.element.parentElement)return;const i=e/this.element.parentElement.clientWidth*this.totalDuration,n=t&&"start"!==t?this.start:this.start+i,s=t&&"end"!==t?this.end:this.end+i,r=s-n;n>0&&s<this.totalDuration&&n<s&&r>=this.minLength&&r<=this.maxLength&&(this.start=n,this.end=s,this.renderPosition(),this.emit("update"))}onMove(e){this.drag&&this._onUpdate(e)}onResize(e,t){this.resize&&this._onUpdate(e,t)}onEndResizing(){this.resize&&this.emit("update-end")}_setTotalDuration(e){this.totalDuration=e,this.renderPosition()}play(){this.emit("play")}setOptions(e){e.color&&(this.color=e.color,this.element.style.backgroundColor=this.color),void 0!==e.drag&&(this.drag=e.drag,this.element.style.cursor=this.drag?"grab":"default"),void 0!==e.resize&&(this.resize=e.resize,this.element.querySelectorAll("[data-resize]").forEach((e=>{e.style.cursor=this.resize?"ew-resize":"default"}))),void 0===e.start&&void 0===e.end||(this.start=e.start??this.start,this.end=e.end??this.end,this.renderPosition())}remove(){this.emit("remove"),this.element.remove(),this.element=null}}class o extends n{constructor(e){super(e),this.regions=[],this.regionsContainer=this.initRegionsContainer()}static create(e){return new o(e)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.wavesurfer.getWrapper().appendChild(this.regionsContainer)}initRegionsContainer(){const e=document.createElement("div");return e.setAttribute("style","\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n pointer-events: none;\n "),e}getRegions(){return this.regions}avoidOverlapping(e){if(!e.content)return;const t=e.content,i=t.getBoundingClientRect().left,n=e.element.scrollWidth,s=this.regions.filter((t=>{if(t===e||!t.content)return!1;const s=t.content.getBoundingClientRect().left,r=t.element.scrollWidth;return i<s+r&&s<i+n})).map((e=>e.content?.getBoundingClientRect().height||0)).reduce(((e,t)=>e+t),0);t.style.marginTop=`${s}px`}saveRegion(e){this.regionsContainer.appendChild(e.element),this.avoidOverlapping(e),this.regions.push(e),this.emit("region-created",e);const t=[e.on("update-end",(()=>{this.avoidOverlapping(e),this.emit("region-updated",e)})),e.on("play",(()=>{this.wavesurfer?.play(),this.wavesurfer?.setTime(e.start)})),e.on("click",(t=>{this.emit("region-clicked",e,t)})),e.on("dblclick",(t=>{this.emit("region-double-clicked",e,t)})),e.once("remove",(()=>{t.forEach((e=>e())),this.regions=this.regions.filter((t=>t!==e))}))];this.subscriptions.push(...t)}addRegion(e){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const t=this.wavesurfer.getDuration(),i=new r(e,t);return t?this.saveRegion(i):this.subscriptions.push(this.wavesurfer.once("ready",(e=>{i._setTotalDuration(e),this.saveRegion(i)}))),i}enableDragSelection(e){const t=this.wavesurfer?.getWrapper()?.querySelector("div");if(!t)return()=>{};let i=null,n=0;return s(t,((e,t,s)=>{i&&i._onUpdate(e,s>n?"end":"start")}),(t=>{if(n=t,!this.wavesurfer)return;const s=this.wavesurfer.getDuration(),o=this.wavesurfer.getWrapper().clientWidth;i=new r({...e,start:t/o*s,end:(t+5)/o*s},s),this.regionsContainer.appendChild(i.element)}),(()=>{i&&(this.saveRegion(i),i=null)}))}clearRegions(){this.regions.forEach((e=>e.remove()))}destroy(){this.clearRegions(),super.destroy()}}const a=o;return t.default})()));
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
* ]
|
|
18
18
|
* });
|
|
19
19
|
*/
|
|
20
|
+
/**
|
|
21
|
+
* Spectrogram plugin for wavesurfer.
|
|
22
|
+
*/
|
|
20
23
|
import BasePlugin from '../base-plugin.js';
|
|
21
24
|
export type SpectrogramPluginOptions = {
|
|
22
25
|
/** Selector of element or element in which to render */
|
|
@@ -50,7 +53,7 @@ export type SpectrogramPluginEvents = {
|
|
|
50
53
|
ready: [];
|
|
51
54
|
click: [relativeX: number];
|
|
52
55
|
};
|
|
53
|
-
export
|
|
56
|
+
export declare class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramPluginOptions> {
|
|
54
57
|
static create(options?: SpectrogramPluginOptions): SpectrogramPlugin;
|
|
55
58
|
utils: {
|
|
56
59
|
style: (el: HTMLElement, styles: Record<string, string>) => CSSStyleDeclaration & Record<string, string>;
|
|
@@ -70,3 +73,4 @@ export default class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvent
|
|
|
70
73
|
loadLabels(bgFill: any, fontSizeFreq: any, fontSizeUnit: any, fontType: any, textColorFreq: any, textColorUnit: any, textAlign: any, container: any): void;
|
|
71
74
|
resample(oldMatrix: any): Uint8Array[];
|
|
72
75
|
}
|
|
76
|
+
export default SpectrogramPlugin;
|
|
@@ -18,9 +18,160 @@
|
|
|
18
18
|
* });
|
|
19
19
|
*/
|
|
20
20
|
// @ts-nocheck
|
|
21
|
+
/**
|
|
22
|
+
* Calculate FFT - Based on https://github.com/corbanbrook/dsp.js
|
|
23
|
+
*
|
|
24
|
+
* @param {Number} bufferSize Buffer size
|
|
25
|
+
* @param {Number} sampleRate Sample rate
|
|
26
|
+
* @param {Function} windowFunc Window function
|
|
27
|
+
* @param {Number} alpha Alpha channel
|
|
28
|
+
*/
|
|
29
|
+
function FFT(bufferSize, sampleRate, windowFunc, alpha) {
|
|
30
|
+
this.bufferSize = bufferSize;
|
|
31
|
+
this.sampleRate = sampleRate;
|
|
32
|
+
this.bandwidth = (2 / bufferSize) * (sampleRate / 2);
|
|
33
|
+
this.sinTable = new Float32Array(bufferSize);
|
|
34
|
+
this.cosTable = new Float32Array(bufferSize);
|
|
35
|
+
this.windowValues = new Float32Array(bufferSize);
|
|
36
|
+
this.reverseTable = new Uint32Array(bufferSize);
|
|
37
|
+
this.peakBand = 0;
|
|
38
|
+
this.peak = 0;
|
|
39
|
+
var i;
|
|
40
|
+
switch (windowFunc) {
|
|
41
|
+
case 'bartlett':
|
|
42
|
+
for (i = 0; i < bufferSize; i++) {
|
|
43
|
+
this.windowValues[i] = (2 / (bufferSize - 1)) * ((bufferSize - 1) / 2 - Math.abs(i - (bufferSize - 1) / 2));
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
case 'bartlettHann':
|
|
47
|
+
for (i = 0; i < bufferSize; i++) {
|
|
48
|
+
this.windowValues[i] =
|
|
49
|
+
0.62 - 0.48 * Math.abs(i / (bufferSize - 1) - 0.5) - 0.38 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1));
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
case 'blackman':
|
|
53
|
+
alpha = alpha || 0.16;
|
|
54
|
+
for (i = 0; i < bufferSize; i++) {
|
|
55
|
+
this.windowValues[i] =
|
|
56
|
+
(1 - alpha) / 2 -
|
|
57
|
+
0.5 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1)) +
|
|
58
|
+
(alpha / 2) * Math.cos((4 * Math.PI * i) / (bufferSize - 1));
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case 'cosine':
|
|
62
|
+
for (i = 0; i < bufferSize; i++) {
|
|
63
|
+
this.windowValues[i] = Math.cos((Math.PI * i) / (bufferSize - 1) - Math.PI / 2);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case 'gauss':
|
|
67
|
+
alpha = alpha || 0.25;
|
|
68
|
+
for (i = 0; i < bufferSize; i++) {
|
|
69
|
+
this.windowValues[i] = Math.pow(Math.E, -0.5 * Math.pow((i - (bufferSize - 1) / 2) / ((alpha * (bufferSize - 1)) / 2), 2));
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case 'hamming':
|
|
73
|
+
for (i = 0; i < bufferSize; i++) {
|
|
74
|
+
this.windowValues[i] = 0.54 - 0.46 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1));
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case 'hann':
|
|
78
|
+
case undefined:
|
|
79
|
+
for (i = 0; i < bufferSize; i++) {
|
|
80
|
+
this.windowValues[i] = 0.5 * (1 - Math.cos((Math.PI * 2 * i) / (bufferSize - 1)));
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
case 'lanczoz':
|
|
84
|
+
for (i = 0; i < bufferSize; i++) {
|
|
85
|
+
this.windowValues[i] =
|
|
86
|
+
Math.sin(Math.PI * ((2 * i) / (bufferSize - 1) - 1)) / (Math.PI * ((2 * i) / (bufferSize - 1) - 1));
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
case 'rectangular':
|
|
90
|
+
for (i = 0; i < bufferSize; i++) {
|
|
91
|
+
this.windowValues[i] = 1;
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case 'triangular':
|
|
95
|
+
for (i = 0; i < bufferSize; i++) {
|
|
96
|
+
this.windowValues[i] = (2 / bufferSize) * (bufferSize / 2 - Math.abs(i - (bufferSize - 1) / 2));
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
throw Error("No such window function '" + windowFunc + "'");
|
|
101
|
+
}
|
|
102
|
+
var limit = 1;
|
|
103
|
+
var bit = bufferSize >> 1;
|
|
104
|
+
var i;
|
|
105
|
+
while (limit < bufferSize) {
|
|
106
|
+
for (i = 0; i < limit; i++) {
|
|
107
|
+
this.reverseTable[i + limit] = this.reverseTable[i] + bit;
|
|
108
|
+
}
|
|
109
|
+
limit = limit << 1;
|
|
110
|
+
bit = bit >> 1;
|
|
111
|
+
}
|
|
112
|
+
for (i = 0; i < bufferSize; i++) {
|
|
113
|
+
this.sinTable[i] = Math.sin(-Math.PI / i);
|
|
114
|
+
this.cosTable[i] = Math.cos(-Math.PI / i);
|
|
115
|
+
}
|
|
116
|
+
this.calculateSpectrum = function (buffer) {
|
|
117
|
+
// Locally scope variables for speed up
|
|
118
|
+
var bufferSize = this.bufferSize, cosTable = this.cosTable, sinTable = this.sinTable, reverseTable = this.reverseTable, real = new Float32Array(bufferSize), imag = new Float32Array(bufferSize), bSi = 2 / this.bufferSize, sqrt = Math.sqrt, rval, ival, mag, spectrum = new Float32Array(bufferSize / 2);
|
|
119
|
+
var k = Math.floor(Math.log(bufferSize) / Math.LN2);
|
|
120
|
+
if (Math.pow(2, k) !== bufferSize) {
|
|
121
|
+
throw 'Invalid buffer size, must be a power of 2.';
|
|
122
|
+
}
|
|
123
|
+
if (bufferSize !== buffer.length) {
|
|
124
|
+
throw ('Supplied buffer is not the same size as defined FFT. FFT Size: ' +
|
|
125
|
+
bufferSize +
|
|
126
|
+
' Buffer Size: ' +
|
|
127
|
+
buffer.length);
|
|
128
|
+
}
|
|
129
|
+
var halfSize = 1, phaseShiftStepReal, phaseShiftStepImag, currentPhaseShiftReal, currentPhaseShiftImag, off, tr, ti, tmpReal;
|
|
130
|
+
for (var i = 0; i < bufferSize; i++) {
|
|
131
|
+
real[i] = buffer[reverseTable[i]] * this.windowValues[reverseTable[i]];
|
|
132
|
+
imag[i] = 0;
|
|
133
|
+
}
|
|
134
|
+
while (halfSize < bufferSize) {
|
|
135
|
+
phaseShiftStepReal = cosTable[halfSize];
|
|
136
|
+
phaseShiftStepImag = sinTable[halfSize];
|
|
137
|
+
currentPhaseShiftReal = 1;
|
|
138
|
+
currentPhaseShiftImag = 0;
|
|
139
|
+
for (var fftStep = 0; fftStep < halfSize; fftStep++) {
|
|
140
|
+
var i = fftStep;
|
|
141
|
+
while (i < bufferSize) {
|
|
142
|
+
off = i + halfSize;
|
|
143
|
+
tr = currentPhaseShiftReal * real[off] - currentPhaseShiftImag * imag[off];
|
|
144
|
+
ti = currentPhaseShiftReal * imag[off] + currentPhaseShiftImag * real[off];
|
|
145
|
+
real[off] = real[i] - tr;
|
|
146
|
+
imag[off] = imag[i] - ti;
|
|
147
|
+
real[i] += tr;
|
|
148
|
+
imag[i] += ti;
|
|
149
|
+
i += halfSize << 1;
|
|
150
|
+
}
|
|
151
|
+
tmpReal = currentPhaseShiftReal;
|
|
152
|
+
currentPhaseShiftReal = tmpReal * phaseShiftStepReal - currentPhaseShiftImag * phaseShiftStepImag;
|
|
153
|
+
currentPhaseShiftImag = tmpReal * phaseShiftStepImag + currentPhaseShiftImag * phaseShiftStepReal;
|
|
154
|
+
}
|
|
155
|
+
halfSize = halfSize << 1;
|
|
156
|
+
}
|
|
157
|
+
for (var i = 0, N = bufferSize / 2; i < N; i++) {
|
|
158
|
+
rval = real[i];
|
|
159
|
+
ival = imag[i];
|
|
160
|
+
mag = bSi * sqrt(rval * rval + ival * ival);
|
|
161
|
+
if (mag > this.peak) {
|
|
162
|
+
this.peakBand = i;
|
|
163
|
+
this.peak = mag;
|
|
164
|
+
}
|
|
165
|
+
spectrum[i] = mag;
|
|
166
|
+
}
|
|
167
|
+
return spectrum;
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Spectrogram plugin for wavesurfer.
|
|
172
|
+
*/
|
|
21
173
|
import BasePlugin from '../base-plugin.js';
|
|
22
|
-
|
|
23
|
-
export default class SpectrogramPlugin extends BasePlugin {
|
|
174
|
+
export class SpectrogramPlugin extends BasePlugin {
|
|
24
175
|
static create(options) {
|
|
25
176
|
return new SpectrogramPlugin(options || {});
|
|
26
177
|
}
|
|
@@ -331,3 +482,4 @@ export default class SpectrogramPlugin extends BasePlugin {
|
|
|
331
482
|
return newMatrix;
|
|
332
483
|
}
|
|
333
484
|
}
|
|
485
|
+
export default SpectrogramPlugin;
|
|
@@ -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.Spectrogram=e():t.Spectrogram=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={d:(e,s)=>{for(var i in s)t.o(s,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:s[i]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(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.Spectrogram=e():t.Spectrogram=e()}("undefined"!=typeof WaveSurfer?WaveSurfer:this,(()=>(()=>{"use strict";var t={d:(e,s)=>{for(var i in s)t.o(s,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:s[i]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>n});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 s=this.on(t,e),i=this.on(t,(()=>{s(),i()}));return s}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)))}},i=class extends s{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}};function a(t,e,s,i){switch(this.bufferSize=t,this.sampleRate=e,this.bandwidth=2/t*(e/2),this.sinTable=new Float32Array(t),this.cosTable=new Float32Array(t),this.windowValues=new Float32Array(t),this.reverseTable=new Uint32Array(t),this.peakBand=0,this.peak=0,s){case"bartlett":for(a=0;a<t;a++)this.windowValues[a]=2/(t-1)*((t-1)/2-Math.abs(a-(t-1)/2));break;case"bartlettHann":for(a=0;a<t;a++)this.windowValues[a]=.62-.48*Math.abs(a/(t-1)-.5)-.38*Math.cos(2*Math.PI*a/(t-1));break;case"blackman":for(i=i||.16,a=0;a<t;a++)this.windowValues[a]=(1-i)/2-.5*Math.cos(2*Math.PI*a/(t-1))+i/2*Math.cos(4*Math.PI*a/(t-1));break;case"cosine":for(a=0;a<t;a++)this.windowValues[a]=Math.cos(Math.PI*a/(t-1)-Math.PI/2);break;case"gauss":for(i=i||.25,a=0;a<t;a++)this.windowValues[a]=Math.pow(Math.E,-.5*Math.pow((a-(t-1)/2)/(i*(t-1)/2),2));break;case"hamming":for(a=0;a<t;a++)this.windowValues[a]=.54-.46*Math.cos(2*Math.PI*a/(t-1));break;case"hann":case void 0:for(a=0;a<t;a++)this.windowValues[a]=.5*(1-Math.cos(2*Math.PI*a/(t-1)));break;case"lanczoz":for(a=0;a<t;a++)this.windowValues[a]=Math.sin(Math.PI*(2*a/(t-1)-1))/(Math.PI*(2*a/(t-1)-1));break;case"rectangular":for(a=0;a<t;a++)this.windowValues[a]=1;break;case"triangular":for(a=0;a<t;a++)this.windowValues[a]=2/t*(t/2-Math.abs(a-(t-1)/2));break;default:throw Error("No such window function '"+s+"'")}for(var a,r=1,n=t>>1;r<t;){for(a=0;a<r;a++)this.reverseTable[a+r]=this.reverseTable[a]+n;r<<=1,n>>=1}for(a=0;a<t;a++)this.sinTable[a]=Math.sin(-Math.PI/a),this.cosTable[a]=Math.cos(-Math.PI/a);this.calculateSpectrum=function(t){var e,s,i,a=this.bufferSize,r=this.cosTable,n=this.sinTable,h=this.reverseTable,o=new Float32Array(a),l=new Float32Array(a),c=2/this.bufferSize,f=Math.sqrt,p=new Float32Array(a/2),u=Math.floor(Math.log(a)/Math.LN2);if(Math.pow(2,u)!==a)throw"Invalid buffer size, must be a power of 2.";if(a!==t.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+a+" Buffer Size: "+t.length;for(var d,w,b,M,g,m,y,v,x=1,S=0;S<a;S++)o[S]=t[h[S]]*this.windowValues[h[S]],l[S]=0;for(;x<a;){d=r[x],w=n[x],b=1,M=0;for(var k=0;k<x;k++){for(S=k;S<a;)m=b*o[g=S+x]-M*l[g],y=b*l[g]+M*o[g],o[g]=o[S]-m,l[g]=l[S]-y,o[S]+=m,l[S]+=y,S+=x<<1;b=(v=b)*d-M*w,M=v*w+M*d}x<<=1}S=0;for(var q=a/2;S<q;S++)(i=c*f((e=o[S])*e+(s=l[S])*s))>this.peak&&(this.peakBand=S,this.peak=i),p[S]=i;return p}}class r extends i{static create(t){return new r(t||{})}constructor(t){if(super(t),this.utils={style:(t,e)=>Object.assign(t.style,e)},this.drawSpectrogram=t=>{isNaN(t[0][0])||(t=[t]);const e=this.spectrCc,s=this.height,i=this.width,a=this.buffer.sampleRate/2,r=this.frequencyMin,n=this.frequencyMax;if(e){for(let h=0;h<t.length;h++){const o=this.resample(t[h]),l=new ImageData(i,s);for(let t=0;t<o.length;t++)for(let e=0;e<o[t].length;e++){const a=this.colorMap[o[t][e]],r=4*((s-e)*i+t);l.data[r]=255*a[0],l.data[r+1]=255*a[1],l.data[r+2]=255*a[2],l.data[r+3]=255*a[3]}createImageBitmap(l).then((t=>{e.drawImage(t,0,s*(1-n/a),i,s*(n-r)/a,0,s*h,i,s)}))}this.emit("ready")}},this.frequenciesDataUrl=t.frequenciesDataUrl,this.container="string"==typeof t.container?document.querySelector(t.container):t.container,t.colorMap){if(t.colorMap.length<256)throw new Error("Colormap must contain 256 elements");for(let e=0;e<t.colorMap.length;e++)if(4!==t.colorMap[e].length)throw new Error("ColorMap entries must contain 4 values");this.colorMap=t.colorMap}else{this.colorMap=[];for(let t=0;t<256;t++){const e=(255-t)/256;this.colorMap.push([e,e,e,1])}}this.fftSamples=t.fftSamples||512,this.height=t.height||this.fftSamples/2,this.noverlap=t.noverlap,this.windowFunc=t.windowFunc,this.alpha=t.alpha,this.channels=1,this.frequencyMin=t.frequencyMin||0,this.frequencyMax=t.frequencyMax||0,this.createWrapper(),this.createCanvas()}onInit(){this.container=this.container||this.wavesurfer.getWrapper(),this.container.appendChild(this.wrapper),this.wavesurfer.options.fillParent&&this.utils.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"}),this.width=this.wavesurfer.getWrapper().offsetWidth,this.subscriptions.push(this.wavesurfer.on("redraw",(()=>this.render())))}destroy(){this.unAll(),this.wavesurfer.un("ready",this._onReady),this.wavesurfer.un("redraw",this._onRender),this.wavesurfer=null,this.util=null,this.options=null,this.wrapper&&(this.wrapper.remove(),this.wrapper=null)}createWrapper(){if(this.wrapper=document.createElement("div"),this.utils.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height*this.channels+"px"}),this.options.labels){const t=document.createElement("canvas");t.setAttribute("part","spec-labels"),t.classList.add("spec-labels"),this.utils.style(t,{position:"absolute",zIndex:9,width:"55px",height:"100%"}),this.wrapper.appendChild(t),this.labelsEl=t}this.wrapper.addEventListener("click",this._onWrapperClick)}_wrapperClickHandler(t){t.preventDefault();const e="offsetX"in t?t.offsetX:t.layerX;this.emit("click",e/this.width||0)}createCanvas(){const t=document.createElement("canvas");this.wrapper.appendChild(t),this.spectrCc=t.getContext("2d"),this.utils.style(t,{position:"absolute",left:0,top:0,width:"100%",height:"100%",zIndex:4}),this.canvas=t}render(){this.canvas.width=this.width,this.canvas.height=this.height,this.frequenciesDataUrl?this.loadFrequenciesData(this.frequenciesDataUrl):this.getFrequencies(this.drawSpectrogram),this.loadLabels(this.options.labelsBackground,"12px","12px","",this.options.labelsColor,this.options.labelsHzColor||this.options.labelsColor,"center","#specLabels")}getFrequencies(t){const e=this.fftSamples,s=this.wavesurfer.getDecodedData(),i=this.channels;if(this.frequencyMax=this.frequencyMax||s.sampleRate/2,!s)return;this.buffer=s;const r=s.sampleRate,n=[];let h=this.noverlap;if(!h){const t=s.length/this.canvas.width;h=Math.max(0,Math.round(e-t))}const o=new a(e,r,this.windowFunc,this.alpha);for(let t=0;t<i;t++){const i=s.getChannelData(t),a=[];let r=0;for(;r+e<i.length;){const t=i.slice(r,r+e),s=o.calculateSpectrum(t),n=new Uint8Array(e/2);let l;for(l=0;l<e/2;l++)n[l]=Math.max(-255,45*Math.log10(s[l]));a.push(n),r+=e-h}n.push(a)}t(n,this)}loadFrequenciesData(t){return fetch(t).then((t=>t.json())).then(this.drawSpectrogram)}freqType(t){return t>=1e3?(t/1e3).toFixed(1):Math.round(t)}unitType(t){return t>=1e3?"KHz":"Hz"}loadLabels(t,e,s,i,a,r,n,h){t=t||"rgba(68,68,68,0)",e=e||"12px",s=s||"12px",i=i||"Helvetica",a=a||"#fff",r=r||"#fff",n=n||"center",h=h||"#specLabels";const o=this.height||512,l=o/256*5,c=this.frequencyMin,f=(this.frequencyMax-c)/l,p=this.labelsEl.getContext("2d"),u=window.devicePixelRatio;if(this.labelsEl.height=this.height*this.channels*u,this.labelsEl.width=55*u,p.scale(u,u),p)for(let h=0;h<this.channels;h++){let u;for(p.fillStyle=t,p.fillRect(0,h*o,55,(1+h)*o),p.fill(),u=0;u<=l;u++){p.textAlign=n,p.textBaseline="middle";const t=c+f*u,l=this.freqType(t),d=this.unitType(t),w=16;let b;b=0==u?(1+h)*o+u-10:(1+h)*o-50*u+2,p.fillStyle=r,p.font=s+" "+i,p.fillText(d,w+24,b),p.fillStyle=a,p.font=e+" "+i,p.fillText(l,w,b)}}}resample(t){const e=this.width,s=[],i=1/t.length,a=1/e;let r;for(r=0;r<e;r++){const e=new Array(t[0].length);let n;for(n=0;n<t.length;n++){const s=n*i,h=s+i,o=r*a,l=o+a,c=h<=o||l<=s?0:Math.min(Math.max(h,o),Math.max(l,s))-Math.max(Math.min(h,o),Math.min(l,s));let f;if(c>0)for(f=0;f<t[0].length;f++)null==e[f]&&(e[f]=0),e[f]+=c/a*t[n][f]}const h=new Uint8Array(t[0].length);let o;for(o=0;o<t[0].length;o++)h[o]=e[o];s.push(h)}return s}}const n=r;return e.default})()));
|
|
@@ -26,7 +26,7 @@ declare const defaultOptions: {
|
|
|
26
26
|
export type TimelinePluginEvents = {
|
|
27
27
|
ready: [];
|
|
28
28
|
};
|
|
29
|
-
declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
|
|
29
|
+
export declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
|
|
30
30
|
private timelineWrapper;
|
|
31
31
|
protected options: TimelinePluginOptions & typeof defaultOptions;
|
|
32
32
|
constructor(options?: TimelinePluginOptions);
|
package/dist/plugins/timeline.js
CHANGED
|
@@ -5,7 +5,7 @@ import BasePlugin from '../base-plugin.js';
|
|
|
5
5
|
const defaultOptions = {
|
|
6
6
|
height: 20,
|
|
7
7
|
};
|
|
8
|
-
class TimelinePlugin extends BasePlugin {
|
|
8
|
+
export class TimelinePlugin extends BasePlugin {
|
|
9
9
|
constructor(options) {
|
|
10
10
|
super(options || {});
|
|
11
11
|
this.options = Object.assign({}, defaultOptions, options);
|
|
@@ -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.Timeline=e():t.Timeline=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>o});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),n=this.on(t,(()=>{i(),n()}));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)))}},n=class extends i{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}},s={height:20};class r extends n{constructor(t){super(t||{}),this.options=Object.assign({},s,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new r(t)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const t=this.options.container??this.wavesurfer.getWrapper();this.options.insertPosition?(t.firstElementChild||t).insertAdjacentElement(this.options.insertPosition,this.timelineWrapper):t.appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("redraw",(()=>{this.initTimeline(this.wavesurfer?.getDuration()||0)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){const t=document.createElement("div");return t.setAttribute("part","timeline"),t}formatTime(t){return t/60>1?`${Math.floor(t/60)}:${(t=Math.round(t%60))<10?"0":""}${t}`:""+Math.round(1e3*t)/1e3}defaultTimeInterval(t){return t>=25?1:5*t>=25?5:15*t>=25?15:60*Math.ceil(.5/t)}defaultPrimaryLabelInterval(t){return t>=25?10:5*t>=25?6:4}defaultSecondaryLabelInterval(t){return t>=25?5:2}initTimeline(t){const e=this.timelineWrapper.scrollWidth/t,i=this.options.timeInterval??this.defaultTimeInterval(e),n=this.options.primaryLabelInterval??this.defaultPrimaryLabelInterval(e),s=this.options.secondaryLabelInterval??this.defaultSecondaryLabelInterval(e),r="beforebegin"===this.options.insertPosition,o=document.createElement("div");if(o.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n display: flex;\n justify-content: space-between;\n align-items: ${r?"flex-start":"flex-end"};\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n `),r){const t="\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n ";o.setAttribute("style",o.getAttribute("style")+t)}"string"==typeof this.options.style?o.setAttribute("style",o.getAttribute("style")+this.options.style):"object"==typeof this.options.style&&Object.assign(o.style,this.options.style);const l=document.createElement("div");l.setAttribute("style",`\n width: 1px;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: ${r?"flex-start":"flex-end"};\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n `);for(let e=0;e<t;e+=i){const t=l.cloneNode(),i=Math.round(100*e)/100%n==0,r=Math.round(100*e)/100%s==0;(i||r)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.formatTime(e),i&&(t.style.opacity="1")),o.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(o),this.emit("ready")}}const o=r;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.Timeline=e():t.Timeline=e()}("undefined"!=typeof WaveSurfer?WaveSurfer:this,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>o});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),n=this.on(t,(()=>{i(),n()}));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)))}},n=class extends i{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}},s={height:20};class r extends n{constructor(t){super(t||{}),this.options=Object.assign({},s,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new r(t)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const t=this.options.container??this.wavesurfer.getWrapper();this.options.insertPosition?(t.firstElementChild||t).insertAdjacentElement(this.options.insertPosition,this.timelineWrapper):t.appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("redraw",(()=>{this.initTimeline(this.wavesurfer?.getDuration()||0)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){const t=document.createElement("div");return t.setAttribute("part","timeline"),t}formatTime(t){return t/60>1?`${Math.floor(t/60)}:${(t=Math.round(t%60))<10?"0":""}${t}`:""+Math.round(1e3*t)/1e3}defaultTimeInterval(t){return t>=25?1:5*t>=25?5:15*t>=25?15:60*Math.ceil(.5/t)}defaultPrimaryLabelInterval(t){return t>=25?10:5*t>=25?6:4}defaultSecondaryLabelInterval(t){return t>=25?5:2}initTimeline(t){const e=this.timelineWrapper.scrollWidth/t,i=this.options.timeInterval??this.defaultTimeInterval(e),n=this.options.primaryLabelInterval??this.defaultPrimaryLabelInterval(e),s=this.options.secondaryLabelInterval??this.defaultSecondaryLabelInterval(e),r="beforebegin"===this.options.insertPosition,o=document.createElement("div");if(o.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n display: flex;\n justify-content: space-between;\n align-items: ${r?"flex-start":"flex-end"};\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n `),r){const t="\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n ";o.setAttribute("style",o.getAttribute("style")+t)}"string"==typeof this.options.style?o.setAttribute("style",o.getAttribute("style")+this.options.style):"object"==typeof this.options.style&&Object.assign(o.style,this.options.style);const l=document.createElement("div");l.setAttribute("style",`\n width: 1px;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: ${r?"flex-start":"flex-end"};\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n `);for(let e=0;e<t;e+=i){const t=l.cloneNode(),i=Math.round(100*e)/100%n==0,r=Math.round(100*e)/100%s==0;(i||r)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.formatTime(e),i&&(t.style.opacity="1")),o.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(o),this.emit("ready")}}const o=r;return e.default})()));
|
package/dist/renderer.js
CHANGED
|
@@ -330,6 +330,10 @@ class Renderer extends EventEmitter {
|
|
|
330
330
|
// Clear previous timeouts
|
|
331
331
|
this.timeouts.forEach((context) => context.timeout && clearTimeout(context.timeout));
|
|
332
332
|
this.timeouts = [];
|
|
333
|
+
// Clear the canvases
|
|
334
|
+
this.canvasWrapper.innerHTML = '';
|
|
335
|
+
this.progressWrapper.innerHTML = '';
|
|
336
|
+
this.wrapper.style.width = '';
|
|
333
337
|
// Determine the width of the waveform
|
|
334
338
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
335
339
|
const parentWidth = this.scrollContainer.clientWidth;
|
|
@@ -337,7 +341,7 @@ class Renderer extends EventEmitter {
|
|
|
337
341
|
// Whether the container should scroll
|
|
338
342
|
this.isScrolling = scrollWidth > parentWidth;
|
|
339
343
|
const useParentWidth = this.options.fillParent && !this.isScrolling;
|
|
340
|
-
// Width
|
|
344
|
+
// Width of the waveform in pixels
|
|
341
345
|
const width = (useParentWidth ? parentWidth : scrollWidth) * pixelRatio;
|
|
342
346
|
// Set the width of the wrapper
|
|
343
347
|
this.wrapper.style.width = useParentWidth ? '100%' : `${scrollWidth}px`;
|
|
@@ -346,9 +350,6 @@ class Renderer extends EventEmitter {
|
|
|
346
350
|
this.scrollContainer.classList.toggle('noScrollbar', !!this.options.hideScrollbar);
|
|
347
351
|
this.cursor.style.backgroundColor = `${this.options.cursorColor || this.options.progressColor}`;
|
|
348
352
|
this.cursor.style.width = `${this.options.cursorWidth}px`;
|
|
349
|
-
// Clear the canvases
|
|
350
|
-
this.canvasWrapper.innerHTML = '';
|
|
351
|
-
this.progressWrapper.innerHTML = '';
|
|
352
353
|
// Render the waveform
|
|
353
354
|
if (this.options.splitChannels) {
|
|
354
355
|
// Render a waveform for each channel
|
package/dist/wavesurfer.d.ts
CHANGED
|
@@ -57,6 +57,8 @@ export type WaveSurferOptions = {
|
|
|
57
57
|
plugins?: GenericPlugin[];
|
|
58
58
|
/** Custom render function */
|
|
59
59
|
renderFunction?: (peaks: Array<Float32Array | number[]>, ctx: CanvasRenderingContext2D) => void;
|
|
60
|
+
/** Options to pass to the fetch method */
|
|
61
|
+
fetchParams?: RequestInit;
|
|
60
62
|
};
|
|
61
63
|
declare const defaultOptions: {
|
|
62
64
|
waveColor: string;
|
|
@@ -103,7 +105,7 @@ export type WaveSurferEvents = {
|
|
|
103
105
|
/** Just before the waveform is destroyed so you can clean up your events */
|
|
104
106
|
destroy: [];
|
|
105
107
|
};
|
|
106
|
-
declare class WaveSurfer extends Player<WaveSurferEvents> {
|
|
108
|
+
export declare class WaveSurfer extends Player<WaveSurferEvents> {
|
|
107
109
|
options: WaveSurferOptions & typeof defaultOptions;
|
|
108
110
|
private renderer;
|
|
109
111
|
private timer;
|
|
@@ -130,7 +132,7 @@ declare class WaveSurfer extends Player<WaveSurferEvents> {
|
|
|
130
132
|
getActivePlugins(): GenericPlugin[];
|
|
131
133
|
/** Load an audio file by URL, with optional pre-decoded audio data */
|
|
132
134
|
load(url: string, channelData?: WaveSurferOptions['peaks'], duration?: number): Promise<void>;
|
|
133
|
-
/** Zoom
|
|
135
|
+
/** Zoom the waveform by a given pixels-per-second factor */
|
|
134
136
|
zoom(minPxPerSec: number): void;
|
|
135
137
|
/** Get the decoded audio data */
|
|
136
138
|
getDecodedData(): AudioBuffer | null;
|
|
@@ -138,13 +140,13 @@ declare class WaveSurfer extends Player<WaveSurferEvents> {
|
|
|
138
140
|
getDuration(): number;
|
|
139
141
|
/** Toggle if the waveform should react to clicks */
|
|
140
142
|
toggleInteraction(isInteractive: boolean): void;
|
|
141
|
-
/**
|
|
143
|
+
/** Seek to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
|
|
142
144
|
seekTo(progress: number): void;
|
|
143
145
|
/** Play or pause the audio */
|
|
144
146
|
playPause(): Promise<void>;
|
|
145
147
|
/** Stop the audio and go to the beginning */
|
|
146
148
|
stop(): void;
|
|
147
|
-
/** Skip N or -N seconds from the current
|
|
149
|
+
/** Skip N or -N seconds from the current position */
|
|
148
150
|
skip(seconds: number): void;
|
|
149
151
|
/** Empty the waveform by loading a tiny silent audio */
|
|
150
152
|
empty(): void;
|
package/dist/wavesurfer.js
CHANGED
|
@@ -14,7 +14,7 @@ const defaultOptions = {
|
|
|
14
14
|
autoCenter: true,
|
|
15
15
|
sampleRate: 8000,
|
|
16
16
|
};
|
|
17
|
-
class WaveSurfer extends Player {
|
|
17
|
+
export class WaveSurfer extends Player {
|
|
18
18
|
/** Create a new WaveSurfer instance */
|
|
19
19
|
static create(options) {
|
|
20
20
|
return new WaveSurfer(options);
|
|
@@ -139,11 +139,13 @@ class WaveSurfer extends Player {
|
|
|
139
139
|
}
|
|
140
140
|
/** Load an audio file by URL, with optional pre-decoded audio data */
|
|
141
141
|
async load(url, channelData, duration) {
|
|
142
|
+
if (this.isPlaying())
|
|
143
|
+
this.pause();
|
|
142
144
|
this.decodedData = null;
|
|
143
145
|
this.duration = null;
|
|
144
146
|
this.emit('load', url);
|
|
145
147
|
// Fetch the entire audio as a blob if pre-decoded data is not provided
|
|
146
|
-
const blob = channelData ? undefined : await Fetcher.fetchBlob(url);
|
|
148
|
+
const blob = channelData ? undefined : await Fetcher.fetchBlob(url, this.options.fetchParams);
|
|
147
149
|
// Set the mediaelement source to the URL
|
|
148
150
|
this.setSrc(url, blob);
|
|
149
151
|
// Wait for the audio duration
|
|
@@ -173,7 +175,7 @@ class WaveSurfer extends Player {
|
|
|
173
175
|
}
|
|
174
176
|
this.emit('ready', this.duration);
|
|
175
177
|
}
|
|
176
|
-
/** Zoom
|
|
178
|
+
/** Zoom the waveform by a given pixels-per-second factor */
|
|
177
179
|
zoom(minPxPerSec) {
|
|
178
180
|
if (!this.decodedData) {
|
|
179
181
|
throw new Error('No audio loaded');
|
|
@@ -195,7 +197,7 @@ class WaveSurfer extends Player {
|
|
|
195
197
|
toggleInteraction(isInteractive) {
|
|
196
198
|
this.options.interact = isInteractive;
|
|
197
199
|
}
|
|
198
|
-
/**
|
|
200
|
+
/** Seek to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
|
|
199
201
|
seekTo(progress) {
|
|
200
202
|
const time = this.getDuration() * progress;
|
|
201
203
|
this.setTime(time);
|
|
@@ -209,7 +211,7 @@ class WaveSurfer extends Player {
|
|
|
209
211
|
this.pause();
|
|
210
212
|
this.setTime(0);
|
|
211
213
|
}
|
|
212
|
-
/** Skip N or -N seconds from the current
|
|
214
|
+
/** Skip N or -N seconds from the current position */
|
|
213
215
|
skip(seconds) {
|
|
214
216
|
this.setTime(this.getCurrentTime() + seconds);
|
|
215
217
|
}
|