npm package: @sparklinkplayjoy/protocol-keyboard-v2
Description: 获取设备的语音相关参数信息。
Author: @sparklinkplayjoy
Latest version: 1.0.18

# Keyboard V2 Protocol

## 语音功能

### 获取语音参数 (getVoiceParams)

获取设备的语音相关参数信息。

#### 协议格式

**命令**: 获取语音参数

**参数**: 无

**响应格式**:
- 返回设备的语音参数信息

### 获取喇叭列表和位置信息 (getTrumpetListAndPosition)

获取设备上所有喇叭的列表和位置信息。

#### 协议格式

**命令**: 获取喇叭列表和位置信息

**参数**: 无

**响应格式**:
- 返回喇叭列表和位置信息

### 启动语音更新 (startVoiceUpdate)

启动语音更新功能，用于向设备传输新的语音文件。

#### 协议格式

**命令**: `0x03` - 启动语音更新

**参数**:
- `number[18]` - 语音编号 (0-255)
- `reserve[13]` - 预留，固定值 0xFF
- `size_[13]` - 文件大小 (4字节，小端序，支持最大4GB文件)
- `wav_head[44]` - WAV文件头信息 (44字节)

**响应格式**:
- `status [18]` - 返回状态
  - `VoiceUpdateStatus.Success (0x00)` - 成功
  - `VoiceUpdateStatus.PeripheralFault (0xFF)` - 外设故障，无法进行数据传输
  - `VoiceUpdateStatus.ParameterError (0xFD)` - 参数错误
  - `VoiceUpdateStatus.ChecksumError (0xFC)` - 数据校验错误
  - `VoiceUpdateStatus.CommandRejected (0xFE)` - 指令被拒绝
  - `VoiceUpdateStatus.InsufficientSpace (0xF6)` - 空间不足
  - `VoiceUpdateStatus.TidySpace (0xF9)` - 空间不足，需整理空间
- `statusText` - 状态中文描述
- `number[13]` - 语音编号
- `size_[13]` - 文件大小

### 传输语音文件 (sendVoiceFile)

传输语音文件的实际数据内容。

#### 协议格式

**命令**: `0x04` - 传输语音文件

**参数**:
- `offset[13]` - 数据偏移量 (4字节，小端序)
- `data[n]` - 语音数据 (可变长度)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `offset[13]` - 数据偏移量
- `receivedSize[13]` - 已接收数据大小

### 停止语音更新 (stopVoiceUpdate)

完成语音更新流程。

#### 协议格式

**命令**: `0x05` - 停止语音更新

**参数**:
- `number[18]` - 语音编号 (0-255)
- `reserve[13]` - 预留，固定值 0xFF
- `size_[13]` - 文件大小 (4字节，小端序)
- `file_name[36]` - 语音文件名 (36字节，UTF-8编码)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `number[13]` - 语音编号
- `size_[13]` - 文件大小

### 读取语音信息 (readVoiceInfo)

读取指定语音文件的元信息。

#### 协议格式

**命令**: `0x06` - 读取语音信息

**参数**:
- `number[18]` - 语音编号 (0-255)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `number[13]` - 语音编号
- `size_[13]` - 文件大小
- `file_name[36]` - 语音文件名

### 启动读取语音文件 (startReadVoiceFile)

启动语音文件读取流程。

#### 协议格式

**命令**: `0x07` - 启动读取语音文件

**参数**:
- `number[18]` - 语音编号 (0-255)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `number [13]` - 语音编号
- `size [18]` - 文件大小 (4字节，小端序)
- `fileName` - 语音文件名 (36字节，UTF-8)

### 读取语音文件 (readVoiceFile)

读取语音文件数据。

#### 协议格式

**命令**: `0x08` - 读取语音文件

**参数**:
- `offset[18]` - 数据索引offset (4字节，小端序)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `number [13]` - 语音编号
- `offset [18]` - 数据偏移量 (4字节，小端序)
- `data` - 语音数据

### 停止读取语音文件 (stopReadVoiceFile)

停止语音文件读取流程。

#### 协议格式

**命令**: `0x09` - 停止读取语音文件

**参数**: 无

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述

### 实时播放语音 (playVoiceRealTime)

实时播放指定的语音数据，支持流式传输。

#### 协议格式

**命令**: `0x0a` - 实时播放语音

