如何把 TTS 音频流接入实时 AI 数字人

从音频来源、格式、分块时机到结束标记,梳理把 TTS 输出稳定接入实时数字人的工程边界。

Spatius Team11 min read 分钟阅读
本页目录

数字人“说话”的声音,乍看像是播放层的小细节,实际却是一个明确的系统边界。

你的应用或 Agent 栈决定说什么,并通过自己的 TTS 层生成语音。Spatius 接收的是这段数字人要说的音频,将其转换为实时动作数据,再由 AvatarKit 在客户端本地渲染数字人。Spatius 不负责你的 ASR、LLM、TTS、用户轮次策略或工具调用。Spatius 开发者文档地图 对这一职责边界有明确说明。

把边界划清后,接入问题会简单很多:发送正确的音频、使用正确的格式、在正确的时间发送,并明确标记这一轮数字人语音何时结束。如果还在决定这条运行时链路应该放在哪里,可以先从 Spatius 集成路径选择指南 开始。

核心结论

  • 应发送数字人要说的 TTS 输出,而不是麦克风输入,也不是从实时播放链路里回录出来的音频。
  • 在发送前统一为单声道 16 位 PCM(s16le),并使用当前会话已配置的采样率;Spatius 不会自动重采样。
  • TTS 生成一段就发送一段,不要按 1 倍播放速度人为 sleep。
  • 一轮语音的最后一个分块要带上 SDK 对应的结束输入标记。它只结束数字人的音频输入,不替代你产品的轮次决策。
  • 如果手上只有按播放速度到达的音频,应为每一轮增加预缓冲,并把增加的首响时间当作应用层取舍。

把语音链路和数字人链路分开

在带语音能力的 SaaS 产品里,往往有多个系统参与。把它们统称为“数字人”,会掩盖真正需要测试和负责的边界。

环节常见负责人负责什么
用户说话你的产品与 ASR 服务采集并转写用户语音(如果产品支持语音输入)
推理与工具你的应用、Agent 框架或后端上下文、权限、知识检索、工具调用与回复策略
语音生成你的 TTS 服务或应用生成数字人要说的音频
动作生成Spatius Motion Server将数字人语音转为动作数据
客户端呈现客户端中的 AvatarKit基于返回的动作数据渲染数字人

Spatius 的输入就在第四行之前:数字人准备说出的音频。在典型语音 Agent 流程里,这通常是应用已经决定回复内容后产生的 TTS 输出,并不是默认意义上的用户麦克风音频。Spatius 的音频说明 对此有直接解释。

关于更广义的流式 TTS 背景,可参考 Stream 对文本转语音系统如何工作的概览、Async 对流式 TTS 系统的工程拆解,以及 Chrome for Developers 的 AudioWorklet 设计模式。它们讨论的是通用音频管线选择;Spatius 的具体输入行为仍以其音频文档为准。

这样划分也让排查更清晰:回复质量差,通常是应用、模型、检索或工具策略的问题;音频格式错误或发送节奏不对,则是集成问题。两者不该混在一起诊断。

使用源音频,而不是播放回传音频

最重要的时序原则很简单:**TTS 生成出数字人语音时,就把它交给数字人链路。**这也是 Spatius 音频概念中说明的发送时机。

如果你的产品已经通过 RTC、浏览器音频元素或 WebSocket 播放声音,这一点可能有些反直觉。这类通道通常按用户听到的速度交付音频,设计目标是播放,而不是作为音频到动作推理的源数据。

Motion Server 能够从慢速到达的有效音频里生成动作,但客户端需要持续拥有足够准备好的音频与动作片段,才能稳定呈现。如果第一个片段已开始播放、下一个片段却还未准备好,数字人就可能卡顿。因此,Spatius 音频说明建议按 TTS 的生成速度发送,而不要按真实播放的墙钟时间节奏发送。

这种投递取舍在流媒体系统中很常见:Spotify Engineering 写过更平滑的流式传输,Mux 解释了如何从 rebuffer 中恢复,Ably 则讨论了实时流中的 backpressure。它们是理解数字人运行时周边“源音频时序与缓冲行为”的有用背景。

