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
@@ -0,0 +1,47 @@
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useRef
} from 'react';
import useWavesurfer from './hooks/use-wavesurfer';
interface AudioPlayerProps {
autoplay: boolean;
audioUrl: string;
speed: number;
ref?: any;
height?: number;
width?: number;
}
const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
const { autoplay, audioUrl, speed = 1, ...rest } = props;
const container = useRef<HTMLDivElement>(null);
const { createWavesurfer, play, pause, destroyWavesurfer } = useWavesurfer({
container,
autoplay: autoplay,
url: audioUrl,
audioRate: speed,
...rest
});
useImperativeHandle(ref, () => {
return {
play,
pause
};
});
useEffect(() => {
if (container.current) {
createWavesurfer();
}
return () => {
destroyWavesurfer();
};
}, [container.current]);
return <div ref={container} className="audio-container"></div>;
});
export default React.memo(AudioPlayer);
@@ -0,0 +1,68 @@
import { useRef } from 'react';
import WaveSurfer from 'wavesurfer.js';
interface Options {
container: React.RefObject<HTMLDivElement>;
waveColor?: string;
progressColor?: string;
url: string;
barWidth?: number;
barGap?: number;
barRadius?: number;
autoplay?: boolean;
audioRate?: number;
}
const useWavesurfer = (options: Options) => {
const wavesurfer = useRef<WaveSurfer | null>(null);
const { container, url, ...rest } = options;
const createWavesurfer = () => {
if (!container.current) {
return;
}
if (wavesurfer.current) {
wavesurfer.current.destroy();
}
wavesurfer.current = WaveSurfer.create({
container: container.current,
waveColor: '#4096ff',
progressColor: 'rgb(100, 0, 100)',
url: url,
height: 60,
barWidth: 2,
barGap: 1,
barRadius: 2,
interact: true,
cursorWidth: 0,
...rest
});
};
const destroyWavesurfer = () => {
if (wavesurfer.current) {
wavesurfer.current.destroy();
}
};
const play = () => {
if (wavesurfer.current) {
wavesurfer.current.play();
}
};
const pause = () => {
if (wavesurfer.current) {
wavesurfer.current.pause();
}
};
return {
createWavesurfer,
play,
pause,
destroyWavesurfer
};
};
export default useWavesurfer;
Binary file not shown.
+20
View File
@@ -0,0 +1,20 @@
import React from 'react';
import SpeechItem from './speech-item';
interface SpeechContentProps {
dataList: any[];
loading?: boolean;
}
const SpeechContent: React.FC<SpeechContentProps> = (props) => {
console.log('SpeechContent', props);
return (
<div>
{props.dataList.map((item) => (
<SpeechItem key={item.uid} {...item} />
))}
</div>
);
};
export default React.memo(SpeechContent);
@@ -0,0 +1,87 @@
import IconFont from '@/components/icon-font';
import {
DownloadOutlined,
FileTextOutlined,
PlayCircleOutlined
} from '@ant-design/icons';
import { Button, Tooltip } from 'antd';
import React, { useRef, useState } from 'react';
import AudioPlayer from './audio-player';
import './styles/index.less';
const audioUrl = require('./ih.mp4');
interface SpeechContentProps {
prompt: string;
autoplay: boolean;
voice: string;
format: string;
speed: number;
}
const SpeechItem: React.FC<SpeechContentProps> = (props) => {
console.log('porps=======', props);
const [collapsed, setCollapsed] = useState(false);
const ref = useRef<HTMLAudioElement>(null);
const handlePlay = () => {
ref.current?.play();
};
const handleCollapse = () => {
setCollapsed(!collapsed);
};
return (
<div>
<div className="speech-item">
{/* <audio controls autoPlay={true} src={require('./ih.mp4')}></audio> */}
<div className="voice">
<IconFont type="icon-user_voice" className="font-size-16" />
<span className="text">{props.voice}</span>
</div>
<div className="wrapper">
<AudioPlayer {...props} audioUrl={audioUrl} ref={ref}></AudioPlayer>
</div>
</div>
<div className="speech-actions">
<span className="tags">
<span className="item">{props.format}</span>
<span className="item splitor"></span>
<span className="item">{props.speed}x</span>
</span>
<div className="actions">
<Tooltip title="Play">
<Button
onClick={handlePlay}
icon={<PlayCircleOutlined />}
type="text"
size="small"
></Button>
</Tooltip>
<Tooltip title="Download">
<Button
icon={<DownloadOutlined />}
type="text"
size="small"
></Button>
</Tooltip>
<Tooltip title="Show Prompt">
<Button
icon={<FileTextOutlined />}
type="text"
size="small"
onClick={handleCollapse}
></Button>
</Tooltip>
</div>
</div>
{collapsed && (
<div className="prompt-box">
<div className="prompt">{props.prompt}</div>
</div>
)}
</div>
);
};
export default React.memo(SpeechItem);
@@ -0,0 +1,93 @@
.speech-item {
display: flex;
justify-content: flex-start;
align-items: center;
.voice {
width: 80px;
display: flex;
justify-content: flex-start;
align-items: center;
gap: 5px;
.text {
display: flex;
padding: 2px 4px;
border-radius: 4px;
border: 1px solid var(--ant-color-border);
}
}
.wrapper {
flex: 1;
display: flex;
justify-content: flex-start;
align-items: center;
.audio-container {
flex: 1;
}
.format {
display: flex;
padding-left: 5px;
}
}
audio {
flex: 1;
}
}
.prompt-box {
padding-left: 80px;
.prompt {
margin-top: 16px;
padding: 10px;
border-radius: var(--border-radius-base);
background-color: var(--ant-color-fill-quaternary);
}
}
.speech-actions {
display: flex;
justify-content: space-between;
margin-top: 10px;
padding-left: 80px;
.actions {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 15px;
.anticon {
font-size: 14px;
}
}
.tags {
display: flex;
justify-content: flex-start;
align-items: center;
.item {
display: flex;
justify-content: center;
align-items: center;
padding: 2px;
border-radius: 4px;
color: var(--ant-color-text-tertiary);
}
.splitor {
display: flex;
width: 1px;
height: 1px;
border-radius: 2px;
margin: 0 8px;
background-color: var(--ant-color-fill-content-hover);
}
}
}