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.
Files changed (69) hide show
  1. package/README.md +65 -33
  2. package/dist/base-plugin.d.ts +5 -2
  3. package/dist/base-plugin.js +1 -0
  4. package/dist/decoder.js +17 -6
  5. package/dist/draggable.js +15 -10
  6. package/dist/fetcher.d.ts +2 -2
  7. package/dist/fetcher.js +14 -3
  8. package/dist/player.d.ts +3 -3
  9. package/dist/player.js +12 -10
  10. package/dist/plugins/base-plugin.d.ts +16 -0
  11. package/dist/plugins/decoder.d.ts +9 -0
  12. package/dist/plugins/draggable.d.ts +1 -0
  13. package/dist/plugins/envelope.cjs +1 -0
  14. package/dist/plugins/envelope.d.ts +12 -4
  15. package/dist/plugins/envelope.esm.js +1 -0
  16. package/dist/plugins/envelope.js +29 -9
  17. package/dist/plugins/envelope.min.js +1 -1
  18. package/dist/plugins/event-emitter.d.ts +19 -0
  19. package/dist/plugins/fetcher.d.ts +5 -0
  20. package/dist/plugins/hover.cjs +1 -0
  21. package/dist/plugins/hover.d.ts +35 -0
  22. package/dist/plugins/hover.esm.js +1 -0
  23. package/dist/plugins/hover.js +101 -0
  24. package/dist/plugins/hover.min.js +1 -0
  25. package/dist/plugins/minimap.cjs +1 -0
  26. package/dist/plugins/minimap.d.ts +2 -2
  27. package/dist/plugins/minimap.esm.js +1 -0
  28. package/dist/plugins/minimap.js +8 -13
  29. package/dist/plugins/minimap.min.js +1 -1
  30. package/dist/plugins/player.d.ts +45 -0
  31. package/dist/plugins/plugins/envelope.d.ts +79 -0
  32. package/dist/plugins/plugins/hover.d.ts +35 -0
  33. package/dist/plugins/plugins/minimap.d.ts +39 -0
  34. package/dist/plugins/plugins/record.d.ts +31 -0
  35. package/dist/plugins/plugins/regions.d.ts +115 -0
  36. package/dist/plugins/plugins/spectrogram.d.ts +76 -0
  37. package/dist/plugins/plugins/timeline.d.ts +47 -0
  38. package/dist/plugins/record.cjs +1 -0
  39. package/dist/plugins/record.d.ts +9 -4
  40. package/dist/plugins/record.esm.js +1 -0
  41. package/dist/plugins/record.js +77 -65
  42. package/dist/plugins/record.min.js +1 -1
  43. package/dist/plugins/regions.cjs +1 -0
  44. package/dist/plugins/regions.d.ts +25 -4
  45. package/dist/plugins/regions.esm.js +1 -0
  46. package/dist/plugins/regions.js +60 -60
  47. package/dist/plugins/regions.min.js +1 -1
  48. package/dist/plugins/renderer.d.ts +44 -0
  49. package/dist/plugins/spectrogram.cjs +1 -0
  50. package/dist/plugins/spectrogram.d.ts +10 -3
  51. package/dist/plugins/spectrogram.esm.js +1 -0
  52. package/dist/plugins/spectrogram.js +166 -20
  53. package/dist/plugins/spectrogram.min.js +1 -1
  54. package/dist/plugins/timeline.cjs +1 -0
  55. package/dist/plugins/timeline.d.ts +5 -3
  56. package/dist/plugins/timeline.esm.js +1 -0
  57. package/dist/plugins/timeline.js +27 -21
  58. package/dist/plugins/timeline.min.js +1 -1
  59. package/dist/plugins/timer.d.ts +11 -0
  60. package/dist/plugins/wavesurfer.d.ts +156 -0
  61. package/dist/renderer.js +25 -11
  62. package/dist/wavesurfer.cjs +1 -0
  63. package/dist/wavesurfer.d.ts +7 -4
  64. package/dist/wavesurfer.esm.js +1 -0
  65. package/dist/wavesurfer.js +74 -42
  66. package/dist/wavesurfer.min.js +1 -1
  67. package/package.json +21 -22
  68. package/dist/plugins/spectrogram-fft.d.ts +0 -9
  69. 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
