wavesurfer.js 4.5.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +36 -1
- package/README.md +6 -3
- package/dist/plugin/wavesurfer.cursor.js +39 -34
- package/dist/plugin/wavesurfer.cursor.js.map +1 -1
- package/dist/plugin/wavesurfer.cursor.min.js +2 -2
- package/dist/plugin/wavesurfer.cursor.min.js.map +1 -1
- package/dist/plugin/wavesurfer.elan.js +29 -27
- package/dist/plugin/wavesurfer.elan.js.map +1 -1
- package/dist/plugin/wavesurfer.elan.min.js +2 -2
- package/dist/plugin/wavesurfer.elan.min.js.map +1 -1
- package/dist/plugin/wavesurfer.markers.js +381 -0
- package/dist/plugin/wavesurfer.markers.js.map +1 -0
- package/dist/plugin/wavesurfer.markers.min.js +7 -0
- package/dist/plugin/wavesurfer.markers.min.js.map +1 -0
- package/dist/plugin/wavesurfer.mediasession.js +29 -27
- package/dist/plugin/wavesurfer.mediasession.js.map +1 -1
- package/dist/plugin/wavesurfer.mediasession.min.js +2 -2
- package/dist/plugin/wavesurfer.mediasession.min.js.map +1 -1
- package/dist/plugin/wavesurfer.microphone.js +29 -27
- package/dist/plugin/wavesurfer.microphone.js.map +1 -1
- package/dist/plugin/wavesurfer.microphone.min.js +2 -2
- package/dist/plugin/wavesurfer.microphone.min.js.map +1 -1
- package/dist/plugin/wavesurfer.minimap.js +30 -28
- package/dist/plugin/wavesurfer.minimap.js.map +1 -1
- package/dist/plugin/wavesurfer.minimap.min.js +2 -2
- package/dist/plugin/wavesurfer.minimap.min.js.map +1 -1
- package/dist/plugin/wavesurfer.playhead.js +324 -0
- package/dist/plugin/wavesurfer.playhead.js.map +1 -0
- package/dist/plugin/wavesurfer.playhead.min.js +7 -0
- package/dist/plugin/wavesurfer.playhead.min.js.map +1 -0
- package/dist/plugin/wavesurfer.regions.js +152 -126
- package/dist/plugin/wavesurfer.regions.js.map +1 -1
- package/dist/plugin/wavesurfer.regions.min.js +2 -2
- package/dist/plugin/wavesurfer.regions.min.js.map +1 -1
- package/dist/plugin/wavesurfer.spectrogram.js +97 -89
- package/dist/plugin/wavesurfer.spectrogram.js.map +1 -1
- package/dist/plugin/wavesurfer.spectrogram.min.js +2 -2
- package/dist/plugin/wavesurfer.spectrogram.min.js.map +1 -1
- package/dist/plugin/wavesurfer.timeline.js +46 -32
- package/dist/plugin/wavesurfer.timeline.js.map +1 -1
- package/dist/plugin/wavesurfer.timeline.min.js +2 -2
- package/dist/plugin/wavesurfer.timeline.min.js.map +1 -1
- package/dist/wavesurfer-html-init.js +9 -5
- package/dist/wavesurfer-html-init.js.map +1 -1
- package/dist/wavesurfer-html-init.min.js +2 -2
- package/dist/wavesurfer-html-init.min.js.map +1 -1
- package/dist/wavesurfer.js +350 -166
- package/dist/wavesurfer.js.map +1 -1
- package/dist/wavesurfer.min.js +2 -2
- package/dist/wavesurfer.min.js.map +1 -1
- package/package.json +19 -16
- package/src/drawer.canvasentry.js +16 -0
- package/src/drawer.js +30 -21
- package/src/drawer.multicanvas.js +63 -39
- package/src/plugin/cursor/index.js +3 -1
- package/src/plugin/markers/index.js +282 -0
- package/src/plugin/playhead/index.js +226 -0
- package/src/plugin/regions/index.js +28 -11
- package/src/plugin/regions/region.js +83 -70
- package/src/plugin/spectrogram/index.js +65 -61
- package/src/plugin/timeline/index.js +20 -11
- package/src/util/index.js +1 -0
- package/src/util/orientation.js +98 -0
- package/src/wavesurfer.js +18 -9
- package/src/webaudio.js +10 -9
|
@@ -15,6 +15,7 @@ export class Region {
|
|
|
15
15
|
this.util = ws.util;
|
|
16
16
|
this.style = this.util.style;
|
|
17
17
|
this.regionsUtil = regionsUtils;
|
|
18
|
+
this.vertical = ws.drawer.params.vertical;
|
|
18
19
|
|
|
19
20
|
this.id = params.id == null ? ws.util.getId() : params.id;
|
|
20
21
|
this.start = Number(params.start) || 0;
|
|
@@ -42,6 +43,7 @@ export class Region {
|
|
|
42
43
|
this.handleRightEl = null;
|
|
43
44
|
this.data = params.data || {};
|
|
44
45
|
this.attributes = params.attributes || {};
|
|
46
|
+
this.showTooltip = params.showTooltip ?? true;
|
|
45
47
|
|
|
46
48
|
this.maxLength = params.maxLength;
|
|
47
49
|
// It assumes the minLength parameter value, or the regionsMinLength parameter value, if the first one not provided
|
|
@@ -129,7 +131,7 @@ export class Region {
|
|
|
129
131
|
/* Remove a single region. */
|
|
130
132
|
remove() {
|
|
131
133
|
if (this.element) {
|
|
132
|
-
this.wrapper.removeChild(this.element);
|
|
134
|
+
this.wrapper.removeChild(this.element.domElement);
|
|
133
135
|
this.element = null;
|
|
134
136
|
this.fireEvent('remove');
|
|
135
137
|
this.wavesurfer.un('zoom', this._onRedraw);
|
|
@@ -168,20 +170,25 @@ export class Region {
|
|
|
168
170
|
|
|
169
171
|
/* Render a region as a DOM element. */
|
|
170
172
|
render() {
|
|
171
|
-
|
|
173
|
+
this.element = this.util.withOrientation(
|
|
174
|
+
this.wrapper.appendChild(document.createElement('region')),
|
|
175
|
+
this.vertical
|
|
176
|
+
);
|
|
172
177
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
this.element.className = 'wavesurfer-region';
|
|
179
|
+
if (this.showTooltip) {
|
|
180
|
+
this.element.title = this.formatTime(this.start, this.end);
|
|
181
|
+
}
|
|
182
|
+
this.element.setAttribute('data-id', this.id);
|
|
176
183
|
|
|
177
184
|
for (const attrname in this.attributes) {
|
|
178
|
-
|
|
185
|
+
this.element.setAttribute(
|
|
179
186
|
'data-region-' + attrname,
|
|
180
187
|
this.attributes[attrname]
|
|
181
188
|
);
|
|
182
189
|
}
|
|
183
190
|
|
|
184
|
-
this.style(
|
|
191
|
+
this.style(this.element, {
|
|
185
192
|
position: 'absolute',
|
|
186
193
|
zIndex: 2,
|
|
187
194
|
height: this.regionHeight,
|
|
@@ -190,11 +197,13 @@ export class Region {
|
|
|
190
197
|
|
|
191
198
|
/* Resize handles */
|
|
192
199
|
if (this.resize) {
|
|
193
|
-
this.handleLeftEl =
|
|
194
|
-
document.createElement('handle')
|
|
200
|
+
this.handleLeftEl = this.util.withOrientation(
|
|
201
|
+
this.element.appendChild(document.createElement('handle')),
|
|
202
|
+
this.vertical
|
|
195
203
|
);
|
|
196
|
-
this.handleRightEl =
|
|
197
|
-
document.createElement('handle')
|
|
204
|
+
this.handleRightEl = this.util.withOrientation(
|
|
205
|
+
this.element.appendChild(document.createElement('handle')),
|
|
206
|
+
this.vertical
|
|
198
207
|
);
|
|
199
208
|
|
|
200
209
|
this.handleLeftEl.className = 'wavesurfer-handle wavesurfer-handle-start';
|
|
@@ -202,7 +211,7 @@ export class Region {
|
|
|
202
211
|
|
|
203
212
|
// Default CSS properties for both handles.
|
|
204
213
|
const css = {
|
|
205
|
-
cursor: 'col-resize',
|
|
214
|
+
cursor: this.vertical ? 'row-resize' : 'col-resize',
|
|
206
215
|
position: 'absolute',
|
|
207
216
|
top: '0px',
|
|
208
217
|
width: '2px',
|
|
@@ -213,7 +222,11 @@ export class Region {
|
|
|
213
222
|
// Merge CSS properties per handle.
|
|
214
223
|
const handleLeftCss =
|
|
215
224
|
this.handleStyle.left !== 'none'
|
|
216
|
-
? Object.assign(
|
|
225
|
+
? Object.assign(
|
|
226
|
+
{ left: '0px' },
|
|
227
|
+
css,
|
|
228
|
+
this.handleStyle.left
|
|
229
|
+
)
|
|
217
230
|
: null;
|
|
218
231
|
const handleRightCss =
|
|
219
232
|
this.handleStyle.right !== 'none'
|
|
@@ -233,9 +246,8 @@ export class Region {
|
|
|
233
246
|
}
|
|
234
247
|
}
|
|
235
248
|
|
|
236
|
-
this.element = this.wrapper.appendChild(regionEl);
|
|
237
249
|
this.updateRender();
|
|
238
|
-
this.bindEvents(
|
|
250
|
+
this.bindEvents();
|
|
239
251
|
}
|
|
240
252
|
|
|
241
253
|
formatTime(start, end) {
|
|
@@ -301,7 +313,9 @@ export class Region {
|
|
|
301
313
|
);
|
|
302
314
|
}
|
|
303
315
|
|
|
304
|
-
|
|
316
|
+
if (this.showTooltip) {
|
|
317
|
+
this.element.title = this.formatTime(this.start, this.end);
|
|
318
|
+
}
|
|
305
319
|
}
|
|
306
320
|
}
|
|
307
321
|
|
|
@@ -407,20 +421,21 @@ export class Region {
|
|
|
407
421
|
let regionRightHalfTime;
|
|
408
422
|
|
|
409
423
|
// Scroll when the user is dragging within the threshold
|
|
410
|
-
const edgeScroll = (
|
|
424
|
+
const edgeScroll = (event) => {
|
|
425
|
+
let orientedEvent = this.util.withOrientation(event, this.vertical);
|
|
411
426
|
const duration = this.wavesurfer.getDuration();
|
|
412
427
|
if (!scrollDirection || (!drag && !resize)) {
|
|
413
428
|
return;
|
|
414
429
|
}
|
|
415
430
|
|
|
416
|
-
const x =
|
|
431
|
+
const x = orientedEvent.clientX;
|
|
417
432
|
let distanceBetweenCursorAndWrapperEdge = 0;
|
|
418
433
|
let regionHalfTimeWidth = 0;
|
|
419
434
|
let adjustment = 0;
|
|
420
435
|
|
|
421
436
|
// Get the currently selected time according to the mouse position
|
|
422
437
|
let time = this.regionsUtil.getRegionSnapToGridValue(
|
|
423
|
-
this.wavesurfer.drawer.handleEvent(
|
|
438
|
+
this.wavesurfer.drawer.handleEvent(event) * duration
|
|
424
439
|
);
|
|
425
440
|
|
|
426
441
|
if (drag) {
|
|
@@ -461,26 +476,28 @@ export class Region {
|
|
|
461
476
|
}
|
|
462
477
|
|
|
463
478
|
// Don't edgescroll if region has reached min or max limit
|
|
479
|
+
const wrapperScrollLeft = this.wrapper.scrollLeft;
|
|
480
|
+
|
|
464
481
|
if (scrollDirection === -1) {
|
|
465
|
-
if (Math.round(
|
|
482
|
+
if (Math.round(wrapperScrollLeft) === 0) {
|
|
466
483
|
return;
|
|
467
484
|
}
|
|
468
485
|
|
|
469
|
-
if (Math.round(
|
|
486
|
+
if (Math.round(wrapperScrollLeft - regionHalfTimeWidth + distanceBetweenCursorAndWrapperEdge) <= 0) {
|
|
470
487
|
return;
|
|
471
488
|
}
|
|
472
489
|
} else {
|
|
473
|
-
if (Math.round(
|
|
490
|
+
if (Math.round(wrapperScrollLeft) === maxScroll) {
|
|
474
491
|
return;
|
|
475
492
|
}
|
|
476
493
|
|
|
477
|
-
if (Math.round(
|
|
494
|
+
if (Math.round(wrapperScrollLeft + regionHalfTimeWidth - distanceBetweenCursorAndWrapperEdge) >= maxScroll) {
|
|
478
495
|
return;
|
|
479
496
|
}
|
|
480
497
|
}
|
|
481
498
|
|
|
482
499
|
// Update scroll position
|
|
483
|
-
let scrollLeft =
|
|
500
|
+
let scrollLeft = wrapperScrollLeft - adjustment + scrollSpeed * scrollDirection;
|
|
484
501
|
|
|
485
502
|
if (scrollDirection === -1) {
|
|
486
503
|
const calculatedLeft = Math.max(0 + regionHalfTimeWidth - distanceBetweenCursorAndWrapperEdge, scrollLeft);
|
|
@@ -498,26 +515,26 @@ export class Region {
|
|
|
498
515
|
|
|
499
516
|
// Repeat
|
|
500
517
|
window.requestAnimationFrame(() => {
|
|
501
|
-
edgeScroll(
|
|
518
|
+
edgeScroll(event);
|
|
502
519
|
});
|
|
503
520
|
};
|
|
504
521
|
|
|
505
|
-
const onDown = (
|
|
522
|
+
const onDown = (event) => {
|
|
506
523
|
const duration = this.wavesurfer.getDuration();
|
|
507
|
-
if (
|
|
524
|
+
if (event.touches && event.touches.length > 1) {
|
|
508
525
|
return;
|
|
509
526
|
}
|
|
510
|
-
touchId =
|
|
527
|
+
touchId = event.targetTouches ? event.targetTouches[0].identifier : null;
|
|
511
528
|
|
|
512
529
|
// stop the event propagation, if this region is resizable or draggable
|
|
513
530
|
// and the event is therefore handled here.
|
|
514
531
|
if (this.drag || this.resize) {
|
|
515
|
-
|
|
532
|
+
event.stopPropagation();
|
|
516
533
|
}
|
|
517
534
|
|
|
518
535
|
// Store the selected startTime we begun dragging or resizing
|
|
519
536
|
startTime = this.regionsUtil.getRegionSnapToGridValue(
|
|
520
|
-
this.wavesurfer.drawer.handleEvent(
|
|
537
|
+
this.wavesurfer.drawer.handleEvent(event, true) * duration
|
|
521
538
|
);
|
|
522
539
|
|
|
523
540
|
// Store the selected point of contact when we begin dragging
|
|
@@ -526,13 +543,17 @@ export class Region {
|
|
|
526
543
|
|
|
527
544
|
// Store for scroll calculations
|
|
528
545
|
maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;
|
|
529
|
-
|
|
546
|
+
|
|
547
|
+
wrapperRect = this.util.withOrientation(
|
|
548
|
+
this.wrapper.getBoundingClientRect(),
|
|
549
|
+
this.vertical
|
|
550
|
+
);
|
|
530
551
|
|
|
531
552
|
this.isResizing = false;
|
|
532
553
|
this.isDragging = false;
|
|
533
|
-
if (
|
|
554
|
+
if (event.target.tagName.toLowerCase() === 'handle') {
|
|
534
555
|
this.isResizing = true;
|
|
535
|
-
resize =
|
|
556
|
+
resize = event.target.classList.contains('wavesurfer-handle-start')
|
|
536
557
|
? 'start'
|
|
537
558
|
: 'end';
|
|
538
559
|
} else {
|
|
@@ -541,8 +562,8 @@ export class Region {
|
|
|
541
562
|
resize = false;
|
|
542
563
|
}
|
|
543
564
|
};
|
|
544
|
-
const onUp = (
|
|
545
|
-
if (
|
|
565
|
+
const onUp = (event) => {
|
|
566
|
+
if (event.touches && event.touches.length > 1) {
|
|
546
567
|
return;
|
|
547
568
|
}
|
|
548
569
|
|
|
@@ -557,17 +578,18 @@ export class Region {
|
|
|
557
578
|
if (updated) {
|
|
558
579
|
updated = false;
|
|
559
580
|
this.util.preventClick();
|
|
560
|
-
this.fireEvent('update-end',
|
|
561
|
-
this.wavesurfer.fireEvent('region-update-end', this,
|
|
581
|
+
this.fireEvent('update-end', event);
|
|
582
|
+
this.wavesurfer.fireEvent('region-update-end', this, event);
|
|
562
583
|
}
|
|
563
584
|
};
|
|
564
|
-
const onMove = (
|
|
585
|
+
const onMove = (event) => {
|
|
565
586
|
const duration = this.wavesurfer.getDuration();
|
|
587
|
+
let orientedEvent = this.util.withOrientation(event, this.vertical);
|
|
566
588
|
|
|
567
|
-
if (
|
|
589
|
+
if (event.touches && event.touches.length > 1) {
|
|
568
590
|
return;
|
|
569
591
|
}
|
|
570
|
-
if (
|
|
592
|
+
if (event.targetTouches && event.targetTouches[0].identifier != touchId) {
|
|
571
593
|
return;
|
|
572
594
|
}
|
|
573
595
|
if (!drag && !resize) {
|
|
@@ -576,7 +598,7 @@ export class Region {
|
|
|
576
598
|
|
|
577
599
|
const oldTime = startTime;
|
|
578
600
|
let time = this.regionsUtil.getRegionSnapToGridValue(
|
|
579
|
-
this.wavesurfer.drawer.handleEvent(
|
|
601
|
+
this.wavesurfer.drawer.handleEvent(event) * duration
|
|
580
602
|
);
|
|
581
603
|
|
|
582
604
|
if (drag) {
|
|
@@ -634,36 +656,22 @@ export class Region {
|
|
|
634
656
|
}
|
|
635
657
|
|
|
636
658
|
if (
|
|
637
|
-
this.scroll &&
|
|
638
|
-
container.clientWidth < this.wrapper.scrollWidth
|
|
659
|
+
this.scroll && container.clientWidth < this.wrapper.scrollWidth
|
|
639
660
|
) {
|
|
640
661
|
// Triggering edgescroll from within edgeScrollWidth
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
scrollDirection = 1;
|
|
649
|
-
} else {
|
|
650
|
-
scrollDirection = null;
|
|
651
|
-
}
|
|
662
|
+
let x = orientedEvent.clientX;
|
|
663
|
+
|
|
664
|
+
// Check direction
|
|
665
|
+
if (x < wrapperRect.left + this.edgeScrollWidth) {
|
|
666
|
+
scrollDirection = -1;
|
|
667
|
+
} else if (x > wrapperRect.right - this.edgeScrollWidth) {
|
|
668
|
+
scrollDirection = 1;
|
|
652
669
|
} else {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
// Check direction
|
|
656
|
-
if (x < wrapperRect.left + this.edgeScrollWidth) {
|
|
657
|
-
scrollDirection = -1;
|
|
658
|
-
} else if (x > wrapperRect.right - this.edgeScrollWidth) {
|
|
659
|
-
scrollDirection = 1;
|
|
660
|
-
} else {
|
|
661
|
-
scrollDirection = null;
|
|
662
|
-
}
|
|
670
|
+
scrollDirection = null;
|
|
663
671
|
}
|
|
664
672
|
|
|
665
673
|
if (scrollDirection) {
|
|
666
|
-
edgeScroll(
|
|
674
|
+
edgeScroll(event);
|
|
667
675
|
}
|
|
668
676
|
}
|
|
669
677
|
};
|
|
@@ -672,20 +680,20 @@ export class Region {
|
|
|
672
680
|
this.element.addEventListener('touchstart', onDown);
|
|
673
681
|
|
|
674
682
|
document.body.addEventListener('mousemove', onMove);
|
|
675
|
-
document.body.addEventListener('touchmove', onMove);
|
|
683
|
+
document.body.addEventListener('touchmove', onMove, {passive: false});
|
|
676
684
|
|
|
677
|
-
document.
|
|
685
|
+
document.addEventListener('mouseup', onUp);
|
|
678
686
|
document.body.addEventListener('touchend', onUp);
|
|
679
687
|
|
|
680
688
|
this.on('remove', () => {
|
|
681
|
-
document.
|
|
689
|
+
document.removeEventListener('mouseup', onUp);
|
|
682
690
|
document.body.removeEventListener('touchend', onUp);
|
|
683
691
|
document.body.removeEventListener('mousemove', onMove);
|
|
684
692
|
document.body.removeEventListener('touchmove', onMove);
|
|
685
693
|
});
|
|
686
694
|
|
|
687
695
|
this.wavesurfer.on('destroy', () => {
|
|
688
|
-
document.
|
|
696
|
+
document.removeEventListener('mouseup', onUp);
|
|
689
697
|
document.body.removeEventListener('touchend', onUp);
|
|
690
698
|
});
|
|
691
699
|
}
|
|
@@ -750,7 +758,12 @@ export class Region {
|
|
|
750
758
|
}
|
|
751
759
|
|
|
752
760
|
updateHandlesResize(resize) {
|
|
753
|
-
|
|
761
|
+
let cursorStyle;
|
|
762
|
+
if (resize) {
|
|
763
|
+
cursorStyle = this.vertical ? 'row-resize' : 'col-resize';
|
|
764
|
+
} else {
|
|
765
|
+
cursorStyle = 'auto';
|
|
766
|
+
}
|
|
754
767
|
|
|
755
768
|
this.handleLeftEl && this.style(this.handleLeftEl, { cursor: cursorStyle });
|
|
756
769
|
this.handleRightEl && this.style(this.handleRightEl, { cursor: cursorStyle });
|
|
@@ -254,30 +254,32 @@ export default class SpectrogramPlugin {
|
|
|
254
254
|
const width = my.width;
|
|
255
255
|
const pixels = my.resample(frequenciesData);
|
|
256
256
|
const heightFactor = my.buffer ? 2 / my.buffer.numberOfChannels : 1;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
for (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
257
|
+
if (spectrCc) {
|
|
258
|
+
const imageData = spectrCc.createImageData(width, height);
|
|
259
|
+
let i;
|
|
260
|
+
let j;
|
|
261
|
+
let k;
|
|
262
|
+
|
|
263
|
+
for (i = 0; i < pixels.length; i++) {
|
|
264
|
+
for (j = 0; j < pixels[i].length; j++) {
|
|
265
|
+
const colorMap = my.colorMap[pixels[i][j]];
|
|
266
|
+
/* eslint-disable max-depth */
|
|
267
|
+
for (k = 0; k < heightFactor; k++) {
|
|
268
|
+
let y = height - j * heightFactor;
|
|
269
|
+
if (heightFactor === 2 && k === 1) {
|
|
270
|
+
y--;
|
|
271
|
+
}
|
|
272
|
+
const redIndex = y * (width * 4) + i * 4;
|
|
273
|
+
imageData.data[redIndex] = colorMap[0] * 255;
|
|
274
|
+
imageData.data[redIndex + 1] = colorMap[1] * 255;
|
|
275
|
+
imageData.data[redIndex + 2] = colorMap[2] * 255;
|
|
276
|
+
imageData.data[redIndex + 3] = colorMap[3] * 255;
|
|
270
277
|
}
|
|
271
|
-
|
|
272
|
-
imageData.data[redIndex] = colorMap[0] * 255;
|
|
273
|
-
imageData.data[redIndex + 1] = colorMap[1] * 255;
|
|
274
|
-
imageData.data[redIndex + 2] = colorMap[2] * 255;
|
|
275
|
-
imageData.data[redIndex + 3] = colorMap[3] * 255;
|
|
278
|
+
/* eslint-enable max-depth */
|
|
276
279
|
}
|
|
277
|
-
/* eslint-enable max-depth */
|
|
278
280
|
}
|
|
281
|
+
spectrCc.putImageData(imageData, 0, 0);
|
|
279
282
|
}
|
|
280
|
-
spectrCc.putImageData(imageData, 0, 0);
|
|
281
283
|
}
|
|
282
284
|
|
|
283
285
|
getFrequencies(callback) {
|
|
@@ -378,47 +380,49 @@ export default class SpectrogramPlugin {
|
|
|
378
380
|
this.labelsEl.height = this.height;
|
|
379
381
|
this.labelsEl.width = bgWidth;
|
|
380
382
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
383
|
+
if (ctx) {
|
|
384
|
+
// fill background
|
|
385
|
+
ctx.fillStyle = bgFill;
|
|
386
|
+
ctx.fillRect(0, 0, bgWidth, getMaxY);
|
|
387
|
+
ctx.fill();
|
|
388
|
+
let i;
|
|
389
|
+
|
|
390
|
+
// render labels
|
|
391
|
+
for (i = 0; i <= labelIndex; i++) {
|
|
392
|
+
ctx.textAlign = textAlign;
|
|
393
|
+
ctx.textBaseline = 'middle';
|
|
394
|
+
|
|
395
|
+
const freq = freqStart + step * i;
|
|
396
|
+
const index = Math.round(
|
|
397
|
+
(freq / (this.sampleRate / 2)) * this.fftSamples
|
|
398
|
+
);
|
|
399
|
+
const label = this.freqType(freq);
|
|
400
|
+
const units = this.unitType(freq);
|
|
401
|
+
const yLabelOffset = 2;
|
|
402
|
+
const x = 16;
|
|
403
|
+
let y;
|
|
404
|
+
|
|
405
|
+
if (i == 0) {
|
|
406
|
+
y = getMaxY + i - 10;
|
|
407
|
+
// unit label
|
|
408
|
+
ctx.fillStyle = textColorUnit;
|
|
409
|
+
ctx.font = fontSizeUnit + ' ' + fontType;
|
|
410
|
+
ctx.fillText(units, x + 24, y);
|
|
411
|
+
// freq label
|
|
412
|
+
ctx.fillStyle = textColorFreq;
|
|
413
|
+
ctx.font = fontSizeFreq + ' ' + fontType;
|
|
414
|
+
ctx.fillText(label, x, y);
|
|
415
|
+
} else {
|
|
416
|
+
y = getMaxY - i * 50 + yLabelOffset;
|
|
417
|
+
// unit label
|
|
418
|
+
ctx.fillStyle = textColorUnit;
|
|
419
|
+
ctx.font = fontSizeUnit + ' ' + fontType;
|
|
420
|
+
ctx.fillText(units, x + 24, y);
|
|
421
|
+
// freq label
|
|
422
|
+
ctx.fillStyle = textColorFreq;
|
|
423
|
+
ctx.font = fontSizeFreq + ' ' + fontType;
|
|
424
|
+
ctx.fillText(label, x, y);
|
|
425
|
+
}
|
|
422
426
|
}
|
|
423
427
|
}
|
|
424
428
|
}
|
|
@@ -434,7 +434,10 @@ export default class TimelinePlugin {
|
|
|
434
434
|
*/
|
|
435
435
|
setFillStyles(fillStyle) {
|
|
436
436
|
this.canvases.forEach(canvas => {
|
|
437
|
-
canvas.getContext('2d')
|
|
437
|
+
const context = canvas.getContext('2d');
|
|
438
|
+
if (context) {
|
|
439
|
+
context.fillStyle = fillStyle;
|
|
440
|
+
}
|
|
438
441
|
});
|
|
439
442
|
}
|
|
440
443
|
|
|
@@ -445,7 +448,10 @@ export default class TimelinePlugin {
|
|
|
445
448
|
*/
|
|
446
449
|
setFonts(font) {
|
|
447
450
|
this.canvases.forEach(canvas => {
|
|
448
|
-
canvas.getContext('2d')
|
|
451
|
+
const context = canvas.getContext('2d');
|
|
452
|
+
if (context) {
|
|
453
|
+
context.font = font;
|
|
454
|
+
}
|
|
449
455
|
});
|
|
450
456
|
}
|
|
451
457
|
|
|
@@ -471,14 +477,17 @@ export default class TimelinePlugin {
|
|
|
471
477
|
};
|
|
472
478
|
|
|
473
479
|
if (intersection.x1 < intersection.x2) {
|
|
474
|
-
canvas
|
|
475
|
-
.getContext('2d')
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
480
|
+
const context = canvas
|
|
481
|
+
.getContext('2d');
|
|
482
|
+
if (context) {
|
|
483
|
+
context
|
|
484
|
+
.fillRect(
|
|
485
|
+
intersection.x1 - leftOffset,
|
|
486
|
+
intersection.y1,
|
|
487
|
+
intersection.x2 - intersection.x1,
|
|
488
|
+
intersection.y2 - intersection.y1
|
|
489
|
+
);
|
|
490
|
+
}
|
|
482
491
|
}
|
|
483
492
|
});
|
|
484
493
|
}
|
|
@@ -502,7 +511,7 @@ export default class TimelinePlugin {
|
|
|
502
511
|
return;
|
|
503
512
|
}
|
|
504
513
|
|
|
505
|
-
if (xOffset + canvasWidth > x) {
|
|
514
|
+
if (xOffset + canvasWidth > x && context) {
|
|
506
515
|
textWidth = context.measureText(text).width;
|
|
507
516
|
context.fillText(text, x - xOffset, y);
|
|
508
517
|
}
|
package/src/util/index.js
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const verticalPropMap = {
|
|
2
|
+
width: 'height',
|
|
3
|
+
height: 'width',
|
|
4
|
+
|
|
5
|
+
overflowX: 'overflowY',
|
|
6
|
+
overflowY: 'overflowX',
|
|
7
|
+
|
|
8
|
+
clientWidth: 'clientHeight',
|
|
9
|
+
clientHeight: 'clientWidth',
|
|
10
|
+
|
|
11
|
+
clientX: 'clientY',
|
|
12
|
+
clientY: 'clientX',
|
|
13
|
+
|
|
14
|
+
scrollWidth: 'scrollHeight',
|
|
15
|
+
scrollLeft: 'scrollTop',
|
|
16
|
+
|
|
17
|
+
offsetLeft: 'offsetTop',
|
|
18
|
+
offsetTop: 'offsetLeft',
|
|
19
|
+
offsetHeight: 'offsetWidth',
|
|
20
|
+
offsetWidth: 'offsetHeight',
|
|
21
|
+
|
|
22
|
+
left: 'top',
|
|
23
|
+
right: 'bottom',
|
|
24
|
+
top: 'left',
|
|
25
|
+
bottom: 'right',
|
|
26
|
+
|
|
27
|
+
borderRightStyle: 'borderBottomStyle',
|
|
28
|
+
borderRightWidth: 'borderBottomWidth',
|
|
29
|
+
borderRightColor: 'borderBottomColor'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Convert a horizontally-oriented property name to a vertical one.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} prop A property name
|
|
36
|
+
* @param {bool} vertical Whether the element is oriented vertically
|
|
37
|
+
* @returns {string} prop, converted appropriately
|
|
38
|
+
*/
|
|
39
|
+
function mapProp(prop, vertical) {
|
|
40
|
+
if (Object.prototype.hasOwnProperty.call(verticalPropMap, prop)) {
|
|
41
|
+
return vertical ? verticalPropMap[prop] : prop;
|
|
42
|
+
} else {
|
|
43
|
+
return prop;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const isProxy = Symbol("isProxy");
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns an appropriately oriented object based on vertical.
|
|
51
|
+
* If vertical is true, attribute getting and setting will be mapped through
|
|
52
|
+
* verticalPropMap, so that e.g. getting the object's .width will give its
|
|
53
|
+
* .height instead.
|
|
54
|
+
* Certain methods of an oriented object will return oriented objects as well.
|
|
55
|
+
* Oriented objects can't be added to the DOM directly since they are Proxy objects
|
|
56
|
+
* and thus fail typechecks. Use domElement to get the actual element for this.
|
|
57
|
+
*
|
|
58
|
+
* @param {object} target The object to be wrapped and oriented
|
|
59
|
+
* @param {bool} vertical Whether the element is oriented vertically
|
|
60
|
+
* @returns {Proxy} An oriented object with attr translation via verticalAttrMap
|
|
61
|
+
* @since 5.0.0
|
|
62
|
+
*/
|
|
63
|
+
export default function withOrientation(target, vertical) {
|
|
64
|
+
if (target[isProxy]) {
|
|
65
|
+
return target;
|
|
66
|
+
} else {
|
|
67
|
+
return new Proxy(
|
|
68
|
+
target, {
|
|
69
|
+
get: function(obj, prop, receiver) {
|
|
70
|
+
if (prop === isProxy) {
|
|
71
|
+
return true;
|
|
72
|
+
} else if (prop === 'domElement') {
|
|
73
|
+
return obj;
|
|
74
|
+
} else if (prop === 'style') {
|
|
75
|
+
return withOrientation(obj.style, vertical);
|
|
76
|
+
} else if (prop === 'canvas') {
|
|
77
|
+
return withOrientation(obj.canvas, vertical);
|
|
78
|
+
} else if (prop === 'getBoundingClientRect') {
|
|
79
|
+
return function(...args) {
|
|
80
|
+
return withOrientation(obj.getBoundingClientRect(...args), vertical);
|
|
81
|
+
};
|
|
82
|
+
} else if (prop === 'getContext') {
|
|
83
|
+
return function(...args) {
|
|
84
|
+
return withOrientation(obj.getContext(...args), vertical);
|
|
85
|
+
};
|
|
86
|
+
} else {
|
|
87
|
+
let value = obj[mapProp(prop, vertical)];
|
|
88
|
+
return typeof value == 'function' ? value.bind(obj) : value;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
set: function(obj, prop, value) {
|
|
92
|
+
obj[mapProp(prop, vertical)] = value;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|