wavesurfer.js 7.0.0-beta.9 → 7.0.0
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 +56 -24
- package/dist/base-plugin.d.ts +5 -2
- package/dist/base-plugin.js +1 -0
- package/dist/decoder.js +17 -6
- package/dist/draggable.js +10 -7
- package/dist/fetcher.d.ts +1 -3
- package/dist/fetcher.js +13 -6
- package/dist/player.d.ts +2 -2
- package/dist/player.js +10 -8
- package/dist/plugins/base-plugin.d.ts +16 -0
- package/dist/plugins/decoder.d.ts +9 -0
- package/dist/plugins/draggable.d.ts +1 -0
- package/dist/plugins/envelope.cjs +1 -0
- package/dist/plugins/envelope.d.ts +12 -4
- package/dist/plugins/envelope.esm.js +1 -0
- package/dist/plugins/envelope.js +29 -9
- package/dist/plugins/envelope.min.js +1 -0
- package/dist/plugins/event-emitter.d.ts +19 -0
- package/dist/plugins/fetcher.d.ts +5 -0
- package/dist/plugins/hover.cjs +1 -0
- package/dist/plugins/hover.d.ts +35 -0
- package/dist/plugins/hover.esm.js +1 -0
- package/dist/plugins/hover.js +101 -0
- package/dist/plugins/hover.min.js +1 -0
- package/dist/plugins/minimap.cjs +1 -0
- package/dist/plugins/minimap.d.ts +2 -2
- package/dist/plugins/minimap.esm.js +1 -0
- package/dist/plugins/minimap.js +8 -13
- package/dist/plugins/minimap.min.js +1 -0
- package/dist/plugins/player.d.ts +45 -0
- package/dist/plugins/plugins/envelope.d.ts +79 -0
- package/dist/plugins/plugins/hover.d.ts +35 -0
- package/dist/plugins/plugins/minimap.d.ts +39 -0
- package/dist/plugins/plugins/record.d.ts +31 -0
- package/dist/plugins/plugins/regions.d.ts +115 -0
- package/dist/plugins/plugins/spectrogram.d.ts +76 -0
- package/dist/plugins/plugins/timeline.d.ts +47 -0
- package/dist/plugins/record.cjs +1 -0
- package/dist/plugins/record.d.ts +7 -4
- package/dist/plugins/record.esm.js +1 -0
- package/dist/plugins/record.js +73 -66
- package/dist/plugins/record.min.js +1 -0
- package/dist/plugins/regions.cjs +1 -0
- package/dist/plugins/regions.d.ts +25 -4
- package/dist/plugins/regions.esm.js +1 -0
- package/dist/plugins/regions.js +60 -60
- package/dist/plugins/regions.min.js +1 -0
- package/dist/plugins/renderer.d.ts +44 -0
- package/dist/plugins/spectrogram.cjs +1 -0
- package/dist/plugins/spectrogram.d.ts +10 -3
- package/dist/plugins/spectrogram.esm.js +1 -0
- package/dist/plugins/spectrogram.js +166 -20
- package/dist/plugins/spectrogram.min.js +1 -0
- package/dist/plugins/timeline.cjs +1 -0
- package/dist/plugins/timeline.d.ts +5 -3
- package/dist/plugins/timeline.esm.js +1 -0
- package/dist/plugins/timeline.js +27 -21
- package/dist/plugins/timeline.min.js +1 -0
- package/dist/plugins/timer.d.ts +11 -0
- package/dist/plugins/wavesurfer.d.ts +156 -0
- package/dist/renderer.js +11 -7
- package/dist/wavesurfer.cjs +1 -0
- package/dist/wavesurfer.d.ts +5 -3
- package/dist/wavesurfer.esm.js +1 -0
- package/dist/wavesurfer.js +62 -41
- package/dist/wavesurfer.min.js +1 -0
- package/package.json +21 -22
- package/dist/plugins/envelope.min.cjs +0 -1
- package/dist/plugins/minimap.min.cjs +0 -1
- package/dist/plugins/record.min.cjs +0 -1
- package/dist/plugins/regions.min.cjs +0 -1
- package/dist/plugins/spectrogram-fft.d.ts +0 -9
- package/dist/plugins/spectrogram-fft.js +0 -150
- package/dist/plugins/spectrogram.min.cjs +0 -1
- package/dist/plugins/timeline.min.cjs +0 -1
- package/dist/wavesurfer.min.cjs +0 -1
package/dist/plugins/regions.js
CHANGED
|
@@ -6,16 +6,21 @@
|
|
|
6
6
|
import BasePlugin from '../base-plugin.js';
|
|
7
7
|
import { makeDraggable } from '../draggable.js';
|
|
8
8
|
import EventEmitter from '../event-emitter.js';
|
|
9
|
-
|
|
9
|
+
class Region extends EventEmitter {
|
|
10
10
|
constructor(params, totalDuration) {
|
|
11
|
+
var _a, _b, _c, _d, _e, _f;
|
|
11
12
|
super();
|
|
12
13
|
this.totalDuration = totalDuration;
|
|
14
|
+
this.minLength = 0;
|
|
15
|
+
this.maxLength = Infinity;
|
|
13
16
|
this.id = params.id || `region-${Math.random().toString(32).slice(2)}`;
|
|
14
17
|
this.start = params.start;
|
|
15
|
-
this.end = params.end
|
|
16
|
-
this.drag = params.drag
|
|
17
|
-
this.resize = params.resize
|
|
18
|
-
this.color = params.color
|
|
18
|
+
this.end = (_a = params.end) !== null && _a !== void 0 ? _a : params.start;
|
|
19
|
+
this.drag = (_b = params.drag) !== null && _b !== void 0 ? _b : true;
|
|
20
|
+
this.resize = (_c = params.resize) !== null && _c !== void 0 ? _c : true;
|
|
21
|
+
this.color = (_d = params.color) !== null && _d !== void 0 ? _d : 'rgba(0, 0, 0, 0.1)';
|
|
22
|
+
this.minLength = (_e = params.minLength) !== null && _e !== void 0 ? _e : this.minLength;
|
|
23
|
+
this.maxLength = (_f = params.maxLength) !== null && _f !== void 0 ? _f : this.maxLength;
|
|
19
24
|
this.element = this.initElement(params.content);
|
|
20
25
|
this.renderPosition();
|
|
21
26
|
this.initMouseEvents();
|
|
@@ -34,13 +39,12 @@ export class Region extends EventEmitter {
|
|
|
34
39
|
transition: background-color 0.2s ease;
|
|
35
40
|
cursor: ${this.drag ? 'grab' : 'default'};
|
|
36
41
|
pointer-events: all;
|
|
37
|
-
padding: 0.2em ${isMarker ? 0.2 : 0.4}em;
|
|
38
|
-
pointer-events: all;
|
|
39
42
|
`);
|
|
40
43
|
// Init content
|
|
41
44
|
if (content) {
|
|
42
45
|
if (typeof content === 'string') {
|
|
43
46
|
this.content = document.createElement('div');
|
|
47
|
+
this.content.style.padding = `0.2em ${isMarker ? 0.2 : 0.4}em`;
|
|
44
48
|
this.content.textContent = content;
|
|
45
49
|
}
|
|
46
50
|
else {
|
|
@@ -81,9 +85,9 @@ export class Region extends EventEmitter {
|
|
|
81
85
|
}
|
|
82
86
|
renderPosition() {
|
|
83
87
|
const start = this.start / this.totalDuration;
|
|
84
|
-
const end = this.end / this.totalDuration;
|
|
88
|
+
const end = (this.totalDuration - this.end) / this.totalDuration;
|
|
85
89
|
this.element.style.left = `${start * 100}%`;
|
|
86
|
-
this.element.style.
|
|
90
|
+
this.element.style.right = `${end * 100}%`;
|
|
87
91
|
}
|
|
88
92
|
initMouseEvents() {
|
|
89
93
|
const { element } = this;
|
|
@@ -111,31 +115,33 @@ export class Region extends EventEmitter {
|
|
|
111
115
|
this.element.style.cursor = 'grab';
|
|
112
116
|
this.emit('update-end');
|
|
113
117
|
}
|
|
114
|
-
|
|
118
|
+
_onUpdate(dx, side) {
|
|
115
119
|
if (!this.element.parentElement)
|
|
116
120
|
return;
|
|
117
121
|
const deltaSeconds = (dx / this.element.parentElement.clientWidth) * this.totalDuration;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
122
|
+
const newStart = !side || side === 'start' ? this.start + deltaSeconds : this.start;
|
|
123
|
+
const newEnd = !side || side === 'end' ? this.end + deltaSeconds : this.end;
|
|
124
|
+
const length = newEnd - newStart;
|
|
125
|
+
if (newStart > 0 &&
|
|
126
|
+
newEnd < this.totalDuration &&
|
|
127
|
+
newStart <= newEnd &&
|
|
128
|
+
length >= this.minLength &&
|
|
129
|
+
length <= this.maxLength) {
|
|
130
|
+
this.start = newStart;
|
|
131
|
+
this.end = newEnd;
|
|
132
|
+
this.renderPosition();
|
|
133
|
+
this.emit('update');
|
|
134
|
+
}
|
|
129
135
|
}
|
|
130
136
|
onMove(dx) {
|
|
131
137
|
if (!this.drag)
|
|
132
138
|
return;
|
|
133
|
-
this.
|
|
139
|
+
this._onUpdate(dx);
|
|
134
140
|
}
|
|
135
141
|
onResize(dx, side) {
|
|
136
142
|
if (!this.resize)
|
|
137
143
|
return;
|
|
138
|
-
this.
|
|
144
|
+
this._onUpdate(dx, side);
|
|
139
145
|
}
|
|
140
146
|
onEndResizing() {
|
|
141
147
|
if (!this.resize)
|
|
@@ -152,6 +158,7 @@ export class Region extends EventEmitter {
|
|
|
152
158
|
}
|
|
153
159
|
/** Update the region's options */
|
|
154
160
|
setOptions(options) {
|
|
161
|
+
var _a, _b;
|
|
155
162
|
if (options.color) {
|
|
156
163
|
this.color = options.color;
|
|
157
164
|
this.element.style.backgroundColor = this.color;
|
|
@@ -168,8 +175,8 @@ export class Region extends EventEmitter {
|
|
|
168
175
|
});
|
|
169
176
|
}
|
|
170
177
|
if (options.start !== undefined || options.end !== undefined) {
|
|
171
|
-
this.start = options.start
|
|
172
|
-
this.end = options.end
|
|
178
|
+
this.start = (_a = options.start) !== null && _a !== void 0 ? _a : this.start;
|
|
179
|
+
this.end = (_b = options.end) !== null && _b !== void 0 ? _b : this.end;
|
|
173
180
|
this.renderPosition();
|
|
174
181
|
}
|
|
175
182
|
}
|
|
@@ -233,7 +240,7 @@ class RegionsPlugin extends BasePlugin {
|
|
|
233
240
|
const width = reg.element.scrollWidth;
|
|
234
241
|
return labelLeft < left + width && left < labelLeft + labelWidth;
|
|
235
242
|
})
|
|
236
|
-
.map((reg) => reg.content
|
|
243
|
+
.map((reg) => { var _a; return ((_a = reg.content) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().height) || 0; })
|
|
237
244
|
.reduce((sum, val) => sum + val, 0);
|
|
238
245
|
div.style.marginTop = `${overlap}px`;
|
|
239
246
|
}
|
|
@@ -248,8 +255,9 @@ class RegionsPlugin extends BasePlugin {
|
|
|
248
255
|
this.emit('region-updated', region);
|
|
249
256
|
}),
|
|
250
257
|
region.on('play', () => {
|
|
251
|
-
|
|
252
|
-
this.wavesurfer
|
|
258
|
+
var _a, _b;
|
|
259
|
+
(_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.play();
|
|
260
|
+
(_b = this.wavesurfer) === null || _b === void 0 ? void 0 : _b.setTime(region.start);
|
|
253
261
|
}),
|
|
254
262
|
region.on('click', (e) => {
|
|
255
263
|
this.emit('region-clicked', region, e);
|
|
@@ -288,52 +296,44 @@ class RegionsPlugin extends BasePlugin {
|
|
|
288
296
|
* Returns a function to disable the drag selection.
|
|
289
297
|
*/
|
|
290
298
|
enableDragSelection(options) {
|
|
291
|
-
|
|
299
|
+
var _a, _b;
|
|
300
|
+
const wrapper = (_b = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getWrapper()) === null || _b === void 0 ? void 0 : _b.querySelector('div');
|
|
292
301
|
if (!wrapper)
|
|
293
302
|
return () => undefined;
|
|
303
|
+
const initialSize = 5;
|
|
294
304
|
let region = null;
|
|
295
|
-
let
|
|
305
|
+
let startX = 0;
|
|
296
306
|
return makeDraggable(wrapper,
|
|
297
307
|
// On drag move
|
|
298
|
-
(dx,
|
|
299
|
-
if (!this.wavesurfer)
|
|
300
|
-
return;
|
|
301
|
-
if (!region) {
|
|
302
|
-
const duration = this.wavesurfer.getDuration();
|
|
303
|
-
const box = wrapper.getBoundingClientRect();
|
|
304
|
-
let start = (x / box.width) * duration;
|
|
305
|
-
let end = ((x - box.left) / box.width) * duration;
|
|
306
|
-
if (start > end)
|
|
307
|
-
[start, end] = [end, start];
|
|
308
|
-
region = new Region({
|
|
309
|
-
...options,
|
|
310
|
-
start,
|
|
311
|
-
end,
|
|
312
|
-
}, duration);
|
|
313
|
-
this.regionsContainer.appendChild(region.element);
|
|
314
|
-
}
|
|
315
|
-
sumDx += dx;
|
|
308
|
+
(dx, _dy, x) => {
|
|
316
309
|
if (region) {
|
|
317
|
-
|
|
318
|
-
|
|
310
|
+
// Update the end position of the region
|
|
311
|
+
// If we're dragging to the left, we need to update the start instead
|
|
312
|
+
region._onUpdate(dx, x > startX ? 'end' : 'start');
|
|
319
313
|
}
|
|
320
314
|
},
|
|
321
315
|
// On drag start
|
|
322
|
-
() =>
|
|
316
|
+
(x) => {
|
|
317
|
+
startX = x;
|
|
318
|
+
if (!this.wavesurfer)
|
|
319
|
+
return;
|
|
320
|
+
const duration = this.wavesurfer.getDuration();
|
|
321
|
+
const width = this.wavesurfer.getWrapper().clientWidth;
|
|
322
|
+
// Calculate the start time of the region
|
|
323
|
+
const start = (x / width) * duration;
|
|
324
|
+
// Give the region a small initial size
|
|
325
|
+
const end = ((x + initialSize) / width) * duration;
|
|
326
|
+
// Create a region but don't save it until the drag ends
|
|
327
|
+
region = new Region(Object.assign(Object.assign({}, options), { start,
|
|
328
|
+
end }), duration);
|
|
329
|
+
// Just add it to the DOM for now
|
|
330
|
+
this.regionsContainer.appendChild(region.element);
|
|
331
|
+
},
|
|
323
332
|
// On drag end
|
|
324
333
|
() => {
|
|
325
334
|
if (region) {
|
|
326
335
|
this.saveRegion(region);
|
|
327
336
|
region = null;
|
|
328
|
-
sumDx = 0;
|
|
329
|
-
// Prevent a click event on the waveform
|
|
330
|
-
if (this.wavesurfer) {
|
|
331
|
-
const { interact } = this.wavesurfer.options;
|
|
332
|
-
if (interact) {
|
|
333
|
-
this.wavesurfer.toggleInteraction(false);
|
|
334
|
-
setTimeout(() => this.wavesurfer?.toggleInteraction(interact), 10);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
337
|
}
|
|
338
338
|
});
|
|
339
339
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):((e="undefined"!=typeof globalThis?globalThis:e||self).WaveSurfer=e.WaveSurfer||{},e.WaveSurfer.Regions=t())}(this,(function(){"use strict";class e{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)))}}class t extends e{constructor(e){super(),this.subscriptions=[],this.options=e}onInit(){}init(e){this.wavesurfer=e,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((e=>e()))}}function i(e,t,i,n,s=5){let r=()=>{};if(!e)return r;const o=o=>{if(2===o.button)return;o.preventDefault(),o.stopPropagation();let l=o.clientX,a=o.clientY,d=!1;const h=n=>{n.preventDefault(),n.stopPropagation();const r=n.clientX,o=n.clientY;if(d||Math.abs(r-l)>=s||Math.abs(o-a)>=s){const{left:n,top:s}=e.getBoundingClientRect();d||(d=!0,null==i||i(l-n,a-s)),t(r-l,o-a,r-n,o-s),l=r,a=o}},u=e=>{d&&(e.preventDefault(),e.stopPropagation())},c=()=>{d&&(null==n||n()),r()};document.addEventListener("pointermove",h),document.addEventListener("pointerup",c),document.addEventListener("pointerleave",c),document.addEventListener("click",u,!0),r=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",c),document.removeEventListener("pointerleave",c),setTimeout((()=>{document.removeEventListener("click",u,!0)}),10)}};return e.addEventListener("pointerdown",o),()=>{r(),e.removeEventListener("pointerdown",o)}}class n extends e{constructor(e,t){var i,n,s,r,o,l;super(),this.totalDuration=t,this.minLength=0,this.maxLength=1/0,this.id=e.id||`region-${Math.random().toString(32).slice(2)}`,this.start=e.start,this.end=null!==(i=e.end)&&void 0!==i?i:e.start,this.drag=null===(n=e.drag)||void 0===n||n,this.resize=null===(s=e.resize)||void 0===s||s,this.color=null!==(r=e.color)&&void 0!==r?r:"rgba(0, 0, 0, 0.1)",this.minLength=null!==(o=e.minLength)&&void 0!==o?o:this.minLength,this.maxLength=null!==(l=e.maxLength)&&void 0!==l?l: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;if(!e)return;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))),i(e,(e=>this.onMove(e)),(()=>this.onStartMoving()),(()=>this.onEndMoving()));i(e.querySelector('[data-resize="left"]'),(e=>this.onResize(e,"start")),(()=>null),(()=>this.onEndResizing()),1),i(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){var t,i;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=null!==(t=e.start)&&void 0!==t?t:this.start,this.end=null!==(i=e.end)&&void 0!==i?i:this.end,this.renderPosition())}remove(){this.emit("remove"),this.element.remove(),this.element=null}}class s extends t{constructor(e){super(e),this.regions=[],this.regionsContainer=this.initRegionsContainer()}static create(e){return new s(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=>{var t;return(null===(t=e.content)||void 0===t?void 0:t.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",(()=>{var t,i;null===(t=this.wavesurfer)||void 0===t||t.play(),null===(i=this.wavesurfer)||void 0===i||i.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 n(e,t);return t?this.saveRegion(i):this.subscriptions.push(this.wavesurfer.once("ready",(e=>{i._setTotalDuration(e),this.saveRegion(i)}))),i}enableDragSelection(e){var t,s;const r=null===(s=null===(t=this.wavesurfer)||void 0===t?void 0:t.getWrapper())||void 0===s?void 0:s.querySelector("div");if(!r)return()=>{};let o=null,l=0;return i(r,((e,t,i)=>{o&&o._onUpdate(e,i>l?"end":"start")}),(t=>{if(l=t,!this.wavesurfer)return;const i=this.wavesurfer.getDuration(),s=this.wavesurfer.getWrapper().clientWidth,r=t/s*i,a=(t+5)/s*i;o=new n(Object.assign(Object.assign({},e),{start:r,end:a}),i),this.regionsContainer.appendChild(o.element)}),(()=>{o&&(this.saveRegion(o),o=null)}))}clearRegions(){this.regions.forEach((e=>e.remove()))}destroy(){this.clearRegions(),super.destroy()}}return s}));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import EventEmitter from './event-emitter.js';
|
|
2
|
+
import type { WaveSurferOptions } from './wavesurfer.js';
|
|
3
|
+
type RendererEvents = {
|
|
4
|
+
click: [relativeX: number];
|
|
5
|
+
drag: [relativeX: number];
|
|
6
|
+
scroll: [relativeStart: number, relativeEnd: number];
|
|
7
|
+
render: [];
|
|
8
|
+
};
|
|
9
|
+
declare class Renderer extends EventEmitter<RendererEvents> {
|
|
10
|
+
private static MAX_CANVAS_WIDTH;
|
|
11
|
+
private options;
|
|
12
|
+
private parent;
|
|
13
|
+
private container;
|
|
14
|
+
private scrollContainer;
|
|
15
|
+
private wrapper;
|
|
16
|
+
private canvasWrapper;
|
|
17
|
+
private progressWrapper;
|
|
18
|
+
private cursor;
|
|
19
|
+
private timeouts;
|
|
20
|
+
private isScrolling;
|
|
21
|
+
private audioData;
|
|
22
|
+
private resizeObserver;
|
|
23
|
+
private isDragging;
|
|
24
|
+
constructor(options: WaveSurferOptions);
|
|
25
|
+
private initEvents;
|
|
26
|
+
private initDrag;
|
|
27
|
+
private getHeight;
|
|
28
|
+
private initHtml;
|
|
29
|
+
setOptions(options: WaveSurferOptions): void;
|
|
30
|
+
getWrapper(): HTMLElement;
|
|
31
|
+
getScroll(): number;
|
|
32
|
+
destroy(): void;
|
|
33
|
+
private createDelay;
|
|
34
|
+
private convertColorValues;
|
|
35
|
+
private renderBars;
|
|
36
|
+
private renderSingleCanvas;
|
|
37
|
+
private renderWaveform;
|
|
38
|
+
render(audioData: AudioBuffer): void;
|
|
39
|
+
reRender(): void;
|
|
40
|
+
zoom(minPxPerSec: number): void;
|
|
41
|
+
private scrollIntoView;
|
|
42
|
+
renderProgress(progress: number, isPlaying?: boolean): void;
|
|
43
|
+
}
|
|
44
|
+
export default Renderer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class t{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)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}function s(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,h=t>>1;r<t;){for(a=0;a<r;a++)this.reverseTable[a+r]=this.reverseTable[a]+h;r<<=1,h>>=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,h=this.sinTable,n=this.reverseTable,l=new Float32Array(a),o=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,M,b,g,m,y,v,x=1,k=0;k<a;k++)l[k]=t[n[k]]*this.windowValues[n[k]],o[k]=0;for(;x<a;){d=r[x],w=h[x],M=1,b=0;for(var q=0;q<x;q++){for(k=q;k<a;)m=M*l[g=k+x]-b*o[g],y=M*o[g]+b*l[g],l[g]=l[k]-m,o[g]=o[k]-y,l[k]+=m,o[k]+=y,k+=x<<1;M=(v=M)*d-b*w,b=v*w+b*d}x<<=1}k=0;for(var S=a/2;k<S;k++)(i=c*f((e=l[k])*e+(s=o[k])*s))>this.peak&&(this.peakBand=k,this.peak=i),p[k]=i;return p}}class i extends e{static create(t){return new i(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,h=this.frequencyMax;if(e){for(let n=0;n<t.length;n++){const l=this.resample(t[n]),o=new ImageData(i,s);for(let t=0;t<l.length;t++)for(let e=0;e<l[t].length;e++){const a=this.colorMap[l[t][e]],r=4*((s-e)*i+t);o.data[r]=255*a[0],o.data[r+1]=255*a[1],o.data[r+2]=255*a[2],o.data[r+3]=255*a[3]}createImageBitmap(o).then((t=>{e.drawImage(t,0,s*(1-h/a),i,s*(h-r)/a,0,s*n,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),super.destroy()}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,i=this.wavesurfer.getDecodedData(),a=this.channels;if(this.frequencyMax=this.frequencyMax||i.sampleRate/2,!i)return;this.buffer=i;const r=i.sampleRate,h=[];let n=this.noverlap;if(!n){const t=i.length/this.canvas.width;n=Math.max(0,Math.round(e-t))}const l=new s(e,r,this.windowFunc,this.alpha);for(let t=0;t<a;t++){const s=i.getChannelData(t),a=[];let r=0;for(;r+e<s.length;){const t=s.slice(r,r+e),i=l.calculateSpectrum(t),h=new Uint8Array(e/2);let o;for(o=0;o<e/2;o++)h[o]=Math.max(-255,45*Math.log10(i[o]));a.push(h),r+=e-n}h.push(a)}t(h,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,h,n){t=t||"rgba(68,68,68,0)",e=e||"12px",s=s||"12px",i=i||"Helvetica",a=a||"#fff",r=r||"#fff",h=h||"center";const l=this.height||512,o=l/256*5,c=this.frequencyMin,f=(this.frequencyMax-c)/o,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 n=0;n<this.channels;n++){let u;for(p.fillStyle=t,p.fillRect(0,n*l,55,(1+n)*l),p.fill(),u=0;u<=o;u++){p.textAlign=h,p.textBaseline="middle";const t=c+f*u,o=this.freqType(t),d=this.unitType(t),w=16;let M;M=0==u?(1+n)*l+u-10:(1+n)*l-50*u+2,p.fillStyle=r,p.font=s+" "+i,p.fillText(d,w+24,M),p.fillStyle=a,p.font=e+" "+i,p.fillText(o,w,M)}}}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 h;for(h=0;h<t.length;h++){const s=h*i,n=s+i,l=r*a,o=l+a,c=n<=l||o<=s?0:Math.min(Math.max(n,l),Math.max(o,s))-Math.max(Math.min(n,l),Math.min(o,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[h][f]}const n=new Uint8Array(t[0].length);let l;for(l=0;l<t[0].length;l++)n[l]=e[l];s.push(n)}return s}}module.exports=i;
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
* ]
|
|
18
18
|
* });
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Spectrogram plugin for wavesurfer.
|
|
22
|
+
*/
|
|
23
|
+
import BasePlugin, { type BasePluginEvents } from '../base-plugin.js';
|
|
21
24
|
export type SpectrogramPluginOptions = {
|
|
22
25
|
/** Selector of element or element in which to render */
|
|
23
26
|
container: string | HTMLElement;
|
|
@@ -27,6 +30,9 @@ export type SpectrogramPluginOptions = {
|
|
|
27
30
|
height?: number;
|
|
28
31
|
/** Set to true to display frequency labels. */
|
|
29
32
|
labels?: boolean;
|
|
33
|
+
labelsBackground?: string;
|
|
34
|
+
labelsColor?: string;
|
|
35
|
+
labelsHzColor?: string;
|
|
30
36
|
/** Size of the overlapping window. Must be < fftSamples. Auto deduced from canvas size by default. */
|
|
31
37
|
noverlap?: number;
|
|
32
38
|
/** The window function to be used. */
|
|
@@ -43,11 +49,11 @@ export type SpectrogramPluginOptions = {
|
|
|
43
49
|
*/
|
|
44
50
|
colorMap?: number[][];
|
|
45
51
|
};
|
|
46
|
-
export type SpectrogramPluginEvents = {
|
|
52
|
+
export type SpectrogramPluginEvents = BasePluginEvents & {
|
|
47
53
|
ready: [];
|
|
48
54
|
click: [relativeX: number];
|
|
49
55
|
};
|
|
50
|
-
|
|
56
|
+
declare class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramPluginOptions> {
|
|
51
57
|
static create(options?: SpectrogramPluginOptions): SpectrogramPlugin;
|
|
52
58
|
utils: {
|
|
53
59
|
style: (el: HTMLElement, styles: Record<string, string>) => CSSStyleDeclaration & Record<string, string>;
|
|
@@ -67,3 +73,4 @@ export default class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvent
|
|
|
67
73
|
loadLabels(bgFill: any, fontSizeFreq: any, fontSizeUnit: any, fontType: any, textColorFreq: any, textColorUnit: any, textAlign: any, container: any): void;
|
|
68
74
|
resample(oldMatrix: any): Uint8Array[];
|
|
69
75
|
}
|
|
76
|
+
export default SpectrogramPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class t{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)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}function s(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,h=t>>1;r<t;){for(a=0;a<r;a++)this.reverseTable[a+r]=this.reverseTable[a]+h;r<<=1,h>>=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,h=this.sinTable,n=this.reverseTable,l=new Float32Array(a),o=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,M,b,g,m,y,v,x=1,k=0;k<a;k++)l[k]=t[n[k]]*this.windowValues[n[k]],o[k]=0;for(;x<a;){d=r[x],w=h[x],M=1,b=0;for(var q=0;q<x;q++){for(k=q;k<a;)m=M*l[g=k+x]-b*o[g],y=M*o[g]+b*l[g],l[g]=l[k]-m,o[g]=o[k]-y,l[k]+=m,o[k]+=y,k+=x<<1;M=(v=M)*d-b*w,b=v*w+b*d}x<<=1}k=0;for(var S=a/2;k<S;k++)(i=c*f((e=l[k])*e+(s=o[k])*s))>this.peak&&(this.peakBand=k,this.peak=i),p[k]=i;return p}}class i extends e{static create(t){return new i(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,h=this.frequencyMax;if(e){for(let n=0;n<t.length;n++){const l=this.resample(t[n]),o=new ImageData(i,s);for(let t=0;t<l.length;t++)for(let e=0;e<l[t].length;e++){const a=this.colorMap[l[t][e]],r=4*((s-e)*i+t);o.data[r]=255*a[0],o.data[r+1]=255*a[1],o.data[r+2]=255*a[2],o.data[r+3]=255*a[3]}createImageBitmap(o).then((t=>{e.drawImage(t,0,s*(1-h/a),i,s*(h-r)/a,0,s*n,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),super.destroy()}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,i=this.wavesurfer.getDecodedData(),a=this.channels;if(this.frequencyMax=this.frequencyMax||i.sampleRate/2,!i)return;this.buffer=i;const r=i.sampleRate,h=[];let n=this.noverlap;if(!n){const t=i.length/this.canvas.width;n=Math.max(0,Math.round(e-t))}const l=new s(e,r,this.windowFunc,this.alpha);for(let t=0;t<a;t++){const s=i.getChannelData(t),a=[];let r=0;for(;r+e<s.length;){const t=s.slice(r,r+e),i=l.calculateSpectrum(t),h=new Uint8Array(e/2);let o;for(o=0;o<e/2;o++)h[o]=Math.max(-255,45*Math.log10(i[o]));a.push(h),r+=e-n}h.push(a)}t(h,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,h,n){t=t||"rgba(68,68,68,0)",e=e||"12px",s=s||"12px",i=i||"Helvetica",a=a||"#fff",r=r||"#fff",h=h||"center";const l=this.height||512,o=l/256*5,c=this.frequencyMin,f=(this.frequencyMax-c)/o,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 n=0;n<this.channels;n++){let u;for(p.fillStyle=t,p.fillRect(0,n*l,55,(1+n)*l),p.fill(),u=0;u<=o;u++){p.textAlign=h,p.textBaseline="middle";const t=c+f*u,o=this.freqType(t),d=this.unitType(t),w=16;let M;M=0==u?(1+n)*l+u-10:(1+n)*l-50*u+2,p.fillStyle=r,p.font=s+" "+i,p.fillText(d,w+24,M),p.fillStyle=a,p.font=e+" "+i,p.fillText(o,w,M)}}}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 h;for(h=0;h<t.length;h++){const s=h*i,n=s+i,l=r*a,o=l+a,c=n<=l||o<=s?0:Math.min(Math.max(n,l),Math.max(o,s))-Math.max(Math.min(n,l),Math.min(o,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[h][f]}const n=new Uint8Array(t[0].length);let l;for(l=0;l<t[0].length;l++)n[l]=e[l];s.push(n)}return s}}export{i as default};
|
|
@@ -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
|
+
class SpectrogramPlugin extends BasePlugin {
|
|
24
175
|
static create(options) {
|
|
25
176
|
return new SpectrogramPlugin(options || {});
|
|
26
177
|
}
|
|
@@ -127,6 +278,7 @@ export default class SpectrogramPlugin extends BasePlugin {
|
|
|
127
278
|
this.wrapper.remove();
|
|
128
279
|
this.wrapper = null;
|
|
129
280
|
}
|
|
281
|
+
super.destroy();
|
|
130
282
|
}
|
|
131
283
|
createWrapper() {
|
|
132
284
|
this.wrapper = document.createElement('div');
|
|
@@ -140,6 +292,7 @@ export default class SpectrogramPlugin extends BasePlugin {
|
|
|
140
292
|
// if labels are active
|
|
141
293
|
if (this.options.labels) {
|
|
142
294
|
const labelsEl = document.createElement('canvas');
|
|
295
|
+
labelsEl.setAttribute('part', 'spec-labels');
|
|
143
296
|
labelsEl.classList.add('spec-labels');
|
|
144
297
|
this.utils.style(labelsEl, {
|
|
145
298
|
position: 'absolute',
|
|
@@ -180,7 +333,7 @@ export default class SpectrogramPlugin extends BasePlugin {
|
|
|
180
333
|
else {
|
|
181
334
|
this.getFrequencies(this.drawSpectrogram);
|
|
182
335
|
}
|
|
183
|
-
this.loadLabels(
|
|
336
|
+
this.loadLabels(this.options.labelsBackground, '12px', '12px', '', this.options.labelsColor, this.options.labelsHzColor || this.options.labelsColor, 'center', '#specLabels');
|
|
184
337
|
}
|
|
185
338
|
getFrequencies(callback) {
|
|
186
339
|
const fftSamples = this.fftSamples;
|
|
@@ -236,7 +389,7 @@ export default class SpectrogramPlugin extends BasePlugin {
|
|
|
236
389
|
const frequenciesHeight = this.height;
|
|
237
390
|
bgFill = bgFill || 'rgba(68,68,68,0)';
|
|
238
391
|
fontSizeFreq = fontSizeFreq || '12px';
|
|
239
|
-
fontSizeUnit = fontSizeUnit || '
|
|
392
|
+
fontSizeUnit = fontSizeUnit || '12px';
|
|
240
393
|
fontType = fontType || 'Helvetica';
|
|
241
394
|
textColorFreq = textColorFreq || '#fff';
|
|
242
395
|
textColorUnit = textColorUnit || '#fff';
|
|
@@ -275,26 +428,18 @@ export default class SpectrogramPlugin extends BasePlugin {
|
|
|
275
428
|
let y;
|
|
276
429
|
if (i == 0) {
|
|
277
430
|
y = (1 + c) * getMaxY + i - 10;
|
|
278
|
-
// unit label
|
|
279
|
-
ctx.fillStyle = textColorUnit;
|
|
280
|
-
ctx.font = fontSizeUnit + ' ' + fontType;
|
|
281
|
-
ctx.fillText(units, x + 24, y);
|
|
282
|
-
// freq label
|
|
283
|
-
ctx.fillStyle = textColorFreq;
|
|
284
|
-
ctx.font = fontSizeFreq + ' ' + fontType;
|
|
285
|
-
ctx.fillText(label, x, y);
|
|
286
431
|
}
|
|
287
432
|
else {
|
|
288
433
|
y = (1 + c) * getMaxY - i * 50 + yLabelOffset;
|
|
289
|
-
// unit label
|
|
290
|
-
ctx.fillStyle = textColorUnit;
|
|
291
|
-
ctx.font = fontSizeUnit + ' ' + fontType;
|
|
292
|
-
ctx.fillText(units, x + 24, y);
|
|
293
|
-
// freq label
|
|
294
|
-
ctx.fillStyle = textColorFreq;
|
|
295
|
-
ctx.font = fontSizeFreq + ' ' + fontType;
|
|
296
|
-
ctx.fillText(label, x, y);
|
|
297
434
|
}
|
|
435
|
+
// unit label
|
|
436
|
+
ctx.fillStyle = textColorUnit;
|
|
437
|
+
ctx.font = fontSizeUnit + ' ' + fontType;
|
|
438
|
+
ctx.fillText(units, x + 24, y);
|
|
439
|
+
// freq label
|
|
440
|
+
ctx.fillStyle = textColorFreq;
|
|
441
|
+
ctx.font = fontSizeFreq + ' ' + fontType;
|
|
442
|
+
ctx.fillText(label, x, y);
|
|
298
443
|
}
|
|
299
444
|
}
|
|
300
445
|
}
|
|
@@ -338,3 +483,4 @@ export default class SpectrogramPlugin extends BasePlugin {
|
|
|
338
483
|
return newMatrix;
|
|
339
484
|
}
|
|
340
485
|
}
|
|
486
|
+
export default SpectrogramPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):((t="undefined"!=typeof globalThis?globalThis:t||self).WaveSurfer=t.WaveSurfer||{},t.WaveSurfer.Spectrogram=e())}(this,(function(){"use strict";class t{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)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}function s(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,l=new Float32Array(a),o=new Float32Array(a),c=2/this.bufferSize,f=Math.sqrt,u=new Float32Array(a/2),p=Math.floor(Math.log(a)/Math.LN2);if(Math.pow(2,p)!==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++)l[S]=t[h[S]]*this.windowValues[h[S]],o[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*l[g=S+x]-M*o[g],y=b*o[g]+M*l[g],l[g]=l[S]-m,o[g]=o[S]-y,l[S]+=m,o[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=l[S])*e+(s=o[S])*s))>this.peak&&(this.peakBand=S,this.peak=i),u[S]=i;return u}}class i extends e{static create(t){return new i(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 l=this.resample(t[h]),o=new ImageData(i,s);for(let t=0;t<l.length;t++)for(let e=0;e<l[t].length;e++){const a=this.colorMap[l[t][e]],r=4*((s-e)*i+t);o.data[r]=255*a[0],o.data[r+1]=255*a[1],o.data[r+2]=255*a[2],o.data[r+3]=255*a[3]}createImageBitmap(o).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),super.destroy()}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,i=this.wavesurfer.getDecodedData(),a=this.channels;if(this.frequencyMax=this.frequencyMax||i.sampleRate/2,!i)return;this.buffer=i;const r=i.sampleRate,n=[];let h=this.noverlap;if(!h){const t=i.length/this.canvas.width;h=Math.max(0,Math.round(e-t))}const l=new s(e,r,this.windowFunc,this.alpha);for(let t=0;t<a;t++){const s=i.getChannelData(t),a=[];let r=0;for(;r+e<s.length;){const t=s.slice(r,r+e),i=l.calculateSpectrum(t),n=new Uint8Array(e/2);let o;for(o=0;o<e/2;o++)n[o]=Math.max(-255,45*Math.log10(i[o]));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";const l=this.height||512,o=l/256*5,c=this.frequencyMin,f=(this.frequencyMax-c)/o,u=this.labelsEl.getContext("2d"),p=window.devicePixelRatio;if(this.labelsEl.height=this.height*this.channels*p,this.labelsEl.width=55*p,u.scale(p,p),u)for(let h=0;h<this.channels;h++){let p;for(u.fillStyle=t,u.fillRect(0,h*l,55,(1+h)*l),u.fill(),p=0;p<=o;p++){u.textAlign=n,u.textBaseline="middle";const t=c+f*p,o=this.freqType(t),d=this.unitType(t),w=16;let b;b=0==p?(1+h)*l+p-10:(1+h)*l-50*p+2,u.fillStyle=r,u.font=s+" "+i,u.fillText(d,w+24,b),u.fillStyle=a,u.font=e+" "+i,u.fillText(o,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,l=r*a,o=l+a,c=h<=l||o<=s?0:Math.min(Math.max(h,l),Math.max(o,s))-Math.max(Math.min(h,l),Math.min(o,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 l;for(l=0;l<t[0].length;l++)h[l]=e[l];s.push(h)}return s}}return i}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class t{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)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}const i={height:20,formatTimeCallback:t=>{if(t/60>1){return`${Math.floor(t/60)}:${`${(t=Math.round(t%60))<10?"0":""}${t}`}`}return`${Math.round(1e3*t)/1e3}`}};class s extends e{constructor(t){super(t||{}),this.options=Object.assign({},i,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new s(t)}onInit(){var t;if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const e=null!==(t=this.options.container)&&void 0!==t?t:this.wavesurfer.getWrapper();this.options.insertPosition?(e.firstElementChild||e).insertAdjacentElement(this.options.insertPosition,this.timelineWrapper):e.appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("redraw",(()=>{var t;this.initTimeline((null===(t=this.wavesurfer)||void 0===t?void 0:t.getDuration())||0)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){const t=document.createElement("div");return t.setAttribute("part","timeline"),t}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){var e,i,s;const n=this.timelineWrapper.scrollWidth/t,r=null!==(e=this.options.timeInterval)&&void 0!==e?e:this.defaultTimeInterval(n),o=null!==(i=this.options.primaryLabelInterval)&&void 0!==i?i:this.defaultPrimaryLabelInterval(n),l=null!==(s=this.options.secondaryLabelInterval)&&void 0!==s?s:this.defaultSecondaryLabelInterval(n),a="beforebegin"===this.options.insertPosition,h=document.createElement("div");if(h.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n position: relative;\n `),a){const t="\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n ";h.setAttribute("style",h.getAttribute("style")+t)}"string"==typeof this.options.style?h.setAttribute("style",h.getAttribute("style")+this.options.style):"object"==typeof this.options.style&&Object.assign(h.style,this.options.style);const p=document.createElement("div");p.setAttribute("part","timeline-notch"),p.setAttribute("style",`\n width: 0;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: ${a?"flex-start":"flex-end"};\n ${a?"top: 0;":"bottom: 0;"}\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n position: absolute;\n z-index: 1;\n `);for(let e=0;e<t;e+=r){const t=p.cloneNode(),i=Math.round(100*e)/100%o==0,s=Math.round(100*e)/100%l==0;(i||s)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.options.formatTimeCallback(e),i&&(t.style.opacity="1")),t.style.left=e*n+"px",h.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(h),this.emit("ready")}}module.exports=s;
|