- import FFT from './spectrogram-fft.js';
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('rgba(68,68,68,0.5)', '12px', '10px', '', '#fff', '#f7f7f7', 'center', '#specLabels');
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 || '10px';
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&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Spectrogram=e():t.Spectrogram=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={d:(e,s)=>{for(var i in s)t.o(s,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:s[i]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>r});const s=class{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const s=this.on(t,e),i=this.on(t,(()=>{s(),i()}));return s}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}},i=class extends s{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}};function a(t,e,s,i){switch(this.bufferSize=t,this.sampleRate=e,this.bandwidth=2/t*(e/2),this.sinTable=new Float32Array(t),this.cosTable=new Float32Array(t),this.windowValues=new Float32Array(t),this.reverseTable=new Uint32Array(t),this.peakBand=0,this.peak=0,s){case"bartlett":for(a=0;a<t;a++)this.windowValues[a]=2/(t-1)*((t-1)/2-Math.abs(a-(t-1)/2));break;case"bartlettHann":for(a=0;a<t;a++)this.windowValues[a]=.62-.48*Math.abs(a/(t-1)-.5)-.38*Math.cos(2*Math.PI*a/(t-1));break;case"blackman":for(i=i||.16,a=0;a<t;a++)this.windowValues[a]=(1-i)/2-.5*Math.cos(2*Math.PI*a/(t-1))+i/2*Math.cos(4*Math.PI*a/(t-1));break;case"cosine":for(a=0;a<t;a++)this.windowValues[a]=Math.cos(Math.PI*a/(t-1)-Math.PI/2);break;case"gauss":for(i=i||.25,a=0;a<t;a++)this.windowValues[a]=Math.pow(Math.E,-.5*Math.pow((a-(t-1)/2)/(i*(t-1)/2),2));break;case"hamming":for(a=0;a<t;a++)this.windowValues[a]=.54-.46*Math.cos(2*Math.PI*a/(t-1));break;case"hann":case void 0:for(a=0;a<t;a++)this.windowValues[a]=.5*(1-Math.cos(2*Math.PI*a/(t-1)));break;case"lanczoz":for(a=0;a<t;a++)this.windowValues[a]=Math.sin(Math.PI*(2*a/(t-1)-1))/(Math.PI*(2*a/(t-1)-1));break;case"rectangular":for(a=0;a<t;a++)this.windowValues[a]=1;break;case"triangular":for(a=0;a<t;a++)this.windowValues[a]=2/t*(t/2-Math.abs(a-(t-1)/2));break;default:throw Error("No such window function '"+s+"'")}for(var a,r=1,n=t>>1;r<t;){for(a=0;a<r;a++)this.reverseTable[a+r]=this.reverseTable[a]+n;r<<=1,n>>=1}for(a=0;a<t;a++)this.sinTable[a]=Math.sin(-Math.PI/a),this.cosTable[a]=Math.cos(-Math.PI/a);this.calculateSpectrum=function(t){var e,s,i,a=this.bufferSize,r=this.cosTable,n=this.sinTable,h=this.reverseTable,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,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],M=1,b=0;for(var k=0;k<x;k++){for(S=k;S<a;)m=M*l[g=S+x]-b*o[g],y=M*o[g]+b*l[g],l[g]=l[S]-m,o[g]=o[S]-y,l[S]+=m,o[S]+=y,S+=x<<1;M=(v=M)*d-b*w,b=v*w+b*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),p[S]=i;return p}}class r extends i{static create(t){return new r(t||{})}constructor(t){if(super(t),this.utils={style:(t,e)=>Object.assign(t.style,e)},this.drawSpectrogram=t=>{isNaN(t[0][0])||(t=[t]);const e=this.spectrCc,s=this.height,i=this.width,a=this.buffer.sampleRate/2,r=this.frequencyMin,n=this.frequencyMax;if(e){for(let h=0;h<t.length;h++){const 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)}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.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("rgba(68,68,68,0.5)","12px","10px","","#fff","#f7f7f7","center","#specLabels")}getFrequencies(t){const e=this.fftSamples,s=this.wavesurfer.getDecodedData(),i=this.channels;if(this.frequencyMax=this.frequencyMax||s.sampleRate/2,!s)return;this.buffer=s;const r=s.sampleRate,n=[];let h=this.noverlap;if(!h){const t=s.length/this.canvas.width;h=Math.max(0,Math.round(e-t))}const l=new a(e,r,this.windowFunc,this.alpha);for(let t=0;t<i;t++){const i=s.getChannelData(t),a=[];let r=0;for(;r+e<i.length;){const t=i.slice(r,r+e),s=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(s[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||"10px",i=i||"Helvetica",a=a||"#fff",r=r||"#fff",n=n||"center",h=h||"#specLabels";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 h=0;h<this.channels;h++){let u;for(p.fillStyle=t,p.fillRect(0,h*l,55,(1+h)*l),p.fill(),u=0;u<=o;u++){p.textAlign=n,p.textBaseline="middle";const t=c+f*u,o=this.freqType(t),d=this.unitType(t),w=2,M=16;let b;0==u?(b=(1+h)*l+u-10,p.fillStyle=r,p.font=s+" "+i,p.fillText(d,M+24,b),p.fillStyle=a,p.font=e+" "+i,p.fillText(o,M,b)):(b=(1+h)*l-50*u+w,p.fillStyle=r,p.font=s+" "+i,p.fillText(d,M+24,b),p.fillStyle=a,p.font=e+" "+i,p.fillText(o,M,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 e.default})()));
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};
@@ -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 ?? this.wavesurfer.getWrapper();
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
- this.initTimeline(this.wavesurfer?.getDuration() || 0);
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 ?? this.defaultTimeInterval(pxPerSec);
102
- const primaryLabelInterval = this.options.primaryLabelInterval ?? this.defaultPrimaryLabelInterval(pxPerSec);
103
- const secondaryLabelInterval = this.options.secondaryLabelInterval ?? this.defaultSecondaryLabelInterval(pxPerSec);
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: 1px;
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.formatTime(i);
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&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Timeline=e():t.Timeline=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>o});const i=class{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),n=this.on(t,(()=>{i(),n()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}},n=class extends i{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}},s={height:20};class r extends n{constructor(t){super(t||{}),this.options=Object.assign({},s,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new r(t)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const t=this.options.container??this.wavesurfer.getWrapper();this.options.insertPosition?(t.firstElementChild||t).insertAdjacentElement(this.options.insertPosition,this.timelineWrapper):t.appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("redraw",(()=>{this.initTimeline(this.wavesurfer?.getDuration()||0)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){const t=document.createElement("div");return t.setAttribute("part","timeline"),t}formatTime(t){return t/60>1?`${Math.floor(t/60)}:${(t=Math.round(t%60))<10?"0":""}${t}`:""+Math.round(1e3*t)/1e3}defaultTimeInterval(t){return t>=25?1:5*t>=25?5:15*t>=25?15:60*Math.ceil(.5/t)}defaultPrimaryLabelInterval(t){return t>=25?10:5*t>=25?6:4}defaultSecondaryLabelInterval(t){return t>=25?5:2}initTimeline(t){const e=this.timelineWrapper.scrollWidth/t,i=this.options.timeInterval??this.defaultTimeInterval(e),n=this.options.primaryLabelInterval??this.defaultPrimaryLabelInterval(e),s=this.options.secondaryLabelInterval??this.defaultSecondaryLabelInterval(e),r="beforebegin"===this.options.insertPosition,o=document.createElement("div");if(o.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n display: flex;\n justify-content: space-between;\n align-items: ${r?"flex-start":"flex-end"};\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n `),r){const t="\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n ";o.setAttribute("style",o.getAttribute("style")+t)}"string"==typeof this.options.style?o.setAttribute("style",o.getAttribute("style")+this.options.style):"object"==typeof this.options.style&&Object.assign(o.style,this.options.style);const l=document.createElement("div");l.setAttribute("style",`\n width: 1px;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: ${r?"flex-start":"flex-end"};\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n `);for(let e=0;e<t;e+=i){const t=l.cloneNode(),i=Math.round(100*e)/100%n==0,r=Math.round(100*e)/100%s==0;(i||r)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.formatTime(e),i&&(t.style.opacity="1")),o.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(o),this.emit("ready")}}const o=r;return e.default})()));
1
+ !function(t,e){"object"==typeof exports&&"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,11 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ type TimerEvents = {
3
+ tick: [];
4
+ };
5
+ declare class Timer extends EventEmitter<TimerEvents> {
6
+ private unsubscribe;
7
+ start(): void;
8
+ stop(): void;
9
+ destroy(): void;
10
+ }
11
+ export default Timer;
@@ -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;