唤醒流程更新,修复重复唤醒的问题
This commit is contained in:
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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(() {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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?) {
|
||||
|
||||
Reference in New Issue
Block a user