|
|
|
@ -65,6 +65,9 @@ class AgentServiceImpl: NSObject { |
|
|
|
private var idleTimer: DispatchSourceTimer? |
|
|
|
private let maxIdleSeconds: TimeInterval = 10 |
|
|
|
|
|
|
|
// 语音识别模式 |
|
|
|
private var currentRecognitionMode: String = "normal" |
|
|
|
|
|
|
|
internal var audioPlayer: AudioPlayer? |
|
|
|
// private var locationInfo:[String: String]? |
|
|
|
|
|
|
|
@ -280,26 +283,56 @@ class AgentServiceImpl: NSObject { |
|
|
|
return true |
|
|
|
} |
|
|
|
|
|
|
|
private func startIdleCheck() { |
|
|
|
private func startIdleCheckForMode(mode: String) { |
|
|
|
stopIdleCheck() |
|
|
|
|
|
|
|
guard isRecognizing else { return } |
|
|
|
|
|
|
|
|
|
|
|
// 根据模式决定空闲检测策略 |
|
|
|
switch mode { |
|
|
|
case "phone_call": |
|
|
|
// 通话模式:禁用空闲检测,保持持续激活 |
|
|
|
os_log("通话模式:禁用空闲检测", log: logger, type: .info) |
|
|
|
return |
|
|
|
case "ble_wakeup": |
|
|
|
// BLE唤醒模式:使用默认的空闲检测 |
|
|
|
startIdleCheck(idleSeconds: maxIdleSeconds) |
|
|
|
case "push_to_talk": |
|
|
|
// 按住说话模式:使用默认的空闲检测 |
|
|
|
startIdleCheck(idleSeconds: maxIdleSeconds) |
|
|
|
case "normal": |
|
|
|
// 普通模式:使用默认的空闲检测 |
|
|
|
startIdleCheck(idleSeconds: maxIdleSeconds) |
|
|
|
default: |
|
|
|
// 未知模式:使用默认的空闲检测 |
|
|
|
os_log("未知识别模式: %{public}@,使用默认空闲检测", log: logger, type: .error, mode) |
|
|
|
startIdleCheck(idleSeconds: maxIdleSeconds) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private func startIdleCheck(idleSeconds: TimeInterval = 10) { |
|
|
|
stopIdleCheck() |
|
|
|
|
|
|
|
guard isRecognizing else { return } |
|
|
|
|
|
|
|
os_log("启动空闲检测,超时时间: %.0f秒,模式: %{public}@", log: logger, type: .info, idleSeconds, currentRecognitionMode) |
|
|
|
|
|
|
|
// 使用全局队列而不是主队列,避免后台挂起问题 |
|
|
|
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .utility)) |
|
|
|
idleTimer = timer |
|
|
|
|
|
|
|
timer.schedule(deadline: .now() + maxIdleSeconds) |
|
|
|
|
|
|
|
timer.schedule(deadline: .now() + idleSeconds) |
|
|
|
timer.setEventHandler { [weak self] in |
|
|
|
// 使用更安全的方式检查self是否存在 |
|
|
|
DispatchQueue.main.async { [weak self] in |
|
|
|
guard let self = self else { return } |
|
|
|
|
|
|
|
|
|
|
|
if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming { |
|
|
|
os_log("空闲检测触发自动停止,模式: %{public}@", log: self.logger, type: .info, self.currentRecognitionMode) |
|
|
|
self.stopRecognition() |
|
|
|
self.sendEvent(name: "auto_stop", data: [ |
|
|
|
"reason": "idle_timeout", |
|
|
|
"seconds": self.maxIdleSeconds |
|
|
|
"reason": "idle_timeout", |
|
|
|
"seconds": idleSeconds, |
|
|
|
"mode": self.currentRecognitionMode |
|
|
|
]) |
|
|
|
} |
|
|
|
} |
|
|
|
@ -314,10 +347,10 @@ class AgentServiceImpl: NSObject { |
|
|
|
|
|
|
|
private func restartIdleCheck() { |
|
|
|
if isRecognizing { |
|
|
|
startIdleCheck() |
|
|
|
startIdleCheckForMode(mode: currentRecognitionMode) |
|
|
|
} |
|
|
|
} |
|
|
|
func startRecognition(useBle: Bool = false) -> Bool { |
|
|
|
func startRecognition(useBle: Bool = false, mode: String = "normal") -> Bool { |
|
|
|
if !isInitialized { |
|
|
|
sendError("服务未初始化", code: "NOT_INITIALIZED") |
|
|
|
return false |
|
|
|
@ -333,9 +366,13 @@ class AgentServiceImpl: NSObject { |
|
|
|
if isRecognizing { |
|
|
|
stopRecognition() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 设置当前识别模式 |
|
|
|
currentRecognitionMode = mode |
|
|
|
os_log("设置语音识别模式: %{public}@", log: logger, type: .info, mode) |
|
|
|
|
|
|
|
let audioSourceType: AzureAsrHelper.AudioSourceType = useBle ? .external : .microphone |
|
|
|
|
|
|
|
|
|
|
|
if useBle { |
|
|
|
AudioSessionHub.shared.begin(.playback) |
|
|
|
} else { |
|
|
|
@ -349,7 +386,10 @@ print("ai启动语音=\(audioSourceType)=\(useBle)") |
|
|
|
sendError("启动语音识别失败", code: "RECOGNITION_START_ERROR") |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 根据模式启动相应的空闲检测 |
|
|
|
startIdleCheckForMode(mode: mode) |
|
|
|
|
|
|
|
return true |
|
|
|
} |
|
|
|
func recognizeCallback() -> Bool { |
|
|
|
@ -367,11 +407,18 @@ print("ai启动语音=\(audioSourceType)=\(useBle)") |
|
|
|
if !isRecognizing { |
|
|
|
return true |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
os_log("停止语音识别,当前模式: %{public}@", log: logger, type: .info, currentRecognitionMode) |
|
|
|
|
|
|
|
guard let success = azureAsrHelper?.stopContinuousRecognition(), success else { |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
stopIdleCheck() |
|
|
|
|
|
|
|
// 重置识别模式 |
|
|
|
currentRecognitionMode = "normal" |
|
|
|
|
|
|
|
return true |
|
|
|
} |
|
|
|
func disableBluetoothAudio() { |
|
|
|
@ -1388,10 +1435,10 @@ extension AgentServiceImpl: BleService.Callback { |
|
|
|
|
|
|
|
func onWakeupSignalReceived() { |
|
|
|
stopTts() |
|
|
|
|
|
|
|
|
|
|
|
stopRecognition() |
|
|
|
|
|
|
|
startRecognition(useBle: true) |
|
|
|
|
|
|
|
startRecognition(useBle: true, mode: "ble_wakeup") |
|
|
|
} |
|
|
|
|
|
|
|
func onDeviceInfoReceived(infoType: Int, infoData: [String: Any]) { |
|
|
|
@ -1620,7 +1667,8 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { |
|
|
|
sendEvent(name: "recognition_started", data: ["status": "started"]) |
|
|
|
isRecognizing = true |
|
|
|
hasSpeechDetected = false |
|
|
|
startIdleCheck() |
|
|
|
// 使用模式感知的空闲检测启动方法 |
|
|
|
startIdleCheckForMode(mode: currentRecognitionMode) |
|
|
|
audioPlayer?.playStartSound() |
|
|
|
} |
|
|
|
|
|
|
|
|