source: gs3-extensions/mars-src/trunk/src/wavesurfer-renderer/drawer.spectrummulticanvas.js@ 34669

Last change on this file since 34669 was 34669, checked in by davidb, 3 years ago

Next step in development of spectrum renderer: now inherits from MultiCanvas

File size: 30.2 KB
Line 
1import MultiCanvas from './drawer.multicanvas';
2import * as util from './util';
3import CanvasEntry from './drawer.canvasentry';
4
5/**
6 * Calculate FFT - Based on https://github.com/corbanbrook/dsp.js
7 */
8/* eslint-disable complexity, no-redeclare, no-var, one-var */
9const FFT = function(bufferSize, sampleRate, windowFunc, alpha) {
10 this.bufferSize = bufferSize;
11 this.sampleRate = sampleRate;
12 this.bandwidth = (2 / bufferSize) * (sampleRate / 2);
13
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
19 this.peakBand = 0;
20 this.peak = 0;
21
22 var i;
23 switch (windowFunc) {
24 case 'bartlett':
25 for (i = 0; i < bufferSize; i++) {
26 this.windowValues[i] =
27 (2 / (bufferSize - 1)) *
28 ((bufferSize - 1) / 2 - Math.abs(i - (bufferSize - 1) / 2));
29 }
30 break;
31 case 'bartlettHann':
32 for (i = 0; i < bufferSize; i++) {
33 this.windowValues[i] =
34 0.62 -
35 0.48 * Math.abs(i / (bufferSize - 1) - 0.5) -
36 0.38 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1));
37 }
38 break;
39 case 'blackman':
40 alpha = alpha || 0.16;
41 for (i = 0; i < bufferSize; i++) {
42 this.windowValues[i] =
43 (1 - alpha) / 2 -
44 0.5 * Math.cos((Math.PI * 2 * i) / (bufferSize - 1)) +
45 (alpha / 2) *
46 Math.cos((4 * Math.PI * i) / (bufferSize - 1));
47 }
48 break;
49 case 'cosine':
50 for (i = 0; i < bufferSize; i++) {
51 this.windowValues[i] = Math.cos(
52 (Math.PI * i) / (bufferSize - 1) - Math.PI / 2
53 );
54 }
55 break;
56 case 'gauss':
57 alpha = alpha || 0.25;
58 for (i = 0; i < bufferSize; i++) {
59 this.windowValues[i] = Math.pow(
60 Math.E,
61 -0.5 *
62 Math.pow(
63 (i - (bufferSize - 1) / 2) /
64 ((alpha * (bufferSize - 1)) / 2),
65 2
66 )
67 );
68 }
69 break;
70 case 'hamming':
71 for (i = 0; i < bufferSize; i++) {
72 this.windowValues[i] =
73 0.54 -
74 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] =
81 0.5 * (1 - Math.cos((Math.PI * 2 * i) / (bufferSize - 1)));
82 }
83 break;
84 case 'lanczoz':
85 for (i = 0; i < bufferSize; i++) {
86 this.windowValues[i] =
87 Math.sin(Math.PI * ((2 * i) / (bufferSize - 1) - 1)) /
88 (Math.PI * ((2 * i) / (bufferSize - 1) - 1));
89 }
90 break;
91 case 'rectangular':
92 for (i = 0; i < bufferSize; i++) {
93 this.windowValues[i] = 1;
94 }
95 break;
96 case 'triangular':
97 for (i = 0; i < bufferSize; i++) {
98 this.windowValues[i] =
99 (2 / bufferSize) *
100 (bufferSize / 2 - Math.abs(i - (bufferSize - 1) / 2));
101 }
102 break;
103 default:
104 throw Error("No such window function '" + windowFunc + "'");
105 }
106
107 var limit = 1;
108 var bit = bufferSize >> 1;
109 var i;
110
111 while (limit < bufferSize) {
112 for (i = 0; i < limit; i++) {
113 this.reverseTable[i + limit] = this.reverseTable[i] + bit;
114 }
115
116 limit = limit << 1;
117 bit = bit >> 1;
118 }
119
120 for (i = 0; i < bufferSize; i++) {
121 this.sinTable[i] = Math.sin(-Math.PI / i);
122 this.cosTable[i] = Math.cos(-Math.PI / i);
123 }
124
125 this.calculateSpectrum = function(buffer) {
126 // Locally scope variables for speed up
127 var bufferSize = this.bufferSize,
128 cosTable = this.cosTable,
129 sinTable = this.sinTable,
130 reverseTable = this.reverseTable,
131 real = new Float32Array(bufferSize),
132 imag = new Float32Array(bufferSize),
133 bSi = 2 / this.bufferSize,
134 sqrt = Math.sqrt,
135 rval,
136 ival,
137 mag,
138 spectrum = new Float32Array(bufferSize / 2);
139
140 var k = Math.floor(Math.log(bufferSize) / Math.LN2);
141
142 if (Math.pow(2, k) !== bufferSize) {
143 throw 'Invalid buffer size, must be a power of 2.';
144 }
145 if (bufferSize !== buffer.length) {
146 throw 'Supplied buffer is not the same size as defined FFT. FFT Size: ' +
147 bufferSize +
148 ' Buffer Size: ' +
149 buffer.length;
150 }
151
152 var halfSize = 1,
153 phaseShiftStepReal,
154 phaseShiftStepImag,
155 currentPhaseShiftReal,
156 currentPhaseShiftImag,
157 off,
158 tr,
159 ti,
160 tmpReal;
161
162 for (var i = 0; i < bufferSize; i++) {
163 real[i] =
164 buffer[reverseTable[i]] * this.windowValues[reverseTable[i]];
165 imag[i] = 0;
166 }
167
168 while (halfSize < bufferSize) {
169 phaseShiftStepReal = cosTable[halfSize];
170 phaseShiftStepImag = sinTable[halfSize];
171
172 currentPhaseShiftReal = 1;
173 currentPhaseShiftImag = 0;
174
175 for (var fftStep = 0; fftStep < halfSize; fftStep++) {
176 var i = fftStep;
177
178 while (i < bufferSize) {
179 off = i + halfSize;
180 tr =
181 currentPhaseShiftReal * real[off] -
182 currentPhaseShiftImag * imag[off];
183 ti =
184 currentPhaseShiftReal * imag[off] +
185 currentPhaseShiftImag * real[off];
186
187 real[off] = real[i] - tr;
188 imag[off] = imag[i] - ti;
189 real[i] += tr;
190 imag[i] += ti;
191
192 i += halfSize << 1;
193 }
194
195 tmpReal = currentPhaseShiftReal;
196 currentPhaseShiftReal =
197 tmpReal * phaseShiftStepReal -
198 currentPhaseShiftImag * phaseShiftStepImag;
199 currentPhaseShiftImag =
200 tmpReal * phaseShiftStepImag +
201 currentPhaseShiftImag * phaseShiftStepReal;
202 }
203
204 halfSize = halfSize << 1;
205 }
206
207 for (var i = 0, N = bufferSize / 2; i < N; i++) {
208 rval = real[i];
209 ival = imag[i];
210 mag = bSi * sqrt(rval * rval + ival * ival);
211
212 if (mag > this.peak) {
213 this.peakBand = i;
214 this.peak = mag;
215 }
216 spectrum[i] = mag;
217 }
218 return spectrum;
219 };
220};
221/* eslint-enable complexity, no-redeclare, no-var, one-var */
222
223
224/**
225 * Experimental SpectrumMultiCanvas renderer for wavesurfer.
226 *
227 * A `SpectrumMultiCanvas` consists of one or more `CanvasEntry` instances, depending
228 * on the zoom level.
229 */
230export default class SpectrumMultiCanvas extends MultiCanvas {
231 /**
232 * @param {HTMLElement} container The container node of the wavesurfer instance
233 * @param {WavesurferParams} params The wavesurfer initialisation options
234 */
235 constructor(container, params) {
236 super(container, params);
237
238 if (params.colorMap) {
239 if (params.colorMap.length < 256) {
240 throw new Error('Colormap must contain 256 elements');
241 }
242 for (let i = 0; i < params.colorMap.length; i++) {
243 const cmEntry = params.colorMap[i];
244 if ((cmEntry.length !== 4) && (cmEntry.length !== 3)) {
245 throw new Error(
246 'ColorMap entries must contain 3 (rgb) or 4 (rgba) values'
247 );
248 }
249 }
250 this.colorMap = params.colorMap;
251 } else {
252 this.colorMap = [];
253 for (let i = 0; i < 256; i++) {
254 const val = (255 - i) / 256;
255 this.colorMap.push([val, val, val, 1]);
256 }
257 }
258 }
259
260 /**
261 * Initialize the drawer
262 */
263 /*
264 init() {
265 this.createWrapper();
266 this.createElements();
267 }
268 */
269
270 /**
271 * Create the canvas elements and style them
272 *
273 */
274 /*
275 createElements() {
276 this.progressWave = this.wrapper.appendChild(
277 this.style(document.createElement('wave'), {
278 position: 'absolute',
279 zIndex: 3,
280 left: 0,
281 top: 0,
282 bottom: 0,
283 overflow: 'hidden',
284 width: '0',
285 display: 'none',
286 boxSizing: 'border-box',
287 borderRightStyle: 'solid',
288 pointerEvents: 'none'
289 })
290 );
291
292 this.addCanvas();
293 this.updateCursor();
294 }*/
295
296 /**
297 * Update cursor style
298 */
299
300 updateCursor() {
301 this.style(this.progressWave, {
302 //borderRightWidth: this.params.cursorSpectrumWidth + 'px',
303 //borderRightColor: this.params.cursorSpectrumColor
304 borderRightWidth: '4px',
305 borderRightColor: 'rgba(255,255,255,0.3)'
306 });
307 }
308
309
310 /**
311 * Adjust to the updated size by adding or removing canvases
312 */
313 /*
314 updateSize() {
315 const totalWidth = Math.round(this.width / this.params.pixelRatio);
316 const requiredCanvases = Math.ceil(
317 totalWidth / (this.maxCanvasElementWidth + this.overlap)
318 );
319
320 // add required canvases
321 while (this.canvases.length < requiredCanvases) {
322 this.addCanvas();
323 }
324
325 // remove older existing canvases, if any
326 while (this.canvases.length > requiredCanvases) {
327 this.removeCanvas();
328 }
329
330 let canvasWidth = this.maxCanvasWidth + this.overlap;
331 const lastCanvas = this.canvases.length - 1;
332 this.canvases.forEach((entry, i) => {
333 if (i == lastCanvas) {
334 canvasWidth = this.width - this.maxCanvasWidth * lastCanvas;
335 }
336 this.updateDimensions(entry, canvasWidth, this.height);
337
338 entry.clearWave();
339 });
340 }
341 */
342
343 /**
344 * Add a canvas to the canvas list
345 *
346 */
347 /*
348 addCanvas() {
349 const entry = new this.EntryClass();
350 entry.canvasContextAttributes = this.canvasContextAttributes;
351 entry.hasProgressCanvas = this.hasProgressCanvas;
352 entry.halfPixel = this.halfPixel;
353 const leftOffset = this.maxCanvasElementWidth * this.canvases.length;
354
355 // wave
356 entry.initWave(
357 this.wrapper.appendChild(
358 this.style(document.createElement('canvas'), {
359 position: 'absolute',
360 zIndex: 2,
361 left: leftOffset + 'px',
362 top: 0,
363 bottom: 0,
364 height: '100%',
365 pointerEvents: 'none'
366 })
367 )
368 );
369
370 // progress
371 if (this.hasProgressCanvas) {
372 entry.initProgress(
373 this.progressWave.appendChild(
374 this.style(document.createElement('canvas'), {
375 position: 'absolute',
376 left: leftOffset + 'px',
377 top: 0,
378 bottom: 0,
379 height: '100%'
380 })
381 )
382 );
383 }
384
385 this.canvases.push(entry);
386 }
387 */
388
389 /**
390 * Pop single canvas from the list
391 *
392 */
393 /*
394 removeCanvas() {
395 let lastEntry = this.canvases[this.canvases.length - 1];
396
397 // wave
398 lastEntry.wave.parentElement.removeChild(lastEntry.wave);
399
400 // progress
401 if (this.hasProgressCanvas) {
402 lastEntry.progress.parentElement.removeChild(lastEntry.progress);
403 }
404
405 // cleanup
406 if (lastEntry) {
407 lastEntry.destroy();
408 lastEntry = null;
409 }
410
411 this.canvases.pop();
412 }
413 */
414
415 /**
416 * Update the dimensions of a canvas element
417 *
418 * @param {CanvasEntry} entry Target entry
419 * @param {number} width The new width of the element
420 * @param {number} height The new height of the element
421 */
422 /*
423 updateDimensions(entry, width, height) {
424 const elementWidth = Math.round(width / this.params.pixelRatio);
425 const totalWidth = Math.round(this.width / this.params.pixelRatio);
426
427 // update canvas dimensions
428 entry.updateDimensions(elementWidth, totalWidth, width, height);
429
430 // style element
431 this.style(this.progressWave, { display: 'block' });
432 }
433 */
434
435 /**
436 * Clear the whole multi-canvas
437 */
438 /*
439 clearWave() {
440 util.frame(() => {
441 this.canvases.forEach(entry => entry.clearWave());
442 })();
443 }
444*/
445
446 drawPeaks(peaks, length, start, end) {
447 if (!this.setWidth(length)) {
448 this.clearWave();
449 }
450
451 this.getFrequencies(this.drawSpectrogram);
452 }
453
454 resample(oldMatrix) {
455 const columnsNumber = this.width;
456 const newMatrix = [];
457
458 const oldPiece = 1 / oldMatrix.length;
459 const newPiece = 1 / columnsNumber;
460 let i;
461
462 for (i = 0; i < columnsNumber; i++) {
463 const column = new Array(oldMatrix[0].length);
464 let j;
465
466 for (j = 0; j < oldMatrix.length; j++) {
467 const oldStart = j * oldPiece;
468 const oldEnd = oldStart + oldPiece;
469 const newStart = i * newPiece;
470 const newEnd = newStart + newPiece;
471
472 const overlap =
473 oldEnd <= newStart || newEnd <= oldStart
474 ? 0
475 : Math.min(
476 Math.max(oldEnd, newStart),
477 Math.max(newEnd, oldStart)
478 ) -
479 Math.max(
480 Math.min(oldEnd, newStart),
481 Math.min(newEnd, oldStart)
482 );
483 let k;
484 /* eslint-disable max-depth */
485 if (overlap > 0) {
486 for (k = 0; k < oldMatrix[0].length; k++) {
487 if (column[k] == null) {
488 column[k] = 0;
489 }
490 column[k] += (overlap / newPiece) * oldMatrix[j][k];
491 }
492 }
493 /* eslint-enable max-depth */
494 }
495
496 const intColumn = new Uint8Array(oldMatrix[0].length);
497 let m;
498
499 for (m = 0; m < oldMatrix[0].length; m++) {
500 intColumn[m] = column[m];
501 }
502
503 newMatrix.push(intColumn);
504 }
505
506 return newMatrix;
507 }
508
509 drawSpectrogram(frequenciesData, my) {
510 console.log("*** spectrummultican as::drawSpectrum() spec ....spec.length = " + frequenciesData.length)
511 console.log(frequenciesData);
512 //const spectrCc = my.spectrCc;
513 //const length = my.wavesurfer.backend.getDuration();
514 //const height = my.height;
515 //const pixels = my.resample(frequenciesData);
516 //const heightFactor = my.buffer ? 2 / my.buffer.numberOfChannels : 1;
517
518 const canvas = my.canvases[0].wave;
519 const spectrCc = canvas.getContext('2d');
520 const length = my.wavesurfer.backend.getDuration();
521 const height = canvas.height;
522 const pixels = my.resample(frequenciesData);
523 const heightFactor = my.buffer ? 2 / my.buffer.numberOfChannels : 1;
524
525
526 let i;
527 let j;
528
529 for (i = 0; i < pixels.length; i++) {
530 for (j = 0; j < pixels[i].length; j++) {
531 const colorMap = my.colorMap[pixels[i][j]]; // ****
532 if (colorMap.length == 4) {
533 //my.spectrCc.fillStyle =
534 spectrCc.fillStyle =
535 'rgba(' +
536 colorMap[0] * 256 +
537 ', ' +
538 colorMap[1] * 256 +
539 ', ' +
540 colorMap[2] * 256 +
541 ',' +
542 colorMap[3] +
543 ')';
544 }
545 else {
546 //my.spectrCc.fillStyle =
547 spectrCc.fillStyle =
548 'rgb(' +
549 colorMap[0] * 256 +
550 ', ' +
551 colorMap[1] * 256 +
552 ', ' +
553 colorMap[2] * 256 +
554 ')';
555 }
556
557 //my.spectrCc.fillRect(
558 spectrCc.fillRect(
559 i,
560 height - j * heightFactor,
561 1,
562 heightFactor
563 );
564 }
565 }
566 }
567
568 getFrequencies(callback) {
569 const fftSamples = this.fftSamples || 512;
570 const buffer = (this.buffer = this.wavesurfer.backend.buffer);
571 if (buffer == null) return; // **** GS3 edit
572 const channelOne = buffer.getChannelData(0);
573 const bufferLength = buffer.length;
574 const sampleRate = buffer.sampleRate;
575 const frequencies = [];
576
577 if (!buffer) {
578 this.fireEvent('error', 'Web Audio buffer is not available');
579 return;
580 }
581
582 let noverlap = this.noverlap;
583 if (!noverlap) {
584 const uniqueSamplesPerPx = buffer.length / this.canvases[0].wave.width; // **** GS3 edit
585 noverlap = Math.max(0, Math.round(fftSamples - uniqueSamplesPerPx));
586 }
587
588 const fft = new FFT(
589 fftSamples,
590 sampleRate,
591 this.windowFunc,
592 this.alpha
593 );
594 const maxSlicesCount = Math.floor(
595 bufferLength / (fftSamples - noverlap)
596 );
597 let currentOffset = 0;
598
599 while (currentOffset + fftSamples < channelOne.length) {
600 const segment = channelOne.slice(
601 currentOffset,
602 currentOffset + fftSamples
603 );
604 const spectrum = fft.calculateSpectrum(segment);
605 const array = new Uint8Array(fftSamples / 2);
606 let j;
607 for (j = 0; j < fftSamples / 2; j++) {
608 array[j] = Math.max(-255, Math.log10(spectrum[j]) * 45);
609 }
610 frequencies.push(array);
611 currentOffset += fftSamples - noverlap;
612 }
613 callback(frequencies, this);
614 }
615
616
617 /**
618 * Draw a waveform with bars
619 *
620 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
621 * for split channel rendering
622 * @param {number} channelIndex The index of the current channel. Normally
623 * should be 0. Must be an integer.
624 * @param {number} start The x-offset of the beginning of the area that
625 * should be rendered
626 * @param {number} end The x-offset of the end of the area that should be
627 * rendered
628 * @returns {void}
629 */
630
631 drawBars(peaks, channelIndex, start, end) {
632 return this.prepareDraw(
633 peaks,
634 channelIndex,
635 start,
636 end,
637 ({ absmax, hasMinVals, height, offsetY, halfH, peaks }) => {
638 // if drawBars was called within ws.empty we don't pass a start and
639 // don't want anything to happen
640 if (start === undefined) {
641 return;
642 }
643 // Skip every other value if there are negatives.
644 const peakIndexScale = hasMinVals ? 2 : 1;
645 const length = peaks.length / peakIndexScale;
646 const bar = this.params.barWidth * this.params.pixelRatio;
647 const gap =
648 this.params.barGap === null
649 ? Math.max(this.params.pixelRatio, ~~(bar / 2))
650 : Math.max(
651 this.params.pixelRatio,
652 this.params.barGap * this.params.pixelRatio
653 );
654 const step = bar + gap;
655
656 const scale = length / this.width;
657 const first = start;
658 const last = end;
659 let i = first;
660
661 for (i; i < last; i += step) {
662 const peak =
663 peaks[Math.floor(i * scale * peakIndexScale)] || 0;
664 let h = Math.round((peak / absmax) * halfH);
665
666 /* in case of silences, allow the user to specify that we
667 * always draw *something* (normally a 1px high bar) */
668 if (h == 0 && this.params.barMinHeight)
669 h = this.params.barMinHeight;
670
671 this.fillRect(
672 i + this.halfPixel,
673 halfH - h + offsetY,
674 bar + this.halfPixel,
675 h * 2,
676 this.barRadius
677 );
678 }
679 }
680 );
681 }
682
683 /**
684 * Draw a waveform
685 *
686 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
687 * for split channel rendering
688 * @param {number} channelIndex The index of the current channel. Normally
689 * should be 0
690 * @param {number?} start The x-offset of the beginning of the area that
691 * should be rendered (If this isn't set only a flat line is rendered)
692 * @param {number?} end The x-offset of the end of the area that should be
693 * rendered
694 * @returns {void}
695 */
696 /*
697 drawWave(peaks, channelIndex, start, end) {
698 return this.prepareDraw(
699 peaks,
700 channelIndex,
701 start,
702 end,
703 ({ absmax, hasMinVals, height, offsetY, halfH, peaks, channelIndex }) => {
704 if (!hasMinVals) {
705 const reflectedPeaks = [];
706 const len = peaks.length;
707 let i = 0;
708 for (i; i < len; i++) {
709 reflectedPeaks[2 * i] = peaks[i];
710 reflectedPeaks[2 * i + 1] = -peaks[i];
711 }
712 peaks = reflectedPeaks;
713 }
714
715 // if drawWave was called within ws.empty we don't pass a start and
716 // end and simply want a flat line
717 if (start !== undefined) {
718 this.drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex);
719 }
720
721 // always draw a median line
722 this.fillRect(
723 0,
724 halfH + offsetY - this.halfPixel,
725 this.width,
726 this.halfPixel,
727 this.barRadius
728 );
729 }
730 );
731 }
732 */
733
734 /**
735 * Tell the canvas entries to render their portion of the waveform
736 *
737 * @param {number[]} peaks Peaks data
738 * @param {number} absmax Maximum peak value (absolute)
739 * @param {number} halfH Half the height of the waveform
740 * @param {number} offsetY Offset to the top
741 * @param {number} start The x-offset of the beginning of the area that
742 * should be rendered
743 * @param {number} end The x-offset of the end of the area that
744 * should be rendered
745 * @param {channelIndex} channelIndex The channel index of the line drawn
746 */
747 /*
748 drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex) {
749 const { waveColor, progressColor } = this.params.splitChannelsOptions.channelColors[channelIndex] || {};
750 this.canvases.forEach((entry, i) => {
751 this.setFillStyles(entry, waveColor, progressColor);
752 entry.drawLines(peaks, absmax, halfH, offsetY, start, end);
753 });
754 }
755 */
756
757 /**
758 * Draw a rectangle on the multi-canvas
759 *
760 * @param {number} x X-position of the rectangle
761 * @param {number} y Y-position of the rectangle
762 * @param {number} width Width of the rectangle
763 * @param {number} height Height of the rectangle
764 * @param {number} radius Radius of the rectangle
765 */
766 /*
767 fillRect(x, y, width, height, radius) {
768 const startCanvas = Math.floor(x / this.maxCanvasWidth);
769 const endCanvas = Math.min(
770 Math.ceil((x + width) / this.maxCanvasWidth) + 1,
771 this.canvases.length
772 );
773 let i = startCanvas;
774 for (i; i < endCanvas; i++) {
775 const entry = this.canvases[i];
776 const leftOffset = i * this.maxCanvasWidth;
777
778 const intersection = {
779 x1: Math.max(x, i * this.maxCanvasWidth),
780 y1: y,
781 x2: Math.min(
782 x + width,
783 i * this.maxCanvasWidth + entry.wave.width
784 ),
785 y2: y + height
786 };
787
788 if (intersection.x1 < intersection.x2) {
789 this.setFillStyles(entry);
790
791 entry.fillRects(
792 intersection.x1 - leftOffset,
793 intersection.y1,
794 intersection.x2 - intersection.x1,
795 intersection.y2 - intersection.y1,
796 radius
797 );
798 }
799 }
800 }
801 */
802
803 /**
804 * Returns whether to hide the channel from being drawn based on params.
805 *
806 * @param {number} channelIndex The index of the current channel.
807 * @returns {bool} True to hide the channel, false to draw.
808 */
809 /*
810 hideChannel(channelIndex) {
811 return this.params.splitChannels && this.params.splitChannelsOptions.filterChannels.includes(channelIndex);
812 }
813 */
814
815 /**
816 * Performs preparation tasks and calculations which are shared by `drawBars`
817 * and `drawWave`
818 *
819 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for
820 * split channel rendering
821 * @param {number} channelIndex The index of the current channel. Normally
822 * should be 0
823 * @param {number?} start The x-offset of the beginning of the area that
824 * should be rendered. If this isn't set only a flat line is rendered
825 * @param {number?} end The x-offset of the end of the area that should be
826 * rendered
827 * @param {function} fn The render function to call, e.g. `drawWave`
828 * @param {number} drawIndex The index of the current channel after filtering.
829 * @returns {void}
830 */
831 /*
832 prepareDraw(peaks, channelIndex, start, end, fn, drawIndex) {
833 return util.frame(() => {
834 // Split channels and call this function with the channelIndex set
835 if (peaks[0] instanceof Array) {
836 const channels = peaks;
837
838 if (this.params.splitChannels) {
839 const filteredChannels = channels.filter((c, i) => !this.hideChannel(i));
840 if (!this.params.splitChannelsOptions.overlay) {
841 this.setHeight(
842 Math.max(filteredChannels.length, 1) *
843 this.params.height *
844 this.params.pixelRatio
845 );
846 }
847
848 return channels.forEach((channelPeaks, i) =>
849 this.prepareDraw(channelPeaks, i, start, end, fn, filteredChannels.indexOf(channelPeaks))
850 );
851 }
852 peaks = channels[0];
853 }
854
855 // Return and do not draw channel peaks if hidden.
856 if (this.hideChannel(channelIndex)) {
857 return;
858 }
859
860 // calculate maximum modulation value, either from the barHeight
861 // parameter or if normalize=true from the largest value in the peak
862 // set
863 let absmax = 1 / this.params.barHeight;
864 if (this.params.normalize) {
865 const max = util.max(peaks);
866 const min = util.min(peaks);
867 absmax = -min > max ? -min : max;
868 }
869
870 // Bar wave draws the bottom only as a reflection of the top,
871 // so we don't need negative values
872 const hasMinVals = [].some.call(peaks, val => val < 0);
873 const height = this.params.height * this.params.pixelRatio;
874 const offsetY = height * drawIndex || 0;
875 const halfH = height / 2;
876
877 return fn({
878 absmax: absmax,
879 hasMinVals: hasMinVals,
880 height: height,
881 offsetY: offsetY,
882 halfH: halfH,
883 peaks: peaks,
884 channelIndex: channelIndex,
885 });
886 })();
887 }
888 */
889
890 /**
891 * Set the fill styles for a certain entry (wave and progress)
892 *
893 * @param {CanvasEntry} entry Target entry
894 * @param {string} waveColor Wave color to draw this entry
895 * @param {string} progressColor Progress color to draw this entry
896 */
897 /*
898 setFillStyles(entry, waveColor = this.params.waveColor, progressColor = this.params.progressColor) {
899 entry.setFillStyles(waveColor, progressColor);
900 }
901*/
902 /**
903 * Return image data of the multi-canvas
904 *
905 * When using a `type` of `'blob'`, this will return a `Promise`.
906 *
907 * @param {string} format='image/png' An optional value of a format type.
908 * @param {number} quality=0.92 An optional value between 0 and 1.
909 * @param {string} type='dataURL' Either 'dataURL' or 'blob'.
910 * @return {string|string[]|Promise} When using the default `'dataURL'`
911 * `type` this returns a single data URL or an array of data URLs,
912 * one for each canvas. When using the `'blob'` `type` this returns a
913 * `Promise` that resolves with an array of `Blob` instances, one for each
914 * canvas.
915 */
916 /*
917 getImage(format, quality, type) {
918 if (type === 'blob') {
919 return Promise.all(
920 this.canvases.map(entry => {
921 return entry.getImage(format, quality, type);
922 })
923 );
924 } else if (type === 'dataURL') {
925 let images = this.canvases.map(entry =>
926 entry.getImage(format, quality, type)
927 );
928 return images.length > 1 ? images : images[0];
929 }
930 }
931 */
932
933 /**
934 * Render the new progress
935 *
936 * @param {number} position X-offset of progress position in pixels
937 */
938 /*
939 updateProgress(position) {
940 this.style(this.progressWave, { width: position + 'px' });
941 }
942*/
943}
Note: See TracBrowser for help on using the repository browser.