音频来源模式是否适合作为数字人语音输入原因
TTS 服务刚生成的一段 PCM它是源音频,可以在生成后立即转发
已完整生成并快速重放的音频素材通常可以它仍可作为源素材,而非实时播放回传
浏览器麦克风音频默认不可以它属于用户输入,而非数字人要说的话
按 1 倍播放速度到达的 RTC / WebSocket 音频不建议直接使用其节奏可能导致音频与动作储备不足

如果系统里混用了多种接入形态,可通过 Spatius 集成路径选择指南确认:在你实际选用的架构中,究竟由哪一层负责发送数字人语音。

不要通过根据分块时长插入 sleep 来解决节奏问题。只要条件允许,应保留从 TTS 源输出到数字人输入的直连路径;这正是 Direct Mode 模型中的源音频路径。

在边界处定义一份音频契约

在接入任意 TTS 服务前,先写清楚一份音频契约:语音产生服务与向 Spatius 发送语音的客户端或后端,都必须遵守它。这个契约应服务于你选择的集成路径,而不是某个临时的浏览器播放实现。

Motion Server 接收单声道 16 位 PCM(s16le)。会话配置的采样率必须是 8000160002205024000320004410048000 Hz 之一。Spatius 不会自动对输入重采样,因此源音频与会话配置不一致时,必须先转换。最新受支持格式以音频文档为准。

对于通用音频工程背景,ForaSoft 对帧、包与音频分块的解释、Chrome 关于 AudioWorklet 默认可用的说明,以及其 AudioWorklet 设计模式文章都值得参考。它们不会改变 Spatius 发布的 PCM 与采样率契约。

契约问题需要记录的决定
什么进入数字人链路?数字人已获批准回复的 TTS 输出
使用什么格式?单声道 PCM16 / s16le
使用哪个采样率?每个会话固定使用一种受支持采样率
在哪里做转换?在发送给数字人之前,由清晰归属的服务或客户端适配层处理
如何标记本轮语音结束?最后一个源分块带上平台对应的结束输入标记
回复被取消时怎么办?应用按自己的轮次策略清除或替换待处理回复

这不是要你选择一个放之四海皆准的采样率。合适的值取决于 TTS 服务产出的音频和你的整体链路。关键在于:它必须被明确选择、被会话配置使用,并在集成测试中得到验证,而不是从浏览器默认值中“碰巧继承”。

如果浏览器负责音频转换或缓冲,MDN Web Audio API 概览AudioWorklet 参考可作为实现层面的外部资料。它们不会改变 Spatius 的输入契约:音频必须在进入数字人链路前完成归一化。

以生成速度发送,并正确结束最后一个分块

一个可靠的实现,会把输出视为连续的源音频分块,并有一个明确的结束输入事件。

  1. 应用开始生成回复并接收 TTS 音频。
  2. 每个分块都被归一化为会话约定的 PCM 格式。
  3. 分块生成后立即转发,而不是等待按用户收听速度播放。
  4. 回复完成时,实际最后一个分块用 end: true 或当前 SDK 的等价标记发送。

这个结束标记非常关键。在 Direct Mode 中,结束输入标记代表当前一轮数字人语音输入结束。数字人仍会播放剩余动作,随后回到 idle;之后再发送音频则会开始新一轮。Direct Mode 的会话说明 描述了这一生命周期。

关于相邻的流生命周期模式,可参考 Ably 关于分布式实时系统连续性的文章、其对可恢复 AI Token 流的分析,以及 Async 的流式 TTS 架构拆解。它们能帮助理解干净的回合结束边界,但不是对 Spatius 行为的替代说明。

一个实用的应用侧做法,是保留一个已归一化的待发送分块:下一个分块到来时,把前一个作为非最终分块发送,再保留新的分块;当 TTS 服务确认结束时,再将保留的最后一个分块标记为最终分块。这样不会在还不知道是否有下一段时,过早宣布一段音频是最后一段。

收到 TTS 分块:
  pcm = 转换为会话约定的 PCM
  若已有待发送分块:
    发送待发送分块(end=false)
  待发送分块 = pcm

