Spaces:
Sleeping
Sleeping
File size: 3,168 Bytes
2b18d49 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | /**
* WaveformVisualizer - handles canvas waveform animation for user and agent audio
*/
export class WaveformVisualizer {
constructor(userCanvas, agentCanvas) {
this.userCanvas = userCanvas;
this.agentCanvas = agentCanvas;
this.userCtx = userCanvas.getContext('2d');
this.agentCtx = agentCanvas.getContext('2d');
this.animationId = null;
this.isUserSpeaking = false;
this.isAgentSpeaking = false;
this._initCanvases();
}
_initCanvases() {
this.userCanvas.width = 350;
this.userCanvas.height = 60;
this.agentCanvas.width = 350;
this.agentCanvas.height = 60;
this._clear(this.userCtx, this.userCanvas);
this._clear(this.agentCtx, this.agentCanvas);
}
_clear(ctx, canvas) {
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
_draw(ctx, canvas, isActive, color, waveformData) {
this._clear(ctx, canvas);
if (!isActive) return;
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.beginPath();
const centerY = canvas.height / 2;
if (waveformData && waveformData.length > 0) {
const sliceWidth = canvas.width / waveformData.length;
let x = 0;
for (let i = 0; i < waveformData.length; i++) {
const v = waveformData[i] / 128.0;
const y = (v * canvas.height) / 2;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
x += sliceWidth;
}
} else {
const frequency = Date.now() * 0.01;
for (let x = 0; x < canvas.width; x++) {
const y = centerY
+ Math.sin((x * 0.02) + frequency) * 12
+ Math.sin((x * 0.05) + frequency * 0.7) * 6
+ Math.sin((x * 0.03) + frequency * 1.3) * 4
+ (Math.random() - 0.5) * 3;
if (x === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
}
ctx.stroke();
}
/**
* Start animation loop.
* @param {Function} getUserWaveformData - returns Uint8Array or null
* @param {Function} getAgentWaveformData - returns Uint8Array or null
*/
start(getUserWaveformData, getAgentWaveformData) {
const animate = () => {
this._draw(this.userCtx, this.userCanvas, this.isUserSpeaking, '#2196f3', getUserWaveformData());
this._draw(this.agentCtx, this.agentCanvas, this.isAgentSpeaking, '#9c27b0', getAgentWaveformData());
this.animationId = requestAnimationFrame(animate);
};
animate();
}
stop() {
if (this.animationId) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
}
startUserSpeaking() {
this.isUserSpeaking = true;
}
stopUserSpeaking() {
this.isUserSpeaking = false;
}
startAgentSpeaking() {
this.isAgentSpeaking = true;
}
stopAgentSpeaking() {
this.isAgentSpeaking = false;
}
}
|