HOW TO: APPROACHING MODULATION IN KSP
I see people are still struggling with this, after all this time. So I thought I could explain this to everyone once and for all, since it's quite an important thing when doing a custom scripted instrument.
First, let's break things down. Kontakt has two types of modulators - internal and external. Internal are: LFO, envelope, glide, step modulator, envelope follower. External are: pitch bend, aftertouch, MIDI CC, constant, random unipolar/bipolar, etc.
Next, internal modulators have two parts: the modulator itself (represented by its module in the Modulation section of Kontakt's instrument edit view), and the modulator target strip (found at the destination - source pitch, filter cutoff, amplifier volume, etc.). External modulators only have the modulator target strip.
Now, how do we KSP this all? This is where KSP offers two commands: get_mod_idx()
and get_target_idx()
. First one targets either internal modulators or external modulator target strips. Second one targets only (and nothing else but) internal modulator target strips. These commands work by searching for a named reference, the name of a modulator/target strip. Kontakt assigns default names to these, but I find them completely useless, so I heartily recommend everyone to do their own naming.
Here's my suggestion for a clear-cut modulation naming scheme:
- use all caps
- name internal mods either by their purpose (
PITCH LFO
,FILTER ENV
), or number them (LFO 1
,LFO 2
,ENV 1
,ENV 2
). I tend to go with the latter. - name internal mod target strips in such a way that you include the modulator name and an arrow pointing to the target (
LFO 1 -> PITCH
,ENV 2 -> CUTOFF 1
- more on why I used 1 here later!) - name external mods like internal mod target strips (
PB -> PITCH
,AT -> VOLUME
,RANDOM -> STARTPOINT
...)
That should be clear enough for everyone, I think. If you have more than one internal modulator of the same type (say, multi LFO), Kontakt does NOT auto-number them incrementally unfortunately, and this can be a source of a lot of confusion. So make sure you rename them immediately upon instantiating them, and it should all be clearer.
But, how to rename modulators, or see their names in the first place? For this, you have to go to Script Editor, and press the Edit button there to open the text input area of the Script Editor. Now you can see the names of modulators and target strips when you right-click them. You don't have to have the Script Editor open at all times - but you have to have the text input area open. So after opening the text input area, close the Script Editor to give yourself a better overview of the instrument. I also recommend closing the Wave and Mapping Editors, as well as Group Editor, too (use Monitor->Groups tab in Kontakt's left-side browser instead of Group Editor, it's more spacious anyways).
IMPORTANT: In Kontakt 4, you could only rename modulators within one group - the renaming operation didn't pass through other editable groups (for example, when Edit All Groups is enabled), so you had to rename every modulator in every group manually (or do one group, then duplicate it - which is what I tend to do anyways, since batch-renaming things can sometimes lead to unexpected results if your modulators were added at different times and in different order, etc.). In Kontakt 5, when you have more than one group selected for editing, the renaming operation WILL be passed through all those selected groups. There are some situations that are very hard to explain in layman's terms when this doesn't happen, but it's mostly related to the above mentioned unexpected results. When you notice this situation, I recommend duplicating one of the existing groups with fully-named modulators, then pasting samples back into it.
Also, I read somewhere that people notice missing modulators after they've added them, etc. This happens when you select a modulator (it gets a yellow frame around it), then type in the Script Editor, and at some point you press the Delete key on your keyboard. Kontakt wrongly assigns keyboard focus to BOTH the Script Editor AND the instrument edit view - so you get both your character from the script deleted, AND the modulator! So be careful!!!
Let's notice one more thing regarding Kontakt 5 here. When you right-click the modulator or its target strip, you will see some numbers there (group, slot, idx). This is essentially what get_mod_idx()
and get_target_idx()
will return when used, these are the numbers that you put in set_engine_par()
or get_engine_par()
. So if you feel like you don't want to be bothering with this all, you can use the numbers, too. But I find that this really affects script readability in a bad way, so I always use the naming scheme. Then everything makes a lot more sense.
Before continuing onward, let me explain my naming scheme above for the case where I used "CUTOFF 1" in the modulator target name. In Kontakt, you can have up to 16 internal modulators per group. Each of those modulators can target up to 16 destinations. So let's say you have several filters loaded in Group FX, and you want one single LFO to modulate the filter cutoff in all of them. Forget_mod_idx()
and get_target_idx()
to work we need to have UNIQUE names for EVERYTHING within a single group! This is why I added a number there. In the above situation, Group FX slot 1 filter cutoff would be called CUTOFF 1, slot 2 would be called CUTOFF 2, and so on. So we would have modulation target strips named like so:
LFO 1 -> CUTOFF 1 LFO 1 -> CUTOFF 2 LFO 1 -> CUTOFF 3 LFO 1 -> CUTOFF 4
etc.
This ensures unique naming and no get_mod_idx()
errors, provided you didn't do a typo of a modulator in the script, or something
Note that we need this naming ONLY when there's a multiple of the SAME modulation links across multiple Group FX slots. So, if we have 4 filters or 4 EQs and we want to modulate the same parameter in all of them with just one modulator, that is the case when we use this incremental naming scheme. If we have an EQ in one slot, a filter in another, a Skreamer in yet another, and we have ONE modulator targetting a DIFFERENT parameter in all of them, we don't need to do this (since you would name the modulator targets differently, for example: LFO 1 -> CUTOFF
, LFO 1 -> EQ GAIN2
, LFO 1 -> SKR TONE
...)
Let's put all this to good use now!
Comments
-
1. CHANGING THE LFO FREQUENCY
Let's say our LFO is named
LFO 1
and we want to change its frequency. Let's say it's found in group 2, and that our ui_knob/slider is called$LFOFreq
. Here's what we do:set_engine_par($ENGINE_PAR_INTMOD_FREQUENCY, $LFOFreq, 1, get_mod_idx(1, "LFO 1"), -1)
That's all there's to it! Same code is used for other LFO parameters, and all envelope parameters in much the same way.
$ENGINE_PAR_INTMOD_FREQUENCY
applies to LFOs and step modulators.2. CHANGING THE ENVELOPE MODULATION AMOUNT
Let's say we have an envelope modulating pitch in group 4. Our envelope is called
ENV 2
and our mod target strip is called, naturally,ENV 2 -> PITCH
(because you're following my naming guidelines, aren't you?). Our ui_knob/slider is called$PitchEnv
. We can deal with this in several ways, one of which is needlessly more complicated than others. Let's see...- UNIPOLAR MODULATION AMOUNT
set_engine_par($ENGINE_PAR_MOD_TARGET_INTENSITY, $PitchEnv, 3, get_mod_idx(3, "ENV 2"), get_target_idx(3, get_mod_idx(3, "ENV 2"), "ENV 2 -> PITCH"))
This will target ONLY the modulation amount slider, which goes from 0 to 100%. Which means unipolar (positive) modulation happens. Notice how the code is structured: first we're telling which engine parameter we want to change, then we tell by what amount, then we point to the group we want to modify, then we tell which modulator we're pointing, and finally which modulator target strip we want to change (we repeat
get_mod_idx()
here because it's important for KSP to know the modulator to which the target strip is attached to). The range of our ui_knob/slider is 0 to 1000000.- BIPOLAR MODULATION AMOUNT
This is the needlessly more complicated way: using the unipolar mod amount code and then targetting the Invert button with a separate engine parameter call. The range of our ui_knob/slider in this case is -1000000 to 1000000.
set_engine_par($ENGINE_PAR_MOD_TARGET_INTENSITY, abs($PitchEnv), 3, get_mod_idx(3, "ENV 2"), get_target_idx(3, get_mod_idx(3, "ENV 2"), "ENV 2 -> PITCH")) if ($PitchEnv < 0) set_engine_par($MOD_TARGET_INVERT_SOURCE, 1, 3, get_mod_idx(3, "ENV 2"), get_target_idx(3, get_mod_idx(3, "ENV 2"), "ENV 2 -> PITCH"))else set_engine_par($MOD_TARGET_INVERT_SOURCE, 0, 3, get_mod_idx(3, "ENV 2"), get_target_idx(3, get_mod_idx(3, "ENV 2"), "ENV 2 -> PITCH"))end if
See? This is needlessly complicated. Thankfully there's another engine parameter that handles this all for itself.
For whatever reason it's not documented, but I think everyone should know about it, since it offers one cool feature many people don't know about. This engine parameter is called
$ENGINE_PAR_INTMOD_INTENSITY
. Its range is 0-1000000, and this covers the ENTIRE bipolar range of the modulation amount slider, which means 0% is at 500000, 100% is at 1000000 and -100% is at 0. So, our code for bipolar modulation amount slider becomes this:set_engine_par($ENGINE_PAR_INTMOD_INTENSITY, $PitchEnv, 3, get_mod_idx(3, "ENV 2"), get_target_idx(3, get_mod_idx(3, "ENV 2"), "ENV 2 -> PITCH"))
Our
$PitchEnv
range for this example is then, of course, 0 to 1000000.But... that's not all! It seems that NI didn't build in any range checking for this engine parameter. Which means, we can go beyond the range of -100%~100%! This is especially important for pitch modulation, because sometimes we want to have a stronger modulation that goes beyond 12 semitones range that Kontakt allows at 100% modulation amount. So, just continue with the values, expanding the range. It follows a sort of exponential curve, so engine parameter of 2000000 is not 24 semitones, it's actually 324 semitones! So you don't want to go that high. Here's a table with some useful values:
-12 to 12 semitones -> 0 to 1000000 -24 to 24 semitones -> -129961 to 1129961 -36 to 36 semitones -> -221125 to 1221125 -48 to 48 semitones -> -293701 to 1293701
I doubt you'll need more than this. Also note that extreme pitch modulation can sometimes crash Kontakt, since this is not only an undocumented feature, but probably actually an unintentional bug on NI side. So it MIGHT be fixed at some point... but somehow I think this "exploit" is here for a reason. So there you go - this is how to extend modulation range for pitch modulations! This might or might not work for other engine parameters. Explore!
3. EXTERNAL MODULATORS
This is as simple as it gets. We can use either unipolar or bipolar modulation amount examples from above, except we do not need to use
get_target_idx()
at all! Just write -1 instead. Let's say we have aftertouch mapped to modulate LFO 1 frequency in group 10.set_engine_par($ENGINE_PAR_INTMOD_INTENSITY, $ATVolume, 9, get_mod_idx(9, "AT -> LFO 1 FREQ"), -1)
That's all there's to it!
NOTE: The following information is not super important since Kontakt 7.1 brought improvements with the new commands!
I think there's one more thing I'd like to say here, and one thing all KSPers should know. When the old
find_mod() or find_target() commands
don't find a modulator or target with designated name, they actually return a value of 0 instead of -1! This is actually a bug on NI side. How does this manifest? Well, it manifests in such a way that it will CHANGE the value of the modulator whose slot ID is 0 REGARDLESS! This is usually amplifier envelope which is assigned by default by Kontakt. Usually it sets the modulation amount of it to 0%, which makes the instrument behave in a very weird way (envelopes don't work and notes are cut short). So, if your instrument starts behaving weirdly after a wrongly targettedfind_mod()
, you know what to look for!Bottom line is, if at all possible, build your instrument for Kontakt 7.1 and up so that you can forget about
find_mod()
andfind_target()
and use new commands instead!________________
I hope this has been helpful. I've probably missed some things I wanted to mention, but that's why this is a forum and this is a thread where you can leave your questions and I'll answer from time to time.
Cheers!
7 -
This is really amazing. Thanks!
0 -
Is there any way to modulate the engine volume knob greater than +6db? If the knob is set to 0db and I raise the $ENGINE_PAR_INTMOD_INTENSITY of my from_script modulator, it still only goes up to +6db max. Something like change_vol($ALL_EVENTS,...) won't work for me because change_vol() seems to have a slew to it.
0 -
No. Amplifier volume modulations are multiplicative, not additive, so they will always modulate up to the value you've set for volume, but never above it.
0
Categories
- All Categories
- 19 Welcome
- 1.3K Hangout
- 59 NI News
- 710 Tech Talks
- 3.7K Native Access
- 15.2K Komplete
- 1.8K Komplete General
- 4K Komplete Kontrol
- 5.3K Kontakt
- 1.5K Reaktor
- 356 Battery 4
- 787 Guitar Rig & FX
- 403 Massive X & Synths
- 1.1K Other Software & Hardware
- 5.3K Maschine
- 6.7K Traktor
- 6.7K Traktor Software & Hardware
- Check out everything you can do
- Create an account
- See member benefits
- Answer questions
- Ask the community
- See product news
- Connect with creators