4D 54 68 64
00 00 00 06
00 01 00 02 00 78
4D 54 72 6B
00 00 00 8E
00 FF 58 04 04 02 18 08 00 FF
51 03 09 27 C0 00 FF 03 08 75 6E 74 69 74 6C 65
64 00 FF 02 1A 43 6F 70 79 72 69 67 68 74 20 3F
20 32 30 31 34 20 62 79 20 C1 F5 D3 EE BF B5 00
FF 01 06 C1 F5 D3 EE BF B5 00 FF 59 02 00 00 00
BF 07 FF 00 BE 07 FF 00 BD 07 FF 00 BC 07 FF 00
BB 07 FF 00 BA 07 FF 00 B9 07 FF 00 B8 07 FF 00
B7 07 FF 00 B6 07 FF 00 B5 07 FF 00 B4 07 FF 00
B3 07 FF 00 B2 07 FF 00 B1 07 FF 00 B0 07 FF AD
14 FF 2F 00 4D 54 72 6B 00 00 01 6A 00 FF 21 01
00 00 FF 03 04 74 65 73 74 00 C0 01 00 90 3C 64
78 80 3C 30 00 90 3C 64 78 80 3C 30 00 90 43 64
78 80 43 30 00 90 43 64 78 80 43 30 00 90 45 64
78 80 45 30 00 90 45 64 78 80 45 30 00 90 43 64
81 70 80 43 30 00 90 41 64 78 80 41 30 00 90 41
64 78 80 41 30 00 90 40 64 78 80 40 30 00 90 40
64 78 80 40 30 00 90 3E 64 78 80 3E 30 00 90 3E
64 78 80 3E 30 00 90 3C 64 81 70 80 3C 30 00 90
43 64 78 80 43 30 00 90 43 64 78 80 43 30 00 90
41 64 78 80 41 30 00 90 41 64 78 80 41 30 00 90
40 64 78 80 40 30 00 90 40 64 78 80 40 30 00 90
3E 64 81 70 80 3E 30 00 90 43 64 78 80 43 30 00
90 43 64 78 80 43 30 00 90 41 64 78 80 41 30 00
90 41 64 78 80 41 30 00 90 40 64 78 80 40 30 00
90 40 64 78 80 40 30 00 90 3E 64 81 70 80 3E 30
00 90 3C 64 78 80 3C 30 00 90 3C 64 78 80 3C 30
00 90 43 64 78 80 43 30 00 90 43 64 78 80 43 30
00 90 45 64 78 80 45 30 00 90 45 64 78 80 45 30
00 90 43 64 81 70 80 43 30 00 90 41 64 78 80 41
30 00 90 41 64 78 80 41 30 00 90 40 64 78 80 40
30 00 90 40 64 78 80 40 30 00 90 3E 64 78 80 3E
30 00 90 3E 64 78 80 3E 30 00 90 3C 64 81 70 80
3C 30 14 FF 2F 00
读取文件头
4D 54 68 64 //MThd
str.substr(position, length=4)
//读取前四个字节(正好对应ascii码),结果为MThd
读取长度
00 00 00 06 //6
(str.charCodeAt(position) << 24)
+ (str.charCodeAt(position + 1) << 16)
+ (str.charCodeAt(position + 2) << 8)
+ str.charCodeAt(position + 3)
str.charCodeAt(position)
//读取指定位置的unicode编码
读取header 数据区
根据上一步读取的长度length=6,读取header的数据部分
00 01 00 02 00 78
00 01
多音轨
00 02
2个轨道(一个主轨道,一个发声轨道)
00 78
tick数(每个四份音符包含120个tick)
读取header 数据区内部
formatType 00 01
多音轨
trackCount 00 02
2个轨道(一个主轨道,一个发声轨道)
timeDivision 00 78
tick数
readInt16()//每次读取两个字节,分三次读完
(str.charCodeAt(position) << 8)
+ str.charCodeAt(position + 1)
track
event
deltaTime
动态字节
一个字节有8位,最高位做标志位,还有7位0~2^7-1
(127),
1)如果表示的数在这个范围内,则标志位(第8位,最高位)为0,比如01111111
为127.
2)如果表示的数字超过这个范围,先记录前7位为一个字节,超过7位的数值交给前面的字节,前字节的标志位必须为1(表明该字节是进位的),如果前字节还是超过了127,则继续(交给前前字节),比如11111111
一共8位,先记录前7位为一个字节0111111
,第8位的1
放在前一个字节里,既10000001
,所以就表示为10000001 01111111
https://www.jianshu.com/p/31d02765e1ec
function readVarInt() {
var result = 0;
while (true) {
var b = readInt8();//先读一个字节
if (b & 0x80) {//比较最高位是否为1
result += (b & 0x7f);//0x7f 二进制01111111
result <<= 7;
} else {
return result + b;
}
}
}
event
meta event :0xff
meta event的类型是一个字节,它的跟随的数据的长度是可变的varint
下一级分类 | 代码 | 描述 |
---|---|---|
sequenceNumber(轨道音序) | 0x00 | |
text(文本) | 0x01 | |
copyrightNotice(版权) | 0x02 | |
trackName(轨道名称) | 0x03 | |
instrumentName(乐器名称) | 0x04 | |
lyrics(歌词) | 0x05 | |
marker(标记) | 0x06 | |
cuePoint | (开始点) | 0x07 |
midiChannelPrefix() | 0x20 | |
音轨开始标识 | 0x21 | |
endOfTrack(音轨结束标志) | 0x2f | |
setTempo(速度) | 0x51 | 四分音符的微妙数,3个字节 |
smpteOffset | 0x54 | |
timeSignature | 0x58 | 4字节(numerator;分子,denominator:分母,metronome:节拍时钟,thirtyseconds:每个四分音符有多少个三十二分音符) |
keySignature(调号) | 0x59 | 2字节(key:大小调,scale:升降) |
sequencerSpecific(音序特定信息) | 0x7f |
system event :0xf0 0xf7
分类 | 代码 | 描述 |
---|---|---|
sysEx | 0xf0 | |
dividedSysEx | 0xf7 |
channel event
除了meta event 和 system event 剩余的都是channel event
Channel是利用状态字节的低 4 位来表示
分类 | 代码 | 描述 |
---|---|---|
noteOff(松开音符) | 0x80~0x8F | |
noteOn(按下音符) | 0x90~0x9F | 需要先判断velocity,等于0是off,不等于0是on |
noteAftertouch(触后音符) | 0xA0~0xAF | |
controller(控制器) | 0xB0~0xBF | |
programChange(改变) | 0xC0~0xCF | |
channelAftertouch(触后通道) | 0xD0~0xDF | |
pitchBend(滑音) | 0xE0~0xEF |