{
+ this.isContinuousModeActive = false
+ this.audioManager.setVADSilenceCallback(undefined)
+ this.audioManager.setBargeInCallback(undefined)
+ this.audioManager.setMuted(false)
+
if (this.voiceState !== 'idle') {
await this.stopRecording()
}
@@ -1137,6 +1264,18 @@ export class VocaTaAIChat {
return this.audioManager.recording
}
+ get micMuted(): boolean {
+ return this.audioManager.muted
+ }
+
+ muteMic(): void {
+ this.audioManager.setMuted(true)
+ }
+
+ unmuteMic(): void {
+ this.audioManager.setMuted(false)
+ }
+
// 清理资源
destroy(): void {
console.log('🧹 清理AI对话系统资源')
diff --git a/vocata-web/src/views/ChatPage.vue b/vocata-web/src/views/ChatPage.vue
index b74ebf1..f985865 100644
--- a/vocata-web/src/views/ChatPage.vue
+++ b/vocata-web/src/views/ChatPage.vue
@@ -126,9 +126,10 @@
@@ -136,10 +137,6 @@
-
-
- 提示:点击麦克风即可开始实时捕获语音,再点一次结束本轮捕获,等待 AI 回答
-
@@ -209,9 +206,8 @@ interface TypewriterState {
const typewriterState = ref(null)
const TYPEWRITER_SPEED = 35
-// VAD相关状态
-const vadActive = ref(false)
-const vadCheckInterval = ref(null)
+// VAD相关状态(已内置于 AudioManager,此处仅保留静音状态)
+const isMicMuted = ref(false)
// 引用
@@ -484,12 +480,11 @@ const characterInitials = computed(() => {
const voiceStatusText = computed(() => {
if (!isAIConnected.value) return '语音通道连接中…'
- if (aiChat.value?.recording) {
- return vadActive.value ? '正在实时捕获…' : '实时捕获中,准备说话'
- }
- if (isAISpeaking.value) return 'AI 正在回答'
+ if (isMicMuted.value) return '麦克风已静音'
+ if (aiChat.value?.recording) return '正在聆听...'
+ if (isAISpeaking.value) return 'AI 回答中'
if (isAIThinking.value) return 'AI 正在思考…'
- return '点击下方按钮开启实时语音对话'
+ return '语音对话中'
})
const visibleVoiceTranscripts = computed(() => voiceTranscripts.value.slice(-6))
@@ -729,13 +724,11 @@ const startAudioCall = async () => {
console.log('📞 开始音频通话')
await aiChat.value.prepareAudioPlayback()
- await aiChat.value.startAudioCall()
+ await aiChat.value.startAudioCall() // 内部立即开始录音
isAudioCallActive.value = true
+ isMicMuted.value = false
voiceTranscripts.value = []
- // 启动VAD状态监控
- startVADMonitoring()
-
} catch (error) {
console.error('❌ 启动音频通话失败:', error)
ElMessage.error('无法启动音频通话: ' + (error as Error).message)
@@ -749,34 +742,25 @@ const stopAudioCall = async () => {
console.log('📞 停止音频通话')
await aiChat.value.stopAudioCall()
isAudioCallActive.value = false
+ isMicMuted.value = false
currentSTTText.value = ''
isAISpeaking.value = false
- // 停止VAD监控
- stopVADMonitoring()
-
} catch (error) {
console.error('❌ 停止音频通话失败:', error)
ElMessage.error('停止音频通话失败: ' + (error as Error).message)
}
}
-const toggleMicrophone = async () => {
+const toggleMicrophone = () => {
if (!aiChat.value || !isAudioCallActive.value) return
- try {
- if (aiChat.value.recording) {
- // 当前在录音,停止录音
- console.log('🛑 停止录音')
- await aiChat.value.stopRecording()
- } else {
- // 当前没有录音,开始录音
- console.log('🎤 开始录音')
- await aiChat.value.startRecording()
- }
- } catch (error) {
- console.error('❌ 切换麦克风状态失败:', error)
- ElMessage.error('切换麦克风状态失败: ' + (error as Error).message)
+ if (isMicMuted.value) {
+ aiChat.value.unmuteMic()
+ isMicMuted.value = false
+ } else {
+ aiChat.value.muteMic()
+ isMicMuted.value = true
}
}
@@ -919,25 +903,6 @@ const scrollToBottomWithRetry = (maxRetries: number = 3) => {
})
}
-// VAD监控相关函数
-const startVADMonitoring = () => {
- if (vadCheckInterval.value) {
- clearInterval(vadCheckInterval.value)
- }
-
- vadCheckInterval.value = window.setInterval(() => {
- vadActive.value = aiChat.value?.voiceActive ?? false
- }, 100) // 每100ms检查一次VAD状态
-}
-
-const stopVADMonitoring = () => {
- if (vadCheckInterval.value) {
- clearInterval(vadCheckInterval.value)
- vadCheckInterval.value = null
- }
- vadActive.value = false
-}
-
// 格式化时间
const formatTime = (dateString: string) => {
return new Date(dateString).toLocaleTimeString([], {
@@ -2006,6 +1971,11 @@ const formatTime = (dateString: string) => {
background: rgba(255, 243, 241, 0.96);
}
+ &__control.is-mic.is-muted {
+ color: #ffffff;
+ background: #f56c6c;
+ }
+
&__control.is-cancel {
color: #ef4444;
}