wavesurfer.js 7.0.0-alpha.9 → 7.0.0-beta.10

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 (55) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +137 -20
  3. package/dist/base-plugin.d.ts +6 -4
  4. package/dist/base-plugin.js +9 -3
  5. package/dist/decoder.d.ts +8 -7
  6. package/dist/decoder.js +43 -38
  7. package/dist/draggable.d.ts +1 -0
  8. package/dist/draggable.js +59 -0
  9. package/dist/event-emitter.d.ts +16 -10
  10. package/dist/event-emitter.js +38 -16
  11. package/dist/fetcher.d.ts +6 -3
  12. package/dist/fetcher.js +9 -15
  13. package/dist/player.d.ts +31 -11
  14. package/dist/player.js +58 -31
  15. package/dist/plugins/envelope.d.ts +71 -0
  16. package/dist/plugins/envelope.js +326 -0
  17. package/dist/plugins/envelope.min.cjs +1 -0
  18. package/dist/plugins/minimap.d.ts +39 -0
  19. package/dist/plugins/minimap.js +114 -0
  20. package/dist/plugins/minimap.min.cjs +1 -0
  21. package/dist/plugins/record.d.ts +28 -0
  22. package/dist/plugins/record.js +132 -0
  23. package/dist/plugins/record.min.cjs +1 -0
  24. package/dist/plugins/regions.d.ts +84 -43
  25. package/dist/plugins/regions.js +330 -195
  26. package/dist/plugins/regions.min.cjs +1 -0
  27. package/dist/plugins/spectrogram-fft.d.ts +9 -0
  28. package/dist/plugins/spectrogram-fft.js +150 -0
  29. package/dist/plugins/spectrogram.d.ts +72 -0
  30. package/dist/plugins/spectrogram.js +341 -0
  31. package/dist/plugins/spectrogram.min.cjs +1 -0
  32. package/dist/plugins/timeline.d.ts +25 -9
  33. package/dist/plugins/timeline.js +65 -24
  34. package/dist/plugins/timeline.min.cjs +1 -0
  35. package/dist/renderer.d.ts +32 -26
  36. package/dist/renderer.js +363 -167
  37. package/dist/timer.d.ts +1 -1
  38. package/dist/wavesurfer.d.ts +154 -0
  39. package/dist/wavesurfer.js +230 -0
  40. package/dist/wavesurfer.min.cjs +1 -0
  41. package/package.json +61 -28
  42. package/dist/index.d.ts +0 -117
  43. package/dist/index.js +0 -227
  44. package/dist/player-webaudio.d.ts +0 -8
  45. package/dist/player-webaudio.js +0 -32
  46. package/dist/plugins/multitrack.d.ts +0 -44
  47. package/dist/plugins/multitrack.js +0 -260
  48. package/dist/plugins/xmultitrack.d.ts +0 -44
  49. package/dist/plugins/xmultitrack.js +0 -260
  50. package/dist/react/useWavesurfer.d.ts +0 -5
  51. package/dist/react/useWavesurfer.js +0 -20
  52. package/dist/wavesurfer.Multitrack.min.js +0 -1
  53. package/dist/wavesurfer.Regions.min.js +0 -1
  54. package/dist/wavesurfer.Timeline.min.js +0 -1
  55. package/dist/wavesurfer.min.js +0 -1
@@ -1,215 +1,350 @@
1
+ /**
2
+ * Regions are visual overlays on the waveform that can be used to mark segments of audio.
3
+ * Regions can be clicked on, dragged and resized.
4
+ * You can set the color and content of each region, as well as their HTML content.
5
+ */
1
6
  import BasePlugin from '../base-plugin.js';
