Skip to main content

Advanced Concepts

Preprocessor & System Scripts

SET_CONDITION(<condition-symbol>)

Define a symbol to be used as a condition.

RESET_CONDITION(<condition-symbol>)

Delete a condition definition.

USE_CODE_IF(<condition-symbol>)

...

END_USE_CODE

Interpret code when <condition-symbol> is defined.

USE_CODE_IF_NOT(<condition-symbol>)

...

END_USE_CODE

Interpret code when <condition-symbol> is not defined.

NO_SYS_SCRIPT_GROUP_START

Condition; if defined with SET_CONDITION(), the system script which handles Group Start Options will be bypassed.

NO_SYS_SCRIPT_PEDAL

Condition; if defined with SET_CONDITION(), the system script which sustains notes when CC# 64 is received will be bypassed.

NO_SYS_SCRIPT_RLS_TRIG

Condition; if defined with SET_CONDITION(), the system script which triggers samples upon key release is bypassed.

reset_rls_trig_counter(<note>)

Resets the release trigger counter (used by the release trigger system script).

will_never_terminate(<event-id>)

Tells the script engine that this event will never be finished (used by the release trigger system script).

Examples

A preprocessor is used to exclude code elements from interpreting. <condition-symbol> refers to a symbolic name which consists of alphanumeric symbols, started by a letter. For example, you could write:

on note
    { do something general }
    $var := 5

    {do some conditional code}
    USE_CODE_IF_NOT(dont_do_sequencer)
    while ($NOTE_HELD = 1)
        play_note($EVENT_NOTE, $EVENT_VELOCITY, 0, $DURATION_SIXTEENTH)
        wait($DURATION_EIGHTH)
    end while
    END_USE_CODE
end on

What's happening here?

Only if the symbol dont_do_sequencer is NOT defined, the code between USE_ and END_USE will be processed. If the symbol were to be found, the code would not be passed on to the parser; it is as if the code was never written. Therefore it does not utilize any CPU resource.

All commands will be interpreted before the script is running, i.e., by using USE_CODE_ , the code might get stalled before it is passed to the script engine. This means, SET_CONDITION and RESET_CONDITION are not actually true commands: they cannot be utilized in if ... else control statements; also a wait() statement before those commands is useless. Each SET_CONDITION and RESET_CONDITION will be executed before something else happens.

All defined symbols are passed on to the following script slots, i.e. if script slot 3 contains conditional code, you can turn it on or off in script 1 or 2.

You can use conditional code to bypass system scripts. If you define one of those symbols with SET_CONDITION(), the corresponding part of the system scripts will be bypassed. For clarity, those definitions should always take place in on init callback.

on init
    { we want to do our own release triggering }
    SET_CONDITION(NO_SYS_SCRIPT_RLS_TRIG)
end on

on release
    { do something custom here }
end on 

If we want to do our own release triggering logic, this is the first step to do so.

PGS

It is possible to send and receive values from one script to another, circumventing the usual left-to-right processing order, by using the Program Global Storage (PGS) commands. PGS is shared memory that can be read or written by any script. 

PGS commands

pgs_create_key(<key-id>, <size>)

pgs_key_exists(<key-id>)

pgs_set_key_val(<key-id>, <index>, <value>)

pgs_get_key_val(<key-id>, <index>)

<key-id> is similar to a variable name; it can only contain letters and numbers and must not start with a number. It also cannot be longer than 64 characters. It is a good idea to always write them in capitals to emphasize their unique status.

Here's an example, insert this script into any slot:

on init
    declare ui_button $Just_Do_It

    pgs_create_key(FIRST_KEY, 1)  { defines a key with 1 element }
    pgs_create_key(NEXT_KEY, 128) { defines a key with 128 elements }
end on

on ui_control($Just_Do_It)
    { writes 70 into the first and only memory location of FIRST_KEY }
    pgs_set_key_val(FIRST_KEY, 0, 70)

    { writes 50 into the first and 60 into the last memory location of NEXT_KEY }
    pgs_set_key_val(NEXT_KEY, 0, 50)
    pgs_set_key_val(NEXT_KEY, 127, 60)
end on

Then, insert the following script into any other slot:

on init
    declare ui_knob $First (0, 100, 1)
    declare ui_table %Next[128] (5, 2, 100)
end on

