speech-recognition

speech-recognition

热门

使用 Apple Speech 框架将语音转录为文本。适用于使用 AVAudioEngine 实现实时麦克风转录、识别录制的音频文件、处理语音和麦克风授权、选择设备端或服务器端 SFSpeechRecognizer 行为,或在 iOS 26+ 上采用 SpeechAnalyzer、SpeechTranscriber、DictationTranscriber、AssetInventory 和异步结果流。

936Star
47Fork
更新于 2026/7/15
SKILL.md
readonly只读
name
speech-recognition
description

使用 Apple Speech 框架将语音转录为文本。适用于使用 AVAudioEngine 实现实时麦克风转录、识别录制的音频文件、处理语音和麦克风授权、选择设备端或服务器端 SFSpeechRecognizer 行为,或在 iOS 26+ 上采用 SpeechAnalyzer、SpeechTranscriber、DictationTranscriber、AssetInventory 和异步结果流。

语音识别

使用 Apple Speech 框架将实时和预录制的音频转录为文本。
涵盖 SpeechAnalyzer / SpeechTranscriber(iOS 26+)和
SFSpeechRecognizer(iOS 10+)的回退指南。

范围边界: 此技能用于语音到文本识别、语音授权、麦克风捕获管道和结果处理。文本分析、转录后的语言识别、情感分析、嵌入和翻译交给 natural-language;音频播放 UI 交给 avkit;转录内容的摘要或生成交给 apple-on-device-ai

目录

SpeechAnalyzer 策略(iOS 26+)

对于现代 iOS 26+ 语音分析,尤其是长格式录音、实时转录、时间索引转录和完全设备端流程,请使用 SpeechAnalyzer。对于 iOS 10+ 部署目标、服务器端语言覆盖或现有的回调/委托实现,保留 SFSpeechRecognizer

在实现 iOS 26+ 转录管道、模型资产处理、易变结果或文件/缓冲区示例时,请阅读 SpeechAnalyzer 模式

SpeechAnalyzer 设置清单

  1. 选择模块:
    • SpeechTranscriber 用于较新的通用设备端模型。
    • DictationTranscriber 当当前设备或语言不支持 SpeechTranscriber 且可接受听写兼容支持时。
    • SpeechDetector 仅在与转录器结合使用时,当语音活动检测值得权衡准确性和功耗时。
  2. 在创建会话前检查支持:
    • SpeechTranscriber.isAvailable
    • SpeechTranscriber.supportedLocale(equivalentTo:)
    • SpeechTranscriber.installedLocales / supportedLocales 在显示语言选择时。
  3. 选择文档化的预设:
    • .transcription 用于基本准确的转录。
    • .progressiveTranscription 用于实时 UI 更新。
    • .timeIndexedProgressiveTranscription 当播放高亮需要 audioTimeRange 时。
  4. 使用 AssetInventory.assetInstallationRequest 安装所需资产。
  5. 在产生 AnalyzerInput 之前,将实时音频缓冲区转换为 SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith:)
  6. 在单独的任务中从模块的 AsyncSequence 消费结果。
  7. 显式使用 finalizeAndFinish(through:)finalizeAndFinishThroughEndOfInput()cancelAndFinishNow() 完成。

不要使用 offlineTranscription 预设;Apple 没有文档化该预设。完成 AsyncStream 输入序列并不会完成分析器会话。

SFSpeechRecognizer 设置

使用语言环境创建识别器

import Speech

// 默认语言环境(用户当前语言)
let recognizer = SFSpeechRecognizer()

// 特定语言环境
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))

// 检查该语言环境是否支持识别
guard let recognizer, recognizer.isAvailable else {
    print("语音识别不可用")
    return
}

监控可用性变化

final class SpeechManager: NSObject, SFSpeechRecognizerDelegate {
    private let recognizer = SFSpeechRecognizer()!

    override init() {
        super.init()
        recognizer.delegate = self
    }

    func speechRecognizer(
        _ speechRecognizer: SFSpeechRecognizer,
        availabilityDidChange available: Bool
    ) {
        // 更新 UI — 不可用时禁用录音按钮
    }
}

授权

在开始实时转录之前,请求同时请求语音识别和麦克风权限。在 Info.plist 中添加以下键:

  • NSSpeechRecognitionUsageDescription
  • NSMicrophoneUsageDescription
import Speech
import AVFoundation

func requestPermissions() async -> Bool {
    let speechStatus = await withCheckedContinuation { continuation in
        SFSpeechRecognizer.requestAuthorization { status in
            continuation.resume(returning: status)
        }
    }
    guard speechStatus == .authorized else { return false }

    let micStatus: Bool
    if #available(iOS 17, *) {
        micStatus = await AVAudioApplication.requestRecordPermission()
    } else {
        micStatus = await withCheckedContinuation { continuation in
            AVAudioSession.sharedInstance().requestRecordPermission { granted in
                continuation.resume(returning: granted)
            }
        }
    }
    return micStatus
}

实时麦克风转录

标准模式:AVAudioEngine 捕获麦克风音频 → 缓冲区附加到 SFSpeechAudioBufferRecognitionRequest → 结果流式传输。

import Speech
import AVFoundation

final class LiveTranscriber {
    private let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!
    private let audioEngine = AVAudioEngine()
    private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
    private var recognitionTask: SFSpeechRecognitionTask?

