Skip to main content

Advanced Concepts

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
    set_engine_par($ENGINE_PAR_EFFECT_TYPE, $EFFECT_TYPE_FILTER, 0, 0, -1)
    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).

WARNING: When changing the effect slot contents from the on init callback, it is executed synchronously, which will affect the initial loading time of the instrument, especially if this is done for a large amount of effect slots. It is advisable to move any such operations to on persistence_changed callback.

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.

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 from or written to 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 alphanumerics and must not start with a number. It also cannot be longer than 64 characters. It is a good idea to always write them with capital letters to emphasize their unique status.

Here's an example. Insert the following 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.

It is possible to have as many PGS 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 work like normal string variables.

PGS string 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>)

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.