Spaces:
Running
Running
| export class Visualizer { | |
| constructor(audioContext, canvasId) { | |
| this.canvas = document.getElementById(canvasId); | |
| this.ctx = this.canvas.getContext('2d'); | |
| this.analyser = audioContext.createAnalyser(); | |
| // Ensure fftSize is a valid power of two (16384 is the max standard value) | |
| this.analyser.fftSize = 16384; | |
| this.bufferLength = this.analyser.frequencyBinCount; | |
| this.dataArray = new Uint8Array(this.bufferLength); | |
| this.sampleRate = audioContext.sampleRate; | |
| this.binWidth = this.sampleRate / this.analyser.fftSize; | |
| // Adjusted frequency range | |
| this.startBin = Math.floor(20 / this.binWidth); | |
| this.endBin = Math.floor(200 / this.binWidth); | |
| this.usableBins = this.endBin - this.startBin; | |
| // Slightly reduced smoothing for faster response | |
| this.analyser.smoothingTimeConstant = 0.45; // Reduced from 0.5 | |
| // Peak tracking with slightly more dynamic range | |
| this.peakLevels = new Array(this.usableBins).fill(0); | |
| this.peakDecayRate = 0.85; // Slightly faster decay | |
| this.dynamicPeakMultiplier = 1.25; // Increased from 1.2 | |
| this.resize(); | |
| window.addEventListener('resize', () => this.resize()); | |
| } | |
| resize() { | |
| const dpr = window.devicePixelRatio || 1; | |
| this.canvas.width = this.canvas.offsetWidth * dpr; | |
| this.canvas.height = this.canvas.offsetHeight * dpr; | |
| this.ctx.scale(dpr, dpr); | |
| this.canvas.style.width = this.canvas.offsetWidth + 'px'; | |
| this.canvas.style.height = this.canvas.offsetHeight + 'px'; | |
| } | |
| getAverageAmplitude() { | |
| let sum = 0; | |
| for (let i = this.startBin; i < this.endBin; i++) { | |
| sum += this.dataArray[i]; | |
| } | |
| // Slightly increased sensitivity | |
| return Math.min((sum / this.usableBins / 255) * 0.75, 1); | |
| } | |
| draw() { | |
| this.analyser.getByteFrequencyData(this.dataArray); | |
| const width = this.canvas.offsetWidth; | |
| const height = this.canvas.offsetHeight; | |
| // Slightly more transparent fade effect with a color closer to the background | |
| this.ctx.fillStyle = 'rgba(31, 3, 107, 0.3)'; | |
| this.ctx.fillRect(0, 0, width, height); | |
| const barWidth = (width / this.usableBins) * 0.9; | |
| const spacing = barWidth * 0.1; | |
| let x = (width - (this.usableBins * (barWidth + spacing))) / 2; | |
| for (let i = this.startBin; i < this.endBin; i++) { | |
| const index = i - this.startBin; | |
| const currentValue = this.dataArray[i]; | |
| // More controlled peak tracking | |
| this.peakLevels[index] = Math.max( | |
| currentValue, | |
| Math.min( | |
| this.peakLevels[index] * this.peakDecayRate, | |
| currentValue * this.dynamicPeakMultiplier | |
| ) | |
| ); | |
| // Slightly reduced peak height scaling | |
| const barHeight = Math.min( | |
| (this.peakLevels[index] * 0.9) * (height / 255), | |
| height | |
| ); | |
| const gradient = this.ctx.createLinearGradient(0, height, 0, height - barHeight); | |
| gradient.addColorStop(0, 'rgba(106, 74, 194, 0.8)'); // Matching the end color of background | |
| gradient.addColorStop(1, 'rgba(31, 3, 107, 0.4)'); // Matching the start color of background | |
| this.ctx.fillStyle = gradient; | |
| this.ctx.beginPath(); | |
| this.ctx.roundRect( | |
| x, | |
| height - barHeight, | |
| barWidth, | |
| barHeight, | |
| 3 | |
| ); | |
| this.ctx.fill(); | |
| x += barWidth + spacing; | |
| } | |
| return this.getAverageAmplitude(); | |
| } | |
| } |