waveform AI changes

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-06-04 20:14:55 +02:00
padre 36c9246210
commit ac5837a479

Ver fichero

@@ -1,4 +1,4 @@
import { useRef, useEffect } from "react";
import { useRef, useEffect, useState } from "react";
import useSize from "./useSize";
const BLUE_SHADES = [
@@ -8,47 +8,130 @@ const BLUE_SHADES = [
"rgba(255,255,255,0.2)",
];
const animateBars = (analyser, canvas, ctx, dataArray, bufferLength) => {
analyser.getByteFrequencyData(dataArray);
// Fallback animation for Tizen browsers without Web Audio API
const animateFallbackBars = (canvas, ctx, fallbackData) => {
const HEIGHT = canvas.height / 2;
const barWidth = Math.ceil(canvas.width / bufferLength) * 2.5;
const barCount = 64; // Reduced for better performance on TV
const barWidth = Math.max(2, Math.floor(canvas.width / barCount));
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] / 255) * HEIGHT;
const blueShade = Math.floor((dataArray[i] / 255) * 4);
ctx.fillStyle = BLUE_SHADES[blueShade];
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 1;
for (let i = 0; i < barCount; i++) {
const barHeight = fallbackData[i] * HEIGHT;
const blueShade = Math.floor(fallbackData[i] * 3);
ctx.fillStyle = BLUE_SHADES[blueShade] || BLUE_SHADES[0];
ctx.fillRect(x, HEIGHT - barHeight, barWidth - 1, barHeight);
x += barWidth;
}
};
// Real Web Audio API animation
const animateBars = (analyser, canvas, ctx, dataArray, bufferLength) => {
try {
analyser.getByteFrequencyData(dataArray);
const HEIGHT = canvas.height / 2;
const barWidth = Math.ceil(canvas.width / bufferLength) * 2.5;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] / 255) * HEIGHT;
const blueShade = Math.floor((dataArray[i] / 255) * 4);
ctx.fillStyle = BLUE_SHADES[blueShade] || BLUE_SHADES[0];
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
} catch (error) {
console.warn('Error in Web Audio API animation:', error);
return false;
}
return true;
};
const WaveForm = ({ analyzerData }) => {
const canvasRef = useRef(null);
const { dataArray, analyzer, bufferLength } = analyzerData;
const [width, height] = useSize();
const [useFallback, setUseFallback] = useState(false);
const [fallbackData, setFallbackData] = useState([]);
const fallbackIntervalRef = useRef(null);
// Initialize fallback data
useEffect(() => {
const initialData = Array(64).fill(0).map(() => Math.random() * 0.3 + 0.1);
setFallbackData(initialData);
}, []);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas || !analyzer) return;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
let animationId;
let webAudioWorking = true;
// Check if we have valid analyzer data
const hasValidAnalyzer = analyzerData &&
analyzerData.analyzer &&
analyzerData.dataArray &&
analyzerData.bufferLength;
const render = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(0, canvas.height / 2);
animateBars(analyzer, canvas, ctx, dataArray, bufferLength);
ctx.restore();
animationId = requestAnimationFrame(render);
try {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(0, canvas.height / 2);
if (hasValidAnalyzer && webAudioWorking && !useFallback) {
// Try Web Audio API first
const success = animateBars(
analyzerData.analyzer,
canvas,
ctx,
analyzerData.dataArray,
analyzerData.bufferLength
);
if (!success) {
webAudioWorking = false;
setUseFallback(true);
}
} else {
// Use fallback animation
animateFallbackBars(canvas, ctx, fallbackData);
}
ctx.restore();
animationId = requestAnimationFrame(render);
} catch (error) {
console.warn('Canvas rendering error:', error);
setUseFallback(true);
animationId = requestAnimationFrame(render);
}
};
// Start fallback data animation if needed
if (useFallback || !hasValidAnalyzer) {
fallbackIntervalRef.current = setInterval(() => {
setFallbackData(prevData =>
prevData.map(val => {
const target = Math.random() * 0.8 + 0.2;
const smoothing = 0.15;
return val + (target - val) * smoothing;
})
);
}, 100); // Update every 100ms for smooth animation
}
render();
return () => {
cancelAnimationFrame(animationId);
if (animationId) {
cancelAnimationFrame(animationId);
}
if (fallbackIntervalRef.current) {
clearInterval(fallbackIntervalRef.current);
}
};
}, [dataArray, analyzer, bufferLength, width, height]);
}, [analyzerData, width, height, useFallback, fallbackData]);
return (
<canvas
@@ -57,10 +140,17 @@ const WaveForm = ({ analyzerData }) => {
top: 0,
left: 0,
zIndex: 0,
// Add hardware acceleration hint for Tizen
willChange: "transform",
// Ensure canvas doesn't cause layout issues on TV
imageRendering: "pixelated"
}}
ref={canvasRef}
width={width}
height={height}
// Add accessibility for Smart TV navigation
role="img"
aria-label={useFallback ? "Audio visualization (compatibility mode)" : "Real-time audio visualization"}
/>
);
};