【Win 10 应用开发】MIDI 音乐合成——乐理篇
针对 MIDI 音乐的 API ,其实在 Win 8.1 的时候就出现。在UWP中采用了新的驱动模式,MIDI 消息传递更加高效。
首先得说明的是,UWP 的 MIDI 相关 API 不是针对 MIDI 文件的,而是针对 MIDI 设备的,所以它不具备保存 MIDI 文件的功能。当然,如果你想把 MIDI 消息存为音频文件,完全可以自己一个字节一个字节地写入。MIDI 文件分为两个数据块——头部和音轨。头部主要描述音轨类型(单轨或多轨),包含轨道数量,以及计时方式。
MIDI 有两种方式来描述音符时值:
1、Timing Clock:单个四分音符(常规是每四分音符为一拍)的“脉冲时钟数”(PPQ),时间单位Tick,一般为24的倍数。
2、帧率。这个跟视频有点像,比如24帧,30帧等。
计时方式用两个字节表示(16位),如果最高位为0,表示用Tick方式来描述,剩下的15位表示Tick值;如果最高位为1,则表示帧率。
比如,如果用Tick方式表示(常用),第16位必须为0,即 0111 1111 1111 1111。
MIDI 文件除头部外,剩下部分都是音轨数据。每个音轨由一系列事件组成。事件就是MIDI指令。MIDI 文件之所以体积小,是因为它不存储音频数据,只存储指令。比如 Note on 开始播放某个音符,Note off 停止播放某个音符。每个事件都由两段组成:
<delta time><event data>
Delta time 指事件的时间偏移量,它是相对于前一个事件而言的,当然,如果是轨道中的第一个事件(或者是元数据事件),delta time 可以是0。常用的是tick计时方式,比如,文件头中指定每个四分音符的时值为96,那么,假调事件A播放中音1(C),事件B停止播放中音1,如果这个中音1是四分音符,即时值为1拍,两个事件可这样排列:
<0><note on 60><96><note off 60>
60中音1的编码,这个老周后面会说,也就是B事件要在A事件后面,相隔时间为96,1拍。如果是八分音符呢,就是48(半拍)。
<0><note on 60><48><note off 60>
好了,关于 MIDI 文件的信息就说到这里,有兴趣的话,你可以到 midi.org 官方网站去查看相关的规范。老周写了一个读写 MIDI 文件的通用类,功能还未完尚,仅供参考。下载链接在这里。
=====================================================
下面咱们说正题。
UWP 中的 MIDI API 是用于与 MIDI 设备通信的。其实总的来说也就两种设备:输入/输出。输入设备一般来说也就是 MIDI 键盘了。这个我们一般人用不上,买一个的话也不便宜,起码要几百大洋。输出设备可以是专门的MIDI声卡,当然,我们一般的声卡也可以。普通声卡支持MIDI 的通用标准,缺点是音色不太真实。专业声卡再配上软音源就可以模拟出更多乐器的音色,而且质量也高。软音源也不便宜,买一套大概也要一千大洋。
本篇咱们先不说 API 怎么用,很简单,因为微软都封装好了的,直接发送 MIDI 消息就行了,或者从外接的 MIDI 键盘中接收 MIDI 消息。但是,前提是你得有一点乐理知识。要求不高,能看懂简谱的话,就可以了。
简谱头部
在简谱上,标题、副标题这些就不多说了,都能看明白的。在简谱头部,我们主要了解三个标记:
1、调号。比如我们看到许多简谱上会标注 1 = C,意思是中音1的发音是钢琴键盘上的【中央C】键(白键)。调号不理解也无所谓,其实不影响MIDI编程,我们就不需要弄得太复杂了,尤其是大调小调的区分,除非你对音乐有兴趣,可以研究研究。反正就是调越高声音越尖锐,调越低声音越柔和。所以小调一般很适合民歌。
2、拍号:常用的是 4/4,分子为4,表示每四拍为一小节;分母为4,表示一个四分音符为一拍。这是最常见的。比如这样:
前面部分是调号,紧跟着是拍号。
如果是,2/4,表明每两拍为一小节,四分音符为一拍。
3、节奏(节拍):表示每分钟多少拍(BPM)。常见的节奏为120。
表示一分钟 120 拍,所以,每拍的时长为 0.5 秒。
如果是60,表明一分钟60拍,即一秒一拍。
实际上,决定曲子速度的不是拍号,而是节奏。120 的曲子速度自然要比 60 的快。拍号只是确定每个音符的相对时值,标准是四分音符为1拍,那么八分音符就是半拍,十六分音符就是1/4拍,三十二分音符就是1/8拍了。总之都是二次方的,而且分的越小时值越短。
前面提到MIDI文件有 Tick 和帧率两种计时方式,其实计时方式也不会影响曲子的速度(时长),就好比2分钟长的视频,你把帧率从 30 帧改为 15 帧,但视频长度依然是 2 分钟,只是变得不太流畅而已。MIDI 中也一样,速度是由节拍映射(Tempo map)决定的。不同的是,我们简谱中用的是 BPM(每分钟多少拍),而MIDI中用的是微秒,比如,BPM=120,即0.5秒一拍,换算为微秒就是 500000了。
小节
上面咱们提到过,4/4表示每四分音符为一拍,每小节一拍。那小节是啥?在简谱上,用小节线(竖线)来划分小节。请看下面例子。
上面例子中有两个小节,按照拍号的规定,每小节必须是四拍,第一个3是两拍,第二个3是两拍,加起来正好是四拍。所以后面紧跟一条竖线,这根线就是小节线。第二个小节中,中音5是一拍,中音2是一拍,紧接着的中音3、1下面都有一横线,是八分音符,各半拍,加起来正好一拍;最后的低音6是一拍,合起来也是四拍。
再看一个例子。
注意看拍号,2/4表示每四分音符为一拍,但每小节是两拍。比如第一小节,中音1、中音2都是四分音符,各一拍,共两拍,所以构成一个小节。
音符
我们刚刚在简谱上看到的1234567,就是音符,当然这是简谱上的表示法,这种表示法,容易识别。在五线谱中,音符是用“蝌蚪文”来表示的,不容易分别,也不好懂。
顺便说说唱名和音名。这两个东西,很多时候都会搞混。所谓唱名,就是你用嘴巴唱出来的时候发的声,就是
dol re mi fa sol la xi
对应的音符就是 1 2 3 4 5 6 7。这个应该不难,小学生都懂。
音名就是钢琴键盘上那些字母,与唱名对应的是 C D E F G A B。
中国很多乐器(尤其是吹管类)的基本音域都在 1 2 3 5 6 这几个音上,那是因为我们古代的定音方式为“宫,商,角,徽,羽”,有的说是“宫,商,角,徴,羽”,对应的大约是1 2 3 5 6,古人是用“三分损益”法计算音阶的。因此,许多民乐都没有 4 这个音(3和4的音程是半音),比如,巴乌就是个典型。 笛子和洞箫虽然有 4 这个音(放开全部音孔,八孔箫要按住半音孔),但发声相对较弱。其实,像巴乌(葫芦丝)这些乐器也可以通过接中音5以下的音孔来调节出 4 的音,但也是比较弱的。
十二平均律
音阶划分方式很多,比如中国古代有“五度相生”法,五度指纯五度,这个很复杂,老周也说不清楚,不过,我可以总结出一句不太靠谱的话——纯五度的总音程为 3.5 个全音(三个全音,一个半音)。
其他的计算方式不多讲,因为 MIDI 的音阶用的是十二平均律,这是世界普及的,琴键上用的也是十二平均律。其实,十二平均律是中国人发明的,在明朝的时候就出现了,只是当时乐器生产工艺限制,没有人愿意接受这种方式,结果让西方人抢了头功。
十二平均律是以每【半音】来划分的,因此,它可以包含12个音:
1、1#、2、2#、3、4、4#、5、5#、6、6#、7
对应的音名为
C、C#、D、D#、E、F、F#、G、G#、A、A#、B
其中,3和4之间的音程是半音,前一八度的7与后一八度的1之间的音程是半音,其余为全音,比如1和2之间是全音,所以,在1和2之半加一个 1#,表示在1的基础上升半音,因此,1# 和 2b 是同一个位置,1升半音就是 1#,2 降半音是 2b。
文字是说不清楚的,看看这个图你就懂了。
不管白键还是黑键,两个键之间的音程都是半音,你会看到,3 和 4 之间没有黑键,因为 3 和 4 之间的音程就是半音。故 12345 就是所谓的纯五度,因为它们的总音程就是 3.5 个全音。
再看一张更大的图。
这样你就看到规律了,一个八度的键排序是这样的:
黑 黑 黑 黑 黑 ……
——————————————————————————————————————-
白 白 白 白 白 白 白 ……
故而,3和4之间是两个白键,7和1之间是两个白键,因为它们的音程都是半音。
将其替换为十二个音符,就是:
1# 2# 4# 5# 6# ……
——————————————————————————-
1 2 3 4 5 6 7 ……
介绍完音符,咱们还要了解音符的时值,所谓时值,就是音符的相对时间长度。
按照时值不同,可以分为以下几种:
1、全音符。标准情况下是四拍,表示方法为 X – – -。
2、二分音符。标准情况下是二拍,表示方法 X -。
3、四分音符。一拍,表示方法 X。
4、八分音符。半拍,表示方法 。
5、十六分音符。四分之一拍,表示方式 。
6、三十二分音符。八分之一拍,表示方式 。
……
后面就不再分了,时值太短了,你也唱不出来。
当然,也有比较特殊的,比如,三拍时值的音符,也可以表示为 X – -。
好了,只要有了上面这些基本知识,就可以开始 MIDI 编程了。下一篇老周就说说如何向声卡发送 MIDI 消息。本篇就扯到这儿了。