# How to control a Synced LFO?



## audiothing (Jan 18, 2012)

Hello,
I have problems trying to control a synced LFO (16th) with a slider with 16 steps. I just need from 1/16 to 16/16 values.

I searched the forum and found this thread: http://www.vi-control.net/forum/viewtopic.php?t=22207
so I downloaded Math Library but can't find the exact function to convert from "Hz" for LFOs.

I thought it was just a matter of finding the right use of $DURATION_SIXTEENTH but maybe I did something wrong.

Any suggestion?

Thanks in advance!


----------



## mk282 (Jan 18, 2012)

You've hit probably the most irritating part of Kontakt. Dealing with tempo synced parameters. It's a BITCH. It also doesn't have absolutely any relation to $DURATION_SIXTEENTH and similar parameters.

So, here's what happens.


You know how every knob in Kontakt is plotted in the range from 0 to 1000000? Right. This is also valid for tempo synced knobs. HOWEVER. Notice that Kontakt has a very weird method of selecting which tempo sync value you're actually going to use.

First you have to select a timebase. You do that by clicking on the "Hz" label, and selecting one of available timebases. Let's say you select 32nd note here. The knob will change its displayed value to "x.0" format, where x is the multiplier of the timebase. So, if you have it selected to "2.0" when using 32nd note timebase, you get a 16th note. Etc.

Here's the catch. When you set the knob to any of the tempo synced values (say, to "2.0" for this example), the *engine parameter value assigned to that value CHANGES DEPENDING ON TEMPO!* So there is no constant value that you can use, say in an array to be swept by a slider, or something! "2.0" at 60 BPM is one engine parameter value, "2.0" at 240 BPM is a totally different engine parameter value! And that, my friend, SUCKS HARD.

There's is a very complicated workaround for this, it involves creating empty groups that are named "1.0", "2.0", "4.0", etc., depending on the values you want to use, then comparing the get_engine_par_disp() of the knob with the group name, then incrementing or decrementing the current knob value to get to the actual value you want to display.

I think this is an almost impossible task to put into Big Bob's math library, because every tempo sync timebase behaves differently, and it behaves differently on LFO/Envelope times vs. effect delay times. AND the engine parameter values CHANGE for every 0.01 BPM increment! Since Kontakt supports a tempo range from 20.00 - 400.00 BPM, that's... 38000 DIFFERENT engine parameter values FOR JUST ONE VALUE USED (in this example, "2.0")!!! Multiply that with the number of timebases, and that with actual number of different values available in every timebase, and you get to a mind boggling number of different engine parameter values.

NI has to make this easier somehow. It's currently extremely inefficient and ridiculous, even though the workaround exists. But it's stupid.


----------



## audiothing (Jan 19, 2012)

Thank you very much for the reply. I'll try to see if the group name trick works for me. Thanks


----------



## UCAudio (Jan 24, 2012)

I'm trying to understand this empty group workaround method. Does anyone have an example script showing how this works?


----------



## mk282 (Jan 26, 2012)

Here's an example NKI.


http://www.mediafire.com/?lzvlseukx8bcpq7


----------



## UCAudio (Jan 26, 2012)

Thanks a lot. I'm going to check this out tonight when I'm home from my day job. 

What about scripting to control a non sync'd lfo, but doing some math to figure out the exact frequency (hz) needed to set the lfo in sync with the project bpm for certain divisions? Would that work, or would it not be precise enough to really sync to the project?


----------



## mk282 (Jan 26, 2012)

