voxmed / static /waveform.js
jay2219's picture
v1.0.0
ad8910e
Raw
History Blame Contribute Delete
2.99 kB
/**
* waveform.js — Microphone waveform visualizer using Web Audio API.
* Exports startWaveform() and stopWaveform().
*/
/**
* Start visualizing the microphone input on a canvas element.
* @param {HTMLCanvasElement} canvas - The canvas to draw on.
* @param {MediaStream} stream - The audio stream from getUserMedia.
* @returns {Function} cleanup - Call this to stop the visualizer and release resources.
*/
export function startWaveform(canvas, stream) {
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
// Set canvas resolution to match display size
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
const width = rect.width;
const height = rect.height;
// Web Audio setup
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const source = audioCtx.createMediaStreamSource(stream);
const analyser = audioCtx.createAnalyser();
analyser.fftSize = 256;
source.connect(analyser);
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
let animFrameId = null;
function draw() {
animFrameId = requestAnimationFrame(draw);
analyser.getByteTimeDomainData(dataArray);
// Clear
ctx.fillStyle = '#F0F2F5';
ctx.fillRect(0, 0, width, height);
// Draw waveform
ctx.lineWidth = 2;
ctx.strokeStyle = '#25D366';
ctx.beginPath();
const sliceWidth = width / bufferLength;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const v = dataArray[i] / 128.0;
const y = (v * height) / 2;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
x += sliceWidth;
}
ctx.lineTo(width, height / 2);
ctx.stroke();
}
draw();
// Return cleanup function
return function cleanup() {
if (animFrameId) cancelAnimationFrame(animFrameId);
try { source.disconnect(); } catch (e) { /* ignore */ }
try { audioCtx.close(); } catch (e) { /* ignore */ }
};
}
/**
* Stop the waveform and draw a flat idle line.
* @param {HTMLCanvasElement} canvas
*/
export function stopWaveform(canvas) {
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
const width = rect.width;
const height = rect.height;
// Fill background
ctx.fillStyle = '#F0F2F5';
ctx.fillRect(0, 0, width, height);
// Draw flat center line
ctx.strokeStyle = '#D0D5DA';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0, height / 2);
ctx.lineTo(width, height / 2);
ctx.stroke();
}