Commit e0b31010 authored by 周宏民's avatar 周宏民
parents 35e27a73 dbf1cdfc
Pipeline #86577 passed with stages
...@@ -4,7 +4,7 @@ import { Tooltip, Typography } from 'antd' ...@@ -4,7 +4,7 @@ import { Tooltip, Typography } from 'antd'
import { Fragment, useCallback, useContext, useEffect, useRef, useState } from 'react' import { Fragment, useCallback, useContext, useEffect, useRef, useState } from 'react'
import { event } from '@wisdom-utils/utils' import { event } from '@wisdom-utils/utils'
import moment from 'moment' import moment from 'moment'
import { Directive, AllDirectiveTypes, CloseSessionDirective } from '../../core' import { Directive, AllDirectiveTypes, CloseSessionDirective , PandaSpeechSynthesisUtterance} from '../../core'
import context from '../../config' import context from '../../config'
import SparkDirective from '../../core/directive/SparkDirective' import SparkDirective from '../../core/directive/SparkDirective'
...@@ -19,8 +19,10 @@ const Dialog = props => { ...@@ -19,8 +19,10 @@ const Dialog = props => {
const timeRef = useRef(null) const timeRef = useRef(null)
const isDebug = globalConfig.get("isDebug") const isDebug = globalConfig.get("isDebug")
const currentDirectiveRef = useRef(null) const currentDirectiveRef = useRef(null)
const pandaSpeechSynthesisUtteranceRef = useRef(null)
//是否开启系统语音 //是否开启系统语音
const isOpenSystemSound = globalConfig.getIn(['key', 'isOpenSystemSound']) const isOpenSystemSound = globalConfig.getIn(['key', 'isOpenSystemSound'])
const defaultEmptyMsg = "亲,我暂时还未理解你的意思,请换一种方式表达或者描述更加具体"
const [data, setData] = useState(() => [{ const [data, setData] = useState(() => [{
role: "system", role: "system",
...@@ -47,6 +49,7 @@ const Dialog = props => { ...@@ -47,6 +49,7 @@ const Dialog = props => {
closeDirectiveRef.current = new Directive({ closeDirectiveRef.current = new Directive({
directiveTypes: [CloseSessionDirective] directiveTypes: [CloseSessionDirective]
}) })
pandaSpeechSynthesisUtteranceRef.current = new PandaSpeechSynthesisUtterance()
if (globalConfig.get('key').isOpenSparkModel) { if (globalConfig.get('key').isOpenSparkModel) {
directiveRef.current.directiveTypes.push(SparkDirective) directiveRef.current.directiveTypes.push(SparkDirective)
directiveRef.current._directiveTypes.push(SparkDirective) directiveRef.current._directiveTypes.push(SparkDirective)
...@@ -56,14 +59,24 @@ const Dialog = props => { ...@@ -56,14 +59,24 @@ const Dialog = props => {
return () => { return () => {
event.off("aiSound:finish", finish) event.off("aiSound:finish", finish)
event.off("aiSound:frame", frame) event.off("aiSound:frame", frame)
if(pandaSpeechSynthesisUtteranceRef.current) {
pandaSpeechSynthesisUtteranceRef.current.clear()
pandaSpeechSynthesisUtteranceRef.current = null
}
destroyTime() destroyTime()
} }
}, []) }, [])
const finish = useCallback(async ({ resultText }) => { const finish = useCallback(async ({ resultText }) => {
if (!resultText) return if (!resultText) return
if (isOpenSystemSound) { pandaSpeechSynthesisUtteranceRef.current?.clear()
pandaRecordWebSocketRef.current && pandaRecordWebSocketRef.current.setStatus("pause") if(pandaRecordWebSocketRef.current) {
if (isOpenSystemSound) {
pandaRecordWebSocketRef.current.stopRecorderManager()
}
// else {
// pandaRecordWebSocketRef.current.setStatus("pause")
// }
} }
const directive = directiveRef.current.parse({ const directive = directiveRef.current.parse({
text: resultText text: resultText
...@@ -77,24 +90,45 @@ const Dialog = props => { ...@@ -77,24 +90,45 @@ const Dialog = props => {
let resultMsg = "" let resultMsg = ""
await directive.excute({ await directive.excute({
sendMsg: msg => { sendMsg: msg => {
isOpenSystemSound && pandaSpeechSynthesisUtteranceRef.current.push(msg)
resultMsg += msg resultMsg += msg
updateLastData({ updateLastData({
content: resultMsg content: resultMsg
}) })
} }
}) })
if (isOpenSystemSound) { if(isOpenSystemSound) {
pandaRecordWebSocketRef.current && pandaRecordWebSocketRef.current.setStatus("playing") destroyTime()
await pandaSpeechSynthesisUtteranceRef.current.speakEnd()
startTime()
} }
if(pandaRecordWebSocketRef.current) {
if(isOpenSystemSound) {
pandaRecordWebSocketRef.current.resumeRecorderManager()
}
// else {
// pandaRecordWebSocketRef.current.setStatus("playing")
// }
}
currentDirectiveRef.current = null currentDirectiveRef.current = null
return return
} }
sendContent({ sendContent({
role: "system", role: "system",
content: "亲,我暂时还未理解你的意思,请换一种方式表达或者描述更加具体" content: defaultEmptyMsg
}) })
if (isOpenSystemSound) { if(isOpenSystemSound) {
pandaRecordWebSocketRef.current && pandaRecordWebSocketRef.current.setStatus("playing") pandaSpeechSynthesisUtteranceRef.current.push(defaultEmptyMsg)
await pandaSpeechSynthesisUtteranceRef.current.speakEnd()
}
if(pandaRecordWebSocketRef.current) {
if(isOpenSystemSound) {
pandaRecordWebSocketRef.current.resumeRecorderManager()
}
// else {
// pandaRecordWebSocketRef.current.setStatus("playing")
// }
} }
}, []) }, [])
...@@ -116,14 +150,10 @@ const Dialog = props => { ...@@ -116,14 +150,10 @@ const Dialog = props => {
const closeSession = ({ resultText }) => { const closeSession = ({ resultText }) => {
if (currentDirectiveRef.current) { if (currentDirectiveRef.current) {
currentDirectiveRef.current.closeResponse() currentDirectiveRef.current.closeResponse()
pandaRecordWebSocketRef.current.wsClose() delayStartSocket()
pandaRecordWebSocketRef.current.setStatus("pause")
setTimeout(() => {
pandaRecordWebSocketRef.current.setStatus("playing")
}, 500)
} else { } else {
if (!isDebug) { if (!isDebug) {
pandaRecordWebSocketRef.current.wsClose() delayStartSocket()
event.emit("aiSound:finish", { event.emit("aiSound:finish", {
resultText resultText
}) })
...@@ -131,6 +161,14 @@ const Dialog = props => { ...@@ -131,6 +161,14 @@ const Dialog = props => {
} }
} }
const delayStartSocket = () => {
pandaRecordWebSocketRef.current.wsClose()
pandaRecordWebSocketRef.current.setStatus("pause")
setTimeout(() => {
pandaRecordWebSocketRef.current.setStatus("playing")
}, 500)
}
const updateUserData = ({ resultTextTemp }) => { const updateUserData = ({ resultTextTemp }) => {
setData(data => { setData(data => {
const length = data.length const length = data.length
......
class PandaSpeechSynthesisUtterance {
constructor (options = {}) {
this.voiceParams = this.getVoiceParams(options.voiceParams ?? {})
this.data = []
window.speechSynthesis.cancel()
this.resolve = null
this.reject = null
this.onEnd = this.onEnd.bind(this)
}
getVoiceParams (voiceParams) {
return {
volume : 1,
rate : 1,
pitch : 1,
...voiceParams
}
}
push(text) {
if(!text) return
this.data.push(this.getSpeechSynthesisUtterance(text))
window.speechSynthesis.speak(this.data.at(-1))
}
getSpeechSynthesisUtterance(text) {
const speak = new SpeechSynthesisUtterance(text)
speak.addEventListener("end", this.onEnd)
for(let key in this.voiceParams) {
speak[key] = this.voiceParams[key]
}
return speak
}
onEnd () {
this.data.shift()
if(this.data.length == 0) {
this.resolve && this.resolve()
}
}
speakEnd () {
return new Promise((resolve , reject) => {
this.resolve = resolve
this.reject = reject
})
}
clear() {
this.data = []
window.speechSynthesis.cancel()
}
}
export default PandaSpeechSynthesisUtterance
\ No newline at end of file
...@@ -121,6 +121,20 @@ class PandaRecordWebSocket extends PandaWebSocket { ...@@ -121,6 +121,20 @@ class PandaRecordWebSocket extends PandaWebSocket {
this.recorderManager.onFrameRecorded = this.onRecorderFrameRecorded; this.recorderManager.onFrameRecorded = this.onRecorderFrameRecorded;
} }
pauseRecorederManager() {
if(this.recorderManager) {
this.recorderManager.stop()
}
}
resumeRecorderManager() {
this.createRecorder()
this.recorderManager.start({
sampleRate: 16000,
frameSize: 1280,
})
}
onRecorderStart(e) { onRecorderStart(e) {
this.recorderManager.status = 'open'; this.recorderManager.status = 'open';
this.events.recorderStart(); this.events.recorderStart();
......
...@@ -66,7 +66,7 @@ class PandaWebSocket { ...@@ -66,7 +66,7 @@ class PandaWebSocket {
wsClose() { wsClose() {
if (this.ws) { if (this.ws) {
this.ws.close(); this.ws.close(1000);
this.ws = null; this.ws = null;
} }
} }
......
...@@ -9,15 +9,17 @@ class CloseMenuDirective extends BaseDirective { ...@@ -9,15 +9,17 @@ class CloseMenuDirective extends BaseDirective {
match() { match() {
const { text } = this; const { text } = this;
let result = null; let result = null;
result = new RegExp(`关闭(所有).*(?=(?:[${config.punctuationMark}]?))`) if(!result) {
result = text.match(new RegExp(`关闭(所有).*(?=(?:[${config.punctuationMark}]?))`))
}
if (!result) { if (!result) {
result = new RegExp(`关闭(当前).*(?=(?:[${config.punctuationMark}]?))`) result = text.match(new RegExp(`关闭(当前).*(?=(?:[${config.punctuationMark}]?))`))
} }
if (!result) { if (!result) {
result = text.match(/关闭(.*)(?=(?:菜单|功能|路径))/); result = text.match(/关闭(.*)(?=(?:菜单|功能|路径))/);
} }
if (!result) { if (!result) {
result = new RegExp(`关闭(.*)(?=(?:[${config.punctuationMark}]?))`) result = text.match(new RegExp(`关闭(.*)(?=(?:[${config.punctuationMark}]?))`))
} }
if (result && result.length >= 2 && !this.matchResult) { if (result && result.length >= 2 && !this.matchResult) {
this.matchResult = result[1]; this.matchResult = result[1];
......
...@@ -15,7 +15,7 @@ class OpenMenuDirective extends BaseDirective { ...@@ -15,7 +15,7 @@ class OpenMenuDirective extends BaseDirective {
result = text.match(/打开(.*)(?=(?:菜单|功能|路径))/) result = text.match(/打开(.*)(?=(?:菜单|功能|路径))/)
} }
if (!result) { if (!result) {
result = new RegExp(`打开(.*)(?=(?:[${config.punctuationMark}]?))`) result = text.match(new RegExp(`打开(.*)(?=(?:[${config.punctuationMark}]?))`))
} }
if (result && result.length >= 2 && !this.matchResult) { if (result && result.length >= 2 && !this.matchResult) {
this.matchResult = result[1] this.matchResult = result[1]
......
...@@ -27,8 +27,14 @@ class TestDirective extends BaseDirective { ...@@ -27,8 +27,14 @@ class TestDirective extends BaseDirective {
} }
testCloseSession({sendMsg}) { testCloseSession({sendMsg}) {
let count = 0;
let random = parseInt(Math.random() * 50 + 1)
this.timer = setInterval(() => { this.timer = setInterval(() => {
sendMsg(parseInt(Math.random()* 10)) count++
if(count % random == 0) {
random = parseInt(Math.random() * 50 + 1)
sendMsg(count)
}
}, 100) }, 100)
} }
} }
......
...@@ -7,6 +7,7 @@ import Directive , {DirectiveTypes, AllDirectiveTypes} from './directive'; ...@@ -7,6 +7,7 @@ import Directive , {DirectiveTypes, AllDirectiveTypes} from './directive';
import AwakenDirective from './directive/AwakenDirective'; import AwakenDirective from './directive/AwakenDirective';
import ByeDirective from './directive/ByeDirective'; import ByeDirective from './directive/ByeDirective';
import CloseSessionDirective from './directive/CloseSessionDirective'; import CloseSessionDirective from './directive/CloseSessionDirective';
import PandaSpeechSynthesisUtterance from './core/SpeechSynthesisUtterance';
export { export {
RecorderManager, RecorderManager,
...@@ -19,5 +20,6 @@ export { ...@@ -19,5 +20,6 @@ export {
ByeDirective, ByeDirective,
DirectiveTypes, DirectiveTypes,
AllDirectiveTypes, AllDirectiveTypes,
CloseSessionDirective CloseSessionDirective,
PandaSpeechSynthesisUtterance
}; };
...@@ -69,7 +69,11 @@ const testCommand = async () => { ...@@ -69,7 +69,11 @@ const testCommand = async () => {
// }); // });
// await testFrameText("熊猫智慧水务解决方案是什么") // await testFrameText("熊猫智慧水务解决方案是什么")
// await Utils.delayTime(6000) // await Utils.delayTime(6000)
// await testFrameText("打开场景搭建功能菜单") // await testFrameText(" 关闭当前他的。")
await testFrameText("今天武汉天气怎么样")
// await testFrameText("熊猫智慧水务解决方案是什么")
// await Utils.delayTime(6000)
// await testFrameText("我需要测试熊猫测试指令")
} }
export default testCommand export default testCommand
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment