Files
Musicreater/docs/MSQ文件格式.md

110 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MSQ 文件格式
MSQ 文件是 · 库存储音符序列的一种格式取自 **M**usic**S**e**Q**uence 类之名
现在 · 库及其上游软件使用的是在 第二版 的基础上增设校验功能的 MSQ 第三版
## 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 是启用反之同理 |
| **总音调偏移** | music_deviation | 15 | 支持数值 -16383~16383这里也是表示三位小数的和最小音量一样这里 15 位中的第一位从左往右是正负标记若为 1 则为负数反之为正数后面的 14 位是数值 |
| **音乐名称** | music_name | 依据先前定义 | 最多可支持 31 个中文字符 63 个西文字符其长度取决于先前获知的 音乐名称长度 的定义 |
在这一元信息中**音乐名称长度****最小音量**合计共 2 字节**高精度音符时间控制启用****总音调偏移**合计共 2 字节因此**音乐名称**为任意长度前四字节内容均为固定
### 音符序列
每个序列前 4 字节为一个用以表示当前通道中音符数量的值也就是**通道音符数**notes_count也即是说一个通道内的音符可以是 0~4294967295
在这之后就是这些数量的音符了其中每个音符的信息存储方式如下
| 信息名称 | 西文代号 | 位长 | 支持说明 |
| ---------------------------- | ------------------------ | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **乐器名称长度** | name_length | 6 | 支持数值 0~63 |
| **Midi 音高** | note_pitch | 7 | 支持数值 0~127 |
| **开始时刻** | start_tick | 17 | 单位 二十分之一秒即约为 1 命令刻支持数值 0~131071 109.22583 分钟 1.8204305 小时 |
| **音符持续刻数** | duration | 17 | 同上 |
| **是否作为打击乐器** | percussive | 1 | 1 是启用反之同理 |
| **响度力度** | velocity | 7 | 支持数值 0~127 |
| **是否启用声像位移** | 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 个单位即取值是此处表示数字的千分倍 |
### 序列校验
_第三版新增_
在每个音符序列结尾包含一个 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(
XOR(
XXH64(meta_info, note_count),
XOR(
XXH64(seq_1_note_count, 3),
XXH64(note_seq_1, seq_1_note_count)
),
),
XOR(
XXH64(seq_2_note_count, 3),
XXH64(note_seq_2, seq_2_note_count),
)
),
note_count
)
```