Commit afe81980 authored by ali's avatar ali

feat: tts 语音播放结束后,视频不不暂停问题;回答状态对应处理。

parent 83f308a7
......@@ -21,17 +21,16 @@ const iconMicrophone = new URL('/images/microphone-input.svg', import.meta.url).
const recordVolume = ref(0)
const url = route.query.url as string
const role = useVideo.list.find((i) => i.url === url)
const microphoneState = ref<'waitInput' | 'input' | 'loading' | 'disabled'>('waitInput')
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];
onMounted(() => {
// init();
})
async function init() {
const videoEle = videoElement.value
}
router.beforeEach((g) => {
if (!g.query.url) return router.push('/error')
})
......@@ -184,7 +183,13 @@ async function startVoskWsAudioInput() {
source.connect(processor)
processor.connect(audioContext.destination)
processor.onaudioprocess = (audioDataChunk) => postAudio(audioDataChunk)
processor.onaudioprocess = (audioDataChunk) => {
if (microphoneState.value === 'loading' || microphoneState.value === 'disabled' || microphoneState.value === 'reply') {
return;
}
postAudio(audioDataChunk);
}
await analyzeMicrophoneVolume(mediaStream, (val) => {
recordVolume.value = val
......@@ -249,29 +254,41 @@ function endAudioInput() {
}
}
function setVideoUrl(url: string) {
const videoEle = videoElement.value as HTMLVideoElement
if (!videoEle) return
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()!);
}
videoEle.src = url
videoEle.load()
videoEle.play()
function loadVideo(url: string) {
videos[1].value!.src = url
videos[1].value!.style.opacity = '0';
videos[1].value!.addEventListener('canplay', canplay);
}
async function onAsr(question: string) {
endAudioInput()
console.log('---------------->', question)
if (!role) return
if (!role) return;
microphoneState.value = 'loading';
question = question.replace(/\s/g, '')
for (let i = 0; i < role.qa.length; i++) {
const { q, url } = role.qa[i]
console.log(question + ' : ' + q)
if (q.includes(question)) {
const videoEle = videoElement.value as HTMLVideoElement
videoEle && (videoEle.loop = false)
videoEle && (videoEle.muted = false)
setVideoUrl(url)
loadVideo(url)
microphoneState.value = 'reply';
const videoEle = videos[1].value
videoEle!.loop = false
videoEle!.muted = false
videoEle!.onended = () => {
videoEle!.onended = null;
microphoneState.value = 'input';
// 是否需要初始化
}
return
}
}
......@@ -358,6 +375,7 @@ async function runTTSTask(tasks: string[]) {
runAudioPlay()
}
} catch (error) {
microphoneState.value = 'input'
console.error(error)
}
......@@ -371,20 +389,21 @@ async function runAudioPlay() {
isPlayRunning = true
const audio = ttsAudios.shift()
const videoEle = videoElement.value as HTMLVideoElement;
if (!audio) {
isPlayRunning = false;
videoEle.pause();
videos[0].value!.pause();
microphoneState.value = 'input';
return
}
audio.onended = () => {
isPlayRunning = false
videoEle && (videoEle.loop = true)
videoEle && (videoEle.muted = true)
setVideoUrl(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!.muted = true
runAudioPlay()
}
await audio.play()
await audio.play();
microphoneState.value = 'reply';
}
// eslint-disable-next-line no-unused-vars
......@@ -400,6 +419,7 @@ async function xfTTS(text: string) {
})
console.log('----------------> tts:', res)
}
</script>
<template>
......@@ -408,7 +428,8 @@ async function xfTTS(text: string) {
class="d-flex justify-center align-center"
:style="{ background: '#000' }"
>
<video id="videoElement" ref="videoElement" :src="url" class="video-ele"></video>
<video id="videoElement" ref="videoElement" :src="url" class="video-ele active"></video>
<video id="videoElement2" ref="videoElement2" class="video-ele2"></video>
</div>
<div class="voice">
......@@ -417,12 +438,13 @@ async function xfTTS(text: string) {
color="#fff"
variant="elevated"
size="x-large"
:disabled="microphoneState === 'loading' || microphoneState === 'disabled'"
:disabled="microphoneState === 'loading' || microphoneState === 'disabled' || microphoneState === 'reply'"
@pointerdown="startVoskWsAudioInput"
>
<v-icon v-if="microphoneState === 'waitInput'" icon="mdi-microphone"></v-icon>
<v-icon v-if="microphoneState === 'loading'" icon="mdi-microphone-settings"></v-icon>
<v-icon v-if="microphoneState === 'disabled'" icon="mdi-microphone-off"></v-icon>
<v-icon v-if="microphoneState === 'reply'" icon="mdi-message-reply-text-outline"></v-icon>
<template v-if="microphoneState === 'input'">
<img width="30" height="30" :src="iconMicrophone" alt="" srcset="" />
......@@ -483,8 +505,14 @@ async function xfTTS(text: string) {
border-radius: 36%;
}
.video-ele {
.video-ele, .video-ele2 {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
.video-ele.active, .video-ele2.active {
opacity: 1;
}
.q-list {
......
import { defineStore } from 'pinia'
type IVideo = {
list: { url: string; name: string; qa: { url: string; q: string; a: string }[] }[]
list: { url: string; poster: string; name: string; qa: { url: string; q: string; a: string }[] }[]
}
const useVideoStore = defineStore('video', {
......@@ -11,6 +11,7 @@ const useVideoStore = defineStore('video', {
list: [
{
url: new URL('/libai/wait.mp4', import.meta.url).href,
poster: new URL('/libai/poster.jpg', import.meta.url).href,
name: '李白',
qa: [
{
......
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