MIDI文件格式解析

来源:互联网 发布:软件开发职业培训 编辑:程序博客网 时间:2024/04/30 12:58

乐器数字接口MIDI(Musical Instrument Digital Interface)是数字音乐国际的标准,定义了计算机音乐程序、合成器及其他电子设备交换信息和电子信号的方式,解决不同电子乐器之间不兼容的问题。 MIDI文件中包含音符、定时和多达16个通道的演奏定义。文件包括每个通道的演奏音符信息:键通道号、音长、音量和力度等。由于MIDI文件是一系列指 令,而不是波形,它需要的磁盘空间非常少,此外对MIDI数据的编辑和修改非常灵活,可以方便地增加或删除某个音符,或者改变音符的属性。

  1 格式说明

  MIDI文件中的数据划分为若干个块(chunk),块是由块标记、块长度和块数据组成的。其中块标记为4个字节的ACSII码,块长度为4个字节的数值,表示块数据域的长度。如表1所示。


表1 MIDI文件组成

  2 MIDI文件格式解析模块的结构与解析流程

  由于MIDI文件格式解析模块需要完成的任务主要有理解MIDI信息、分析MIDI信息、生成五线谱信息几大部分,因此可以将MIDI文件解析分作 两大块。第一块称为文件分析器,主要负责按顺序阅读MIDI文件,将各数据成分分离并分别储存,完成底层面向文件的数据读入和错误处理,保证向上层提供完 整的正确的排列有序的音符信息。第二块称为整合分析器,主要负责有选择的使用文件分析器从MIDI文件中获得的原始信息,参照一系列的规则,找出音符之间 的组合关系,生成五线谱的逻辑内容。整体结构与处理流程如图4所示。

  2.1 文件分析器

  头块中表明了MIDI文件类型、包含轨道数和四分音符的时间增量数。头块的解析相对简单,但其中的文件类型和四分音符时间增量数对解析流程影响很大。

  MIDI文件类型不同,MIDI文件内部的复杂度会有较大差别。不过MIDI文件的三种类型之间是复杂度递增的关系,而且前一种类型总可以近似的看作后一种类型的特例,所以只要面向最复杂的类型2做好各种处理,相对简单的类型0和1只要稍作修改即可支持。

  MIDI文件中一般把四分音符的时间增量数设置为120,但MIDI文件也支持更改这个值。由于这种情况在实际应用中比较少见,因此可以通过统一转换为120的方式,在不影响正确性的前提条件下,降低解析的复杂度。

  MIDI事件的读取

  MIDI消息的种类很丰富,而且没有统一的格式,这意味着只能把每一种消息单独处理。值得庆幸的是,不是所有的MIDI消息都跟五线谱有关,所以要 真正理解的MIDI消息实际上不是很多。要面对的最多的消息是音符打开消息(Note-On),它的实际使用量占了所有消息的总使用量的95%以上。音符 关闭消息(Note-Off)也是必须要处理的,如果不处理这种消息,会造成漏音(打开的音符没有对应的关闭信号而一直打开)。

  由于MIDI中没有音符的概念,因此要通过将对应的音符开启和关闭事件配对形成一个音符,称之为原始音符,之后还需要将音符开始时间戳和结束时间戳转换成音符开始时间戳和音符持续长度。

  为了完成上述两个任务,使用一个大数组缓存16个通道里的128个音的状态。在接收到音符打开与关闭消息时进行记录,并同时计算开始时间与持续时间。

  Meta事件的读取

  MIDI中的Meta事件中描述的信息在五线谱显示中基本上都是有用的,有些信息还起着至关重要的作用。例如很多说明性的文字信息,需要直接添加到五线谱和各音轨的属性中。当这些说明性信息重复出现时,可以把两段信息的文字连起来,作为一条长的信息出现。

  调号和拍号信息是Meta信息中非常关键的两条。调号决定了每个音符在五线谱上显示的确切位置及其升降号标志,拍号决定了小节的长度还同时影响合成音符组的规则。这些信息都是整合分析器不可缺少的重要信息。

  2.2 整合分析器

  文件分析器只能够读出MIDI文件中直接说明的音符,如果直接显示这样的音符,得到的五线谱将非常难看。整合分析器的任务就是接收原始音符表与其它如拍号调号等信息进行排列重组,生成一个正确美观的五线谱。由于要分析的信息非常多,本小节仅列举了一些相对重要的工作。

  确定谱号

  Meta信息里只提供了调号和拍号的信息,而同样重要的谱号信息却没有提供。由于在整合分析器中的许多工作都要使用谱号信息,所以对音符的统计工作必须在进入整合分析器之前就进行完毕,即把对音符的统计穿插到文件分析器中。

  确定谱号需要统计音轨中最高和最低音符的音高。得到了这两个值后,便可以决定使用低音谱号和高音谱号中的一种。如果最高音和最低音相差太多,以至于使用无论使用低音谱号还是使用高音谱号都不能满足要求,可以考虑将一个音轨拆成两条五线显示。

  添加休止符

  休止符是五线谱中与音符同样重要的符号元素,而在原始音符表中只有实在的音符而没有休止符。休止符是指在一定的时间范围内音轨上没有任何音处于打开状态,所以只要监视文件分析器中的音状态矩阵就可以判断是非存在休止符。

  小节划分与音符的拆分组合

  五线谱中的小节划分对显示来说具有两个作用,方便上下同步声部乐谱对齐和在换行时保持末端对齐。由于原始音符可能完全属于某一个小节,又或跨越若干 个小节,因此就要把音符拆分成多个小的音符,并用延音符号连接他们,或者把一个小节中的几个音符组合在一起构成和弦或者共尾音符组等情况。图5例举出了一 种比较简单的情况,三个音符被拆分组合成了两个和弦。

  完成这一部分工作需要将原始音符表转换为有序栈。有序栈的特点保证了虽然每个音符都有可能被反复插入,但是它插入的次数与整体栈规模无关,只与音符 的复杂度有关。在音符复杂度一定的情况下,每个栈元素被反复操作的次数接近一个常量,故整体函数仍停留在nlog(n)级别,是可以接受的。

  为了降低运算量,可以利用只含有一个和弦的共尾音符组和只含有一个音符的和弦统一各种符号间的比较,把3x3=9步比较合并为两步,降低了比较的代价,大大减少了代码量。另一个优化之处是利用了有序栈,把时间复杂度降低到nlog(n)。

原创粉丝点击