有没有懂midi文件的朋友,求解midi文件中的音符数量是怎么计算的

最近在写一款将midi文件转为某音游谱面的工具,但是发现个问题,我根据MIDI事件9x按下音符判断音符数量,得出的结果比实际的音符数量差很多,我想知道midi文件是否还有其他方式来判断音符数量呢?

3赞

来,这个事情正对老夫的口味。让老夫给你引用一下n久以前写过的一些内容,你看看是不是这个缘故——

这里要注意MIDI文件的“状态字省略”特点。为了减少MIDI文件的体积,人们规定:如果同一Track Chunk中的下一条MIDI事件,和上一条事件,属于同一类型同一通道的事件(即状态字相同)时,下一条事件的状态字可以省略,而只需记录数据。音序器碰到这种情况时,应自动填充上一事件的状态字。

举个例子,MIDI文件中的事件,大多数都是“Note-On”和“Note-Off”事件,其中Note-Off事件也可以用Note-On + 力度为0 来表示。所以在MIDI文件中,这种缩略形式会用到很多。比如连续演奏几个音符时,MIDI文件就会使用这种缩略法来减少文件体积。比如在MIDI文件中,常常会看到这种序列(十六进制):

00 93 3C 6B // 音符0x3C的Note-On,力度为0x6B
70 3C 00 // 实际发送的指令为隔0x70 Ticks之后,发送93 3C 00。

因为力度为0,也就相当于让某个音符停止发音。83 3C 6B和93 3C 00的效果是一样的。所以通过这种写法,MIDI文件可以省略掉一个字节的空间。

8赞

NoteOn+力度0,不仅是MIDI文件里可以这么用,在音源接收到93 3C 00(力度0)这样的事件后,也是做音符关闭处理的。但音序器不能向音源发送不带状态字的MIDI事件(即不能只发送3C 00),所以这种状态字省略,只能用在MIDI文件中,不能发送给音源。音序器虽然读出了状态字省略,但要根据上一次发出的状态字0x93,补齐之后再发给音源。

4赞

受教了,就是说,一些没有8x Noteoff的音符实际上是在音序器中自动补充了吧

1赞

大佬我想再请教一个问题,我发现有些midi文件中的音符与实际输出的音符相差很大

比如下面这一串:
91 82 77 90 29 2e ed 00 39 45 82 23 29 00 9b 26 39 00 81 85 36 39 58 b6


除了开头音符能对的上后面的好多音符都是对不上的,这种是什么原因造成的呢?

2赞

这一串数,如果把第一个字节0x91当成是DeltaTime的话,那就应该有如下断句方式:

1、91 82 77 90 29 2e // DT=91 82 77,ENV=90 29 2e,即过了0x40177个Tick后,1通道演奏音符0x29,力度0x2e
2、ed 00 39 45       // DT=ed 00,   ENV=90 39 45,即过了0x6D个Tick后,   1通道演奏音符0x39,力度0x45
3、82 23 29 00       // DT=82 23,   ENV=90 29 00,即过了0xA3个Tick后,   1通道关闭0x29音符
4、9b 26 39 00       // DT=9b 26,   ENV=90 39 00,即过了0x6E6个Tick后,  1通道关闭0x39音符
5、81 85 36 39 58    // DT=81 85 36,ENV=90 39 58,即过了0x2176个Tick后, 1通道演奏0x39音符,力度0x58
6、b6……

所以这一串数只描述了完整的两个音符和不完整的一个音符的情况:

一个是从事件串1和3来看,音符0x29持续了0x6D+0xA3个Ticks。

另一个是从事件串2和4来看,音符0x39持续了0xA3+0x6E6个Ticks。

不完整的音符是串5,只是演奏了一个音符,但没有关。

手绘了一张图供参考:

2赞

非常感谢大佬的回答,根据您的解释再去看了看文件确实清晰明了多了

俺把之前写的MIDI文件解析的帖子,现在也转到咱坛子里了,可以拿去看一下~

另外俺还发了一个很老但很实用的小软件,MIDI-OX,可以查看系统底层MIDI数据流。如果你是做MIDI系统研发和调试的话,可能会用得上。不知道Win10能不能用,但XP和Win7 64位系统是能用的~

3赞

主要是脑子里要时刻留意这个字节或者是下一字节的数据是不是最高位为1,也就是是不是比0x80大。

比如在读DeltaTime时,就得看当前字节最高位是否为1,是1就表明下一字节还是DeltaTime,是0就表示DeltaTime结束;DeltaTime结束后的下一字节就是具体的事件了。

在读MIDI事件时,也得看当前字节最高位是否为1。是0的话,说明包括目前这个以及后续若干字节都会是数据,而状态字被缩写了,得延续前一个状态字;是1的话,表明这是个状态字,那么除了这个之外的后面若干字节都是数据。

在读某个MIDI事件的数据时,要知道这个MIDI事件包含的数据字节是多长。有1字节的,如换音色的Program Change;有2字节的,如音符NoteOn/Off、弯音轮、控制器等;有不定长的,如Meta-Event或者SysEx系统码,不定长的事件会在事件当中写明了长度,相当于还是有确定的长度。

以上的过程是能画出代码流程图的,MIDI文件的读取也就是这么读的。

另外,能看出来,MIDI文件更像是一种把各个MIDI事件打包压缩出来的一种东西。MIDI文件的时码是一种相对的时差,不是绝对的时刻;MIDI事件的长度也都各自不同,从1个字节到几十k字节都有可能。这就很讨厌了,在读取MIDI文件并解码的时候,要在内存里创建出所有MIDI事件的完整数据结构,包括时间码,状态字,通道号,以及不定长的数据。还得把所有事件串成链表。对于MIDI 1格式的文件还得考虑各个Chunk(或者说音轨)之间在一个播放时间下的同步问题,烦得很。

