chore: speech to text

This commit is contained in:
jialin
2024-11-22 10:00:34 +08:00
parent f24aef300b
commit c3473673da
47 changed files with 3364 additions and 904 deletions
+10
View File
@@ -0,0 +1,10 @@
.canvas-wrap {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
canvas {
display: block;
}
}
+121
View File
@@ -0,0 +1,121 @@
import React, { useEffect } from 'react';
import './index.less';
interface AudioAnimationProps {
width: number;
height: number;
analyserData: {
data: Uint8Array;
analyser: any;
};
}
const AudioAnimation: React.FC<AudioAnimationProps> = (props) => {
const { width, height, analyserData } = props;
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const animationId = React.useRef<number>(0);
const isScaled = React.useRef<boolean>(false);
const oscillationOffset = React.useRef(0);
const direction = React.useRef(1);
const startAudioVisualization = () => {
if (!canvasRef.current || !analyserData.data?.length) return;
const canvas = canvasRef.current;
const canvasCtx = canvas.getContext('2d');
if (!canvasCtx) return;
const WIDTH = (canvas.width = width * 2);
const HEIGHT = (canvas.height = height * 2);
if (!isScaled.current) {
canvasCtx.scale(2, 2);
isScaled.current = true;
}
const barWidth = 3;
const barSpacing = 2;
const centerX = HEIGHT / 2;
const centerLine = Math.floor(HEIGHT / 2);
const jitterAmplitude = 60; // 最大抖动幅度
const minJitter = 15; // 最小抖动幅度
const frameInterval = 2;
let frameCount = 0;
canvasCtx.fillStyle = '#0073EF';
const draw = () => {
frameCount++;
if (frameCount % frameInterval !== 0) {
animationId.current = requestAnimationFrame(draw);
return;
}
analyserData.analyser?.current?.getByteFrequencyData(analyserData.data);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
const barCount = analyserData.data.length;
const totalWidth = barCount * (barWidth + barSpacing) - barSpacing;
let x = centerX - totalWidth / 2 + oscillationOffset.current;
oscillationOffset.current += direction.current * 0.5;
if (oscillationOffset.current > 20 || oscillationOffset.current < -20) {
direction.current *= -1;
}
for (let i = 0; i < barCount; i++) {
const baseHeight = Math.floor(analyserData.data[i] / 2);
const jitter =
minJitter +
Math.round((Math.random() - 0.5) * (jitterAmplitude - minJitter));
const barHeight = baseHeight + jitter;
const topY = Math.round(centerLine - barHeight / 2);
const bottomY = Math.round(centerLine + barHeight / 2);
canvasCtx.beginPath();
canvasCtx.moveTo(x, bottomY);
canvasCtx.lineTo(x, topY + 2);
canvasCtx.arcTo(x + barWidth, topY + 2, x + barWidth, bottomY, 2);
canvasCtx.lineTo(x + barWidth, bottomY);
canvasCtx.closePath();
canvasCtx.fill();
x += barWidth + barSpacing;
}
animationId.current = requestAnimationFrame(draw);
};
draw();
};
useEffect(() => {
if (!analyserData.data?.length || !analyserData.analyser.current) {
canvasRef.current
?.getContext('2d')
?.clearRect(0, 0, width * 2, height * 2);
cancelAnimationFrame(animationId.current);
animationId.current = 0;
return;
}
startAudioVisualization();
return () => {
if (animationId.current) cancelAnimationFrame(animationId.current);
};
}, [analyserData, width, height]);
return (
<div
className="canvas-wrap"
style={{
width: '100%',
height: height
}}
>
<canvas ref={canvasRef} style={{ width, height }}></canvas>
</div>
);
};
export default React.memo(AudioAnimation);