import Foundation import Speech import AVFAudio /// 流式语音识别,使用 Apple Speech Recognition 框架 final class SpeechRecognizer: NSObject, SFSpeechRecognizerDelegate { private var recognizer: SFSpeechRecognizer? private var request: SFSpeechAudioBufferRecognitionRequest? private var task: SFSpeechRecognitionTask? private let engine = AVAudioEngine() private var isRunning = false var onResult: ((String) -> Void)? var onError: ((Error) -> Void)? var onStateChange: ((Bool) -> Void)? // true = 开始识别中 override init() { super.init() } /// 请求权限 func requestAuthorization() async -> Bool { let status = await withCheckedContinuation { (continuation: CheckedContinuation) in SFSpeechRecognizer.requestAuthorization { status in continuation.resume(returning: status) } } return status == .authorized } /// 使用指定语言启动识别 func start(language: String) async -> Bool { guard !isRunning else { return true } let authorized = await requestAuthorization() guard authorized else { onError?(NSError(domain: "SpeechRecognizer", code: -1, userInfo: [NSLocalizedDescriptionKey: "Speech recognition not authorized"])) return false } guard let recognizer = SFSpeechRecognizer(locale: Locale(identifier: language)), recognizer.isAvailable else { let msg = "Speech recognizer unavailable for \(language)" onError?(NSError(domain: "SpeechRecognizer", code: -2, userInfo: [NSLocalizedDescriptionKey: msg])) return false } self.recognizer = recognizer recognizer.delegate = self recognizer.defaultTaskHint = .dictation // 创建新的请求 request = SFSpeechAudioBufferRecognitionRequest() guard let request = request else { return false } request.shouldReportPartialResults = true request.addsPunctuation = true // 获取音频输入 let inputNode = engine.inputNode let format = inputNode.outputFormat(forBus: 0) inputNode.installTap(onBus: 0, bufferSize: 1024, format: format) { [weak self] buffer, _ in self?.request?.append(buffer) } do { try engine.start() } catch { onError?(error) return false } // 开始识别任务 task = recognizer.recognitionTask(with: request) { [weak self] result, error in guard let self = self else { return } if let error = error { DispatchQueue.main.async { self.onError?(error) } return } if let result = result { // 获取当前最佳转录及未完成的备选 let text = result.bestTranscription.formattedString DispatchQueue.main.async { self.onResult?(text) } } } isRunning = true onStateChange?(true) print("[Speech] Started recognition for \(language)") return true } func stop() -> String? { guard isRunning else { return nil } isRunning = false engine.inputNode.removeTap(onBus: 0) engine.stop() // 结束识别流 request?.endAudio() task?.finish() // 结果已在回调中处理 request = nil task = nil recognizer = nil onStateChange?(false) print("[Speech] Stopped recognition") return nil } func cancel() { task?.cancel() _ = stop() } // MARK: - SFSpeechRecognizerDelegate func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) { print("[Speech] Availability changed: \(available)") } }