**参数**:
- `ctl[18]` - 控制类型
  - `0x00` - 停止实时播放
  - `0x01` - 启动实时播放
  - `0x02` - 传输语音数据，直接播放
  - `0x03` - 传输语音数据，ADPCM压缩数据
  - `0x04` - 重新启动播放，直接播放
  - `0x05` - 重新启动播放，ADPCM压缩数据
- `index[13]` - 喇叭索引 (1字节)
- `data[60B]` - 语音数据 (60字节，某些控制类型需要)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `index [13]` - 喇叭索引

**注意事项**: 实时播放的音量由上位机进行数字调整

### 播放语音文件 (playVoice)

播放存储的语音文件，支持音量控制和播放控制。

#### 协议格式

**命令**: `0x0b` - 播放语音文件

**参数**:
- `ctl[18]` - 控制类型
  - `0x00` - 停止语音播放 (后随 `number`)
  - `0x01` - 开始语音播放 (后随 `number`)
  - `0x02` - 设置音量 (后随 `volume`)
- 当 `ctl=0x00/0x01` 时：`number[18]` - 语音编号 (0-255)
- 当 `ctl=0x02` 时：`volume[13]` - 音量 (0-100)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `number [13]` - 语音编号

**注意事项**: 设置音量与开始/停止播放的后续参数不同，注意区分顺序

### 语音空间管理 (voiceSpaceManagement)

管理语音存储空间，包括删除、整理等操作。

#### 协议格式

**命令**: `0x0c` - 语音空间管理

**参数**:
- `ctl[18]` - 控制类型
  - `0x01` - 删除全部语音
  - `0x02` - 删除全部按键对应关系
  - `0x03` - 按照键值删除对应关系
  - `0x04` - 按照语音编号删除语音
  - `0x05` - 整理空间碎片
- `number[13]` - 语音编号
  - 当 `ctl=0x04` 时：填写实际语音编号
  - 其他情况：自动填写 `0xFF`
- `key[2B]` - 键值
  - 当 `ctl=0x03` 时：填写实际键值
  - 其他情况：自动填写 `0xFFFF`

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `totalSpace [18]` - 总空间 (4字节，小端序)
- `usedSpace [18]` - 已使用空间 (4字节，小端序)
- `freeSpace [18]` - 可用空间 (4字节，小端序)
- `voiceCount [18]` - 语音文件数量

**注意事项**: 
- 除按照语音编号删除语音外，语音编号自动写0xFF
- 除按照键值删除对应关系外，键值自动写0xFFFF
- SDK会自动根据控制类型填写正确的参数值

### 写入键值对应语音 (writeKeyVoice)

设置按键对应的语音文件和播放音量。

#### 协议格式

**命令**: `0x0d` - 写入键值对应语音

**参数**:
- `key[2B]` - 按键键值 (2字节)
- `volume[1B]` - 音量 (0-100，1字节)
- `number[1B]` - 语音编号 (1字节)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `keyCode [18]` - 键码 (2字节，小端序)
- `voiceNumber [13]` - 语音编号

**功能说明**: 写入键值对应的语音索引，且标注播放音量

### 读取键值对应语音 (readKeyVoice)

读取按键对应的语音文件信息。

#### 协议格式

**命令**: `0x0e` - 读取键值对应语音

**参数**:
- `key[2B]` - 按键键值 (2字节)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `keyCode [18]` - 键码 (2字节，小端序)
- `voiceNumber [13]` - 语音编号

### 根据位置读取键值 (readKeyVoiceByPosition)

根据索引位置读取按键对应的语音文件信息。

#### 协议格式

**命令**: `0x0f` - 根据位置读取键值

**参数**:
- `index[2B]` - 索引值 (0 - key_count之间，2字节)

**响应格式**:
- `status [18]` - 返回状态
- `statusText` - 状态中文描述
- `index [18]` - 索引值 (2字节，小端序)
- `keyCode [18]` - 键码 (2字节，小端序)
- `voiceNumber [13]` - 语音编号

**功能说明**: 根据索引的位置读取键值对应的语音信息

### 高级功能方法

#### 一键更新语音文件 (updateVoiceFile)

封装完整的语音文件更新流程，包括启动、分片传输和停止。

**参数**:
- `number`: 语音编号 (0-255)
- `fileName`: 文件名（用于stop）
- `wav`: 完整WAV字节（包含44字节头）
- `chunkSize`: 分片大小（默认58字节，数据区58B以满足64B报文：1+1+4+58）
- `cb`: 回调函数，返回进度信息