TTS 生成完成:
  若存在待发送分块:
    发送待发送分块(end=true)
  清空待发送分块

具体调用方式应以对应的 AvatarKit Web SDK 参考为准。这个模式强调的不是某个 API 名称,而是职责边界:应用负责判断 TTS 是否完成,数字人接入层接收一段被正确收尾的语音输入。若要把这条边界映射为可见的客户端状态,请参考 Client State & Events

只有播放速度音频时,诚实地使用预缓冲

有时你拿到的并不是按生成速度输出的 TTS,而只是一个按收听速度推进的流。例如某些第三方服务只提供适合播放的音频流。此时逐段立即转发,可能出现前文提到的播放卡顿。

文档给出的做法是在每一轮数字人语音开始时加入预缓冲:

  1. 先在本地积累一段按播放速度到达的音频。
  2. 缓冲覆盖 Motion Server 启动窗口后,再开始向数字人链路发送。
  3. 发送的同时持续从源流补充缓冲。
  4. 本轮被打断或下一轮开始时,重置该缓冲。

Spatius 建议从约 3.5 秒音频开始尝试,4 秒会更保守;之后再根据实际卡顿情况在应用中调整。这个时长不是 SDK 承诺,也不是通用延迟指标,而是“更长启动时间”与“更稳定音频/动作供给”之间的明确取舍。选择生产阈值前,请阅读完整的播放速度音频说明

如果能够直接获得原始 TTS 输出,通常仍是更干净的架构。预缓冲是受限音频来源下的兜底方案,而不是把正常 TTS 输出故意变成播放速度数据的理由。调整这个阈值或音频来源类型时,应再次核对当前的 Spatius 音频说明

若要从更广义角度理解这项取舍,可对照 Spotify Engineering 关于流式平滑度的讨论、Mux 对rebuffer 恢复的解释,以及 Stream 的 TTS 延迟与部署指南。它们让“更长启动时间”与“较少中断体验”之间的产品取舍更容易被讨论清楚。

把数字人输入结束和用户轮次策略分开

很容易让一个“结束”事件承担所有含义:TTS 已生成完、数字人已播完、用户可以说话、Agent 可以接收新指令。它们有关联,但并不是同一个事件。

最后一个音频分块上的标记,只是告诉数字人链路:当前这轮不再有新的数字人语音输入。以下问题仍由产品或 Agent 层决定:

  • 什么时候允许用户打断?
  • 未完成的 Agent 回复应如何处理?
  • 取消时要停止工具工作、TTS 生成,还是只停止呈现?
  • 什么时候把输入焦点还给用户?
  • 外部工具或人工同事接手时,界面应展示什么状态?

请在应用或 Agent 层设计这些策略,再向数字人提供清晰的源音频边界。若想讨论产品层面的中断决策,可参考 用户何时应该能够打断 AI 数字人?

若要梳理现有 Agent 的整体系统边界,可继续阅读如何为现有 SaaS AI Agent 加入 AI 数字人;若是在小范围上线前评估,也可参考如何在 SaaS 产品中试点 AI 数字人

关于实时 AI 周边的打断与传输,Ably 对专用 AI 流式 SDK的讨论、Daily 对实时媒体浏览器性能的建议,以及 WebRTC.ventures 对语音 AI 架构传输方式的概览,提供了有用的外部背景。它们不会把用户轮次策略变成数字人层的职责。

把它当成产品流程测试,而不只是一次 API 调用

连接成功并不代表体验没有问题。应在真实的源音频条件下,测试一轮语音从开始到结束、再到下一轮的完整过渡。

场景需要验证什么有价值的失败信号
正常流式回复分块按生成速度发送,最后一段被正确标记数字人完成回复并回到 idle,不出现无解释卡顿
格式不匹配输入在进入数字人链路前已转换不支持或不匹配的源格式不会悄悄穿透到运行时
缓慢的播放速度源只有在无法获取源 TTS 时,才逐轮使用预缓冲播放稳定性成为被明确管理的取舍,而不是上线后的意外
用户打断应用按策略丢弃或替换待处理语音过期语音不会被误呈现为下一条回复
TTS 在完成前失败待处理状态会被清除,界面进入应用定义的恢复路径不会因为永远等不到最后一段而持续等待