所以各个音序器或者DAW软硬件(是的,还有硬件)基本都有专有的工程文件格式,不一定是MIDI。比如各个软件有Cubase的cpr,Cakewalk的cwp,FL也有它自己的工程格式;硬件有老雅马哈合成器QS300的ESEQ,雅马哈双排键电子琴EL-900M的EVT,或者老KORG合成器Trinity的SNG(好象是这个扩展名吧,时间长了,忘了)。这些软硬音序器也都有自己独门的对MIDI文件的解读和导出方式。

俺之前有帖子:


这里提到了Cakewalk对MIDI文件的处理有点儿自作主张。想说的是啥呢?就是:
你在如今这些大型的,功能完整的DAW软件里,比如什么Cubase啊,Logic Pro啊,Sonar啊,FL Studio啊,等等软件中看到的音符或者其他什么什么东西的参数和状态,保存成专用的工程文件倒没什么。但导出成MIDI文件之后,MIDI文件里的内容可能会和你在编辑时有所不同了。反而有些小型的纯粹的MIDI音序器软件(比如俺用的那个“世界树”),能告诉你最真实的MIDI文件内容(但它因为太简单了,有些复杂的MIDI它也搞不定,比如俺碰到好多次对SMPTE时码的兼容性不佳)。俺那个帖子也看到了俺被Sonar坑的故事,所以这里稍微提醒一下楼主。咱搞开发的,软件也好硬件也好,要有“怀疑一切”的精神,心里得留个心眼儿,看到的未必是真的。所以在楼主后续的开发中如果还会碰到什么稀奇古怪的情况时,不要慌张,要知道一切皆有可能~

不过绝大多数情况下,用那些大型DAW软件编曲作曲写MIDI还都是真香的~就算存成MIDI文件,里边有些东西被改变了,通常也不会影响其播放或者再制作。不必过分恐慌~

2赞

:joy:确实,我之前对于动态字节的理解不太彻底(应该说对整个midi文件的理解也不太彻底),虽然知道动态字节该怎么算,但在实际应用中就显得捉襟见肘,比如判断音符数量,因为一直用的fl,用fl新建轨道保存的midi基本每个音符都是有 NoteOn 和 NoteOff 的,所以最开始写出来的工具就只判断0x90有多少(老实说现在觉得这样判断是真的蠢 :joy:),测的东西也比较简单,所以当时就没发现问题,但昨天从网上下载了些midi就发现这些个文件几乎就没有几个 NoteOn 和 NoteOff 事件,于是用我那辣鸡工具就读不出来了。。。

当时我猜想的是,这些文件的 NoteOn 事件里可能包含了多个音符,于是就拿着音符表慢慢看,看第一个对了,但是后面全是连续的音符,再加上对动态字节的理解不彻底(我单纯的认为这种连续的音符的 Delta Time 是每 2 个字节就是一个 DeltaTime,原谅我太蠢了),所以后面读出来的东西全都对不上号,不过通过您的解答后,自己又回去看了看,我总算是意识到的了自己的问题,通过判断 Delta Time 当前字节是否小于 0x80,如果不成立就说明delta time 还没结束,反之成立则结束

很高兴有您这样的大佬回答,刚刚又去看了看您的干货,感觉收获不少

ps:非常感谢大佬的补充 :+1:

1赞

嘘,小声点儿~你造吗?如果被站长河总发现这里有一只正在写而且很有兴趣写MIDI相关软件的程序猿的话,那你会被站长大人抓去丢进地下室,大皮鞭子挨着抽,强迫着敲代码,只给馊面包不给红票票,榨干最后一颗脑细胞的~噫hihihi,噫hihihi~【:rofl:x1048576~and系统表情包里居然木有滑稽,少了很多趣味哇…】

1赞

总写代码的河一直在仰望大佬谈技术,无奈还是太菜插不上嘴。话说网站里解析mid文件的代码也有不少bug,包括判断时长的和这个音符数量的逻辑什么的都还没仔细调教,到时候要参考着你们的帖子改。

最近熬夜写业务逻辑写的心脏疼,King 博士半夜还在探讨技术也要注意休息,我吐一口血继续干活了,你们继续聊技术 :sweat_smile:

1赞

年前刚给医院贡献了十来张红票票…河总也要多多注意身体哇,虽然俺承认咱这种攻城狮程序猿没几个不肝的…至于MIDI技术,真的,说实话,一点儿也不难。一个快四十年没怎么变过的技术,没什么数学模型和数据结构,能复杂到哪儿去。国内资料可能不多,但蔷外啥资料都有,只要肯肝肯看,没有搞不定的。哪位童鞋要是觉得MIDI难,就去啃MP3编码原理吧,把数字滤波器和离散余弦变换的原理和算法啃明白了然后自己写出来,这才是大神,巨佬~

1赞

midi技术发展是越来越快了,多年不上MIDI网,发现新东西也很多

好复杂,入门迷惑

有一说一确实 入门选手迷惑中

这是程序猿专用的内容,只是做编曲、做MIDI的话,看不懂也是没关系的~

这是程序猿专用的内容,只是做编曲、做MIDI的话,看不懂也是没关系的~

大佬教导的真好啊

midi比我见过的任何一种采样记录模式的音频格式都复杂得多,midi这种事件记录模式的音频格式算是很稀奇了,而且里面有很多电子影音业的专业内容,而且事件记录模式的音频格式能做到文件大小压缩到极致,这如果被应用到各种行业上,会有很大的用途。我是很看好MIDI的,但如果让MIDI沦陷为专业到不砸几百张红票票连个midi都放不出来那种水平就不好了,Midi应该是平易近人的,像7z flac这种简单又强大但不臃肿的格式