LocalSpeechRecognizer对比
时间: 2026-04-16 13:26:54
(部分内容来自网络,其真实性存疑,为了避免对您造成误导,请谨慎甄别。)
好的,我来一行行对比你的两个 LocalSpeechRecognizer:
📋 完整 Diff 对比
| 行号 | 坏的版本(不录音) | 好的版本(能录音) | ||
|---|---|---|---|---|
| 1 | import Speech | import Speech ✅ 相同 | ||
| 2 | import AVFoundation | import AVFoundation ✅ 相同 | ||
| 3 | (空行) | (空行) ✅ 相同 | ||
| 4 | class LocalSpeechRecognizer: NSObject, ObservableObject { | class LocalSpeechRecognizer: NSObject, ObservableObject { ✅ 相同 | ||
| 5 | @Published var isRecognizing = false | @Published var isRecognizing = false ✅ 相同 | ||
| 6 | @Published var recognizedText = "" | @Published var recognizedText = "" ✅ 相同 | ||
| 7 | @Published var errorMessage: String? | @Published var errorMessage: String? ✅ 相同 | ||
| 8 | (空行) | (空行) ✅ 相同 | ||
| 9 | private var speechRecognizer: SFSpeechRecognizer? | private var speechRecognizer: SFSpeechRecognizer? ✅ 相同 | ||
| 10 | private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? | private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? ✅ 相同 | ||
| 11 | private var recognitionTask: SFSpeechRecognitionTask? | private var recognitionTask: SFSpeechRecognitionTask? ✅ 相同 | ||
| 12 | private let audioEngine = AVAudioEngine() | private let audioEngine = AVAudioEngine() ✅ 相同 | ||
| 13 | (空行) | (空行) ✅ 相同 | ||
| 14 | override init() { | override init() { ✅ 相同 | ||
| 15 | super.init() | super.init() ✅ 相同 | ||
| 16 | self.speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US")) | self.speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US")) ✅ 相同 | ||
| 17 | self.speechRecognizer?.delegate = self | self.speechRecognizer?.delegate = self ✅ 相同 | ||
| 18 | } | } ✅ 相同 | ||
| 19 | (空行) | (空行) ✅ 相同 | ||
| 20 | func setLanguage(locale: String) { | func setLanguage(locale: String) { ✅ 相同 | ||
| 21 | guard !isRecognizing else { | guard !isRecognizing else { ✅ 相同 | ||
| 22 | errorMessage = "请先停止录音再切换语言" | errorMessage = "请先停止录音再切换语言" ✅ 相同 | ||
| 23 | return | return ✅ 相同 | ||
| 24 | } | } ✅ 相同 | ||
| 25 | speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: locale)) | speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: locale)) ✅ 相同 | ||
| 26 | speechRecognizer?.delegate = self | speechRecognizer?.delegate = self ✅ 相同 | ||
| 27 | print("已切换到语言: $locale)") | print("已切换到语言: $locale)") ✅ 相同 | ||
| 28 | } | } ✅ 相同 | ||
| 29 | (空行) | (空行) ✅ 相同 | ||
| 30 | func checkAuthorization() { | func checkAuthorization() { ✅ 相同 | ||
| 31 | (空行) | (空行) ✅ 相同 | ||
| 32 | AVAudioSession.sharedInstance().requestRecordPermission { granted in | AVAudioSession.sharedInstance().requestRecordPermission { granted in ✅ 相同 | ||
| 33 | print("麦克风权限: $granted)") | print("麦克风权限: $granted)") ✅ 相同 | ||
| 34 | } | } ✅ 相同 | ||
| 35 | (空行) | (空行) ✅ 相同 | ||
| 36 | SFSpeechRecognizer.requestAuthorization { [weak self] status in | SFSpeechRecognizer.requestAuthorization { [weak self] status in ✅ 相同 | ||
| 37 | DispatchQueue.main.async { | DispatchQueue.main.async { ✅ 相同 | ||
| 38 | switch status { | switch status { ✅ 相同 | ||
| 39 | case .authorized: | case .authorized: ✅ 相同 | ||
| 40 | print("语音识别权限已授权") | print("语音识别权限已授权") ✅ 相同 | ||
| 41 | case .denied: | case .denied: ✅ 相同 | ||
| 42 | self?.errorMessage = "请在设置中开启语音识别权限" | self?.errorMessage = "请在设置中开启语音识别权限" ✅ 相同 | ||
| 43 | case .restricted: | case .restricted: ✅ 相同 | ||
| 44 | self?.errorMessage = "设备不支持语音识别" | self?.errorMessage = "设备不支持语音识别" ✅ 相同 | ||
| 45 | case .notDetermined: | case .notDetermined: ✅ 相同 | ||
| 46 | print("语音识别权限未确定") | print("语音识别权限未确定") ✅ 相同 | ||
| 47 | @unknown default: | @unknown default: ✅ 相同 | ||
| 48 | self?.errorMessage = "未知错误" | self?.errorMessage = "未知错误" ✅ 相同 | ||
| 49 | } | } ✅ 相同 | ||
| 50 | } | } ✅ 相同 | ||
| 51 | } | } ✅ 相同 | ||
| 52 | (空行) | (空行) ✅ 相同 | ||
| 53 | func startRecording(completion: @escaping (String) -> Void) { | func startRecording() throws { ❌ 不同! | ||
| 54 | guard !isRecognizing else { return } | guard !isRecognizing else { return } ✅ 相同 | ||
| 55 | (空行) | (空行) ✅ 相同 | ||
| 56 | if recognitionTask != nil { | guard let speechRecognizer = speechRecognizer, speechRecognizer.isAvailable else { ❌ 不同! | ||
| 57 | recognitionTask?.cancel() | throw NSError(...) ❌ 不同! | ||
| 58 | recognitionTask = nil | } ❌ 不同! | ||
| 59 | } | (空行) ❌ 不同! | ||
| 60 | (空行) | if recognitionTask != nil { ❌ 不同! | ||
| 61 | guard let speechRecognizer = speechRecognizer, speechRecognizer.isAvailable else { | recognitionTask?.cancel() ❌ 不同! | ||
| 62 | DispatchQueue.main.async { | recognitionTask = nil ❌ 不同! | ||
| 63 | self.errorMessage = "请开启 Siri 和听写权限" | } ❌ 不同! | ||
| 64 | completion("") | (空行) ❌ 不同! | ||
| 65 | } | let audioSession = AVAudioSession.sharedInstance() ✅ 相同(位置不同) | ||
| 66 | return | try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers) ❌ 不同! | ||
| 67 | } | try audioSession.setActive(true, options: .notifyOthersOnDeactivation) ❌ 不同! | ||
| 68 | (空行) | (空行) ✅ 相同 | ||
| 69 | DispatchQueue.main.async { | recognitionRequest = SFSpeechAudioBufferRecognitionRequest() ✅ 相同(位置不同) | ||
| 70 | self.isRecognizing = true | guard let recognitionRequest = recognitionRequest else { ❌ 不同! | ||
| 71 | self.recognizedText = "" | throw NSError(...) ❌ 不同! | ||
| 72 | } | } ❌ 不同! | ||
| 73 | (空行) | (空行) ✅ 相同 | ||
| 74 | let audioSession = AVAudioSession.sharedInstance() | recognitionRequest.shouldReportPartialResults = true ✅ 相同(位置不同) | ||
| 75 | do { | recognitionRequest.requiresOnDeviceRecognition = true ✅ 相同(位置不同) | ||
| 76 | try audioSession.setCategory(.record, mode: .default) | (空行) ❌ 不同! | ||
| 77 | try audioSession.setActive(true) | let inputNode = audioEngine.inputNode ✅ 相同(位置不同) | ||
| 78 | } catch { | let recordingFormat = inputNode.outputFormat(forBus: 0) ✅ 相同(位置不同) | ||
| 79 | print("音频会话失败: $error)") | (空行) ❌ 不同! | ||
| 80 | DispatchQueue.main.async { | inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, _ in ❌ 不同! | ||
| 81 | self.isRecognizing = false | recognitionRequest.append(buffer) ❌ 不同! | ||
| 82 | completion("") | } ❌ 不同! | ||
| 83 | } | (空行) ❌ 不同! | ||
| 84 | return | audioEngine.prepare() ✅ 相同(位置不同) | ||
| 85 | } | try audioEngine.start() ✅ 相同(位置不同) | ||
| 86 | } | (空行) ❌ 不同! | ||
| 87 | (空行) | isRecognizing = true ✅ 相同(位置不同) | ||
| 88 | recognitionRequest = SFSpeechAudioBufferRecognitionRequest() | (空行) ❌ 不同! | ||
| 89 | guard let recognitionRequest = recognitionRequest else { | recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak self] result, error in ❌ 不同! | ||
| 90 | DispatchQueue.main.async { | DispatchQueue.main.async { ✅ 相同 | ||
| 91 | self.isRecognizing = false | if let result = result { ✅ 相同(位置不同) | ||
| 92 | completion("") | self?.recognizedText = result.bestTranscription.formattedString ✅ 相同(位置不同) | ||
| 93 | } | } ✅ 相同 | ||
| 94 | return | (空行) ❌ 不同! | ||
| 95 | } | if error != nil | (result?.isFinal ?? false) { ✅ 相同(位置不同) | |
| 96 | } | _ = self?.stopRecording() ✅ 相同(位置不同) | ||
| 97 | (空行) | } ✅ 相同 | ||
| 98 | recognitionRequest.shouldReportPartialResults = false | } ✅ 相同 | ||
| 99 | recognitionRequest.requiresOnDeviceRecognition = false | } ✅ 相同 | ||
| 100 | (空行) | (空行) ✅ 相同 | ||
| 101 | let inputNode = audioEngine.inputNode | } ❌ 不同! | ||
| 102 | let recordingFormat = inputNode.outputFormat(forBus: 0) | (文件结束) ❌ 不同! | ||
| 103 | (空行) | |||
| 104 | inputNode.removeTap(onBus: 0) | |||
| 105 | inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, when in | |||
| 106 | recognitionRequest.append(buffer) | |||
| 107 | } | |||
| 108 | (空行) | |||
| 109 | audioEngine.prepare() | |||
| 110 | do { | |||
| 111 | try audioEngine.start() | |||
| 112 | print("✅ 录音已启动") | |||
| 113 | } catch { | |||
| 114 | print("音频引擎启动失败: $error)") | |||
| 115 | DispatchQueue.main.async { | |||
| 116 | self.isRecognizing = false | |||
| 117 | completion("") | |||
| 118 | } | |||
| 119 | return | |||
| 120 | } | |||
| 121 | (空行) | |||
| 122 | recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in | |||
| 123 | DispatchQueue.main.async { | |||
| 124 | if let result = result { | |||
| 125 | if result.isFinal { | |||
| 126 | let text = result.bestTranscription.formattedString | |||
| 127 | print("✅ 识别结果: $text)") | |||
| 128 | self.stopRecording() | |||
| 129 | completion(text) | |||
| 130 | } | |||
| 131 | } else if let error = error { | |||
| 132 | print("识别错误: $error.localizedDescription)") | |||
| 133 | self.stopRecording() | |||
| 134 | completion("") | |||
| 135 | } | |||
| 136 | } | |||
| 137 | } | |||
| 138 | } |
🎯 核心区别(一句话总结)
| 坏的版本(不录音) | 好的版本(能录音) |
|---|---|
startRecording(completion: @escaping (String) -> Void) | startRecording() throws |
音频会话用 mode: .default | 音频会话用 mode: .measurement, options: .duckOthers |
requiresOnDeviceRecognition = false | requiresOnDeviceRecognition = true |
有 inputNode.removeTap(onBus: 0) | 没有这行 |
回调里只处理 result.isFinal | 回调里实时更新 recognizedText |
把坏的版本的 startRecording 方法整个替换成好的版本的 startRecording 方法就行了。