对于 Direct Mode,还应把连接失败纳入测试。当前文档说明:如果其 WebSocket 连接在 15 秒内失败,SDK 会进入仅音频回退模式——音频继续播放,动画不可用。这只是一个狭义的连接回退,不替代产品更广义的错误恢复、重试或人工交接设计。

若要超出这条 SDK 专属回退设计测试,Mux 关于媒体处理采样时序的经验、Ably 对 WebSocket 与实时数据流的概述,以及 Chrome 对 Web Audio 自动播放的解释都是有价值的背景。它们支持团队将时序、重连与浏览器条件和数字人接入契约分开测试。

发布前检查

  • 已确认数字人的语音来源是 TTS 输出,而不是默认麦克风输入。
  • 每个会话都明确使用一种单声道 PCM16 格式和一种受支持采样率。
  • 所有重采样都发生在发送给数字人之前,并有测试覆盖。
  • 分块会在生成后立即转发,不会按播放速度插入 sleep。
  • 已保留足够状态,确保实际最后一个分块被标记为结束输入。
  • 应用取消或替换一轮语音时,会重置待发送音频。
  • 轮次、打断、权限、工具和人工交接仍由应用负责,而不是由数字人层承担。
  • 已测试正常流、播放速度源的预缓冲、格式错误、取消和连接回退。

若要把加载、连接、播放与回到 idle 的客户端状态一并纳入检查,可结合当前的 Spatius Client Lifecycle 指南完成这份音频专项检查。

关于更广义的浏览器音频实现层,Chrome 的 AudioWorklet 设计模式、Stream 的 TTS 管线概览和 Async 对流式 TTS的文章,都是相关的外部资料。当客户端负责 Spatius 输入边界周围的转换、缓冲或播放控制时,它们尤其有参考价值。

常见问题

Spatius 接收的是用户麦克风音频吗?

默认不是。在语音 Agent 场景里,用户麦克风音频通常进入你的 ASR 与 Agent 流程;发送给 Spatius 的是数字人应该说出的 TTS 输出。Spatius 音频文档对两者做了区分。

可以直接发送 MP3 或其他压缩格式吗?

Motion Server 当前文档定义的输入是单声道 16 位 PCM(s16le),采样率需是受支持的值之一。压缩格式应先转换,并确保会话配置匹配;Spatius 不会自动重采样。

为什么不建议直接转发 RTC 音频?

RTC 音频通常按用户收听的 1 倍速度到达,可能让 AvatarKit 在消耗当前片段前拿不到下一段同步音频与动作。能拿到原始 TTS 输出时,应优先转发原始输出;只有播放速度音频时,再按文档中的播放速度音频方案增加每轮预缓冲。

最后的结束输入标记是否决定用户何时能继续说话?

不是。它只标记当前数字人回复的输入音频结束;Direct Mode 会话生命周期定义了这条数字人侧的边界。用户轮次、打断策略、焦点管理以及后续动作,仍由你的产品或 Agent 框架决定。

若要理解如何把流的传输生命周期与产品会话分开,可参考 Ably 对流连续性的说明、Daily 的媒体会话性能建议,以及 Chrome 的 AudioWorklet 后台处理指南。它们是通用工程参考,并非 Direct Mode 的替代文档。

发送的是产品已经决定呈现的语音

优秀的 TTS 到数字人接入,应该看起来并不戏剧化:应用逻辑决定回复,TTS 产生源音频,数字人层收到格式正确且边界清晰的流,客户端在本地渲染结果。

这种分层让每一层都更容易替换、测试和解释。如果你正在评估 SaaS 产品中的实时数字人能力,可以预约 Spatius 演示

若团队想继续研究,Ably 关于可恢复 AI 流的文章、ForaSoft 对音频帧与数据包的指南,以及 Spotify Engineering 关于更平滑媒体投递的讨论,都是与本文产品专属资料相配套的第三方阅读材料。

参考资料

第三方延伸阅读

相关文章