🅰 示例:
import sys.midiOut;
//打开播放设备
var midiOut = sys.midiOut();
//播放简谱
midiOut.play("
两只老虎,
1__,2__,3__,1__,
两只老虎,
1__,2__,3__,1__,
跑得快,
3__,4__,5__,__,
跑得快,
3__,4__,5__,__,
一只没有耳朵,
5_,6_,5_,4_,3__,1__,
一只没有尾巴,
5_,6_,5_,4_,3__,1__,
真奇怪,
2__,'5__,1__,__
");
sys.midiOut 使用的编程记谱法基于简谱记号设计。
中音使用简谱记号:1,2,3,4,5,6,7
高音在后面加一个单引号:1',2',3',4',5',6',7'
低音在音符前面加一个单引号:'1,'2,'3,'4,'5,'6,'7
编程记谱时可使用 sys.midiOut.notes 名字空间指定的所有音名,这些音名使用科学音高记号(Scientific pitch notation)。
音名与音符的对应关系如下:
音名 | 唱名 | 简谱音符 |
---|---|---|
C4 | do | 1 |
D4 | re | 2 |
E4 | mi | 3 |
F4 | fa | 4 |
G4 | sol | 5 |
A4 | la | 6 |
B4 | ti (si,xi) | 7 |
音名与唱名的对应关系可以变更,这里先不用管这些。
SPN 音名后面的数值越大表示越高的音,例如 C4( 中央C ,简谱中的 1 ) 高八度就是 C5( 高音 do,简谱 1 上面加一点),低八度的音就是 C3 ( 低音 do,简谱 1 下面加一点)。
注意:
-1
在 sys.midiOut 中需要省略♯
(Sharp) 用小写 s
替代例如:C-1♯
在 sys.midiOut 里需要略写为 Cs
分隔符:
所有音符、指令、字幕使用逗号、竖线、换行(兼容任何换行符格式)之一分隔。忽略空格、制表符。
消音:
数字音符前面加负号表示消音,例如: -5,-'5
。SPN 音名小写表示消音,例如:g4,g3
。
延时:
用下划线 _
表示一个延时单位(默认为 250 毫秒 ),前面的数字音符
或下划线
与后面的下划线
可以连起来写,例如:5___,5___
。但是 SPN 音名
必须单独写,不能与下划线
或数字音符
连起来写。
和弦只要连续写多个音符就可以,例如 midiOut.play("1,3,5, __, 2,4,6, __, 3,5,7, __, 4,6,1',______","C4"/* 1=C */,250/* ♩=120 */);
。
函数调用:
记谱时可以直接调用 midiOut 的成员函数,函数名后必须有括号 () 且必须有参数,例如: delay(1000),1__,2__,3__
。
以下是所有可用于编程记谱的函数:
delay(millisecond )
用于延时。
setVelocity(value)
用于调整音量。
changeInstrument(instrument)
用于改变乐器,
afterTouch(pitch,pressure)
指定单个音符按键的触后效果。
channelPressure(pressure)
指定触后通道压力,影响所有音符按键。
cc(number,value)
发送 CC(Continuous controller) 消息
pitchBend(value)
调整弯音轮。
pitchBend2(value)
调整弯音轮。
其他数值与字符:
其他大于 127 的数值表示毫秒单位的延时,其他所有字符表示字幕。可自定义 sys.midiOut 对象的 log 方法用于显示字幕。
🅰 示例:
import sys.midiOut;
var midiOut = sys.midiOut();
//自定义字幕显示函数
midiOut.log = lambda (v) print(v)
//合成并播放音乐
midiOut.play("
1,150,
pitchBend(0.6), 弯音,
500, 延时 500 毫秒,
-1,停(音符前加负号表示消音)
1000
");
自定义音高、拍子快慢:
midiOut.play 函数可选用第 2 个字符串参数
指定数字音符 1
对应的 SPN 音名,默认值为 "C4"
。可选用第 3 个参数指定单个下划线对应的延时单位(默认为 250 毫秒 )
我们将上面示例的数字音符 1
改为指向 "E4"
(其他数字音符会自动调整音高),一个延时单位改为 125 毫秒(加快一倍),代码如下:
import sys.midiOut;
var midiOut = sys.midiOut();
midiOut.play("
两只老虎,
1__,2__,3__,1__,
两只老虎,
1__,2__,3__,1__,
跑得快,
3__,4__,5__,__,
跑得快,
3__,4__,5__,__,
一只没有耳朵,
5_,6_,5_,4_,3__,1__,
一只没有尾巴,
5_,6_,5_,4_,3__,1__,
真奇怪,
2__,'5__,1__,__
","E4",125);
运行上面的示例,播放的音乐更轻快了。
sys.midiOut 允许使用数组记谱,要点:
示例:
import sys.midiOut;
var midiOut = sys.midiOut();
//只能在 sys.midiOut.notes 名字空间内使用数组记谱。
namespace sys.midiOut.notes{
drm = [
C4,//数组内不能用字符串表示音符
150,
["pitchBend",0.6], "弯音",
500, " 延时 500 毫秒",
c4,"停(小写 SPN 音名表示消音)",
1000
]
}
//合成并播放音乐
midiOut.play(sys.midiOut.notes.drm);
🅰 示例:
//创建桌面歌词窗口
import win.util.lyric;
var lyric = win.util.lyric();
lyric.show();
//打开播放设备
import sys.midiOut;
var midiOut = sys.midiOut();
//定义显示歌词的函数
midiOut.log = function(str){
lyric.text = str;
}
//播放简谱
midiOut.play("
两只老虎,
1__,2__,3__,1__,
两只老虎,
1__,2__,3__,1__,
跑得快,
3__,4__,5__,__,
跑得快,
3__,4__,5__,__,
一只没有耳朵,
5_,6_,5_,4_,3__,1__,
一只没有尾巴,
5_,6_,5_,4_,3__,1__,
真奇怪,
2__,'5__,1__,__
");
上面的代码我们可自定义 sys.midiOut 对象的 log 方法用于显示歌词,并且使用了 win.util.lyric 扩展库显示桌面歌词。
合奏要点:
midiOut.play
本身是阻塞运行而非异步运行,所以必须先启动其他工作线程,然后再在主线程调用 midiOut.play
才能实现多线程同时演奏。0~16
范围的值。因为默认通道的值为 0, 因此除主线程以外其他线程的 channel 应指定为 1~16
范围的值,这样才能实现多通道合奏。 🅰 示例:
import sys.midiOut;
import win.util.lyric;
// 创建桌面歌词
var lyric = win.util.lyric();
lyric.show();
// 打开 MIDI 播放设备
var midiOut = sys.midiOut();
midiOut.log = function(v) {
lyric.text = v;//显示字幕
};
// 创建工作线程播放鸟鸣效果
thread.invoke(
function(midiOut) {
// 指定不同的通道以实现合奏
midiOut.channel = 3;
// 选择乐器为鸟鸣音效(乐器编号 123)
midiOut.changeInstrument(123);
// 播放鸟鸣效果,音量适中,节奏较快
midiOut.setVelocity(52);
//鸟鸣 - 森林的生机
midiOut.play("
delay(1000), 5'_,6'_,5'_,
delay(500), 6'_,5'_,6'_,
delay(800), 5'_,6'_,5'_,
delay(1200), 6'_,5'_,6'_,
delay(600), 5'_,6'_,5'_,
delay(1000), 6'_,5'_,6'_,
delay(1500), 5'_,6'_,5'_,
delay(2000), 森林晨曦结束,
","C5",100); // 高音区,延时单位短促
}, midiOut
);
// 伴奏
thread.invoke(
function(midiOut) {
midiOut.channel = 2;
midiOut.changeInstrument(122);
midiOut.setVelocity(50);
//伴奏 - 海浪声,
midiOut.play("
1____,5____,
1____,5____,
3____,6____,
1____,5____,
3____,6____,
1____,5____,
3____,5'____,
1____,1____,
","C3",500);
}, midiOut
);
// 在主线程中播放音乐,选择钢琴
midiOut.changeInstrument(0);
// 演奏乐曲,必须先运行其他线程,因为 midiOut.play 函数本身是阻塞运行的。
midiOut.play("
清晨的第一缕阳光,
1__,2__,3__,5__,
3__,2__,1__,5_,
森林中的鸟鸣,
6__,5__,3__,2__,
1__,2__,3__,1__,
微风拂过树梢,
5__,6__,5__,3__,
2__,1__,2__,5_,
晨雾渐渐散去,
3__,5__,6__,5'__,
3__,2__,1__,1__,6000
","C4",300);
//关闭窗口
lyric.close();
我们还可以用代码调用 sys.midiOut 对象的 changeInstrument 函数选择不同的乐器,示例:
import sys.midiOut;
var midiOut = sys.midiOut();
//选择八音盒,参数为乐器编号
midiOut.changeInstrument(10);
也可以在记谱时调用函数,示例:
import sys.midiOut;
var midiOut = sys.midiOut();
midiOut.play( "
changeInstrument(10),
1___,
2___,
3___,
" );
可用的乐器编号为 0~127
范围的数值,全部编号如下:
//钢琴
0 大钢琴(声学钢琴)
1 明亮的钢琴
2 电钢琴
3 酒吧钢琴
4 柔和的电钢琴
5 加合唱效果的电钢琴
6 羽管键琴(拨弦古钢琴)
7 科拉维科特琴(击弦古钢琴)
//色彩打击乐器
8 钢片琴
9 钟琴
10 八音盒
11 颤音琴
12 马林巴
13 木琴
14 管钟
15 大扬琴
//风琴
16 击杆风琴
17 打击式风琴
18 摇滚风琴
19 教堂风琴
20 簧管风琴
21 手风琴
22 口琴
23 探戈手风琴
//吉他
24 尼龙弦吉他
25 钢弦吉他
26 爵士电吉他
27 清音电吉他
28 闷音电吉他
29 加驱动效果的电吉他
30 加失真效果的电吉他
31 吉他和音
//贝司
32 大贝司(声学贝司)
33 电贝司(指弹)
34 电贝司(拨片)
35 无品贝司
36 掌击1
37 掌击2
38 电子合成1
39 电子合成2
//弦乐
40 小提琴
41 中提琴
42 大提琴
43 低音大提琴
44 弦乐群颤音音色
45 弦乐群拨弦音色
46 竖琴
47 定音鼓
//合奏/合唱
48 弦乐合奏音色1
49 弦乐合奏音色2
50 合成弦乐合奏音色1
51 合成弦乐合奏音色2
52 人声合唱“啊”
53 人声“嘟”
54 合成人声
55 管弦乐敲击齐奏
//铜管
56 小号
57 长号
58 大号
59 加弱音器小号
60 法国号(圆号)
61 铜管组(铜管乐器合奏音色)
62 合成铜管音色1
63 合成铜管音色2
//簧管
64 高音萨克斯风
65 次中音萨克斯风
66 中音萨克斯风
67 低音萨克斯风
68 双簧管
69 英国管
70 巴松(大管)
71 单簧管(黑管)
//笛
72 短笛
73 长笛
74 竖笛
75 排箫
76 Bottle Blow
77 日本尺八
78 口哨声
79 奥卡雷那
//合成主音
80 合成主音1(方波)
81 合成主音2(锯齿波)
82 合成主音3
83 合成主音4
84 合成主音5
85 合成主音6(人声)
86 合成主音7(平行五度)
87 +合成主音8(贝司加主音)
//合成音色
88 合成音色1(新世纪)
89 合成音色2(温暖)
90 合成音色3
91 合成音色4(合唱)
92 合成音色5
93 合成音色6(金属声)
94 合成音色7(光环)
95 合成音色8
//合成效果
96 雨声
97 音轨
98 水晶
99 大气
100 明亮
101 鬼怪
102 回声
103 科幻
//民间乐器
104 西塔尔(印度)
105 班卓琴(美洲)
106 三昧线(日本)
107 十三弦筝(日本)
108 卡林巴
109 风笛
110 民族提琴
111 山奈
//打击乐器
112 叮当铃
113 Agogo 钟
114 钢鼓
115 木鱼
116 太鼓
117 通通鼓
118 合成鼓
119 铜钹
//声音效果
120 吉他换把杂音
121 呼吸声
122 海浪声
123 鸟鸣
124 电话铃
125 直升机
126 鼓掌声
127 Q 声