export class Visualizer { private canvas: any; private ctx: CanvasRenderingContext2D; private buffer: AudioBuffer; private slices = []; private width: number; constructor(canvas: any, buffer: AudioBuffer) { this.canvas = canvas.nativeElement; this.buffer = buffer; this.ctx = this.canvas.getContext('2d'); this.width = this.canvas.width; this.recalculateSlices(); } public updateBuffer(buffer: AudioBuffer): void { this.buffer = buffer; this.recalculateSlices(); } public draw(): void { if (this.width !== this.canvas.width) { this.width = this.canvas.width; this.recalculateSlices(); } const WIDTH = this.canvas.width; const HEIGHT = this.canvas.height; this.ctx.save(); this.ctx.translate(0, HEIGHT / 2); this.ctx.strokeStyle = 'cornflowerblue'; this.ctx.beginPath(); for (let i = 0; i < WIDTH; ++i) { this.ctx.moveTo(i, Math.floor(this.slices[i][0] * HEIGHT / 2)); this.ctx.lineTo(i, Math.floor(this.slices[i][1] * HEIGHT / 2)); } this.ctx.stroke(); this.ctx.strokeStyle = 'cadetblue'; this.ctx.beginPath(); this.ctx.moveTo(0, 0); this.ctx.lineTo(WIDTH, 0); this.ctx.stroke(); this.ctx.restore(); } private recalculateSlices(): void { this.slices = []; const leftChannel = this.buffer.getChannelData(0); const binSize = Math.floor(leftChannel.length / this.canvas.width); for (let i = 0; i < this.canvas.width; ++i) { const slice = leftChannel.slice(i * binSize, (i + 1) * binSize); this.slices.push(this.minMax(slice)); } } private minMax(arr: Float32Array): number[] { let min = Infinity; let max = -Infinity; for (let i = 0; i < arr.length; ++i) { if (arr[i] > max) { max = arr[i]; } if (arr[i] < min) { min = arr[i]; } } return [min, max]; } }