主题
D. 数字音频基础
语音对话的本质是声波数字化后在网络中传输。本附录讲解数字音频的核心概念,帮助你理解 JoyInside 为什么要求 PCM 16k/16bit/单声道,为什么推荐 Opus,以及单包 300ms、帧长 60ms 这些参数背后的原理。
目录
D.1 信号转换基础(模拟 → 数字)
数字音频是将模拟声波转换为数字信号并进行处理、存储与传输的技术。核心分三步:采样 → 量化 → 编码。
采样(Sampling)
以固定时间间隔采集模拟信号的瞬时幅度。采样率决定可重现的最高频率(奈奎斯特定理:采样率 ≥ 2 × 最高频率)。
- 人耳听觉范围约 20Hz-20kHz,故 CD 采样率 44.1kHz(>40kHz)即可覆盖全频段;
- 语音频段主要集中在 300Hz-3.4kHz,故通信语音常用 8kHz(电话)或 16kHz(VoIP);
- JoyInside 默认 16kHz,覆盖语音频段且带宽适中。
量化(Quantization)
将采样值映射到离散电平。位深决定精度与动态范围。
- 16bit 对应 2^16 = 65536 个电平,信噪比约 96dB(CD 质量);
- 8bit 仅 256 个电平,音质明显劣化;
- JoyInside 使用 16bit,平衡音质与带宽。
编码(Encoding)
将量化值转换为二进制码流,形成原始 PCM 数字音频数据。
模拟声波 → 采样(16kHz) → 量化(16bit) → 编码 → PCM 二进制流D.2 编码与压缩
音频压缩分无损(去冗余保全部信息,如 FLAC/ALAC,高保真存储)与有损(基于心理声学模型舍弃不敏感成分,如 MP3/Opus,流媒体与实时通信)。JoyInside 上行/下行用 PCM(不压缩)、Opus(有损)、MP3(有损下行)。
码率控制
| 模式 | 全称 | 特点 | JoyInside 支持 |
|---|---|---|---|
| CBR | 恒定码率 | 每帧固定字节数,传输稳定 | ✅(Opus enableOpusCbr=true) |
| VBR | 可变码率 | 按内容复杂度调整码率,音质更优 | ✅(默认) |
| ABR | 平均码率 | 按目标平均码率编码 | — |
JoyInside 支持 CBR 与 VBR,默认 VBR。详见 4.4 音频格式。
D.3 三大音频格式对比
PCM(脉冲编码调制)---- 数字音频的基石
未经压缩的原始数字音频格式,所有数字音频处理都始于 PCM 数据流。
核心原理:采样 + 量化 + 编码三位一体。
优点:
- 保真度高,无压缩损失;
- 处理灵活,可直接进行滤波、均衡等操作;
- 无编码延迟,仅传输/存储延迟。
局限:
- 文件体积大(CD 音质约 1411kbps);
- 存储与传输成本高。
在 JoyInside 中的应用:
- 上行默认格式:16k/16bit/单声道,单包 ≤300ms;
- 下行 TTS 支持格式,帧长 60-120ms;
- 详见 4.4。
MP3 ---- 有损压缩的里程碑
首个成功实现高压缩比且保持"可接受"音质的有损音频编码标准,基于心理声学模型。压缩比高、兼容性极广,但有损不可逆、延迟较高(>100ms)、不支持流式切分。
在 JoyInside 中的应用:
Opus ---- 现代低延迟、高效率的编码标准
IETF 制定的开源、免版税音频编码格式,结合 SILK(语音)与 CELT(音乐)两种编码模式自适应选择。极低延迟(单帧可低至 2.5ms)+ 高压缩比,适合实时通信。
在 JoyInside 中的应用:
- 上行推荐格式:体积约 PCM 的 1/8,单包 ≤480 字节,帧长 ≤120ms(建议 60ms);
- 下行 TTS 支持格式,帧长 10/20/40/60ms;
- 支持 CBR(
enableOpusCbr=true,默认 VBR); - 下发的 Opus 裸流是单声道;
- 压缩比建议 8:1(≈32kbps),最高 24:1(超过报错),详见 Opus 压缩比与目标比特率;
- 详见 4.4。
Opus 压缩比与目标比特率
Opus 将 PCM 压缩成二进制码流,压缩比决定目标比特率。以 JoyInside 默认 PCM 配置(16k/16bit/单声道,原始 256kbps)为例:
目标比特率 = PCM 原始比特率 ÷ 压缩比 = 256kbps ÷ 压缩比| 压缩比 | 目标比特率 | 说明 |
|---|---|---|
| 8:1 | 32 kbps | 建议 |
| 16:1 | 16 kbps | 可用 |
| 24:1 | ≈10.7 kbps | 最高(不推荐),超过报错 |
字节闭环:8:1 压缩比下,32kbps × 120ms ÷ 8 = 480 字节——正好等于 JoyInside 默认 opus 音频包字节上限,三个数字(8:1 / 120ms / 480B)互相印证。
Opus 包结构与拼包原理
本节讲 Opus 包的内部结构与拼包机制,解释 4.4 能力清单与 5.3.4 落地选型背后的原理。设备端工程师按需阅读,接入调试时理解 frameSize 参数用。
三层概念:数据包 / opus 音频包 / opus 帧
JoyInside 上行 opus 涉及三个层级(与 4.4 一致):
| 层级 | 定义 |
|---|---|
| 数据包 | 一次 WebSocket 音频上传的 byte[](传输层产物) |
| opus 音频包 | opus 标准的包,以 TOC 字节开头(opus Packet) |
| opus 帧 | opus 音频包内的帧(opus Frame) |
- 单包模式:数据包 = 1 个 opus 音频包(1:1)
- 拼包模式:数据包 = 多个 opus 音频包裸拼接成 1 个
byte[]
帧与包的关系
Opus 编码器把音频按**帧(Frame)**编码。一个 opus 音频包可含 1 帧或多帧;同一个包内的帧参数必须一致:编码模式、音频带宽、帧大小、声道数。
Opus 支持的帧长:2.5 / 5 / 10 / 20 / 40 / 60 ms(由 TOC 的 config 决定)。单帧最大 60ms。JoyInside 建议上行用 60ms/帧。
TOC 字节与四种包
每个 opus 音频包以一个 TOC(Table of Contents)字节开头,结构如下:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| config |s| c |
+-+-+-+-+-+-+-+-+- config(高 5 位):32 种编码配置,由编码模式(SILK/Hybrid/CELT)、音频带宽(NB/MB/WB/SWB/FB)、帧长度组合而成,决定本包的帧长。详见 opus 标准的 32 配置表
- s:0 = 单声道,1 = 立体声
- c(低 2 位):决定包内帧数与封装方式,即下表四种包
| c 值 | 名称 | 帧数 | 是否需帧长度/帧数指示 | 说明 |
|---|---|---|---|---|
| 0 | 0 号包 | 1 帧 | 否(结构固定) | TOC 后直接跟 1 帧数据,最简单 |
| 1 | 1 号包 | 2 帧(等长) | 否(各占一半,包字节必为奇数) | 两帧大小相同 |
| 2 | 2 号包 | 2 帧(不等长) | 是(需编码第 1 帧长度) | 两帧大小不同 |
| 3 | 3 号包 | 任意帧 M | 是(TOC 后有帧数字节 v|p|M) | 唯一支持任意帧数,且支持 CBR/VBR |
四种包的本质区别:是否需要额外的帧长度/帧数指示字节。0/1 号包结构固定(1 帧、或 2 等长帧),解码器靠包大小自然确定边界,无需指示;2/3 号包帧大小或帧数不固定,必须显式编码指示字节。JoyInside 上行单包模式可用 0 号或 3 号包;1/2 号包很少用。
一个 opus 音频包如何支持 CBR
3 号包的帧数字节最高位 v 决定码率模式:
| v 值 | 模式 | 说明 |
|---|---|---|
| 0 | CBR | 恒定码率,每帧字节数相同,传输稳定 |
| 1 | VBR | 可变码率,按内容复杂度调整,音质更优 |
JoyInside 默认 VBR,可通过 enableOpusCbr=true 切 CBR(弱网下推荐,传输更稳定)。详见 B.2 码率控制。
拼包机制:多个 opus 音频包裸拼接
Opus 包与包之间没有边界(裸拼接无法区分),需额外机制告诉解码器每个包的大小(标准做法是用 Ogg 等容器格式)。JoyInside 的拼包不走容器,而是:
- 拼包 = 把多个 opus 音频包裸拼接成 1 个
byte[]作为数据包上传 - 服务端按
frameSize等长切分还原每个 opus 音频包
正因服务端按定长切分,拼包必然是 CBR(每个 opus 音频包字节数必须相同,否则切不开)。这也是拼包只能用 3 号包 + CBR 的根本原因。
时长与字节上限
- opus 音频包时长:≤120ms(opus 标准硬上限,按最小帧 2.5ms 算最多 48 帧)
- 数据包总时长:建议 ≤300ms(软建议,与 PCM 单包对齐,保证 ASR 流式处理时延)
- opus 单帧字节上限:1275 字节(对应 20ms 帧约 510kbps,MDCT 算法最高有效码率)
- JoyInside opus 音频包字节:默认上限 480 字节,超过需显式设 frameSize
frameSize 语义
audio.input.frameSize = 一个 opus 音频包的字节数,仅拼包时传:
- 单包模式:数据包 = 1 个 opus 音频包,边界由包大小自然确定,无需 frameSize
- 拼包模式:数据包 = 多个 opus 音频包,服务端按 frameSize 等长切分,必须传
- 设错(< 实际 opus 音频包字节)→ 服务端切错 → 解码失败
这就是 5.3.4 易踩坑点里「frameSize 是单个 opus 音频包字节数」的原因。
三大格式对比总表
| 特性 | PCM | MP3 | Opus |
|---|---|---|---|
| 本质 | 原始未压缩数据 | 有损压缩(心理声学模型) | 有损压缩(自适应混合编码) |
| 码率范围 | 固定(16k/16bit/单声道 ≈ 256kbps) | 通常 128-320kbps | 6-510kbps(自适应) |
| 音质上限 | 最高(无损失) | 接近 CD(高码率下) | 接近 CD(中高码率下) |
| 延迟 | 无编码延迟 | 较高(>100ms) | 极低(2.5-65ms) |
| JoyInside 上行 | ✅ 默认 | ❌ 不支持 | ✅ 推荐 |
| JoyInside 下行 | ✅ | ✅(不流式) | ✅ |
| 流式切分 | ✅ | ❌ | ✅ |
| 主要用途 | 端侧采集、原始处理 | 音乐分发、大众播放 | 实时通信、低带宽流媒体 |
D.4 关键参数关系
码率公式
音频码率(kbps) = 采样率(Hz) × 位深(bit) × 声道数 / 1000示例(JoyInside PCM 默认配置):
码率 = 16000 × 16 × 1 / 1000 = 256 kbps即每秒 PCM 数据约 32KB(256kbps ÷ 8)。
参数决定关系
| 参数 | 决定因素 | 典型值 | 影响 |
|---|---|---|---|
| 采样率 | 最高可记录频率 | 16kHz(JoyInside)/ 44.1kHz(CD)/ 48kHz(影视) | 高频响应、时间精度 |
| 位深 | 动态范围与噪声 | 16 bit(JoyInside/CD)/ 24 bit(专业) | 信噪比、细节层次 |
| 声道数 | 空间维度 | 1(JoyInside 单声道)/ 2(立体声) | 沉浸感、混音复杂度 |
| 码率 | 质量与文件大小 | 256kbps(JoyInside PCM)/ 1411kbps(CD PCM) | 存储、传输带宽 |
为什么 JoyInside 选 16k/16bit/单声道
| 选择 | 原因 |
|---|---|
| 16kHz 采样率 | 覆盖语音频段(300Hz-3.4kHz),带宽适中;高于电话音质(8kHz),低于 CD(44.1kHz)以节省带宽 |
| 16bit 位深 | 信噪比约 96dB,语音动态范围足够 |
| 单声道 | 语音对话无需立体声,节省一半带宽 |
D.5 JoyInside 中的音频实践
上行音频(设备 → 云)
| 参数 | 取值 | 说明 |
|---|---|---|
| codec | pcm(默认)/ opus(推荐,体积 1/8) | mp3 不支持上行 |
| sampleRate | 16000 / 24000 / 32000 | 默认 16k |
| PCM 单包 | ≤300ms | 16k/16bit/单声道 |
| Opus 单包 | opus 音频包 ≤480 字节(默认上限)、≤120ms(硬上限,帧长建议 60ms) | 数据包总时长建议 ≤300ms;单包不传 frameSize,拼包传(=单 opus 音频包字节) |
| 发送间隔 | = 音频时长 | 均匀发送,否则延时异常 |
下行 TTS(云 → 设备)
| 参数 | 取值 | 说明 |
|---|---|---|
| codec | pcm / opus / mp3 | mp3 不支持流式切分 |
| frameSizeMs | pcm 60-120ms / opus 10/20/40/60ms | 下行 TTS 帧长 |
| enableOpusCbr | true / false | Opus CBR,默认 VBR |
| 下发节奏 | 首次 3 包 × 60ms,后续间隔 54ms | 见 10.3.4 |
| 二进制传输 | audio.binary=true | 上行 ws.send(bytes),下行 binary frame |
常见音频问题速查
| 现象 | 根因 | 章节 |
|---|---|---|
| 上传音频无回复 | ASR 未识别(EMPTY_CONTENT),检查音频有效性、格式匹配、发送间隔 | 10.2.1 |
| TTS 卡顿 | 端侧攒包或单线程,改直接播放 + 多线程 | 10.3.4 |
| TTS 自言自语 | AEC 未生效,TTS 被回采触发 ASR | 10.2.4 |
| binary not support | 未开 audio.binary=true 但传了 bytes | 10.7 |
| 音频格式不匹配 | 端侧解码格式与 CLIENT_VOICE_CHAT_UPDATE 配置不一致 | 4.4 |