View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

更多视频

  • 简介
  • 概要
  • 转写文稿
  • 代码
  • 借助 SpeechAnalyzer 将先进的语音转文本功能引入 App

    探索带来语音转文本功能的全新 SpeechAnalyzer API。我们将了解这一 Swift API 及其丰富的功能,这些功能为“备忘录”“语音备忘录”“手记”等 App 提供了支持。我们将深入探讨相关细节,了解语音转文本功能的运作方式,以及 SpeechAnalyzer 和 SpeechTranscriber 如何助你构建精彩、实用的功能。你还将跟着视频学习如何通过编程将 SpeechAnalyzer 和实时转录功能整合到 App 中。

    章节

    • 0:00 - 简介
    • 2:41 - SpeechAnalyzer API
    • 7:03 - SpeechTranscriber 模型
    • 9:06 - 构建语音转文本功能

    资源

    • Bringing advanced speech-to-text capabilities to your app
    • Speech
      • 高清视频
      • 标清视频

    相关视频

    WWDC23

    • 使用个人和自定义声音扩展语音合成
  • 搜索此视频…

    大家好!我叫 Donovan 是 Speech 框架团队的一名工程师 我叫 Shantini 是备忘录团队的一名工程师 今年 我们很高兴为大家带来 全新一代的 语音转文本 API 和技术: SpeechAnalyzer 在这个讲座中 我们将为大家 介绍 SpeechAnalyzer API 及其最重要的概念 我们还会简单介绍这个 API 背后的模型的一些新功能 最后 我们将演示一个实时编码 帮助大家了解如何使用这个 API 很多系统 App 的功能 都在使用 SpeechAnalyzer 了 相应 App 包括“备忘录”、 “语音备忘录”、“手记”等

    通过将 SpeechAnalyzer 与 Apple 智能相结合 我们打造了极其强大的功能 例如 Call Summarization 稍后我将为大家展示 如何使用这个 API 构建自己的实时转录功能 首先 有请 Donovan 为大家简单 介绍全新的 SpeechAnalyzer API 语音转文本 也叫做自动语音识别或 ASR 是一种用途很广泛的技术 可帮助你打造出色的用户体验 这项技术将实时语音或录制的语音 转换为文本形式 以便轻松在设备上显示或解读 App 可以实时储存、搜索 或传输这些文本 或将文本传递给 基于文本的大语言模型

    在 iOS 10 中 我们推出了 SFSpeechRecognizer 通过这个类 你可以访问 为 Siri 提供支持的语音转文本模型 这个类适用于简短的听写 如果设备资源有限 它可以使用 Apple 服务器 但它并没有像我们期望的那样 满足某些用例的需求 还需要用户添加语言 现在 在 iOS 26 中 我们将为所有平台引入一个新的 API 称为 SpeechAnalyzer 它支持更多用例 效果也更好 新的 API 利用 Swift 的强大功能 来执行语音转文本处理 并且只需使用很少的代码 便可以在用户设备上 管理模型资源 除了这个 API 我们还提供了 一个全新的语音转文本模型 我们平台上很多应用程序的功能 已经在使用这个模型了 新模型更快也更灵活 优于以前通过 SFSpeechRecognizer 提供的模型 它适用于时长较长的音频 和远距离音频场景 如讲座 会议和对话 基于这些改进 Apple 的这个新模型 (以及新 API) 已经在“备忘录”和我们前面提到的 其他应用程序中得到了应用 你可以使用这些新功能 来构建自己的应用程序 提供类似的语音转文本功能 就像“备忘录”和我们 的其他应用程序那样 首先 我们来看看 API 的设计 这个 API 由 SpeechAnalyzer 类 以及其他几个类组成 SpeechAnalyzer 类 管理分析会话 你可以在会话中 添加一个“module”类 用于执行特定类型的分析 在会话中添加转录器模块 可使它成为转录会话 用于执行语音转文本处理 你将音频缓冲区 传递给分析器实例 后者随后将通过 转录器和它的语音转文字模型 来路由这些缓冲区 模型预测与语音音频对应的文本 接着将相应文本连同 一些元数据返回给应用程序

    一切都以异步方式进行 当一个任务中有可用的音频时 你的应用程序可以添加音频 并在另一个任务中单独显示结果 或进行进一步处理 Swift 的异步序列对输入进行缓冲 并将输入与结果解耦

    WWDC21 的 “了解 AsyncSequence”讲座 介绍了如何提供输入序列 以及如何读取结果序列

    为了将输入与结果相关联 这个 API 使用相应音频的时间码 事实上 所有 API 操作都是使用 音频时间线上的时间码来排定时间的 因此 它们的顺序是可预测的 并且与被调用的时间无关 时间码精确到单个音频样本 请注意转录器如何按顺序提供结果 每个结果都覆盖了自己的音频范围 且不会重叠 它通常就是这样运作的 但是作为可选功能 你可以在音频范围内迭代转录 如果希望在应用程序的 UI 中提供即时反馈 你可能需要这样做 你可以立即显示粗略的结果 然后在接下来的几秒钟内 显示这个结果的更优迭代版本 我们将立即得到的粗略结果 称为“临时结果” 临时结果基本会在第一时间提供 但它们只是准确性较低的猜测 不过随着音频和上下文的增加 模型会改进自身的转录结果 最终 结果会尽可能达到最佳状态 并且转录器提供一个最终结果 提供最终结果后 转录器就不会继续 再为这段音频提供任何结果 并会开始处理下一段音频 请留意时间码 看看后来改进的结果 如何替换之前的结果 仅当启用临时结果时 才会出现这种情况 通常情况下 转录器只提供最终的结果 这些结果均不会替换以前的结果 如果你只需要读取文件 并返回转录文本 那么只需一个函数 就能构建一个转录功能 这项任务不需要处理临时结果 或太多并发的操作 这里显示了相应函数 我们在这里创建了转录器模块 我们要告诉它转录的目标语言 它还没有提供任何结果 但我们会在出现结果时读取 并使用“reduce”的 AsyncSequence 版本来连接它们 我们将在后台 使用“async let”来完成这项操作 现在 我们创建分析器 并添加转录器模块 然后我们开始分析文件 analyzeSequence 方法从文件中读取 并将它的音频添加到输入序列中 当文件被读取后 我们告诉分析器完成操作 因为我们不打算继续处理其他音频 最后我们将 返回在后台处理的转录 这就是文件中的语音内容 以单个带属性的字符串形式显示 大功告成了

    现在 我已经介绍了 这个 API 的概念和基本用法 你可以将模块添加到分析会话中 以执行转录等任务 它可以并发和异步工作 将音频输入与结果解耦 你可以使用会话的音频时间线 将音频、结果和操作关联起来 其中一些结果是临时的 (如果你希望它们是临时的) 其余的结果都是最终结果 不会改变 我还展示了如何将各个部分 整合到一个函数的用例中 稍后 Shantini 将演示如何 将这个函数的功能扩展到 不同的视图、模型和视图模型 她将展示 SpeechAnalyzer 和 Transcriber 类的几个方法和属性 它们可以满足一些基本使用需求 你也可以在文档中阅读相关内容 现在 我们来介绍一下 SpeechTranscriber 类的 新语音转文本模型的好处 SpeechTranscriber 由 Apple 设计的全新模型驱动 能支持很多应用场景 我们想要创建一个可以支持 长文本和对话式用例的模型 在用例中 某些讲话人 可能离麦克风较远 例如在录制会议时 我们还希望支持实时转录 既要保证低延迟 同时又不能牺牲准确性或可读性 我们希望保持讲话内容的私密性 我们全新的设备端模型 实现了所有这些目标 我们与内部合作伙伴密切合作 为开发者们打造了优秀的体验 现在 你可以在自己的应用程序中 支持相同的用例 有了 SpeechTranscriber 你可以获得强大的语音转文本模型 你不必自己购买和管理模型 只需通过新的 AssetInventory API 安装相关的模型资源即可 你可以根据需要进行下载 模型保留在系统存储中 不会增加应用程序的下载大小 或占用的存储空间 也不会增加运行时占用的内存 因为它在应用程序的 内存空间之外运行 因此 不必担心会超出大小限制 我们会不断改进模型 因此系统将在有可用更新时 自动安装更新 SpeechTranscriber 目前可以转录 这些语言 未来会支持更多语言 并且支持除 watchOS 之外的所有平台 只要硬件满足一定的要求即可 如果你的目标语言或设备还不受支持 我们还提供第二个转录器类: DictationTranscriber 它支持的语言、 语音转文本模型和设备 与 iOS 10 的设备端 SFSpeechRecognizer 相同 但在改进 SFSpeechRecognizer 后 你就无需让用户进入“设置” 去为任何特定语言 打开 Siri 或键盘听写功能了 以上就是对全新 API 和模型的介绍 之前这些都很抽象 现在大家应该有更具体的认识了 接下来有请 Shantini 她将展示如何将 SpeechAnalyzer 整合到你的 App 中 谢谢你的精彩讲述 Donovan! 你可能已经用过 iOS 18 的 “备忘录”中新增的精彩功能 来进行录音 以及转录通话、 实时音频和录制的音频 此外 我们还将这些功能 与 Apple 智能整合在一起 可以生成实用的摘要 为用户节省时间 我们与 Speech 团队密切合作 确保 SpeechAnalyzer 和 SpeechTranscriber 能够帮助我们打造 高质量的“备忘录”功能 SpeechTranscriber 是一个绝佳选择 因为它速度快 即使当距离较远时也能保持准确 而且是在设备端运行的 我们的另一个目标是 让开发者能够 构建功能 就像我们 在“备忘录”中添加的功能一样 还可以对功能进行自定 以满足用户的需求 我希望能够帮助大家 开始打造这样的功能 来看看我正在构建的 一款具有实时转录功能的 App 这是一款面向儿童的 App 可以录制和转录睡前故事 让用户可以重复播放 这是实时转录的结果

    播放音频时 相应的文本部分将高亮显示 这样他们就可以边看边听 让我们来看一下项目设置

    在我的示例 App 代码中 有一个 Recorder 类 和一个 SpokenWordTranscriber 类 我把这两个类都设置为可观察

    我还创建了这个 Story 模型 用来封装我们的转录信息 以及其他要显示的相关详细信息 最后我展示一下转录视图 它包含实时转录 和播放视图 以及录制和播放按钮 它还会处理录制和播放状态 我们先来看看转录 设置实时转录只需 3 个简单步骤: 配置 SpeechTranscriber 确保模型已经存在 处理结果 我们先来设置 SpeechTranscriber 需要使用一个 locale 对象 和我们需要的选项来进行初始化 locale 的语言代码对应的是 我们要转录的目标语言 正如 Donovan 之前介绍的 临时结果是实时给出的猜测结果 最终结果是最后给出的最佳猜测结果 我们同时提供这两种结果 临时结果用较浅的不透明颜色显示 最后会被最终的结果所替代 要在 SpeechTranscriber 中 配置这一显示方式 我们要设置这些选项类型 我添加了 audioTimeRange 选项 以便获得时间信息

    这样能同步播放文本和音频

    这里还有几个提供 不同选项的预配置预设

    现在 我们将使用 我们的 SpeechTranscriber 模块 设置 SpeechAnalyzer 对象

    这样我们就可以使用 自己需要的音频格式了

    我们现在还可以确保 语音转文本模型已准备就绪

    在完成 SpeechTranscriber 设置时 我们要保存 对 AsyncStream 输入的引用 并启动分析器

    现在我们已经完成了 SpeechTranscriber 的设置 我们来看看如何获取模型 在我们的“ensure model”方法中 我们将添加检查 来确认 SpeechTranscriber 是否支持转录我们的目标语言

    我们还将检查 是否已下载并安装这种语言

    如果支持这种语言但没有下载 我们可以继续向 AssetInventory 发送下载支持请求

    请记住 转写功能完全在设备端进行 但需要获取模型 下载请求 包含一个“progress”对象 你可以用它来 让用户了解下载的进展情况

    你的 App 一次只能支持 有限数量的语言 如果超过限制 可以让 AssetInventory 取消分配其中一个或多个语言 以满足限制要求

    现在我们已经得到了模型 接下来看看最精彩的部分:结果

    在 SpeechTranscriber 设置代码旁 我要创建一个任务 并保存对它的引用

    我还创建了两个变量来跟踪 我们的临时结果和最终结果

    SpeechTranscriber 通过 AsyncStream 返回结果 每个结果对象都有几个不同的字段

    我们首先要获取的是“text” 它由 AttributedString 表示 这是一段音频的转录结果 每次我们在流中获取返回的结果时 我们需要检查 是临时结果还是最终结果 方法是使用“isFinal”属性

    如果是临时结果 我们会将它 保存到 volatileTranscript

    每当我们得到最终结果时 便会清理 volatileTranscript 并将 结果添加到 finalizedTranscript

    如果我们不清理临时结果 我们最终可能会得到重复的结果

    每当得到最终结果时 我们就把它写下来 添加到 Story 模型中 供以后使用

    我还要设置一些条件格式 使用的是 SwiftUI AttributedString API

    这样我们就能直观地显示转录结果 从临时结果变为最终结果的整个过程

    如果想知道我如何获得 转录文本的时间数据 只需要看看 attributedstring

    每次运行都有“audioTimeRange” 属性 用 CMTimeRange 表示 我将在视图代码中 用它来突出显示正确的片段 接下来 我们看看如何设置音频输入

    在我的 record 函数中 (当用户 按下“Record”时会调用这个函数) 我将请求音频权限 并启动 AVAudioSession 我们还应该确保 已在项目设置中将 App 配置为 使用麦克风

    然后 我将调用我之前创建的 setUpTranscriber 函数

    最后 我将处理音频流的输入 来看看我是如何设置的 涉及的操作包括: 将 AVAudioEngine 配置为 返回异步流 并将传入的缓冲区传递给流

    我们还将音频写入磁盘

    最后启动 audioEngine

    回到我的 Record 函数 我把 AsyncStream 输入 传递给转录器

    音频源具有不同的输出格式和采样率 SpeechTranscriber 给了我们 一个 bestAvailableAudioFormat 供我们使用

    我通过一个转换步骤来 传递音频缓冲区 以确保格式与 bestAvailableAudioFormat 匹配

    然后 我会将异步流 从 SpeechTranscriber 路由至 inputBuilder 对象 停止录制时 我们还需要完成几个操作 我停止了音频引擎和转录器 一定记得要取消任务 还要在分析器流上调用 finalize 这样是为了确保 用最终结果代替临时结果 我们来看看如何 将所有这些与视图联系起来

    我的 TranscriptView 绑定了一个当前的 story 还绑定了我们的 SpokenWordTranscriber 如果正在录制 便会显示最终脚本与 我们从 SpokenWordTranscriber 类 中观测到的 临时转录结果的关联 在播放时 我们可以看到 数据模型的最终脚本 我添加了一个方法来分解句子 让它看起来更清晰一些

    我提到的一个关键功能是 在播放时突出显示每个词 我使用了一些辅助方法来计算 是否每次运行时都要突出显示 具体取决于它的 audioTimeRange 属性和当前播放时间

    SpeechTranscriber 的准确性非常高 原因有很多 其中最重要的是能够 使用 Apple 智能 对输出进行有用的转换

    在这里 我使用 新的 FoundationModels API 在故事完成时为它生成一个标题 这个 API 能帮我 轻松创建巧妙的标题 所以我不用自己绞尽脑汁 如需进一步了解 FoundationModels API 请观看讲座 “了解 Foundation Models 框架”

    我们来看看这个功能如何发挥作用! 我将轻点“+”号按钮 创建一个新故事

    然后我开始录制 很久很久以前 在神秘的红土地上 有一个名叫 Delilah 的小女孩 她住在山上的一座城堡里 Delilah 每天都在森林里玩耍 并照料森林里的动物

    录制完成时 用户可以播放 每个字都会随着音频高亮显示

    很久很久以前 在神秘的红土地上 有一个名叫 Delilah 的小女孩 她住在山上的一座城堡里

    Delilah 每天都在森林里玩耍 并照料森林里的动物 借助 SpeechAnalyzer 和 SpeechTranscriber 我们构建了一个 完整的 App 并只花费了很少的时间 如需了解更多信息 请查看 Speech 框架文档 其中包括我们创建的示例 App 以上就是关于 SpeechAnalyzer 的完整介绍! 你也可以用它来构建令人惊叹的功能 感谢大家的观看!

    • 5:21 - Transcribe a file

      // Set up transcriber. Read results asynchronously, and concatenate them together.
      let transcriber = SpeechTranscriber(locale: locale, preset: .offlineTranscription)
      async let transcriptionFuture = try transcriber.results
          .reduce("") { str, result in str + result.text }
      
      let analyzer = SpeechAnalyzer(modules: [transcriber])
      if let lastSample = try await analyzer.analyzeSequence(from: file) {
          try await analyzer.finalizeAndFinish(through: lastSample)
      } else {
          await analyzer.cancelAndFinishNow()
      }
          
      return try await transcriptionFuture
    • 11:02 - Speech Transcriber setup (volatile results + timestamps)

      func setUpTranscriber() async throws {
              transcriber = SpeechTranscriber(locale: Locale.current,
                                              transcriptionOptions: [],
                                              reportingOptions: [.volatileResults],
                                              attributeOptions: [.audioTimeRange])
          }
    • 11:47 - Speech Transcriber setup (volatile results, no timestamps)

      // transcriber = SpeechTranscriber(locale: Locale.current, preset: .progressiveLiveTranscription)
    • 11:54 - Set up SpeechAnalyzer

      func setUpTranscriber() async throws {
          transcriber = SpeechTranscriber(locale: Locale.current,
                                          transcriptionOptions: [],
                                          reportingOptions: [.volatileResults],
                                          attributeOptions: [.audioTimeRange])
          
          guard let transcriber else {
              throw TranscriptionError.failedToSetupRecognitionStream
          }
      
          analyzer = SpeechAnalyzer(modules: [transcriber])
      }
    • 12:00 - Get audio format

      func setUpTranscriber() async throws {
          transcriber = SpeechTranscriber(locale: Locale.current,
                                          transcriptionOptions: [],
                                          reportingOptions: [.volatileResults],
                                          attributeOptions: [.audioTimeRange])
          
          guard let transcriber else {
              throw TranscriptionError.failedToSetupRecognitionStream
          }
      
          analyzer = SpeechAnalyzer(modules: [transcriber])
          
          self.analyzerFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriber])
      }
    • 12:06 - Ensure models

      func setUpTranscriber() async throws {
          transcriber = SpeechTranscriber(locale: Locale.current,
                                          transcriptionOptions: [],
                                          reportingOptions: [.volatileResults],
                                          attributeOptions: [.audioTimeRange])
          
          guard let transcriber else {
              throw TranscriptionError.failedToSetupRecognitionStream
          }
      
          analyzer = SpeechAnalyzer(modules: [transcriber])
          
          self.analyzerFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriber])
          
          do {
              try await ensureModel(transcriber: transcriber, locale: Locale.current)
          } catch let error as TranscriptionError {
              print(error)
              return
          }
      }
    • 12:15 - Finish SpeechAnalyzer setup

      func setUpTranscriber() async throws {
          transcriber = SpeechTranscriber(locale: Locale.current,
                                          transcriptionOptions: [],
                                          reportingOptions: [.volatileResults],
                                          attributeOptions: [.audioTimeRange])
          
          guard let transcriber else {
              throw TranscriptionError.failedToSetupRecognitionStream
          }
      
          analyzer = SpeechAnalyzer(modules: [transcriber])
          
          self.analyzerFormat = await SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith: [transcriber])
          
          do {
              try await ensureModel(transcriber: transcriber, locale: Locale.current)
          } catch let error as TranscriptionError {
              print(error)
              return
          }
          
          (inputSequence, inputBuilder) = AsyncStream<AnalyzerInput>.makeStream()
          
          guard let inputSequence else { return }
          
          try await analyzer?.start(inputSequence: inputSequence)
      }
    • 12:30 - Check for language support

      public func ensureModel(transcriber: SpeechTranscriber, locale: Locale) async throws {
              guard await supported(locale: locale) else {
                  throw TranscriptionError.localeNotSupported
              }
          }
          
          func supported(locale: Locale) async -> Bool {
              let supported = await SpeechTranscriber.supportedLocales
              return supported.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47))
          }
      
          func installed(locale: Locale) async -> Bool {
              let installed = await Set(SpeechTranscriber.installedLocales)
              return installed.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47))
          }
    • 12:39 - Check for model installation

      public func ensureModel(transcriber: SpeechTranscriber, locale: Locale) async throws {
              guard await supported(locale: locale) else {
                  throw TranscriptionError.localeNotSupported
              }
              
              if await installed(locale: locale) {
                  return
              } else {
                  try await downloadIfNeeded(for: transcriber)
              }
          }
          
          func supported(locale: Locale) async -> Bool {
              let supported = await SpeechTranscriber.supportedLocales
              return supported.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47))
          }
      
          func installed(locale: Locale) async -> Bool {
              let installed = await Set(SpeechTranscriber.installedLocales)
              return installed.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47))
          }
    • 12:52 - Download the model

      func downloadIfNeeded(for module: SpeechTranscriber) async throws {
              if let downloader = try await AssetInventory.assetInstallationRequest(supporting: [module]) {
                  self.downloadProgress = downloader.progress
                  try await downloader.downloadAndInstall()
              }
          }
    • 13:19 - Deallocate an asset

      func deallocate() async {
              let allocated = await AssetInventory.allocatedLocales
              for locale in allocated {
                  await AssetInventory.deallocate(locale: locale)
              }
          }
    • 13:31 - Speech result handling

      recognizerTask = Task {
                  do {
                      for try await case let result in transcriber.results {
                          let text = result.text
                          if result.isFinal {
                              finalizedTranscript += text
                              volatileTranscript = ""
                              updateStoryWithNewText(withFinal: text)
                              print(text.audioTimeRange)
                          } else {
                              volatileTranscript = text
                              volatileTranscript.foregroundColor = .purple.opacity(0.4)
                          }
                      }
                  } catch {
                      print("speech recognition failed")
                  }
              }
    • 15:13 - Set up audio recording

      func record() async throws {
              self.story.url.wrappedValue = url
              guard await isAuthorized() else {
                  print("user denied mic permission")
                  return
              }
      #if os(iOS)
              try setUpAudioSession()
      #endif
              try await transcriber.setUpTranscriber()
                      
              for await input in try await audioStream() {
                  try await self.transcriber.streamAudioToTranscriber(input)
              }
          }
    • 15:37 - Set up audio recording via AVAudioEngine

      #if os(iOS)
          func setUpAudioSession() throws {
              let audioSession = AVAudioSession.sharedInstance()
              try audioSession.setCategory(.playAndRecord, mode: .spokenAudio)
              try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
          }
      #endif
          
          private func audioStream() async throws -> AsyncStream<AVAudioPCMBuffer> {
              try setupAudioEngine()
              audioEngine.inputNode.installTap(onBus: 0,
                                               bufferSize: 4096,
                                               format: audioEngine.inputNode.outputFormat(forBus: 0)) { [weak self] (buffer, time) in
                  guard let self else { return }
                  writeBufferToDisk(buffer: buffer)
                  self.outputContinuation?.yield(buffer)
              }
              
              audioEngine.prepare()
              try audioEngine.start()
              
              return AsyncStream(AVAudioPCMBuffer.self, bufferingPolicy: .unbounded) {
                  continuation in
                  outputContinuation = continuation
              }
          }
    • 16:01 - Stream audio to SpeechAnalyzer and SpeechTranscriber

      func streamAudioToTranscriber(_ buffer: AVAudioPCMBuffer) async throws {
              guard let inputBuilder, let analyzerFormat else {
                  throw TranscriptionError.invalidAudioDataType
              }
              
              let converted = try self.converter.convertBuffer(buffer, to: analyzerFormat)
              let input = AnalyzerInput(buffer: converted)
              
              inputBuilder.yield(input)
          }
    • 16:29 - Finalize the transcript stream

      try await analyzer?.finalizeAndFinishThroughEndOfInput()
    • 0:00 - 简介
    • Apple 在 iOS 26 中推出了全新的语音转文本 API —— SpeechAnalyzer,作为 iOS 10 中推出的 SFSpeechRecognizer 的替代方案。SpeechAnalyzer 采用 Swift 构建,具备更快的处理速度与更高的灵活性,支持长音频与远距离录音,适用于讲座、会议、对话等多种场景。 这一全新 API 支持实时转录功能,现已应用于系统级 App,如“备忘录”、“语音备忘录”和“手记”等。结合 Apple 智能使用时,这个 API 可实现强大的功能,例如 Call Summarization。

    • 2:41 - SpeechAnalyzer API
    • 这个 API 的设计以 SpeechAnalyzer 类为核心,用于管理语音分析会话。通过添加转录器模块,分析会话将会变为转录会话,能够执行语音转文本处理操作。音频缓冲区会传递给分析器实例,并通过转录器的语音转文本模型进行处理。模型会预测文本及元数据,并通过 Swift 的异步序列以异步方式返回给应用程序。 所有 API 操作都会基于音频时间轴上的时间码来排定时间,从而确保执行顺序可预测且彼此独立。转录器会按顺序返回结果,其中涵盖特定的音频区段。可选功能支持对指定音频区段进行迭代转录,先返回快速但精确度较低的“临时结果”,用于提升界面响应速度,随后再替换为更准确的最终结果。 在这个讲座的后续部分,将会介绍一个实际用例,演示如何创建转录模块、设置语言区域、从音频文件中读取数据、通过异步序列拼接转录结果,并以富文本字符串的形式返回最终转录内容。这个 API 支持并发和异步处理,将音频输入与结果解耦,并可以进行扩展以满足在不同视图、模型和视图模型中的复杂使用需求,这一点将在后续示例中进行演示。

    • 7:03 - SpeechTranscriber 模型
    • Apple 为 SpeechTranscriber 类开发了全新的语音转文本模型,专为应对多种场景而设计,包括长音频录音、会议以及低延迟、高准确度的实时转录。这个模型完全在设备端运行,兼顾隐私保护与处理效率。它不会增加 App 的体积或内存占用,并可自动完成更新。 你可以通过 AssetInventory API 轻松将这个模型集成到你的应用程序中。SpeechTranscriber 类目前支持多种语言,并适用于大多数 Apple 平台;对于暂不支持的语言或设备,系统会提供后备选项 DictationTranscriber。

    • 9:06 - 构建语音转文本功能
    • 在 iOS 18 中,“备忘录”App 现已支持全新功能,用户可录制并转录电话通话、实时音频及录音内容。这些功能与 Apple 智能集成,可用于生成摘要内容。Apple 的 Speech 团队开发了 SpeechAnalyzer 和 SpeechTranscriber,实现了高质量的设备端转录,即使在远距离情况下也能快速且准确地完成语音转文本处理。 你现在可以使用这些工具构建自定的转录功能。示例 App 专为儿童设计,用于录制并转录睡前故事内容。这个 App 会实时显示转录结果,并在音频播放过程中高亮显示对应的文本片段。 要在 App 中实现实时转录功能,可按照以下三个主要步骤进行操作:配置 SpeechTranscriber 时,指定相应的语言区域和选项,并确保设备上已下载并安装所需的语音转文本模型。接着,通过 AsyncStream 接收并处理转录结果。转录结果包含临时文本 (实时预测) 和最终文本,可实现音频播放与文本内容的平滑同步。 当获得最终转录结果时,volatileTranscript 会被清除,结果会添加到 finalizedTranscript 中,以避免重复显示。最终转录结果也会写入到 Story 模型中,以供后续使用,并通过 SwiftUI 的 AttributedString API 实现条件格式化呈现。 设置音频输入时,需先请求录音权限,启动 AVAudioSession,并配置 AVAudioEngine,使它返回一个 AsyncStream 以传递音频数据。音频会在转换为最佳可用格式后写入磁盘,并传递给转录器进行处理。 当录音停止时,音频引擎与转录器会一并停止运行,所有尚未完成的临时转录结果也会最终确认。TranscriptView 在录音过程中会同时显示最终与临时转录内容的拼接结果,在播放过程中则从数据模型中加载最终转录内容,并根据音频播放进度高亮显示对应词句。 在这个示例 App 中,借助 Apple 智能和 FoundationModels API,为故事内容生成标题,展示了如何将 Apple 智能应用于语音转文本结果,实现实用的信息处理与转换。 Speech 框架支持以最短的启动时间开发这类 App,更多技术细节可参考它的文档。

Developer Footer

  • 视频
  • WWDC25
  • 借助 SpeechAnalyzer 将先进的语音转文本功能引入 App
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习与 AI
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则