That would not be precise enough (you need precision of as many decimals as possible, and Kontakt's LFO for the most part has precision of just one decimal), so no.


----------



## UCAudio (Jan 26, 2012)

Ahhh, sucks. Sounds like nobody knows if NI has updated this in the K5 KSP so we can control sync'd LFOs.


----------



## polypx (Jan 26, 2012)

You can control sync'd LFOs, you just can't do 16 16ths in 16 steps and have it work at all tempos.

You could mirror Kontakt's own weird behaviour by using a slider of a million steps, and then just get the value that Kontakt creates for your display.


----------



## mk282 (Jan 26, 2012)

polypx - my workaround using group names works with all tempos...



UCAudio @ 26.1.2012 said:


> Ahhh, sucks. Sounds like nobody knows if NI has updated this in the K5 KSP so we can control sync'd LFOs.



I know. They did not. You can only use the method I outlined in that NKI file above, if you want to control tempo synced LFOs in all tempos correctly. I don't know of any other way.


----------



## polypx (Jan 27, 2012)

> polypx - my workaround using group names works with all tempos...



Yes, I tried it. That's really clever!

But I get the feeling that it's too complicated for some people. So I my suggestion is using a standard EP slider and letting Kontakt be Kontakt -- a sort of lazy way to deal with the problem.


----------



## mk282 (Jan 27, 2012)

True, that's always an option!


----------



## UCAudio (Jan 27, 2012)

Polypx - can you elaborate on using a standard slider and letting kontakt be kontakt? I'm not sure I follow. Basically, I'm trying to code for a simple synth. 16 groups... each contains an oscillator sample. I want to be able to assign sync'd lfo to modulate volume or pitch for each of those so the LFO would lock in at a whole note, half note, quarter note... etc. How would you approach that?


----------



## polypx (Jan 27, 2012)

In your init, declare the slider to have a million values (all Kontakt EPs are a million values).

declare ui_slider $Speed (0, 1000000)

set the LFO to be a sync LFO to the minimum value you need, let's say a 16th note

then in the ui_control section you can set the speed, the same as if you were editing the Kontakt instrument

set_engine_par($ENGINE_PAR_INTMOD_FREQUENCY,$Speed,$group_number,find_mod($group_number,"LFO_RECT"),-1)

and also get the value from it to display somewhere in your interface, here I use a label named $label to display the number of 16ths set by the slider:

set_text($label, "Speed: " & get_engine_par_disp($ENGINE_PAR_INTMOD_FREQUENCY,$group_number,find_mod($group_number,"LFO_RECT"), -1) & " " & "16ths")


----------



## Blackster (Jul 26, 2012)

Hi mk282,

that is a very cleaver workaround! I have tried to solve this synced-lfo problem for hours, even days now ... but could find a solution. 

Let me just shoot a quick question concerning your way of doing it: Why does the values have to be written in group names? Why not to use constants instead?

Cheers,
Blackster


----------



## mk282 (Jul 26, 2012)

Because KSP in Kontakt cannot compare string arrays, unless you compare a string with group name. It's stupid, really. You simply cannot do:

if (get_engine_par_disp(...) = !text[$pointer])
...do something...
end if

Our lives could have been much easier if this would've been possible. But, no!


----------



## Blackster (Nov 12, 2013)

Hi mk282,

is it possible to renew the new to your example nki file? I'd like to have another look at it for reference. Unfortunately I don't find the file anymore.

Thanks a lot!


----------



## mk282 (Nov 13, 2013)

Mayhaps somebody else here who downloaded it can re-up, I seem to have lost the file as well. :/


----------



## scottfraser (Nov 13, 2013)

Does the use of group names allow for KSP to compare strings. Not sure I follow. I thought KSP could not perform string compares.


----------



## mk282 (Nov 14, 2013)

Yes, you can compare group names with text.


if (group_name(0) = "test")
<do something>
else
<do something else>
end i


----------



## scottfraser (Nov 14, 2013)

That is neat.
Is this possible?
if ( !guittype[1] = group_name(1) )

<do something>
else

<do something else>
end if


KSP gives a boolean expression expected error.
Is it not possible to compare a string variable or a string array with a group name or another string variable?


----------



## mk282 (Nov 14, 2013)

Not sure if it works with string arrays, but it does work with literal strings.


----------



## nosfoe (Nov 14, 2013)

Yes, this also works with string arrays, I've used it. For example, create a group in a Kontakt instrument called "my_group" and run this script. Don't make it the first group though, since Kontakt will then return a zero (which is the group index of the first group). That could be unhandy because it also returns a zero if your string is not found at all...


```
on init 
	declare !string_array[3]
	!string_array[0] := "my_group"
	declare $group_idx
	$group_idx := find_group(!string_array[0])
	message ("The group index is: " & $group_idx)
end on
```


----------



## mk282 (Nov 14, 2013)

Yes. That's why your first instrument group should always be a dummy empty group in case you want to use this way of comparing strings.


----------



## scottfraser (Nov 14, 2013)

Still getting KSP error boolean expression expected when I try this:

on init 
declare @stringval 
@stringval := "harp"

if (@stringval = group_name(1))
message(@stringval)
end if

end on 

Am I on the right track? Seems strange KSP does not have much in the way of string manipulation.


----------



## Andrew Aversa (Nov 14, 2013)

Don't forget lookup tables! Finding the EP value for every sync type at every tempo (say 40-180bpm) and then putting those in big honkin' arrays


----------



## Big Bob (Nov 15, 2013)

scottfraser @ Thu Nov 14 said:


> Still getting KSP error boolean expression expected when I try this:
> 
> on init
> declare @stringval
> ...



The reason for the compiler flag is that you are still trying to compare one string against another.

To use the Group name trick, you would have to recast the above like this:


```
on init 
   declare @stringval 
   @stringval := "harp"
   
		if (find_group(@stringval) # 0)
			message("There is a group with the name in @stringval")
		end if

end on
```

If there is a group with the same name as what is in @stringval, it's index can be obtained using find_group(@stringval) as exemplified above.

Rejoice,

Bob


----------



## Blackster (Nov 15, 2013)

I need (once again) a little help with that comparison track. Could anybody please tell me why this isn't working? The error is in the while-line. I assume it's because of the string in get_engine_par_disp() ... right? ... But how do I apply this trick in order to sync the speed to my group name? Thanks again! 


```
$i1 := 1000000
        while (get_engine_par_disp($ENGINE_PAR_INTMOD_FREQUENCY,$group_index_dry,find_mod($group_index_dry, "LFO_MULTI1_MOD"),-1) # find_group("1.0"))
            set_engine_par($ENGINE_PAR_INTMOD_FREQUENCY,$i1,$group_index_dry,find_mod($group_index_dry,"LFO_MULTI1_MOD"),-1)
            set_engine_par($ENGINE_PAR_INTMOD_FREQUENCY,$i1,$group_index_dry,find_mod($group_index_dry,"LFO_MULTI2_MOD"),-1)
            $i1 := $i1 - 100
            wait(100)
        end while
```


----------



## scottfraser (Nov 15, 2013)

Big Bob @ Fri Nov 15 said:


> The reason for the compiler flag is that you are still trying to compare one string against another.
> 
> To use the Group name trick, you would have to recast the above like this:
> 
> ...


Big thanks, Bob.

Very clever workaround.
o-[][]-o


----------



## scottfraser (Nov 15, 2013)

Blackster @ Fri Nov 15 said:


> I need (once again) a little help with that comparison track. Could anybody please tell me why this isn't working? The error is in the while-line. I assume it's because of the string in get_engine_par_disp() ... right? ... But how do I apply this trick in order to sync the speed to my group name? Thanks again!
> 
> 
> ```
> ...



Blackster,
should the solution use group_name and not find_group? 
Instead of this type of setup could you not have a number of lfo's(one for each rate) and then just unbypass the one you want via a script and bypass all the others via a script? Could get quite busy with lfo's I suppose.


----------



## Blackster (Nov 15, 2013)

Hi Scott,

thanks for your reply. Basically, you are right about using dedicated lfos. But that's no option for me since I have lots of internal modulation going on and the number of mods you can use in Kontakt for one parameter is limited. I'm hitting the limit before I get what I want  ... I have already tried that workaround. 

Yeah, I'm aware that group_name() returns a string and find_group() returns the index of the group. But the thing is that I always get the same error message (also when I use group_name()). I guess that while() expects an integer instead of a string.


----------



## Big Bob (Nov 16, 2013)

scottfraser @ Fri Nov 15 said:


> Blackster @ Fri Nov 15 said:
> 
> 
> > I need (once again) a little help with that comparison track. Could anybody please tell me why this isn't working? The error is in the while-line. I assume it's because of the string in get_engine_par_disp() ... right? ... But how do I apply this trick in order to sync the speed to my group name? Thanks again!
> ...



Actually, the problem here is you are trying to compare a string (from the get_ep expression) against an integer from find_group. You would have to recast your while statement so that the get_engine_par_disp expression is inside the find_group function.

For example, if the group named "1.0" is group 1, then you could use:


```
while (find_group(get_engine_par_disp .....  ) # 1)
{ etc, etc }
end while
```

Rejoice,

Bob


----------



## Blackster (Nov 16, 2013)

Hi Bob,

thank you very much! I got it now and it works fine - YIHAA!  ...


----------



## patchen (Aug 12, 2014)

Hi there,
I'm new to scripting and am really just trying to wrap my head around it. Can someone possibly post an example script of mk282's group names workaround to control synced lfo's/delays? 

The old link is dead and I haven't been able to find any other reference to it here or on the googles. 


I'd be very grateful!


----------



## patchen (Aug 21, 2014)

So I've got the workaround to work from hints I've gleaned on this thread, I'm using a loop to increment delay times until they match the display value. BUT try as I might, I can only get the loop to run one direction which seems to cause the CPU to spike pretty badly! I can't get the delay time to decrement if > or increment if <. If tried several different ways of doing this and several days staring at the computer screen with no luck..... Hopefully somebody could chime in with a little advice??

Here is the code for the loop function I'm using:

while 
(find_group (get_engine_par_disp(ENGINE_PAR_DL_TIME, -1, 0, 0)) # (Dl_Slider / Dl_Mltplr) + 1 )
message ("")
set_engine_par(ENGINE_PAR_DL_TIME, Delay_Time_Counter * 100, -1, 0, 0) 
inc (Delay_Time_Counter)


if (Delay_Time_Counter > 10000)
Delay_Time_Counter := 0
end if

wait (100)

end while


----------



## patchen (Aug 21, 2014)

Also it turns out that if I try to link another LFO's rate to this Master LFO by indexing it's Ep it only works up to a length setting of 3 bars, after that they drift. This does not make sense because I'm matching the 2 Engine parameters together yet they do not match. Pretty confusing/frustrating. 


L1LF1Time_ep := get_engine_par(ENGINE_PAR_INTMOD_FREQUENCY, 0, 1, -1)

set_engine_par(ENGINE_PAR_INTMOD_FREQUENCY, L1LF1Time_ep, 0, 2, -1)


^^In this case the 2 Eps SHOULD be equal, but they are not.....

EDIT---Looks like the answer to this part of the problem lies in BigBobs 'GetTrueEp' function.


----------



## patchen (Aug 22, 2014)

After messing around with BigBobs "GetTrueEP" function I'm wondering if it couldn't be modified somehow to work to control a Synced LFO/delay. Anybody have any thoughts on this??


----------



## patchen (Sep 19, 2014)

patchen @ Thu Aug 21 said:


> So I've got the workaround to work from hints I've gleaned on this thread, I'm using a loop to increment delay times until they match the display value. BUT try as I might, I can only get the loop to run one direction which seems to cause the CPU to spike pretty badly! I can't get the delay time to decrement if > or increment if <. If tried several different ways of doing this and several days staring at the computer screen with no luck..... Hopefully somebody could chime in with a little advice??
> 
> Here is the code for the loop function I'm using:
> 
> ...



Can anybody possibly chime in on this? Still haven't found a solution. Big, BIG thanks if you can help!!


----------



## Big Bob (Sep 19, 2014)

Hi Patchen,

First off, let me say up front that I have zero experience using Kontakt's LFOs, synced or otherwise, so I doubt that I could be any help to you in that department :D 

I might be able to help you with getting your servo-loop to null properly but you haven't posted enough information for me to analyze.

1. I would need you to post the list of names you are using for your groups in order to know what compares can produce hits (compared to the universe of DL values that Kontakt might display).

2. You need to specify how the variables named Dl_Slider and Dl_Mltplr are set and what range of values they can assume. 

3. Are these values fixed for a given execution of your loop or can they change during the waits?

4. What does the Delay_Time_Counter start out as when you enter the loop? Maybe just the value it left off with the last time the loop exited? (if it ever does :? )

Rejoice,

Bob


----------



## patchen (Sep 26, 2014)

Thanks for your reply Bob, PM sent. If anybody else is interested here are a couple example files of the way I am doing it currently. One for LFO and one for Delay control

https://www.dropbox.com/s/gsfx5g01lon2z ... s.zip?dl=0


----------



## Big Bob (Sep 26, 2014)

Sorry but for some reason, the forum didn't send me the usual email to inform me of your PM but I have finally responded with a PM this morning.

Hopefully someone else can chime in here to try to help you with this.

Rejoice,

Bob


----------

