|
|
|
@ -27,6 +27,16 @@ public class AzureAsrHelper: NSObject { |
|
|
|
private var networkMonitor: NetworkStateMonitor = NetworkStateMonitor() |
|
|
|
private var continuousCallback: ContinuousRecognizeCallback? |
|
|
|
private var isNetworkRecovering = false |
|
|
|
|
|
|
|
// 网络丢失计数器 |
|
|
|
private var networkLostCount = 0 |
|
|
|
// 最大网络丢失次数(3秒检测) |
|
|
|
private let maxNetworkLostCount = 3 |
|
|
|
|
|
|
|
// 防抖机制相关变量 |
|
|
|
private var lastNetworkLostTime: Int64 = 0 |
|
|
|
private let networkLostDebounceInterval: Int64 = 3000 // 3秒防抖间隔 |
|
|
|
|
|
|
|
// 状态管理 |
|
|
|
private var _isContinuousRecognitionActive = false |
|
|
|
|
|
|
|
@ -141,61 +151,52 @@ public class AzureAsrHelper: NSObject { |
|
|
|
private func handleNetworkAvailable() { |
|
|
|
os_log("网络已恢复", log: log, type: .info) |
|
|
|
|
|
|
|
DispatchQueue.main.async { [weak self] in |
|
|
|
guard let self = self else { return } |
|
|
|
|
|
|
|
if !self.isNetworkRecovering { |
|
|
|
self.isNetworkRecovering = true |
|
|
|
os_log("网络恢复,准备重新启动识别", log: self.log, type: .info) |
|
|
|
|
|
|
|
// 如果之前在进行连续识别,重新启动 |
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { |
|
|
|
self.restartContinuousRecognition() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 重新启动连续识别(网络恢复时使用) |
|
|
|
|
|
|
|
/** |
|
|
|
* 处理网络连接丢失(带防抖机制) |
|
|
|
*/ |
|
|
|
private func restartContinuousRecognition() { |
|
|
|
os_log("重新启动连续识别", log: log, type: .info) |
|
|
|
private func handleNetworkLost() { |
|
|
|
let currentTime = Int64(Date().timeIntervalSince1970 * 1000) // 转换为毫秒 |
|
|
|
|
|
|
|
// 检查continuousCallback是否为空 |
|
|
|
guard let callback = continuousCallback else { |
|
|
|
os_log("连续识别回调为空,无法重新启动", log: log, type: .info) |
|
|
|
// 防抖机制:如果距离上次触发时间小于防抖间隔,则忽略本次触发 |
|
|
|
if currentTime - lastNetworkLostTime < networkLostDebounceInterval { |
|
|
|
os_log("网络丢失事件被防抖过滤,距离上次触发仅%{public}dms", log: log, type: .debug, currentTime - lastNetworkLostTime) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
do { |
|
|
|
_ = setupEventListeners(callback: callback) |
|
|
|
isNetworkRecovering = false |
|
|
|
os_log("连续识别重新启动成功", log: log, type: .info) |
|
|
|
} catch { |
|
|
|
os_log("重新启动连续识别失败: %@", log: log, type: .error, error.localizedDescription) |
|
|
|
isNetworkRecovering = false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 立即停止音频处理 |
|
|
|
*/ |
|
|
|
private func stopAudioProcessingImmediately() { |
|
|
|
audioStream?.stopMicrophoneCapture() |
|
|
|
// 更新最后触发时间 |
|
|
|
lastNetworkLostTime = currentTime |
|
|
|
|
|
|
|
if _isContinuousRecognitionActive { |
|
|
|
do { |
|
|
|
try recognizer?.stopContinuousRecognition() |
|
|
|
_isContinuousRecognitionActive = false |
|
|
|
os_log("因网络问题强制停止识别", log: log, type: .info) |
|
|
|
} catch { |
|
|
|
_isContinuousRecognitionActive = false |
|
|
|
os_log("强制停止识别失败: %@", log: log, type: .error, error.localizedDescription) |
|
|
|
// 这里启动一个3秒定时器,3秒后检查网络是否恢复 |
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [weak self] in |
|
|
|
guard let self = self else { return } |
|
|
|
|
|
|
|
os_log("网络不可用", log: self.log, type: .error) |
|
|
|
let isNetworkAvailable = self.checkNetworkStatus() |
|
|
|
|
|
|
|
if isNetworkAvailable { |
|
|
|
// 如果网络恢复,重置计数器和防抖时间 |
|
|
|
os_log("网络已恢复,重置计数器", log: self.log, type: .info) |
|
|
|
self.lastNetworkLostTime = 0 |
|
|
|
self.isNetworkRecovering = true |
|
|
|
} else { |
|
|
|
self.continuousCallback?.onError(1000, "网络连接不可用,请检查网络设置") |
|
|
|
os_log("网络仍未恢复,继续检测", log: self.log, type: .debug) |
|
|
|
self.networkLostCount = 0 |
|
|
|
self.stopContinuousRecognition() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 预初始化音频组件 |
|
|
|
* 在初始化阶段就准备好音频流和相关组件,减少首次启动延迟 |
|
|
|
@ -245,13 +246,11 @@ public class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
|
|
|
|
do { |
|
|
|
guard recognizer != nil else { |
|
|
|
if !setupRecognizer() { |
|
|
|
print("重置识别器失败") |
|
|
|
// callback.onError("重置识别器失败") |
|
|
|
return false |
|
|
|
if recognizer == nil { |
|
|
|
// 检查continuousCallback是否为空,只有在不为空时才设置事件监听器 |
|
|
|
if let callback = self.continuousCallback { |
|
|
|
setupEventListeners(callback: callback) |
|
|
|
} |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
print("startContinuousRecognition:\(audioSourceType)") |
|
|
|
@ -266,7 +265,7 @@ public class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
return true |
|
|
|
} catch { |
|
|
|
audioStream?.stopMicrophoneCapture() |
|
|
|
stopContinuousRecognition() |
|
|
|
_isContinuousRecognitionActive = false |
|
|
|
//callback.onError("启动连续识别失败: \(error.localizedDescription)") |
|
|
|
return false |
|
|
|
@ -300,13 +299,10 @@ public class AzureAsrHelper: NSObject { |
|
|
|
do { |
|
|
|
_isContinuousRecognitionActive = false |
|
|
|
|
|
|
|
if audioSourceType == .external { |
|
|
|
//pushAudioData(data: Data()) |
|
|
|
} |
|
|
|
|
|
|
|
audioStream?.stopMicrophoneCapture() |
|
|
|
// 停止连续识别 |
|
|
|
try recognizer.stopContinuousRecognition() |
|
|
|
self.recognizer = nil |
|
|
|
print("stopContinuousRecognition") |
|
|
|
return true |
|
|
|
} catch { |
|
|
|
@ -320,6 +316,7 @@ public class AzureAsrHelper: NSObject { |
|
|
|
// 【修复】在 catch 块中安全地停止识别器,避免再次抛出异常 |
|
|
|
do { |
|
|
|
try recognizer.stopContinuousRecognition() |
|
|
|
self.recognizer = nil |
|
|
|
} catch { |
|
|
|
// 如果强制停止也失败,记录错误但不再抛出异常 |
|
|
|
os_log("强制停止识别器失败: %@", log: log, type: .error, error.localizedDescription) |
|
|
|
@ -623,18 +620,9 @@ public class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
os_log("识别取消: %{public}@", log: self.log, type: .error, errorDetails) |
|
|
|
|
|
|
|
// 检查是否为网络相关错误 |
|
|
|
if self.isNetworkRelatedError(errorDetails) { |
|
|
|
os_log("检测到网络相关错误,停止音频处理", log: self.log, type: .info) |
|
|
|
self.stopAudioProcessingImmediately() |
|
|
|
|
|
|
|
// 如果不是正在恢复网络,通知上层网络错误 |
|
|
|
if !self.isNetworkRecovering { |
|
|
|
callback.onError(1000,"启动连续识别失败,网络连接已断开") |
|
|
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
callback.onCanceled(reason, errorDetails) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
return true |
|
|
|
} |
|
|
|
@ -845,75 +833,17 @@ extension AzureAsrHelper: NetworkStateMonitor.NetworkStateListener { |
|
|
|
func onNetworkAvailable() { |
|
|
|
os_log("网络已恢复", log: log, type: .info) |
|
|
|
|
|
|
|
DispatchQueue.main.async { [weak self] in |
|
|
|
self?.handleNetworkAvailable() |
|
|
|
} |
|
|
|
self.handleNetworkAvailable() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 处理网络丢失事件 |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
func onNetworkLost() { |
|
|
|
os_log("网络连接丢失", log: log, type: .error) |
|
|
|
|
|
|
|
// 立即停止音频处理 |
|
|
|
stopAudioProcessingImmediately() |
|
|
|
|
|
|
|
DispatchQueue.main.async { [weak self] in |
|
|
|
self?.continuousCallback?.onError(1000,"网络连接已断开") |
|
|
|
} |
|
|
|
self.handleNetworkLost() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
// //ios 语言检测 |
|
|
|
// /** |
|
|
|
// * 检测输入文本的语言,从支持的语言列表中选择最匹配的 |
|
|
|
// * |
|
|
|
// * @param text 需要检测的文本 |
|
|
|
// * @return 从supportedLanguages中选择的最匹配语言代码 |
|
|
|
// */ |
|
|
|
// public func detectTextLanguage(_ text: String) -> String? { |
|
|
|
// // 如果只有一种支持的语言,直接返回 |
|
|
|
// if supportedLanguages.count == 1 { |
|
|
|
// return supportedLanguages[0] |
|
|
|
// } |
|
|
|
|
|
|
|
// // 获取所有可能的语言及其置信度 |
|
|
|
// let languageResults = LanguageDetector.detectLanguageWithConfidence(from: text) |
|
|
|
|
|
|
|
// // 遍历检测结果,找到第一个在supportedLanguages中的语言 |
|
|
|
// for result in languageResults { |
|
|
|
// let azureLanguageCode = LanguageDetector.convertToAzureLanguageCode(result.language) |
|
|
|
// if supportedLanguages.contains(azureLanguageCode) { |
|
|
|
// return azureLanguageCode |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
// // 如果没有找到匹配的语言,返回第一个支持的语言作为默认值 |
|
|
|
// return supportedLanguages.first |
|
|
|
// } |
|
|
|
|
|
|
|
// /** |
|
|
|
// * 检测文本语言并返回详细信息 |
|
|
|
// * |
|
|
|
// * @param text 需要检测的文本 |
|
|
|
// * @return 包含语言代码、置信度和语言名称的字典 |
|
|
|
// */ |
|
|
|
// public func detectTextLanguageWithDetails(_ text: String) -> [String: Any]? { |
|
|
|
// let results = LanguageDetector.detectLanguageWithConfidence(from: text) |
|
|
|
// guard let topResult = results.first else { |
|
|
|
// return nil |
|
|
|
// } |
|
|
|
|
|
|
|
// let azureLanguageCode = LanguageDetector.convertToAzureLanguageCode(topResult.language) |
|
|
|
// let languageName = LanguageDetector.getLanguageName(from: topResult.language) |
|
|
|
|
|
|
|
// return [ |
|
|
|
// "languageCode": azureLanguageCode, |
|
|
|
// "confidence": topResult.confidence, |
|
|
|
// "languageName": languageName, |
|
|
|
// "originalCode": topResult.language |
|
|
|
// ] |
|
|
|
// } |
|
|
|
|
|
|
|
|