UNPKG

wavesurfer

Version:

Interactive navigable audio visualization using Web Audio and Canvas

214 lines (181 loc) 7.3 kB
'use strict'; WaveSurfer.Drawer.Canvas = Object.create(WaveSurfer.Drawer); WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { createElements: function () { var waveCanvas = this.wrapper.appendChild( this.style(document.createElement('canvas'), { position: 'absolute', zIndex: 1, left: 0, top: 0, bottom: 0 }) ); this.waveCc = waveCanvas.getContext('2d'); this.progressWave = this.wrapper.appendChild( this.style(document.createElement('wave'), { position: 'absolute', zIndex: 2, left: 0, top: 0, bottom: 0, overflow: 'hidden', width: '0', display: 'none', boxSizing: 'border-box', borderRightStyle: 'solid', borderRightWidth: this.params.cursorWidth + 'px', borderRightColor: this.params.cursorColor }) ); if (this.params.waveColor != this.params.progressColor) { var progressCanvas = this.progressWave.appendChild( document.createElement('canvas') ); this.progressCc = progressCanvas.getContext('2d'); } }, updateSize: function () { var width = Math.round(this.width / this.params.pixelRatio); this.waveCc.canvas.width = this.width; this.waveCc.canvas.height = this.height; this.style(this.waveCc.canvas, { width: width + 'px'}); this.style(this.progressWave, { display: 'block'}); if (this.progressCc) { this.progressCc.canvas.width = this.width; this.progressCc.canvas.height = this.height; this.style(this.progressCc.canvas, { width: width + 'px'}); } this.clearWave(); }, clearWave: function () { this.waveCc.clearRect(0, 0, this.width, this.height); if (this.progressCc) { this.progressCc.clearRect(0, 0, this.width, this.height); } }, drawBars: function (peaks, channelIndex, start, end) { var my = this; // Split channels if (peaks[0] instanceof Array) { var channels = peaks; if (this.params.splitChannels) { this.setHeight(channels.length * this.params.height * this.params.pixelRatio); channels.forEach(function(channelPeaks, i) { my.drawBars(channelPeaks, i, start, end); }); return; } else { peaks = channels[0]; } } // Bar wave draws the bottom only as a reflection of the top, // so we don't need negative values var hasMinVals = [].some.call(peaks, function (val) { return val < 0; }); // Skip every other value if there are negatives. var peakIndexScale = 1; if (hasMinVals) { peakIndexScale = 2; } // A half-pixel offset makes lines crisp var $ = 0.5 / this.params.pixelRatio; var width = this.width; var height = this.params.height * this.params.pixelRatio; var offsetY = height * channelIndex || 0; var halfH = height / 2; var length = peaks.length / peakIndexScale; var bar = this.params.barWidth * this.params.pixelRatio; var gap = Math.max(this.params.pixelRatio, ~~(bar / 2)); var step = bar + gap; var absmax = 1; if (this.params.normalize) { var max = WaveSurfer.util.max(peaks); var min = WaveSurfer.util.min(peaks); absmax = -min > max ? -min : max; } var scale = length / width; this.waveCc.fillStyle = this.params.waveColor; if (this.progressCc) { this.progressCc.fillStyle = this.params.progressColor; } [ this.waveCc, this.progressCc ].forEach(function (cc) { if (!cc) { return; } for (var i = (start / scale); i < (end / scale); i += step) { var peak = peaks[Math.floor(i * scale * peakIndexScale)] || 0; var h = Math.round(peak / absmax * halfH); cc.fillRect(i + $, halfH - h + offsetY, bar + $, h * 2); } }, this); }, drawWave: function (peaks, channelIndex, start, end) { var my = this; // Split channels if (peaks[0] instanceof Array) { var channels = peaks; if (this.params.splitChannels) { this.setHeight(channels.length * this.params.height * this.params.pixelRatio); channels.forEach(function(channelPeaks, i) { my.drawWave(channelPeaks, i, start, end); }); return; } else { peaks = channels[0]; } } // Support arrays without negative peaks var hasMinValues = [].some.call(peaks, function (val) { return val < 0; }); if (!hasMinValues) { var reflectedPeaks = []; for (var i = 0, len = peaks.length; i < len; i++) { reflectedPeaks[2 * i] = peaks[i]; reflectedPeaks[2 * i + 1] = -peaks[i]; } peaks = reflectedPeaks; } // A half-pixel offset makes lines crisp var $ = 0.5 / this.params.pixelRatio; var height = this.params.height * this.params.pixelRatio; var offsetY = height * channelIndex || 0; var halfH = height / 2; var length = ~~(peaks.length / 2); var scale = 1; if (this.params.fillParent && this.width != length) { scale = this.width / length; } var absmax = 1; if (this.params.normalize) { var max = WaveSurfer.util.max(peaks); var min = WaveSurfer.util.min(peaks); absmax = -min > max ? -min : max; } this.waveCc.fillStyle = this.params.waveColor; if (this.progressCc) { this.progressCc.fillStyle = this.params.progressColor; } [ this.waveCc, this.progressCc ].forEach(function (cc) { if (!cc) { return; } cc.beginPath(); cc.moveTo(start * scale + $, halfH + offsetY); for (var i = start; i < end; i++) { var h = Math.round(peaks[2 * i] / absmax * halfH); cc.lineTo(i * scale + $, halfH - h + offsetY); } // Draw the bottom edge going backwards, to make a single // closed hull to fill. for (var i = end - 1; i >= start; i--) { var h = Math.round(peaks[2 * i + 1] / absmax * halfH); cc.lineTo(i * scale + $, halfH - h + offsetY); } cc.closePath(); cc.fill(); // Always draw a median line cc.fillRect(0, halfH + offsetY - $, this.width, $); }, this); }, updateProgress: function (pos) { this.style(this.progressWave, { width: pos + 'px' }); }, getImage: function(type, quality) { return this.waveCc.canvas.toDataURL(type, quality); } });