**功能**: 自动处理语音更新的完整流程，支持进度回调

#### 一键读取语音文件 (readVoiceFileAll)

封装完整的语音文件读取流程，包括启动、分块读取和停止。

**参数**:
- `number`: 语音编号 (0-255)
- `chunkSize`: 读取步进（默认56字节，根据设备返回数据长度自适应）
- `cb`: 回调函数，返回读取进度信息

**功能**: 自动处理语音读取的完整流程，支持进度回调

#### 实时播放流程 (playVoiceRealTimeFlow)

封装实时播放的完整流程，包括启动、数据传输和停止。

**参数**:
- `index`: 喇叭索引
- `data`: 实时播放的原始数据
- `chunkSize`: 分片大小（默认60字节）
- `cb`: 回调函数，返回播放进度信息

**功能**: 自动处理实时播放的完整流程，支持进度回调

#### 使用示例

```typescript
import { VoiceController } from '@sparklinkplayjoy/sdk-keyboard-v2';

const voiceController = new VoiceController(deviceBase);

// 1. 获取语音参数
const voiceParams = await voiceController.getVoiceParams();
console.log('语音参数:', voiceParams);

// 2. 获取喇叭列表和位置信息
const trumpetInfo = await voiceController.getTrumpetListAndPosition();
console.log('喇叭信息:', trumpetInfo);

// 3. 启动语音更新
const voiceUpdateParams = {
  number: 1,                    // 语音编号
  size: 0x1000000,             // 文件大小 (16MB，支持最大4GB)
  wavHead: new Uint8Array(44).fill(0) // WAV文件头信息 (44字节)
};

const startResponse = await voiceController.startVoiceUpdate(voiceUpdateParams);
console.log(`启动状态: ${startResponse.statusText}`);

// 4. 传输语音文件数据
const voiceData = new Uint8Array([/* 语音数据 */]);
const sendResponse = await voiceController.sendVoiceFile({
  offset: 0,                    // 数据偏移量
  data: voiceData               // 语音数据
});
console.log(`传输状态: ${sendResponse.statusText}`);

// 5. 停止语音更新
const stopResponse = await voiceController.stopVoiceUpdate({
  number: 1,                    // 语音编号
  size: 0x1000000,             // 文件大小 (16MB)
  fileName: 'voice_sample.wav'  // 语音文件名
});
console.log(`完成状态: ${stopResponse.statusText}`);

// 6. 读取语音信息
const infoResponse = await voiceController.readVoiceInfo({ number: 1 });
console.log(`语音信息: ${infoResponse.fileName}, 大小: ${infoResponse.size}`);

// 7. 启动读取语音文件
const startReadResponse = await voiceController.startReadVoiceFile({ number: 1 });
console.log(`启动读取状态: ${startReadResponse.statusText}`);

// 8. 读取语音文件数据
const readResponse = await voiceController.readVoiceFile({ offset: 0 });
console.log(`读取状态: ${readResponse.statusText}`);

// 9. 停止读取语音文件
const stopReadResponse = await voiceController.stopReadVoiceFile();
console.log(`停止读取状态: ${stopReadResponse.statusText}`);

// 10. 实时播放语音
const playRealTimeResponse = await voiceController.playVoiceRealTime({ 
  ctl: VoiceRealTimeControl.Start, // 启动实时播放
  index: 1, // 喇叭索引
  data: new Uint8Array(60).fill(0) // 语音数据
});
console.log(`实时播放状态: ${playRealTimeResponse.statusText}`);

// 11. 播放语音文件
const playResponse = await voiceController.playVoice({ 
  ctl: VoicePlayControl.Start, // 开始播放
  number: 2 // 语音编号
});
console.log(`播放状态: ${playResponse.statusText}`);

// 12. 设置音量
const volumeResponse = await voiceController.playVoice({ 
  ctl: VoicePlayControl.Volume, // 设置音量
  volume: 80 // 音量
});
console.log(`音量设置状态: ${volumeResponse.statusText}`);

// 13. 语音空间管理
// 删除全部语音
const deleteAllVoiceResponse = await voiceController.voiceSpaceManagement({ 
  ctl: VoiceSpaceControl.DeleteAllVoice
});

// 删除全部按键对应关系
const deleteAllKeyMappingResponse = await voiceController.voiceSpaceManagement({ 
  ctl: VoiceSpaceControl.DeleteAllKeyMapping
});

// 按照键值删除对应关系
const deleteKeyMappingResponse = await voiceController.voiceSpaceManagement({ 
  ctl: VoiceSpaceControl.DeleteKeyMapping,
  key: 0x1B // ESC键的键值
});

// 按照语音编号删除语音
const deleteVoiceResponse = await voiceController.voiceSpaceManagement({ 
  ctl: VoiceSpaceControl.DeleteVoiceByNumber,
  number: 5 // 删除语音编号5
});

// 整理空间碎片
const defragmentResponse = await voiceController.voiceSpaceManagement({ 
  ctl: VoiceSpaceControl.Defragment
});

// 14. 写入键值对应语音
const writeKeyResponse = await voiceController.writeKeyVoice({ 
  keyCode: 0x1B, // ESC键 (2字节)
  volume: 80, // 音量 (0-100)
  voiceNumber: 3 // 语音编号
});
console.log(`写入键值语音状态: ${writeKeyResponse.statusText}`);

// 15. 读取键值对应语音
const readKeyResponse = await voiceController.readKeyVoice({ 
  keyCode: 0x1B // ESC键 (2字节)
});
console.log(`键值${readKeyResponse.keyCode}对应语音编号: ${readKeyResponse.voiceNumber}`);

// 16. 根据位置读取键值
const readPosResponse = await voiceController.readKeyVoiceByPosition({ 
  index: 0 // 索引值 (0 - key_count之间)
});
console.log(`索引${readPosResponse.index}对应键值${readPosResponse.keyCode}, 语音编号${readPosResponse.voiceNumber}`);

// 17. 一键更新语音文件（高级功能）
const updateResult = await voiceController.updateVoiceFile({
  number: 1,
  fileName: 'new_voice.wav',
  wav: wavFileData,
  chunkSize: 58
}, (progress) => {
  console.log(`更新进度: ${progress.percentage}% - ${progress.updateStatus}`);
});

// 18. 一键读取语音文件（高级功能）
const readResult = await voiceController.readVoiceFileAll({
  number: 1,
  chunkSize: 56
}, (progress) => {
  console.log(`读取进度: ${progress.percentage}% - ${progress.readStatus}`);
});

// 19. 实时播放流程（高级功能）
const playFlowResult = await voiceController.playVoiceRealTimeFlow({
  index: 1,
  data: realtimeVoiceData,
  chunkSize: 60
}, (progress) => {
  console.log(`播放进度: ${progress.percentage}% - ${progress.rtStatus}`);
});

// 处理响应
if (startResponse.status === VoiceUpdateStatus.Success) {
  console.log('语音更新启动成功');
} else if (startResponse.status === VoiceUpdateStatus.InsufficientSpace) {
  console.log('空间不足，需要先进行空间整理');
} else {
  console.log(`语音更新启动失败: ${startResponse.statusText}`);
}
```

