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
Show 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') {
async
function
changeOpenDevTools
()
{
await
window
.
mainApi
.
send
(
'openDevTools'
,
setting
.
isOpenDevTools
.
value
)
}
function
clear
()
{
localStorage
.
clear
()
location
.
reload
()
}
</
script
>
<
template
>
<v-app-bar
color=
"#d71b1b"
density=
"compact"
class=
"header"
>
...
...
@@ -181,6 +186,27 @@ async function changeOpenDevTools() {
:model-value=
"setting.llmUrl"
></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-model=
"setting.isFullscreen.value"
hide-details
...
...
@@ -202,6 +228,7 @@ async function changeOpenDevTools() {
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color=
"#d71b1b"
text=
"清除缓存并刷新"
@
click=
"clear"
></v-btn>
<v-btn
text=
"关闭"
@
click=
"isActive.value = false"
></v-btn>
</v-card-actions>
</v-card>
...
...
src/renderer/screens/ShowPhoto.vue
View file @
2a5e9aa6
...
...
@@ -252,7 +252,7 @@ async function startVoskWsAudioInput() {
return
}
await
initVoskWS
()
;
await
initVoskWS
()
sampleRate
=
8000
const
mediaStream
=
await
navigator
.
mediaDevices
.
getUserMedia
({
audio
:
{
...
...
@@ -369,15 +369,19 @@ async function onAsr(question: string) {
answer
+=
text
isTime
&&
console
.
time
(
'sliceAnswer'
)
isTime
=
false
sliceAnswer
+=
text
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
text
)
&&
sliceAnswer
.
length
>=
20
)
{
const
textArr
=
text
.
split
(
''
);
for
(
let
i
=
0
;
i
<
textArr
.
length
;
i
++
)
{
const
t
=
textArr
[
i
];
sliceAnswer
+=
t
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
t
)
&&
sliceAnswer
.
length
>=
settings
.
llmToTTSSliceLength
)
{
console
.
timeEnd
(
'sliceAnswer'
)
answerArray
.
push
(
sliceAnswer
)
runTTSTask
(
answerArray
)
sliceAnswer
=
''
isTime
=
true
}
}
}
catch
(
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)
const
microphoneState
=
ref
<
'waitInput'
|
'input'
|
'loading'
|
'disabled'
|
'reply'
>
(
'waitInput'
)
const
videoElement
=
ref
<
HTMLVideoElement
|
null
>
(
null
)
const
videoElement2
=
ref
<
HTMLVideoElement
|
null
>
(
null
)
const
videos
=
[
videoElement
,
videoElement2
];
const
videos
=
[
videoElement
,
videoElement2
]
onMounted
(()
=>
{
// init();
...
...
@@ -166,7 +165,7 @@ async function startVoskWsAudioInput() {
}
initVoskWS
()
sampleRate
=
8
000
sampleRate
=
16
000
const
mediaStream
=
await
navigator
.
mediaDevices
.
getUserMedia
({
audio
:
{
echoCancellation
:
true
,
...
...
@@ -184,11 +183,15 @@ async function startVoskWsAudioInput() {
processor
.
connect
(
audioContext
.
destination
)
processor
.
onaudioprocess
=
(
audioDataChunk
)
=>
{
if
(
microphoneState
.
value
===
'loading'
||
microphoneState
.
value
===
'disabled'
||
microphoneState
.
value
===
'reply'
)
{
return
;
if
(
microphoneState
.
value
===
'loading'
||
microphoneState
.
value
===
'disabled'
||
microphoneState
.
value
===
'reply'
)
{
return
}
postAudio
(
audioDataChunk
)
;
postAudio
(
audioDataChunk
)
}
await
analyzeMicrophoneVolume
(
mediaStream
,
(
val
)
=>
{
...
...
@@ -255,24 +258,24 @@ function endAudioInput() {
}
const
canplay
=
()
=>
{
videos
[
1
].
value
!
.
style
.
opacity
=
'1'
;
videos
[
0
].
value
!
.
style
.
opacity
=
'0'
;
videos
[
0
].
value
!
.
pause
()
;
videos
[
1
].
value
!
.
play
()
;
videos
[
1
].
value
!
.
removeEventListener
(
'canplay'
,
canplay
)
;
videos
.
unshift
(
videos
.
pop
()
!
)
;
videos
[
1
].
value
!
.
style
.
opacity
=
'1'
videos
[
0
].
value
!
.
style
.
opacity
=
'0'
videos
[
0
].
value
!
.
pause
()
videos
[
1
].
value
!
.
play
()
videos
[
1
].
value
!
.
removeEventListener
(
'canplay'
,
canplay
)
videos
.
unshift
(
videos
.
pop
()
!
)
}
function
loadVideo
(
url
:
string
)
{
videos
[
1
].
value
!
.
src
=
url
videos
[
1
].
value
!
.
style
.
opacity
=
'0'
;
videos
[
1
].
value
!
.
addEventListener
(
'canplay'
,
canplay
)
;
videos
[
1
].
value
!
.
style
.
opacity
=
'0'
videos
[
1
].
value
!
.
addEventListener
(
'canplay'
,
canplay
)
}
async
function
onAsr
(
question
:
string
)
{
console
.
log
(
'---------------->'
,
question
)
if
(
!
role
)
return
;
microphoneState
.
value
=
'loading'
;
if
(
!
role
)
return
microphoneState
.
value
=
'loading'
question
=
question
.
replace
(
/
\s
/g
,
''
)
for
(
let
i
=
0
;
i
<
role
.
qa
.
length
;
i
++
)
{
...
...
@@ -280,13 +283,13 @@ async function onAsr(question: string) {
console
.
log
(
question
+
' : '
+
q
)
if
(
q
.
includes
(
question
))
{
loadVideo
(
url
)
microphoneState
.
value
=
'reply'
;
microphoneState
.
value
=
'reply'
const
videoEle
=
videos
[
1
].
value
videoEle
!
.
loop
=
false
videoEle
!
.
muted
=
false
videoEle
!
.
onended
=
()
=>
{
videoEle
!
.
onended
=
null
;
microphoneState
.
value
=
'input'
;
videoEle
!
.
onended
=
null
microphoneState
.
value
=
'input'
// 是否需要初始化
}
return
...
...
@@ -324,18 +327,23 @@ async function onAsr(question: string) {
answer
+=
text
isTime
&&
console
.
time
(
'sliceAnswer'
)
isTime
=
false
sliceAnswer
+=
text
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
text
)
&&
sliceAnswer
.
length
>=
10
)
{
const
textArr
=
text
.
split
(
''
);
for
(
let
i
=
0
;
i
<
textArr
.
length
;
i
++
)
{
const
t
=
textArr
[
i
];
sliceAnswer
+=
t
if
(
/
[
。,?!;,.?!;
]
/
.
test
(
t
)
&&
sliceAnswer
.
length
>=
settings
.
llmToTTSSliceLength
)
{
console
.
timeEnd
(
'sliceAnswer'
)
answerArray
.
push
(
sliceAnswer
)
runTTSTask
(
answerArray
)
sliceAnswer
=
''
isTime
=
true
}
}
}
catch
(
error
)
{
console
.
log
(
'返回答案错误 -----> '
+
JSON
.
stringify
(
error
))
microphoneState
.
value
=
'input'
;
microphoneState
.
value
=
'input'
}
}
...
...
@@ -360,11 +368,11 @@ async function runTTSTask(tasks: string[]) {
while
(
tasks
.
length
)
{
const
task
=
tasks
.
shift
()
if
(
!
task
)
break
if
(
task
.
length
<
1
)
continue
if
(
task
.
trim
().
length
<
1
)
continue
console
.
time
(
task
+
' TTS: '
)
microphoneState
.
value
=
'loading'
;
microphoneState
.
value
=
'loading'
const
res
=
await
localTTS
({
url
:
settings
.
ttsHost
,
text
:
task
,
...
...
@@ -395,21 +403,21 @@ async function runAudioPlay() {
const
audio
=
ttsAudios
.
shift
()
if
(
!
audio
)
{
isPlayRunning
=
false
;
videos
[
0
].
value
!
.
pause
()
;
!
isTTSRunning
&&
(
microphoneState
.
value
=
'input'
)
;
isPlayRunning
=
false
videos
[
0
].
value
!
.
pause
()
!
isTTSRunning
&&
(
microphoneState
.
value
=
'input'
)
return
}
audio
.
onended
=
()
=>
{
isPlayRunning
=
false
runAudioPlay
()
}
await
audio
.
play
()
;
await
audio
.
play
()
loadVideo
(
new
URL
(
'/libai/10.mp4'
,
import
.
meta
.
url
).
href
)
videos
[
1
].
value
!
.
loop
=
true
videos
[
1
].
value
!
.
muted
=
true
microphoneState
.
value
=
'reply'
;
microphoneState
.
value
=
'reply'
}
// eslint-disable-next-line no-unused-vars
...
...
@@ -425,7 +433,6 @@ async function xfTTS(text: string) {
})
console
.
log
(
'----------------> tts:'
,
res
)
}
</
script
>
<
template
>
...
...
@@ -444,7 +451,11 @@ async function xfTTS(text: string) {
color=
"#fff"
variant=
"elevated"
size=
"x-large"
:disabled=
"microphoneState === 'loading' || microphoneState === 'disabled' || microphoneState === 'reply'"
:disabled=
"
microphoneState === 'loading' ||
microphoneState === 'disabled' ||
microphoneState === 'reply'
"
@
pointerdown=
"startVoskWsAudioInput"
>
<v-icon
v-if=
"microphoneState === 'waitInput'"
icon=
"mdi-microphone"
></v-icon>
...
...
@@ -512,13 +523,15 @@ async function xfTTS(text: string) {
border-radius
:
36%
;
}
.video-ele
,
.video-ele2
{
.video-ele
,
.video-ele2
{
position
:
absolute
;
width
:
100%
;
height
:
100%
;
opacity
:
0
;
}
.video-ele.active
,
.video-ele2.active
{
.video-ele.active
,
.video-ele2.active
{
opacity
:
1
;
}
...
...
src/renderer/store/settings.ts
View file @
2a5e9aa6
...
...
@@ -25,6 +25,7 @@ export type ISettings = {
isFullscreen
:
'yes'
|
'no'
isOpenDevTools
:
boolean
llmUrl
:
string
llmToTTSSliceLength
:
number
voskWsLUrl
:
string
}
...
...
@@ -58,7 +59,8 @@ const useSettingsStore = defineStore('settings', {
selectSource
:
''
,
isFullscreen
:
'no'
,
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'
})
as
ISettings
,
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