录音/制作/创作 吉他 扩声技术 视频技术 作品展示 生活 信息 更多... | 音频应用专卖店
Sonar / Cakewalk

[教程] CAL编写教程(打造自己独特的MIDI系统)

( 90 )
 
[收藏]
-  第 5 页  -

1159
#61 15-2-27 13:58

回复 stanley_mean 在 #33 的 pid=4404598 的贴子

目前虽然提到的实例CAL,都是CW9的,但因为其中没有涉及到SONAR的BUG变量,所以它们都是可以在SONAR全系列中使用的,另外,比较强大的CAL,也可以通过前边讲到的修改方式,使其可以在SONAR全系列中完美运行,还是很爽的。

1159
#62 15-2-27 13:59

回复 sfire001 在 #34 的 pid=4404987 的贴子

初次接触CAL编写的朋友,确实很难看懂,我当年拿到CAL的语句指令时,也感觉像天书。不过,只要花些功夫,尝试的编写几个CAL脚本,很快就会领悟的。

1020
#63 15-2-27 14:17
实在 不会~~~当下伸手党,求前辈发下 自己制作 CAL脚本~~~~感激不尽啊

1159
#64 15-2-27 14:22

这里说说我自己的一些小感受!

自从发表CAL教程贴以来,无论是本贴回复中,还是在QQ群或是QQ上与我交流的朋友中, 都存在有些朋友依然不太能明白CAL到底是怎么回事的情况。

也许有朋友认为,编写CAL是一个很逻辑化的东西,而做音乐的人一般又都是十分感性的,
所以感觉做音乐还要靠自己去研究CAL编写,是一件很恼火的事。
因为逻辑就意味着:刻板,繁复,计算,稳定……
而感性却意味着:灵感,随意,意外,火花……
这二者看起来是那么的冲突,甚至是相反的两种思维模式,其实这是一个小小的误会。

逻辑性的事件,也有因为创意上突发奇想,而实现强大目的的时候。
音乐上的感性,通过细心领悟,也会发现一些规律。

从我自身的经历来看,我自己是一个十分感性的人,无论是思想上,还是行为上。
这从我大半夜还坐在电脑前发贴,聊群,玩自己感兴趣的玩意儿就不难看出,
我在很多时候,并不是一个克己的人。

但是,我带过的很多制作人,大家对我的评价,最多的就是刻板,工作狂。
虽然我自己也不喜欢这种评价,但工作上的标准是死的,
为了让大家更快的掌握标准,很多东西就需要有一个规矩方圆。

说了一大堆,想表达的就是,感性与逻辑只是两种处事态度,态度在很大程度上就是习惯,
作为万物之灵的大家,是可以同时具备这两种习惯的。
唯一阻挡在我们面前的,不是思维模式的不同,更不是能力上的区别,只是愿不愿意而以。

说真的,在输入MIDI时,很多功能,只有做MIDI的人自己才知道,自己到底需要什么样的功能,
官方在软件上的MIDI功能,本质上只是一个可以适应所有音乐人的基本功能,
是肯定无法满足各种各样业务需要的!

所以大家不要怕编写CAL,多尝试编写,就算成不了CAL高手,也应该能大幅度的提升工作效率。
至少,在下载到一些CAL时,也要能通过修改其中的一些指令,将其变得更适合自己所需的目的。


中国的科幻大师“刘慈欣”曾说过——
——没有读不懂的科幻小说读者,只有不会写科幻小说的作者。

所以,大家如果看不懂这篇教程贴,我只能请大家原谅,本人文笔有限,应该是我写得有些混乱,
才令童鞋们产生这种不好的观感,我会努力改进。

[ 本帖最后由 溺水鱼 于 15-2-27 14:38 编辑 ]

1640
#65 15-2-27 14:28
开阔了我的另一个视野,大赞

1159
#66 15-2-27 16:02

CAL教程06(实例讲解:初级三)

