import Flutter import UIKit import AVFoundation import MicrosoftCognitiveServicesSpeech // 自定义语音处理组件,提供音频流处理等功能 import speech /** * Azure Speech Plugin * * 基于微软Azure语音服务的Flutter插件 * 提供语音识别(ASR)和语音合成(TTS)功能 */ @objc public class AzureSpeechPlugin: NSObject, FlutterPlugin { // 日志标签 private let tag = "AzureSpeechPlugin" // ASR相关 private var asrChannel: FlutterMethodChannel? private var asrEventChannel: FlutterEventChannel? private var asrEventSink: FlutterEventSink? private let azureAsrHelper = AzureAsrHelper() // TTS相关 private var ttsChannel: FlutterMethodChannel? private var ttsEventChannel: FlutterEventChannel? private var ttsEventSink: FlutterEventSink? private let azureTtsHelper = AzureTtsHelper() // 是否已添加TTS事件监听器 private var isTtsListenerAdded = false // 插件注册 public static func register(with registrar: FlutterPluginRegistrar) { let instance = AzureSpeechPlugin() // 初始化ASR通道 let asrChannel = FlutterMethodChannel(name: "azure_speech/asr", binaryMessenger: registrar.messenger()) registrar.addMethodCallDelegate(instance, channel: asrChannel) instance.asrChannel = asrChannel // 初始化TTS通道 let ttsChannel = FlutterMethodChannel(name: "azure_speech/tts", binaryMessenger: registrar.messenger()) registrar.addMethodCallDelegate(instance, channel: ttsChannel) instance.ttsChannel = ttsChannel // 初始化ASR事件通道 let asrEventChannel = FlutterEventChannel(name: "azure_speech/asr_events", binaryMessenger: registrar.messenger()) asrEventChannel.setStreamHandler(instance) instance.asrEventChannel = asrEventChannel // 初始化TTS事件通道 let ttsEventChannel = FlutterEventChannel(name: "azure_speech/tts_events", binaryMessenger: registrar.messenger()) ttsEventChannel.setStreamHandler(instance) instance.ttsEventChannel = ttsEventChannel } // 发送ASR事件方法 private func sendAsrEvent(_ event: [String: Any]) { if asrEventSink == nil { print("\(tag): 无法发送ASR事件:事件通道未准备好") return } DispatchQueue.main.async { [weak self] in guard let self = self else { return } self.asrEventSink?(event) } } // 发送TTS事件方法 private func sendTtsEvent(_ event: [String: Any]) { if ttsEventSink == nil { print("\(tag): 无法发送TTS事件:事件通道未准备好") return } DispatchQueue.main.async { [weak self] in guard let self = self else { return } self.ttsEventSink?(event) } } // 设置TTS事件监听器 private func setupTtsEventListener() { if !isTtsListenerAdded { azureTtsHelper.addListener(self) isTtsListenerAdded = true } } // 设置ASR事件监听器 private func setupAsrEventListener() { azureAsrHelper.addListener(self) } // 处理Flutter方法调用 public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { if call.method.hasPrefix("tts_") { handleTtsMethodCall(call, result) } else { handleAsrMethodCall(call, result) } } // MARK: - ASR 方法处理 private func handleAsrMethodCall(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { switch call.method { case "initialize": guard let args = call.arguments as? [String: Any], let subscriptionKey = args["subscriptionKey"] as? String, let region = args["region"] as? String else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "必要的参数不能为空", details: nil)) return } let supportedLanguages = args["supportedLanguages"] as? [String] // 初始化ASR引擎并设置事件监听 let success = azureAsrHelper.initialize(subscriptionKey: subscriptionKey, region: region, supportedLanguages: supportedLanguages) setupAsrEventListener() result(success) case "recognizeOnce": guard let args = call.arguments as? [String: Any], let audioBytes = args["data"] as? FlutterStandardTypedData else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "音频数据不能为空", details: nil)) return } // 执行一次性识别 let success = azureAsrHelper.recognizeOnce(audioData: audioBytes.data) { recognizedText in result(["text": recognizedText, "detectedLanguage": ""]) } if !success { result(FlutterError(code: "RECOGNITION_ERROR", message: "启动识别失败", details: nil)) } case "startContinuousRecognition": // 确保事件通道已准备好 if asrEventSink == nil { result(FlutterError(code: "EVENT_CHANNEL_NOT_READY", message: "事件通道未准备好,无法开始连续识别", details: nil)) return } let success = azureAsrHelper.startContinuousRecognition() result(success) case "stopContinuousRecognition": let success = azureAsrHelper.stopContinuousRecognition() result(success) case "isContinuousRecognitionActive": result(azureAsrHelper.isRecognitionActive()) case "dispose": azureAsrHelper.dispose() result(true) case "pushAudioData": guard let args = call.arguments as? [String: Any], let audioBytes = args["data"] as? FlutterStandardTypedData else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "音频数据不能为空", details: nil)) return } let success = azureAsrHelper.pushAudioData(audioData: audioBytes.data) result(success) default: result(FlutterMethodNotImplemented) } } // MARK: - TTS 方法处理 private func handleTtsMethodCall(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { switch call.method { case "tts_initialize": guard let args = call.arguments as? [String: Any], let subscriptionKey = args["subscriptionKey"] as? String, let region = args["region"] as? String else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "必要的参数不能为空", details: nil)) return } // 初始化TTS引擎 let language = args["language"] as? String ?? "zh-CN" let success = azureTtsHelper.initialize(ttsAppId: "", ttsAppToken: subscriptionKey, ttsResource: region, language: language) // 设置TTS事件监听器 setupTtsEventListener() result(success) case "tts_set_voice": guard let args = call.arguments as? [String: Any], let voiceName = args["voiceName"] as? String else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "语音名称不能为空", details: nil)) return } let success = azureTtsHelper.setVoice(voiceName) result(success) case "tts_speak_once": guard let args = call.arguments as? [String: Any], let text = args["text"] as? String else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "文本不能为空", details: nil)) return } let success = azureTtsHelper.speakOnce(text) result(success) case "tts_speak_stream": guard let args = call.arguments as? [String: Any], let text = args["text"] as? String else { result(FlutterError(code: "INVALID_ARGUMENTS", message: "文本不能为空", details: nil)) return } let success = azureTtsHelper.speakStream(text) result(success) case "tts_flush_stream": let success = azureTtsHelper.flushStream() result(success) case "tts_stop": let success = azureTtsHelper.stop() result(success) case "tts_isSpeaking": result(azureTtsHelper.isSpeaking()) case "tts_release": azureTtsHelper.dispose() result(true) default: result(FlutterMethodNotImplemented) } } } // MARK: - 事件处理 extension AzureSpeechPlugin: FlutterStreamHandler { public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { // 根据通道类型设置事件接收器 if let args = arguments as? [String: Any], let channel = args["channel"] as? String { if channel == "asr" { asrEventSink = events } else if channel == "tts" { ttsEventSink = events setupTtsEventListener() } } else if arguments == nil { // 如果没有指定通道,尝试弄清楚是哪个通道在监听 if asrEventSink == nil && ttsEventSink != nil { asrEventSink = events } else if asrEventSink != nil && ttsEventSink == nil { ttsEventSink = events setupTtsEventListener() } else { // 无法确定哪个通道,默认设置为ASR asrEventSink = events } } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { // 根据通道类型清除事件接收器 if let args = arguments as? [String: Any], let channel = args["channel"] as? String { if channel == "asr" { asrEventSink = nil } else if channel == "tts" { ttsEventSink = nil } } else { // 如果没有指定通道,清除所有通道 asrEventSink = nil ttsEventSink = nil } return nil } } // MARK: - ASR 事件监听实现 extension AzureSpeechPlugin: AsrEventListener { // 为ASR事件监听提供明确的选择器名称 @objc(onAsrEvent:) public func onEvent(_ event: AsrEvent) { var eventMap: [String: Any] = [ "type": getAsrEventTypeName(event.type) ] // 添加参数 for (key, value) in event.params { eventMap[key] = value } sendAsrEvent(eventMap) } private func getAsrEventTypeName(_ type: AsrEventType) -> String { switch type { case .sessionStarted: return "sessionStarted" case .sessionStopped: return "sessionStopped" case .recognizing: return "recognizing" case .recognized: return "result" case .canceled: return "canceled" case .error: return "error" @unknown default: return "unknown" } } } // MARK: - TTS 事件监听实现 extension AzureSpeechPlugin: TtsEventListener { // 使用@objc特性为方法提供一个不同的Objective-C选择器名称 @objc(onTtsEvent:) public func onEvent(_ event: TtsEvent) { var eventMap: [String: Any] = [:] // 根据事件类型转换 switch event.type { case .synthesisStarted: eventMap["type"] = "synthesis_started" case .synthesisCompleted: eventMap["type"] = "synthesis_completed" case .synthesisCanceled: eventMap["type"] = "synthesis_canceled" if let reason = event.params["reason"] { eventMap["reason"] = reason } if let errorDetails = event.params["errorDetails"] { eventMap["errorDetails"] = errorDetails } case .error: eventMap["type"] = "error" if let errorCode = event.params["errorCode"] { eventMap["errorCode"] = errorCode } if let errorMessage = event.params["errorMessage"] { eventMap["errorMessage"] = errorMessage } @unknown default: eventMap["type"] = "unknown" for (key, value) in event.params { eventMap[key] = value } } sendTtsEvent(eventMap) } } // MARK: - 音频数据监听实现 extension AzureSpeechPlugin: AudioDataListener { public func onAudioData(_ data: Data) { if ttsEventSink == nil { return } let eventMap: [String: Any] = [ "type": "audio_data", "data": FlutterStandardTypedData(bytes: data) ] sendTtsEvent(eventMap) } }