Java版!MSQ流式解析初适配!

This commit is contained in:
2025-04-02 02:52:41 +08:00
parent 2df8d6a270
commit 889f8f9641
36 changed files with 786 additions and 61 deletions

View File

@ -2,18 +2,25 @@
MSQ 文件是 · 存储音符序列的一种格式取自 MusicSeQuence
## MSQ 第二版
现在 · 及其上游软件使用的是在 第二版 的基础上增设验证功能的 MSQ 第三版
第二版的码头是 MSQ@ 这一版中所有的**字符串** _**GB18030**_ 编码进行编解码**数值****_大端序_**存储
## MSQ 第三版
码头是文件前四个字节的内容这一部分内容是可读的 ASCII 字串因此第二版的文件前四个字节的内容必为 MSQ@
第二版的码头是 `MSQ@` 这一版中所有的**字符串** _**GB18030**_ 编码进行编解码**数值****_大端序_**存储
MSQ 第三版的码头是 `MSQ!`
码头是文件前四个字节的内容这一部分内容是可读的 ASCII 字串因此第三版的文件前四个字节的内容必为 `MSQ!`
MSQ@ 是因为美式键盘上 @ Shift+2 按下取得的故代表 MSQ 第二版
你猜为什么第三版是 `MSQ!`
### 元信息
| 信息名称 | 西文代号 | 位长多少个 0 1 | 支持说明 |
| ------------------------------ | -------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **码头** | _无_ | 32 | 值为 `MSQ!` |
| **音乐名称长度** | music_name_length | 6 | 支持数值 0~63 |
| **最小音量** | minimum_volume | 10 | 支持数值 0~1023注意这里每个 1 代表最小音量的 0.001 个单位即取值是此处表示数字的千分倍 |
| **是否启用高精度音符时间控制** | enable_high_precision_time | 1 | 1 是启用反之同理 |
@ -39,4 +46,58 @@ MSQ 文件是 音·创 存储音符序列的一种格式,取自 MusicSeQuence
| **是否启用声像位移** | is_displacement_included | 1 | 1 是启用反之同理 |
| **时间精度提升值**非必含 | high_time_precision | 8 | 支持数值 0~255若在 元信息 中启用**高精度音符时间控制**则此值启用代表音符时间控制精度偏移此值每增加 1则音符开始时刻向后增加 1/1250 |
| **乐器名称** | sound_name | 依据先前定义 | 最多可支持 31 个中文字符 63 个西文字符其长度取决于先前获知的 乐器名称长度 的定义 |
| **声像位移**非必含 | position_displacement | 共三个值每个值 16 48 | 若前述**是否启用声像位移**已启用则此值启用三个值分别代表 xyz 轴上的便宜每个值支持数值 0~65535注意这里每个 1 代表最小音量的 0.001 个单位即取值是此处表示数字的千分倍 |
| **声像位移**非必含 | position_displacement | 共三个值每个值 16 48 | 若前述**是否启用声像位移**已启用则此值启用三个值分别代表 xyz 轴上的偏移每个值支持数值 0~65535注意这里每个 1 代表最小音量的 0.001 个单位即取值是此处表示数字的千分倍 |
### 序列验证
_第三版新增_
在每个音符序列结尾包含一个 128 位的校验值用以标识该序列结束的同时验证该序列的完整性
在这 128 位里 64 位是该通道音符数的 XXHASH64 校验值 3 作为种子值
64 位是整个通道全部字节串的 XXHASH64 校验值包括通道开头的音符数 该通道音符数 作为种子值
### 文件验证
_第三版新增_
在所有有效数据之后包含一个 128 位的校验值用以标识整个文件结束的同时验证整个文件的完整性
128 位的校验值是 包括码头在内的元信息的 XXHASH64 校验值种子值是全曲音符数 对于前述所有校验值彼此异或的异或 所得值之 XXHASH128 校验值 全曲音符总数 作为种子值
请注意是前述每个 XXHASH64 校验值的异或每次取 XXHASH64 都计一遍也就并非是每个序列结尾那个已经合并了的 128 位校验值再彼此异或对于这个异或值再取其种子是 全曲音符数 XXHASH128 校验字节码
听起来很复杂我来举个例子以下是该算法的伪代码我们设
- `meta_info` : `bytes` 元信息字节串
- `note_seq_1` : `bytes` 第一个音符序列的编码字节串
- `note_seq_2` : `bytes` 第二个音符序列的编码字节串
- `XXH64(bytes, seed)` : `bytes` XXHASH64 校验函数
- `XXH128(bytes, seed)` : `bytes` XXHASH128 校验函数
- `XOR(bytesLike, bytesLike)` : `bytes` 异或 函数
- `note_count` : `int` 全曲音符数
- `seq_1_note_count` : `int` 第一个音符序列的音符数
- `seq_2_note_count` : `int` 第二个音符序列的音符数
为了简化我们假设只有两个序列实际上每个通道都是一个序列最多 16 个序列
那么一个完整的 MSQ 文件应当如下排列其字节串
```assembly
ADD meta_info
ADD note_seq_1
ADD XXH64(seq_1_note_count, 3)
ADD XXH64(note_seq_1, seq_1_note_count)
ADD note_seq_2
ADD XXH64(seq_2_note_count, 3)
ADD XXH64(note_seq_2, seq_2_note_count)
ADD XXH128(
XOR(
XXH64(meta_info, note_count),
XOR(
XXH64(seq_1_note_count, 3),
XXH64(note_seq_1, seq_1_note_count)
),
),
note_count
)
```