#### 注意事项

1. 更新的语音使用WAV格式存储
2. WAV文件头信息占用44字节，包含采样率、声道数、位深度等音频参数
3. 启动语音更新后，需要使用 `sendVoiceFile` 命令传输实际的语音数据
4. 传输完成后，使用 `stopVoiceUpdate` 命令完成更新
5. 如果返回空间不足错误，需要先进行空间整理
6. 文件名使用UTF-8编码，最大36字节，不足部分用0填充
7. 语音读取操作需要先启动读取流程，然后分块读取数据
8. 支持多种状态码，包括缓冲区管理、实时播放等状态
9. 所有状态码都提供中文描述，便于调试和用户理解
10. 实时播放语音支持流式传输，包含60字节数据包，音量由上位机数字调整
11. 播放语音文件支持三种控制类型：停止播放、开始播放、设置音量
12. 语音空间管理支持五种操作：删除全部语音、删除全部按键对应关系、按键值删除对应关系、按语音编号删除语音、整理空间碎片
13. 键值语音映射支持通过键码或行列位置进行设置和查询
14. 所有多字节数值（如键码、空间大小）都使用小端序字节序
15. 实时播放时，快空快满时设备会主动上报ack状态
16. 高级功能方法提供了完整的流程封装，简化了复杂操作的使用
17. 进度回调支持实时监控操作状态和进度
18. 分片大小设置需要根据协议要求进行调整，确保数据包格式正确

## 介绍

### 目录结构

- index.js 入口文件
- byte 字节相关文件 例如 Head：0x5c
- message 消息系统,统一处理消息的收发
- pack 协议参数计算相关
- tools 数据处理公共方法