wavesurfer.js 7.0.0-beta.9 → 7.0.1

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 (76) hide show
  1. package/README.md +56 -24
  2. package/dist/base-plugin.d.ts +6 -3
  3. package/dist/base-plugin.js +1 -0
  4. package/dist/decoder.js +17 -6
  5. package/dist/draggable.js +10 -7
  6. package/dist/fetcher.d.ts +1 -3
  7. package/dist/fetcher.js +13 -6
  8. package/dist/player.d.ts +2 -2
  9. package/dist/player.js +10 -8
  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 +34 -10
  17. package/dist/plugins/envelope.min.js +1 -0
  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 -0
  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 +118 -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 +7 -4
  40. package/dist/plugins/record.esm.js +1 -0
  41. package/dist/plugins/record.js +73 -66
  42. package/dist/plugins/record.min.js +1 -0
  43. package/dist/plugins/regions.cjs +1 -0
  44. package/dist/plugins/regions.d.ts +28 -4
  45. package/dist/plugins/regions.esm.js +1 -0
  46. package/dist/plugins/regions.js +74 -61
  47. package/dist/plugins/regions.min.js +1 -0
  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 -0
  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 -0
  59. package/dist/plugins/timer.d.ts +11 -0
  60. package/dist/plugins/wavesurfer.d.ts +156 -0
  61. package/dist/renderer.js +11 -7
  62. package/dist/wavesurfer.cjs +1 -0
  63. package/dist/wavesurfer.d.ts +5 -3
  64. package/dist/wavesurfer.esm.js +1 -0
  65. package/dist/wavesurfer.js +62 -41
  66. package/dist/wavesurfer.min.js +1 -0
  67. package/package.json +21 -22
  68. package/dist/plugins/envelope.min.cjs +0 -1
  69. package/dist/plugins/minimap.min.cjs +0 -1
  70. package/dist/plugins/record.min.cjs +0 -1
  71. package/dist/plugins/regions.min.cjs +0 -1
  72. package/dist/plugins/spectrogram-fft.d.ts +0 -9
  73. package/dist/plugins/spectrogram-fft.js +0 -150
  74. package/dist/plugins/spectrogram.min.cjs +0 -1
  75. package/dist/plugins/timeline.min.cjs +0 -1
  76. package/dist/wavesurfer.min.cjs +0 -1
@@ -6,16 +6,21 @@
6
6
  import BasePlugin from '../base-plugin.js';
7
7
  import { makeDraggable } from '../draggable.js';
8
8
  import EventEmitter from '../event-emitter.js';
