针对 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 消息。本篇就扯到这儿了。

【Win 10 应用开发】MIDI 音乐合成——乐理篇的更多相关文章

  1. 【Win 10 应用开发】MIDI 音乐合成——音符消息篇

    在上一篇中,老周介绍了一些乐理知识,有了那些常识后,进行 MIDI 编程就简单得多了.尽管微软已经把 API 封装好,用起来也很简单,但是,如果你没有相应的音乐知识基础,你是无法进行 MIDI 编程的 ...

  2. 【Win 10 应用开发】启动远程设备上的应用

    这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...

  3. 【Win 10 应用开发】导入.pfx证书

    这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...

  4. 【Win 10应用开发】Adaptive磁贴模板的XML文档结构

    在若干天之前,老周给大家讲了Adaptive Toast通知的XML模板,所以相应地,今天老周给大家介绍一下Adaptive磁贴的新XML模板. 同样道理,你依旧可以使用8.1时候的磁贴模板,在win ...

  5. 【Win 10 应用开发】RTM版的UAP项目解剖

    Windows 10 发布后,其实SDK也偷偷地在VS的自定义安装列表中出现了,今天开发人员中心也更新了下载.正式版的SDK在API结构上和以前预览的时候是一样的,只是版本变成10240罢了,所以大家 ...

  6. 【Win 10应用开发】认识一下UAP项目

    Windows 10 SDK预览版需要10030以上版本号的Win 10预览版系统才能使用.之前我安装的9926的系统,然后安装VS 2015 CTP 6,再装Win 10 SDK,但是在新建项目后, ...

  7. 【Win 10 应用开发】在代码中加载文本资源

    记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...

  8. 【Win 10应用开发】延迟共享

    延迟共享是啥呢,这么说吧,就是在应用程序打开共享面板选择共享目标时,不会设置要共享的数据,而是等到共享目标请求数据时,才会发送数据,而且,延迟操作可以在后台进行. 这样说似乎过于抽象,最好的诠释方法, ...

  9. 【Win 10 应用开发】Toast通知激活应用——前台&后台

    老周最近热衷于讲故事,接下来还是讲故事时间. 有人问我:你上大学的时候,有加入过学生会吗?读大学有没有必要加入学生会? 哎哟,这怎么回答呢,从短期来说,加入学生会有点用,至少可以娱乐一下,运气好的话, ...

随机推荐

  1. Problem G

    Problem Description A relay is a race for two or more teams of runners. Each member of a team runs o ...

  2. Cocoapods安装过程

    1.升级Ruby环境 gem -v gem update --system 如果没有权限去升级Ruby ?就输入 sudo gem update --system 2.换掉Ruby镜像 首先移除现有的 ...

  3. 查看 docker 容器使用的资源

    在容器的使用过程中,如果能及时的掌握容器使用的系统资源,无论对开发还是运维工作都是非常有益的.幸运的是 docker 自己就提供了这样的命令:docker stats. 默认输出 docker sta ...

  4. java工程师学习线路图

  5. C#获取指定月指定周的日期范围

    );        MessageBox.Show(end.ToShortDateString());

  6. 《天书夜读:从汇编语言到windows内核编程》七 内核字符串与内存

    1)驱动中的字符串使用如下结构: typedef struct _UNICODE_STRING{ USHORT Length; //字符串的长度(字节数) USHORT MaximumLength; ...

  7. java 压缩导出多个excel

    简单介绍下我实现的功能,首先是我的excel上传,它是以blob字段存储在oracel数据库中的,我实现的是循环遍历blob字段并使用io流进行打包下载,如有需要可自行修改 使用技术有,springM ...

  8. SICK激光雷达LMS511测量数据说明

    帧结构说明 LMS511的官方手册存在几个版本,在<Laser Measurement Systems of the LMS500 Product Family>的英文手册中,对单次(连续 ...

  9. C#设计模式之十五命令模式(Command Pattern)【行为型】

    一.引言   今天我们开始讲"行为型"设计模式的第二个模式,该模式是[命令模式],又称为行动(Action)模式或交易(Transaction)模式,英文名称是:Command P ...

  10. [C#]使用dnSpy对目标程序(EXE或DLL)进行反编译修改并编译运行

    本文为原创文章.源代码为原创代码,如转载/复制,请在网页/代码处明显位置标明原文名称.作者及网址,谢谢! 本文使用的工具下载地址为: https://github.com/cnxy/dnSpy/arc ...