今天讲解一些和控制器值与弯音相关的CAL实例,因为编写方式与前边讲到的音符类编写实例很类似,所以会讲得比较简捷一些,只要大家通过之前的实例,掌握了编写的格式,下文将是十分易懂的,
甚至,很多童鞋可能已经自己旁通了……

[size=16.3799991607666px]—————————————————————————————————————————————————


[size=16.3799991607666px]例1:控制器值百分比增减

[size=16.3799991607666px](do
[size=16.3799991607666px] (int BFB 80)
[size=16.3799991607666px] (getInt BFB "% ?" 0 1000)
[size=16.3799991607666px] (forEachEvent
[size=16.3799991607666px]  (= Control.Val (/ (* Control.Val BFB) 100))
[size=16.3799991607666px] )
[size=16.3799991607666px])
[size=16.3799991607666px]翻译:
[size=16.3799991607666px](执行
[size=16.3799991607666px] (设置一个int变量,变量名为BFB,默认值为80%)
[size=16.3799991607666px] (弹出标有“%?”字样的对话框,由用户输入一个具体的百分比值,它的取值被约束在0至1000。)
[size=16.3799991607666px] (对框选的每一个事件执行一次下列操作
[size=16.3799991607666px]  (当前控制器值=((该控制器值×变量)÷100))
[size=16.3799991607666px] )
[size=16.3799991607666px])

[size=16.3799991607666px]这里用到了:=;/;* 这三个运算符,也用到了我们在小学学到的百分比计算公式,
[size=16.3799991607666px]需要注意的是,如果我们输入的值,其计算得出的结果超出了控制器值所能到达的极限,
[size=16.3799991607666px]程序将报错停止。

[size=16.3799991607666px]此例大家可以旁通一下,之前的音符力度,音符时值,也可以用这个编写方法来实现百分比效果。
[size=16.3799991607666px]同理,之前的音符时值,力度的固定值增减,也可以旁通到控制器上来,给控制器增加或减少一个定值。

[size=16.3799991607666px]—————————————————————————————————————————————————

[size=16.3799991607666px]例2,在光标处插入控制器:

CW9写法:
[size=16.3799991607666px](do
[size=16.3799991607666px] (int CC 10)
[size=16.3799991607666px] (int V 64)
[size=16.3799991607666px] (getInt CC "CC#=?" 0 127)
[size=16.3799991607666px] (getInt V "VEL=?" 0 127)
[size=16.3799991607666px] (insert Now Event.Chan CONTROL CC V)
[size=16.3799991607666px])

[size=16.3799991607666px]SONAR写法:
[size=16.3799991607666px](do
[size=16.3799991607666px] (int CC 10)
[size=16.3799991607666px] (int V 64)
[size=16.3799991607666px] (getInt CC "CC#=?" 0 127)
[size=16.3799991607666px] (getInt V "VEL=?" 0 127)
[size=16.3799991607666px] (insert (/ (* Now TIMEBASE) 960) Event.Chan CONTROL CC V)
[size=16.3799991607666px])

