You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SFSpeechRecognizer多音频文件转录并发问题优化方案咨询

解决方案

一、更简洁的串行处理方式:Swift Concurrency(Async/Await)替代信号量

你当前用信号量实现串行的方式确实繁琐,改用Swift原生的异步并发框架可以大幅简化代码,天然支持异步任务的串行等待:

  1. 将转录方法改造为异步函数:
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)
            }
        }
    }
}
  1. 批量处理方法改用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:16:02