对于变长表示法的解释,如下【来源自百度百科】
MTrk块类型是存放实际歌曲数据的地方。它是MIDI事件(和非MIDI事件)的序列。在MTrk块的有些数字是以叫可变长的数量的形式进行存储的。 这些数字首先每个字节用7位,最高位不是有效位。 除最后一位之外的所有字节,最高位设为1;最后一个字节最高位设为0。 如果数字在0和127之间,它能正确地表示为一个字节。 这作为可变长的数量代表的数字的有些例子:
数字(十六进制) 变长表示法(十六进制)
00000000 00
00000040 40
0000007F 7F
00000080 81 00
00002000 C0 00
00003FFF FF 7F
00004000 81 80 00 00
100000C0 80 00
001FFFFF FF FF 7F
00200000 81 80 80 00
08000000 C0 80 80 00
0FFFFFFF FF FF FF 7F
允许的大数是0FFFFFFF,这是以可变长表示法表示的32位的最大数字。 理论上,大数是有可能的,但是实际中不必要。
但是对于以上的解释,相信很多朋友遇到过困惑,怎样写code去实现它?
以下为笔者整理的一些code供大家参考。
先简单解释一下算法:
一个字节有8位,如果仅使用7位,它可以表示0~127这128个数,而剩下的一位,则用来作为标志。如果要表示的数在以上范围,则这个标志为0,这时,一个7位的字节可以表示0~127tick。如果要表示的数超出了这个范围(比如240),则把标志设置成1,然后记录下高7位,剩下的留给下一个字节,在该例中240可以分解成128*1+112,这里的1就是第一个字节要记录的,加上标志位,应该为10000001,即十六进制的81;而112是下一个字节记录的,它的十六进制为70:所以要表示240这个时间,要写成81 70。同理,如果要表示65535tick,则可以先计算出65535=1282*3+1281*127+1280*127,然后得出结果:83 FF 7F。由此,我们反过来也可以知道如何确定时间差:只要标志位为0,则表示结束读取时间差。比如82 C0 03表示1282*2+1281*64+1280*3=40963,如果基本时间为120,则有341:043个四分音符。
以这种方式记录整数的字节称为动态字节,它根据记录的整数改变自身的长度,这在后面还要用到,所以必须熟练计算。
了解了算法,以下就是代码了:
void ConvertVarLen( DWORD dwLen )
{
BYTE btTemp[4];
memset( btTemp, 0, sizeof(btTemp) );
DWORD dwTemp = dwLen;
for( int i = 0; i < 4; i++ )
{
if( i == 0 )
btTemp[i] = dwTemp & 0x7F;
else
btTemp[i] = dwTemp | 0x80;
dwTemp = ( dwTemp >> 7);
if( 0 == dwTemp )
break;
}
TRACE( "%08X ==> %2X %2X %2X %2X \n", dwLen, btTemp[3], btTemp[2], btTemp[1], btTemp[0] );
}
通过以上方法,发现百度上的例子是有笔误的:
100000C0 80 00