Collaborate on building Reaktor filters in Gen?
For Reaktor 5 oweers: would anyone be interested in collaborating on rebuilding Reaktor CORE filters in Gen?
Hi Ernest,
I would be very interested... what exactly do you have in mind?
- Luigi
FWIW, lots of work in this area has already been covered by Peter McCulloch, Oli Larkin, and others, which might provide some helpful starting points.
Thank you for the replies. I have seen a couple of GEN filters, but not implementation of the Reaktor filters. I was thinking of starting with the filter in 2-osc, Reaktor version 5.9. Then thre are a number of other core filters in 5.9. I'm open to suggestion on how to divvy up the parts.
Looking through this one, I think I can get it working and post it next week. Maybe someone else would like to create the other 2-pole SVF types, and we could build a library of different filters together.
hi there.
a couple years ago i spent quite a while doing lots of reaktor-core -> to -> gen~ porting.
attached is a zip of some of the filters i found in my reaktorports folder.
warning 1: not all of reaktor-core is that good, and the code ideas are quite oldschool. although there is more than we get in max ;-) .
warning 2: i have not opened these in a while, they are not heavily tested, and a bit rough around the edges, and also i could not resist redesigning them a little it seems. and they could very easily be cpu-usage improved.
i never ended up using these ones, but hopefully these are helpful for a start, anyway. at least all of the svf's are a hell of a lot better than the terrible quality single [svf~] we have in msp ;-)
it is really quite easy porting reaktor-core to gen~ - the key is to thoroughly understand the reaktor-core latching / bus / rates system (gen~ is a hell of a lot simpler and easier, although in gen~ you must understand the signal / param rate system, even though it does try to do everything for you automatically).
hth.
Well thank you. I see you wrote code :)
Im not entirely certain how many of the parameter inputs will need to be latched in by a clock every cycle, but I did start building the 2osc svf (3x oversampled with crossfade and saturation) in gen objects. This is as far as I got so far, its just a beginning.
Ah. Well the behavior has certainly changed. In more recent gen versions. I can send a parameter into a gen subpatch by using 'setparam' on the subpatch input. So it is possible to pass params into subpatches. However I am still not sure if a param gets converted into a signal which is recalculated every clock when it passed through a send/receive pair.
Also, I am not sure if a param gets converted into a signal that is recalculated on every clock signal if it passes through a signal input of a gen subpatch.
ok, I will pass the params into gen subpatch objects with setparam and use send/receive terminals to transmit them then. Thanks.
params can indeed be inside subpatchers and abstractions in gen~, and indeed can be passed with 'setparam' as ernest points out. and yes they remain params if passed through inlets / outlets internally in the gen.
once a param passes into gen it is a lower resolution signal 'based on' vectorsize. it continues to be at param rate until it stops being a param ;-) . meaning, as soon as it is mixed / used with a signal rate calculation. this means that smoothing (which will involve single-sample calculations via history) or mul with a full rate signal, will create full rate signal.
keeping params at param rate for as long as possible is one of the tricks of keeping cpu usage down in gen~. the rule is, if in doubt, make it param rate.
coming from reaktor-core, the idea of "clock" is confusing. in gen~ there is no clock as such, in that it is an always on one sample audio graph. so much of the crazy reaktor-core patching around clocking can either be ignored or, better still, re-written / re-imagined. i found that exact porting (graphically) is never the way to go. you need to rewrite the algos in gen~ specific language, just as when porting from another language like C or whatever.
for example, ernest, all that calculation you do in your '3x' system is not neccessary in gen~ - you have a much better and more efficient and native solution in the [interp @mode cubic] or [interp @mode spline] object. over-complex interpolation methods are common in reaktor-core, but in gen~ you don't need to copy them.
hth
Ah, that is what I suspected about params but was not sure, thank you. When you talk about interpolation, are you thinking the entire gen~ object would be oversampled? I dont want ti do that,.because I have other many gen~ functions, including ADSR and oscillator objects that i previously shared, to include in the same gen~ object.
hi ernest.
no, maybe i was not clear, sorry.
i meant your [gen @title up3x] and [gen @title down3x] are performing a simple oversampling calculation (for three branches of you svf's = three times oversampled here) and doing so by performing cubic interpolation which in reaktor is done with an insane patched algo. all i was saying is that you do not need this insane patched algo in gen~ - you just need [interp @mode cubic (or spline)]. the 'ladderx3' i posted in my previous zip does this in a much simpler way, but in the 2-osc filter you'd be better using [interp]. was just a suggestion regards cpu etc, that is all. it would be easier to help if you posted a patch.
'resolution' was indeed the wrong word. params use less cpu than signal rate connections. but no, indeed, you wouldn't use them for signal rate modulation. i was just trying to be helpful.
stkr, thank you for talking with me, but I don't actually see there is any significant overhead from what you and Raja said. The calculations for the multiplication coefficients are calculated from constants, for 64-bit accuracy of irrational numbers. That could be replaced, but I don't see any performance benefit in doing so. The coefficient calculations are from constants, and therefore occur only once. The real-time calculation is simply eight multiplies and six adds on the results of three single-cycle delays, so it couldn't really be more efficient, except perhaps for some instruction reordering. Maybe using interpolation objects could be less in count, but the advantage of this method is the actual coefficients can easily be modified, and the actual coefficients are plain in sight. As they don't incur any more real-time processing overhead, it seems the argument against them is pedantic, but maybe I am missing something.
I had one other question. I cant find any gen~ function to invert the sign. Is the most efficient way to invert a sign in gen~ to multiple by -1.0? It iseemns strange to program a multiply function for a sign inversion. Maybe I just missed something.
@ ernest, fair enough!
yes [* -1] is the only direct way, but there are lots of useful operators like [sign] & [change] etc if you are just looking for analysis / info. yes it is weird everything being 64-bit doubles.
i replied to that other thread about managing cpu usage in blocks.
it would be nice if luigi castelli replied as he knows a hell of a lot more than me ;-)
Thanks STKR. The first implementation is available here:
Maybe that's it. How strange. 'neg' does not appear to be in the online help. I will add the improvement with some other optimizations in a bundle.
Well, who wants to do something next?
This is awesome, btw. I'm not good enough at gen to help out yet, I'm afraid.
quick question; there's quite a bit of zipper noise on that otherwise beautifully sounding svf - if I understand gen right this could be solved by using audio-rate inputs instead of the [p $1], [s $1] etc?
Could I trouble someone to make that mod real quick? It's such a nice implementation, I'd love to get to use it.
cheers.
nice job ernest!
regards neg / etc, you can learn a lot about gen~ patching by watching the code compilation sidebar as you patch. i am pretty sure the compiler does not care if you use [* -1], [neg] or [-(something)], the compiler optimises it to same difference anyway.
@wetterberg (hi!), no, there is no magic about signal inlets and zippering. those controls can (but not always in ernest's patch) zipper simply because there is no smoothing on them. one needs to smooth in gen~ just as one needs to do it in msp with line~ / curve~ / rampsmooth~ / slide~ / whatever. there are examples of smoothing functions inside the zipped folder i posted earlier in this thread.
p .
@stkr: dude... hi! I'd love to talk, maybe a google hangout some day? :D
@ernest: thanks man - it's all coming back to me now, hehe. I remember finding myself doing those smoothers for every little thing in gen.
I don't know, I find the inputs more or less unusable until this process has been done to them, at least in... "practical applications", with modulation and all that.
Awesome stuff, again. Keep it up!
You're most welcome.For gaincompensation, I've also uploaded a fast response limiter. The link is now on the same page:
That being said, I think it would be a good idea to add table-based gain compensation for selfoscillation to this filter. While there are table-based gain compensators in the Reaktor core library, they don't work for this filter because it provides mixed 2/4-pole output, rather than switched output, as well as internal saturation. Has anyone made a patch to generate gain compensation tables for filters at different resonance and pitch settings?
An updated version of the SVF is uplloaded.
* Optimized Mixer Control Path
* Encapsulated Filter Object
* Added optional smoother object
If you have contributed to the discussion and wish to have a credit on the web page, please send your name and contact information to the contact form on Yofiel.
Port of Integrator-based zero delay feedback Butterworth low-pass filter(to 8 poles).
Thank you for the link, afw. I did see a Reaktor version was made available on a third party website some time ago, yes.
As mentioned before, please send a note on the Yofiel webserver with your full name and contact information if you wish your filter work to be included in the distribution package. I wont distribute other people's files without such written request.
Here is a performance test of the SVF versus Reaktor without a polyphonic noise source and no modulation. With 16 voices on a 3GHz i7 in standard configuration, Max 64 reports 18%, and Windows reports 5% usage. For an equivalent design in Reaktor, it is 15% and 3%. So it appears gen~ is, at a base level, very approximately 20% slower than Reaktor core. This does not take into account any modulation, which would increase Max cpu load more than Reaktor.
I repeated the test with 200 voices. Max does a better job at palatalization, and while it reports 27% CPU usage, the load was more balanced across CPU cores. However attempting to reopen the patch caused Max to crash. Reaktor uses SSE2 MIMD acceleration on Intel CPUs which provides much smoother polyphonic performance, but it is not so scalable for future multicore enhancement for a single instrument, as it runs on one core only.
HI striker, I did quite a bit of experimentation with code boxes and params, and I think you are right that in code boxes, params are evaluated on vector boundaries. But also, and I could be wrong, I think handling of code boxes is slightly different in code boxes and with ui objects. Outside code boxes, the compiler can scan through the net list and re-evaluate params only when they change; but in codeboxes the compiler can't know whats going on inside loops and conditional expressions, so it re-evaluates all params on every boundary vector.
I tested how params are handled with the gen~ code
Param sel, x1,x2;
if (sel != 0) {
out1 = selector(sel,x1,x2);
}
when setting x1 to a some value and sel to 1, it outputs the value of x1 as expected. However, when setting sel to 0, the output still goes to zero. This means the selector statement is being reevaluated when sel changes, even though the condition should be stopping it.
The following code also does not work as expected:
Param x1, sel;
if (sel != 0) out1 = x1;
When sel is set to 0, the output is forced to zero, instead of staying at the previous value of x1. My conclusions, at vector boundaries, the compiler sets all codebox outputs to zero then re-evaluates the code. But according to the documentation, outside codeboxes, Params are only evaluated when they change, until they reach some object that causes recalculation at signal rate.
This is a longshot, but has anyone ever ported over the ZDF ladder? It'd be really awesome to have something as customizeable as that (with the adjustable highpasses in and outside the feedback loop saturation and variable poles, you know) and despite being a tiny brained weakling who don't know nothin bout programming filters in general, I've also tried to just brute force it by directly copying it (stupid idea, I know) with no luck.
Not asking anyone to work on it, mind you, but if it's already been done it'd be nice to have.
Noah, go to the File menu of Max, choose Show Package Manager and download the smFilterPack. The sm. ladder ~ abstraction realises the original Zavalishin paper Zdf ladder filter. And more.