Reaktor 小贴士:Reaktor Core Cell 搭建日记
我总是喜欢去想一个模块它的功能究竟是怎么实现的,然后在纸上写写计算,最终再回到Reaktor上来实践测试。
通常性我都是在Primary Level上利用原有的模块来进行相应的操作,尽管提供的模块能帮助去实现一些自己想要的功能,但有时候也因为如此让它的灵活性变低,显然,Core Cell是一个很好的补充,其实现在Reaktor Block就是普及Core Cell的好典例,所以我现在在搭建一些模块都会用Core Cell搭建的思路来进行搭建;表面上Core Cell跟原有的Primary Level上的搭建并没有什么不同,但Core Cell会更底层些,而且有一点很好的是,你可以去模仿一个模块,然后通过修改来改变它原有的功能,这也是Core Cell的目的,拓展你的需求。
今天我要来利用Core Cell来模仿搭建几个看似简单但又有点难点的模块;首先我来搭建的是Clipper模块(图1.1)。
图1.1
我们看到Reaktor给我们提供的Clipper是一个非常简单的模块,它有两个可控的参数Max和Min,它的运作原理就是,当输入信号大于Max或者小于Min的都要被切掉,切掉的意思其实就是等于相应的临界点,也就是说大于Max的信号最终输出的是一个恒值为Max的信号,反之小于Min的信号也如此;要知道对于Clipper模块也是经常会用到,从运作原理来看能知道它除了能制造点失真效果以外,还可以对控制信号进行限幅,比如控制一个滤波器的截止频率,有时候控制信号的复杂度不是我们能预期的,这时候用Clipper模块来限制,会对最终信号起到一个保护的作用。
好的,既然已经知道了Clipper的运作原理,那么在Core Cell搭建应该就不会那么难了?!事实也如此,所以我们可以创建一个空的Core Cell,给它命名为Clipper,并添加相应的输入和输出(图1.2)。
图1.2
创建完后,接下来我们要思考输入信号与Max、Min之间的关系,很显然,我们是要让输入信号与Max和Min进行比较,也就是说我们这里要利用Max和Min来路由输入信号进行不同的处理;在Primary Level我们知道有Compare模块来比较路由信号,同样Core Level也有比较信号的模块,它也叫Compare。(图1.3)
图1.3
我们看到了Core Level的Compare模块有两个输入,显然它们是用来连接要进行比较的信号,当然有输入也应该要有输出,不过Compare的输出不是一个数值的输出,而是一个判断性的输出,它属于Boolean类型,用来判断比较的真假,以图1.3来讲,假设In输入的信号大于Max,那么Compare的输出为真值,反之亦然;尽管Compare能帮我们判断两个信号比较的结果,但它的输出又不能直接用来进行任何信号的处理,所以Core Level还提供了一个叫Router的模块,它就是用来对Compare模块进行进一步的处理。(图1.4)
图1.4
我们看到了Router模块也有一个跟Compare模块输出一样的端口,实际上Router模块跟Compare模块就是一对搭档,有比较的地方肯定有它们的存在,而Router模块下面的输入则是用来连接你要路由的信号,显然我们应该是要把In的输入信号连接到这个输入,这样Router模块的输出信号就会根据Compare模块的判断来选择输入信号究竟该从Router模块上面的输出还是下面的输出;但从上面Clipper的运作原理,我们知道超过Max的信号将会等于一个恒值为Max的信号,所以我们要对Router模块上面的输出信号进行处理,让它最终等于一个恒值信号。那么怎么样才能让它等于一个恒值信号呢?
要让一个信号等于恒值信号并非难事,Core Level有为我们提供了一个叫Latch的模块,这个模块就能帮我们彻底地解决问题,但问题是很多人估计不明白Latch模块是个什么东西?它是怎么运作的?实际上Latch模块是一个Macro模块,它不是最基本的模块,就像Primary Level的Macro模块一样,当然Core Level的Macro模块跟Primary Level的Macro模块还是有一定的区别,也就是说Latch模块是在原有的模块基础上创建出来的新模块,它可以被修改的,而它的本质就是Read和Write这个模块的运作。(图1.5)
图1.5(Latch模块内部结构)
我相信很多人可以通过Core Cell帮助文档去了解Read和Write这两个模块的相关介绍,并且对它们的使用也有一定的掌握,因此想要看懂Latch模块是秒秒钟的问题,所以我们不难理解数值先被存储起来,然后通过事件触发来读取。(图1.6)
图1.6
好了,从图1.6我们已经看到对超过Max的信号进行了限制的处理了,同样对于小于Min的信号我们可以同样的方法来处理。(图1.7)
图1.7
虽然图1.7已经真真正正地完成了一个Clipper模块的搭建,但它还是有一些需要改进和注意的问题,比如一个大家经常性会使用错误的操作就是处理后的每个信号会用加法模块,注意图7整合信号不是用加法模块,而是用到了Merge模块,Merge模块处理信号的方式跟加法模块不同的是哪个信号先到先输出,如果是几个信号同时到达,那么输出的信号是Merge模块最底下输入的信号,而加法模块则是把所有信号相加在一起,这样对最终结果会产生很大的变化;除了这一点要注意以外,我们还需要改善一下搭建结构,首先很多人一开始看到这个搭建会觉得好乱啊,如果你是一个习惯这种乱乱的搭建并自己能看得懂的话,那么我觉得这倒无所谓了,但实际上这样的搭建对后续的修改和可读性是非常差的,搞不好时间一久或者状态很差的情况下回过头来看,你就会讨厌自己当初的做法了,所以我们需要对这个搭建进行整理,让它的可读性变高。
其实从图1.7我们不难发现有一些相似的东西,比如比较的部分,我们发现不管是比较Max还是Min,Compare模块的符号都不变,都是默认的大于号,唯独不同的Router模块要处理的信号选择不同,对于Max来说,相应的Router模块是处理上面输出的信号,而对于Min来说,相应的Router模块是处理下面的信号,所以我们可以来搭建一个通用的Macro。(图1.8)
图1.8
没错,图1.8所示的就是一个通用的Macro,它就是一个实实在在的比较信号来路由信号的Macro,你甚至可以把它当做就是一个全新的模块来看,这样我们就可以对图1.7的搭建进一步整理了。(图1.9)
图1.9
图1.9的搭建显然比图1.7的好太多了,一看也比较明了,当然这里用到了QuickBus,相比连线来说,它更像是一种无线连接,这样做的目的是防止连线一多,整个搭建看起来会显得很复杂,当然你硬要用连线也可以,不过最终我还是会建议用图1.9的方式来做,反正慢慢你就会发现这样做的好处了。
当然很多人会问为什么不把Latch的部分也放到那个通用的Macro里呢?其实要那样做也可以,只是我个人觉得如果很多东西都放到一块的话,对于理解信号处理的直观性会有所影响,因为可读性是很重要的,所以我觉得这样我一下子就能知道信号是怎么处理的,保持搭建的可读性又能重温对信号处理的理解,这点对每个搭建者来说都是非常重要的。
第二天:
如果你只是单纯地沉溺在Clipper这么一种处理,那么我只能说你太小看Core Cell的能力了;实际上Clipper还可以延伸出很多变化,比如Mod. Clipper。(图2.1)
图2.1
可以说Mod. Clipper是Clipper的另一变种,它的本质跟Clipper是一样的,不同的是Clipper用两个可控的参数来截取信号,而Mod. Clipper则只有一个可控参数M,通过M来对信号做新的Clipper效果,也就是说Mod. Clipper的M既是Clipper的Max又是Clipper的Min,但显然这样的理解是有一定的错误,如果这么认定的话,M既控制Max又控制Min,可M只是一个数值的变化,那么岂不是说Max和Min都是一样地被M操控着,因此结果就是只是单向的Clipper效果;当然Mod. Clipper模块的变化就在于这个M,尽管只是一个参数,但它能起到的功能性可不小,实际上Mod. Clipper的运作原理是M的绝对值以及绝对值的相反数将分别作为Max和Min,这样我们就能知道Max和Min是互为相反数的,因此我们很快就能在之前搭建好的Clipper模块的基础上搭建出Mod. Clipper。(图2.2)
图2.2
到这里你可能认为已经结束了,但Mod. Clipper的M不单单只是Clipper的作用,它能还起到改变输入信号的相位,也就是说当M为正值的话,输入信号不变,而当M为负值的话,输入信号将反相,反相的数学表示就是把信号乘以-1,所以说我们还要对M数值变化做判断;到这里不知道你们有没有注意到,我们之前搭建的通用Macro又起到关键性的作用了,我们可以利用通用Macro再来实现M正负值的结果。(图2.3)
图2.3
对图2.3已经完成了Mod. Clipper的搭建,当然我们可以把M处理的那部分再整理一下,比如利用Macro把它们包含在一块,并命名为Mod。(图2.4)
图2.4
好吧,在这里我们歇一会儿,来回想一下前面的搭建,可能你会认为我为什么要去模仿一个Reaktor本来就有提供的模块,这样做有必要吗?显然是没必要,因为Reaktor就有提供了,因此我们也真心不需要再去复制一个一模一样的模块,但我们还是这样做了,在前面我已经说了,Core Cell跟Primary Level那些模块不同,它的灵活性更好,比如如果想再次对Clipper或者Mod. Clipper做不一样的变化,显然对于Reaktor原有的模块是没办法的,除非你去改变代码,当然Reaktor并不是一个开源的项目,可有人说我可以再利用原有的那些模块去创建一个Clipper模块,这样不就可以了吗?这点我赞同,我也相信你能去做出一个Clipper模块出来,但相比Core Cell来说,Core Cell更是一个明智的选择,甚至你可能会觉得它更方便,你的所有操作所有处理都在Core Cell里完成,并且你更容易修改,比如你可以再对Mod. Clipper的最终信号乘以M,当然这只是一个简单的举例,至于它的可调性,真心不是Primary能比拟的。
除了Mod. Clipper,Reaktor还有一种选择性通过信号的处理方式,它叫Chopper,Chop有砍断截断的意思。(图2.5)
图2.5
同样它也有跟Mod. Clipper一样的M参数,除此之外,它还多了一个X参数,而它的运作原理是当M大于0的时候,信号通过,并且信号的大小将由X来控制,而当M小于等于0的时候,信号通过,并不受X控制;所以对于M来说,它不像Mod. Clipper的M一样,它只负责判断是否大于0来路由信号,不用再去乘以输入信号,而X则成了最关键的一步,因此我们可以很清楚地搭建出Chopper模块出来。(图2.6)
图2.6
这里值得一提是Router,它是我们之前通用Macro变化得来的,其实它就相当于Primary Level的Distributor信号路由模块,根据位置点不同把信号输出到不同的输出端,Router同样也是这样的作用,判断M是否大于0,来路由输入的信号。(图2.7)
图2.7
所以,相比Clipper和Mod. Clipper,Chopper貌似更简单些,但正是因为这种简单的运作原理,简单的搭建可以让我们更有发挥的余地,甚至你也可以再变化出一个叫Mod. Chopper的模块出来,比如这样子。(图2.8)
图2.8
尽管看上去没有多大差别,但事实上信号的处理已经发生了本质上的变化了,关键的是对于原有的Chopper,我们是没办法对它二次处理的,但现在我们通过Core Cell可以自己搭建一个完全一样的Chopper,重要的是我们可以去改变它的规则,这才是我们所要做的,也是Core Cell的用意。
第三天:
Chopper模块能给信号带来不一样的效果,但我们也看到了它跟Clipper、Mod. Clipper还是有本质上的不同;如果让我再想一个跟Chipper有相似的模块,那么我想到的肯定是Mirror模块。(图3.1)
图3.1
Primary Level为我们提供了两个Mirror模块,它们分别是Mirror 1和Mirror 2;而Mirror 2是Mirror 1的升级版;它们可以说跟Chipper是非常相似的,不同在于Clipper舍弃了超出范围的信号,而Mirror则重新利用超出范围的信号,并把超出范围的信号像镜子一样折射回来;那么问题来了,怎么把信号折射回来呢?可能有些人会想到Wrap的效果,但注意Wrap并不会把信号反相,而只是把信号折回到一个特定的范围内,并不断地重复。
其实要把一个信号反相过来很简单,我们知道只要把要反相的信号乘以-1即可,同理对于Mirror也是如此,但现在的问题是假设我们正要处理是一个正弦波信号,那么要反相整个信号是很简答,可我们要反相是超过某个特定数值的信号,比如说大于0.75的信号要怎么给它反相呢?(图3.2)
图3.2
事实上要计算图3.2是非常简单的,我们只要把要反相的信号先折回到从零开始的信号,然后再把其信号乘以-1并加上临界值即可,也就是我们刚刚提到的Wrap折回是有用的,所以如果针对Mirror 1模块来说,我们可以给它列出一个计算公式。
If( In > Max ):Out = (In - Max)*(-1) + Max
If( In <= Max ):Out = In
从计算公式我们可以知道,首先我们还是要先分离信号,让大于Max和小于等于Max的信号分离开来;通过上面的Clipper搭建,分离信号对我们来说已经是一件熟到不能再熟的技能了。(图3.3)
图3.3
上面就是一个完整的Mirror 1模块搭建,同理我们也可以去实现Mirror 2模块,我们只需要对小于等于Max的信号进行Min镜像操作即可。(图3.4)
图3.4
可实际上图3.4最终处理的信号跟原有Mirror 2有所不同,我们可以来看看原来的Mirror 2模块和搭建后的Mirror 2模块最终信号的对比图。(图3.5)
图3.5
从图3.5的信号对比,我们可以看到跟原有Mirror 2差别在于缺少了超过Max信号的折射还有Max和Min中间没有被处理的信号,那为什么会这样呢?究竟是什么原因呢?难道是我们搭建的有问题;可事实上我们搭建的是没有任何问题,无论从计算还是逻辑上,我们的搭建都没有问题的,那么为什么还是会跟原来的有差别呢?其实如果仔细分析图3.4,我们会发现问题其实是出在Merge模块上,在之前我有稍微提到Merge这个模块,但其实在使用上还是要有所考虑,尤其是处理多个同时到达的信号,因为Merge模块无法同时输出多个信号,针对多个同步信号,它只能输出它最底下输入端到达的信号,从图3.4,我们无法避免的是小于等于Max信号的那部分处理,因为无论你信号是否开始路由了,后面那一串计算反相信号始终都有信号输出,因为Min输入本身就是个声音信号,所以这样会导致Merge不是根据路由信号来逐个处理,而是同时处理两个信号,所以Merge最终输出的信号是它最底下那个输入端的信号,既然如此,我们应该要怎么做才能避免这种情况的发生呢?
因为我们知道路由信号是为了更好地、单独地去处理各自的信号,只有当信号发生了才能开始进行一系列的处理,讲到这里,似乎有些开始明白了,好像之前有讲到一个模块能解决我们这个问题... ....(此处停顿思考),没错它就是Latch模块,我们前面已经知道Latch的功能性,只要当有事件触发才能读取被保存后的数值,而我们这里恰好是要对Max和Min进行这样的处理,把Max和Min的数值信号先存储起来,然后等到路由信号有发生的时候才开始调用,这样一来我们就可以避免Max和Min信号没必要的干扰了。(图3.6)
图3.6
上面那些计算模块都是Latch模块变化过来的,而且从上面我们也发现了一些共性的东西,比如后面计算的部分可以通过Macro来整合,然后命名为(x-a)*b+a。(图3.7)
图3.7
注意上面b输入有一个值为-1的QuickConst,实际上这是给b输入赋一个初始状态的数值,当然你也可以到这个Macro外的输入端添加QuickConst,最终效果都是一样的,好的,既然这样,我们就可以重新整理搭建结构。(图3.8)
图3.8
第四天:
通过对Mirror模块的搭建,我们对Clipper有更进一步的理解,并且我们有发现这些模块之间有些共性的东西存在,也就是说它们有一般式,然后从一般式扩展到特殊式,我想细心的朋友会发现Mirror 1实际上是Shaper 1 BP模块的特殊化,Mirror 2是Shaper 2 BP模块的特殊化,所以从Mirror 1和Mirror 2的搭建,我们可以把它们发展到一般式。首先我们来看看Mirror 1的一般式Shaper 1 BP(图4.1):
图4.1
从图4.1我们可以看出来,其实它跟Mirror 1是一样的,只是在这里,我们把参数的名称变了,然后多加了个Sl的参数,同样的我们也能知道了Mirror 2的一般式Shaper 2 BP。(图4.2)
图4.2
虽然Shaper 2 BP能在Shaper 1 BP的基础上帮我们更丰富信号的变化,但它还有另外一种延伸,叫Shaper 3 BP,它补充的功能就是分别放大或缩小正负信号。(图4.3)
图4.3
我们也看到了Shaper 3 BP跟Shaper 2 BP的区别在于多了Sl+和Sl-这两个可控参数,可实际上除此之外,Shaper 3 BP和Shaper 2 BP还是有本质上的不同,我们可以来看看Shaper 3 BP最终的搭建结果。(图4.4)
图4.4
注意Shaper 3 BP是先计算大于Bp1的信号,然后整合信号再计算小于Bp2的信号,这跟Shaper 2 BP显然是不同的,Shaper 2 BP也是有计算大于Bp1的信号,只是它没有马上整合信号,而在小于等于Bp1的信号再计算是否有小于Bp2的信号,最终再来整合;从操作上来讲的话,对Shaper 3 BP而言,就是Bp1的变化会受到Bp2的影响,而对于Shaper 2 BP,Bp1的变化不会受到Bp2的影响,因此最终的信号完全就不同了,还有最后Shaper 3 BP还对整合后的信号的正负再次放大或者缩小;尽管它们可以说已经是完全不同的信号处理了,可对于搭建的角度来看,它们是很多相同的地方,因为用到都是相同的模块来搭建,只不过信号路由和处理的方式不同了。
当然Reaktor除了提供这种相对比较复杂的信号整形模块之外,它也有提供几个比较简单的,像Shaper Parabolic、Shaper Cubic。(图4.4)
图4.4
上面这两个模块的运作原理相比上面搭建的那几个就简单多了,可以说就是在套公式,给你个数值,你马上就能算出结果,甚至能也马上写出公式表达式出来,我们假设Lin等于a,Par等于b,Cub等于c,然后In等于x,Out等于y,那么:
所以通过上面的表达式,我们可以非常轻松地在Core Cell搭建出Shaper Parabolic、Shaper Cubic这样的模块出来。(图4.5)
图4.5
第五天:
如果你觉得前面搭建的Shaper效果还无法满足你,那么接下来这个模块,我认为它是有必要让我们对它了解并掌握,它就是Sample + Hold。(图5.1)
图5.1
Sample Hold(为了方便讲述,我省掉了“+”符号)的运作原理不难理解,它有两个输入,一个叫Trig,另外一个叫In,In输入不用说了,肯定是连接要处理的信号,而Trig呢?其实从字面上来看,我们可以知道它其实就是Trigger,即触发的意思,也就是In输入的信号是否能在Out输出取决了Trig这个输入,那么Trig应该是什么样的信号才会让In输入的信号通过呢?如果你去看关于Sample Hold的文档描述,你会知道Trig只有在当信号大于0的时候才会触发In信号输出,假设我们连接一个正弦波信号到Trig输入,那么我们可以知道Trig的正负信号各占一半,那是不是当正弦波信号处于正值的时候,Sample Hold的In输入信号就一直通过呢?我们来用数字表示看看,比如Trig输入是一个频率为1Hz,然后周期样点为512的正弦波,那么大于0的就占有256个样点,如果把一个样点当做一个事件来统计的话,Trig信号大于0的就是有256个事件,倘若这256个事件同时能让In输入信号通过的话,那么In信号在Out输出将停留256个样点的时间,如果按一秒44100个样点来计算的话,停留的时间将会是256/44100,即0.0058秒。
上面只是我们在猜测,但实际上Sample Hold并没有在Trig信号大于0的情况下让In输入信号一直都通过,而只是在Trig信号每次大于0的时候只触发一次让In输入信号通过,所以如果我们要利用Core Cell去搭建一个Sample Hold模块,我们需要对Trig信号判断。(图5.2)
图5.2
上面我们都是用到了之前就有用到的模块,连接Trig是用来判断信号是否大于0并路由信号,而下面是Latch模块,第一个输入用来连接要处理的信号,第二个则用来触发让信号通过,我们把判断路由后的信号连接到Latch的第二个输入,表示当Trig信号大于0的时候,Latch模块就会让In信号通过,可到这里,它还不是一个真正的Sample Hold,因为Trig判断后的信号有可能一直持续不停,只要是一个正值信号就可以了,所以我们应该怎么做呢?其实仔细想想要让一个正负信号在经过0后只触发一个事件不是一件很困难的时候,我们可以让大于0的信号变成一个恒值信号即可,比如说大于0的信号变成1,而小于等于0的信号变成-1,这样我们可以得到一个方波,但仅仅这样还是不够的,尽管变成了一个方波,可方波的正负信号都有可能持续一段时间,尽管数值是一样,但是事件仍然在触发,所以为了防止相同数值的事件,我们需要过滤时间,这就是需要比较两个事件是否一样,例如当前数值事件为1,而前一个数值事件为2,那么它们不相同,我们就可以让事件通过,倘若前一个数值事件也是1的话,那么事件就不通过,因此我们需要一个过滤事件的模块。
事实上想要得到一个过滤事件的模块很简单,Reaktor有帮我们提供,它叫Dup Flt,不过我们也可以自己来搭建一个。(图5.3)
图5.3
可能有些人一开始没看没明白,毕竟这里用到了Read和Write这对搭档模块,首先我们看到了输入连接到了四个地方,Read的输入,Write的输入,Compare的第一个输入,Router的输入;首先很多人会认为这里的比较是没有意义的,因为输入一直在跟自身比较,无论何时,输入跟Read模块输出的数值都是一样的,所有这里永远没有任何事件输出;如果你也是这样理解的话,那么我只能说你对Read和Write这对搭档模块还不是很了解,我们也看到了Read和Write除了有我们经常看到的输入类型,它们还有属于自己独特的数据类型,它叫OBC,全称叫Object Bus Connection,我们看到不管是Read还是Write模块都具备这样类型的端口,那么它们的意义在哪里呢?如果你是Read连接到Write的话,那么说明Read模块处理在Write之前,也就是说它会先读取内存块里的数值,然后再写入,相反则先写入再读取,那么这样会导致一个什么结果呢?如果是先读后写的话,信号处理会延迟一个样点,如果是先写后读的话,则信号处理照旧;所以现在你应该可以明白为什么我们上面的搭建要先读后写了,这样输入信号的数值每次就会跟之前的数值进行比较,一旦它们不相等的话,事件就会被触发,因此这样就可以帮我们避免了事件重复的可能。(图5.4)
图5.4
如果你认为到这里已经结束了的话,那么我只能说你忽略了最细节的东西,尽管上面我们已经控制了重复事件的发生,但我们也知道Sample Hold只有大于0的时候才触发一个事件,而小于等于0的则没有任何触发事件,可图5.4的Dup Flt依然会产生两个触发事件,原因就在于小于0的部分也在触发事件,所以我们还要再做一次比较,让大于0的能触发事件即可。(图5.5)
图5.5
上面我们利用之前搭建好的比较路由信号模块,将Dup Flt模块产生的数值1事件和数值-1事件分离,而我们最终想要的是数值1事件,当然个人觉得这里我们也可以逆反Sample Hold的原意,让小于0的来触发也可以,所以如果你把数值-1事件连接到Latch模块最底下的输入也能达到Sample Hold的效果,只是说在本意有发生变化的,当然有时候也会对最终结果造成不同,如果是在处理一些比较复杂的信号,那么用大于0的来触发和小于等于0的来触发是完全不同的,所以这里又展示了Core Cell的可塑性了,它可以随时根据我们的不同需求来进行一系列的改变,只要你够有想法。
可下载Midifan for iOS应用在手机或平板上阅读(直接在App Store里搜索Midifan即可找到,或扫描下面的二维码直接下载),在iPad或iPhone上下载并阅读。
转载文章请注明出自 Midifan.com
-
2016-12-06
匿名
此人很懒,懒到只做了个表情而没有说话。