You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编译MIDI文件:求十进制数转Variable Length Quantity的算法

可变长度量(VLQ)的理解与实现

首先明确VLQ的核心逻辑:它是一种用多字节存储整数的紧凑格式,每个字节只用低7位存实际数据,最高位作为续位标记——1表示后面还有字节,0表示这是最后一个字节。这样小数字(0-127)用1字节,大数字自动扩展成多字节,节省MIDI文件的存储空间。

举个例子:十进制128的VLQ表示

十进制128的二进制是10000000,一共8位。按VLQ规则拆分:

  1. 把二进制补前导0,让总长度是7的倍数:00000010000000(补了6个前导0)。
  2. 分成两个7位的块:0000001(高位块)和0000000(低位块)。
  3. 给高位块的最前面加1(标记有后续字节),得到10000001(即1_0000001);给低位块加0(标记无后续),得到00000000(即0_0000000)。
  4. 最终的VLQ字节序列就是这两个字节,和你给出的例子一致。

编码算法(十进制转VLQ)

直接给可落地的步骤,用代码辅助理解:

  1. 特殊情况:如果数值是0,直接返回0x00。
  2. 把数值转成二进制字符串,去掉前缀(比如bin(128)得到0b10000000,取后面的10000000)。
  3. 补前导0,让字符串长度是7的倍数(确保能分成完整的7位块)。
  4. 按每7位分割成块,顺序是从高位到低位。
  5. 每个块前面加续位标记:除了最后一个块加1,最后一个块加0。
  6. 把每个8位的二进制串转成十六进制字节,就是最终的VLQ序列。

Python编码实现

def decimal_to_vlq(n):
    if n == 0:
        return bytes([0x00])
    # 转成二进制字符串,去掉0b前缀
    bin_str = bin(n)[2:]
    # 补前导0,让长度是7的倍数
    padding = (7 - len(bin_str) % 7) % 7
    bin_str = '0' * padding + bin_str
    # 分割成7位的块
    blocks = [bin_str[i:i+7] for i in range(0, len(bin_str), 7)]
    vlq_bytes = []
    for idx, block in enumerate(blocks):
        # 非最后一个块加1,最后一个加0
        prefix = '1' if idx != len(blocks)-1 else '0'
        vlq_byte = int(prefix + block, 2)
        vlq_bytes.append(vlq_byte)
    return bytes(vlq_bytes)

测试decimal_to_vlq(128)会返回b'\x81\x00',对应你给出的二进制表示。

解码算法(VLQ转十进制)

如果需要解析MIDI里的VLQ时长,步骤更简单:

  1. 初始化结果为0。
  2. 逐个处理每个字节:
    • 取字节的低7位数据,拼接到结果中(结果先左移7位,再或上低7位数据)。
    • 如果字节的最高位是0,说明这是最后一个字节,停止处理。
  3. 最终结果就是对应的十进制数。

Python解码实现

def vlq_to_decimal(vlq_bytes):
    result = 0
    for byte in vlq_bytes:
        # 左移7位腾出空间,再加上当前字节的低7位
        result = (result << 7) | (byte & 0x7F)
        # 最高位为0,结束
        if not (byte & 0x80):
            break
    return result

测试vlq_to_decimal(b'\x81\x00')会返回128,验证正确。

内容的提问来源于stack exchange,提问作者FireTheLost

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:55:24