Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
C
CharIP-Electron
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ali
CharIP-Electron
Commits
2a5e9aa6
You need to sign in or sign up before continuing.
Commit
2a5e9aa6
authored
Dec 06, 2023
by
ali
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 更换 icon;修复视频数字人切换视频闪烁问题,回答内容分割不准确问题,优化回答内容麦克风状态联动;新增 llmToTTSSliceLength 配置控制回答内容分割字数
parent
fd503c93
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
93 additions
and
47 deletions
+93
-47
icon.png
buildAssets/icons/icon.png
+0
-0
HeaderLayout.vue
src/renderer/components/layout/HeaderLayout.vue
+27
-0
ShowPhoto.vue
src/renderer/screens/ShowPhoto.vue
+12
-8
ShowVideo.vue
src/renderer/screens/ShowVideo.vue
+51
-38
settings.ts
src/renderer/store/settings.ts
+3
-1
No files found.
buildAssets/icons/icon.png
View replaced file @
fd503c93
View file @
2a5e9aa6
16.4 KB
|
W:
|
H:
52.3 KB
|
W:
|
H:
2-up
Swipe
Onion skin
src/renderer/components/layout/HeaderLayout.vue
View file @
2a5e9aa6
...
@@ -84,6 +84,11 @@ if (setting.asr.value === 'vosk_asr') {
...
@@ -84,6 +84,11 @@ if (setting.asr.value === 'vosk_asr') {
async
function
changeOpenDevTools
()
{
async
function
changeOpenDevTools
()
{
await
window
.
mainApi
.
send
(
'openDevTools'
,
setting
.
isOpenDevTools
.
value
)
await
window
.
mainApi
.
send
(
'openDevTools'
,
setting
.
isOpenDevTools
.
value
)
}
}
function
clear
()
{
localStorage
.
clear
()
location
.
reload
()
}
</
script
>
</
script
>
<
template
>
<
template
>
<v-app-bar
color=
"#d71b1b"
density=
"compact"
class=
"header"
>
<v-app-bar
color=
"#d71b1b"
density=
"compact"
class=
"header"
>
...
@@ -181,6 +186,27 @@ async function changeOpenDevTools() {
...
@@ -181,6 +186,27 @@ async function changeOpenDevTools() {
:model-value=
"setting.llmUrl"
:model-value=
"setting.llmUrl"
></v-text-field>
></v-text-field>
<v-slider
v-model=
"setting.llmToTTSSliceLength.value"
label=
"TTS 分句长度"
class=
"align-center"
:max=
"100"
:min=
"0"
hide-details
:step=
"1"
>
<
template
#
append
>
<v-text-field
v-model=
"setting.llmToTTSSliceLength.value"
hide-details
single-line
density=
"compact"
type=
"number"
style=
"width: 80px"
></v-text-field>
</
template
>
</v-slider>
<v-switch
<v-switch
v-model=
"setting.isFullscreen.value"
v-model=
"setting.isFullscreen.value"
hide-details
hide-details
...
@@ -202,6 +228,7 @@ async function changeOpenDevTools() {
...
@@ -202,6 +228,7 @@ async function changeOpenDevTools() {
<v-card-actions>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer></v-spacer>
<v-btn
color=
"#d71b1b"
text=
"清除缓存并刷新"
@
click=
"clear"
></v-btn>
<v-btn
text=
"关闭"
@
click=
"isActive.value = false"
></v-btn>
<v-btn
text=
"关闭"
@
click=
"isActive.value = false"
></v-btn>
</v-card-actions>
</v-card-actions>
</v-card>
</v-card>
...
...
src/renderer/screens/ShowPhoto.vue
View file @
2a5e9aa6
...
@@ -252,7 +252,7 @@ async function startVoskWsAudioInput() {
...
@@ -252,7 +252,7 @@ async function startVoskWsAudioInput() {
return
return
}
}
await
initVoskWS
()
;
await
initVoskWS
()
sampleRate
=
8000
sampleRate
=
8000
const
mediaStream
=
await
navigator
.
mediaDevices
.
getUserMedia
({
const
mediaStream
=
await
navigator
.
mediaDevices
.
getUserMedia
({
audio
:
{
audio
:
{
...
@@ -369,14 +369,18 @@ async function onAsr(question: string) {
...
@@ -369,14 +369,18 @@ async function onAsr(question: string) {
answer
+=
text
answer
+=
text
isTime
&&
console
.
time
(
'sliceAnswer'
)
isTime
&&
console
.
time
(
'sliceAnswer'
)
isTime
=
false
isTime
=
false
sliceAnswer
+=
text
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
text
)
&&
sliceAnswer
.
length
>=
20
)
{
const
textArr
=
text
.
split
(
''
);
console
.
timeEnd
(
'sliceAnswer'
)
for
(
let
i
=
0
;
i
<
textArr
.
length
;
i
++
)
{
answerArray
.
push
(
sliceAnswer
)
const
t
=
textArr
[
i
];
runTTSTask
(
answerArray
)
sliceAnswer
+=
t
sliceAnswer
=
''
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
t
)
&&
sliceAnswer
.
length
>=
settings
.
llmToTTSSliceLength
)
{
isTime
=
true
console
.
timeEnd
(
'sliceAnswer'
)
answerArray
.
push
(
sliceAnswer
)
runTTSTask
(
answerArray
)
sliceAnswer
=
''
isTime
=
true
}
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
'返回答案错误 -----> '
+
JSON
.
stringify
(
error
))
console
.
log
(
'返回答案错误 -----> '
+
JSON
.
stringify
(
error
))
...
...
src/renderer/screens/ShowVideo.vue
View file @
2a5e9aa6
...
@@ -24,8 +24,7 @@ const role = useVideo.list.find((i) => i.url === url)
...
@@ -24,8 +24,7 @@ const role = useVideo.list.find((i) => i.url === url)
const
microphoneState
=
ref
<
'waitInput'
|
'input'
|
'loading'
|
'disabled'
|
'reply'
>
(
'waitInput'
)
const
microphoneState
=
ref
<
'waitInput'
|
'input'
|
'loading'
|
'disabled'
|
'reply'
>
(
'waitInput'
)
const
videoElement
=
ref
<
HTMLVideoElement
|
null
>
(
null
)
const
videoElement
=
ref
<
HTMLVideoElement
|
null
>
(
null
)
const
videoElement2
=
ref
<
HTMLVideoElement
|
null
>
(
null
)
const
videoElement2
=
ref
<
HTMLVideoElement
|
null
>
(
null
)
const
videos
=
[
videoElement
,
videoElement2
];
const
videos
=
[
videoElement
,
videoElement2
]
onMounted
(()
=>
{
onMounted
(()
=>
{
// init();
// init();
...
@@ -166,7 +165,7 @@ async function startVoskWsAudioInput() {
...
@@ -166,7 +165,7 @@ async function startVoskWsAudioInput() {
}
}
initVoskWS
()
initVoskWS
()
sampleRate
=
8
000
sampleRate
=
16
000
const
mediaStream
=
await
navigator
.
mediaDevices
.
getUserMedia
({
const
mediaStream
=
await
navigator
.
mediaDevices
.
getUserMedia
({
audio
:
{
audio
:
{
echoCancellation
:
true
,
echoCancellation
:
true
,
...
@@ -184,11 +183,15 @@ async function startVoskWsAudioInput() {
...
@@ -184,11 +183,15 @@ async function startVoskWsAudioInput() {
processor
.
connect
(
audioContext
.
destination
)
processor
.
connect
(
audioContext
.
destination
)
processor
.
onaudioprocess
=
(
audioDataChunk
)
=>
{
processor
.
onaudioprocess
=
(
audioDataChunk
)
=>
{
if
(
microphoneState
.
value
===
'loading'
||
microphoneState
.
value
===
'disabled'
||
microphoneState
.
value
===
'reply'
)
{
if
(
return
;
microphoneState
.
value
===
'loading'
||
microphoneState
.
value
===
'disabled'
||
microphoneState
.
value
===
'reply'
)
{
return
}
}
postAudio
(
audioDataChunk
)
;
postAudio
(
audioDataChunk
)
}
}
await
analyzeMicrophoneVolume
(
mediaStream
,
(
val
)
=>
{
await
analyzeMicrophoneVolume
(
mediaStream
,
(
val
)
=>
{
...
@@ -255,24 +258,24 @@ function endAudioInput() {
...
@@ -255,24 +258,24 @@ function endAudioInput() {
}
}
const
canplay
=
()
=>
{
const
canplay
=
()
=>
{
videos
[
1
].
value
!
.
style
.
opacity
=
'1'
;
videos
[
1
].
value
!
.
style
.
opacity
=
'1'
videos
[
0
].
value
!
.
style
.
opacity
=
'0'
;
videos
[
0
].
value
!
.
style
.
opacity
=
'0'
videos
[
0
].
value
!
.
pause
()
;
videos
[
0
].
value
!
.
pause
()
videos
[
1
].
value
!
.
play
()
;
videos
[
1
].
value
!
.
play
()
videos
[
1
].
value
!
.
removeEventListener
(
'canplay'
,
canplay
)
;
videos
[
1
].
value
!
.
removeEventListener
(
'canplay'
,
canplay
)
videos
.
unshift
(
videos
.
pop
()
!
)
;
videos
.
unshift
(
videos
.
pop
()
!
)
}
}
function
loadVideo
(
url
:
string
)
{
function
loadVideo
(
url
:
string
)
{
videos
[
1
].
value
!
.
src
=
url
videos
[
1
].
value
!
.
src
=
url
videos
[
1
].
value
!
.
style
.
opacity
=
'0'
;
videos
[
1
].
value
!
.
style
.
opacity
=
'0'
videos
[
1
].
value
!
.
addEventListener
(
'canplay'
,
canplay
)
;
videos
[
1
].
value
!
.
addEventListener
(
'canplay'
,
canplay
)
}
}
async
function
onAsr
(
question
:
string
)
{
async
function
onAsr
(
question
:
string
)
{
console
.
log
(
'---------------->'
,
question
)
console
.
log
(
'---------------->'
,
question
)
if
(
!
role
)
return
;
if
(
!
role
)
return
microphoneState
.
value
=
'loading'
;
microphoneState
.
value
=
'loading'
question
=
question
.
replace
(
/
\s
/g
,
''
)
question
=
question
.
replace
(
/
\s
/g
,
''
)
for
(
let
i
=
0
;
i
<
role
.
qa
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
role
.
qa
.
length
;
i
++
)
{
...
@@ -280,13 +283,13 @@ async function onAsr(question: string) {
...
@@ -280,13 +283,13 @@ async function onAsr(question: string) {
console
.
log
(
question
+
' : '
+
q
)
console
.
log
(
question
+
' : '
+
q
)
if
(
q
.
includes
(
question
))
{
if
(
q
.
includes
(
question
))
{
loadVideo
(
url
)
loadVideo
(
url
)
microphoneState
.
value
=
'reply'
;
microphoneState
.
value
=
'reply'
const
videoEle
=
videos
[
1
].
value
const
videoEle
=
videos
[
1
].
value
videoEle
!
.
loop
=
false
videoEle
!
.
loop
=
false
videoEle
!
.
muted
=
false
videoEle
!
.
muted
=
false
videoEle
!
.
onended
=
()
=>
{
videoEle
!
.
onended
=
()
=>
{
videoEle
!
.
onended
=
null
;
videoEle
!
.
onended
=
null
microphoneState
.
value
=
'input'
;
microphoneState
.
value
=
'input'
// 是否需要初始化
// 是否需要初始化
}
}
return
return
...
@@ -324,18 +327,23 @@ async function onAsr(question: string) {
...
@@ -324,18 +327,23 @@ async function onAsr(question: string) {
answer
+=
text
answer
+=
text
isTime
&&
console
.
time
(
'sliceAnswer'
)
isTime
&&
console
.
time
(
'sliceAnswer'
)
isTime
=
false
isTime
=
false
sliceAnswer
+=
text
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
text
)
&&
sliceAnswer
.
length
>=
10
)
{
const
textArr
=
text
.
split
(
''
);
console
.
timeEnd
(
'sliceAnswer'
)
for
(
let
i
=
0
;
i
<
textArr
.
length
;
i
++
)
{
answerArray
.
push
(
sliceAnswer
)
const
t
=
textArr
[
i
];
runTTSTask
(
answerArray
)
sliceAnswer
+=
t
sliceAnswer
=
''
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
t
)
&&
sliceAnswer
.
length
>=
settings
.
llmToTTSSliceLength
)
{
isTime
=
true
console
.
timeEnd
(
'sliceAnswer'
)
answerArray
.
push
(
sliceAnswer
)
runTTSTask
(
answerArray
)
sliceAnswer
=
''
isTime
=
true
}
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
log
(
'返回答案错误 -----> '
+
JSON
.
stringify
(
error
))
console
.
log
(
'返回答案错误 -----> '
+
JSON
.
stringify
(
error
))
microphoneState
.
value
=
'input'
;
microphoneState
.
value
=
'input'
}
}
}
}
...
@@ -360,11 +368,11 @@ async function runTTSTask(tasks: string[]) {
...
@@ -360,11 +368,11 @@ async function runTTSTask(tasks: string[]) {
while
(
tasks
.
length
)
{
while
(
tasks
.
length
)
{
const
task
=
tasks
.
shift
()
const
task
=
tasks
.
shift
()
if
(
!
task
)
break
if
(
!
task
)
break
if
(
task
.
length
<
1
)
continue
if
(
task
.
trim
().
length
<
1
)
continue
console
.
time
(
task
+
' TTS: '
)
console
.
time
(
task
+
' TTS: '
)
microphoneState
.
value
=
'loading'
;
microphoneState
.
value
=
'loading'
const
res
=
await
localTTS
({
const
res
=
await
localTTS
({
url
:
settings
.
ttsHost
,
url
:
settings
.
ttsHost
,
text
:
task
,
text
:
task
,
...
@@ -395,21 +403,21 @@ async function runAudioPlay() {
...
@@ -395,21 +403,21 @@ async function runAudioPlay() {
const
audio
=
ttsAudios
.
shift
()
const
audio
=
ttsAudios
.
shift
()
if
(
!
audio
)
{
if
(
!
audio
)
{
isPlayRunning
=
false
;
isPlayRunning
=
false
videos
[
0
].
value
!
.
pause
()
;
videos
[
0
].
value
!
.
pause
()
!
isTTSRunning
&&
(
microphoneState
.
value
=
'input'
)
;
!
isTTSRunning
&&
(
microphoneState
.
value
=
'input'
)
return
return
}
}
audio
.
onended
=
()
=>
{
audio
.
onended
=
()
=>
{
isPlayRunning
=
false
isPlayRunning
=
false
runAudioPlay
()
runAudioPlay
()
}
}
await
audio
.
play
()
;
await
audio
.
play
()
loadVideo
(
new
URL
(
'/libai/10.mp4'
,
import
.
meta
.
url
).
href
)
loadVideo
(
new
URL
(
'/libai/10.mp4'
,
import
.
meta
.
url
).
href
)
videos
[
1
].
value
!
.
loop
=
true
videos
[
1
].
value
!
.
loop
=
true
videos
[
1
].
value
!
.
muted
=
true
videos
[
1
].
value
!
.
muted
=
true
microphoneState
.
value
=
'reply'
;
microphoneState
.
value
=
'reply'
}
}
// eslint-disable-next-line no-unused-vars
// eslint-disable-next-line no-unused-vars
...
@@ -425,7 +433,6 @@ async function xfTTS(text: string) {
...
@@ -425,7 +433,6 @@ async function xfTTS(text: string) {
})
})
console
.
log
(
'----------------> tts:'
,
res
)
console
.
log
(
'----------------> tts:'
,
res
)
}
}
</
script
>
</
script
>
<
template
>
<
template
>
...
@@ -444,7 +451,11 @@ async function xfTTS(text: string) {
...
@@ -444,7 +451,11 @@ async function xfTTS(text: string) {
color=
"#fff"
color=
"#fff"
variant=
"elevated"
variant=
"elevated"
size=
"x-large"
size=
"x-large"
:disabled=
"microphoneState === 'loading' || microphoneState === 'disabled' || microphoneState === 'reply'"
:disabled=
"
microphoneState === 'loading' ||
microphoneState === 'disabled' ||
microphoneState === 'reply'
"
@
pointerdown=
"startVoskWsAudioInput"
@
pointerdown=
"startVoskWsAudioInput"
>
>
<v-icon
v-if=
"microphoneState === 'waitInput'"
icon=
"mdi-microphone"
></v-icon>
<v-icon
v-if=
"microphoneState === 'waitInput'"
icon=
"mdi-microphone"
></v-icon>
...
@@ -512,13 +523,15 @@ async function xfTTS(text: string) {
...
@@ -512,13 +523,15 @@ async function xfTTS(text: string) {
border-radius
:
36%
;
border-radius
:
36%
;
}
}
.video-ele
,
.video-ele2
{
.video-ele
,
.video-ele2
{
position
:
absolute
;
position
:
absolute
;
width
:
100%
;
width
:
100%
;
height
:
100%
;
height
:
100%
;
opacity
:
0
;
opacity
:
0
;
}
}
.video-ele.active
,
.video-ele2.active
{
.video-ele.active
,
.video-ele2.active
{
opacity
:
1
;
opacity
:
1
;
}
}
...
...
src/renderer/store/settings.ts
View file @
2a5e9aa6
...
@@ -25,6 +25,7 @@ export type ISettings = {
...
@@ -25,6 +25,7 @@ export type ISettings = {
isFullscreen
:
'yes'
|
'no'
isFullscreen
:
'yes'
|
'no'
isOpenDevTools
:
boolean
isOpenDevTools
:
boolean
llmUrl
:
string
llmUrl
:
string
llmToTTSSliceLength
:
number
voskWsLUrl
:
string
voskWsLUrl
:
string
}
}
...
@@ -58,7 +59,8 @@ const useSettingsStore = defineStore('settings', {
...
@@ -58,7 +59,8 @@ const useSettingsStore = defineStore('settings', {
selectSource
:
''
,
selectSource
:
''
,
isFullscreen
:
'no'
,
isFullscreen
:
'no'
,
isOpenDevTools
:
false
,
isOpenDevTools
:
false
,
llmUrl
:
'ws://127.0.0.1:9001/api/v1/stream'
,
llmUrl
:
'ws://127.0.0.1:9899/api/v1/stream'
,
llmToTTSSliceLength
:
20
,
voskWsLUrl
:
'ws://127.0.0.1:2700'
voskWsLUrl
:
'ws://127.0.0.1:2700'
})
as
ISettings
,
})
as
ISettings
,
getters
:
{},
getters
:
{},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment