Browse Source

上传代码

newdev_shunjiawei
liwei1dao 3 months ago
parent
commit
8c6d1c1f2e
  1. 382
      docs/云衍通话通话翻译协议和数据结构说明.md

382
docs/云衍通话通话翻译协议和数据结构说明.md

@ -0,0 +1,382 @@
# 通话翻译 硬件协议与数据结构对接文档
> 适用产品:Voitrans(Deep Voice)AI 翻译耳机
> 通信方式:BLE(GATT)
> 音频编码:Opus(16 kHz / 单声道 / 无文件头裸流)
> 本文档以 App 端实现(`local_plugins/ble_service`)为准,供硬件/固件侧对接。
---
## 0. 名词约定
| 名词 | 含义 |
|---|---|
| App / 手机 | 主控端(BLE Central),负责 ASR、翻译、TTS、下行翻译音频编码 |
| 设备 / 耳机 | 从机(BLE Peripheral),采集通话双方音频、播放翻译音频 |
| 本端 / 近端 | 佩戴耳机的用户(mic 采集) |
| 对端 / 远端 | 通话另一方(耳机 DAC / 通话下行声音) |
| 上行 | 设备 → App(原始通话音频) |
| 下行 | App → 设备(翻译后的音频) |
通话翻译整体链路:
```
上行 opus(双声道) ASR+翻译+TTS 下行 opus(分声道)
[耳机 mic/dac] ───────────────► [App 解码/分离] ─────────────► [App 编码] ───────────────► [耳机 左/右声道播放]
近端 = 左声道 左声道(0)=本端译文
远端 = 右声道 右声道(1)=对端译文
```
---
## 1. BLE GATT 服务与特征
### 1.1 命令与控制服务(主服务)
| 项 | UUID | 属性 | 用途 |
|---|---|---|---|
| 主服务 | `0000ABC0-0000-1111-2222-123456789ABC` | — | 命令控制 |
| 写特征 | `0000ABC1-0000-1111-2222-123456789ABC` | Write | App 下发指令(0xAA 帧) |
| 通知特征 | `0000ABC2-0000-1111-2222-123456789ABC` | Notify | 设备上报响应/主动上报(0xBB / 0xCC 帧) |
### 1.2 通话音频服务(通话翻译核心)
| 项 | UUID | 属性 | 用途 |
|---|---|---|---|
| 通话音频服务 | `0000ABC0-0001-1111-2222-123456789ABC` | — | 通话翻译音频 |
| 通话写音频特征 | `0000ABC1-0001-1111-2222-123456789ABC` | Write (No Response) | **下行**:App → 设备,翻译后 opus |
| 通话收音频特征 | `0000ABC2-0001-1111-2222-123456789ABC` | Notify | **上行**:设备 → App,通话原始 opus |
### 1.3 普通音频服务(AI 单次对话 / 音乐等,非通话翻译)
| 项 | UUID | 属性 | 用途 |
|---|---|---|---|
| 音频服务 | `0000AE00-0000-1000-8000-00805F9B34FB` | — | 普通音频上行 |
| 接收音频特征 | `0000AE02-0000-1000-8000-00805F9B34FB` | Notify | 上行 opus(AI 对话等) |
### 1.4 唤醒词 OTA 服务(升级唤醒词资源,非通话翻译)
| 项 | UUID |
|---|---|
| 服务 | `0000ABC0-0002-1111-2222-123456789ABC` |
| 写 | `0000ABC1-0002-1111-2222-123456789ABC` |
| 通知 | `0000ABC2-0002-1111-2222-123456789ABC` |
> 客户端启用通知使用标准 CCCD 描述符 `00002902-0000-1000-8000-00805F9B34FB`。
> App 端建议协商 MTU(下行单包最大约 165B,MTU 需 ≥ ~180,见 §7)。
### 1.5 广播识别信息(参考)
厂商自定义广播字段常量:
| 字段 | 值 |
|---|---|
| 厂商 ID(Manufacturer ID) | `0xABEF` |
| 客户 ID | `0xA001` |
| 产品 ID | `0xA001` |
| 蓝牙状态-未连接 | `0x00` |
| 蓝牙状态-已连接 | `0x0C` |
> App 端当前扫描/匹配主要依据设备名,广播厂商字段为约定信息。
---
## 2. 命令控制协议(0xABC1 / 0xABC2 通道)
所有控制指令走**命令控制服务**的写/通知特征,采用统一帧格式。
### 2.1 帧格式
| 方向 | 帧头 | 结构 |
|---|---|---|
| App → 设备(请求) | `0xAA` | `[0xAA] [CMD] [LEN] [DATA(LEN 字节)] [CRC]` |
| 设备 → App(响应) | `0xBB` | `[0xBB] [CMD] [LEN] [DATA(LEN 字节)] [CRC]` |
| 设备 → App(主动上报) | `0xCC` | `[0xCC] [CMD/类型] [LEN] [DATA(LEN 字节)] [CRC]` |
- **帧头**:1 字节,区分帧类型(`0xAA`/`0xBB`/`0xCC`)。
- **CMD**:1 字节,指令码,见 §3。
- **LEN**:1 字节,`DATA` 的字节数(不含帧头/CMD/LEN/CRC)。无数据时 `LEN=0x00`。
- **DATA**:`LEN` 字节负载。
- **CRC**:1 字节,CRC-8(多项式 0x31),**校验范围 = 帧头 + CMD + LEN + DATA(即除 CRC 自身外的全部字节)**。
- 整帧总长 = `LEN + 4`。
> 说明:设备响应(0xBB)App 侧会做 CRC 校验;主动上报(0xCC)当前 App 侧未强制校验 CRC,但**仍要求带上正确的 CRC 字节并保证 `总长 = LEN + 4`**,否则会被丢弃(长度不匹配直接返回)。
### 2.2 CRC-8 算法(务必与本实现一致)
多项式 `0x31`、初值 `0x00`、MSB 优先、不反转、不异或输出。C 参考实现:
```c
uint8_t crc8(const uint8_t *data, int len) {
uint8_t crc = 0x00;
for (int i = 0; i < len; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
if (crc & 0x80) crc = (uint8_t)((crc << 1) ^ 0x31);
else crc = (uint8_t)(crc << 1);
}
}
return crc;
}
```
### 2.3 帧示例(含实算 CRC)
| 含义 | 完整字节 |
|---|---|
| 获取版本号(请求) | `AA 01 00 86` |
| 获取电量(请求) | `AA 04 00 F1` |
| 打开解码(0xA1,立体声,请求) | `AA 05 02 A1 03 A3` |
| **开启通话翻译(0xA2,立体声,请求)** | `AA 05 02 A2 03 8E` |
| 打开通话录音解码(0xA3,立体声,请求) | `AA 05 02 A3 03 7A` |
| 关闭编解码(请求) | `AA 05 02 00 03 A8` |
| 查询语音唤醒状态(请求) | `AA 14 00 9F` |
| 设置语音唤醒开启(请求) | `AA 15 01 E1 55` |
---
## 3. 指令码(CMD)总表
| CMD | 名称 | 方向 | 说明 |
|---|---|---|---|
| `0x01` | 获取版本号 | 请求/响应 | DATA 为版本号字节(丢弃开头连续 0xFF,其余按十进制点分) |
| `0x02` | 获取产品 ID | 请求/响应 | |
| `0x03` | 获取颜色 ID | 请求/响应 | DATA[0]=颜色 ID |
| `0x04` | 获取电量 | 请求/响应/上报 | 见 §6.1 |
| `0x05` | **编解码控制** | 请求/响应/上报 | **通话翻译核心指令**,见 §4、§5 |
| `0x06` | 音量增大 | 请求 | 无 DATA |
| `0x07` | 音量减小 | 请求 | 无 DATA |
| `0x08` | 获取唤醒词版本 | 请求/响应 | 响应含左右耳各 4 字节版本 |
| `0x09` | 开始 AI 单次对话 | 上报(0xCC) | 设备侧触发,App 打开编码器 |
| `0x10` | 结束 AI 单次对话 | 上报(0xCC) | 设备侧触发,App 关闭编码器 |
| `0x11` | 唤醒(语音唤醒信号) | 上报(0xCC) | 设备上报唤醒 |
| `0x14` | 查询语音唤醒状态 | 请求/响应 | 响应 DATA[0]:`0xE1`=开 / `0xE0`=关 |
| `0x15` | 设置语音唤醒状态 | 请求/响应 | 请求 DATA[0]:`0xE1`=开 / `0xE0`=关 |
| `0x16` | **上报:开启通话翻译** | 上报(0xCC) | 设备侧发起开启通话翻译,见 §5.2 |
| `0x17` | **上报:关闭通话翻译** | 上报(0xCC) | 设备侧发起关闭通话翻译,见 §5.2 |
---
## 4. 编解码控制指令 0x05(通话翻译核心)
### 4.1 请求格式
```
AA 05 02 [control] [channel] [CRC]
```
`DATA = [control(1B), channel(1B)]`,`LEN=0x02`。
**control(编解码控制值)**
| 值 | 名称 | 含义 |
|---|---|---|
| `0x00` | 关闭编解码 | 关闭全部编解码(结束通话翻译/录音/播放) |
| `0xA1` | 打开解码 | 播放远端/音乐声音(上行单声道 opus 40B) |
| `0xA2` | **开启通话翻译** | mic + dac 声音上行,并接收 App 下行翻译重编码音频(上行双声道 opus 80B) |
| `0xA3` | 通话录音 | mic + dac 声音上行录制(上行双声道 opus 80B) |
| `0xB1` | 打开编码 | 打开编码器(备用) |
| `0xF0` | 左声道通话休眠 | 见 §5.3(一般由设备主动上报) |
| `0xF1` | 右声道通话休眠 | 见 §5.3 |
| `0xF2` | 左声道通话恢复 | 见 §5.3 |
| `0xF3` | 右声道通话恢复 | 见 §5.3 |
**channel(声道模式)**
| 值 | 含义 |
|---|---|
| `0x01` | 左声道 |
| `0x02` | 右声道 |
| `0x03` | 立体声(通话翻译默认) |
### 4.2 响应格式
设备回:`BB 05 02 [control] [channel] [CRC]`(原样回显 control 与 channel)。
若执行失败,可回 `LEN` 与实际不符或 DATA[0]=`0x00`,App 会按失败处理。
### 4.3 各 control 值对应的上行音频参数
App 收到对应 control 后,按下表启动 Opus 解码器(**必须与固件上行编码参数一致**):
| control | 上行声道数 | 采样率 | 帧长(packetSize) | 备注 |
|---|---|---|---|---|
| `0xA1` 打开解码 | 1(单声道) | 16000 | 40 | 远端/音乐 |
| `0xA2` 开启通话翻译 | 2(双声道) | 16000 | 80 | 左=mic(近端),右=dac(远端) |
| `0xA3` 通话录音 | 2(双声道) | 16000 | 80 | 同上 |
---
## 5. 通话翻译业务协议
### 5.1 开启/关闭方式
通话翻译可由**两侧任意一方发起**:
- **App 主动开启**:App 下发 `0x05 / 0xA2`(`AA 05 02 A2 03 8E`)→ 设备开始上行双声道通话音频,并准备接收下行翻译音频。
- **设备主动发起**:设备上报 `0x16`(开启)/ `0x17`(关闭),App 收到后进入/退出通话翻译流程。
关闭时下发 `0x05 / 0x00`(`AA 05 02 00 03 A8`)关闭编解码。
### 5.2 设备主动上报开启/关闭(0x16 / 0x17)
帧格式(0xCC 主动上报):
```
CC 16 [LEN] [DATA...] [CRC] // 开启通话翻译
CC 17 [LEN] [DATA...] [CRC] // 关闭通话翻译
```
- App 侧解析:若 `DATA` 长度 ≥ 4,取 `DATA[3]` 作为 status(约定 `0xFF`);否则 status 视为无效。
- 建议固件按约定 `LEN=0x01, status=0xFF` 或与固件协商一致的固定格式发送;关键在于 `总长 = LEN + 4` 必须成立。
### 5.3 声道休眠 / 恢复(流控,0x05 的 0xF0~0xF3)
用于通话过程中,设备侧临时暂停某一声道的下行音频(例如该声道当前无需播放),App **不会丢包**,休眠期间该声道下行包转入积压缓存,收到恢复指令后按序补发。
由**设备主动上报**(0xCC,CMD=0x05,DATA[0]=控制值):
```
CC 05 [LEN] [F0] ... [CRC] // 左声道休眠:App 暂停下发左声道(channel=0)
CC 05 [LEN] [F1] ... [CRC] // 右声道休眠:App 暂停下发右声道(channel=1)
CC 05 [LEN] [F2] ... [CRC] // 左声道恢复:App 恢复并按序补发左声道积压
CC 05 [LEN] [F3] ... [CRC] // 右声道恢复:App 恢复并按序补发右声道积压
```
| 上报值 | 含义 | App 行为 | 配对 |
|---|---|---|---|
| `0xF0` | 左声道通话休眠 | 暂停下发左声道(0),仅发右声道 | 与 `0xF2` 配对 |
| `0xF1` | 右声道通话休眠 | 暂停下发右声道(1),仅发左声道 | 与 `0xF3` 配对 |
| `0xF2` | 左声道通话恢复 | 恢复左声道下发,补发休眠期间积压包 | 与 `0xF0` 配对 |
| `0xF3` | 右声道通话恢复 | 恢复右声道下发,补发休眠期间积压包 | 与 `0xF1` 配对 |
> 休眠/恢复为幂等操作;重复上报不叠加。
---
## 6. 部分响应数据结构
### 6.1 电量信息(0x04)
`DATA` 至少 3 字节:`[左耳, 右耳, 充电仓]`,每字节:
- bit0~bit6:电量百分比(`value & 0x7F`)
- bit7:是否充电(`value & 0x80 != 0`)
主动上报同格式,帧头 `0xCC`,CMD=`0x04`。
### 6.2 唤醒词版本(0x08)
响应 `DATA` 为 8 字节:`[左耳 4B][右耳 4B]`,每 4 字节点分十进制即版本号。
### 6.3 语音唤醒(0x14 / 0x15)
- `0x14` 响应:`DATA[0]` = `0xE1`(开) / `0xE0`(关)。
- `0x15` 请求:`DATA[0]` = `0xE1`(开) / `0xE0`(关);响应:`DATA[0]`=命令状态,`DATA[1]`=`0x01` 表示设置成功。
---
## 7. 音频数据结构(0xABC1-0001 / 0xABC2-0001 通道)
音频不走 0xAA/0xBB/0xCC 帧,直接在**通话音频服务**的特征上传输原始负载。
### 7.1 上行:设备 → App(通话收音频特征 `0000ABC2-0001-...`)
- **纯 Opus 裸流**(无 0xAA/CRC 帧头、无文件头、无自定义包头),直接把 opus 码流分片放入 Notify 负载即可。
- 通话翻译(`0xA2`/`0xA3`):**双声道(stereo)交织** Opus,采样率 **16 kHz**,帧长(packetSize)**80**。
- 左声道 = 近端(mic 采集,本端说话人)
- 右声道 = 远端(dac / 通话对端)
- 普通解码(`0xA1`):单声道 Opus,16 kHz,帧长 40。
- App 侧把收到的负载按到达顺序送入解码器,因此**分片可任意长度**,但需保证 opus 帧边界正确、顺序不乱。
### 7.2 下行:App → 设备(通话写音频特征 `0000ABC1-0001-...`)
App 将本端/对端译文分别用**独立** Opus 编码器编码(16 kHz / 单声道 / 每帧约 40B),左右各累积 N 帧后合并为 1 个下行包发送。
**下行包结构:**
```
+---------+---------+---------------------------------------------+
| 序号 | 声道 | N 个 opus 帧顺次拼接 |
| 4 字节 | 1 字节 | 每帧约 40 字节(16kHz/mono/无头) |
| uint32 | 0/1 | |
| 小端 | | |
+---------+---------+---------------------------------------------+
[0..3] [4] [5 .. 5 + N*framelen)
```
| 字段 | 偏移 | 长度 | 说明 |
|---|---|---|---|
| 序号 seq | 0 | 4 | **小端序 uint32**(低字节在前)。每个声道各自独立自增,从 0 开始 |
| 声道 channel | 4 | 1 | `0x00`=左声道(本端译文) / `0x01`=右声道(对端译文) |
| opus 数据 | 5 | N×帧长 | N 帧 opus 顺次拼接,每帧 16kHz/单声道 |
- **N(合包帧数)**:默认 `4`,可运行时调整(范围 1~20)。故单包总长 = `5 + N × 帧长`(帧长约 40B,默认约 165B)。
- 写类型:**Write Without Response**(不等 ACK,保吞吐)。
- 发送节拍:App 按固定间隔逐包下发(默认约 60ms/包,可调);左右声道公平轮转。
- **不丢包保证**:底层写拥塞时 App 会退避重试并按声道队头重发,固件侧只需按 `seq` 归位/去重与顺序播放。
> ⚠️ **字节序特别说明**:下行包序号在实现中为**小端序(little-endian)**(`buildBundlePacket` 写入与发送线程解析均为小端)。代码注释里出现的“大端”描述为笔误,**请以小端为准**。
---
## 8. 通话翻译完整时序(示例:App 发起)
```
App 设备(耳机)
│ (已连接, 已订阅 0xABC2 / 0xABC2-0001 通知, 已协商 MTU)
│
│ ── 0x05/0xA2 开启通话翻译 ──► AA 05 02 A2 03 8E
│ ◄── 0xBB 响应回显 ── BB 05 02 A2 03 ..
│
│ 开始采集通话音频
│ ◄════ 上行双声道 opus(16k/80) ════ (通话收音频特征 0xABC2-0001, 裸流)
│
│ [App: 解码→分离近/远端→ASR→翻译→TTS→独立编码]
│
│ ════ 下行 opus 合包 ════► (通话写音频特征 0xABC1-0001)
│ [seq(4B小端)][ch 0/1][N×opus] 耳机按 ch 播放到对应声道
│
│ ◄── (可选) 0xCC 05 F0/F1 休眠 ── 暂停对应声道下发
│ ◄── (可选) 0xCC 05 F2/F3 恢复 ── 恢复并按序补发
│
│ ── 0x05/0x00 关闭编解码 ──► AA 05 02 00 03 A8
│ 或 ◄── 0xCC 0x17 设备请求关闭 ──
│ 停止采集/播放
```
---
## 9. 关键参数汇总(固件对接必对齐项)
| 项 | 值 |
|---|---|
| Opus 采样率 | 16000 Hz |
| Opus 声道(上行通话翻译) | 2(stereo,左=近端 右=远端) |
| Opus 帧长(上行通话翻译 0xA2/0xA3) | packetSize = 80 |
| Opus 帧长(上行单声道 0xA1) | packetSize = 40 |
| Opus 帧长(下行译文) | 单声道,每帧约 40B |
| Opus 文件头 | 无(裸流) |
| 下行合包序号字节序 | **小端 uint32** |
| 下行声道标识 | 0=左(本端译文) / 1=右(对端译文) |
| 下行默认合包帧数 N | 4(可调 1~20) |
| 下行写类型 | Write Without Response |
| 控制帧 CRC | CRC-8,多项式 0x31,初值 0x00,MSB 优先,校验含帧头 |
| 控制帧总长 | LEN + 4 |
| 响应超时(App 侧) | 1000 ms |
---
## 10. 对接注意事项 / 已知实现细节
1. **CRC 覆盖范围包含帧头**(`0xAA/0xBB/0xCC`),且 `CRC` 前的所有字节参与计算。
2. **主动上报(0xCC)CRC**:App 侧当前未强制校验,但**长度必须满足 `总长 = LEN + 4`**,否则整帧被丢弃。请固件正确填 `LEN` 并补 CRC 字节。
3. **下行序号字节序为小端**(代码注释“大端”为笔误,见 §7.2)。
4. **下行合包帧数**:默认 `4`(部分注释写“5/205B”为旧值,请以运行参数为准,可由 App 通过调试接口下发调整)。
5. **上行为纯 opus 裸流**,无任何自定义包头/序号;分片长度自由,但 opus 帧边界与顺序必须正确。
6. **上行通话翻译为双声道交织**(channel=2),近端/远端由左右声道区分;固件需保证左=mic、右=dac 的固定映射。
7. 通话翻译期间设备可用 `0xF0~0xF3` 做单声道流控,App 保证不丢包、按 seq 顺序补发;固件按声道 seq 播放即可。
8. 建议连接后协商较大 MTU(≥ ~180 字节)以容纳单个下行包,减少分片。
---
*本文档依据 App 端 `local_plugins/ble_service`(Android 实现)整理:`BleConst.kt`、`BleCommandSender.kt`、`OpusAudioManager.kt`、`BleService.kt`。如与固件实测不一致,请以双方联调确认为准。*
Loading…
Cancel
Save