feat: 更新音频处理逻辑,添加最后识别文本功能并优化相关提示信息

This commit is contained in:
2025-12-23 23:53:42 +08:00
parent 4407c6157f
commit b6157deec9
5 changed files with 59 additions and 24 deletions

View File

@@ -7,7 +7,7 @@ import tailwindcss from '@tailwindcss/vite';
const isDev = process.env.NODE_ENV === 'development';
let target = process.env.VITE_API_URL || 'http://localhost:51015';
let target = process.env.VITE_API_URL || 'http://localhost:51515';
const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' };
let proxy = {

View File

@@ -1,6 +1,6 @@
{
"name": "@kevisual/light-code-center",
"version": "0.0.1",
"version": "0.0.2",
"description": "",
"main": "index.js",
"basename": "/root/light-code-center",
@@ -8,7 +8,7 @@
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"pub": "envision deploy ./dist -k light-code-center -v 0.0.1 -u",
"pub": "envision deploy ./dist -k light-code-center -v 0.0.2 -u -y y",
"ui": "pnpm dlx shadcn@latest add ",
"sn": "pnpm dlx shadcn@latest add "
},

View File

@@ -363,7 +363,8 @@ export const VadVoice = () => {
addVoice,
setError: setStoreError,
relatimeParialText,
relatimeFinalText
relatimeFinalText,
lastRecognizedText
} = useVoiceStore();
const showText = relatimeFinalText || relatimeParialText;
// 使用设置 store
@@ -399,10 +400,11 @@ export const VadVoice = () => {
onSpeechEnd: async (audio) => {
try {
const wavBuffer = utils.encodeWAV(audio)
const audioBlob = new Blob([wavBuffer], { type: 'audio/wav' })
const tempUrl = URL.createObjectURL(audioBlob)
const relatime = useVoiceStore.getState().relatime;
relatime?.sendBase64?.(utils.arrayBufferToBase64(wavBuffer));
const audioBlob = new Blob([wavBuffer], { type: 'audio/wav' })
const tempUrl = URL.createObjectURL(audioBlob)
// 从实际音频文件获取准确时长
const getDuration = (): Promise<number> => {
return new Promise((resolve) => {
@@ -413,8 +415,9 @@ export const VadVoice = () => {
});
});
};
relatime?.showCostTime?.();
const duration = await getDuration();
relatime?.showCostTime?.();
console.log(`Detected speech end. Duration: ${duration.toFixed(2)}s`);
// 使用 store 添加语音记录
@@ -431,7 +434,9 @@ export const VadVoice = () => {
onSpeechRealStart: () => {
console.log('VAD real start');
setRealListen(true);
}
const relatime = useVoiceStore.getState().relatime;
relatime?.setStartTime?.(Date.now());
},
});
ref.current = myvad;
@@ -524,8 +529,8 @@ export const VadVoice = () => {
voiceList.length === 0 ? (
<div className="text-center text-gray-400 text-sm py-8">
<div className="mb-2">🎤</div>
<div>No recordings yet</div>
<div className="text-xs mt-1">Start talking to record</div>
<div></div>
<div className="text-xs mt-1"></div>
</div>
) : (
<ShowVoicePlayer data={voiceList} />
@@ -537,8 +542,8 @@ export const VadVoice = () => {
initializeVAD(listen)
}}>
<div className="mb-2">🎤</div>
<div>Click anywhere to initialize microphone</div>
<div className="text-xs mt-1">Browser requires user interaction for microphone access</div>
<div></div>
<div className="text-xs mt-1">访</div>
</div>
)}
</div>
@@ -576,6 +581,27 @@ export const VadVoice = () => {
)}
</div>
<div className=" ">
{lastRecognizedText && (
<div className="flex">
<div className="text-xs text-gray-400 mt-1 truncate">
🕘 : {lastRecognizedText}
</div>
<div className="cursor-pointer " onClick={() => {
// copy
navigator.clipboard.writeText(lastRecognizedText).then(() => {
toast.success('已复制', {
autoClose: 500,
position: 'top-center'
});
}).catch((error) => {
console.error('复制失败:', error);
toast.error('复制失败,请手动选择文字复制');
});
}}>
<Copy className='text-gray-400' />
</div>
</div>
)}
{showText && (
<div className="flex">
<div className="text-xs text-gray-600 mt-1 truncate">

View File

@@ -5,6 +5,7 @@ export class Relatime {
asr: WSServer
ready = false
timeoutHandle: NodeJS.Timeout | null = null
startTime: number = 0
constructor() {
// const url = new URL('/ws/asr', "http://localhost:51015")
const url = new URL('/ws/asr', window.location.origin)
@@ -51,15 +52,20 @@ export class Relatime {
}
sendBase64(data: string) {
if (!this.ready) return;
this.asr.ws.send(JSON.stringify({ voice: data, format: 'float32' }));
if (this.timeoutHandle) {
clearTimeout(this.timeoutHandle);
console.log('send 花费时间:', Date.now() - this.startTime);
this.asr.ws.send(JSON.stringify({ voice: data, format: 'float32', time: Date.now() }));
// if (this.timeoutHandle) {
// clearTimeout(this.timeoutHandle);
// }
// this.timeoutHandle = setTimeout(() => {
// this.asr.sendBlankJson()
// this.timeoutHandle = null;
// }, 20000); // 20秒钟没有数据则发送空JSON保持连接
}
this.timeoutHandle = setTimeout(() => {
this.asr.sendBlankJson()
this.timeoutHandle = null;
}, 10000); // 5秒钟没有数据则发送空JSON保持连接
setStartTime(time: number) {
this.startTime = time;
}
showCostTime() {
console.log('当前花费时间:', Date.now() - this.startTime);
}
}

View File

@@ -26,6 +26,7 @@ interface VoiceState {
setLoading: (loading: boolean) => void;
relatime: Relatime;
relatimeParialText: string;
lastRecognizedText: string;
relatimeFinalText: string;
setRelatimeParialText: (text: string) => void;
setRelatimeFinalText: (text: string) => void;
@@ -338,12 +339,14 @@ export const useVoiceStore = create<VoiceState>()(
relatimeFinalText: '',
setRelatimeFinalText: (text: string) => {
set({ relatimeFinalText: text, relatimeParialText: '' });
const { relatimeFinalText } = get();
set(() => ({ relatimeFinalText: text, relatimeParialText: '', lastRecognizedText: relatimeFinalText }));
},
setRelatimeParialText: (text: string) => {
set({ relatimeFinalText: '', relatimeParialText: text });
set({ relatimeParialText: text });
},
relatimeParialText: '',
lastRecognizedText: '',
}),
{
name: 'voice-store', // persist key