wavesurfer.js 7.0.0-beta.6 → 7.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugins/minimap.min.js +1 -1
- package/dist/renderer.d.ts +2 -0
- package/dist/renderer.js +25 -15
- package/dist/wavesurfer.d.ts +2 -1
- package/dist/wavesurfer.js +0 -1
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +1 -1
- package/dist/cypress/support/commands.d.ts +0 -1
- package/dist/cypress/support/commands.js +0 -39
- package/dist/cypress/support/e2e.d.ts +0 -1
- package/dist/cypress/support/e2e.js +0 -18
- package/dist/cypress.config.d.ts +0 -2
- package/dist/cypress.config.js +0 -10
- package/dist/plugins/envelope.min.cjs +0 -1
- package/dist/plugins/minimap.min.cjs +0 -1
- package/dist/plugins/multitrack.d.ts +0 -117
- package/dist/plugins/multitrack.js +0 -507
- package/dist/plugins/multitrack.min.cjs +0 -1
- package/dist/plugins/multitrack.min.js +0 -1
- package/dist/plugins/record.min.cjs +0 -1
- package/dist/plugins/regions.min.cjs +0 -1
- package/dist/plugins/spectrogram.min.cjs +0 -1
- package/dist/plugins/timeline.min.cjs +0 -1
- package/dist/src/base-plugin.d.ts +0 -13
- package/dist/src/base-plugin.js +0 -22
- package/dist/src/decoder.d.ts +0 -9
- package/dist/src/decoder.js +0 -48
- package/dist/src/event-emitter.d.ts +0 -19
- package/dist/src/event-emitter.js +0 -45
- package/dist/src/fetcher.d.ts +0 -5
- package/dist/src/fetcher.js +0 -7
- package/dist/src/player.d.ts +0 -45
- package/dist/src/player.js +0 -114
- package/dist/src/plugins/envelope.d.ts +0 -71
- package/dist/src/plugins/envelope.js +0 -350
- package/dist/src/plugins/minimap.d.ts +0 -39
- package/dist/src/plugins/minimap.js +0 -117
- package/dist/src/plugins/record.d.ts +0 -26
- package/dist/src/plugins/record.js +0 -124
- package/dist/src/plugins/regions.d.ts +0 -93
- package/dist/src/plugins/regions.js +0 -395
- package/dist/src/plugins/spectrogram-fft.d.ts +0 -9
- package/dist/src/plugins/spectrogram-fft.js +0 -150
- package/dist/src/plugins/spectrogram.d.ts +0 -69
- package/dist/src/plugins/spectrogram.js +0 -340
- package/dist/src/plugins/timeline.d.ts +0 -45
- package/dist/src/plugins/timeline.js +0 -162
- package/dist/src/renderer.d.ts +0 -41
- package/dist/src/renderer.js +0 -420
- package/dist/src/timer.d.ts +0 -11
- package/dist/src/timer.js +0 -19
- package/dist/src/wavesurfer.d.ts +0 -149
- package/dist/src/wavesurfer.js +0 -229
- package/dist/wavesurfer.min.cjs +0 -1
|
@@ -1,93 +0,0 @@
|
|
|
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
|
-
*/
|
|
6
|
-
import BasePlugin from '../base-plugin.js';
|
|
7
|
-
import EventEmitter from '../event-emitter.js';
|
|
8
|
-
export type RegionsPluginOptions = undefined;
|
|
9
|
-
export type RegionsPluginEvents = {
|
|
10
|
-
'region-created': [region: Region];
|
|
11
|
-
'region-updated': [region: Region];
|
|
12
|
-
'region-clicked': [region: Region, e: MouseEvent];
|
|
13
|
-
};
|
|
14
|
-
export type RegionEvents = {
|
|
15
|
-
remove: [];
|
|
16
|
-
update: [];
|
|
17
|
-
'update-end': [];
|
|
18
|
-
play: [];
|
|
19
|
-
click: [event: MouseEvent];
|
|
20
|
-
dblclick: [event: MouseEvent];
|
|
21
|
-
over: [event: MouseEvent];
|
|
22
|
-
leave: [event: MouseEvent];
|
|
23
|
-
};
|
|
24
|
-
export type RegionParams = {
|
|
25
|
-
id?: string;
|
|
26
|
-
start: number;
|
|
27
|
-
end?: number;
|
|
28
|
-
drag?: boolean;
|
|
29
|
-
resize?: boolean;
|
|
30
|
-
color?: string;
|
|
31
|
-
content?: string | HTMLElement;
|
|
32
|
-
};
|
|
33
|
-
export declare class Region extends EventEmitter<RegionEvents> {
|
|
34
|
-
private totalDuration;
|
|
35
|
-
element: HTMLElement;
|
|
36
|
-
id: string;
|
|
37
|
-
start: number;
|
|
38
|
-
end: number;
|
|
39
|
-
drag: boolean;
|
|
40
|
-
resize: boolean;
|
|
41
|
-
color: string;
|
|
42
|
-
content?: HTMLElement;
|
|
43
|
-
constructor(params: RegionParams, totalDuration: number);
|
|
44
|
-
private initElement;
|
|
45
|
-
private renderPosition;
|
|
46
|
-
private initMouseEvents;
|
|
47
|
-
private onStartMoving;
|
|
48
|
-
private onEndMoving;
|
|
49
|
-
private onUpdate;
|
|
50
|
-
private onMove;
|
|
51
|
-
private onResize;
|
|
52
|
-
private onEndResizing;
|
|
53
|
-
_setTotalDuration(totalDuration: number): void;
|
|
54
|
-
/** Play the region from start to end */
|
|
55
|
-
play(): void;
|
|
56
|
-
/** Update the region's options */
|
|
57
|
-
setOptions(options: {
|
|
58
|
-
color?: string;
|
|
59
|
-
drag?: boolean;
|
|
60
|
-
resize?: boolean;
|
|
61
|
-
start?: number;
|
|
62
|
-
end?: number;
|
|
63
|
-
}): void;
|
|
64
|
-
/** Remove the region */
|
|
65
|
-
remove(): void;
|
|
66
|
-
}
|
|
67
|
-
declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions> {
|
|
68
|
-
private regions;
|
|
69
|
-
private regionsContainer;
|
|
70
|
-
/** Create an instance of RegionsPlugin */
|
|
71
|
-
constructor(options?: RegionsPluginOptions);
|
|
72
|
-
/** Create an instance of RegionsPlugin */
|
|
73
|
-
static create(options?: RegionsPluginOptions): RegionsPlugin;
|
|
74
|
-
/** Called by wavesurfer, don't call manually */
|
|
75
|
-
onInit(): void;
|
|
76
|
-
private initRegionsContainer;
|
|
77
|
-
/** Get all created regions */
|
|
78
|
-
getRegions(): Region[];
|
|
79
|
-
private avoidOverlapping;
|
|
80
|
-
private saveRegion;
|
|
81
|
-
/** Create a region with given parameters */
|
|
82
|
-
addRegion(options: RegionParams): Region;
|
|
83
|
-
/**
|
|
84
|
-
* Enable creation of regions by dragging on an empty space on the waveform.
|
|
85
|
-
* Returns a function to disable the drag selection.
|
|
86
|
-
*/
|
|
87
|
-
enableDragSelection(options: Omit<RegionParams, 'start' | 'end'>): () => void;
|
|
88
|
-
/** Remove all regions */
|
|
89
|
-
clearRegions(): void;
|
|
90
|
-
/** Destroy the plugin and clean up */
|
|
91
|
-
destroy(): void;
|
|
92
|
-
}
|
|
93
|
-
export default RegionsPlugin;
|
|
@@ -1,395 +0,0 @@
|
|
|
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
|
-
*/
|
|
6
|
-
import BasePlugin from '../base-plugin.js';
|
|
7
|
-
import EventEmitter from '../event-emitter.js';
|
|
8
|
-
function makeDraggable(element, onStart, onMove, onEnd, threshold = 5) {
|
|
9
|
-
if (!element)
|
|
10
|
-
return () => undefined;
|
|
11
|
-
let isDragging = false;
|
|
12
|
-
const onClick = (e) => {
|
|
13
|
-
isDragging && e.stopPropagation();
|
|
14
|
-
};
|
|
15
|
-
const onMouseDown = (e) => {
|
|
16
|
-
e.stopPropagation();
|
|
17
|
-
let x = e.clientX;
|
|
18
|
-
let sumDx = 0;
|
|
19
|
-
onStart(x);
|
|
20
|
-
const onMouseMove = (e) => {
|
|
21
|
-
const newX = e.clientX;
|
|
22
|
-
const dx = newX - x;
|
|
23
|
-
sumDx += dx;
|
|
24
|
-
x = newX;
|
|
25
|
-
if (isDragging || Math.abs(sumDx) >= threshold) {
|
|
26
|
-
onMove(isDragging ? dx : sumDx);
|
|
27
|
-
isDragging = true;
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
const onMouseUp = () => {
|
|
31
|
-
if (isDragging) {
|
|
32
|
-
onEnd();
|
|
33
|
-
setTimeout(() => (isDragging = false), 10);
|
|
34
|
-
}
|
|
35
|
-
document.removeEventListener('mousemove', onMouseMove);
|
|
36
|
-
document.removeEventListener('mouseup', onMouseUp);
|
|
37
|
-
};
|
|
38
|
-
document.addEventListener('mousemove', onMouseMove);
|
|
39
|
-
document.addEventListener('mouseup', onMouseUp);
|
|
40
|
-
};
|
|
41
|
-
element.addEventListener('click', onClick);
|
|
42
|
-
element.addEventListener('mousedown', onMouseDown);
|
|
43
|
-
return () => {
|
|
44
|
-
element.removeEventListener('click', onClick);
|
|
45
|
-
element.removeEventListener('mousedown', onMouseDown);
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
export class Region extends EventEmitter {
|
|
49
|
-
totalDuration;
|
|
50
|
-
element;
|
|
51
|
-
id;
|
|
52
|
-
start;
|
|
53
|
-
end;
|
|
54
|
-
drag;
|
|
55
|
-
resize;
|
|
56
|
-
color;
|
|
57
|
-
content;
|
|
58
|
-
constructor(params, totalDuration) {
|
|
59
|
-
super();
|
|
60
|
-
this.totalDuration = totalDuration;
|
|
61
|
-
this.id = params.id || `region-${Math.random().toString(32).slice(2)}`;
|
|
62
|
-
this.start = params.start;
|
|
63
|
-
this.end = params.end ?? params.start;
|
|
64
|
-
this.drag = params.drag ?? true;
|
|
65
|
-
this.resize = params.resize ?? true;
|
|
66
|
-
this.color = params.color ?? 'rgba(0, 0, 0, 0.1)';
|
|
67
|
-
this.element = this.initElement(params.content);
|
|
68
|
-
this.renderPosition();
|
|
69
|
-
this.initMouseEvents();
|
|
70
|
-
}
|
|
71
|
-
initElement(content) {
|
|
72
|
-
const element = document.createElement('div');
|
|
73
|
-
const isMarker = this.start === this.end;
|
|
74
|
-
element.setAttribute('part', `${isMarker ? 'marker' : 'region'} ${this.id}`);
|
|
75
|
-
element.setAttribute('style', `
|
|
76
|
-
position: absolute;
|
|
77
|
-
height: 100%;
|
|
78
|
-
background-color: ${isMarker ? 'none' : this.color};
|
|
79
|
-
border-left: ${isMarker ? '2px solid ' + this.color : 'none'};
|
|
80
|
-
border-radius: 2px;
|
|
81
|
-
box-sizing: border-box;
|
|
82
|
-
transition: background-color 0.2s ease;
|
|
83
|
-
cursor: ${this.drag ? 'grab' : 'default'};
|
|
84
|
-
pointer-events: all;
|
|
85
|
-
padding: 0.2em ${isMarker ? 0.2 : 0.4}em;
|
|
86
|
-
pointer-events: all;
|
|
87
|
-
`);
|
|
88
|
-
// Init content
|
|
89
|
-
if (content) {
|
|
90
|
-
if (typeof content === 'string') {
|
|
91
|
-
this.content = document.createElement('div');
|
|
92
|
-
this.content.textContent = content;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
this.content = content;
|
|
96
|
-
}
|
|
97
|
-
this.content.setAttribute('part', 'region-content');
|
|
98
|
-
element.appendChild(this.content);
|
|
99
|
-
}
|
|
100
|
-
// Add resize handles
|
|
101
|
-
if (!isMarker) {
|
|
102
|
-
const leftHandle = document.createElement('div');
|
|
103
|
-
leftHandle.setAttribute('data-resize', 'left');
|
|
104
|
-
leftHandle.setAttribute('style', `
|
|
105
|
-
position: absolute;
|
|
106
|
-
z-index: 2;
|
|
107
|
-
width: 6px;
|
|
108
|
-
height: 100%;
|
|
109
|
-
top: 0;
|
|
110
|
-
left: 0;
|
|
111
|
-
border-left: 2px solid rgba(0, 0, 0, 0.5);
|
|
112
|
-
border-radius: 2px 0 0 2px;
|
|
113
|
-
cursor: ${this.resize ? 'ew-resize' : 'default'};
|
|
114
|
-
word-break: keep-all;
|
|
115
|
-
`);
|
|
116
|
-
leftHandle.setAttribute('part', 'region-handle region-handle-left');
|
|
117
|
-
const rightHandle = leftHandle.cloneNode();
|
|
118
|
-
rightHandle.setAttribute('data-resize', 'right');
|
|
119
|
-
rightHandle.style.left = '';
|
|
120
|
-
rightHandle.style.right = '0';
|
|
121
|
-
rightHandle.style.borderRight = rightHandle.style.borderLeft;
|
|
122
|
-
rightHandle.style.borderLeft = '';
|
|
123
|
-
rightHandle.style.borderRadius = '0 2px 2px 0';
|
|
124
|
-
rightHandle.setAttribute('part', 'region-handle region-handle-right');
|
|
125
|
-
element.appendChild(leftHandle);
|
|
126
|
-
element.appendChild(rightHandle);
|
|
127
|
-
}
|
|
128
|
-
return element;
|
|
129
|
-
}
|
|
130
|
-
renderPosition() {
|
|
131
|
-
const start = this.start / this.totalDuration;
|
|
132
|
-
const end = this.end / this.totalDuration;
|
|
133
|
-
this.element.style.left = `${start * 100}%`;
|
|
134
|
-
this.element.style.width = `${(end - start) * 100}%`;
|
|
135
|
-
}
|
|
136
|
-
initMouseEvents() {
|
|
137
|
-
const { element } = this;
|
|
138
|
-
element.addEventListener('click', (e) => this.emit('click', e));
|
|
139
|
-
element.addEventListener('mouseenter', (e) => this.emit('over', e));
|
|
140
|
-
element.addEventListener('mouseleave', (e) => this.emit('leave', e));
|
|
141
|
-
element.addEventListener('dblclick', (e) => this.emit('dblclick', e));
|
|
142
|
-
// Drag
|
|
143
|
-
makeDraggable(element, () => this.onStartMoving(), (dx) => this.onMove(dx), () => this.onEndMoving());
|
|
144
|
-
// Resize
|
|
145
|
-
const resizeThreshold = 1;
|
|
146
|
-
makeDraggable(element.querySelector('[data-resize="left"]'), () => null, (dx) => this.onResize(dx, 'start'), () => this.onEndResizing(), resizeThreshold);
|
|
147
|
-
makeDraggable(element.querySelector('[data-resize="right"]'), () => null, (dx) => this.onResize(dx, 'end'), () => this.onEndResizing(), resizeThreshold);
|
|
148
|
-
}
|
|
149
|
-
onStartMoving() {
|
|
150
|
-
if (!this.drag)
|
|
151
|
-
return;
|
|
152
|
-
this.element.style.cursor = 'grabbing';
|
|
153
|
-
}
|
|
154
|
-
onEndMoving() {
|
|
155
|
-
if (!this.drag)
|
|
156
|
-
return;
|
|
157
|
-
this.element.style.cursor = 'grab';
|
|
158
|
-
this.emit('update-end');
|
|
159
|
-
}
|
|
160
|
-
onUpdate(dx, sides) {
|
|
161
|
-
if (!this.element.parentElement)
|
|
162
|
-
return;
|
|
163
|
-
const deltaSeconds = (dx / this.element.parentElement.clientWidth) * this.totalDuration;
|
|
164
|
-
sides.forEach((side) => {
|
|
165
|
-
this[side] += deltaSeconds;
|
|
166
|
-
if (side === 'start') {
|
|
167
|
-
this.start = Math.max(0, Math.min(this.start, this.end));
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
this.end = Math.max(this.start, Math.min(this.end, this.totalDuration));
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
this.renderPosition();
|
|
174
|
-
this.emit('update');
|
|
175
|
-
}
|
|
176
|
-
onMove(dx) {
|
|
177
|
-
if (!this.drag)
|
|
178
|
-
return;
|
|
179
|
-
this.onUpdate(dx, ['start', 'end']);
|
|
180
|
-
}
|
|
181
|
-
onResize(dx, side) {
|
|
182
|
-
if (!this.resize)
|
|
183
|
-
return;
|
|
184
|
-
this.onUpdate(dx, [side]);
|
|
185
|
-
}
|
|
186
|
-
onEndResizing() {
|
|
187
|
-
if (!this.resize)
|
|
188
|
-
return;
|
|
189
|
-
this.emit('update-end');
|
|
190
|
-
}
|
|
191
|
-
_setTotalDuration(totalDuration) {
|
|
192
|
-
this.totalDuration = totalDuration;
|
|
193
|
-
this.renderPosition();
|
|
194
|
-
}
|
|
195
|
-
/** Play the region from start to end */
|
|
196
|
-
play() {
|
|
197
|
-
this.emit('play');
|
|
198
|
-
}
|
|
199
|
-
/** Update the region's options */
|
|
200
|
-
setOptions(options) {
|
|
201
|
-
if (options.color) {
|
|
202
|
-
this.color = options.color;
|
|
203
|
-
this.element.style.backgroundColor = this.color;
|
|
204
|
-
}
|
|
205
|
-
if (options.drag !== undefined) {
|
|
206
|
-
this.drag = options.drag;
|
|
207
|
-
this.element.style.cursor = this.drag ? 'grab' : 'default';
|
|
208
|
-
}
|
|
209
|
-
if (options.resize !== undefined) {
|
|
210
|
-
this.resize = options.resize;
|
|
211
|
-
this.element.querySelectorAll('[data-resize]').forEach((handle) => {
|
|
212
|
-
;
|
|
213
|
-
handle.style.cursor = this.resize ? 'ew-resize' : 'default';
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
if (options.start !== undefined || options.end !== undefined) {
|
|
217
|
-
this.start = options.start ?? this.start;
|
|
218
|
-
this.end = options.end ?? this.end;
|
|
219
|
-
this.renderPosition();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
/** Remove the region */
|
|
223
|
-
remove() {
|
|
224
|
-
this.emit('remove');
|
|
225
|
-
this.element.remove();
|
|
226
|
-
// This violates the type but we want to clean up the DOM reference
|
|
227
|
-
// w/o having to have a nullable type of the element
|
|
228
|
-
this.element = null;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
class RegionsPlugin extends BasePlugin {
|
|
232
|
-
regions = [];
|
|
233
|
-
regionsContainer;
|
|
234
|
-
/** Create an instance of RegionsPlugin */
|
|
235
|
-
constructor(options) {
|
|
236
|
-
super(options);
|
|
237
|
-
this.regionsContainer = this.initRegionsContainer();
|
|
238
|
-
}
|
|
239
|
-
/** Create an instance of RegionsPlugin */
|
|
240
|
-
static create(options) {
|
|
241
|
-
return new RegionsPlugin(options);
|
|
242
|
-
}
|
|
243
|
-
/** Called by wavesurfer, don't call manually */
|
|
244
|
-
onInit() {
|
|
245
|
-
if (!this.wavesurfer) {
|
|
246
|
-
throw Error('WaveSurfer is not initialized');
|
|
247
|
-
}
|
|
248
|
-
this.wavesurfer.getWrapper().appendChild(this.regionsContainer);
|
|
249
|
-
}
|
|
250
|
-
initRegionsContainer() {
|
|
251
|
-
const div = document.createElement('div');
|
|
252
|
-
div.setAttribute('style', `
|
|
253
|
-
position: absolute;
|
|
254
|
-
top: 0;
|
|
255
|
-
left: 0;
|
|
256
|
-
width: 100%;
|
|
257
|
-
height: 100%;
|
|
258
|
-
z-index: 3;
|
|
259
|
-
pointer-events: none;
|
|
260
|
-
`);
|
|
261
|
-
return div;
|
|
262
|
-
}
|
|
263
|
-
/** Get all created regions */
|
|
264
|
-
getRegions() {
|
|
265
|
-
return this.regions;
|
|
266
|
-
}
|
|
267
|
-
avoidOverlapping(region) {
|
|
268
|
-
if (!region.content)
|
|
269
|
-
return;
|
|
270
|
-
// Check that the label doesn't overlap with other labels
|
|
271
|
-
// If it does, push it down until it doesn't
|
|
272
|
-
const div = region.content;
|
|
273
|
-
const labelLeft = div.getBoundingClientRect().left;
|
|
274
|
-
const labelWidth = region.element.scrollWidth;
|
|
275
|
-
const overlap = this.regions
|
|
276
|
-
.filter((reg) => {
|
|
277
|
-
if (reg === region || !reg.content)
|
|
278
|
-
return false;
|
|
279
|
-
const left = reg.content.getBoundingClientRect().left;
|
|
280
|
-
const width = reg.element.scrollWidth;
|
|
281
|
-
return labelLeft < left + width && left < labelLeft + labelWidth;
|
|
282
|
-
})
|
|
283
|
-
.map((reg) => reg.content?.getBoundingClientRect().height || 0)
|
|
284
|
-
.reduce((sum, val) => sum + val, 0);
|
|
285
|
-
div.style.marginTop = `${overlap}px`;
|
|
286
|
-
}
|
|
287
|
-
saveRegion(region) {
|
|
288
|
-
this.regionsContainer.appendChild(region.element);
|
|
289
|
-
this.avoidOverlapping(region);
|
|
290
|
-
this.regions.push(region);
|
|
291
|
-
this.emit('region-created', region);
|
|
292
|
-
const regionSubscriptions = [
|
|
293
|
-
region.on('update-end', () => {
|
|
294
|
-
this.avoidOverlapping(region);
|
|
295
|
-
this.emit('region-updated', region);
|
|
296
|
-
}),
|
|
297
|
-
region.on('play', () => {
|
|
298
|
-
this.wavesurfer?.play();
|
|
299
|
-
this.wavesurfer?.setTime(region.start);
|
|
300
|
-
}),
|
|
301
|
-
region.on('click', (e) => {
|
|
302
|
-
this.emit('region-clicked', region, e);
|
|
303
|
-
}),
|
|
304
|
-
// Remove the region from the list when it's removed
|
|
305
|
-
region.once('remove', () => {
|
|
306
|
-
regionSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
307
|
-
this.regions = this.regions.filter((reg) => reg !== region);
|
|
308
|
-
}),
|
|
309
|
-
];
|
|
310
|
-
this.subscriptions.push(...regionSubscriptions);
|
|
311
|
-
}
|
|
312
|
-
/** Create a region with given parameters */
|
|
313
|
-
addRegion(options) {
|
|
314
|
-
if (!this.wavesurfer) {
|
|
315
|
-
throw Error('WaveSurfer is not initialized');
|
|
316
|
-
}
|
|
317
|
-
const duration = this.wavesurfer.getDuration();
|
|
318
|
-
const region = new Region(options, duration);
|
|
319
|
-
if (!duration) {
|
|
320
|
-
this.subscriptions.push(this.wavesurfer.once('ready', (duration) => {
|
|
321
|
-
region._setTotalDuration(duration);
|
|
322
|
-
this.saveRegion(region);
|
|
323
|
-
}));
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
this.saveRegion(region);
|
|
327
|
-
}
|
|
328
|
-
return region;
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Enable creation of regions by dragging on an empty space on the waveform.
|
|
332
|
-
* Returns a function to disable the drag selection.
|
|
333
|
-
*/
|
|
334
|
-
enableDragSelection(options) {
|
|
335
|
-
const wrapper = this.wavesurfer?.getWrapper()?.querySelector('div');
|
|
336
|
-
if (!wrapper)
|
|
337
|
-
return () => undefined;
|
|
338
|
-
let region = null;
|
|
339
|
-
let startX = 0;
|
|
340
|
-
let sumDx = 0;
|
|
341
|
-
return makeDraggable(wrapper,
|
|
342
|
-
// On mousedown
|
|
343
|
-
(x) => (startX = x),
|
|
344
|
-
// On mousemove
|
|
345
|
-
(dx) => {
|
|
346
|
-
if (!this.wavesurfer)
|
|
347
|
-
return;
|
|
348
|
-
if (!region) {
|
|
349
|
-
const duration = this.wavesurfer.getDuration();
|
|
350
|
-
const box = wrapper.getBoundingClientRect();
|
|
351
|
-
let start = ((startX - box.left) / box.width) * duration;
|
|
352
|
-
let end = ((startX + dx - box.left) / box.width) * duration;
|
|
353
|
-
if (start > end)
|
|
354
|
-
[start, end] = [end, start];
|
|
355
|
-
region = new Region({
|
|
356
|
-
...options,
|
|
357
|
-
start,
|
|
358
|
-
end,
|
|
359
|
-
}, duration);
|
|
360
|
-
this.regionsContainer.appendChild(region.element);
|
|
361
|
-
}
|
|
362
|
-
sumDx += dx;
|
|
363
|
-
if (region) {
|
|
364
|
-
const privateRegion = region;
|
|
365
|
-
privateRegion.onUpdate(dx, [sumDx > 0 ? 'end' : 'start']);
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
// On mouseup
|
|
369
|
-
() => {
|
|
370
|
-
if (region) {
|
|
371
|
-
this.saveRegion(region);
|
|
372
|
-
region = null;
|
|
373
|
-
sumDx = 0;
|
|
374
|
-
// Prevent a click event on the waveform
|
|
375
|
-
if (this.wavesurfer) {
|
|
376
|
-
const { interact } = this.wavesurfer.options;
|
|
377
|
-
if (interact) {
|
|
378
|
-
this.wavesurfer.toggleInteraction(false);
|
|
379
|
-
setTimeout(() => this.wavesurfer?.toggleInteraction(interact), 10);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
/** Remove all regions */
|
|
386
|
-
clearRegions() {
|
|
387
|
-
this.regions.forEach((region) => region.remove());
|
|
388
|
-
}
|
|
389
|
-
/** Destroy the plugin and clean up */
|
|
390
|
-
destroy() {
|
|
391
|
-
this.clearRegions();
|
|
392
|
-
super.destroy();
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
export default RegionsPlugin;
|
|
@@ -1,9 +0,0 @@
|
|
|
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;
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/**
|
|
3
|
-
* Calculate FFT - Based on https://github.com/corbanbrook/dsp.js
|
|
4
|
-
*
|
|
5
|
-
* @param {Number} bufferSize Buffer size
|
|
6
|
-
* @param {Number} sampleRate Sample rate
|
|
7
|
-
* @param {Function} windowFunc Window function
|
|
8
|
-
* @param {Number} alpha Alpha channel
|
|
9
|
-
*/
|
|
10
|
-
export default function FFT(bufferSize, sampleRate, windowFunc, alpha) {
|
|
11
|
-
this.bufferSize = bufferSize;
|
|
12
|
-
this.sampleRate = sampleRate;
|
|
13
|
-
this.bandwidth = (2 / bufferSize) * (sampleRate / 2);
|
|
14
|
-
this.sinTable = new Float32Array(bufferSize);
|
|
15
|
-
this.cosTable = new Float32Array(bufferSize);
|
|
16
|
-
this.windowValues = new Float32Array(bufferSize);
|
|
17
|
-
this.reverseTable = new Uint32Array(bufferSize);
|
|
18
|
-
this.peakBand = 0;
|
|
19
|
-
this.peak = 0;
|
|
20
|
-
var i;
|
|
21
|
-
switch (windowFunc) {
|
|
22
|
-
case 'bartlett':
|
|
23
|
-
for (i = 0; i < bufferSize; i++) {
|
|
24
|
-
this.windowValues[i] = (2 / (bufferSize - 1)) * ((bufferSize - 1) / 2 - Math.abs(i - (bufferSize - 1) / 2));
|
|
25
|
-
}
|
|
26
|
-
break;
|
|
27
|
-
case 'bartlettHann':
|
|
28
|
-
for (i = 0; i < bufferSize; i++) {
|
|
29
|
-
this.windowValues[i] =
|
|
30
|
-
0.62 - 0.48 * Math.abs(i / (bufferSize - 1) - 0.5) - 0.38 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1));
|
|
31
|
-
}
|
|
32
|
-
break;
|
|
33
|
-
case 'blackman':
|
|
34
|
-
alpha = alpha || 0.16;
|
|
35
|
-
for (i = 0; i < bufferSize; i++) {
|
|
36
|
-
this.windowValues[i] =
|
|
37
|
-
(1 - alpha) / 2 -
|
|
38
|
-
0.5 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1)) +
|
|
39
|
-
(alpha / 2) * Math.cos((4 * Math.PI * i) / (bufferSize - 1));
|
|
40
|
-
}
|
|
41
|
-
break;
|
|
42
|
-
case 'cosine':
|
|
43
|
-
for (i = 0; i < bufferSize; i++) {
|
|
44
|
-
this.windowValues[i] = Math.cos((Math.PI * i) / (bufferSize - 1) - Math.PI / 2);
|
|
45
|
-
}
|
|
46
|
-
break;
|
|
47
|
-
case 'gauss':
|
|
48
|
-
alpha = alpha || 0.25;
|
|
49
|
-
for (i = 0; i < bufferSize; i++) {
|
|
50
|
-
this.windowValues[i] = Math.pow(Math.E, -0.5 * Math.pow((i - (bufferSize - 1) / 2) / ((alpha * (bufferSize - 1)) / 2), 2));
|
|
51
|
-
}
|
|
52
|
-
break;
|
|
53
|
-
case 'hamming':
|
|
54
|
-
for (i = 0; i < bufferSize; i++) {
|
|
55
|
-
this.windowValues[i] = 0.54 - 0.46 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1));
|
|
56
|
-
}
|
|
57
|
-
break;
|
|
58
|
-
case 'hann':
|
|
59
|
-
case undefined:
|
|
60
|
-
for (i = 0; i < bufferSize; i++) {
|
|
61
|
-
this.windowValues[i] = 0.5 * (1 - Math.cos((Math.PI * 2 * i) / (bufferSize - 1)));
|
|
62
|
-
}
|
|
63
|
-
break;
|
|
64
|
-
case 'lanczoz':
|
|
65
|
-
for (i = 0; i < bufferSize; i++) {
|
|
66
|
-
this.windowValues[i] =
|
|
67
|
-
Math.sin(Math.PI * ((2 * i) / (bufferSize - 1) - 1)) / (Math.PI * ((2 * i) / (bufferSize - 1) - 1));
|
|
68
|
-
}
|
|
69
|
-
break;
|
|
70
|
-
case 'rectangular':
|
|
71
|
-
for (i = 0; i < bufferSize; i++) {
|
|
72
|
-
this.windowValues[i] = 1;
|
|
73
|
-
}
|
|
74
|
-
break;
|
|
75
|
-
case 'triangular':
|
|
76
|
-
for (i = 0; i < bufferSize; i++) {
|
|
77
|
-
this.windowValues[i] = (2 / bufferSize) * (bufferSize / 2 - Math.abs(i - (bufferSize - 1) / 2));
|
|
78
|
-
}
|
|
79
|
-
break;
|
|
80
|
-
default:
|
|
81
|
-
throw Error("No such window function '" + windowFunc + "'");
|
|
82
|
-
}
|
|
83
|
-
var limit = 1;
|
|
84
|
-
var bit = bufferSize >> 1;
|
|
85
|
-
var i;
|
|
86
|
-
while (limit < bufferSize) {
|
|
87
|
-
for (i = 0; i < limit; i++) {
|
|
88
|
-
this.reverseTable[i + limit] = this.reverseTable[i] + bit;
|
|
89
|
-
}
|
|
90
|
-
limit = limit << 1;
|
|
91
|
-
bit = bit >> 1;
|
|
92
|
-
}
|
|
93
|
-
for (i = 0; i < bufferSize; i++) {
|
|
94
|
-
this.sinTable[i] = Math.sin(-Math.PI / i);
|
|
95
|
-
this.cosTable[i] = Math.cos(-Math.PI / i);
|
|
96
|
-
}
|
|
97
|
-
this.calculateSpectrum = function (buffer) {
|
|
98
|
-
// Locally scope variables for speed up
|
|
99
|
-
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);
|
|
100
|
-
var k = Math.floor(Math.log(bufferSize) / Math.LN2);
|
|
101
|
-
if (Math.pow(2, k) !== bufferSize) {
|
|
102
|
-
throw 'Invalid buffer size, must be a power of 2.';
|
|
103
|
-
}
|
|
104
|
-
if (bufferSize !== buffer.length) {
|
|
105
|
-
throw ('Supplied buffer is not the same size as defined FFT. FFT Size: ' +
|
|
106
|
-
bufferSize +
|
|
107
|
-
' Buffer Size: ' +
|
|
108
|
-
buffer.length);
|
|
109
|
-
}
|
|
110
|
-
var halfSize = 1, phaseShiftStepReal, phaseShiftStepImag, currentPhaseShiftReal, currentPhaseShiftImag, off, tr, ti, tmpReal;
|
|
111
|
-
for (var i = 0; i < bufferSize; i++) {
|
|
112
|
-
real[i] = buffer[reverseTable[i]] * this.windowValues[reverseTable[i]];
|
|
113
|
-
imag[i] = 0;
|
|
114
|
-
}
|
|
115
|
-
while (halfSize < bufferSize) {
|
|
116
|
-
phaseShiftStepReal = cosTable[halfSize];
|
|
117
|
-
phaseShiftStepImag = sinTable[halfSize];
|
|
118
|
-
currentPhaseShiftReal = 1;
|
|
119
|
-
currentPhaseShiftImag = 0;
|
|
120
|
-
for (var fftStep = 0; fftStep < halfSize; fftStep++) {
|
|
121
|
-
var i = fftStep;
|
|
122
|
-
while (i < bufferSize) {
|
|
123
|
-
off = i + halfSize;
|
|
124
|
-
tr = currentPhaseShiftReal * real[off] - currentPhaseShiftImag * imag[off];
|
|
125
|
-
ti = currentPhaseShiftReal * imag[off] + currentPhaseShiftImag * real[off];
|
|
126
|
-
real[off] = real[i] - tr;
|
|
127
|
-
imag[off] = imag[i] - ti;
|
|
128
|
-
real[i] += tr;
|
|
129
|
-
imag[i] += ti;
|
|
130
|
-
i += halfSize << 1;
|
|
131
|
-
}
|
|
132
|
-
tmpReal = currentPhaseShiftReal;
|
|
133
|
-
currentPhaseShiftReal = tmpReal * phaseShiftStepReal - currentPhaseShiftImag * phaseShiftStepImag;
|
|
134
|
-
currentPhaseShiftImag = tmpReal * phaseShiftStepImag + currentPhaseShiftImag * phaseShiftStepReal;
|
|
135
|
-
}
|
|
136
|
-
halfSize = halfSize << 1;
|
|
137
|
-
}
|
|
138
|
-
for (var i = 0, N = bufferSize / 2; i < N; i++) {
|
|
139
|
-
rval = real[i];
|
|
140
|
-
ival = imag[i];
|
|
141
|
-
mag = bSi * sqrt(rval * rval + ival * ival);
|
|
142
|
-
if (mag > this.peak) {
|
|
143
|
-
this.peakBand = i;
|
|
144
|
-
this.peak = mag;
|
|
145
|
-
}
|
|
146
|
-
spectrum[i] = mag;
|
|
147
|
-
}
|
|
148
|
-
return spectrum;
|
|
149
|
-
};
|
|
150
|
-
}
|