【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,表示一个四分音符为一拍。这是最常见的。比如这样:

【Win 10 应用开发】MIDI 音乐合成——乐理篇

前面部分是调号,紧跟着是拍号。

如果是,2/4,表明每两拍为一小节,四分音符为一拍。

3、节奏(节拍):表示每分钟多少拍(BPM)。常见的节奏为120。

【Win 10 应用开发】MIDI 音乐合成——乐理篇

表示一分钟 120 拍,所以,每拍的时长为 0.5 秒。

如果是60,表明一分钟60拍,即一秒一拍。

【Win 10 应用开发】MIDI 音乐合成——乐理篇

实际上,决定曲子速度的不是拍号,而是节奏。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表示每四分音符为一拍,每小节一拍。那小节是啥?在简谱上,用小节线(竖线)来划分小节。请看下面例子。

【Win 10 应用开发】MIDI 音乐合成——乐理篇

上面例子中有两个小节,按照拍号的规定,每小节必须是四拍,第一个3是两拍,第二个3是两拍,加起来正好是四拍。所以后面紧跟一条竖线,这根线就是小节线。第二个小节中,中音5是一拍,中音2是一拍,紧接着的中音3、1下面都有一横线,是八分音符,各半拍,加起来正好一拍;最后的低音6是一拍,合起来也是四拍。

再看一个例子。

【Win 10 应用开发】MIDI 音乐合成——乐理篇

注意看拍号,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。

文字是说不清楚的,看看这个图你就懂了。

【Win 10 应用开发】MIDI 音乐合成——乐理篇

不管白键还是黑键,两个键之间的音程都是半音,你会看到,3 和 4 之间没有黑键,因为 3 和 4 之间的音程就是半音。故 12345 就是所谓的纯五度,因为它们的总音程就是 3.5 个全音。

再看一张更大的图。

【Win 10 应用开发】MIDI 音乐合成——乐理篇

这样你就看到规律了,一个八度的键排序是这样的:

黑              黑                       黑              黑                黑                        ……

-------------------------------------------------------------------------------------------------------------------

白            白              白      白             白               白                 白             ……

故而,3和4之间是两个白键,7和1之间是两个白键,因为它们的音程都是半音。

将其替换为十二个音符,就是:

1#       2#              4#        5#        6#            ……

-------------------------------------------------------------------------------

1         2          3   4          5          6          7       ……

介绍完音符,咱们还要了解音符的时值,所谓时值,就是音符的相对时间长度。

按照时值不同,可以分为以下几种:

1、全音符。标准情况下是四拍,表示方法为 X - - -。

2、二分音符。标准情况下是二拍,表示方法 X -。

3、四分音符。一拍,表示方法 X。

4、八分音符。半拍,表示方法 【Win 10 应用开发】MIDI 音乐合成——乐理篇

5、十六分音符。四分之一拍,表示方式 【Win 10 应用开发】MIDI 音乐合成——乐理篇

6、三十二分音符。八分之一拍,表示方式 【Win 10 应用开发】MIDI 音乐合成——乐理篇

……

后面就不再分了,时值太短了,你也唱不出来。

当然,也有比较特殊的,比如,三拍时值的音符,也可以表示为 X - -。

好了,只要有了上面这些基本知识,就可以开始 MIDI 编程了。下一篇老周就说说如何向声卡发送 MIDI 消息。本篇就扯到这儿了。

上一篇:__sync_fetch_and_add


下一篇:docker的网络模式