Extend Speech Synthesis with personal and custom voices
System & Services 进阶 20m

扩展语音合成:个性化语音与自定义语音

Extend Speech Synthesis with personal and custom voices

2023年6月5日

在 Apple 官方观看视频

一句话判断

如果你在做 TTS(文字转语音)相关的产品,这场 Session 提供的 Speech Synthesis Provider API 和 Personal Voice 功能值得仔细研究——前者让你能把自定义语音引擎接入系统级别,后者让用户用自己的声音进行语音合成。

这场 Session 讲了什么

Session 围绕三个主题展开:SSML 标准的介绍与应用、自定义语音合成器的实现方式、以及全新的 Personal Voice 功能。

SSML(Speech Synthesis Markup Language)是 W3C 的口语表示标准,用 XML 格式描述语音属性。你可以用 <break> 标签插入停顿,用 <prosody> 标签控制语速和音高。AVSpeechUtterance 直接支持 SSML 输入,这是苹果第一方合成器(包括 WebKit 中的 WebSpeech)的标准输入格式。

Speech Synthesis Provider 是今年的重头戏。它基于 Audio Unit Extension 架构,允许开发者将自己的语音合成引擎以 Extension 形式嵌入宿主 App。系统负责音频会话管理和播放,你的 Extension 只需关注 SSML 到音频的渲染。整个流程包括四个核心组件:speechVoices 属性声明可用语音、synthesizeSpeechRequest 接收合成请求、cancelSpeechRequest 取消当前请求、以及 renderBlock 填充音频帧。

Personal Voice 是一项辅助功能,让用户通过录制自己的声音来生成个性化合成语音。这对渐冻症等可能影响发声能力的用户意义重大——他们可以用自己的声音与外界交流。

值得深挖的点

语音购买与共享机制:Session 展示了一个语音商店的完整实现。通过 App Group 共享 UserDefaults,宿主 App 和 Extension 可以同步已购买的语音列表。购买完成后调用 AVSpeechSynthesisProviderVoice.updateSpeechVoices() 通知系统重建语音列表。这个设计模式在其他 Extension 场景中也很通用。

Audio Unit Extension 的渲染管线renderBlock 是系统以指定 frameCount 调用的回调,你需要在 outputAudioBuffer 中填充对应数量的音频帧。渲染完成后设置 actionFlags = offlineUnitRenderAction_Complete 通知系统。这个底层音频处理模式对理解 iOS 音频架构很有帮助。

系统级集成:通过 Speech Synthesis Provider 注册的语音可以在 VoiceOver、系统设置以及任何使用 AVSpeechSynthesizer 的地方使用,真正做到了全系统可用。

代码片段

通过 App Group 共享已购语音列表:

// 宿主 App 和 Extension 使用同一个 suite name
let defaults = UserDefaults(suiteName: "group.com.example.synthesizer")

// 购买完成后更新语音列表
func purchaseVoice(_ voice: WWDCVoice) {
    purchasedVoices.append(voice)
    // 通知 Extension 语音列表已变化
    updateDefaults()
    // 通知系统重建语音列表
    AVSpeechSynthesisProviderVoice.updateSpeechVoices()
}

Extension 中声明可用语音并处理合成请求:

// 声明可用的语音列表
override var speechVoices: [AVSpeechSynthesisProviderVoice] {
    let defaults = UserDefaults(suiteName: "group.com.example.synthesizer")
    return defaults?.stringArray(forKey: "voices")?.compactMap { identifier in
        AVSpeechSynthesisProviderVoice(name: identifier, identifier: identifier, locale: Locale(identifier: "en-US"))
    } ?? []
}

// 接收合成请求
override func synthesizeSpeechRequest(_ request: AVSpeechSynthesisProviderRequest) {
    // request 包含 SSML 和目标语音信息
    currentBuffer = speechEngine.getAudioBuffer(voice: request.voice, ssml: request.ssml)
    framePosition = 0
}

最佳实践

  • 监听 availableVoicesDidChangeNotification 来响应系统语音列表的变化(用户删除或下载语音时触发)
  • 在宿主 App 中购买语音后,务必调用 updateSpeechVoices() 让系统感知变化
  • Extension 中的 synthesizeSpeechRequest 收到请求后,先准备好音频缓冲区,再在 renderBlock 中逐帧填充
  • SSML 是语音合成的标准输入格式,如果你的 TTS 引擎还不支持 SSML,建议优先适配

还有什么值得关注

  • Personal Voice 功能对无障碍场景意义深远,值得关注其 API 设计思路
  • 苹果在 WWDC 后续可能会开放更多 Personal Voice 的开发者接口
  • Audio Unit Extension 架构的稳定性需要关注——Extension 进程崩溃不应影响宿主 App
  • SSML 在 WebSpeech 中的应用方式可以作为 Web 端 TTS 的参考
WWDC 2023