feat: 更新音频处理逻辑,添加最后识别文本功能并优化相关提示信息
This commit is contained in:
@@ -7,7 +7,7 @@ import tailwindcss from '@tailwindcss/vite';
|
|||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
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' };
|
const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' };
|
||||||
|
|
||||||
let proxy = {
|
let proxy = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@kevisual/light-code-center",
|
"name": "@kevisual/light-code-center",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"basename": "/root/light-code-center",
|
"basename": "/root/light-code-center",
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"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 ",
|
"ui": "pnpm dlx shadcn@latest add ",
|
||||||
"sn": "pnpm dlx shadcn@latest add "
|
"sn": "pnpm dlx shadcn@latest add "
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -363,7 +363,8 @@ export const VadVoice = () => {
|
|||||||
addVoice,
|
addVoice,
|
||||||
setError: setStoreError,
|
setError: setStoreError,
|
||||||
relatimeParialText,
|
relatimeParialText,
|
||||||
relatimeFinalText
|
relatimeFinalText,
|
||||||
|
lastRecognizedText
|
||||||
} = useVoiceStore();
|
} = useVoiceStore();
|
||||||
const showText = relatimeFinalText || relatimeParialText;
|
const showText = relatimeFinalText || relatimeParialText;
|
||||||
// 使用设置 store
|
// 使用设置 store
|
||||||
@@ -399,10 +400,11 @@ export const VadVoice = () => {
|
|||||||
onSpeechEnd: async (audio) => {
|
onSpeechEnd: async (audio) => {
|
||||||
try {
|
try {
|
||||||
const wavBuffer = utils.encodeWAV(audio)
|
const wavBuffer = utils.encodeWAV(audio)
|
||||||
const audioBlob = new Blob([wavBuffer], { type: 'audio/wav' })
|
|
||||||
const tempUrl = URL.createObjectURL(audioBlob)
|
|
||||||
const relatime = useVoiceStore.getState().relatime;
|
const relatime = useVoiceStore.getState().relatime;
|
||||||
relatime?.sendBase64?.(utils.arrayBufferToBase64(wavBuffer));
|
relatime?.sendBase64?.(utils.arrayBufferToBase64(wavBuffer));
|
||||||
|
const audioBlob = new Blob([wavBuffer], { type: 'audio/wav' })
|
||||||
|
const tempUrl = URL.createObjectURL(audioBlob)
|
||||||
|
|
||||||
// 从实际音频文件获取准确时长
|
// 从实际音频文件获取准确时长
|
||||||
const getDuration = (): Promise<number> => {
|
const getDuration = (): Promise<number> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@@ -413,8 +415,9 @@ export const VadVoice = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
relatime?.showCostTime?.();
|
||||||
const duration = await getDuration();
|
const duration = await getDuration();
|
||||||
|
relatime?.showCostTime?.();
|
||||||
console.log(`Detected speech end. Duration: ${duration.toFixed(2)}s`);
|
console.log(`Detected speech end. Duration: ${duration.toFixed(2)}s`);
|
||||||
|
|
||||||
// 使用 store 添加语音记录
|
// 使用 store 添加语音记录
|
||||||
@@ -431,7 +434,9 @@ export const VadVoice = () => {
|
|||||||
onSpeechRealStart: () => {
|
onSpeechRealStart: () => {
|
||||||
console.log('VAD real start');
|
console.log('VAD real start');
|
||||||
setRealListen(true);
|
setRealListen(true);
|
||||||
}
|
const relatime = useVoiceStore.getState().relatime;
|
||||||
|
relatime?.setStartTime?.(Date.now());
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.current = myvad;
|
ref.current = myvad;
|
||||||
@@ -524,8 +529,8 @@ export const VadVoice = () => {
|
|||||||
voiceList.length === 0 ? (
|
voiceList.length === 0 ? (
|
||||||
<div className="text-center text-gray-400 text-sm py-8">
|
<div className="text-center text-gray-400 text-sm py-8">
|
||||||
<div className="mb-2">🎤</div>
|
<div className="mb-2">🎤</div>
|
||||||
<div>No recordings yet</div>
|
<div>暂无录音</div>
|
||||||
<div className="text-xs mt-1">Start talking to record</div>
|
<div className="text-xs mt-1">开始说话以录音</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<ShowVoicePlayer data={voiceList} />
|
<ShowVoicePlayer data={voiceList} />
|
||||||
@@ -537,8 +542,8 @@ export const VadVoice = () => {
|
|||||||
initializeVAD(listen)
|
initializeVAD(listen)
|
||||||
}}>
|
}}>
|
||||||
<div className="mb-2">🎤</div>
|
<div className="mb-2">🎤</div>
|
||||||
<div>Click anywhere to initialize microphone</div>
|
<div>点击任意位置以初始化麦克风</div>
|
||||||
<div className="text-xs mt-1">Browser requires user interaction for microphone access</div>
|
<div className="text-xs mt-1">浏览器需要用户交互以访问麦克风</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -576,6 +581,27 @@ export const VadVoice = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className=" ">
|
<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 && (
|
{showText && (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="text-xs text-gray-600 mt-1 truncate">
|
<div className="text-xs text-gray-600 mt-1 truncate">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export class Relatime {
|
|||||||
asr: WSServer
|
asr: WSServer
|
||||||
ready = false
|
ready = false
|
||||||
timeoutHandle: NodeJS.Timeout | null = null
|
timeoutHandle: NodeJS.Timeout | null = null
|
||||||
|
startTime: number = 0
|
||||||
constructor() {
|
constructor() {
|
||||||
// const url = new URL('/ws/asr', "http://localhost:51015")
|
// const url = new URL('/ws/asr', "http://localhost:51015")
|
||||||
const url = new URL('/ws/asr', window.location.origin)
|
const url = new URL('/ws/asr', window.location.origin)
|
||||||
@@ -51,15 +52,20 @@ export class Relatime {
|
|||||||
}
|
}
|
||||||
sendBase64(data: string) {
|
sendBase64(data: string) {
|
||||||
if (!this.ready) return;
|
if (!this.ready) return;
|
||||||
this.asr.ws.send(JSON.stringify({ voice: data, format: 'float32' }));
|
console.log('send 花费时间:', Date.now() - this.startTime);
|
||||||
if (this.timeoutHandle) {
|
this.asr.ws.send(JSON.stringify({ voice: data, format: 'float32', time: Date.now() }));
|
||||||
clearTimeout(this.timeoutHandle);
|
// if (this.timeoutHandle) {
|
||||||
}
|
// clearTimeout(this.timeoutHandle);
|
||||||
this.timeoutHandle = setTimeout(() => {
|
// }
|
||||||
this.asr.sendBlankJson()
|
// this.timeoutHandle = setTimeout(() => {
|
||||||
this.timeoutHandle = null;
|
// this.asr.sendBlankJson()
|
||||||
}, 10000); // 5秒钟没有数据则发送空JSON保持连接
|
// this.timeoutHandle = null;
|
||||||
|
// }, 20000); // 20秒钟没有数据则发送空JSON保持连接
|
||||||
|
}
|
||||||
|
setStartTime(time: number) {
|
||||||
|
this.startTime = time;
|
||||||
|
}
|
||||||
|
showCostTime() {
|
||||||
|
console.log('当前花费时间:', Date.now() - this.startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,7 @@ interface VoiceState {
|
|||||||
setLoading: (loading: boolean) => void;
|
setLoading: (loading: boolean) => void;
|
||||||
relatime: Relatime;
|
relatime: Relatime;
|
||||||
relatimeParialText: string;
|
relatimeParialText: string;
|
||||||
|
lastRecognizedText: string;
|
||||||
relatimeFinalText: string;
|
relatimeFinalText: string;
|
||||||
setRelatimeParialText: (text: string) => void;
|
setRelatimeParialText: (text: string) => void;
|
||||||
setRelatimeFinalText: (text: string) => void;
|
setRelatimeFinalText: (text: string) => void;
|
||||||
@@ -338,12 +339,14 @@ export const useVoiceStore = create<VoiceState>()(
|
|||||||
|
|
||||||
relatimeFinalText: '',
|
relatimeFinalText: '',
|
||||||
setRelatimeFinalText: (text: string) => {
|
setRelatimeFinalText: (text: string) => {
|
||||||
set({ relatimeFinalText: text, relatimeParialText: '' });
|
const { relatimeFinalText } = get();
|
||||||
|
set(() => ({ relatimeFinalText: text, relatimeParialText: '', lastRecognizedText: relatimeFinalText }));
|
||||||
},
|
},
|
||||||
setRelatimeParialText: (text: string) => {
|
setRelatimeParialText: (text: string) => {
|
||||||
set({ relatimeFinalText: '', relatimeParialText: text });
|
set({ relatimeParialText: text });
|
||||||
},
|
},
|
||||||
relatimeParialText: '',
|
relatimeParialText: '',
|
||||||
|
lastRecognizedText: '',
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'voice-store', // persist key
|
name: 'voice-store', // persist key
|
||||||
|
|||||||
Reference in New Issue
Block a user