|
|
|
@ -8,7 +8,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
// MARK: - 属性 |
|
|
|
|
|
|
|
/// 事件处理回调 |
|
|
|
private var eventHandler: (String, [String: Any]) -> Void |
|
|
|
private var eventHandler: (([String: Any]) -> Void)? |
|
|
|
|
|
|
|
/// 语音配置信息 |
|
|
|
private var speechSubscriptionKey: String = "" |
|
|
|
@ -33,14 +33,14 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
// MARK: - 初始化 |
|
|
|
|
|
|
|
init(eventHandler: @escaping (String, [String: Any]) -> Void) { |
|
|
|
self.eventHandler = eventHandler |
|
|
|
override init() { |
|
|
|
super.init() |
|
|
|
} |
|
|
|
|
|
|
|
deinit { |
|
|
|
dispose() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// MARK: - ASR Service 接口实现 |
|
|
|
|
|
|
|
@ -56,10 +56,15 @@ class AzureAsrHelper: NSObject { |
|
|
|
// 检查配置是否为空 |
|
|
|
if speechSubscriptionKey.isEmpty || serviceRegion.isEmpty { |
|
|
|
print("[AzureAsrHelper] 错误: Azure 配置信息不完整") |
|
|
|
eventHandler("error", ["message": "Azure 配置信息不完整"]) |
|
|
|
eventHandler?(["type": "error", "message": "Azure 配置信息不完整"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
// 检查语言参数 |
|
|
|
if let languages = supportedLanguages, languages.isEmpty { |
|
|
|
print("[AzureAsrHelper] 警告: 传入的支持语言列表为空,将使用默认语言") |
|
|
|
} |
|
|
|
|
|
|
|
// 释放之前的资源 |
|
|
|
dispose() |
|
|
|
|
|
|
|
@ -134,7 +139,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
return true |
|
|
|
} catch { |
|
|
|
print("[AzureAsrHelper] 错误: 创建识别器失败: \(error.localizedDescription)") |
|
|
|
eventHandler("error", ["message": "创建识别器失败: \(error.localizedDescription)"]) |
|
|
|
eventHandler?(["type": "error", "message": "创建识别器失败: \(error.localizedDescription)"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
@ -158,7 +163,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
if event.result.reason == SPXResultReason.recognizedSpeech { |
|
|
|
let detectedLanguage = self.getDetectedLanguage(from: event.result) |
|
|
|
print("[AzureAsrHelper] 识别结果: \(event.result.text ?? ""), 语言: \(detectedLanguage)") |
|
|
|
self.eventHandler("result", [ |
|
|
|
self.eventHandler?(["type": "result", |
|
|
|
"text": event.result.text ?? "", |
|
|
|
"detectedLanguage": detectedLanguage |
|
|
|
]) |
|
|
|
@ -172,7 +177,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
if event.result.reason == SPXResultReason.recognizingSpeech { |
|
|
|
let detectedLanguage = self.getDetectedLanguage(from: event.result) |
|
|
|
// print("[AzureAsrHelper] 识别中: \(event.result.text ?? ""), 语言: \(detectedLanguage)") |
|
|
|
self.eventHandler("recognizing", [ |
|
|
|
self.eventHandler?(["type": "recognizing", |
|
|
|
"text": event.result.text ?? "", |
|
|
|
"detectedLanguage": detectedLanguage |
|
|
|
]) |
|
|
|
@ -185,7 +190,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
print("[AzureAsrHelper] 识别会话已开始") |
|
|
|
self._isContinuousRecognitionActive = true |
|
|
|
self.eventHandler("sessionStarted", [:]) |
|
|
|
self.eventHandler?(["type": "sessionStarted"]) |
|
|
|
} |
|
|
|
|
|
|
|
recognizer.addSessionStoppedEventHandler { [weak self] _, _ in |
|
|
|
@ -193,7 +198,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
print("[AzureAsrHelper] 识别会话已结束") |
|
|
|
self._isContinuousRecognitionActive = false |
|
|
|
self.eventHandler("sessionStopped", [:]) |
|
|
|
self.eventHandler?(["type": "sessionStopped"]) |
|
|
|
} |
|
|
|
|
|
|
|
// 取消事件 |
|
|
|
@ -205,10 +210,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
print("[AzureAsrHelper] 识别取消: \(errorDetails)") |
|
|
|
|
|
|
|
self.eventHandler("canceled", [ |
|
|
|
"reason": reason, |
|
|
|
"errorDetails": errorDetails |
|
|
|
]) |
|
|
|
self.eventHandler?(["type": "canceled", "reason": reason, "errorDetails": errorDetails]) |
|
|
|
|
|
|
|
self._isContinuousRecognitionActive = false |
|
|
|
} |
|
|
|
@ -217,9 +219,17 @@ class AzureAsrHelper: NSObject { |
|
|
|
/// 执行一次性语音识别 |
|
|
|
/// - Returns: 是否成功启动识别 |
|
|
|
func recognizeOnce() -> Bool { |
|
|
|
// 检查是否初始化 |
|
|
|
if !isInitialized { |
|
|
|
print("[AzureAsrHelper] 错误: 语音服务未初始化") |
|
|
|
eventHandler("error", ["message": "语音服务未初始化"]) |
|
|
|
eventHandler?(["type": "error", "message": "语音服务未初始化"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
// 检查参数有效性 |
|
|
|
if speechSubscriptionKey.isEmpty || serviceRegion.isEmpty { |
|
|
|
print("[AzureAsrHelper] 错误: Azure 配置信息不完整") |
|
|
|
eventHandler?(["type": "error", "message": "Azure 配置信息不完整"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
@ -235,7 +245,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
do { |
|
|
|
// 通知会话开始 |
|
|
|
eventHandler("sessionStarted", [:]) |
|
|
|
eventHandler?(["type": "sessionStarted"]) |
|
|
|
|
|
|
|
// 执行识别 |
|
|
|
try recognizer?.recognizeOnceAsync { [weak self] result in |
|
|
|
@ -243,21 +253,21 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
if result.reason == SPXResultReason.recognizedSpeech { |
|
|
|
let detectedLanguage = self.getDetectedLanguage(from: result) |
|
|
|
self.eventHandler("result", [ |
|
|
|
self.eventHandler?(["type": "result", |
|
|
|
"text": result.text ?? "", |
|
|
|
"detectedLanguage": detectedLanguage |
|
|
|
]) |
|
|
|
} else if result.reason == SPXResultReason.noMatch { |
|
|
|
print("[AzureAsrHelper] 无匹配结果") |
|
|
|
self.eventHandler("noMatch", [:]) |
|
|
|
self.eventHandler?(["type": "noMatch"]) |
|
|
|
} else if result.reason == SPXResultReason.canceled { |
|
|
|
do { |
|
|
|
let details = try SPXCancellationDetails(fromCanceledRecognitionResult: result) |
|
|
|
let errorDetails = details.errorDetails ?? "未知错误" |
|
|
|
self.eventHandler("error", ["message": "识别取消: \(errorDetails)"]) |
|
|
|
self.eventHandler?(["type": "error", "message": "识别取消: \(errorDetails)"]) |
|
|
|
} catch { |
|
|
|
print("[AzureAsrHelper] 错误: 获取取消详情失败: \(error.localizedDescription)") |
|
|
|
self.eventHandler("error", ["message": "识别取消,无法获取详细原因"]) |
|
|
|
self.eventHandler?(["type": "error", "message": "识别取消,无法获取详细原因"]) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -265,7 +275,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
return true |
|
|
|
} catch { |
|
|
|
print("[AzureAsrHelper] 错误: 识别异常: \(error.localizedDescription)") |
|
|
|
eventHandler("error", ["message": "识别异常: \(error.localizedDescription)"]) |
|
|
|
eventHandler?(["type": "error", "message": "识别异常: \(error.localizedDescription)"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
@ -273,9 +283,17 @@ class AzureAsrHelper: NSObject { |
|
|
|
/// 开始连续语音识别 |
|
|
|
/// - Returns: 是否成功启动识别 |
|
|
|
func startContinuousRecognition() -> Bool { |
|
|
|
// 检查是否初始化 |
|
|
|
if !isInitialized { |
|
|
|
print("[AzureAsrHelper] 错误: 语音服务未初始化") |
|
|
|
eventHandler("error", ["message": "语音服务未初始化"]) |
|
|
|
eventHandler?(["type": "error", "message": "语音服务未初始化"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
// 检查参数有效性 |
|
|
|
if speechSubscriptionKey.isEmpty || serviceRegion.isEmpty { |
|
|
|
print("[AzureAsrHelper] 错误: Azure 配置信息不完整") |
|
|
|
eventHandler?(["type": "error", "message": "Azure 配置信息不完整"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
@ -289,7 +307,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
if recognizer == nil { |
|
|
|
print("[AzureAsrHelper] 尝试重新创建识别器...") |
|
|
|
if !createRecognizerAndSetupCallbacks() { |
|
|
|
eventHandler("error", ["message": "无法创建语音识别器"]) |
|
|
|
eventHandler?(["type": "error", "message": "无法创建语音识别器"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
@ -309,7 +327,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
print("[AzureAsrHelper] 错误: 开始连续识别失败: \(error.localizedDescription)") |
|
|
|
|
|
|
|
// 发送错误通知 |
|
|
|
eventHandler("error", ["message": "开始连续识别失败: \(error.localizedDescription)"]) |
|
|
|
eventHandler?(["type": "error", "message": "开始连续识别失败: \(error.localizedDescription)"]) |
|
|
|
|
|
|
|
return false |
|
|
|
} |
|
|
|
@ -319,6 +337,13 @@ class AzureAsrHelper: NSObject { |
|
|
|
/// 停止连续语音识别 |
|
|
|
/// - Returns: 是否成功停止识别 |
|
|
|
func stopContinuousRecognition() -> Bool { |
|
|
|
// 检查是否初始化 |
|
|
|
if !isInitialized { |
|
|
|
print("[AzureAsrHelper] 错误: 语音服务未初始化") |
|
|
|
eventHandler?(["type": "error", "message": "语音服务未初始化"]) |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
if !_isContinuousRecognitionActive { |
|
|
|
print("[AzureAsrHelper] 未进行连续识别,忽略停止请求") |
|
|
|
return true |
|
|
|
@ -330,7 +355,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
guard let recognizer = recognizer else { |
|
|
|
print("[AzureAsrHelper] 警告: 识别器为空,但状态显示活跃") |
|
|
|
_isContinuousRecognitionActive = false |
|
|
|
eventHandler("sessionStopped", [:]) |
|
|
|
eventHandler?(["type": "sessionStopped"]) |
|
|
|
return true |
|
|
|
} |
|
|
|
|
|
|
|
@ -355,7 +380,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
// 主线程回调通知结果 |
|
|
|
DispatchQueue.main.async { |
|
|
|
print("[AzureAsrHelper] 连续识别已停止") |
|
|
|
self.eventHandler("sessionStopped", [:]) |
|
|
|
self.eventHandler?(["type": "sessionStopped"]) |
|
|
|
} |
|
|
|
} catch { |
|
|
|
// 主线程回调通知错误 |
|
|
|
@ -363,7 +388,7 @@ class AzureAsrHelper: NSObject { |
|
|
|
print("[AzureAsrHelper] 错误: 停止连续识别失败: \(error.localizedDescription)") |
|
|
|
// 停止失败,恢复状态 |
|
|
|
self._isContinuousRecognitionActive = true |
|
|
|
self.eventHandler("error", ["error": "停止识别失败: \(error.localizedDescription)"]) |
|
|
|
self.eventHandler?(["type": "error", "message": "停止识别失败: \(error.localizedDescription)"]) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -380,9 +405,17 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
/// 释放资源 |
|
|
|
func dispose() { |
|
|
|
// 停止所有识别操作 |
|
|
|
// 尝试停止所有识别操作 |
|
|
|
if _isContinuousRecognitionActive { |
|
|
|
stopContinuousRecognition() |
|
|
|
do { |
|
|
|
if let recognizer = recognizer { |
|
|
|
try recognizer.stopContinuousRecognition() |
|
|
|
print("[AzureAsrHelper] 连续识别已停止") |
|
|
|
} |
|
|
|
} catch { |
|
|
|
print("[AzureAsrHelper] 警告: 停止连续识别失败: \(error.localizedDescription)") |
|
|
|
} |
|
|
|
_isContinuousRecognitionActive = false |
|
|
|
} |
|
|
|
|
|
|
|
// 释放识别器 |
|
|
|
@ -392,7 +425,6 @@ class AzureAsrHelper: NSObject { |
|
|
|
|
|
|
|
// 重置状态 |
|
|
|
isInitialized = false |
|
|
|
_isContinuousRecognitionActive = false |
|
|
|
|
|
|
|
print("[AzureAsrHelper] 资源已释放") |
|
|
|
} |
|
|
|
@ -411,4 +443,9 @@ class AzureAsrHelper: NSObject { |
|
|
|
return currentLanguage |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 设置事件处理器 |
|
|
|
@objc func setEventHandler(_ handler: @escaping ([String: Any]) -> Void) { |
|
|
|
self.eventHandler = handler |
|
|
|
} |
|
|
|
} |