发明内容
本发明的目的在于提供一种能把一个或者几个音色嵌入到标准MIDI文件中的方法,本发明的另一个目的是提供一种能提取在MIDI文件嵌入音色的方法。
为了实现上述目的,本发明提供的一种在MIDI文件嵌入音色的方法,包括以下步骤:
a.选择合成器所支持的音色库进行编码,使其转换成该合成器支持的***码;
b.把编码后的***码嵌入到MIDI文件中。
所述步骤a的音色库格式包括:音色库中的音色数以及各音色的位置表,该表位置记录有各个音色所对应的主要库号、次要库号、程序号以及音色的描述在音色库中的偏移地址。
所述音色的描述是一种对应合成器支持的音色库中音色的描述;如果通过音色的描述不能确定整个描述的尺寸信息,则在整个描述前补上这个信息,或者位置表中补上该信息。
所述步骤a的使音色库转换成***码的过程依次为:把表示为开头的F0写入到所要编码转换成***码的块A中;依次将公司ID、表示本公司的音色库事件的00写入到***码的块B中;再把音色库数据的长度编码后写入***码的块B;再把音色库数据各字节的低7位,以及由音色库数据各字节的最高位以7个一组组成的一个字节,分别写入***码的块C和块D;把标识***码结束的F7写入***码的块E;把B~E部分的总尺寸按照可变长编码方式编码后写入***码的块A;最后使A到E按顺序合并一起成为要编码转换的***码总体。其中,所述的各字节的最高位以7个一组组成的一个字节,若在最后几个不足7个,则补0,其中,由其组成块D的长度为:(音色库文件长度+7)/8。
所述的步骤b把编码后的***码嵌入到MIDI文件中的过程依次为:取得合法MIDI文件第1轨内容的字节数,存入轨道长;保存***码的字节数+1的值到***码长;调整MIDI文件的长度为:原长度+***码长;把从第一轨开始到整个文件结尾的所有数据向结尾处方向移动***码长个字节;在第一轨数据没移动前的第一个字节上的位置写入0,作为Midi事件的时间差,并紧接在其后位置写入***码A~E的总体;更新第一轨的数据长度为:***码长+轨道长;更新整个MIDI文件的长度为:原长度+***码长。
上述在MIDI文件嵌入音色的提取方法,包括如下步骤:
1)逐轨解析MIDI文件,若文件已经解析完,则结束解析;
2)判断是否是***码,如果是,则进入步骤5);
3)判断是否是音色选择信息,如果是,则进入步骤6);
4)如果是其他标准MIDI信息,则进行一般的相应的MIDI信息的操作;
5)检测是否是音色信息,如果是,则向***中增加该音色;
6)检测是否找到该外部音色库的地址表,如果找到,则在***中选定该通道的音色。
所述步骤5)的向***中增加该音色,是通过对***码的解码还原音色库的所有字节,然后实现把还原的音色库合并到***音色库的过程,其中合并到***音色库的过程包括:取得合成器当前的外部音色库数,并令其加1;把该音色库追加到外部音色库的容器中。其中,所述的对***码的解码过程为,把已编码过的***码的块B的第2~5个字节取出,并存入数据长度变量,再把块B的第6个字节取出,然后逐位填入数据长度变量各字节的最高位,得出的数据就把长度值及所有字节还原出来。
所述步骤6)的在***中选定该通道的音色,是根据外部音色库的地址表,找到符合当前记录的乐器库号主要位、乐器库号次要位以及乐器号的音色,而实现为其装载音色的过程,其中,外部音色库的地址表接根据该外部音色库的音色数来查找获得。
采用本发明所述的方法,可以把标准制作MIDI文件之外的音色能通过采样方便做到合成器中,使音色得到很大的扩展,从而很大程度的实现对各音色进行渲染。此外,本发明设置的方法,即使当MIDI文件在其他不支持这种信息的合成器上回放,也可根据音色库中给各个音色指定乐器库号主要位、乐器库号次要位以及乐器号,实现最大限度不失真的目的。
具体实施方式
本发明具体的实施方式可分成如下几个部分:
一、音色库的格式
二、把音色库转换成***码
三、把***码嵌入MIDI文件
四、解析和渲染这种MIDI文件(含还原音色库并与当前音色库合并,以及选择音色库和音色的具体方法)
现依次说明:
一、音色库的格式
音色库的格式由以下两部分构成:
1、库中含有多少个音色。
2、各个音色的位置表。该表记录了各个音色所对应的主要库号(Bank SelectMSB)、次要库号(Bank Select LSB)、程序号(Program Select)以及音色的描述在音色库中的偏移地址。
其中,音色的描述应该是一种对应合成器的描述。比如对于使用FM算法的合成器,这个描述应该包括合成矩阵ID、各个震荡器的波型ID、ADSR的参数等用来定义音色的各种信息;而对于一个基于波表算法的合成器,该描述应该包括该音色使用的波型数据(可能是压缩过的),以及ADSR的参数,甚至是波型循环点信息等等。
如果通过音色的描述的首地址不能确定整个描述的尺寸信息,则需要在整个描述前补上这个信息,或者在上述2的位置表中补上该信息,这里并没有严格要求。
二、音色库转换成***码
由于***码的要求是从F0开始到F7之间的内容必需是0x00~0x7F之间的值,否则将被视为不标准的***码。而在F0后接上的表示尺寸的内容,是按照MIDI文件的要求而设置的。
所以以下的步骤的根本目的就是把一个二进制数块转成标准的***码的过程,如图1所示,其过程如下:
1、根据***码要求写入字节0xF0作为开头,写入该值到块A
2、根据***码的一般要求写入公司ID到块B。该值需要向MIDI组织
注册申请,若不申请,至少要避开MIDI组织列出的公司ID。
3、写入0x00到块B表示该***码事件是本公司的音色库事件。如果还有其他类型的事件,这里将不会再写成0x00。或者如果该公司不再有打算扩展***码,可以跳过这个值。
4、把音色库文件的长度编码后写入块B。该编码方式跟第5~6步使用的一样,其理由是满足***码的要求。具体如下:
(1)假设能支持最长的音色库文件长度为32位整数,则可以把前4个字节的低7位写入块B。假如音色库的文件长为0x02FFB28C,则其前4个字节为:0x8C,0xB2,0xFF,0x02,保留低7位则变成:0x0C,0x32,0x7F,0x02。
(2)把前4个字节的最高位(后4个由于没有字节,则补0)组合成一个字节,写入块B。比如本例中,则是组成00000111b这样的字节。
5、把音色库的数据的低7位写入块C,其方法如上述的4.(1)。把音色库的数据的最高位按7个一组,组成一个字节,写入块D,最后几个若不足7个,则补0,就像4.(2)中描述的一样;得到的块D长度就为:(音色库文件长度+7)/8。其中,采用最高位按7个一组以及要补0的理由如下:因为按照General MIDI标准对***码的规定,组成Sysx数据部分的每一字节的最高位必需为0,即每个字节仅后7位有效。故把整块数据首先按各个字节的后7位直接送入块C,然后把这个数据的第1位按7个一组构成一个字节(其最高位为0),然后进行合成编码。如果原数据的字节个数不是7的倍数,到最后必定会出现不够7个来群组,所以同时需要采取补0的方法。
6、把表示***码结束的0xF7写入块E。
7、把B~E部分的总尺寸按照可变长编码方式编码后写入A中。所谓可变长编码方式,就是跟MIDI文件的事件间的时间差(DeltaTime)同样的方式。
8、把块A~E合并起来就成了整个编码过的***码总体(SysxData)三、***码嵌入MIDI文件
如图2所示,为音色库嵌入MIDI文件的示意图,其过程如下:
1、根据一般解析MIDI文件的方法判断是否是合法的MIDI文件,然后找到第1轨所在的位置;
2、保存第1轨的数据长度到轨道长(TrkLen);
3、保存***码的长度+1的值到***码长(SysxLen);
4、调整MIDI文件的长度为原长度(Midilen)+***码长(SysxLen);
5、从第一轨开始到整个文件结尾的所有数据向向移动***码长(SysxLen)个字节;
6、在第一轨数据原先的位置上写入0,作为Midi事件的时间差(DeltaTime);
7、在其后写入***码A~E的总体(SysxData)
8、更新第一轨的数据长度为***码(SysxLen)+轨道长(TrkLen)
9、更新整个MIDI文件的长度为原长度(Midilen)+***码长(SysxLen);
四、解析和渲染这种MIDI文件
如图3所示,解析和渲染MIDI文件经以下步骤1~8:
1、按照通常解析MIDI文件的方式逐轨解析MIDI文件;
2、若文件已经解析完,则结束解析;
3、判断是Sysx,则进入步骤7;
4、判断是控制器0、控制器32或者是音色选择中任何一个信息,则进入步骤8;
5、若是其他MIDI信息,则进行一般的操作;
6、回到步骤1;
7、经过了一般是***码的判断后,剩下的字节就是上述B~D之间的内容,如下过程:
(1)、根据公司ID判断是否是本公司的ID,若是,则进入(4)。
(2)、如果不是公司的ID,再检查是否是GM标准的保留Sysx事件,比如GM Reset、Master Volume等等。若是,则根据该合成器是否支持这些事件而可选地去执行。
(3)、返回1(上级解析和渲染MIDI文件的步骤1);
(4)、检查是否是音色库信息,若是,则进入(6);否则看是否是该合成器支持的其他信息,若是,则进入相关的处理。
(5)、返回1(上级解析和渲染MIDI文件的步骤1)。
(6)、取得C块的尺寸信息。该信息是就是对尺寸的解码过程:
(a)把B的第2~5个字节取出,并存入数据长度变量(DataLen)。对于本例,此时的数据长度变量(DataLen)就是0x027F320C。
(b)把B的第6个字节取出,然后逐位填入数据长度变量(DataLen)各字节的最高位.本例中就是把00000111b取出,再填完后的数据长度变量(DataLen)就是0x02FFB28C,这样就把长度值还原回来了。
(7)、根据长度值,可读出音色库的所有字节的低7位。
(8)、根据前面的方法换算出D的长度并读取。再使用上述(6).b的方法还原音色库的所有字节。
(9)、通过对音色库的还原,现在可以进行对还原后的音色库做合并到***音色库,其步骤:
a)得合成器当前的外部音色库数,并令其加1;
b)把该音色库追加到外部音色库的容器后(比如是音色库数组、或者是音色库链表)。
(10)、返回1(上级解析和渲染MIDI文件的步骤1)。
8、这里将分别对跟选择音色有关的几个MIDI事件做处理:
(1)、如果是控制器0或者32信息(Controller),就直接分别记录其值到乐器库号主要位(BnkMsb)和乐器库号次要位(BnkLsb)。然后返回1(上级解析和渲染MIDI文件的步骤1);
(2)、如果不是音色改变信息(Program Change),则返回1(上级解析和渲染MIDI文件的步骤1);
(3)令ExLibNum等于合成器的“外部音色库数”;
(4)、如果ExLibNum为0,则跳转到(9);
(5)、并取得第ExLibNum-1个外部音色库;
(6)、令ExPatchNum等于这个外部音色库的音色数;
(7)、根据ExPatchNum查找该外部音色库的地址表,看能否找到符合当前记录的乐器库号主要位(BnkMsb)、乐器库号次要位(BnkLsb)以及乐器号(Program)的音色,若找到,则根据音色描述的地址装载音色,并返回1(上级解析和渲染MIDI文件的步骤1);
(8)、令ExLibNum--,跳转到(4);
(9)、表明在所有外部音色库中无法找到该组合的音色,则根据乐器号(Program)在标准128号中找相应的音色;
(10)、返回1(上级解析和渲染MIDI文件的步骤1)。
最后,关于音色库中的音色的“位置”安排的建议。之所以在音色库中需要给各个音色指定乐器库号主要位(BnkMsb)、乐器库号次要位(BnkLsb)以及乐器号(Program),就是为了当这个MIDI文件在其他不支持这种信息的合成器上回放能最大限度不失真的目的。
举例说明,假设要扩展一种二胡的乐器,而最好把这个二胡的乐器库号主要位(BnkMsb)、乐器库号次要位(BnkLsb)设置成非0,比如一个是70,一个是12,而乐器号(Program)最好设置成中提琴或者小提琴的乐器号(Program)。这样,当这个MIDI曲子在不支持这种扩展音色的合成器上播放时,就算找不到二胡的音色,也会使用中提琴或者小提琴来播放,使其味道不会有太大的偏差。
此外,这里对“音色”的概念可以更广义地理解,比如:把人的说话或者唱歌的声音作为波表,这样就相当于用这个说话声或者歌声来作为乐器演奏,可以变相实现所谓的“人声唱歌”的功能。而如果波表的声音是一种GM没有的打击乐,那就是扩展了打击乐。所以上述说明并没有专门对打击乐进行特别的说明。