[size=16.3799991607666px]翻译:
[size=16.3799991607666px](执行
[size=16.3799991607666px] (设置一个int变量,变量名:CC,默认值:10)
[size=16.3799991607666px] (设置一个int变量,变量名:V,默认值:64)
[size=16.3799991607666px] (弹出标有“CC#=?”字样的对话框,由用户为变量CC,输入一个值。)
[size=16.3799991607666px] (弹出标有“VEL=?”字样的对话框,由用户为变量V,输入一个值。)
[size=16.3799991607666px] (插入 在光标位置 在当前通道 控制器 控制器号为CC 控制器值为V)
[size=16.3799991607666px])
[size=16.3799991607666px]注解:
[size=16.3799991607666px]CAL将在光标(Now)位置插入一个控制器,
[size=16.3799991607666px]控制器号由变量CC代替,CC变量的值由用户输入,
[size=16.3799991607666px]控制器值由变量V代替,V变量的值由用户输入。


[size=16.3799991607666px]—————————————————————————————————————————————————


例3,在光标位置插入一个弯音:

CW9写法:
[size=16.3799991607666px](do
[size=16.3799991607666px] (int W 1365)
[size=16.3799991607666px] (getInt W "Wheel=?" -8192 8191)
[size=16.3799991607666px] (insert Now Event.Chan WHEEL W)
[size=16.3799991607666px])

SONAR写法:
(do
(int W 1365)
(getInt W "Wheel=?" -8192 8191)
(insert (/ (* Now TIMEBASE) 960) Event.Chan WHEEL W)
)

[size=16.3799991607666px]此例就不作翻译与注解了,因为编写格式与音符和控制器,并没有本质上的区别。
[size=16.3799991607666px]但要注意的是,弯音的值,是有固定标准的,我们在表达上,最常用的是正负八度的取值。
[size=16.3799991607666px]这样可以对付绝大多数弯音需要。这里列出弯音被设置成正负八度时的每一个弯音值:
+8191
+-7508
+-6825
+-6143
+-5460
+-4778
+-4096
+-3413
+-2731
+-2048
+-1365
+-683
0
-8192
上列中,只有正负八度的值有1个值的区别,其它的只有正负之分,这个死记即可。
并且,就算取值上有个位数上的区别,也不会影响听感,人的耳多分辨不出这么细微的音高区别。
但如果你的业务中,有这方面的标准要求,那就必须以标准来写值。

此例中,细心的童鞋已经发现,这样写弯音并不直观,因为要去记忆这些弯音值,也是一件花时间的事,
有没有方法可以改变这种表达值的方式呢?
比如,我希望直观的输入半音数(1个半音,7个半音,12个半音……)
而CAL执行的结果,可以自动的识别,并给出标准的取值。
就像这样,我输入“-2”,CAL就在光标位置写入一个“-1365”的值。

答案是肯定的,因为我们的CAL是无所不能的对吧……

但这依然是中级CAL的讲诉内容,就留在下文中讲解吧。
那么,本初级教程就到这里,还有大量属于初级编写的方面没有讲到,希望大家可以逐一旁通,
多多编写练习,有不明白的地方,可以上贴交流,也可以QQ交流。
但是一些很基础的知识,大家可以在前文中找到答案。

[size=16.3799991607666px]—————————————————————————————————————————————————


[size=16.3799991607666px]下文,进入CAL的中级教程,到时会出现大量的假设语句,
[size=16.3799991607666px]希望大家多多的掌握前边的初级内容,[size=16.3799991607666px]不要给后面的学习带来障碍。


[size=16.3799991607666px]待续……

[ 本帖最后由 溺水鱼 于 15-3-2 11:32 编辑 ]
观众反应

187
#67 15-2-28 10:00
我想问一下楼主,像kontakt、bestservice engine音源加载这些t行为是否也可以编CAL用快捷键操作呢? 我希望能脱离鼠标用快捷键来加载音源。谢谢!

1159
#68 15-2-28 13:52

回复 stanley_mean 在 #39 的 pid=4405608 的贴子

这个不行的,CAL只能对MIDI事件的输入输出起作用。你需要的功能已经不属于MIDI编辑了。另外,kontakt 每次在新建工程时,一个个的加载音源确实头痛,
建义做几种不同风格的,已经在 kontakt 中加载好音色的多通道模版,
每次需要新建时,就打开类似风格的模版,如果需要改音色,
只需要将音色拖入到kontakt中,已经加载好的音色上,就能自动替换掉的。
这样就方便了。

1159
#69 15-3-2 11:44

回复 溺水鱼 在 #1 的 pid=4401232 的贴子

8楼:个别CAL效果展示
CAL初级基础:----------------------------------------
29楼:CAL热键设置!
10楼:CAL在SONAR全系列上不正常的解决方法
16楼:CAL教程01(语句列表A)
17楼:CAL教程01(语句列表B)
18楼:CAL教程02(熟习CAL语句的用法)上
19楼:CAL教程03(熟习CAL语句的用法)下
27楼:CAL教程04(实例讲解:初级一)
31楼:CAL教程05(实例讲解:初级二)上
32楼:CAL教程05(实例讲解:初级二)下
38楼:CAL教程06(实例讲解:初级三)
-----------------------------------------------------------

1159
#70 15-3-2 15:16

CAL教程07(实例讲解:中级一)

本篇开始,我们将摸到真正发挥CAL威力的门槛!
—————————————————————————————————————————————————


接下来的CAL语句中,会大量出现假设概念,它不只是 if 指令,
从本质上来说,while 与 switch 指令,其实也是类似假设这种概念的,只是在假设之后执行语句的方式有所不同。


—————————————————————————————————————————————————


我们首先来了解一下 if 指令的概念,以及编写格式。
我们的各类指令,原则上来说,在指令后都只能跟随一条执行语句,如 forEachEvent 后面就只能跟一条执行语句。
(当然,我们已经在之前的实例中,掌握了如何将任意多条语句,用一条 do 指令来打包)
if 指令也差不多,它的后面首先跟的是一个条件语句,即我们要假设的条件,
然后在之后,紧跟着就是这个条件成立的情况下,应该执行的一条语句(可由do指令来扩展任意多条指令)
再然后,紧跟着的就是这个条件失败的情况下,应该执行的一条语句(也可由do指令来扩展任意多条指令)


从这个解释中,我们已经发现其规律,即 if 指令中,虽然有两条被执行的语句,但本质上只会执行一条,

具体执行前一条,还是后一条,取决于假设的条件是否成立。
基本格式:(如果 条件 成立时执行指令 失败时执行指令)
需要被执行的指令,我们在之前的初级教程中已经有详细讲解,相信大家已经掌握。
条件一项,我们需要了解一下,它很简单,一般都是由一段关系运算符,以及逻辑运算符语句组成,
关系运算符:
<
>
<=
>=
==
!=
逻辑运算符:
&&
||



我们直接来一个简单的实例,大家就了然了。


—————————————————————————————————————————————————


例1:控制器互转
说明:执行本CAL后,在弹出的对话框中输入控制器号,程序将把被选中的控制器转换为用户输入的控制器号。
注:
各种控制器号之间的相互转换,在操作上并不是将某某控制器号,转变成别的控制器号,CAL 中并没有这类指令,
那么我们是怎么实现这个功能的呢?
其实,这是利用了插入指令,与删除指令来完成的操作。


(do
(int CC)
(getInt CC "CC#=" 0 127)
(forEachEvent
  (if (== Event.Kind CONTROL)
   (do
    (insert Event.Time Event.Chan CONTROL CC Control.Val)
    (delete)
   )
   NIL
  )
)
)
翻译:
(执行
 (设置变量为int类型 变量名为CC 默认变量值为零)
 (弹出标有“CC#=”字样的输入窗 将输入值赋予变量CC)
 (对被选中的所有事件都执行一次扫描,并在每一个扫描操作后进行一次下面指令
  (如果 (事件类型=控制器类型)
   (就执行
    (插入 当前位置 当前通道 插入类型为控制器 控制器号为CC 当前控制器值)
    (删除当前控制器)
   )
   否则执行:NIL(什么也不执行)
  )
 )

翻译中,只要解释为“当前”的,就是指当前正在被扫描的事件。

这个例子中,对于 (forEachEvent 来说,整段 if 语句都被看作一条指令,即:
  (if (== Event.Kind CONTROL)
   (do
    (insert Event.Time Event.Chan CONTROL CC Control.Val)
    (delete)
   )
   NIL
  )

就是一条完整的 if 指令。

在这条 if 指令中,整个do指令中的插入与删除两条指令,都被当作一条do指令,
并作为条件成立后要执行的指令操作。
后面跟着的NIL(不执行),是与do指令并列的另一条指令,
它被作为条件不成立时需要执行的指令操作。

在此例中,我们为什么要加入 if 指令呢?为什么不能像前边的初级教程中提到的那样,
直接对每一个事件执行一次插入与删除的操作呢?

这是因为我们在框事件时,特别是以时间标尺处拉出选区为操作方式时,
其选区内所有的事件都会被选中,包括弯音,音符,控制器……等等,
如果不加入这段 if 指令,则所有的事件都会被删除,并插入一个控制器……
所以必须加入一个 if 指令,对我们框选的事件类型进行区别,
才能实现只对控制器事件进行相应操作,而对别的事件类型不进行任何操作。

为了加深我们对遍历指令,与假设指令的正确认知,我们跟随CAL程序来走一趟,大家就明白其中的概念了,
请一定认真仔细理解下面的讲解:


比如,我们框选了一段选区,这里有以下事件被选中:
第1个事件:音符
第2个事件:弯音
第3个事件:表情
第4个事件:弯音
第5个事件:表情


OK,我们现在跟随上例CAL进行一次旅行,看看CAL程序是如何完成整个操作的:
首先是设定用户变量,CAL将名为CC的变量设定为int类型的变量,它的取值范围是 -32768 至 +32767。
然后是用户输入一个数值,CAL将此值交给变量CC,假设我们输入的值是10,那么变量CC的值就是10。
之后进入遍历指令,CAL要求将所有被选中的事件,都执行一次下面的操作,
下面的操作是一个假设语句,这里就是关键了,


当CAL对第1个事件进行扫描时,发现该事件是音符,而我们的假设条件是,事件类型要等于控制器,
如此,扫描第一个事件时,我们的假设不成立,所以执行下面第二个指令,即:什么也不执行的指令。


然后CAL会扫描第2个事件,因为该事件是弯音,而非控制器,所以假设条件依然失败,
于是,再次执行第二个指令:什么也不做。


继续扫描第3个事件,第3个事件是表情,即11号控制器,那么“事件类型=控制器”的这一假设成立了,
于是执行下面第一个指令:即插入控制器,控制器号是用户设定的10号(声相)控制器,
并且删除被扫描的这个事件(11号表情控制器)


继续扫描第4个事件,条件不成立,什么也不做。


继续扫描第5个事件,条件成立,执行插入,并删除操作……

—————————————————————————————————————————————————

本讲的功能虽然简单,但这个编写思路,以及CAL的运行思路一定要理解透彻,
这对之后的高级CAL编写很重要,大家可以反复的多理解几遍。

待续……



[ 本帖最后由 溺水鱼 于 15-3-2 15:41 编辑 ]

54
#71 15-3-2 22:17

学习是种习惯

学习是种习惯;当真正爱上一个东西的时候,没有任何困难成为困难

1159
#72 15-3-3 02:45

CAL教程08(实例讲解:中级一)

本篇主要讲解逻辑运算符的使用,这是一个非常重要的指令,如果没有它,很多高级的CAL就无法诞生了!逻辑运算符只有两个:“&&”以及“||”,它们是在条件语句中使用的。
&& 的概念是:必须同时满足两个条件,假设才成立。
|| 的概念则是:只需要满足两个条件中的任意一个,假设就成立。
它们的编写格式都一样,运算符写在最前边,后面跟写两个条件语句:
(&& 条件1 条件2)
(| | 条件1 条件2)
理论上,它们可以无限的扩展,即在条件1与条件2中,再次写入&&或 | | 逻辑语句。


我们在使用MIDI键盘进行音符输入时,时常会因为一些不明确的软件问题,
造成输入的音符中,存在错误的音符属性,比如时值为零,比如力度为零,
下面我们就用逻辑运算,编写一个CAL来删除这些音符。


—————————————————————————————————————————————————


例2:删除力度为零,或者时值为零的音符


(forEachEvent
(if (|| (== Note.Vel 0) (== Note.Dur 0))
  (delete)
  NIL
)
)
翻译:
(对所选事件进行逐一扫描,并在每一次扫描后进行下列操作
 (如果 力度=0 或者 时值=0
  就执行删除
  否则不操作
 )


此例我们用到了“||“运算符,大家可以直接将其理解成”或者“这个概念。
旁通:
这个CAL语句中,我们还可以将条件中的数值“0”进行修改,使其变成删除其它属性的工具,
也可以将数字“0”用变量来取代,再写入用户输入指令,
使变量由用户来设定其值。
这样就可以由用户来输入删除什么属性的音符了。
再旁通:
同样的编写方式,只需要修改相应的参数指令,就可以删除特定控制器,弯音,音色……等事件了。

—————————————————————————————————————————————————


某些较老的音源中,或是某些扒带业务中,
钢琴延音控制器(64号CC),只认127与0这两个值,127为踩下延音踏板,0为放开踏板,
其它值不认。这种情况下,我们在写值时就很麻烦。
如果我们可以把大于64的值统一变成127,小于64的值统一变成0,那么在写控制器时,就可以轻松随意的写了。


例3:量化64号控制器


(forEachEvent
(if (&& (== Event.Kind CONTROL) (== Control.Num 64))
  (if (> Control.Val 64)
   (= Control.Val 127)
   (= Control.Val 0)
  )
  NIL
)
)

翻译:
(对所选事件进行逐一扫描,并在每一次扫描后进行下列操作
 (如果 事件类型=控制器 并且 控制器号=64
  就执行(如果 控制器值 大于 64
      (就执行 控制器值=127)
      (否则执行 控制器值=0)
  否则什么也不做
 )

本例中用到了&&运算符,大家可以直接将它理解为“并且”概念,
这意味着只有当两个条件全部成立时,假设才成立。

大家应该注意到了,本例中还有一个特点,就是在 if 指令中又钳套了一个 if 指令,
只要大家分清它们的关系,
在第一个假设语句:(if (&& (== Event.Kind CONTROL) (== Control.Num 64))下面,
作为条件成立后要执行的指令,就是整个第2个假设语句,
即:
  (if (> Control.Val 64)
   (= Control.Val 127)
   (= Control.Val 0)
  )

下面的“NIL”是作为第1个假设语句,当条件不成立时执行的指令。

理论上,if 指令中可以套无数多个 if 指令,也可以套while、switch、forEachEvent……等指令,
并且这些指令相互间也可以钳套,只要不发生逻辑混乱就行。


—————————————————————————————————————————————————


待续……


[ 本帖最后由 溺水鱼 于 15-3-3 02:49 编辑 ]

8448
#73 15-3-3 10:32
当年就靠这个养家糊口,真是怀念

1159
#74 15-3-3 14:41

CAL教程09(实例讲解:中级二)

本篇讲解 “对比 switch” 指令的编写以及格式。它是进入高级编写的必备指令,虽然非常重要,但编写十分简单,
只要我们认真仔细的理解了 if 指令的编写与格式,要理解 switch 就不是难事。


—————————————————————————————————————————————————


switch 指令:本质上与 if 指令是一模一样的,也是通过给出一个假设的母条件,
但它并不判断这个条件是否成立,而是逐一的对比下面列出的子条件,
一但发现与母条件得出的结果相同的子条件,就执行该子条件后面的指令。
格式如下:
(switch 母条件
 子条件1 需要执行的指令
 子条件2 需要执行的指令
 子条件3 需要执行的指令
 …… ……

格式中的母条件与子条件,均可由:公式;用户自定义变量;内置变量;具体数值……等等来表达。


之前,我们的初级实例中,最后一个例子是关于弯音写入的,
那个实例中,我们写入弯音时,需要输入标准的弯音值来对自定义变量进行设置,
这显得很麻烦,咱们可是搞音乐的,不是搞数据的对吧?
所以我们当然应该输入与音乐相关的值,这样才更加直观。
下例,我们就来编写一个写入弯音值的CAL,要求只需要用户输入半音数,CAL就能自动的写入标准值。


—————————————————————————————————————————————————


例1:弯音写入


CW9写法:
(do
(int W -2)
(getInt W "Wheel=?" -12 12)
(switch W
  1 (= W 683)
  2 (= W 1365)
  3 (= W 2048)
  4 (= W 2731)
  5 (= W 3413)
  6 (= W 4096)
  7 (= W 4778)
  8 (= W 5460)
  9 (= W 6143)
  10 (= W 6825)
  11 (= W 7508)
  12 (= W 8191)
  -1 (= W -683)
  -2 (= W -1365)
  -3 (= W -2048)
  -4 (= W -2731)
  -5 (= W -3413)
  -6 (= W -4096)
  -7 (= W -4778)
  -8 (= W -5460)
  -9 (= W -6143)
  -10 (= W -6825)
  -11 (= W -7508)
  -12 (= W -8192)
  W (= W 0)
)
(insert Now Event.Chan WHEEL W)
)

SONAR写法:
(do
(int W -2)
(getInt W "Wheel=?" -12 12)
(switch W
  1 (= W 683)
  2 (= W 1365)
  3 (= W 2048)
  4 (= W 2731)
  5 (= W 3413)
  6 (= W 4096)
  7 (= W 4778)
  8 (= W 5460)
  9 (= W 6143)
  10 (= W 6825)
  11 (= W 7508)
  12 (= W 8191)
  -1 (= W -683)
  -2 (= W -1365)
  -3 (= W -2048)
  -4 (= W -2731)
  -5 (= W -3413)
  -6 (= W -4096)
  -7 (= W -4778)
  -8 (= W -5460)
  -9 (= W -6143)
  -10 (= W -6825)
  -11 (= W -7508)
  -12 (= W -8192)
  W (= W 0)
)
(insert (/ (* Now TIMEBASE) 960) Event.Chan WHEEL W)
)

本例中,用到的对比指令,本质上就是将用户自定义变量W的半音值(-12至+12),
通过对比指令转换为标准弯音值(-8192 至 +8191),然后由插入指令写入。

旁通思路:
我们可以通过输入半音值,使CAL完成插入一个单独标准值的操作,当然也可以批量输入多个标准弯音值。
只需要在插入指令后再多跟一系列插入指令,使其在光标后更多的位置插入弯音。
同样是利用对比语句的方式,我们可以使CAL执行一系列假设:
如果输入的是“-1”,则执行插入“-683”,并在更后面一点的位置插入“0”。
如果输入的是“-2”,则执行插入“-3165”,并在更后面一点插入“-683”,再在更后面一些插入“0”
……依此思路,我们即可完成一键批量输入一系列需要的弯音值,并自动归零。

再旁通:
我们可以给出一个框选范围,利用选区首与选区尾的距离,通过一些小学的数学知识,来约束输入的弯音间距,
由此,我们输入弯音的效率,将远超MIDI键盘输入,并且,通过对标准值的修改,它能满足任何业务要求。

—————————————————————————————————————————————————


需要编写天马行空的MIDI功能,就要一步一步的仔细理解前边的内容,否则之后的高级内容,极有可能看不懂。
当然,并不希望在CAL中取得多么变态功能的朋友,学习到这里,已经可以提升不少效率了。
只要将前边的实例吃透,就可以旁通出大量提升效率的CAL。


如果只是伸手Dang-的话,虽然很多CAL确实能帮助大家提高输入效率,甚至是输入的乐趣,
但毕竟他人写出来的CAL,一般都是针对此人自己所做的业务要求编写的,功能上就不见得是我们需要的了,
所以我们虽然只是伸手党,但至少应该能看懂一些语句,可以将别人编写的CAL进行一些修改,
以达到更适合自己需要的目的。


—————————————————————————————————————————————————


想了解更多?
待续……



1159
#75 15-3-4 16:24

CAL教程10(实例讲解:中级三)

本篇讲解两个内容:while 循环指令;random 随机化指令


首先,我们来了解一下随机化指令,
顾名思义,随机指令的效果,肯定就是将各种不同的值,变量……等,作随机化处理。
格式:(random 上限 下限)或(random 下限 上限)
上限与下限均可由数值;内置变量;用户自定义变量来表达,
一但给出了上下限,那么整段随机语句将给出一个确定的随机值,此值在上下限之间(包括上下限值)


如:(random 85 105),此行语句即表示85至105的随意一个值。
如:(random Note.Vel 105),此行语句即表示当前被扫描音符的力度值,至105的随意一个值。


还是用实例来学习更直观:


我们在很多情况下,感觉MIDI的节奏太过标准,使其听起来非常机械,
如果能在一定范围内,将节奏随机化,那么就能给MIDI的演奏带来一定程度上的人性化听感。
我们在设计这个功能的CAL程序时,需要注意两个方面的音乐问题:
一:影响音乐节奏的变量是哪一个?
二:真实演奏中,节奏的不稳定概念是什么?


一:我们知道音乐中的节奏,本质上就是音符的起始位置,
要改变节奏上的死板,就是使节奏出现不同程度的随机效果,也就是使音符起始位置在一定程度上随机化。


二:在真实演奏中,节奏的不稳定性分为两个方面:
1、指我们在一个需要同时演奏多个音符的时候,是不可能使每一个音符在同一时间奏响的,
比如钢琴的柱式和弦演奏,虽然听感上是一个柱式演奏,但事实上,没有任何两个音符是同时奏响的。
所以我们必须使这个节奏上的每一个音符起始位置,发生一点小小的错位,来模拟真实的演奏。
2、是从情绪上来讲的,在从平稳向激昂发展时,我们的速度,会在一定程度上不自觉得越来越抢,
而从激昂向平稳过度时,我们的速度在一定程度上,又会越来越拖。


第一个概念十分好满足,就是将音符起始位置作一定程度的随机化即可。
但是第二个概念就很难满足了,因为我们的CAL中所有的语句,都与速度无关,看起来,就不可能完成了。
不过,我们依然可以在节奏随机之后,通过将音符整体前移的方式,
或是直接在速度窗中画一系列渐快的速度值来满足这个需要。


那么,我们的重心,就只需要放在节奏的随机上,
上面已经提到,当我们一个柱式按下去时,本质上,不可能出现完全同时一起奏响的两个音符,
但是我们知道,在随机指令中,就算我们的上限与下限的取值差拉得再宽,理论上依然有可能出现相同的随机值,
这就违背了上诉的概念,所以,我们必须想办法令随机处理时的值,不可能出现重复。
这就要用到循环指令。
即:——我们将随机指令夹在循环指令中,以达到这样一个效果:
如果当前随机值与上一次随机值相同,则反复执行随机取值操作,直到取值与上一次不同为止。


循环指令while格式:(while 条件 条件成立的情况下需要执行的语句)
它与 if 指令是不是很像,都是对条件进行判断,不同的是,当条件成立时,程序将反复执行后面的语句,
一直要到条件失败时,才停止反复执行。


那么,上实例吧……


—————————————————————————————————————————————————


例1:音符节奏人性化处理(给出防相同随机值指令)


(do
(word TK (/ TIMEBASE 24))
(long SJ)
(long BX)
(getWord TK "TK=" 1 65535)
(if (> TK 1)
  (forEachEvent
   (do
    (while (== SJ BX)
     (= SJ (random 0 TK))
    )
    (= BX SJ)
    (+= Event.Time (- SJ (/ TK 2)))
   )
  )
  (forEachEvent
   (do
    (= SJ (random 0 TK))
    (+= Event.Time (- SJ (/ TK 2)))
   )
  )
)
)
之前的实例中,为了大家快速的掌握编写格式,都作了翻译,相信大家已经掌握了精要,
此例以及之后的实例中,就不再作翻译了。如有疑惑,请发问交流。

旁通思路:
事件的起始位置可以随机,力度当然也可以,编写方式几乎一模一样,只需要注意实质变量的修改即可。

再旁通:
音长时值;音高键位;控制器值;弯音值也可以随机,当然,如果有兴趣,我们甚至可以使音色随机,
吼吼吼,只是音色的随机,对音乐是破坏性的操作,除非就是想要那么奇怪的效果……

—————————————————————————————————————————————————


下一篇,我们讲一讲钳入外部CAL的用法……
待续……

您需要登录后才可以回帖 登录 | 注册

本版积分规则

搜索