唤醒流程更新,修复重复唤醒的问题

This commit is contained in:
2025-09-29 14:11:33 +08:00
parent 5be50b4dbb
commit ba73d6d780
5 changed files with 36 additions and 25 deletions

View File

@@ -89,7 +89,7 @@ class AIChatAssistantManager {
Future<void> startVoskWakeword() async {
FlutterVoskWakeword.instance.initialize(
wakeWords: ['你好众众', '你好', '众众'], // 唤醒词列表
wakeWords: ['你好众众', '你好', '众众', '测试', '哈喽', '唤醒'], // 唤醒词列表
);
debugPrint('Starting Vosk Wakeword detection...');
@@ -99,12 +99,10 @@ class AIChatAssistantManager {
debugPrint('Vosk Wakeword detected: ${event.text}');
_wakeWordDetected = true;
// 通知所有监听者
// _wakeWordController.add(null);
// 可选:一段时间后自动重置状态
Future.delayed(const Duration(seconds: 2), () {
_wakeWordDetected = false;
});
_wakeWordController.add(null);
// 监听到之后就停止,这个时候已经弹出了对话框
// 对话框消失就可以继续监听 recognitionStream 的订阅不会断开
FlutterVoskWakeword.instance.stop();
});
}

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:ui';
import 'package:ai_chat_assistant/manager.dart';
import 'package:flutter/material.dart';
import 'package:flutter_vosk_wakeword/flutter_vosk_wakeword.dart';
import 'package:provider/provider.dart';
import '../pages/full_screen.dart';
import '../services/message_service.dart';
@@ -44,6 +45,7 @@ class _ChatPopupState extends State<ChatPopup> with SingleTickerProviderStateMix
// 订阅唤醒词事件
_wakeWordSubscription = AIChatAssistantManager.instance.onWakeWordDetected.listen((_) async {
await FlutterVoskWakeword.instance.stop();
// 在这里处理唤醒事件,例如显示聊天窗口
debugPrint("唤醒词被检测到执行UI操作");
final messageService = MessageService.instance;
@@ -59,8 +61,9 @@ class _ChatPopupState extends State<ChatPopup> with SingleTickerProviderStateMix
super.dispose();
}
void _insertOverlay() {
Future<void> _insertOverlay() async {
if (_isShowingPopup) return;
await FlutterVoskWakeword.instance.stop(); // 停止唤醒词监听,避免误触发
setState(() {
_isShowingPopup = true;
});
@@ -69,6 +72,7 @@ class _ChatPopupState extends State<ChatPopup> with SingleTickerProviderStateMix
void _removeOverlay() {
if (!_isShowingPopup) return;
FlutterVoskWakeword.instance.start(); // 重新开始监听唤醒词
_partScreenAnimationController.reverse().then((_) {
if (mounted) {
setState(() {

View File

@@ -51,13 +51,31 @@ class AudioRecorder(private val recognizer: Recognizer,
while (isRecording()) {
val nread = audioRecord?.read(buffer, 0, buffer.size) ?: 0
if (nread > 0) {
Log.i(TAG, "Read $nread bytes from AudioRecord")
// 判断是否有完整的识别结果
val ac = recognizer.acceptWaveForm(buffer, nread)
Log.i(TAG, "Recognizer acceptWaveForm returned: $ac")
if (ac) {
listener.onResult(recognizer.result)
} else {
listener.onPartialResult(recognizer.partialResult)
val partialResultText = recognizer.partialResult
val resultText = recognizer.result
val pRes1 = VoskRecognizer.parsePartialResult(partialResultText)
val pRes2 = VoskRecognizer.parseFinalResult(partialResultText)
val finalResult = VoskRecognizer.parseFinalResult(resultText)
val partialResult = pRes1.ifEmpty { pRes2.ifEmpty { "" } }
// Log.i(TAG, "Partial: $partialResultText, Parsed: $partialResult; Final: $resultText, Parsed: $finalResult; Accepted: $ac")
if (partialResult.isNotBlank() || finalResult.isNotBlank()) {
Log.i(
TAG,
"Partial result: ${partialResult}, Final result: $finalResult"
)
// 去除 [unk] 的影响
if (partialResult == "[unk]" || finalResult == "[unk]") {
continue
}
if (ac) {
listener.onResult(resultText)
recognizer.reset()
} else {
listener.onPartialResult(partialResultText)
// recognizer.reset()
}
}
}
}

View File

@@ -21,7 +21,7 @@ class WakewordDetector(private val wakeWords: ArrayList<String>) {
val jsonArray = JSONArray()
wakeWords.forEach { jsonArray.put(it) }
// 添加 [unk] 允许识别唤醒词之外的其他词语
// jsonArray.put("[unk]")
jsonArray.put("[unk]")
return jsonArray.toString()
}
}

View File

@@ -16,7 +16,6 @@ import org.vosk.Recognizer
import org.vosk.android.RecognitionListener
import org.vosk.android.StorageService
import java.io.IOException
import kotlin.collections.contentToString
class WakewordService: Service(), RecognitionListener {
@@ -231,18 +230,10 @@ class WakewordService: Service(), RecognitionListener {
private fun broadcastWake(word: String, now: Long, source: String) {
lastWakeWord = word
lastDetectTs = now
recognizer?.reset()
Log.i(TAG, "Wake word detected($source): $word")
EventReceiver.broadcastWakeWord(this, word)
// 重置识别器状态,为下一次唤醒做准备
recognizer?.reset()
// 可选:重建 recognizer 减少同一发音尾部抖动:
// try {
// val grammar = wakeWordDetector?.getVoskGrammar()
// recognizer?.close()
// recognizer = Recognizer(model, 16000.0f, grammar)
// } catch (e: Exception) {
// Log.w(TAG, "Recognizer reset failed: ${e.message}")
// }
}
override fun onError(e: Exception?) {