murmuraba
Version:
Real-time audio noise reduction with advanced chunked processing for web applications
49 lines (48 loc) • 4.8 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React, { useMemo } from 'react';
import { ResponsiveContainer, AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';
export const VadTimeline = React.memo(function VadTimeline({ vadData, chunkId }) {
// Show loading state while data is being processed
if (!vadData || vadData.length === 0) {
return (_jsxs("div", { className: "details__section", children: [_jsx("h4", { className: "section__title", children: "\uD83D\uDCC8 Voice Activity Detection (VAD) Timeline" }), _jsxs("div", { className: "vad-loading-container", children: [_jsxs("div", { className: "vad-loading-spinner", children: [_jsx("div", { className: "spinner-dot" }), _jsx("div", { className: "spinner-dot" }), _jsx("div", { className: "spinner-dot" })] }), _jsx("p", { className: "vad-loading-text", children: "Analizando actividad de voz, por favor espera..." })] })] }));
}
const stats = useMemo(() => {
const voiceDetectedPercentage = (vadData.filter(d => d.vad > 0.5).length / vadData.length) * 100;
const peakVad = Math.max(...vadData.map(d => d.vad));
const minVad = Math.min(...vadData.map(d => d.vad));
return { voiceDetectedPercentage, peakVad, minVad };
}, [vadData]);
return (_jsxs("div", { className: "details__section", children: [_jsx("h4", { className: "section__title", children: "\uD83D\uDCC8 Voice Activity Detection (VAD) Timeline" }), _jsxs("div", { className: "vad-chart-container vad-chart-fade-in", children: [vadData && vadData.length > 0 ? (_jsx("div", { style: {
height: '200px',
width: '100%',
minHeight: '200px',
minWidth: '300px',
position: 'relative'
}, children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", minWidth: 300, minHeight: 200, children: _jsxs(AreaChart, { data: vadData, children: [_jsx("defs", { children: _jsxs("linearGradient", { id: `vadGradient-${chunkId}`, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "5%", stopColor: "#7ED321", stopOpacity: 0.8 }), _jsx("stop", { offset: "95%", stopColor: "#7ED321", stopOpacity: 0.1 })] }) }), _jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "#3b3c5a", opacity: 0.5 }), _jsx(XAxis, { dataKey: "time", stroke: "#a0a0a0", tickFormatter: (value) => `${value.toFixed(1)}s` }), _jsx(YAxis, { domain: [0, 1], stroke: "#a0a0a0", ticks: [0, 0.25, 0.5, 0.75, 1], tickFormatter: (value) => value.toFixed(2) }), _jsx(Tooltip, { contentStyle: {
backgroundColor: 'rgba(36, 37, 58, 0.95)',
border: '1px solid #3b3c5a',
borderRadius: '8px',
color: '#e0e0e0'
}, formatter: (value) => [`VAD: ${value.toFixed(3)}`, ''], labelFormatter: (label) => `Time: ${label}s` }), _jsx(Area, { type: "monotone", dataKey: "vad", stroke: "#7ED321", fill: `url(#vadGradient-${chunkId})`, fillOpacity: 1, strokeWidth: 2, isAnimationActive: false })] }) }) })) : (_jsx("div", { style: { height: '200px', display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: _jsx("p", { children: "No VAD data available" }) })), vadData && vadData.length > 0 && (_jsxs("div", { className: "vad-stats", children: [_jsxs("span", { className: "vad-stat", children: [_jsx("strong", { children: "Voice Detected:" }), " ", stats.voiceDetectedPercentage.toFixed(1), "%"] }), _jsxs("span", { className: "vad-stat", children: [_jsx("strong", { children: "Peak VAD:" }), " ", stats.peakVad.toFixed(3)] }), _jsxs("span", { className: "vad-stat", children: [_jsx("strong", { children: "Min VAD:" }), " ", stats.minVad.toFixed(3)] })] }))] })] }));
}, (prevProps, nextProps) => {
// Custom comparison to prevent unnecessary re-renders
if (prevProps.chunkId !== nextProps.chunkId)
return false;
if (!prevProps.vadData && !nextProps.vadData)
return true;
if (!prevProps.vadData || !nextProps.vadData)
return false;
if (prevProps.vadData.length !== nextProps.vadData.length)
return false;
// Deep comparison of first and last few data points
const checkPoints = [0, 1, prevProps.vadData.length - 2, prevProps.vadData.length - 1];
for (const i of checkPoints) {
if (i >= 0 && i < prevProps.vadData.length) {
if (prevProps.vadData[i]?.time !== nextProps.vadData[i]?.time ||
prevProps.vadData[i]?.vad !== nextProps.vadData[i]?.vad) {
return false;
}
}
}
return true;
});