    func startTranscribing() throws {
        // 取消任何进行中的任务
        recognitionTask?.cancel()
        recognitionTask = nil

        // 配置音频会话
        let audioSession = AVAudioSession.sharedInstance()
        try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
        try audioSession.setActive(true, options: .notifyOthersOnDeactivation)

        // 创建请求
        let request = SFSpeechAudioBufferRecognitionRequest()
        request.shouldReportPartialResults = true
        self.recognitionRequest = request

        // 开始识别任务
        recognitionTask = recognizer.recognitionTask(with: request) { result, error in
            if let result {
                let text = result.bestTranscription.formattedString
                print("转录:\(text)")

                if result.isFinal {
                    self.stopTranscribing()
                }
            }
            if let error {
                print("识别错误:\(error)")
                self.stopTranscribing()
            }
        }

        // 安装音频 tap
        let inputNode = audioEngine.inputNode
        let recordingFormat = inputNode.outputFormat(forBus: 0)
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) {
            buffer, _ in
            request.append(buffer)
        }

        audioEngine.prepare()
        try audioEngine.start()
    }

    func stopTranscribing() {
        audioEngine.stop()
        audioEngine.inputNode.removeTap(onBus: 0)
        recognitionRequest?.endAudio()
        recognitionRequest = nil
        recognitionTask?.cancel()
        recognitionTask = nil
    }
}

预录制音频文件识别

使用 SFSpeechURLRecognitionRequest 识别磁盘上的音频文件:

func transcribeFile(at url: URL) async throws -> String {
    guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
        throw SpeechError.unavailable
    }
    let request = SFSpeechURLRecognitionRequest(url: url)
    request.shouldReportPartialResults = false

    return try await withCheckedThrowingContinuation { continuation in
        var didResume = false
        recognizer.recognitionTask(with: request) { result, error in
            guard !didResume else { return }
            if let error {
                didResume = true
                continuation.resume(throwing: error)
            } else if let result, result.isFinal {
                didResume = true
                continuation.resume(
                    returning: result.bestTranscription.formattedString
                )
            }
        }
    }
}

设备端与服务器端识别

SFSpeechRecognizer 可以在 iOS 13+ 上对支持的语言环境使用设备端识别。如果 supportsOnDeviceRecognition 为 false,则识别器需要网络连接。requiresOnDeviceRecognition 仅在识别器支持时生效。

let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!

// 检查该语言环境是否支持设备端
if recognizer.supportsOnDeviceRecognition {
    let request = SFSpeechAudioBufferRecognitionRequest()
    request.requiresOnDeviceRecognition = true  // 强制设备端
}

SFSpeechRecognizer 请求可能仍然不适合长格式捕获。Apple 文档指出语音识别大约有一分钟的任务限制和其他服务限制。对于 iOS 26+ 上的长录音,优先使用 SpeechAnalyzer;否则在达到限制之前分块或重新启动识别,并在任务之间保留转录状态。

处理结果

部分结果与最终结果

使用 shouldReportPartialResults 时,替换显示的部分转录,直到 SFSpeechRecognitionResult.isFinal 提交它。这与 SpeechTranscriber.Result.isFinal 不同,后者的易变属性范围必须被该范围的最终结果替换。实时示例和 references/speechanalyzer-patterns.md 包含标准循环。

访问替代转录和置信度

recognizer.recognitionTask(with: request) { result, error in
    guard let result else { return }

    // 最佳转录
    let best = result.bestTranscription

    // 所有替代(按置信度降序排列)
    for transcription in result.transcriptions {
        for segment in transcription.segments {
            print("\(segment.substring): \(segment.confidence)")
        }
    }
}

添加标点(iOS 16+)

let request = SFSpeechAudioBufferRecognitionRequest()
request.addsPunctuation = true

上下文字符串

提高领域特定术语的识别:

let request = SFSpeechAudioBufferRecognitionRequest()
request.contextualStrings = ["SwiftUI", "Xcode", "CloudKit"]

常见错误

错误 修复
实时音频请求仅语音授权 同时请求语音和麦克风权限。
识别器可用性只检查一次 观察委托可用性变化和模型服务丢失。
最终/错误/滚动路径执行不同的清理 使用一个 stopTranscribing() 所有者来处理引擎停止、tap 移除、endAudio 和任务取消。
对每个语言环境强制使用设备端模式 检查 supportsOnDeviceRecognition 并提供回退。
一个 SFSpeechRecognizer 任务用于长格式捕获 在 iOS 26+ 上优先使用 SpeechAnalyzer,或滚动有界段同时保留已提交的转录。
完成分析器输入被视为会话完成 显式通过最后一个样本完成或取消并完成。
易变的 SpeechAnalyzer 结果被追加 替换易变范围,直到最终结果提交它。
第二个识别任务在第一个结束前开始 取消/完成活动任务并在替换前完成清理。

加载 references/speechanalyzer-patterns.md 获取完整的分析器完成和易变结果代码。

审查清单

  • [ ] NSSpeechRecognitionUsageDescription 在 Info.plist 中
  • [ ] NSMicrophoneUsageDescription 在 Info.plist 中(如果使用实时音频)
  • [ ] 在开始识别前请求授权
  • [ ] 设置 SFSpeechRecognizerDelegate 以处理 availabilityDidChange
  • [ ] 识别结束时停止音频引擎并移除 tap
  • [ ] 录制完成时调用 recognitionRequest.endAudio()
  • [ ] 在开始新任务前取消之前的 recognitionTask
  • [ ] 在要求设备端模式前检查 supportsOnDeviceRecognition
  • [ ] 部分结果与最终结果(isFinal)分开处理
  • [ ] 考虑 SFSpeechRecognizer 的一分钟/服务限制
  • [ ] 对于 iOS 26+:在使用 SpeechAnalyzer 前安装 AssetInventory 资产
  • [ ] 对于 iOS 26+:检查 SpeechTranscriber.isAvailable 和语言支持
  • [ ] 对于 iOS 26+:将实时缓冲区转换为分析器兼容格式
  • [ ] 对于 iOS 26+:显式完成或取消分析器会话
  • [ ] 对于 iOS 26+:易变结果被最终结果替换,而不是重复

参考资料