唤醒流程更新,修复重复唤醒的问题
This commit is contained in:
@@ -89,7 +89,7 @@ class AIChatAssistantManager {
|
|||||||
|
|
||||||
Future<void> startVoskWakeword() async {
|
Future<void> startVoskWakeword() async {
|
||||||
FlutterVoskWakeword.instance.initialize(
|
FlutterVoskWakeword.instance.initialize(
|
||||||
wakeWords: ['你好众众', '你好', '众众'], // 唤醒词列表
|
wakeWords: ['你好众众', '你好', '众众', '测试', '哈喽', '唤醒'], // 唤醒词列表
|
||||||
);
|
);
|
||||||
|
|
||||||
debugPrint('Starting Vosk Wakeword detection...');
|
debugPrint('Starting Vosk Wakeword detection...');
|
||||||
@@ -99,12 +99,10 @@ class AIChatAssistantManager {
|
|||||||
debugPrint('Vosk Wakeword detected: ${event.text}');
|
debugPrint('Vosk Wakeword detected: ${event.text}');
|
||||||
_wakeWordDetected = true;
|
_wakeWordDetected = true;
|
||||||
// 通知所有监听者
|
// 通知所有监听者
|
||||||
// _wakeWordController.add(null);
|
_wakeWordController.add(null);
|
||||||
|
// 监听到之后就停止,这个时候已经弹出了对话框
|
||||||
// 可选:一段时间后自动重置状态
|
// 对话框消失就可以继续监听 recognitionStream 的订阅不会断开
|
||||||
Future.delayed(const Duration(seconds: 2), () {
|
FlutterVoskWakeword.instance.stop();
|
||||||
_wakeWordDetected = false;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:ai_chat_assistant/manager.dart';
|
import 'package:ai_chat_assistant/manager.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_vosk_wakeword/flutter_vosk_wakeword.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../pages/full_screen.dart';
|
import '../pages/full_screen.dart';
|
||||||
import '../services/message_service.dart';
|
import '../services/message_service.dart';
|
||||||
@@ -44,6 +45,7 @@ class _ChatPopupState extends State<ChatPopup> with SingleTickerProviderStateMix
|
|||||||
|
|
||||||
// 订阅唤醒词事件
|
// 订阅唤醒词事件
|
||||||
_wakeWordSubscription = AIChatAssistantManager.instance.onWakeWordDetected.listen((_) async {
|
_wakeWordSubscription = AIChatAssistantManager.instance.onWakeWordDetected.listen((_) async {
|
||||||
|
await FlutterVoskWakeword.instance.stop();
|
||||||
// 在这里处理唤醒事件,例如显示聊天窗口
|
// 在这里处理唤醒事件,例如显示聊天窗口
|
||||||
debugPrint("唤醒词被检测到,执行UI操作!");
|
debugPrint("唤醒词被检测到,执行UI操作!");
|
||||||
final messageService = MessageService.instance;
|
final messageService = MessageService.instance;
|
||||||
@@ -59,8 +61,9 @@ class _ChatPopupState extends State<ChatPopup> with SingleTickerProviderStateMix
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _insertOverlay() {
|
Future<void> _insertOverlay() async {
|
||||||
if (_isShowingPopup) return;
|
if (_isShowingPopup) return;
|
||||||
|
await FlutterVoskWakeword.instance.stop(); // 停止唤醒词监听,避免误触发
|
||||||
setState(() {
|
setState(() {
|
||||||
_isShowingPopup = true;
|
_isShowingPopup = true;
|
||||||
});
|
});
|
||||||
@@ -69,6 +72,7 @@ class _ChatPopupState extends State<ChatPopup> with SingleTickerProviderStateMix
|
|||||||
|
|
||||||
void _removeOverlay() {
|
void _removeOverlay() {
|
||||||
if (!_isShowingPopup) return;
|
if (!_isShowingPopup) return;
|
||||||
|
FlutterVoskWakeword.instance.start(); // 重新开始监听唤醒词
|
||||||
_partScreenAnimationController.reverse().then((_) {
|
_partScreenAnimationController.reverse().then((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -51,13 +51,31 @@ class AudioRecorder(private val recognizer: Recognizer,
|
|||||||
while (isRecording()) {
|
while (isRecording()) {
|
||||||
val nread = audioRecord?.read(buffer, 0, buffer.size) ?: 0
|
val nread = audioRecord?.read(buffer, 0, buffer.size) ?: 0
|
||||||
if (nread > 0) {
|
if (nread > 0) {
|
||||||
Log.i(TAG, "Read $nread bytes from AudioRecord")
|
// 判断是否有完整的识别结果
|
||||||
val ac = recognizer.acceptWaveForm(buffer, nread)
|
val ac = recognizer.acceptWaveForm(buffer, nread)
|
||||||
Log.i(TAG, "Recognizer acceptWaveForm returned: $ac")
|
val partialResultText = recognizer.partialResult
|
||||||
if (ac) {
|
val resultText = recognizer.result
|
||||||
listener.onResult(recognizer.result)
|
val pRes1 = VoskRecognizer.parsePartialResult(partialResultText)
|
||||||
} else {
|
val pRes2 = VoskRecognizer.parseFinalResult(partialResultText)
|
||||||
listener.onPartialResult(recognizer.partialResult)
|
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()
|
val jsonArray = JSONArray()
|
||||||
wakeWords.forEach { jsonArray.put(it) }
|
wakeWords.forEach { jsonArray.put(it) }
|
||||||
// 添加 [unk] 允许识别唤醒词之外的其他词语
|
// 添加 [unk] 允许识别唤醒词之外的其他词语
|
||||||
// jsonArray.put("[unk]")
|
jsonArray.put("[unk]")
|
||||||
return jsonArray.toString()
|
return jsonArray.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,6 @@ import org.vosk.Recognizer
|
|||||||
import org.vosk.android.RecognitionListener
|
import org.vosk.android.RecognitionListener
|
||||||
import org.vosk.android.StorageService
|
import org.vosk.android.StorageService
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.collections.contentToString
|
|
||||||
|
|
||||||
class WakewordService: Service(), RecognitionListener {
|
class WakewordService: Service(), RecognitionListener {
|
||||||
|
|
||||||
@@ -231,18 +230,10 @@ class WakewordService: Service(), RecognitionListener {
|
|||||||
private fun broadcastWake(word: String, now: Long, source: String) {
|
private fun broadcastWake(word: String, now: Long, source: String) {
|
||||||
lastWakeWord = word
|
lastWakeWord = word
|
||||||
lastDetectTs = now
|
lastDetectTs = now
|
||||||
|
recognizer?.reset()
|
||||||
Log.i(TAG, "Wake word detected($source): $word")
|
Log.i(TAG, "Wake word detected($source): $word")
|
||||||
EventReceiver.broadcastWakeWord(this, 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?) {
|
override fun onError(e: Exception?) {
|
||||||
|
|||||||
Reference in New Issue
Block a user