on pgs_changed
    { checks if FIRST_KEY and NEXT_KEY have been declared }
    if (pgs_key_exists(FIRST_KEY) and _pgs_key_exists(NEXT_KEY))
        $First := pgs_get_key_val(FIRST_KEY, 0)      { in this case 70 }
        %Next[0] := pgs_get_key_val(NEXT_KEY, 0)     { in this case 50 }
        %Next[127] := pgs_get_key_val(NEXT_KEY, 127) { in this case 60 }
    end if
end on

As illustrated above, there is also a callback that is executed whenever a pgs_set_key_val() command has been executed.

on pgs_changed

Callback type, executed whenever any pgs_set_key_val() is executed in any script

It is possible to have as many keys as you want, however each key can only have up to 256 elements.

The basic handling for PGS strings is the same as for normal PGS keys; there’s only one difference: PGS strings keys aren’t arrays like the standard PGS keys you already know – they resemble normal string variables.

PGS strings commands

pgs_create_str_key(<key-id>)

pgs_str_key_exists(<key-id>)

pgs_set_str_key_val(<key-id>, <string>)

<string> := pgs_get_str_key_val(<key-id>)

Resource Container

Introduction

The resource container is a useful tool for library developers. It is a dedicated location to store scripts, graphics, .nka files, Creator Tools GUI Designer performance views and impulse response files that can be referenced by any NKI or a group of NKIs that are linked to the container. Another benefit is that you can create a resource container monolith file containing all the scripts, graphics etc, so that you can easily move them around or send them to other team members. When loading an NKI, the resource container is treated like a sample, so if it is not found it will appear in the Content Missing dialog.

Setup

To create a resource container for your library, open up the instrument Options dialog for the Instrument you're working on, and click the Create button beside the area labeled Resource Container. After creating a new resource container file, Kontakt checks if there is already a Resources folder available. If there isn’t, Kontakt will ask to create it for you. If you do this, you will find Resources and Data folders next to the NKR file you have just created.