9
- export class Region extends EventEmitter {
9
+ class SingleRegion extends EventEmitter {
10
10
  constructor(params, totalDuration) {
11
+ var _a, _b, _c, _d, _e, _f;
11
12
  super();
12
13
  this.totalDuration = totalDuration;
14
+ this.minLength = 0;
15
+ this.maxLength = Infinity;
13
16
  this.id = params.id || `region-${Math.random().toString(32).slice(2)}`;
14
17
  this.start = params.start;
15
- this.end = params.end ?? params.start;
16
- this.drag = params.drag ?? true;
17
- this.resize = params.resize ?? true;
18
- this.color = params.color ?? 'rgba(0, 0, 0, 0.1)';
18
+ this.end = (_a = params.end) !== null && _a !== void 0 ? _a : params.start;
19
+ this.drag = (_b = params.drag) !== null && _b !== void 0 ? _b : true;
20
+ this.resize = (_c = params.resize) !== null && _c !== void 0 ? _c : true;
21
+ this.color = (_d = params.color) !== null && _d !== void 0 ? _d : 'rgba(0, 0, 0, 0.1)';
22
+ this.minLength = (_e = params.minLength) !== null && _e !== void 0 ? _e : this.minLength;
23
+ this.maxLength = (_f = params.maxLength) !== null && _f !== void 0 ? _f : this.maxLength;
19
24
  this.element = this.initElement(params.content);
20
25
  this.renderPosition();
21
26
  this.initMouseEvents();
@@ -34,13 +39,12 @@ export class Region extends EventEmitter {
34
39
  transition: background-color 0.2s ease;
35
40
  cursor: ${this.drag ? 'grab' : 'default'};
36
41
  pointer-events: all;
37
- padding: 0.2em ${isMarker ? 0.2 : 0.4}em;
38
- pointer-events: all;
39
42
  `);
40
43
  // Init content
41
44
  if (content) {
42
45
  if (typeof content === 'string') {
43
46
  this.content = document.createElement('div');
47
+ this.content.style.padding = `0.2em ${isMarker ? 0.2 : 0.4}em`;
44
48
  this.content.textContent = content;
45
49
  }
46
50
  else {
@@ -81,9 +85,9 @@ export class Region extends EventEmitter {
81
85
  }
82
86
  renderPosition() {
83
87
  const start = this.start / this.totalDuration;
84
- const end = this.end / this.totalDuration;
88
+ const end = (this.totalDuration - this.end) / this.totalDuration;
85
89
  this.element.style.left = `${start * 100}%`;
86
- this.element.style.width = `${(end - start) * 100}%`;
90
+ this.element.style.right = `${end * 100}%`;
87
91
  }
88
92
  initMouseEvents() {
89
93
  const { element } = this;
@@ -111,31 +115,33 @@ export class Region extends EventEmitter {
111
115
  this.element.style.cursor = 'grab';
112
116
  this.emit('update-end');
113
117
  }
114
- onUpdate(dx, sides) {
118
+ _onUpdate(dx, side) {
115
119
  if (!this.element.parentElement)
116
120
  return;
117
121
  const deltaSeconds = (dx / this.element.parentElement.clientWidth) * this.totalDuration;
118
- sides.forEach((side) => {
119
- this[side] += deltaSeconds;
120
- if (side === 'start') {
121
- this.start = Math.max(0, Math.min(this.start, this.end));
122
- }
123
- else {
124
- this.end = Math.max(this.start, Math.min(this.end, this.totalDuration));
125
- }
126
- });
127
- this.renderPosition();
128
- this.emit('update');
122
+ const newStart = !side || side === 'start' ? this.start + deltaSeconds : this.start;
123
+ const newEnd = !side || side === 'end' ? this.end + deltaSeconds : this.end;
124
+ const length = newEnd - newStart;
125
+ if (newStart >= 0 &&
126
+ newEnd <= this.totalDuration &&
127
+ newStart <= newEnd &&
128
+ length >= this.minLength &&
129
+ length <= this.maxLength) {
130
+ this.start = newStart;
131
+ this.end = newEnd;
132
+ this.renderPosition();
133
+ this.emit('update');
134
+ }
129
135
  }
130
136
  onMove(dx) {
131
137
  if (!this.drag)
132
138
  return;
133
- this.onUpdate(dx, ['start', 'end']);
139
+ this._onUpdate(dx);
134
140
  }
135
141
  onResize(dx, side) {
136
142
  if (!this.resize)
137
143
  return;
138
- this.onUpdate(dx, [side]);
144
+ this._onUpdate(dx, side);
139
145
  }
140
146
  onEndResizing() {
141
147
  if (!this.resize)
@@ -152,6 +158,7 @@ export class Region extends EventEmitter {
152
158
  }
153
159
  /** Update the region's options */
154
160
  setOptions(options) {
161
+ var _a, _b;
155
162
  if (options.color) {
156
163
  this.color = options.color;
157
164
  this.element.style.backgroundColor = this.color;
@@ -168,8 +175,8 @@ export class Region extends EventEmitter {
168
175
  });
169
176
  }
170
177
  if (options.start !== undefined || options.end !== undefined) {
171
- this.start = options.start ?? this.start;
172
- this.end = options.end ?? this.end;
178
+ this.start = (_a = options.start) !== null && _a !== void 0 ? _a : this.start;
179
+ this.end = (_b = options.end) !== null && _b !== void 0 ? _b : this.end;
173
180
  this.renderPosition();
174
181
  }
175
182
  }
@@ -199,6 +206,19 @@ class RegionsPlugin extends BasePlugin {
199
206
  throw Error('WaveSurfer is not initialized');
200
207
  }
201
208
  this.wavesurfer.getWrapper().appendChild(this.regionsContainer);
209
+ // Detect when a region is being played
210
+ let activeRegion = null;
211
+ this.subscriptions.push(this.wavesurfer.on('timeupdate', (currentTime) => {
212
+ const playedRegion = this.regions.find((region) => region.start <= currentTime && region.end >= currentTime);
213
+ if (activeRegion && activeRegion !== playedRegion) {
214
+ this.emit('region-out', activeRegion);
215
+ activeRegion = null;
216
+ }
217
+ if (playedRegion && playedRegion !== activeRegion) {
218
+ activeRegion = playedRegion;
219
+ this.emit('region-in', playedRegion);
220
+ }
221
+ }));
202
222
  }
203
223
  initRegionsContainer() {
204
224
  const div = document.createElement('div');
@@ -233,7 +253,7 @@ class RegionsPlugin extends BasePlugin {
233
253
  const width = reg.element.scrollWidth;
234
254
  return labelLeft < left + width && left < labelLeft + labelWidth;
235
255
  })
236
- .map((reg) => reg.content?.getBoundingClientRect().height || 0)
256
+ .map((reg) => { var _a; return ((_a = reg.content) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().height) || 0; })
237
257
  .reduce((sum, val) => sum + val, 0);
238
258
  div.style.marginTop = `${overlap}px`;
239
259
  }
@@ -248,8 +268,9 @@ class RegionsPlugin extends BasePlugin {
248
268
  this.emit('region-updated', region);
249
269
  }),
250
270
  region.on('play', () => {
251
- this.wavesurfer?.play();
252
- this.wavesurfer?.setTime(region.start);
271
+ var _a, _b;
272
+ (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.play();
273
+ (_b = this.wavesurfer) === null || _b === void 0 ? void 0 : _b.setTime(region.start);
253
274
  }),
254
275
  region.on('click', (e) => {
255
276
  this.emit('region-clicked', region, e);
@@ -271,7 +292,7 @@ class RegionsPlugin extends BasePlugin {
271
292
  throw Error('WaveSurfer is not initialized');
272
293
  }
273
294
  const duration = this.wavesurfer.getDuration();
274
- const region = new Region(options, duration);
295
+ const region = new SingleRegion(options, duration);
275
296
  if (!duration) {
276
297
  this.subscriptions.push(this.wavesurfer.once('ready', (duration) => {
277
298
  region._setTotalDuration(duration);
@@ -288,52 +309,44 @@ class RegionsPlugin extends BasePlugin {
288
309
  * Returns a function to disable the drag selection.
289
310
  */
290
311
  enableDragSelection(options) {
291
- const wrapper = this.wavesurfer?.getWrapper()?.querySelector('div');
312
+ var _a, _b;
313
+ const wrapper = (_b = (_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getWrapper()) === null || _b === void 0 ? void 0 : _b.querySelector('div');
292
314
  if (!wrapper)
293
315
  return () => undefined;
316
+ const initialSize = 5;
294
317
  let region = null;
295
- let sumDx = 0;
318
+ let startX = 0;
296
319
  return makeDraggable(wrapper,
297
320
  // On drag move
298
- (dx, _, x) => {
299
- if (!this.wavesurfer)
300
- return;
301
- if (!region) {
302
- const duration = this.wavesurfer.getDuration();
303
- const box = wrapper.getBoundingClientRect();
304
- let start = (x / box.width) * duration;
305
- let end = ((x - box.left) / box.width) * duration;
306
- if (start > end)
307
- [start, end] = [end, start];
308
- region = new Region({
309
- ...options,
310
- start,
311
- end,
312
- }, duration);
313
- this.regionsContainer.appendChild(region.element);
314
- }
315
- sumDx += dx;
321
+ (dx, _dy, x) => {
316
322
  if (region) {
317
- const privateRegion = region;
318
- privateRegion.onUpdate(dx, [sumDx > 0 ? 'end' : 'start']);
323
+ // Update the end position of the region
324
+ // If we're dragging to the left, we need to update the start instead
325
+ region._onUpdate(dx, x > startX ? 'end' : 'start');
319
326
  }
320
327
  },
321
328
  // On drag start
322
- () => null,
329
+ (x) => {
330
+ startX = x;
331
+ if (!this.wavesurfer)
332
+ return;
333
+ const duration = this.wavesurfer.getDuration();
334
+ const width = this.wavesurfer.getWrapper().clientWidth;
335
+ // Calculate the start time of the region
336
+ const start = (x / width) * duration;
337
+ // Give the region a small initial size
338
+ const end = ((x + initialSize) / width) * duration;
339
+ // Create a region but don't save it until the drag ends
340
+ region = new SingleRegion(Object.assign(Object.assign({}, options), { start,
341
+ end }), duration);
342
+ // Just add it to the DOM for now
343
+ this.regionsContainer.appendChild(region.element);
344
+ },
323
345
  // On drag end
324
346
  () => {
325
347
  if (region) {
326
348
  this.saveRegion(region);
327
349
  region = null;
328
- sumDx = 0;
329
- // Prevent a click event on the waveform
330
- if (this.wavesurfer) {
331
- const { interact } = this.wavesurfer.options;
332
- if (interact) {
333
- this.wavesurfer.toggleInteraction(false);
334
- setTimeout(() => this.wavesurfer?.toggleInteraction(interact), 10);
335
- }
336
- }
337
350
  }
338
351
  });
339
352
  }
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):((t="undefined"!=typeof globalThis?globalThis:t||self).WaveSurfer=t.WaveSurfer||{},t.WaveSurfer.Regions=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()))}}function i(t,e,i,n,s=5){let r=()=>{};if(!t)return r;const o=o=>{if(2===o.button)return;o.preventDefault(),o.stopPropagation();let l=o.clientX,a=o.clientY,h=!1;const d=n=>{n.preventDefault(),n.stopPropagation();const r=n.clientX,o=n.clientY;if(h||Math.abs(r-l)>=s||Math.abs(o-a)>=s){const{left:n,top:s}=t.getBoundingClientRect();h||(h=!0,null==i||i(l-n,a-s)),e(r-l,o-a,r-n,o-s),l=r,a=o}},u=t=>{h&&(t.preventDefault(),t.stopPropagation())},c=()=>{h&&(null==n||n()),r()};document.addEventListener("pointermove",d),document.addEventListener("pointerup",c),document.addEventListener("pointerleave",c),document.addEventListener("click",u,!0),r=()=>{document.removeEventListener("pointermove",d),document.removeEventListener("pointerup",c),document.removeEventListener("pointerleave",c),setTimeout((()=>{document.removeEventListener("click",u,!0)}),10)}};return t.addEventListener("pointerdown",o),()=>{r(),t.removeEventListener("pointerdown",o)}}class n extends t{constructor(t,e){var i,n,s,r,o,l;super(),this.totalDuration=e,this.minLength=0,this.maxLength=1/0,this.id=t.id||`region-${Math.random().toString(32).slice(2)}`,this.start=t.start,this.end=null!==(i=t.end)&&void 0!==i?i:t.start,this.drag=null===(n=t.drag)||void 0===n||n,this.resize=null===(s=t.resize)||void 0===s||s,this.color=null!==(r=t.color)&&void 0!==r?r:"rgba(0, 0, 0, 0.1)",this.minLength=null!==(o=t.minLength)&&void 0!==o?o:this.minLength,this.maxLength=null!==(l=t.maxLength)&&void 0!==l?l:this.maxLength,this.element=this.initElement(t.content),this.renderPosition(),this.initMouseEvents()}initElement(t){const e=document.createElement("div"),i=this.start===this.end;if(e.setAttribute("part",`${i?"marker":"region"} ${this.id}`),e.setAttribute("style",`\n position: absolute;\n height: 100%;\n background-color: ${i?"none":this.color};\n border-left: ${i?"2px solid "+this.color:"none"};\n border-radius: 2px;\n box-sizing: border-box;\n transition: background-color 0.2s ease;\n cursor: ${this.drag?"grab":"default"};\n pointer-events: all;\n `),t&&("string"==typeof t?(this.content=document.createElement("div"),this.content.style.padding=`0.2em ${i?.2:.4}em`,this.content.textContent=t):this.content=t,this.content.setAttribute("part","region-content"),e.appendChild(this.content)),!i){const t=document.createElement("div");t.setAttribute("data-resize","left"),t.setAttribute("style",`\n position: absolute;\n z-index: 2;\n width: 6px;\n height: 100%;\n top: 0;\n left: 0;\n border-left: 2px solid rgba(0, 0, 0, 0.5);\n border-radius: 2px 0 0 2px;\n cursor: ${this.resize?"ew-resize":"default"};\n word-break: keep-all;\n `),t.setAttribute("part","region-handle region-handle-left");const i=t.cloneNode();i.setAttribute("data-resize","right"),i.style.left="",i.style.right="0",i.style.borderRight=i.style.borderLeft,i.style.borderLeft="",i.style.borderRadius="0 2px 2px 0",i.setAttribute("part","region-handle region-handle-right"),e.appendChild(t),e.appendChild(i)}return e}renderPosition(){const t=this.start/this.totalDuration,e=(this.totalDuration-this.end)/this.totalDuration;this.element.style.left=100*t+"%",this.element.style.right=100*e+"%"}initMouseEvents(){const{element:t}=this;if(!t)return;t.addEventListener("click",(t=>this.emit("click",t))),t.addEventListener("mouseenter",(t=>this.emit("over",t))),t.addEventListener("mouseleave",(t=>this.emit("leave",t))),t.addEventListener("dblclick",(t=>this.emit("dblclick",t))),i(t,(t=>this.onMove(t)),(()=>this.onStartMoving()),(()=>this.onEndMoving()));i(t.querySelector('[data-resize="left"]'),(t=>this.onResize(t,"start")),(()=>null),(()=>this.onEndResizing()),1),i(t.querySelector('[data-resize="right"]'),(t=>this.onResize(t,"end")),(()=>null),(()=>this.onEndResizing()),1)}onStartMoving(){this.drag&&(this.element.style.cursor="grabbing")}onEndMoving(){this.drag&&(this.element.style.cursor="grab",this.emit("update-end"))}_onUpdate(t,e){if(!this.element.parentElement)return;const i=t/this.element.parentElement.clientWidth*this.totalDuration,n=e&&"start"!==e?this.start:this.start+i,s=e&&"end"!==e?this.end:this.end+i,r=s-n;n>=0&&s<=this.totalDuration&&n<=s&&r>=this.minLength&&r<=this.maxLength&&(this.start=n,this.end=s,this.renderPosition(),this.emit("update"))}onMove(t){this.drag&&this._onUpdate(t)}onResize(t,e){this.resize&&this._onUpdate(t,e)}onEndResizing(){this.resize&&this.emit("update-end")}_setTotalDuration(t){this.totalDuration=t,this.renderPosition()}play(){this.emit("play")}setOptions(t){var e,i;t.color&&(this.color=t.color,this.element.style.backgroundColor=this.color),void 0!==t.drag&&(this.drag=t.drag,this.element.style.cursor=this.drag?"grab":"default"),void 0!==t.resize&&(this.resize=t.resize,this.element.querySelectorAll("[data-resize]").forEach((t=>{t.style.cursor=this.resize?"ew-resize":"default"}))),void 0===t.start&&void 0===t.end||(this.start=null!==(e=t.start)&&void 0!==e?e:this.start,this.end=null!==(i=t.end)&&void 0!==i?i:this.end,this.renderPosition())}remove(){this.emit("remove"),this.element.remove(),this.element=null}}class s extends e{constructor(t){super(t),this.regions=[],this.regionsContainer=this.initRegionsContainer()}static create(t){return new s(t)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.wavesurfer.getWrapper().appendChild(this.regionsContainer);let t=null;this.subscriptions.push(this.wavesurfer.on("timeupdate",(e=>{const i=this.regions.find((t=>t.start<=e&&t.end>=e));t&&t!==i&&(this.emit("region-out",t),t=null),i&&i!==t&&(t=i,this.emit("region-in",i))})))}initRegionsContainer(){const t=document.createElement("div");return t.setAttribute("style","\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n pointer-events: none;\n "),t}getRegions(){return this.regions}avoidOverlapping(t){if(!t.content)return;const e=t.content,i=e.getBoundingClientRect().left,n=t.element.scrollWidth,s=this.regions.filter((e=>{if(e===t||!e.content)return!1;const s=e.content.getBoundingClientRect().left,r=e.element.scrollWidth;return i<s+r&&s<i+n})).map((t=>{var e;return(null===(e=t.content)||void 0===e?void 0:e.getBoundingClientRect().height)||0})).reduce(((t,e)=>t+e),0);e.style.marginTop=`${s}px`}saveRegion(t){this.regionsContainer.appendChild(t.element),this.avoidOverlapping(t),this.regions.push(t),this.emit("region-created",t);const e=[t.on("update-end",(()=>{this.avoidOverlapping(t),this.emit("region-updated",t)})),t.on("play",(()=>{var e,i;null===(e=this.wavesurfer)||void 0===e||e.play(),null===(i=this.wavesurfer)||void 0===i||i.setTime(t.start)})),t.on("click",(e=>{this.emit("region-clicked",t,e)})),t.on("dblclick",(e=>{this.emit("region-double-clicked",t,e)})),t.once("remove",(()=>{e.forEach((t=>t())),this.regions=this.regions.filter((e=>e!==t))}))];this.subscriptions.push(...e)}addRegion(t){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const e=this.wavesurfer.getDuration(),i=new n(t,e);return e?this.saveRegion(i):this.subscriptions.push(this.wavesurfer.once("ready",(t=>{i._setTotalDuration(t),this.saveRegion(i)}))),i}enableDragSelection(t){var e,s;const r=null===(s=null===(e=this.wavesurfer)||void 0===e?void 0:e.getWrapper())||void 0===s?void 0:s.querySelector("div");if(!r)return()=>{};let o=null,l=0;return i(r,((t,e,i)=>{o&&o._onUpdate(t,i>l?"end":"start")}),(e=>{if(l=e,!this.wavesurfer)return;const i=this.wavesurfer.getDuration(),s=this.wavesurfer.getWrapper().clientWidth,r=e/s*i,a=(e+5)/s*i;o=new n(Object.assign(Object.assign({},t),{start:r,end:a}),i),this.regionsContainer.appendChild(o.element)}),(()=>{o&&(this.saveRegion(o),o=null)}))}clearRegions(){this.regions.forEach((t=>t.remove()))}destroy(){this.clearRegions(),super.destroy()}}return s}));
@@ -0,0 +1,44 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ import type { WaveSurferOptions } from './wavesurfer.js';
3
+ type RendererEvents = {
4
+ click: [relativeX: number];
5
+ drag: [relativeX: number];
6
+ scroll: [relativeStart: number, relativeEnd: number];
7
+ render: [];
8
+ };
9
+ declare class Renderer extends EventEmitter<RendererEvents> {
10
+ private static MAX_CANVAS_WIDTH;
11
+ private options;
12
+ private parent;
13
+ private container;
14
+ private scrollContainer;
15
+ private wrapper;
16
+ private canvasWrapper;
17
+ private progressWrapper;
18
+ private cursor;
19
+ private timeouts;
20
+ private isScrolling;
21
+ private audioData;
22
+ private resizeObserver;
23
+ private isDragging;
24
+ constructor(options: WaveSurferOptions);
25
+ private initEvents;
26
+ private initDrag;
27
+ private getHeight;
28
+ private initHtml;
29
+ setOptions(options: WaveSurferOptions): void;
30
+ getWrapper(): HTMLElement;
31
+ getScroll(): number;
32
+ destroy(): void;
33
+ private createDelay;
34
+ private convertColorValues;
35
+ private renderBars;
36
+ private renderSingleCanvas;
37
+ private renderWaveform;
38
+ render(audioData: AudioBuffer): void;
39
+ reRender(): void;
40
+ zoom(minPxPerSec: number): void;
41
+ private scrollIntoView;
42
+ renderProgress(progress: number, isPlaying?: boolean): void;
43
+ }
44
+ export default Renderer;
@@ -0,0 +1 @@
1
+ "use strict";class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const s=this.on(t,e),i=this.on(t,(()=>{s(),i()}));return s}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}function s(t,e,s,i){switch(this.bufferSize=t,this.sampleRate=e,this.bandwidth=2/t*(e/2),this.sinTable=new Float32Array(t),this.cosTable=new Float32Array(t),this.windowValues=new Float32Array(t),this.reverseTable=new Uint32Array(t),this.peakBand=0,this.peak=0,s){case"bartlett":for(a=0;a<t;a++)this.windowValues[a]=2/(t-1)*((t-1)/2-Math.abs(a-(t-1)/2));break;case"bartlettHann":for(a=0;a<t;a++)this.windowValues[a]=.62-.48*Math.abs(a/(t-1)-.5)-.38*Math.cos(2*Math.PI*a/(t-1));break;case"blackman":for(i=i||.16,a=0;a<t;a++)this.windowValues[a]=(1-i)/2-.5*Math.cos(2*Math.PI*a/(t-1))+i/2*Math.cos(4*Math.PI*a/(t-1));break;case"cosine":for(a=0;a<t;a++)this.windowValues[a]=Math.cos(Math.PI*a/(t-1)-Math.PI/2);break;case"gauss":for(i=i||.25,a=0;a<t;a++)this.windowValues[a]=Math.pow(Math.E,-.5*Math.pow((a-(t-1)/2)/(i*(t-1)/2),2));break;case"hamming":for(a=0;a<t;a++)this.windowValues[a]=.54-.46*Math.cos(2*Math.PI*a/(t-1));break;case"hann":case void 0:for(a=0;a<t;a++)this.windowValues[a]=.5*(1-Math.cos(2*Math.PI*a/(t-1)));break;case"lanczoz":for(a=0;a<t;a++)this.windowValues[a]=Math.sin(Math.PI*(2*a/(t-1)-1))/(Math.PI*(2*a/(t-1)-1));break;case"rectangular":for(a=0;a<t;a++)this.windowValues[a]=1;break;case"triangular":for(a=0;a<t;a++)this.windowValues[a]=2/t*(t/2-Math.abs(a-(t-1)/2));break;default:throw Error("No such window function '"+s+"'")}for(var a,r=1,h=t>>1;r<t;){for(a=0;a<r;a++)this.reverseTable[a+r]=this.reverseTable[a]+h;r<<=1,h>>=1}for(a=0;a<t;a++)this.sinTable[a]=Math.sin(-Math.PI/a),this.cosTable[a]=Math.cos(-Math.PI/a);this.calculateSpectrum=function(t){var e,s,i,a=this.bufferSize,r=this.cosTable,h=this.sinTable,n=this.reverseTable,l=new Float32Array(a),o=new Float32Array(a),c=2/this.bufferSize,f=Math.sqrt,p=new Float32Array(a/2),u=Math.floor(Math.log(a)/Math.LN2);if(Math.pow(2,u)!==a)throw"Invalid buffer size, must be a power of 2.";if(a!==t.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+a+" Buffer Size: "+t.length;for(var d,w,M,b,g,m,y,v,x=1,k=0;k<a;k++)l[k]=t[n[k]]*this.windowValues[n[k]],o[k]=0;for(;x<a;){d=r[x],w=h[x],M=1,b=0;for(var q=0;q<x;q++){for(k=q;k<a;)m=M*l[g=k+x]-b*o[g],y=M*o[g]+b*l[g],l[g]=l[k]-m,o[g]=o[k]-y,l[k]+=m,o[k]+=y,k+=x<<1;M=(v=M)*d-b*w,b=v*w+b*d}x<<=1}k=0;for(var S=a/2;k<S;k++)(i=c*f((e=l[k])*e+(s=o[k])*s))>this.peak&&(this.peakBand=k,this.peak=i),p[k]=i;return p}}class i extends e{static create(t){return new i(t||{})}constructor(t){if(super(t),this.utils={style:(t,e)=>Object.assign(t.style,e)},this.drawSpectrogram=t=>{isNaN(t[0][0])||(t=[t]);const e=this.spectrCc,s=this.height,i=this.width,a=this.buffer.sampleRate/2,r=this.frequencyMin,h=this.frequencyMax;if(e){for(let n=0;n<t.length;n++){const l=this.resample(t[n]),o=new ImageData(i,s);for(let t=0;t<l.length;t++)for(let e=0;e<l[t].length;e++){const a=this.colorMap[l[t][e]],r=4*((s-e)*i+t);o.data[r]=255*a[0],o.data[r+1]=255*a[1],o.data[r+2]=255*a[2],o.data[r+3]=255*a[3]}createImageBitmap(o).then((t=>{e.drawImage(t,0,s*(1-h/a),i,s*(h-r)/a,0,s*n,i,s)}))}this.emit("ready")}},this.frequenciesDataUrl=t.frequenciesDataUrl,this.container="string"==typeof t.container?document.querySelector(t.container):t.container,t.colorMap){if(t.colorMap.length<256)throw new Error("Colormap must contain 256 elements");for(let e=0;e<t.colorMap.length;e++){if(4!==t.colorMap[e].length)throw new Error("ColorMap entries must contain 4 values")}this.colorMap=t.colorMap}else{this.colorMap=[];for(let t=0;t<256;t++){const e=(255-t)/256;this.colorMap.push([e,e,e,1])}}this.fftSamples=t.fftSamples||512,this.height=t.height||this.fftSamples/2,this.noverlap=t.noverlap,this.windowFunc=t.windowFunc,this.alpha=t.alpha,this.channels=1,this.frequencyMin=t.frequencyMin||0,this.frequencyMax=t.frequencyMax||0,this.createWrapper(),this.createCanvas()}onInit(){this.container=this.container||this.wavesurfer.getWrapper(),this.container.appendChild(this.wrapper),this.wavesurfer.options.fillParent&&this.utils.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"}),this.width=this.wavesurfer.getWrapper().offsetWidth,this.subscriptions.push(this.wavesurfer.on("redraw",(()=>this.render())))}destroy(){this.unAll(),this.wavesurfer.un("ready",this._onReady),this.wavesurfer.un("redraw",this._onRender),this.wavesurfer=null,this.util=null,this.options=null,this.wrapper&&(this.wrapper.remove(),this.wrapper=null),super.destroy()}createWrapper(){if(this.wrapper=document.createElement("div"),this.utils.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height*this.channels+"px"}),this.options.labels){const t=document.createElement("canvas");t.setAttribute("part","spec-labels"),t.classList.add("spec-labels"),this.utils.style(t,{position:"absolute",zIndex:9,width:"55px",height:"100%"}),this.wrapper.appendChild(t),this.labelsEl=t}this.wrapper.addEventListener("click",this._onWrapperClick)}_wrapperClickHandler(t){t.preventDefault();const e="offsetX"in t?t.offsetX:t.layerX;this.emit("click",e/this.width||0)}createCanvas(){const t=document.createElement("canvas");this.wrapper.appendChild(t),this.spectrCc=t.getContext("2d"),this.utils.style(t,{position:"absolute",left:0,top:0,width:"100%",height:"100%",zIndex:4}),this.canvas=t}render(){this.canvas.width=this.width,this.canvas.height=this.height,this.frequenciesDataUrl?this.loadFrequenciesData(this.frequenciesDataUrl):this.getFrequencies(this.drawSpectrogram),this.loadLabels(this.options.labelsBackground,"12px","12px","",this.options.labelsColor,this.options.labelsHzColor||this.options.labelsColor,"center","#specLabels")}getFrequencies(t){const e=this.fftSamples,i=this.wavesurfer.getDecodedData(),a=this.channels;if(this.frequencyMax=this.frequencyMax||i.sampleRate/2,!i)return;this.buffer=i;const r=i.sampleRate,h=[];let n=this.noverlap;if(!n){const t=i.length/this.canvas.width;n=Math.max(0,Math.round(e-t))}const l=new s(e,r,this.windowFunc,this.alpha);for(let t=0;t<a;t++){const s=i.getChannelData(t),a=[];let r=0;for(;r+e<s.length;){const t=s.slice(r,r+e),i=l.calculateSpectrum(t),h=new Uint8Array(e/2);let o;for(o=0;o<e/2;o++)h[o]=Math.max(-255,45*Math.log10(i[o]));a.push(h),r+=e-n}h.push(a)}t(h,this)}loadFrequenciesData(t){return fetch(t).then((t=>t.json())).then(this.drawSpectrogram)}freqType(t){return t>=1e3?(t/1e3).toFixed(1):Math.round(t)}unitType(t){return t>=1e3?"KHz":"Hz"}loadLabels(t,e,s,i,a,r,h,n){t=t||"rgba(68,68,68,0)",e=e||"12px",s=s||"12px",i=i||"Helvetica",a=a||"#fff",r=r||"#fff",h=h||"center";const l=this.height||512,o=l/256*5,c=this.frequencyMin,f=(this.frequencyMax-c)/o,p=this.labelsEl.getContext("2d"),u=window.devicePixelRatio;if(this.labelsEl.height=this.height*this.channels*u,this.labelsEl.width=55*u,p.scale(u,u),p)for(let n=0;n<this.channels;n++){let u;for(p.fillStyle=t,p.fillRect(0,n*l,55,(1+n)*l),p.fill(),u=0;u<=o;u++){p.textAlign=h,p.textBaseline="middle";const t=c+f*u,o=this.freqType(t),d=this.unitType(t),w=16;let M;M=0==u?(1+n)*l+u-10:(1+n)*l-50*u+2,p.fillStyle=r,p.font=s+" "+i,p.fillText(d,w+24,M),p.fillStyle=a,p.font=e+" "+i,p.fillText(o,w,M)}}}resample(t){const e=this.width,s=[],i=1/t.length,a=1/e;let r;for(r=0;r<e;r++){const e=new Array(t[0].length);let h;for(h=0;h<t.length;h++){const s=h*i,n=s+i,l=r*a,o=l+a,c=n<=l||o<=s?0:Math.min(Math.max(n,l),Math.max(o,s))-Math.max(Math.min(n,l),Math.min(o,s));let f;if(c>0)for(f=0;f<t[0].length;f++)null==e[f]&&(e[f]=0),e[f]+=c/a*t[h][f]}const n=new Uint8Array(t[0].length);let l;for(l=0;l<t[0].length;l++)n[l]=e[l];s.push(n)}return s}}module.exports=i;
@@ -17,7 +17,10 @@
17
17
  * ]
18
18
  * });
19
19
  */
20
- import BasePlugin from '../base-plugin.js';
20
+ /**
21
+ * Spectrogram plugin for wavesurfer.
22
+ */
23
+ import BasePlugin, { type BasePluginEvents } from '../base-plugin.js';
21
24
  export type SpectrogramPluginOptions = {
22
25
  /** Selector of element or element in which to render */
23
26
  container: string | HTMLElement;
@@ -27,6 +30,9 @@ export type SpectrogramPluginOptions = {
27
30
  height?: number;
28
31
  /** Set to true to display frequency labels. */
29
32
  labels?: boolean;
33
+ labelsBackground?: string;
34
+ labelsColor?: string;
35
+ labelsHzColor?: string;
30
36
  /** Size of the overlapping window. Must be < fftSamples. Auto deduced from canvas size by default. */
31
37
  noverlap?: number;
32
38
  /** The window function to be used. */
@@ -43,11 +49,11 @@ export type SpectrogramPluginOptions = {
43
49
  */
44
50
  colorMap?: number[][];
45
51
  };
46
- export type SpectrogramPluginEvents = {
52
+ export type SpectrogramPluginEvents = BasePluginEvents & {
47
53
  ready: [];
48
54
  click: [relativeX: number];
49
55
  };
50
- export default class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramPluginOptions> {
56
+ declare class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvents, SpectrogramPluginOptions> {
51
57
  static create(options?: SpectrogramPluginOptions): SpectrogramPlugin;
52
58
  utils: {
53
59
  style: (el: HTMLElement, styles: Record<string, string>) => CSSStyleDeclaration & Record<string, string>;
@@ -67,3 +73,4 @@ export default class SpectrogramPlugin extends BasePlugin<SpectrogramPluginEvent
67
73
  loadLabels(bgFill: any, fontSizeFreq: any, fontSizeUnit: any, fontType: any, textColorFreq: any, textColorUnit: any, textAlign: any, container: any): void;
68
74
  resample(oldMatrix: any): Uint8Array[];
69
75
  }
76
+ export default SpectrogramPlugin;
@@ -0,0 +1 @@
1
+ class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const s=this.on(t,e),i=this.on(t,(()=>{s(),i()}));return s}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}function s(t,e,s,i){switch(this.bufferSize=t,this.sampleRate=e,this.bandwidth=2/t*(e/2),this.sinTable=new Float32Array(t),this.cosTable=new Float32Array(t),this.windowValues=new Float32Array(t),this.reverseTable=new Uint32Array(t),this.peakBand=0,this.peak=0,s){case"bartlett":for(a=0;a<t;a++)this.windowValues[a]=2/(t-1)*((t-1)/2-Math.abs(a-(t-1)/2));break;case"bartlettHann":for(a=0;a<t;a++)this.windowValues[a]=.62-.48*Math.abs(a/(t-1)-.5)-.38*Math.cos(2*Math.PI*a/(t-1));break;case"blackman":for(i=i||.16,a=0;a<t;a++)this.windowValues[a]=(1-i)/2-.5*Math.cos(2*Math.PI*a/(t-1))+i/2*Math.cos(4*Math.PI*a/(t-1));break;case"cosine":for(a=0;a<t;a++)this.windowValues[a]=Math.cos(Math.PI*a/(t-1)-Math.PI/2);break;case"gauss":for(i=i||.25,a=0;a<t;a++)this.windowValues[a]=Math.pow(Math.E,-.5*Math.pow((a-(t-1)/2)/(i*(t-1)/2),2));break;case"hamming":for(a=0;a<t;a++)this.windowValues[a]=.54-.46*Math.cos(2*Math.PI*a/(t-1));break;case"hann":case void 0:for(a=0;a<t;a++)this.windowValues[a]=.5*(1-Math.cos(2*Math.PI*a/(t-1)));break;case"lanczoz":for(a=0;a<t;a++)this.windowValues[a]=Math.sin(Math.PI*(2*a/(t-1)-1))/(Math.PI*(2*a/(t-1)-1));break;case"rectangular":for(a=0;a<t;a++)this.windowValues[a]=1;break;case"triangular":for(a=0;a<t;a++)this.windowValues[a]=2/t*(t/2-Math.abs(a-(t-1)/2));break;default:throw Error("No such window function '"+s+"'")}for(var a,r=1,h=t>>1;r<t;){for(a=0;a<r;a++)this.reverseTable[a+r]=this.reverseTable[a]+h;r<<=1,h>>=1}for(a=0;a<t;a++)this.sinTable[a]=Math.sin(-Math.PI/a),this.cosTable[a]=Math.cos(-Math.PI/a);this.calculateSpectrum=function(t){var e,s,i,a=this.bufferSize,r=this.cosTable,h=this.sinTable,n=this.reverseTable,l=new Float32Array(a),o=new Float32Array(a),c=2/this.bufferSize,f=Math.sqrt,p=new Float32Array(a/2),u=Math.floor(Math.log(a)/Math.LN2);if(Math.pow(2,u)!==a)throw"Invalid buffer size, must be a power of 2.";if(a!==t.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+a+" Buffer Size: "+t.length;for(var d,w,M,b,g,m,y,v,x=1,k=0;k<a;k++)l[k]=t[n[k]]*this.windowValues[n[k]],o[k]=0;for(;x<a;){d=r[x],w=h[x],M=1,b=0;for(var q=0;q<x;q++){for(k=q;k<a;)m=M*l[g=k+x]-b*o[g],y=M*o[g]+b*l[g],l[g]=l[k]-m,o[g]=o[k]-y,l[k]+=m,o[k]+=y,k+=x<<1;M=(v=M)*d-b*w,b=v*w+b*d}x<<=1}k=0;for(var S=a/2;k<S;k++)(i=c*f((e=l[k])*e+(s=o[k])*s))>this.peak&&(this.peakBand=k,this.peak=i),p[k]=i;return p}}class i extends e{static create(t){return new i(t||{})}constructor(t){if(super(t),this.utils={style:(t,e)=>Object.assign(t.style,e)},this.drawSpectrogram=t=>{isNaN(t[0][0])||(t=[t]);const e=this.spectrCc,s=this.height,i=this.width,a=this.buffer.sampleRate/2,r=this.frequencyMin,h=this.frequencyMax;if(e){for(let n=0;n<t.length;n++){const l=this.resample(t[n]),o=new ImageData(i,s);for(let t=0;t<l.length;t++)for(let e=0;e<l[t].length;e++){const a=this.colorMap[l[t][e]],r=4*((s-e)*i+t);o.data[r]=255*a[0],o.data[r+1]=255*a[1],o.data[r+2]=255*a[2],o.data[r+3]=255*a[3]}createImageBitmap(o).then((t=>{e.drawImage(t,0,s*(1-h/a),i,s*(h-r)/a,0,s*n,i,s)}))}this.emit("ready")}},this.frequenciesDataUrl=t.frequenciesDataUrl,this.container="string"==typeof t.container?document.querySelector(t.container):t.container,t.colorMap){if(t.colorMap.length<256)throw new Error("Colormap must contain 256 elements");for(let e=0;e<t.colorMap.length;e++){if(4!==t.colorMap[e].length)throw new Error("ColorMap entries must contain 4 values")}this.colorMap=t.colorMap}else{this.colorMap=[];for(let t=0;t<256;t++){const e=(255-t)/256;this.colorMap.push([e,e,e,1])}}this.fftSamples=t.fftSamples||512,this.height=t.height||this.fftSamples/2,this.noverlap=t.noverlap,this.windowFunc=t.windowFunc,this.alpha=t.alpha,this.channels=1,this.frequencyMin=t.frequencyMin||0,this.frequencyMax=t.frequencyMax||0,this.createWrapper(),this.createCanvas()}onInit(){this.container=this.container||this.wavesurfer.getWrapper(),this.container.appendChild(this.wrapper),this.wavesurfer.options.fillParent&&this.utils.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"}),this.width=this.wavesurfer.getWrapper().offsetWidth,this.subscriptions.push(this.wavesurfer.on("redraw",(()=>this.render())))}destroy(){this.unAll(),this.wavesurfer.un("ready",this._onReady),this.wavesurfer.un("redraw",this._onRender),this.wavesurfer=null,this.util=null,this.options=null,this.wrapper&&(this.wrapper.remove(),this.wrapper=null),super.destroy()}createWrapper(){if(this.wrapper=document.createElement("div"),this.utils.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height*this.channels+"px"}),this.options.labels){const t=document.createElement("canvas");t.setAttribute("part","spec-labels"),t.classList.add("spec-labels"),this.utils.style(t,{position:"absolute",zIndex:9,width:"55px",height:"100%"}),this.wrapper.appendChild(t),this.labelsEl=t}this.wrapper.addEventListener("click",this._onWrapperClick)}_wrapperClickHandler(t){t.preventDefault();const e="offsetX"in t?t.offsetX:t.layerX;this.emit("click",e/this.width||0)}createCanvas(){const t=document.createElement("canvas");this.wrapper.appendChild(t),this.spectrCc=t.getContext("2d"),this.utils.style(t,{position:"absolute",left:0,top:0,width:"100%",height:"100%",zIndex:4}),this.canvas=t}render(){this.canvas.width=this.width,this.canvas.height=this.height,this.frequenciesDataUrl?this.loadFrequenciesData(this.frequenciesDataUrl):this.getFrequencies(this.drawSpectrogram),this.loadLabels(this.options.labelsBackground,"12px","12px","",this.options.labelsColor,this.options.labelsHzColor||this.options.labelsColor,"center","#specLabels")}getFrequencies(t){const e=this.fftSamples,i=this.wavesurfer.getDecodedData(),a=this.channels;if(this.frequencyMax=this.frequencyMax||i.sampleRate/2,!i)return;this.buffer=i;const r=i.sampleRate,h=[];let n=this.noverlap;if(!n){const t=i.length/this.canvas.width;n=Math.max(0,Math.round(e-t))}const l=new s(e,r,this.windowFunc,this.alpha);for(let t=0;t<a;t++){const s=i.getChannelData(t),a=[];let r=0;for(;r+e<s.length;){const t=s.slice(r,r+e),i=l.calculateSpectrum(t),h=new Uint8Array(e/2);let o;for(o=0;o<e/2;o++)h[o]=Math.max(-255,45*Math.log10(i[o]));a.push(h),r+=e-n}h.push(a)}t(h,this)}loadFrequenciesData(t){return fetch(t).then((t=>t.json())).then(this.drawSpectrogram)}freqType(t){return t>=1e3?(t/1e3).toFixed(1):Math.round(t)}unitType(t){return t>=1e3?"KHz":"Hz"}loadLabels(t,e,s,i,a,r,h,n){t=t||"rgba(68,68,68,0)",e=e||"12px",s=s||"12px",i=i||"Helvetica",a=a||"#fff",r=r||"#fff",h=h||"center";const l=this.height||512,o=l/256*5,c=this.frequencyMin,f=(this.frequencyMax-c)/o,p=this.labelsEl.getContext("2d"),u=window.devicePixelRatio;if(this.labelsEl.height=this.height*this.channels*u,this.labelsEl.width=55*u,p.scale(u,u),p)for(let n=0;n<this.channels;n++){let u;for(p.fillStyle=t,p.fillRect(0,n*l,55,(1+n)*l),p.fill(),u=0;u<=o;u++){p.textAlign=h,p.textBaseline="middle";const t=c+f*u,o=this.freqType(t),d=this.unitType(t),w=16;let M;M=0==u?(1+n)*l+u-10:(1+n)*l-50*u+2,p.fillStyle=r,p.font=s+" "+i,p.fillText(d,w+24,M),p.fillStyle=a,p.font=e+" "+i,p.fillText(o,w,M)}}}resample(t){const e=this.width,s=[],i=1/t.length,a=1/e;let r;for(r=0;r<e;r++){const e=new Array(t[0].length);let h;for(h=0;h<t.length;h++){const s=h*i,n=s+i,l=r*a,o=l+a,c=n<=l||o<=s?0:Math.min(Math.max(n,l),Math.max(o,s))-Math.max(Math.min(n,l),Math.min(o,s));let f;if(c>0)for(f=0;f<t[0].length;f++)null==e[f]&&(e[f]=0),e[f]+=c/a*t[h][f]}const n=new Uint8Array(t[0].length);let l;for(l=0;l<t[0].length;l++)n[l]=e[l];s.push(n)}return s}}export{i as default};
@@ -18,9 +18,160 @@
18
18
  * });
19
19
  */
20
20
  // @ts-nocheck
21
+ /**
22
+ * Calculate FFT - Based on https://github.com/corbanbrook/dsp.js
23
+ *
24
+ * @param {Number} bufferSize Buffer size
25
+ * @param {Number} sampleRate Sample rate
26
+ * @param {Function} windowFunc Window function
27
+ * @param {Number} alpha Alpha channel
28
+ */
29
+ function FFT(bufferSize, sampleRate, windowFunc, alpha) {
30
+ this.bufferSize = bufferSize;
31
+ this.sampleRate = sampleRate;
32
+ this.bandwidth = (2 / bufferSize) * (sampleRate / 2);
33
+ this.sinTable = new Float32Array(bufferSize);
34
+ this.cosTable = new Float32Array(bufferSize);
35
+ this.windowValues = new Float32Array(bufferSize);
36
+ this.reverseTable = new Uint32Array(bufferSize);
37
+ this.peakBand = 0;
38
+ this.peak = 0;
39
+ var i;
40
+ switch (windowFunc) {
41
+ case 'bartlett':
42
+ for (i = 0; i < bufferSize; i++) {
43
+ this.windowValues[i] = (2 / (bufferSize - 1)) * ((bufferSize - 1) / 2 - Math.abs(i - (bufferSize - 1) / 2));
44
+ }
45
+ break;
46
+ case 'bartlettHann':
47
+ for (i = 0; i < bufferSize; i++) {
48
+ this.windowValues[i] =
49
+ 0.62 - 0.48 * Math.abs(i / (bufferSize - 1) - 0.5) - 0.38 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1));
50
+ }
51
+ break;
52
+ case 'blackman':
53
+ alpha = alpha || 0.16;
54
+ for (i = 0; i < bufferSize; i++) {
55
+ this.windowValues[i] =
56
+ (1 - alpha) / 2 -
57
+ 0.5 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1)) +
58
+ (alpha / 2) * Math.cos((4 * Math.PI * i) / (bufferSize - 1));
59
+ }
60
+ break;
61
+ case 'cosine':
62
+ for (i = 0; i < bufferSize; i++) {
63
+ this.windowValues[i] = Math.cos((Math.PI * i) / (bufferSize - 1) - Math.PI / 2);
64
+ }
65
+ break;
66
+ case 'gauss':
67
+ alpha = alpha || 0.25;
68
+ for (i = 0; i < bufferSize; i++) {
69
+ this.windowValues[i] = Math.pow(Math.E, -0.5 * Math.pow((i - (bufferSize - 1) / 2) / ((alpha * (bufferSize - 1)) / 2), 2));
70
+ }
71
+ break;
72
+ case 'hamming':
73
+ for (i = 0; i < bufferSize; i++) {
74
+ this.windowValues[i] = 0.54 - 0.46 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1));
75
+ }
76
+ break;
77
+ case 'hann':
78
+ case undefined:
79
+ for (i = 0; i < bufferSize; i++) {
80
+ this.windowValues[i] = 0.5 * (1 - Math.cos((Math.PI * 2 * i) / (bufferSize - 1)));
81
+ }
82
+ break;
83
+ case 'lanczoz':
84
+ for (i = 0; i < bufferSize; i++) {
85
+ this.windowValues[i] =
86
+ Math.sin(Math.PI * ((2 * i) / (bufferSize - 1) - 1)) / (Math.PI * ((2 * i) / (bufferSize - 1) - 1));
87
+ }
88
+ break;
89
+ case 'rectangular':
90
+ for (i = 0; i < bufferSize; i++) {
91
+ this.windowValues[i] = 1;
92
+ }
93
+ break;
94
+ case 'triangular':
95
+ for (i = 0; i < bufferSize; i++) {
96
+ this.windowValues[i] = (2 / bufferSize) * (bufferSize / 2 - Math.abs(i - (bufferSize - 1) / 2));
97
+ }
98
+ break;
99
+ default:
100
+ throw Error("No such window function '" + windowFunc + "'");
101
+ }
102
+ var limit = 1;
103
+ var bit = bufferSize >> 1;
104
+ var i;
105
+ while (limit < bufferSize) {
106
+ for (i = 0; i < limit; i++) {
107
+ this.reverseTable[i + limit] = this.reverseTable[i] + bit;
108
+ }
109
+ limit = limit << 1;
110
+ bit = bit >> 1;
111
+ }
112
+ for (i = 0; i < bufferSize; i++) {
113
+ this.sinTable[i] = Math.sin(-Math.PI / i);
114
+ this.cosTable[i] = Math.cos(-Math.PI / i);
115
+ }
116
+ this.calculateSpectrum = function (buffer) {
117
+ // Locally scope variables for speed up
118
+ var bufferSize = this.bufferSize, cosTable = this.cosTable, sinTable = this.sinTable, reverseTable = this.reverseTable, real = new Float32Array(bufferSize), imag = new Float32Array(bufferSize), bSi = 2 / this.bufferSize, sqrt = Math.sqrt, rval, ival, mag, spectrum = new Float32Array(bufferSize / 2);
119
+ var k = Math.floor(Math.log(bufferSize) / Math.LN2);
120
+ if (Math.pow(2, k) !== bufferSize) {
121
+ throw 'Invalid buffer size, must be a power of 2.';
122
+ }
123
+ if (bufferSize !== buffer.length) {
124
+ throw ('Supplied buffer is not the same size as defined FFT. FFT Size: ' +
125
+ bufferSize +
126
+ ' Buffer Size: ' +
127
+ buffer.length);
128
+ }
129
+ var halfSize = 1, phaseShiftStepReal, phaseShiftStepImag, currentPhaseShiftReal, currentPhaseShiftImag, off, tr, ti, tmpReal;
130
+ for (var i = 0; i < bufferSize; i++) {
131
+ real[i] = buffer[reverseTable[i]] * this.windowValues[reverseTable[i]];
132
+ imag[i] = 0;
133
+ }
134
+ while (halfSize < bufferSize) {
135
+ phaseShiftStepReal = cosTable[halfSize];
136
+ phaseShiftStepImag = sinTable[halfSize];
137
+ currentPhaseShiftReal = 1;
138
+ currentPhaseShiftImag = 0;
139
+ for (var fftStep = 0; fftStep < halfSize; fftStep++) {
140
+ var i = fftStep;
141
+ while (i < bufferSize) {
142
+ off = i + halfSize;
143
+ tr = currentPhaseShiftReal * real[off] - currentPhaseShiftImag * imag[off];
144
+ ti = currentPhaseShiftReal * imag[off] + currentPhaseShiftImag * real[off];
145
+ real[off] = real[i] - tr;
146
+ imag[off] = imag[i] - ti;
147
+ real[i] += tr;
148
+ imag[i] += ti;
149
+ i += halfSize << 1;
150
+ }
151
+ tmpReal = currentPhaseShiftReal;
152
+ currentPhaseShiftReal = tmpReal * phaseShiftStepReal - currentPhaseShiftImag * phaseShiftStepImag;
153
+ currentPhaseShiftImag = tmpReal * phaseShiftStepImag + currentPhaseShiftImag * phaseShiftStepReal;
154
+ }
155
+ halfSize = halfSize << 1;
156
+ }
157
+ for (var i = 0, N = bufferSize / 2; i < N; i++) {
158
+ rval = real[i];
159
+ ival = imag[i];
160
+ mag = bSi * sqrt(rval * rval + ival * ival);
161
+ if (mag > this.peak) {
162
+ this.peakBand = i;
163
+ this.peak = mag;
164
+ }
165
+ spectrum[i] = mag;
166
+ }
167
+ return spectrum;
168
+ };
169
+ }
170
+ /**
171
+ * Spectrogram plugin for wavesurfer.
172
+ */
21
173
  import BasePlugin from '../base-plugin.js';
22
- 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;
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):((t="undefined"!=typeof globalThis?globalThis:t||self).WaveSurfer=t.WaveSurfer||{},t.WaveSurfer.Spectrogram=e())}(this,(function(){"use strict";class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const s=this.on(t,e),i=this.on(t,(()=>{s(),i()}));return s}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}function s(t,e,s,i){switch(this.bufferSize=t,this.sampleRate=e,this.bandwidth=2/t*(e/2),this.sinTable=new Float32Array(t),this.cosTable=new Float32Array(t),this.windowValues=new Float32Array(t),this.reverseTable=new Uint32Array(t),this.peakBand=0,this.peak=0,s){case"bartlett":for(a=0;a<t;a++)this.windowValues[a]=2/(t-1)*((t-1)/2-Math.abs(a-(t-1)/2));break;case"bartlettHann":for(a=0;a<t;a++)this.windowValues[a]=.62-.48*Math.abs(a/(t-1)-.5)-.38*Math.cos(2*Math.PI*a/(t-1));break;case"blackman":for(i=i||.16,a=0;a<t;a++)this.windowValues[a]=(1-i)/2-.5*Math.cos(2*Math.PI*a/(t-1))+i/2*Math.cos(4*Math.PI*a/(t-1));break;case"cosine":for(a=0;a<t;a++)this.windowValues[a]=Math.cos(Math.PI*a/(t-1)-Math.PI/2);break;case"gauss":for(i=i||.25,a=0;a<t;a++)this.windowValues[a]=Math.pow(Math.E,-.5*Math.pow((a-(t-1)/2)/(i*(t-1)/2),2));break;case"hamming":for(a=0;a<t;a++)this.windowValues[a]=.54-.46*Math.cos(2*Math.PI*a/(t-1));break;case"hann":case void 0:for(a=0;a<t;a++)this.windowValues[a]=.5*(1-Math.cos(2*Math.PI*a/(t-1)));break;case"lanczoz":for(a=0;a<t;a++)this.windowValues[a]=Math.sin(Math.PI*(2*a/(t-1)-1))/(Math.PI*(2*a/(t-1)-1));break;case"rectangular":for(a=0;a<t;a++)this.windowValues[a]=1;break;case"triangular":for(a=0;a<t;a++)this.windowValues[a]=2/t*(t/2-Math.abs(a-(t-1)/2));break;default:throw Error("No such window function '"+s+"'")}for(var a,r=1,n=t>>1;r<t;){for(a=0;a<r;a++)this.reverseTable[a+r]=this.reverseTable[a]+n;r<<=1,n>>=1}for(a=0;a<t;a++)this.sinTable[a]=Math.sin(-Math.PI/a),this.cosTable[a]=Math.cos(-Math.PI/a);this.calculateSpectrum=function(t){var e,s,i,a=this.bufferSize,r=this.cosTable,n=this.sinTable,h=this.reverseTable,l=new Float32Array(a),o=new Float32Array(a),c=2/this.bufferSize,f=Math.sqrt,u=new Float32Array(a/2),p=Math.floor(Math.log(a)/Math.LN2);if(Math.pow(2,p)!==a)throw"Invalid buffer size, must be a power of 2.";if(a!==t.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+a+" Buffer Size: "+t.length;for(var d,w,b,M,g,m,y,v,x=1,S=0;S<a;S++)l[S]=t[h[S]]*this.windowValues[h[S]],o[S]=0;for(;x<a;){d=r[x],w=n[x],b=1,M=0;for(var k=0;k<x;k++){for(S=k;S<a;)m=b*l[g=S+x]-M*o[g],y=b*o[g]+M*l[g],l[g]=l[S]-m,o[g]=o[S]-y,l[S]+=m,o[S]+=y,S+=x<<1;b=(v=b)*d-M*w,M=v*w+M*d}x<<=1}S=0;for(var q=a/2;S<q;S++)(i=c*f((e=l[S])*e+(s=o[S])*s))>this.peak&&(this.peakBand=S,this.peak=i),u[S]=i;return u}}class i extends e{static create(t){return new i(t||{})}constructor(t){if(super(t),this.utils={style:(t,e)=>Object.assign(t.style,e)},this.drawSpectrogram=t=>{isNaN(t[0][0])||(t=[t]);const e=this.spectrCc,s=this.height,i=this.width,a=this.buffer.sampleRate/2,r=this.frequencyMin,n=this.frequencyMax;if(e){for(let h=0;h<t.length;h++){const l=this.resample(t[h]),o=new ImageData(i,s);for(let t=0;t<l.length;t++)for(let e=0;e<l[t].length;e++){const a=this.colorMap[l[t][e]],r=4*((s-e)*i+t);o.data[r]=255*a[0],o.data[r+1]=255*a[1],o.data[r+2]=255*a[2],o.data[r+3]=255*a[3]}createImageBitmap(o).then((t=>{e.drawImage(t,0,s*(1-n/a),i,s*(n-r)/a,0,s*h,i,s)}))}this.emit("ready")}},this.frequenciesDataUrl=t.frequenciesDataUrl,this.container="string"==typeof t.container?document.querySelector(t.container):t.container,t.colorMap){if(t.colorMap.length<256)throw new Error("Colormap must contain 256 elements");for(let e=0;e<t.colorMap.length;e++){if(4!==t.colorMap[e].length)throw new Error("ColorMap entries must contain 4 values")}this.colorMap=t.colorMap}else{this.colorMap=[];for(let t=0;t<256;t++){const e=(255-t)/256;this.colorMap.push([e,e,e,1])}}this.fftSamples=t.fftSamples||512,this.height=t.height||this.fftSamples/2,this.noverlap=t.noverlap,this.windowFunc=t.windowFunc,this.alpha=t.alpha,this.channels=1,this.frequencyMin=t.frequencyMin||0,this.frequencyMax=t.frequencyMax||0,this.createWrapper(),this.createCanvas()}onInit(){this.container=this.container||this.wavesurfer.getWrapper(),this.container.appendChild(this.wrapper),this.wavesurfer.options.fillParent&&this.utils.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"}),this.width=this.wavesurfer.getWrapper().offsetWidth,this.subscriptions.push(this.wavesurfer.on("redraw",(()=>this.render())))}destroy(){this.unAll(),this.wavesurfer.un("ready",this._onReady),this.wavesurfer.un("redraw",this._onRender),this.wavesurfer=null,this.util=null,this.options=null,this.wrapper&&(this.wrapper.remove(),this.wrapper=null),super.destroy()}createWrapper(){if(this.wrapper=document.createElement("div"),this.utils.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height*this.channels+"px"}),this.options.labels){const t=document.createElement("canvas");t.setAttribute("part","spec-labels"),t.classList.add("spec-labels"),this.utils.style(t,{position:"absolute",zIndex:9,width:"55px",height:"100%"}),this.wrapper.appendChild(t),this.labelsEl=t}this.wrapper.addEventListener("click",this._onWrapperClick)}_wrapperClickHandler(t){t.preventDefault();const e="offsetX"in t?t.offsetX:t.layerX;this.emit("click",e/this.width||0)}createCanvas(){const t=document.createElement("canvas");this.wrapper.appendChild(t),this.spectrCc=t.getContext("2d"),this.utils.style(t,{position:"absolute",left:0,top:0,width:"100%",height:"100%",zIndex:4}),this.canvas=t}render(){this.canvas.width=this.width,this.canvas.height=this.height,this.frequenciesDataUrl?this.loadFrequenciesData(this.frequenciesDataUrl):this.getFrequencies(this.drawSpectrogram),this.loadLabels(this.options.labelsBackground,"12px","12px","",this.options.labelsColor,this.options.labelsHzColor||this.options.labelsColor,"center","#specLabels")}getFrequencies(t){const e=this.fftSamples,i=this.wavesurfer.getDecodedData(),a=this.channels;if(this.frequencyMax=this.frequencyMax||i.sampleRate/2,!i)return;this.buffer=i;const r=i.sampleRate,n=[];let h=this.noverlap;if(!h){const t=i.length/this.canvas.width;h=Math.max(0,Math.round(e-t))}const l=new s(e,r,this.windowFunc,this.alpha);for(let t=0;t<a;t++){const s=i.getChannelData(t),a=[];let r=0;for(;r+e<s.length;){const t=s.slice(r,r+e),i=l.calculateSpectrum(t),n=new Uint8Array(e/2);let o;for(o=0;o<e/2;o++)n[o]=Math.max(-255,45*Math.log10(i[o]));a.push(n),r+=e-h}n.push(a)}t(n,this)}loadFrequenciesData(t){return fetch(t).then((t=>t.json())).then(this.drawSpectrogram)}freqType(t){return t>=1e3?(t/1e3).toFixed(1):Math.round(t)}unitType(t){return t>=1e3?"KHz":"Hz"}loadLabels(t,e,s,i,a,r,n,h){t=t||"rgba(68,68,68,0)",e=e||"12px",s=s||"12px",i=i||"Helvetica",a=a||"#fff",r=r||"#fff",n=n||"center";const l=this.height||512,o=l/256*5,c=this.frequencyMin,f=(this.frequencyMax-c)/o,u=this.labelsEl.getContext("2d"),p=window.devicePixelRatio;if(this.labelsEl.height=this.height*this.channels*p,this.labelsEl.width=55*p,u.scale(p,p),u)for(let h=0;h<this.channels;h++){let p;for(u.fillStyle=t,u.fillRect(0,h*l,55,(1+h)*l),u.fill(),p=0;p<=o;p++){u.textAlign=n,u.textBaseline="middle";const t=c+f*p,o=this.freqType(t),d=this.unitType(t),w=16;let b;b=0==p?(1+h)*l+p-10:(1+h)*l-50*p+2,u.fillStyle=r,u.font=s+" "+i,u.fillText(d,w+24,b),u.fillStyle=a,u.font=e+" "+i,u.fillText(o,w,b)}}}resample(t){const e=this.width,s=[],i=1/t.length,a=1/e;let r;for(r=0;r<e;r++){const e=new Array(t[0].length);let n;for(n=0;n<t.length;n++){const s=n*i,h=s+i,l=r*a,o=l+a,c=h<=l||o<=s?0:Math.min(Math.max(h,l),Math.max(o,s))-Math.max(Math.min(h,l),Math.min(o,s));let f;if(c>0)for(f=0;f<t[0].length;f++)null==e[f]&&(e[f]=0),e[f]+=c/a*t[n][f]}const h=new Uint8Array(t[0].length);let l;for(l=0;l<t[0].length;l++)h[l]=e[l];s.push(h)}return s}}return i}));