wavesurfer.js 7.0.0-beta.8 → 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 +65 -33
- 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 +15 -10
- package/dist/fetcher.d.ts +2 -2
- package/dist/fetcher.js +14 -3
- package/dist/player.d.ts +3 -3
- package/dist/player.js +12 -10
- 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 -1
- 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 -1
- 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 +9 -4
- package/dist/plugins/record.esm.js +1 -0
- package/dist/plugins/record.js +77 -65
- package/dist/plugins/record.min.js +1 -1
- 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 -1
- 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 -1
- 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 -1
- package/dist/plugins/timer.d.ts +11 -0
- package/dist/plugins/wavesurfer.d.ts +156 -0
- package/dist/renderer.js +25 -11
- package/dist/wavesurfer.cjs +1 -0
- package/dist/wavesurfer.d.ts +7 -4
- package/dist/wavesurfer.esm.js +1 -0
- package/dist/wavesurfer.js +74 -42
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +21 -22
- package/dist/plugins/spectrogram-fft.d.ts +0 -9
- package/dist/plugins/spectrogram-fft.js +0 -150
|
@@ -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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"
|
|
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;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Timeline plugin adds timestamps and notches under the waveform.
|
|
3
3
|
*/
|
|
4
|
-
import BasePlugin from '../base-plugin.js';
|
|
4
|
+
import BasePlugin, { type BasePluginEvents } from '../base-plugin.js';
|
|
5
5
|
export type TimelinePluginOptions = {
|
|
6
6
|
/** The height of the timeline in pixels, defaults to 20 */
|
|
7
7
|
height?: number;
|
|
@@ -19,11 +19,14 @@ export type TimelinePluginOptions = {
|
|
|
19
19
|
secondaryLabelInterval?: number;
|
|
20
20
|
/** Custom inline style to apply to the container */
|
|
21
21
|
style?: Partial<CSSStyleDeclaration> | string;
|
|
22
|
+
/** Turn the time into a suitable label for the time. */
|
|
23
|
+
formatTimeCallback?: (seconds: number) => string;
|
|
22
24
|
};
|
|
23
25
|
declare const defaultOptions: {
|
|
24
26
|
height: number;
|
|
27
|
+
formatTimeCallback: (seconds: number) => string;
|
|
25
28
|
};
|
|
26
|
-
export type TimelinePluginEvents = {
|
|
29
|
+
export type TimelinePluginEvents = BasePluginEvents & {
|
|
27
30
|
ready: [];
|
|
28
31
|
};
|
|
29
32
|
declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
|
|
@@ -36,7 +39,6 @@ declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePl
|
|
|
36
39
|
/** Unmount */
|
|
37
40
|
destroy(): void;
|
|
38
41
|
private initTimelineWrapper;
|
|
39
|
-
private formatTime;
|
|
40
42
|
private defaultTimeInterval;
|
|
41
43
|
private defaultPrimaryLabelInterval;
|
|
42
44
|
private defaultSecondaryLabelInterval;
|
|
@@ -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 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)))}}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 n extends e{constructor(t){super(t||{}),this.options=Object.assign({},i,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new n(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,n;const s=this.timelineWrapper.scrollWidth/t,r=null!==(e=this.options.timeInterval)&&void 0!==e?e:this.defaultTimeInterval(s),o=null!==(i=this.options.primaryLabelInterval)&&void 0!==i?i:this.defaultPrimaryLabelInterval(s),l=null!==(n=this.options.secondaryLabelInterval)&&void 0!==n?n:this.defaultSecondaryLabelInterval(s),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,n=Math.round(100*e)/100%l==0;(i||n)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.options.formatTimeCallback(e),i&&(t.style.opacity="1")),t.style.left=e*s+"px",h.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(h),this.emit("ready")}}export{n as default};
|
package/dist/plugins/timeline.js
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
import BasePlugin from '../base-plugin.js';
|
|
5
5
|
const defaultOptions = {
|
|
6
6
|
height: 20,
|
|
7
|
+
formatTimeCallback: (seconds) => {
|
|
8
|
+
if (seconds / 60 > 1) {
|
|
9
|
+
// calculate minutes and seconds from seconds count
|
|
10
|
+
const minutes = Math.floor(seconds / 60);
|
|
11
|
+
seconds = Math.round(seconds % 60);
|
|
12
|
+
const paddedSeconds = `${seconds < 10 ? '0' : ''}${seconds}`;
|
|
13
|
+
return `${minutes}:${paddedSeconds}`;
|
|
14
|
+
}
|
|
15
|
+
const rounded = Math.round(seconds * 1000) / 1000;
|
|
16
|
+
return `${rounded}`;
|
|
17
|
+
},
|
|
7
18
|
};
|
|
8
19
|
class TimelinePlugin extends BasePlugin {
|
|
9
20
|
constructor(options) {
|
|
@@ -16,10 +27,11 @@ class TimelinePlugin extends BasePlugin {
|
|
|
16
27
|
}
|
|
17
28
|
/** Called by wavesurfer, don't call manually */
|
|
18
29
|
onInit() {
|
|
30
|
+
var _a;
|
|
19
31
|
if (!this.wavesurfer) {
|
|
20
32
|
throw Error('WaveSurfer is not initialized');
|
|
21
33
|
}
|
|
22
|
-
const container = this.options.container
|
|
34
|
+
const container = (_a = this.options.container) !== null && _a !== void 0 ? _a : this.wavesurfer.getWrapper();
|
|
23
35
|
if (this.options.insertPosition) {
|
|
24
36
|
;
|
|
25
37
|
(container.firstElementChild || container).insertAdjacentElement(this.options.insertPosition, this.timelineWrapper);
|
|
@@ -32,7 +44,8 @@ class TimelinePlugin extends BasePlugin {
|
|
|
32
44
|
}
|
|
33
45
|
else {
|
|
34
46
|
this.subscriptions.push(this.wavesurfer.on('redraw', () => {
|
|
35
|
-
|
|
47
|
+
var _a;
|
|
48
|
+
this.initTimeline(((_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getDuration()) || 0);
|
|
36
49
|
}));
|
|
37
50
|
}
|
|
38
51
|
}
|
|
@@ -46,17 +59,6 @@ class TimelinePlugin extends BasePlugin {
|
|
|
46
59
|
div.setAttribute('part', 'timeline');
|
|
47
60
|
return div;
|
|
48
61
|
}
|
|
49
|
-
formatTime(seconds) {
|
|
50
|
-
if (seconds / 60 > 1) {
|
|
51
|
-
// calculate minutes and seconds from seconds count
|
|
52
|
-
const minutes = Math.floor(seconds / 60);
|
|
53
|
-
seconds = Math.round(seconds % 60);
|
|
54
|
-
const paddedSeconds = `${seconds < 10 ? '0' : ''}${seconds}`;
|
|
55
|
-
return `${minutes}:${paddedSeconds}`;
|
|
56
|
-
}
|
|
57
|
-
const rounded = Math.round(seconds * 1000) / 1000;
|
|
58
|
-
return `${rounded}`;
|
|
59
|
-
}
|
|
60
62
|
// Return how many seconds should be between each notch
|
|
61
63
|
defaultTimeInterval(pxPerSec) {
|
|
62
64
|
if (pxPerSec >= 25) {
|
|
@@ -97,20 +99,19 @@ class TimelinePlugin extends BasePlugin {
|
|
|
97
99
|
return 2;
|
|
98
100
|
}
|
|
99
101
|
initTimeline(duration) {
|
|
102
|
+
var _a, _b, _c;
|
|
100
103
|
const pxPerSec = this.timelineWrapper.scrollWidth / duration;
|
|
101
|
-
const timeInterval = this.options.timeInterval
|
|
102
|
-
const primaryLabelInterval = this.options.primaryLabelInterval
|
|
103
|
-
const secondaryLabelInterval = this.options.secondaryLabelInterval
|
|
104
|
+
const timeInterval = (_a = this.options.timeInterval) !== null && _a !== void 0 ? _a : this.defaultTimeInterval(pxPerSec);
|
|
105
|
+
const primaryLabelInterval = (_b = this.options.primaryLabelInterval) !== null && _b !== void 0 ? _b : this.defaultPrimaryLabelInterval(pxPerSec);
|
|
106
|
+
const secondaryLabelInterval = (_c = this.options.secondaryLabelInterval) !== null && _c !== void 0 ? _c : this.defaultSecondaryLabelInterval(pxPerSec);
|
|
104
107
|
const isTop = this.options.insertPosition === 'beforebegin';
|
|
105
108
|
const timeline = document.createElement('div');
|
|
106
109
|
timeline.setAttribute('style', `
|
|
107
110
|
height: ${this.options.height}px;
|
|
108
111
|
overflow: hidden;
|
|
109
|
-
display: flex;
|
|
110
|
-
justify-content: space-between;
|
|
111
|
-
align-items: ${isTop ? 'flex-start' : 'flex-end'};
|
|
112
112
|
font-size: ${this.options.height / 2}px;
|
|
113
113
|
white-space: nowrap;
|
|
114
|
+
position: relative;
|
|
114
115
|
`);
|
|
115
116
|
if (isTop) {
|
|
116
117
|
const topStyle = `
|
|
@@ -129,15 +130,19 @@ class TimelinePlugin extends BasePlugin {
|
|
|
129
130
|
Object.assign(timeline.style, this.options.style);
|
|
130
131
|
}
|
|
131
132
|
const notchEl = document.createElement('div');
|
|
133
|
+
notchEl.setAttribute('part', 'timeline-notch');
|
|
132
134
|
notchEl.setAttribute('style', `
|
|
133
|
-
width:
|
|
135
|
+
width: 0;
|
|
134
136
|
height: 50%;
|
|
135
137
|
display: flex;
|
|
136
138
|
flex-direction: column;
|
|
137
139
|
justify-content: ${isTop ? 'flex-start' : 'flex-end'};
|
|
140
|
+
${isTop ? 'top: 0;' : 'bottom: 0;'}
|
|
138
141
|
overflow: visible;
|
|
139
142
|
border-left: 1px solid currentColor;
|
|
140
143
|
opacity: 0.25;
|
|
144
|
+
position: absolute;
|
|
145
|
+
z-index: 1;
|
|
141
146
|
`);
|
|
142
147
|
for (let i = 0; i < duration; i += timeInterval) {
|
|
143
148
|
const notch = notchEl.cloneNode();
|
|
@@ -146,10 +151,11 @@ class TimelinePlugin extends BasePlugin {
|
|
|
146
151
|
if (isPrimary || isSecondary) {
|
|
147
152
|
notch.style.height = '100%';
|
|
148
153
|
notch.style.textIndent = '3px';
|
|
149
|
-
notch.textContent = this.
|
|
154
|
+
notch.textContent = this.options.formatTimeCallback(i);
|
|
150
155
|
if (isPrimary)
|
|
151
156
|
notch.style.opacity = '1';
|
|
152
157
|
}
|
|
158
|
+
notch.style.left = `${i * pxPerSec}px`;
|
|
153
159
|
timeline.appendChild(notch);
|
|
154
160
|
}
|
|
155
161
|
this.timelineWrapper.innerHTML = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"
|
|
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.Timeline=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 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)))}}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 n extends e{constructor(t){super(t||{}),this.options=Object.assign({},i,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new n(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,n;const s=this.timelineWrapper.scrollWidth/t,r=null!==(e=this.options.timeInterval)&&void 0!==e?e:this.defaultTimeInterval(s),o=null!==(i=this.options.primaryLabelInterval)&&void 0!==i?i:this.defaultPrimaryLabelInterval(s),l=null!==(n=this.options.secondaryLabelInterval)&&void 0!==n?n:this.defaultSecondaryLabelInterval(s),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,n=Math.round(100*e)/100%l==0;(i||n)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.options.formatTimeCallback(e),i&&(t.style.opacity="1")),t.style.left=e*s+"px",h.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(h),this.emit("ready")}}return n}));
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { GenericPlugin } from './base-plugin.js';
|
|
2
|
+
import Player from './player.js';
|
|
3
|
+
export type WaveSurferOptions = {
|
|
4
|
+
/** HTML element or CSS selector */
|
|
5
|
+
container: HTMLElement | string;
|
|
6
|
+
/** The height of the waveform in pixels, or "auto" to fill the container height */
|
|
7
|
+
height?: number | 'auto';
|
|
8
|
+
/** The color of the waveform */
|
|
9
|
+
waveColor?: string | string[] | CanvasGradient;
|
|
10
|
+
/** The color of the progress mask */
|
|
11
|
+
progressColor?: string | string[] | CanvasGradient;
|
|
12
|
+
/** The color of the playpack cursor */
|
|
13
|
+
cursorColor?: string;
|
|
14
|
+
/** The cursor width */
|
|
15
|
+
cursorWidth?: number;
|
|
16
|
+
/** Render the waveform with bars like this: ▁ ▂ ▇ ▃ ▅ ▂ */
|
|
17
|
+
barWidth?: number;
|
|
18
|
+
/** Spacing between bars in pixels */
|
|
19
|
+
barGap?: number;
|
|
20
|
+
/** Rounded borders for bars */
|
|
21
|
+
barRadius?: number;
|
|
22
|
+
/** A vertical scaling factor for the waveform */
|
|
23
|
+
barHeight?: number;
|
|
24
|
+
/** Vertical bar alignment */
|
|
25
|
+
barAlign?: 'top' | 'bottom';
|
|
26
|
+
/** Minimum pixels per second of audio (i.e. zoom level) */
|
|
27
|
+
minPxPerSec?: number;
|
|
28
|
+
/** Stretch the waveform to fill the container, true by default */
|
|
29
|
+
fillParent?: boolean;
|
|
30
|
+
/** Audio URL */
|
|
31
|
+
url?: string;
|
|
32
|
+
/** Pre-computed audio data */
|
|
33
|
+
peaks?: Array<Float32Array | number[]>;
|
|
34
|
+
/** Pre-computed duration */
|
|
35
|
+
duration?: number;
|
|
36
|
+
/** Use an existing media element instead of creating one */
|
|
37
|
+
media?: HTMLMediaElement;
|
|
38
|
+
/** Play the audio on load */
|
|
39
|
+
autoplay?: boolean;
|
|
40
|
+
/** Pass false to disable clicks on the waveform */
|
|
41
|
+
interact?: boolean;
|
|
42
|
+
/** Hide the scrollbar */
|
|
43
|
+
hideScrollbar?: boolean;
|
|
44
|
+
/** Audio rate */
|
|
45
|
+
audioRate?: number;
|
|
46
|
+
/** Automatically scroll the container to keep the current position in viewport */
|
|
47
|
+
autoScroll?: boolean;
|
|
48
|
+
/** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
|
|
49
|
+
autoCenter?: boolean;
|
|
50
|
+
/** Decoding sample rate. Doesn't affect the playback. Defaults to 8000 */
|
|
51
|
+
sampleRate?: number;
|
|
52
|
+
/** Render each audio channel as a separate waveform */
|
|
53
|
+
splitChannels?: WaveSurferOptions[];
|
|
54
|
+
/** Stretch the waveform to the full height */
|
|
55
|
+
normalize?: boolean;
|
|
56
|
+
/** The list of plugins to initialize on start */
|
|
57
|
+
plugins?: GenericPlugin[];
|
|
58
|
+
/** Custom render function */
|
|
59
|
+
renderFunction?: (peaks: Array<Float32Array | number[]>, ctx: CanvasRenderingContext2D) => void;
|
|
60
|
+
/** Options to pass to the fetch method */
|
|
61
|
+
fetchParams?: RequestInit;
|
|
62
|
+
};
|
|
63
|
+
declare const defaultOptions: {
|
|
64
|
+
waveColor: string;
|
|
65
|
+
progressColor: string;
|
|
66
|
+
cursorWidth: number;
|
|
67
|
+
minPxPerSec: number;
|
|
68
|
+
fillParent: boolean;
|
|
69
|
+
interact: boolean;
|
|
70
|
+
autoScroll: boolean;
|
|
71
|
+
autoCenter: boolean;
|
|
72
|
+
sampleRate: number;
|
|
73
|
+
};
|
|
74
|
+
export type WaveSurferEvents = {
|
|
75
|
+
/** When audio starts loading */
|
|
76
|
+
load: [url: string];
|
|
77
|
+
/** When the audio has been decoded */
|
|
78
|
+
decode: [duration: number];
|
|
79
|
+
/** When the audio is both decoded and can play */
|
|
80
|
+
ready: [duration: number];
|
|
81
|
+
/** When a waveform is drawn */
|
|
82
|
+
redraw: [];
|
|
83
|
+
/** When the audio starts playing */
|
|
84
|
+
play: [];
|
|
85
|
+
/** When the audio pauses */
|
|
86
|
+
pause: [];
|
|
87
|
+
/** When the audio finishes playing */
|
|
88
|
+
finish: [];
|
|
89
|
+
/** On audio position change, fires continuously during playback */
|
|
90
|
+
timeupdate: [currentTime: number];
|
|
91
|
+
/** An alias of timeupdate but only when the audio is playing */
|
|
92
|
+
audioprocess: [currentTime: number];
|
|
93
|
+
/** When the user seeks to a new position */
|
|
94
|
+
seeking: [currentTime: number];
|
|
95
|
+
/** When the user interacts with the waveform (i.g. clicks or drags on it) */
|
|
96
|
+
interaction: [newTime: number];
|
|
97
|
+
/** When the user clicks on the waveform */
|
|
98
|
+
click: [relativeX: number];
|
|
99
|
+
/** When the user drags the cursor */
|
|
100
|
+
drag: [relativeX: number];
|
|
101
|
+
/** When the waveform is scrolled (panned) */
|
|
102
|
+
scroll: [visibleStartTime: number, visibleEndTime: number];
|
|
103
|
+
/** When the zoom level changes */
|
|
104
|
+
zoom: [minPxPerSec: number];
|
|
105
|
+
/** Just before the waveform is destroyed so you can clean up your events */
|
|
106
|
+
destroy: [];
|
|
107
|
+
};
|
|
108
|
+
declare class WaveSurfer extends Player<WaveSurferEvents> {
|
|
109
|
+
options: WaveSurferOptions & typeof defaultOptions;
|
|
110
|
+
private renderer;
|
|
111
|
+
private timer;
|
|
112
|
+
private plugins;
|
|
113
|
+
private decodedData;
|
|
114
|
+
private duration;
|
|
115
|
+
protected subscriptions: Array<() => void>;
|
|
116
|
+
/** Create a new WaveSurfer instance */
|
|
117
|
+
static create(options: WaveSurferOptions): WaveSurfer;
|
|
118
|
+
/** Create a new WaveSurfer instance */
|
|
119
|
+
constructor(options: WaveSurferOptions);
|
|
120
|
+
setOptions(options: Partial<WaveSurferOptions>): void;
|
|
121
|
+
private initTimerEvents;
|
|
122
|
+
private initPlayerEvents;
|
|
123
|
+
private initRendererEvents;
|
|
124
|
+
private initPlugins;
|
|
125
|
+
/** Register a wavesurfer.js plugin */
|
|
126
|
+
registerPlugin<T extends GenericPlugin>(plugin: T): T;
|
|
127
|
+
/** For plugins only: get the waveform wrapper div */
|
|
128
|
+
getWrapper(): HTMLElement;
|
|
129
|
+
/** Get the current scroll position in pixels */
|
|
130
|
+
getScroll(): number;
|
|
131
|
+
/** Get all registered plugins */
|
|
132
|
+
getActivePlugins(): GenericPlugin[];
|
|
133
|
+
/** Load an audio file by URL, with optional pre-decoded audio data */
|
|
134
|
+
load(url: string, channelData?: WaveSurferOptions['peaks'], duration?: number): Promise<void>;
|
|
135
|
+
/** Zoom the waveform by a given pixels-per-second factor */
|
|
136
|
+
zoom(minPxPerSec: number): void;
|
|
137
|
+
/** Get the decoded audio data */
|
|
138
|
+
getDecodedData(): AudioBuffer | null;
|
|
139
|
+
/** Get the duration of the audio in seconds */
|
|
140
|
+
getDuration(): number;
|
|
141
|
+
/** Toggle if the waveform should react to clicks */
|
|
142
|
+
toggleInteraction(isInteractive: boolean): void;
|
|
143
|
+
/** Seek to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
|
|
144
|
+
seekTo(progress: number): void;
|
|
145
|
+
/** Play or pause the audio */
|
|
146
|
+
playPause(): Promise<void>;
|
|
147
|
+
/** Stop the audio and go to the beginning */
|
|
148
|
+
stop(): void;
|
|
149
|
+
/** Skip N or -N seconds from the current position */
|
|
150
|
+
skip(seconds: number): void;
|
|
151
|
+
/** Empty the waveform by loading a tiny silent audio */
|
|
152
|
+
empty(): void;
|
|
153
|
+
/** Unmount wavesurfer */
|
|
154
|
+
destroy(): void;
|
|
155
|
+
}
|
|
156
|
+
export default WaveSurfer;
|