2
- const MIN_WIDTH = 10;
3
- const defaultOptions = {
4
- dragSelection: true,
5
- draggable: true,
6
- resizable: true,
7
- };
8
- const style = (element, styles) => {
9
- for (const key in styles) {
10
- element.style[key] = styles[key] || '';
11
- }
12
- };
13
- const el = (tagName, css) => {
14
- const element = document.createElement(tagName);
15
- style(element, css);
16
- return element;
17
- };
18
- class RegionsPlugin extends BasePlugin {
19
- /** Create an instance of RegionsPlugin */
20
- constructor(params, options) {
21
- super(params, options);
22
- this.dragStart = NaN;
23
- this.regions = [];
24
- this.createdRegion = null;
25
- this.modifiedRegion = null;
26
- this.isResizingLeft = false;
27
- this.isMoving = false;
28
- this.handleMouseDown = (e) => {
29
- if (this.options.draggable || this.options.resizable || this.options.dragSelection) {
30
- this.dragStart = e.clientX - this.container.getBoundingClientRect().left;
31
- }
32
- };
33
- this.handleMouseMove = (e) => {
34
- const dragEnd = e.clientX - this.container.getBoundingClientRect().left;
35
- if (this.options.draggable && this.modifiedRegion && this.isMoving) {
36
- this.moveRegion(this.modifiedRegion, dragEnd - this.dragStart);
37
- this.dragStart = dragEnd;
38
- return;
7
+ import { makeDraggable } from '../draggable.js';
8
+ import EventEmitter from '../event-emitter.js';
9
+ export class Region extends EventEmitter {
10
+ constructor(params, totalDuration) {
11
+ super();
12
+ this.totalDuration = totalDuration;
13
+ this.id = params.id || `region-${Math.random().toString(32).slice(2)}`;
14
+ 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)';
19
+ this.element = this.initElement(params.content);
20
+ this.renderPosition();
21
+ this.initMouseEvents();
22
+ }
23
+ initElement(content) {
24
+ const element = document.createElement('div');
25
+ const isMarker = this.start === this.end;
26
+ element.setAttribute('part', `${isMarker ? 'marker' : 'region'} ${this.id}`);
27
+ element.setAttribute('style', `
28
+ position: absolute;
29
+ height: 100%;
30
+ background-color: ${isMarker ? 'none' : this.color};
31
+ border-left: ${isMarker ? '2px solid ' + this.color : 'none'};
32
+ border-radius: 2px;
33
+ box-sizing: border-box;
34
+ transition: background-color 0.2s ease;
35
+ cursor: ${this.drag ? 'grab' : 'default'};
36
+ pointer-events: all;
37
+ padding: 0.2em ${isMarker ? 0.2 : 0.4}em;
38
+ pointer-events: all;
39
+ `);
40
+ // Init content
41
+ if (content) {
42
+ if (typeof content === 'string') {
43
+ this.content = document.createElement('div');
44
+ this.content.textContent = content;
39
45
  }
40
- if (this.options.resizable && this.modifiedRegion) {
41
- this.updateRegion(this.modifiedRegion, this.isResizingLeft ? dragEnd : undefined, this.isResizingLeft ? undefined : dragEnd);
42
- return;
46
+ else {
47
+ this.content = content;
43
48
  }
44
- if (this.options.dragSelection && !isNaN(this.dragStart)) {
45
- const dragEnd = e.clientX - this.wrapper.getBoundingClientRect().left;
46
- if (dragEnd - this.dragStart >= MIN_WIDTH) {
47
- if (!this.createdRegion) {
48
- this.container.style.pointerEvents = 'none';
49
- this.createdRegion = this.createRegion(this.dragStart, dragEnd);
50
- }
51
- else {
52
- this.updateRegion(this.createdRegion, this.dragStart, dragEnd);
53
- }
54
- }
49
+ this.content.setAttribute('part', 'region-content');
50
+ element.appendChild(this.content);
51
+ }
52
+ // Add resize handles
53
+ if (!isMarker) {
54
+ const leftHandle = document.createElement('div');
55
+ leftHandle.setAttribute('data-resize', 'left');
56
+ leftHandle.setAttribute('style', `
57
+ position: absolute;
58
+ z-index: 2;
59
+ width: 6px;
60
+ height: 100%;
61
+ top: 0;
62
+ left: 0;
63
+ border-left: 2px solid rgba(0, 0, 0, 0.5);
64
+ border-radius: 2px 0 0 2px;
65
+ cursor: ${this.resize ? 'ew-resize' : 'default'};
66
+ word-break: keep-all;
67
+ `);
68
+ leftHandle.setAttribute('part', 'region-handle region-handle-left');
69
+ const rightHandle = leftHandle.cloneNode();
70
+ rightHandle.setAttribute('data-resize', 'right');
71
+ rightHandle.style.left = '';
72
+ rightHandle.style.right = '0';
73
+ rightHandle.style.borderRight = rightHandle.style.borderLeft;
74
+ rightHandle.style.borderLeft = '';
75
+ rightHandle.style.borderRadius = '0 2px 2px 0';
76
+ rightHandle.setAttribute('part', 'region-handle region-handle-right');
77
+ element.appendChild(leftHandle);
78
+ element.appendChild(rightHandle);
79
+ }
80
+ return element;
81
+ }
82
+ renderPosition() {
83
+ const start = this.start / this.totalDuration;
84
+ const end = this.end / this.totalDuration;
85
+ this.element.style.left = `${start * 100}%`;
86
+ this.element.style.width = `${(end - start) * 100}%`;
87
+ }
88
+ initMouseEvents() {
89
+ const { element } = this;
90
+ if (!element)
91
+ return;
92
+ element.addEventListener('click', (e) => this.emit('click', e));
93
+ element.addEventListener('mouseenter', (e) => this.emit('over', e));
94
+ element.addEventListener('mouseleave', (e) => this.emit('leave', e));
95
+ element.addEventListener('dblclick', (e) => this.emit('dblclick', e));
96
+ // Drag
97
+ makeDraggable(element, (dx) => this.onMove(dx), () => this.onStartMoving(), () => this.onEndMoving());
98
+ // Resize
99
+ const resizeThreshold = 1;
100
+ makeDraggable(element.querySelector('[data-resize="left"]'), (dx) => this.onResize(dx, 'start'), () => null, () => this.onEndResizing(), resizeThreshold);
101
+ makeDraggable(element.querySelector('[data-resize="right"]'), (dx) => this.onResize(dx, 'end'), () => null, () => this.onEndResizing(), resizeThreshold);
102
+ }
103
+ onStartMoving() {
104
+ if (!this.drag)
105
+ return;
106
+ this.element.style.cursor = 'grabbing';
107
+ }
108
+ onEndMoving() {
109
+ if (!this.drag)
110
+ return;
111
+ this.element.style.cursor = 'grab';
112
+ this.emit('update-end');
113
+ }
114
+ onUpdate(dx, sides) {
115
+ if (!this.element.parentElement)
116
+ return;
117
+ 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));
55
122
  }
56
- };
57
- this.handleMouseUp = () => {
58
- if (this.createdRegion) {
59
- this.addRegion(this.createdRegion);
60
- this.createdRegion = null;
123
+ else {
124
+ this.end = Math.max(this.start, Math.min(this.end, this.totalDuration));
61
125
  }
62
- this.modifiedRegion = null;
63
- this.isMoving = false;
64
- this.dragStart = NaN;
65
- this.container.style.pointerEvents = '';
66
- };
67
- this.options = Object.assign({}, defaultOptions, options);
68
- this.wrapper = this.initWrapper();
69
- const unsubscribe = this.wavesurfer.once('decode', () => {
70
- this.container.appendChild(this.wrapper);
71
126
  });
72
- this.subscriptions.push(unsubscribe);
73
- this.container.addEventListener('mousedown', this.handleMouseDown);
74
- document.addEventListener('mousemove', this.handleMouseMove);
75
- document.addEventListener('mouseup', this.handleMouseUp);
127
+ this.renderPosition();
128
+ this.emit('update');
76
129
  }
77
- /** Unmount */
78
- destroy() {
79
- this.container.removeEventListener('mousedown', this.handleMouseDown);
80
- document.removeEventListener('mousemove', this.handleMouseMove);
81
- document.removeEventListener('mouseup', this.handleMouseUp, true);
82
- this.wrapper.remove();
83
- super.destroy();
130
+ onMove(dx) {
131
+ if (!this.drag)
132
+ return;
133
+ this.onUpdate(dx, ['start', 'end']);
84
134
  }
85
- initWrapper() {
86
- return el('div', {
87
- position: 'absolute',
88
- top: '0',
89
- left: '0',
90
- width: '100%',
91
- height: '100%',
92
- zIndex: '3',
93
- pointerEvents: 'none',
94
- });
135
+ onResize(dx, side) {
136
+ if (!this.resize)
137
+ return;
138
+ this.onUpdate(dx, [side]);
95
139
  }
96
- createRegionElement(start, end, title = '') {
97
- const noWidth = start === end;
98
- const div = el('div', {
99
- position: 'absolute',
100
- left: `${start}px`,
101
- width: `${end - start}px`,
102
- height: '100%',
103
- backgroundColor: noWidth ? '' : 'rgba(0, 0, 0, 0.1)',
104
- borderRadius: '2px',
105
- boxSizing: 'border-box',
106
- borderLeft: '2px solid rgba(0, 0, 0, 0.5)',
107
- borderRight: noWidth ? '' : '2px solid rgba(0, 0, 0, 0.5)',
108
- transition: 'background-color 0.2s ease',
109
- cursor: this.options.draggable ? 'move' : '',
110
- pointerEvents: 'all',
111
- padding: '0.2em',
112
- });
113
- div.textContent = title;
114
- const leftHandle = el('div', {
115
- position: 'absolute',
116
- left: '0',
117
- width: '6px',
118
- height: '100%',
119
- cursor: this.options.resizable ? 'ew-resize' : '',
120
- pointerEvents: 'all',
121
- });
122
- div.appendChild(leftHandle);
123
- const rightHandle = leftHandle.cloneNode();
124
- style(rightHandle, {
125
- left: '',
126
- right: '0',
127
- borderLeft: '',
128
- });
129
- div.appendChild(rightHandle);
130
- leftHandle.addEventListener('mousedown', (e) => {
131
- if (!this.options.resizable)
132
- return;
133
- e.stopPropagation();
134
- this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
135
- this.isResizingLeft = true;
136
- this.isMoving = false;
137
- });
138
- rightHandle.addEventListener('mousedown', (e) => {
139
- if (!this.options.resizable)
140
- return;
141
- e.stopPropagation();
142
- this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
143
- this.isResizingLeft = false;
144
- this.isMoving = false;
145
- });
146
- div.addEventListener('mousedown', () => {
147
- if (!this.options.draggable)
148
- return;
149
- this.modifiedRegion = this.regions.find((r) => r.element === div) || null;
150
- this.isMoving = true;
151
- });
152
- div.addEventListener('click', () => {
153
- const region = this.regions.find((r) => r.element === div);
154
- if (region) {
155
- this.emit('region-clicked', { region });
156
- }
157
- });
158
- this.wrapper.appendChild(div);
159
- return div;
140
+ onEndResizing() {
141
+ if (!this.resize)
142
+ return;
143
+ this.emit('update-end');
160
144
  }
161
- createRegion(start, end, title = '') {
162
- const duration = this.wavesurfer.getDuration();
163
- const width = this.wrapper.clientWidth;
164
- start = Math.max(0, Math.min(start, width - 1));
165
- end = Math.max(0, Math.min(end, width - 1));
166
- return {
167
- element: this.createRegionElement(start, end, title),
168
- start,
169
- end,
170
- startTime: (start / width) * duration,
171
- endTime: (end / width) * duration,
172
- title,
173
- };
174
- }
175
- addRegion(region) {
176
- this.regions.push(region);
177
- this.emit('region-created', { region });
178
- }
179
- updateRegion(region, start, end) {
180
- const width = this.wrapper.clientWidth;
181
- if (start != null) {
182
- start = Math.max(0, Math.min(start, width));
183
- region.start = start;
184
- region.element.style.left = `${region.start}px`;
185
- region.startTime = (start / this.wrapper.clientWidth) * this.wavesurfer.getDuration();
145
+ _setTotalDuration(totalDuration) {
146
+ this.totalDuration = totalDuration;
147
+ this.renderPosition();
148
+ }
149
+ /** Play the region from start to end */
150
+ play() {
151
+ this.emit('play');
152
+ }
153
+ /** Update the region's options */
154
+ setOptions(options) {
155
+ if (options.color) {
156
+ this.color = options.color;
157
+ this.element.style.backgroundColor = this.color;
158
+ }
159
+ if (options.drag !== undefined) {
160
+ this.drag = options.drag;
161
+ this.element.style.cursor = this.drag ? 'grab' : 'default';
162
+ }
163
+ if (options.resize !== undefined) {
164
+ this.resize = options.resize;
165
+ this.element.querySelectorAll('[data-resize]').forEach((handle) => {
166
+ ;
167
+ handle.style.cursor = this.resize ? 'ew-resize' : 'default';
168
+ });
169
+ }
170
+ if (options.start !== undefined || options.end !== undefined) {
171
+ this.start = options.start ?? this.start;
172
+ this.end = options.end ?? this.end;
173
+ this.renderPosition();
186
174
  }
187
- if (end != null) {
188
- end = Math.max(0, Math.min(end, width));
189
- region.end = end;
190
- region.element.style.width = `${region.end - region.start}px`;
191
- region.endTime = (end / this.wrapper.clientWidth) * this.wavesurfer.getDuration();
175
+ }
176
+ /** Remove the region */
177
+ remove() {
178
+ this.emit('remove');
179
+ this.element.remove();
180
+ // This violates the type but we want to clean up the DOM reference
181
+ // w/o having to have a nullable type of the element
182
+ this.element = null;
183
+ }
184
+ }
185
+ class RegionsPlugin extends BasePlugin {
186
+ /** Create an instance of RegionsPlugin */
187
+ constructor(options) {
188
+ super(options);
189
+ this.regions = [];
190
+ this.regionsContainer = this.initRegionsContainer();
191
+ }
192
+ /** Create an instance of RegionsPlugin */
193
+ static create(options) {
194
+ return new RegionsPlugin(options);
195
+ }
196
+ /** Called by wavesurfer, don't call manually */
197
+ onInit() {
198
+ if (!this.wavesurfer) {
199
+ throw Error('WaveSurfer is not initialized');
192
200
  }
193
- this.emit('region-updated', { region });
201
+ this.wavesurfer.getWrapper().appendChild(this.regionsContainer);
194
202
  }
195
- moveRegion(region, delta) {
196
- this.updateRegion(region, region.start + delta, region.end + delta);
203
+ initRegionsContainer() {
204
+ const div = document.createElement('div');
205
+ div.setAttribute('style', `
206
+ position: absolute;
207
+ top: 0;
208
+ left: 0;
209
+ width: 100%;
210
+ height: 100%;
211
+ z-index: 3;
212
+ pointer-events: none;
213
+ `);
214
+ return div;
215
+ }
216
+ /** Get all created regions */
217
+ getRegions() {
218
+ return this.regions;
197
219
  }
198
- /** Create a region at a given start and end time, with an optional title */
199
- add(startTime, endTime, title = '', color = '') {
220
+ avoidOverlapping(region) {
221
+ if (!region.content)
222
+ return;
223
+ // Check that the label doesn't overlap with other labels
224
+ // If it does, push it down until it doesn't
225
+ const div = region.content;
226
+ const labelLeft = div.getBoundingClientRect().left;
227
+ const labelWidth = region.element.scrollWidth;
228
+ const overlap = this.regions
229
+ .filter((reg) => {
230
+ if (reg === region || !reg.content)
231
+ return false;
232
+ const left = reg.content.getBoundingClientRect().left;
233
+ const width = reg.element.scrollWidth;
234
+ return labelLeft < left + width && left < labelLeft + labelWidth;
235
+ })
236
+ .map((reg) => reg.content?.getBoundingClientRect().height || 0)
237
+ .reduce((sum, val) => sum + val, 0);
238
+ div.style.marginTop = `${overlap}px`;
239
+ }
240
+ saveRegion(region) {
241
+ this.regionsContainer.appendChild(region.element);
242
+ this.avoidOverlapping(region);
243
+ this.regions.push(region);
244
+ this.emit('region-created', region);
245
+ const regionSubscriptions = [
246
+ region.on('update-end', () => {
247
+ this.avoidOverlapping(region);
248
+ this.emit('region-updated', region);
249
+ }),
250
+ region.on('play', () => {
251
+ this.wavesurfer?.play();
252
+ this.wavesurfer?.setTime(region.start);
253
+ }),
254
+ region.on('click', (e) => {
255
+ this.emit('region-clicked', region, e);
256
+ }),
257
+ region.on('dblclick', (e) => {
258
+ this.emit('region-double-clicked', region, e);
259
+ }),
260
+ // Remove the region from the list when it's removed
261
+ region.once('remove', () => {
262
+ regionSubscriptions.forEach((unsubscribe) => unsubscribe());
263
+ this.regions = this.regions.filter((reg) => reg !== region);
264
+ }),
265
+ ];
266
+ this.subscriptions.push(...regionSubscriptions);
267
+ }
268
+ /** Create a region with given parameters */
269
+ addRegion(options) {
270
+ if (!this.wavesurfer) {
271
+ throw Error('WaveSurfer is not initialized');
272
+ }
200
273
  const duration = this.wavesurfer.getDuration();
201
- const width = this.wrapper.clientWidth;
202
- const start = (startTime / duration) * width;
203
- const end = (endTime / duration) * width;
204
- const region = this.createRegion(start, end, title);
205
- this.addRegion(region);
206
- if (color)
207
- this.setRegionColor(region, color);
274
+ const region = new Region(options, duration);
275
+ if (!duration) {
276
+ this.subscriptions.push(this.wavesurfer.once('ready', (duration) => {
277
+ region._setTotalDuration(duration);
278
+ this.saveRegion(region);
279
+ }));
280
+ }
281
+ else {
282
+ this.saveRegion(region);
283
+ }
208
284
  return region;
209
285
  }
210
- /** Set the background color of a region */
211
- setRegionColor(region, color) {
212
- region.element.style[region.startTime === region.endTime ? 'borderColor' : 'backgroundColor'] = color;
286
+ /**
287
+ * Enable creation of regions by dragging on an empty space on the waveform.
288
+ * Returns a function to disable the drag selection.
289
+ */
290
+ enableDragSelection(options) {
291
+ const wrapper = this.wavesurfer?.getWrapper()?.querySelector('div');
292
+ if (!wrapper)
293
+ return () => undefined;
294
+ let region = null;
295
+ let sumDx = 0;
296
+ return makeDraggable(wrapper,
297
+ // 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;
316
+ if (region) {
317
+ const privateRegion = region;
318
+ privateRegion.onUpdate(dx, [sumDx > 0 ? 'end' : 'start']);
319
+ }
320
+ },
321
+ // On drag start
322
+ () => null,
323
+ // On drag end
324
+ () => {
325
+ if (region) {
326
+ this.saveRegion(region);
327
+ 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
+ }
338
+ });
339
+ }
340
+ /** Remove all regions */
341
+ clearRegions() {
342
+ this.regions.forEach((region) => region.remove());
343
+ }
344
+ /** Destroy the plugin and clean up */
345
+ destroy() {
346
+ this.clearRegions();
347
+ super.destroy();
213
348
  }
214
349
  }
215
350
  export default RegionsPlugin;
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Regions=t():e.Regions=t()}(WaveSurfer,(()=>(()=>{"use strict";var e={d:(t,i)=>{for(var n in i)e.o(i,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:i[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>a});const i=class{constructor(){this.listeners={}}on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),()=>this.un(e,t)}once(e,t){const i=this.on(e,t),n=this.on(e,(()=>{i(),n()}));return i}un(e,t){this.listeners[e]&&(t?this.listeners[e].delete(t):delete this.listeners[e])}unAll(){this.listeners={}}emit(e,...t){this.listeners[e]&&this.listeners[e].forEach((e=>e(...t)))}},n=class extends i{constructor(e){super(),this.subscriptions=[],this.options=e}onInit(){}init(e){this.wavesurfer=e,this.onInit()}destroy(){this.subscriptions.forEach((e=>e()))}};function s(e,t,i,n,s=5){let r=()=>{};if(!e)return r;const o=o=>{o.preventDefault(),o.stopPropagation();let a=o.clientX,l=o.clientY,d=!1;const h=n=>{n.preventDefault(),n.stopPropagation();const r=n.clientX,o=n.clientY;if(d||Math.abs(r-a)>=s||Math.abs(o-l)>=s){d||(d=!0,i?.(a,l));const{left:n,top:s}=e.getBoundingClientRect();t(r-a,o-l,r-n,o-s),a=r,l=o}},c=e=>{d&&(e.preventDefault(),e.stopPropagation())},u=()=>{d&&n?.(),r()};r=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",u),document.removeEventListener("pointerleave",u),setTimeout((()=>{document.removeEventListener("click",c,!0)}),10)},document.addEventListener("pointermove",h),document.addEventListener("pointerup",u),document.addEventListener("pointerleave",u),document.addEventListener("click",c,!0)};return e.addEventListener("pointerdown",o),()=>{r(),e.removeEventListener("pointerdown",o)}}class r extends i{constructor(e,t){super(),this.totalDuration=t,this.id=e.id||`region-${Math.random().toString(32).slice(2)}`,this.start=e.start,this.end=e.end??e.start,this.drag=e.drag??!0,this.resize=e.resize??!0,this.color=e.color??"rgba(0, 0, 0, 0.1)",this.element=this.initElement(e.content),this.renderPosition(),this.initMouseEvents()}initElement(e){const t=document.createElement("div"),i=this.start===this.end;if(t.setAttribute("part",`${i?"marker":"region"} ${this.id}`),t.setAttribute("style",`\n position: absolute;\n height: 100%;\n background-color: ${i?"none":this.color};\n border-left: ${i?"2px solid "+this.color:"none"};\n border-radius: 2px;\n box-sizing: border-box;\n transition: background-color 0.2s ease;\n cursor: ${this.drag?"grab":"default"};\n pointer-events: all;\n padding: 0.2em ${i?.2:.4}em;\n pointer-events: all;\n `),e&&("string"==typeof e?(this.content=document.createElement("div"),this.content.textContent=e):this.content=e,this.content.setAttribute("part","region-content"),t.appendChild(this.content)),!i){const e=document.createElement("div");e.setAttribute("data-resize","left"),e.setAttribute("style",`\n position: absolute;\n z-index: 2;\n width: 6px;\n height: 100%;\n top: 0;\n left: 0;\n border-left: 2px solid rgba(0, 0, 0, 0.5);\n border-radius: 2px 0 0 2px;\n cursor: ${this.resize?"ew-resize":"default"};\n word-break: keep-all;\n `),e.setAttribute("part","region-handle region-handle-left");const i=e.cloneNode();i.setAttribute("data-resize","right"),i.style.left="",i.style.right="0",i.style.borderRight=i.style.borderLeft,i.style.borderLeft="",i.style.borderRadius="0 2px 2px 0",i.setAttribute("part","region-handle region-handle-right"),t.appendChild(e),t.appendChild(i)}return t}renderPosition(){const e=this.start/this.totalDuration,t=this.end/this.totalDuration;this.element.style.left=100*e+"%",this.element.style.width=100*(t-e)+"%"}initMouseEvents(){const{element:e}=this;e&&(e.addEventListener("click",(e=>this.emit("click",e))),e.addEventListener("mouseenter",(e=>this.emit("over",e))),e.addEventListener("mouseleave",(e=>this.emit("leave",e))),e.addEventListener("dblclick",(e=>this.emit("dblclick",e))),s(e,(e=>this.onMove(e)),(()=>this.onStartMoving()),(()=>this.onEndMoving())),s(e.querySelector('[data-resize="left"]'),(e=>this.onResize(e,"start")),(()=>null),(()=>this.onEndResizing()),1),s(e.querySelector('[data-resize="right"]'),(e=>this.onResize(e,"end")),(()=>null),(()=>this.onEndResizing()),1))}onStartMoving(){this.drag&&(this.element.style.cursor="grabbing")}onEndMoving(){this.drag&&(this.element.style.cursor="grab",this.emit("update-end"))}onUpdate(e,t){if(!this.element.parentElement)return;const i=e/this.element.parentElement.clientWidth*this.totalDuration;t.forEach((e=>{this[e]+=i,"start"===e?this.start=Math.max(0,Math.min(this.start,this.end)):this.end=Math.max(this.start,Math.min(this.end,this.totalDuration))})),this.renderPosition(),this.emit("update")}onMove(e){this.drag&&this.onUpdate(e,["start","end"])}onResize(e,t){this.resize&&this.onUpdate(e,[t])}onEndResizing(){this.resize&&this.emit("update-end")}_setTotalDuration(e){this.totalDuration=e,this.renderPosition()}play(){this.emit("play")}setOptions(e){e.color&&(this.color=e.color,this.element.style.backgroundColor=this.color),void 0!==e.drag&&(this.drag=e.drag,this.element.style.cursor=this.drag?"grab":"default"),void 0!==e.resize&&(this.resize=e.resize,this.element.querySelectorAll("[data-resize]").forEach((e=>{e.style.cursor=this.resize?"ew-resize":"default"}))),void 0===e.start&&void 0===e.end||(this.start=e.start??this.start,this.end=e.end??this.end,this.renderPosition())}remove(){this.emit("remove"),this.element.remove(),this.element=null}}class o extends n{constructor(e){super(e),this.regions=[],this.regionsContainer=this.initRegionsContainer()}static create(e){return new o(e)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.wavesurfer.getWrapper().appendChild(this.regionsContainer)}initRegionsContainer(){const e=document.createElement("div");return e.setAttribute("style","\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n pointer-events: none;\n "),e}getRegions(){return this.regions}avoidOverlapping(e){if(!e.content)return;const t=e.content,i=t.getBoundingClientRect().left,n=e.element.scrollWidth,s=this.regions.filter((t=>{if(t===e||!t.content)return!1;const s=t.content.getBoundingClientRect().left,r=t.element.scrollWidth;return i<s+r&&s<i+n})).map((e=>e.content?.getBoundingClientRect().height||0)).reduce(((e,t)=>e+t),0);t.style.marginTop=`${s}px`}saveRegion(e){this.regionsContainer.appendChild(e.element),this.avoidOverlapping(e),this.regions.push(e),this.emit("region-created",e);const t=[e.on("update-end",(()=>{this.avoidOverlapping(e),this.emit("region-updated",e)})),e.on("play",(()=>{this.wavesurfer?.play(),this.wavesurfer?.setTime(e.start)})),e.on("click",(t=>{this.emit("region-clicked",e,t)})),e.on("dblclick",(t=>{this.emit("region-double-clicked",e,t)})),e.once("remove",(()=>{t.forEach((e=>e())),this.regions=this.regions.filter((t=>t!==e))}))];this.subscriptions.push(...t)}addRegion(e){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const t=this.wavesurfer.getDuration(),i=new r(e,t);return t?this.saveRegion(i):this.subscriptions.push(this.wavesurfer.once("ready",(e=>{i._setTotalDuration(e),this.saveRegion(i)}))),i}enableDragSelection(e){const t=this.wavesurfer?.getWrapper()?.querySelector("div");if(!t)return()=>{};let i=null,n=0;return s(t,((s,o,a)=>{if(this.wavesurfer){if(!i){const n=this.wavesurfer.getDuration(),s=t.getBoundingClientRect();let o=a/s.width*n,l=(a-s.left)/s.width*n;o>l&&([o,l]=[l,o]),i=new r({...e,start:o,end:l},n),this.regionsContainer.appendChild(i.element)}n+=s,i&&i.onUpdate(s,[n>0?"end":"start"])}}),(()=>null),(()=>{if(i&&(this.saveRegion(i),i=null,n=0,this.wavesurfer)){const{interact:e}=this.wavesurfer.options;e&&(this.wavesurfer.toggleInteraction(!1),setTimeout((()=>this.wavesurfer?.toggleInteraction(e)),10))}}))}clearRegions(){this.regions.forEach((e=>e.remove()))}destroy(){this.clearRegions(),super.destroy()}}const a=o;return t.default})()));
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Calculate FFT - Based on https://github.com/corbanbrook/dsp.js
3
+ *
4
+ * @param {Number} bufferSize Buffer size
5
+ * @param {Number} sampleRate Sample rate
6
+ * @param {Function} windowFunc Window function
7
+ * @param {Number} alpha Alpha channel
8
+ */
9
+ export default function FFT(bufferSize: any, sampleRate: any, windowFunc: any, alpha: any): void;