The Resources folder is the place where you can store the files that an Instrument can use which are not samples. As you can see, Kontakt has already created several subfolders for you: ir_samples, pictures (for GUI graphics and wallpapers), data (for .nka files), performance_view (for Creator Tools' GUI Designer) and scripts. The only thing to do now is to move your files into the right folders and you are ready to go.

Working with the Resource Container

Let’s say you’re creating a new library: after setting up the resource container as described above, you can tell all of the NKIs that are part of your library to use this particular resource container. Just open up the Instrument Options dialog and use the Browse function (click on the open folder button to the left of Create button).

As long as the Resources folder exist besides the NKR file (which is the resource container monolith), Kontakt will read all files directly from this folder structure.

For loading scripts from the scripts subfolder, use the “Apply from… → Resources folder” function within the Script Editor.

Now let’s say you want to send your current working status to another team member. Open up the Instrument Options dialog, click the Repack button, which will quickly fully update your existing NKR file. Be aware that this will completely overwrite your monolith, it won’t be matched in any way. Now Kontakt will do the following:

  • Check the ir_samples subfolder for any .wav, .aif, .aiff or .ncw files and put them into the monolith.

  • Check the pictures folder for any .png files that also have a .txt file of the same filename next to them. All of these will be packed into the monolith. Note that wallpapers also need a .txt file, or they will be ignored.

  • Check the scripts subfolder for any .txtfiles which will then be put into the monolith.

  • Check the data subfolder for any .nka files which will then be put into the monolith.

  • Check the performance_view folder for any .nckp files containing performance views created by Creator Tools.

After that, rename your Resources folder and reopen your Instrument. Now that the Resources folder is not present anymore, Kontakt will automatically read from the NKR monolith file. If everything is still working as expected, you can send your library (instruments, along with samples and the NKR monolith) to your team members. This is also how your library should be deployed to the market - you are not supposed to release your library with the Resources folder present!

To continue your work, simply rename the Resources folder back to Resources.

Remarks

  • The resource container will be checked in the Content Missing dialog.

  • When you save your Instrument as a monolith file, the resource container will not be integrated into the monolith. The path to the resource container will be saved as an absolute path, which will only work locally on your machine - on other computers Content Missing dialog will show up upon loading such Instrument, since the path to the resource container is most likely not going to be valid anymore.

Changing FX from KSP

Introduction

Prior to Kontakt 5.5, the infrastructure to get information about the content of effect slots was already in place via engine parameter variables like $ENGINE_PAR_EFFECT_TYPE and built-in constants like $EFFECT_TYPE_FILTER (refer to Module Types and Subtypes).

Starting with Kontakt 5.5, it is also possible to change the loaded effects with the same set of built-in constants.

Example

on init
    wait_async(set_engine_par($ENGINE_PAR_EFFECT_TYPE, $EFFECT_TYPE_FILTER, 0, 0, -1))
    wait_async(set_engine_par($ENGINE_PAR_EFFECT_SUBTYPE, $FILTER_TYPE_LDR_LP4, 0, 0, -1))
end on

Inserts a 4-pole lowpass ladder filter into the first Group FX slot of the first group in the Instrument.

on async_complete callback

Changing the effect slot contents is an asynchronous operation. This means, one cannot reliably access the newly instantiated effect immediately after instantiation. To resolve this, the command returns an $NI_ASYNC_ID and triggers the on async_complete callback. In addition, wait_async() command can also be used to streamline this process (and is recommended).

Default Filter Type

Filters are somewhat special as they are effect types that feature subtypes. Since one can now instantiate a new filter from KSP without explicitly selecting its subtype, there is a need for a predefined default filter subtype. This is SV LP4.

Implications on Modulation and Automation assignments

When changing the contents of effect slots through KSP, it is expected that the handling of assigned automation and modulation is identical to performing the same action using Kontakt's GUI.

  • When changing a slot's effect type or removing it entirely, all modulation and automation assignments are also removed. Specifically to modulators, if the removed assignments are the only ones belonging to a certain internal modulator (i.e., if the modulator is not assigned to other targets as well), the modulator itself is also removed.

  • When changing a slot's effect subtype (only applies to filters), everything is left unchanged. It is accepted that in certain cases, one may end up with "orphaned" modulation assignments as it is the case right now; e.g., when having modulation assigned to a parameter that is no longer available, like Resonance or Gain.

Changing Modulator Subtypes

Using the same commands described above, one can also change the subtype of internal modulators. Specifically, one could switch between envelope types (AHDSR, Flex and DBD), or LFO types (Rectangle, Triangle, Sawtooth, Random, Multi, Multi Digital). A modulator cannot be inserted or removed. Its type (LFO, Envelope, Step Modulator, Envelope Follower and Glide) cannot be changed either.

Special Cases

There are two effect types which cannot be set from KSP: Surround Panner and AET Filter.

The Advanced Engine Tab

The Advanced Engine tab can be a useful tool for debugging and measuring the performance of your instruments and scripts.

While the Engine tab (a sub-tab of the Monitor tab in Kontakt's Side Pane) can provide a useful display of performance statistics, the advanced version gives higher accuracy to things like CPU usage, and also displays information on multiple instances of Kontakt when it is used as a plug-in.

Displaying the Advanced Engine Tab 

As mentioned earlier, the Engine tab is a sub section of the Monitor tab, which can be found in Kontakt's Side Pane.

  • To access the Advanced Engine tab, hold the [Alt] (Windows) or [Opt] (macOS) key while clicking on the Engine tab.

  • To return to the main Engine tab, just click on the Engine tab again with no keys held.

Instance Overview

If you are running multiple instances of Kontakt as a plug-in in a DAW or host, each instance will be given an entry in this section. If you are using Kontakt standalone, only the current instance will be displayed.

There are five performance statistics you can view here:

  • CPU: displays the current CPU load in percent (at a higher resolution than the other CPU readouts in Kontakt) as well as the highest recorded peak CPU level (displayed in parentheses). You can reset the high peak by re-initializing the Kontakt instance by clicking on the "!" button in the top right of Kontakt's interface.

  • Voices: displays the total number of voices currently in use by the current Kontakt instance.

  • Voices killed: displays the total number of voices that have been killed due to CPU overload (displayed on the left) and DFD overload (displayed on the right).

  • Process buffer: displays the current audio buffer size in samples.

  • Events: displays the total number of events currently in the event queue. While a voice is the equivalent to a sample being played back, an event is more closely related to MIDI note messages being processed by the engine. For example, a single event could produce 3 voices, if there are 3 samples mapped to a single note. Additionally, if you are holding a MIDI key event though the triggered sample has finished playback, the voice will terminate, but the event will remain in the queue. As such, this display can be useful for tracking down events that are hanging, as these are not always audible in the way that hanging voices would be.

Total

The lower section displays the total performance statistics for all Kontakt instances currently loaded. It has the following parameters: 

  • Voices and Voices killed: like the displays in the Instance Overview, but a total for all instances.

  • DFD load: if you are playing Instruments that use DFD mode, this measures their hard disk access. It is essentially a more accurate version of the Disk meter in Kontakt’s Header.

  • DFD memory: a measurement of how much RAM is being used to process the DFD stream.

  • DFD requests: the total number of requests made by Kontakt to read data from the hard disk.