SFSpeechRecognizer多音频文件转录并发问题优化方案咨询
解决方案
一、更简洁的串行处理方式:Swift Concurrency(Async/Await)替代信号量
你当前用信号量实现串行的方式确实繁琐,改用Swift原生的异步并发框架可以大幅简化代码,天然支持异步任务的串行等待:
- 将转录方法改造为异步函数:
private func transcribeAudioFile(url: URL, preferredWords: [String]) async throws -> String { guard SFSpeechRecognizer.authorizationStatus() == .authorized else { throw NSError(domain: "SpeechError", code: 1, userInfo: [NSLocalizedDescriptionKey: "语音识别未授权"]) } guard let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US")), speechRecognizer.isAvailable else { throw NSError(domain: "SpeechError", code: 2, userInfo: [NSLocalizedDescriptionKey: "语音识别当前不可用"]) } let request = SFSpeechURLRecognitionRequest(url: url) request.contextualStrings = preferredWords request.taskHint = .dictation request.requiresOnDeviceRecognition = true return try await withCheckedThrowingContinuation { continuation in speechRecognizer.recognitionTask(with: request) { result, error in if let error = error { continuation.resume(throwing: error) return } guard let result = result else { continuation.resume(throwing: NSError(domain: "SpeechError", code: 3, userInfo: [NSLocalizedDescriptionKey: "无识别结果返回"])) return } if result.isFinal { continuation.resume(returning: result.bestTranscription.formattedString) } } } }
- 批量处理方法改用
await实现串行执行:
func processAllAudioFiles() async { if items.isEmpty && !selectedMasterList.isEmpty { // 填充自定义词典 } let files = fetchFilesWithPrefix(prefix: "T", invName: invName) var timeCounter = 0.0 for file in files { // 获取音频时长 let duration = try? AVAudioPlayer(contentsOf: file).duration ?? 0.0 timeCounter += duration ?? 0.0 do { let recognizedText = try await transcribeAudioFile(url: file, preferredWords: items) print("文件\(file.lastPathComponent)最终识别文本:", recognizedText) } catch { print("文件\(file.lastPathComponent)转写失败: \(error.localizedDescription)") } } }
这种方式无需手动管理信号量,Swift并发框架会自动处理任务的串行等待,代码逻辑更清晰。
二、关于defer过早触发的问题
你之前把defer { transcriptionSemaphore.signal() }放在闭包里导致提前触发,核心原因是recognitionTask的闭包会被多次调用:每次返回临时识别结果(result.isFinal == false)时都会执行闭包,而defer在闭包执行结束时就会触发,无法区分临时结果和最终结果的回调。
如果一定要保留信号量方案,就必须像你当前代码那样,只在错误发生、拿到最终识别结果这两个场景下调用signal(),不能用defer自动触发。
内容的提问来源于stack exchange,提问作者Bartender1382
相关产品推荐
相关产品推荐

