Sie sind auf Seite 1von 14

What are scripts? 4.

Type in your script in the script text editor (the rest of the tutorial will
show you how to write the scripts).
Scripts can be seen as small plugins. They are quite similar to the MIDI effect It's possible to use a couple of shortcuts in the editor:
plugins you can use in sequencers but differs by being integrated into the
sampler and this provides some benefits. Unlike MIDI effects, scripts have access Ctrl+A - select all
to some internal Kontakt features which in addition lets them: Ctrl+X - cut
Ctrl+C - copy
 change volume/panning/tuning of a specific note
Ctrl+V - paste
 fade in/out a specific note
 specify which groups should be used for playing back a specific note
 start playback of a note at any sample offset (only in Sampler mode) 5. Important: if you're accustomed to using Ctrl+Z for undo be sure to
 control a wide range of Kontakt parameters (maybe in the Kontakt 2.1 avoid it in the script editor as it'll discard your changes and you may
update) loose all of what you have typed in.
6. Press the "Apply" button for the changes to take effect. The little box to
Scripts are written in the Kontakt Script Language and entered as plain text. A the left of the Apply button will light up whenever there are unapplied
script basically consists of a number text lines with instructions to the Kontakt changes.
Script Processor (KSP). The KSP interpretes your script and executes your
instructions when it receives notes or MIDI CC.

To be able to enter your own scripts you must learn the syntax of the Kontakt
Script Language which is what this tutorial aims to help you with.

Managing scripts in Kontakt

To create and edit your own script in Kontakt you go through these steps (see
image below):
The process of writing a script consists of first going through steps 1-5 and then
1. Make sure the Script Editor is active. If not activate it. repeating steps 4-5 (editing and applying changes) until the script works as it
2. Press the "Edit" button to show the text editor where the script is should. A quick way to test any of the example scripts below is to copy it from
entered this page, and then press Ctrl+A followed by Ctrl+V in the Kontakt Script Editor to
3. Specify a title for your script. Make sure to press the enter key after replace any previous text with the script you want to try out, followed by clicking
having typed this in or Kontakt will not remember the title properly. The the Apply button.
title is completely up to you, choose something descriptive.
Editing scripts do something and instructing a computer, is that the computer needs to have it
specified in an unambigious language and broken down into small steps.
To make Kontakt load a script, the script code must be present in the internal
Kontakt Script Editor and you have to press Apply to make any changes apply. Callbacks
However, there's nothing that says that you must actually enter the script text,
henceforth called source code, using the internal script editor. You may equally Scripts have four main parts called callbacks. A callback represents a task that is
well use any text editor of your choice to enter the source code, and then copy to be performed when some event occurs (eg. a note is pressed or the script is
and paste it into the Kontakt script editor (followed by clicking Apply). loaded). The word callback indicates that it's not up to you to decide when these
tasks are performed, instead KSP triggers them for you (for a real world analogy
So why not use internal Kontakt script editor from start? Well, the reasons may think of someone calling you back on the phone letting you know something has
vary: you may think the text size is too small, you want to use the undo function happened, prompting you to do something). But it's still your task to say what
of your text editor, want an editor that does automatic indentation and code should happen. Eg. when a note is pressed KSP triggers the note callback where
coloring etc. For really short scripts it's probably not worth the extra work of you have specified what then should happen. The following script illustrates how
copying and pasting from an external editor, but for larger scripts I would like to these four parts are written:
recommend using my freely available KScript Editor (available both for Mac OSX
and Windows). on init
end on

Building blocks of problem solving on note


end on
To perform a task you need to:
on release
end on
 Keep certain things in memory and,
on controller
 Perform certain operations: end on
o sequentially (one after another),
o conditionally (only if a certain condition is true), or
The "on init" and "end on" lines serves as markers of the beginning and end of
o iteratively (over and over again until some condition is met that
the init callback. Between these you can add lines that specify what should
makes us go on with something else).
happen in that callback, and the same goes for the three other callbacks. Since
the callbacks above are empty (no such "middle" lines have been added) the
Just think about an everyday task such as sorting socks by color after laundry and script will do nothing and hence have no effect on incoming notes (it is a valid
you should be able to relate to all those parts (please actually try to picture this script however). In the next couple of sections you'll see how to make them
as vividly as you can). Scripting is basically no different, only now you're handling actually do something but first a description of the four callbacks:
notes and you have to learn how to express to KSP what things needs to be
remembered and what operations need to be performed, which is what the
following sections is about. The main difference between instructing a human to
on init time. The kind of variables shown here can contain a single integer value. A good
The init callback is performed once at the time the script is loaded. This way to think of them is as named containers of information - the name $x is used
is a place where scripts do all kinds of initialization, eg. of user interface to refer to whatever number that variable contains (which depends on which
controls like knobs and edit boxes. number was previously stored in it).
on note
The note callback is performed whenever a note is pressed. This gives To use a variable you must first declare it, which is a way of saying to Kontakt:
your script a chance to alter the note (eg. pitch, velocity, volume, "I want an information container with this name. Please create it and let me use it
panning, tuning), or generate new notes (eg. for accompaniment). to store/retrieve values henceforth".
on release All variables must be declared in the init callback part of your script and are then
The release callback is performed whenever a note is released. This lets available for use in all callbacks.
you customize which release samples are used, which could be useful Here's an example of a declaration:
eg. if your script is for a guitar instrument.
on controller on init
The controller callback is performed whenever a MIDI CC message is declare $x
received. You can use it eg. to let your script react to movements of the end on
mod-wheel or the sustain pedal being pressed or released.
To assign a value to a variable ("store the value in the container") you write $x :=
Note that empty callbacks can be omitted if you like, and we will do that later. value where value can be anything from a simple number to a complex
Furthermore I might add that actually there is one additional type of callback - a mathematical expression possibly containing other variables. When you assign a
user interface callback which is performed whenever the user changes the setting value to a variable the previous value it containted is lost and replaced with the
of a certain knob or button. It'll be introduced later though. new one. Here's an example of three variables being declared and then having
values assigned to them. If nothing other is specified the lines of a callback are
always performed sequentially by KSP, starting with the top line. If no value is
Variables assigned to a variable it will by default equal zero, until you change it. The script
below will initialize $sum to the value 60.
Variables are the way for scripts to remember things - they are used for storing
and retrieving information. So what is it that we need to remember? Well, it on init
might be settings (what user control knobs or edit boxes are set to), or it might declare $x
be information used to keep track of where we are in a process so that we know declare $y
what to do next. declare $sum
$x := 40
$y := 20
Script variables look just like the variables you're used to from algebra, except $sum := $x + $y
they start with the '$' character. So instead of eg. x and y we have $x and $y. The end on
main difference between these variables and the ones you use when solving
equations is that the script variables actually do vary - whereas the equation It's often the case that one wants to assign an initial value to a newly declared
variables commonly have a fixed value that you're trying to find out. So one and variable, and therefore it is also possible to combine declaration and assignment
the same script variable $x can contain different values at different points in as this script shows.
on init Note that changing the value of a variable can make one and same expression
declare $x := 40 evaluate to different values at different times.
declare $y := 20
end on
Now you probably wonder what "mod" on the last line does. Just like +, -, / and *
At some point you'll probably be confronted with code that looks like this: it is a mathematical operator. It returns the remainder resulting from dividing the
two numbers, so 11 mod 4 will be evaluated to 3. Speaking of division also note
$sum := $sum + $x that since Kontakt scripts only deal with integers the result of a division is
rounded down to the nearest integer, eg. the result of 11 / 4 is 2 (2.75 rounded
The two occurences of the variable on both sides of the assignment operator := down). Because of this rounding it's important to think about in which order you
may seem confusing, but then it's important to know that the right hand side is do calculations, eg. say you want to calculate two thirds of the $x. Then you
evaluated first. So first $sum + $x is calculated and then the resulting value is might be tempted to write 2/3 * $x, but 2/3 is rounded down to zero so the result
assigned to $sum (replacing its old value). So the line above actually is the way will always be zero. Instead one should collect factors and try to do one single
you would tell KSP to increase $sum by $x. If before running this line $sum was division as the last step like this: 2*$x / 3. The key thing is to try to keep precision
equal to 10 and $x was equal to 5, afterwards $sum will be 15 (and naturally $x as long as possible.
will be unchanged).
Array variables
Mathematical expressions
In addition to normal variables introduced above it's also possible to use list
As the examples above showed you can combine numbers and values into variables. Instead of just containing one value these variables contains a fixed
mathematical expressions. Almost anywhere where one can use a number it's number of values which each can be accessed by specifying its position in the list.
also possible to use more complex mathematical expressions, mixing variables Each such value slot is denoted as element. The list variables themselves are
and numbers as you wish. Here are a couple of examples: called arrays. Say for example that you like to make a script that tunes the twelve
different tones separately. Then you could declare an array variable of length 12
whose twelve elements would represent the different tunings. To distinguish
 127 / 10
array variables from normal variables they are prefixed with % instead of $.
 $x + 1
Here's an example:
 $x * 10
 ((-$y + 1) * $x) - 100
on init
 $sum / $n declare %note_tunings[12]
 $sum mod $n end on

When KSP executes one of your script lines it will start by evaluating all The size of the array is specified within brackets to the right of the name. Arrays
expressions it contains. For every variable in the expression the current value of in Kontakt scripts are always created with a fixed size that cannot be changed
the variable will be used. You can picture this as if every variable would have afterwards.
been substituted by the value it contains at the time the expresion is evaluated.
To access a specific element you write the variable name followed by the Constant variables
element index within brackets:
Sometimes it's desirable to prevent a variable's value from being changed after
on init the declaration - to make the variable constant. To do this you use the word
declare %note_tunings[12]
%note_tunings[0] := 10
const in the declaration:
%note_tunings[1] := -3
%note_tunings[2] := -15 on init
%note_tunings[3] := %note_tunings[2] - 10 declare const $max_velocity := 100
end on end on

The above example assigns values to the first four elements. Array elements are This variable behaves like any other with the exception that it's not possible to
initialized to zero by default, so the elements not assigned to above will contain assign a new value to it (trying to do this will generate an error message). After
the value zero. An array element (written as the array name followed by the having declared the constant you can use the variable name anywhere you would
element index inside brackets) behaves in every way just like a normal variable. have written 100. So why do this instead of actually writing the value 100 which
As you see the indexing does not start counting at 1 but instead at 0 - the first seems a lot easier? Well, using a constant variable instead has two benefits:
element in an array always has index zero. If this seems strange to you, think of
the index as a distance from the beginning. The first element begins the array and  Your script will be easier to understand since a variable name such as
therefor has distance 0, the second element lies at offset 1 from the beginning, $max_velocity provides information about what the number signifies. In
the third at 2 and so on. a complex script $max_velocity would be easier to understand than the
value 100.
Note that the value inside brackets on the declaration line has a different  If you decide to change the value from 100 to 110 you only have to
meaning than values inside brackets on other lines. On the declaration the value make the change at one place in the script. Had you not used a constant
signifies array size, and on the other lines it signifies element index. Finally we variable you might have to change this value at thirty different places,
shall note that like the declaration and first assignment of a normal variable may maybe forgetting one occurance thereby introducing a bug.
be combined into one line, that is also the case for array variables. But an array is
not initialized with a single value, but rather a list of values. This list of values is User interface controls
written inside parenthesis and the values comma-separated, like this:
Scripts can use different types of user interface controls to let the end-user
on init
declare %note_tunings[12] := (10, -3, -15, 5, 4, -8, 6, 3, -22, 0, -2, 7) specify settings:
end on
 knobs
This is just a shorter way of writing assignments to the individual elements like  value edit boxes (which you can double-click on to enter a value)
above. Naturally the number of values specified must be equal to the size of the  menus
array.  buttons
 text labels
 tables
A user interface control can be seen as a special kind of variable whose value is integers at our disposal. Eg. the $humanize_delay variable above ranges from 0
not only used internally in the script but also displayed visually. And it turns out to 1000 milliseconds. However, even though variable values can only be integers
that the syntax for declaring user interface controls is very similar to how it is still possible to have Kontakt present the value in another unit to the user.
ordinary variables are declared. Let's create a button. Please try this script out in That's what the scale factor is used for, the value presented to the user will be
Kontakt and you'll see a button with the caption "mybutton" appear: divided by the scale factor. So in the example above, the internally used
milliseconds will be divided by one thousand when presented to the user who
on init will see it as a setting between 0.000 and 1.000 seconds. The scaling only affects
declare ui_button $mybutton the presentation and setting it to 1 the internally used value will be presented as
end on
is to the user (since division by 1 leaves any number unchanged).

The additional word "ui_button" in the declaration indicates that the variable
Knobs and value edit variables work similarily to the button. That is, you can
$mybutton should appear visually as a button. The variable $mybutton will have
control the knob/value edit by assigning a value to the variable anywhere in your
the value 1 when the button is active/pressed and 0 otherwise. You can also
script, and any change the user makes to the control will change the value.
change whether it's active/pressed or not by assigning either 0 or 1 to
$mybutton, so it works in both directions. We will see later how the value of
variables can be used to control which operations should be performed in the Comments
callbacks using conditional statements. For now let's look at knobs and value
edits as well: As scripts grow larger it's good practice to annotate them with comments.
Comments are text annotations that are completely ignored by KSP (the script
on init processor) when executing the script. They have no effect whatsoever, but they
declare ui_knob $volume(-10, 10, 1) provide a way for you to document and explain to readers of the script what you
declare ui_value_edit $humanize_delay(0, 1000, 1000) are doing. Think of commenting as writing a note in the margin of a book - while
end on
not changing the contents of the book they give you information about how to
interprete the text.
As you see these are similar to the button but instead you use "ui_knob" and
"ui_value_edit" in the declaration to show how they should be displayed. There's
Comments are to the benefit of other people who might be interested in your
another difference: the parenthesis and the three comma-separated values to
script, but mostly for your own sake. Without them you most likely will have
the right. These three values are mandatory and represent the minimum value,
forgotten your line of thought when you return to your own script after a couple
maximum value and scaling factor respectively. Min and max value is easy to
of days/weeks of not working with it. Use of comments is not a sign of
understand; test the script above and you'll see that value range of volume knob
amateurishness, on the contrary they are often used extensively by experienced
is -10 to 10.
script writers. I chose to introduce the concept of comments here because our
scripts will soon become more complex and we will need them to describe what
But what is the scaling factor? Well since script variables can only represent we're doing. Here's an example of how to use them:
integer numbers it's necessary to work with small units to get good resolution.
Eg. for a variable containing a delay setting, instead of measuring it in seconds we on init
use milliseconds or microseconds - if we had used seconds it would only be { volume adjustment (in dB) }
possible to have whole seconds, not fractions like 0.1s since we only have declare ui_knob $volume(-10, 10, 1)
{ random delay for humanizing (in milliseconds) } This function requires of you to provide four pieces of information, or
declare ui_value_edit $humanize_delay(0, 1000, 1000) parameters:
end on

Anything within curley braces - { } - is considered a comment and ignored by KSP. note number (pitch)
a value in the 0-127 range as specified by the MIDI standard.
You can place them anywhere in the code, eg. on their own lines as above or at
note velocity
the end of lines to make the script more compact:
a value in the 0-127 range as specified by the MIDI standard.
sample offset
on init
declare ui_knob $volume(-10, 10, 1) { volume adjustment (in dB) } for instruments which use Sampler mode this value may be used to start
declare ui_value_edit $humanize_delay(0, 1000, 1000) { humanizing delay (in milliseconds) } playback somewhere in the middle of the sample. To start playback from
end on the start of the sample use the value 0. In DFD mode only 0 is supported.
note duration
Finally a note about whitespace. As you may have noticed in the first script above the length of the note in microseconds. After this time a note-off
you can use empty lines wherever you wish to make scripts easier to read - just message will automatically be generated.
like you would divide a text into paragraphs. Furthermore on a single line, at any
place where I used a single space character, you are free to use any number of To use a function you write it's name followed by any parameters (four in this
spaces or tabs, eg. to align things (at many places you still need at least one case) wrapped inside parenthesis separated by comma:
white-space character though, to separate parts from each other). As an example
of this, the two leading spaces used to indent the middle lines above are purely on note
optional. However indentation is often used to put emphasis on the structure of play_note(60, 100, 0, 1000000) { play C3 at velocity 100, release after 1 second }
the script - the indentation makes it easier to spot where the init callback begins end on
and where it ends. Like comments extra whitespace only serves to make the code
more comprehensible to humans without affecting how the script works. Please try this script in Kontakt. You will find that any note you play has an
accompaniment of C3 (having note number 60), played at velocity 100, starting
Functions from the beginning of the sample, with a length of one second (one million
microseconds). As I said earlier, whatever is written inside the note callback will
be performed whenever a note is pressed. By the way, people use many different
So far I have shown how to declare variables and assign them values. To actually
words to describe that a function is executed which can be good to know, eg. one
control the behaviour of Kontakt (which is the whole point of using a script) you
say the function is called/invoked/executed/run. Also note, that while we passed
need functions. Think of a function as a named operation/command that Kontakt
simple numbers as parameters to the function above these could also have been
can perform for you whenever your order it to. There are functions for a lot of
mathematical expressions of arbitrary complexity. As an exercise, try changing
tasks: fading in/out, playing notes, releasing notes, changing tuning, etc. Some
some of the four parameters above and notice the different result as you play
functions need you to provide information (called parameters) that controls how
some notes on your keyboard.
the task is performed, and some functions also give you back an integer as result.
Let's begin with looking at how you can let the script generate an artifical MIDI
note. The script language provides a function called play_note for this purpose. In many cases you will want the length of your artificially generated notes to be
the same as the note your pressed on your keyboard and which triggered the
callback - you want the generated note to be released at the same time as the
triggering note. As this is such a common use the play_note function provides a initialized to the values 60 and 100 respectively. These are only initial values - if
way to do this: just specify -1 for the note duration and Kontakt will handle the the user sets the knobs to other values later these two variables will then contain
rest. Of course -1 is not used as the actual note duration but rather serves as a the changed values. As I said earlier the init callback is triggered only once when
signal that the note duration should be handled in a special way. the script is loaded at which time its body, the four lines in this case, is executed.
A script is considered to be loaded not only when you load a script from the script
on note preset menu in Kontakt, but also every time you press the Apply button (causing
play_note(60, 100, 0, -1) { play C3 at velocity 100, release when the triggering note is } the updated version of the script to load!).
end on

In the note callback instead of using constant numbers as we previously did, now
A final note about which notes trigger the note callback. If the artificially
we pass variables as parameters to the function. As mentioned earlier you can
generated note above would trigger the note callback just like "real" notes do,
picture this as if each time the line is executed, all variables would be replaced
using play_note would cause the lines inside the callback to be executed and
with whatever value they happen to contain at the time. As you see, the use of
another note would be played (that is what the above callback does, right) and
variables can make a single script line have different effects at different times
that note would in turn trigger the note callback again, and we would have an
depending on the values of the variables. If you turn the knobs, the values of the
infinite chain reaction. This is of course not desirable and is why KSP is designed
two variables will change and thereby the accompaniment note and its velocity
so that only "real" notes (of external origin) trigger the note callback and not
(try this!).
artificially generated ones.

Built-in variables
Using functions and variables together
There are a number of built-in variables that provide you with information about
Let's now try to bring the two concepts of functions and variables together to
things like tempo, time, MIDI controller states, etc. For example, in the note
make the above example less static:
callback you will often want to know the pitch and velocity of the note that
caused the triggering of the callback (the nonartificial note played on your
on init
declare ui_knob $note(0, 127, 1) { declare knob to control which note to play }
keyboard). Most of the built-in variables can be used within all callbacks, but
declare ui_knob $velocity(0, 127, 1) { declare knob to control velocity } some of them contain information about the press/release of a note and may
$note := 60 { assign the value 60 to the first knob } therefor only be used in the note and release callbacks (the only places where
$velocity := 100 { assign the value 100 to the second knob } their use makes sense).
end on

on note Now you may wonder what makes builtin-in variables different from the
play_note($note, $velocity, 0, -1) { play the user specified note at given velocity } variables we used earlier. They are different by being:
end on

 predeclared - they aren't and shouldn't be declared with a "declare" line


As you see this script defines both what should happen in the init callback and in
in your script.
the note callback. In the init callback two variables displayed as knobs are
 read-only - you cannot assign a value to a built-in variable. However the
declared. Their value range is 0-127 and the values displayed to the user are not
KSP engine can, and it assigns values to these variables that you can
rescaled (displayed as is) since the rescaling factor is 1. The two variables are
later use.
Here are the most important built-in variables: exercise you could try adding an init callback to this script, setup a knob that
determines the amount of transposition used and then transpose the incoming
$EVENT_NOTE note by this user-configurable number of notes instead of the currently fixed 12.
the MIDI note number (pitch) of the note that triggered the note/release
callback. In the range 0-127. All this time we have heard the original note together with the artificially
$EVENT_VELOCITY generated note. To mute the original you can use another function, called
the velocity of the note that triggered the note/release callback. In the ignore_event which stops a certain note event from propagating to the Kontakt
range 0-127. engine. This function requires you to specify one parameter: the ID number of
$EVENT_ID the note event you want to hinder. So what is this ID? Well, since a script deals
a number which is unique to the note event that triggered the with multiple notes we must have some way of specifying exactly which one we
note/release callback. We'll use this later. mean. Pitch isn't enough since multiple notes of the same pitch may be played at
the same time, so Kontakt assigns a unique identification number to each note.
Every time a note or release callback is triggered KSP will assign suitable values to The built-in variable $EVENT_ID contains the id number of the note that triggered
the above variables before executing the script code in the callback. If you play the callback, so if we pass this to the ignore_event function it will stop the
C3 on your keyboard $EVENT_NOTE will contain the value (note number) 60, and original note from being played:
if you play C#3 the next second $EVENT_NOTE will then contain the value 61. As
you see, the built-in variables are Kontakt's way of providing you with on note
information about note events and other things. ignore_event($EVENT_ID) { stop original note }
play_note($EVENT_NOTE+12, $EVENT_VELOCITY, 0, -1) { play transposed note }
end on
Before we go on, note that variable names cannot contain spaces, and that's why
the underscore character _ is often used as a replacement in names which consist Let's try some other functions as well:
of several words. All built-in variables have capital-only names.
on note
Using functions and built-in variables together change_note($EVENT_ID, $EVENT_NOTE + 12) { change pitch, transpose 12 steps upwards }
change_velo($EVENT_ID, $EVENT_VELOCITY / 2) { change velocity to half or original }
change_pan($EVENT_ID, -1000, 0) { change panning, pan left }
Let's look at a script which uses built-in variables together with functions: change_vol($EVENT_ID, -5000, 0) { change volume, turn it down 5000 milli-dB }
change_tune($EVENT_ID, 50000, 0) { change tuning, tune up 50000 millicents }
end on
on note
play_note($EVENT_NOTE+12, $EVENT_VELOCITY, 0, -1)
end on The names and comments make it pretty clear what they do so let's concentrate
on the parameters. All these functions take the ID of the note to change as their
As I said above, at the time the note callback is executed the $EVENT_NOTE and first parameter. The second parameter of the functions represent, in order: note
$EVENT_VELOCITY variables will automagically contain values corresponding to number (0 to 127), velocity (0 to 127), panning (-1000 to 1000), volume (in milli-
the pressed note and its velocity. So in the the script above we tell Kontakt to dB), tuning (in millicents).
play the note with note number $EVENT_NOTE plus 12 - the incoming note
transposed up one octave - at the same velocity as the original note. As an
Note number and velocity of a note event can only be changed before Kontakt end if
end on
has started playing the note. After that any changes will have no effect. On the
other hand panning, volume and tuning of a note event may be changed
continously while the note is being held pressed. This can be done by invoking In the init callback two variables are declared. The first one represents the
the last three functions multiple times for one and the same note event, amount of detuning. The range is specified as -100 000 to 100 000. Since this is in
providing different values as parameters each time. KSP lets you do these millicents it corresponds to -100 to 100 cents. We want to display this as cents to
changes relative to the current value - in which case the third parameter should the user so we specify 1000 as scaling value - the millicent value will be divided
be 1, or relative to the original value in which you should specify 0 as third by 1000 before presentation. The second variable is a button. If you recall it will
parameter. If you use change_vol to turn the volume down 5 dB and call this have the value 0 when the button is unpressed/inactive and 1 when it's
function twice using 0 as last parameter, the volume will go down 5 dB. If you do pressed/active. We will want to use this button to turn the detuning on or off
the same but use 1 as last parameter, the volume will go down 10 dB since the depending on whether it's pressed or not.
change is now relative to the current value and not the original one and the
changes accumulate. In the note callback a new piece of the script language is introduced: the if-
statement. To use it you write if followed by a logical condition inside parenthesis
As an exercise try to add an init callback, declare some knob or value edit (the parenthesis are mandatory). Then follows a couple of script lines - only one
variables and replace some of the parameter values in the example above with in this case - that should only be performed if the condition was true and skipped
your variables to make it possible for the user of the script to set the amount of otherwise. You indicate the end of these by writing end if. It's also possible to add
change. When declaring the user interface controls also try using the scaling an else-part if you want to say what should be done if the condition was false,
value, eg. to present tuning in cents instead of millicents. like this:

on note
Conditional execution if ($tuning_active = 1)
{ lines to be executed if the condition is true }
Let's return to the problem solving building blocks. So far you have learnt how to else
{ lines to be executed if the condition is false }
use memory (by using variables) and perform operations in callbacks. But the end if
operations inside a callback have been executed sequentially (one line after the end on
other) in all scripts so far. To do more complex things one has to be able to make
decisions - to say that if this the case then do this, otherwise do that. That is, to Logical conditions
be able to execute script lines conditionally (only if a certain condition is met.
Here's an example that shows conditional execution:
Logical conditions are very much like mathematical expressions, but instead of
evaluating to an integer value they yield true or false. We have already seen an
on init
declare ui_value_edit $tuning(-100000, 100000, 1000) { amount of detuning } example of a logical condition in the if-statement above. Here are the basics, a
declare ui_button $tuning_active { detuning on/off } condition is formed by comparing mathematical expressions using:
end on

on note = equal
if ($tuning_active = 1) { if the button is in it's pressed state then ... } # not equal
change_tune($EVENT_ID, $tuning, 0) { change tuning by user-specified amount }
< less than At the first line we ignore the original note event (stop the midi event from
> greater than propagating). Then a new construct is introduced: while. It works very much like
an if-statement, but if the condition is true and the lines in it's body are executed
<= less than or equal it afterwards goes back and checks the condition again. If the condition is still
>= greater than or equal true it executes the lines once again and so on while the condition is true. Since it
Furthermore, you may combine or negate conditions using: goes back in this way, it's often referred to as a while-loop. I'm sure you realize
and both conditions true that it's important that the logic condition evaluates to false at some point or one
would get stuck in the loop. Please try the script out.
or one or both conditions true
not condition not true
Iteration and arrays
Here are some examples of conditions used in if-statements:
A common way use of while loops is for iterating through the elements of an
 if ($x = $y) array variable and do something with each element. To test this let's build a
 if ($x # $y) simple arpeggiator script which uses an array to hold the notes to use. Before
 if ($x <= $max) looking at the script I'd like to introduce two new functions:
 if ($y < 10 and $x < 10)
 if ($EVENT_NOTE > 60 and $EVENT_VELOCITY < 100)  inc($x) - "inc" is short for "increment" and this function increments the
 if (not ($x = 100 or $y = 100)) value of the given variable by 1.
 dec($x) - "dec" is short for "decrement" and this function decrements
Iterative execution the value of the given variable by 1.

Now we are going to introduce the final missing building block mentioned in the Let's look at the script now:
problem solving section: iterative execution - doing something over and over
again. Here's an example of how to repeat something: on init
on note declare %notes[8] := (0, 4, 7, 4, 12, 4, 7, 4)
ignore_event($EVENT_ID) { ignore original note } declare $i
while ($NOTE_HELD = 1) { repeat while the original is held: } end on
play_note($EVENT_NOTE, $EVENT_VELOCITY, 0, 200000) { play note of length 0.2 seconds }
wait(200000) { wait 0.2 seconds } on note
end while ignore_event($EVENT_ID)
end on $i := 0 { assign the value 0 to $i }
while ($i < 8) { repeat while $i is less than 8 }
play_note($EVENT_NOTE + %notes[$i], $EVENT_VELOCITY, 0, 100000)
Before explaining this let's just introduce a new builtin-variable: $NOTE_HELD. wait(100000)
This variable will contain the value 1 for as long as the note that triggered the inc($i) { increment $i by one }
note callback is still held, but as soon as the triggering note is released KSP will end while
assign the value 0 to it (this happens automatically behind the scenes). end on
If the user presses say C3 the script is supposed to play the notes C3-E3-G3-E3- while ($i < 8 and $NOTE_HELD = 1) { repeat while $i is less than 8 }
play_note($EVENT_NOTE + %notes[$i], $EVENT_VELOCITY, 0, 100000)
C4-E3-G3-E3. The %notes array declared in the init callback contains eight values
wait(100000)
which correspond to the offsets of the notes we want to play relative to the note inc($i) { increment $i by one }
the user played. Eg. the first note has offset 0 since C3 and C3 are identical, the end while
second note (E3) has offset 4 from C3 and the third note (G3) has offset 7 from end while
C3, and so on. We will see how the variable $i is used later. end on

In the note callback we start with ignoring the incoming note and setting $i to The $NOTE_HELD = 1 condition was added to the inner while-loop as well to
zero. Then there's a while loop that continue to run for as long as $i is less than ensure that the arpeggiator stops as soon as the note is released. Without this it
eight. If you look at the last line in the while-loop body - "inc($i)" - you see that would first play the remaining of the eight notes and then stop.
the last thing that is done in every iteration is to increment the value of $i by one.
So the first time around $i will equal zero, the second time it will have been Polyphonic variables
incremented to 1, the third time it will have been further incremented to 2, and
so on. Eventually it will reach the value 8 which makes the while condition false The above script is working nice, but it has a problem: try playing two notes
and thus causes the loop to stop. Hence the body of the while loop will be which lie two octaves apart at the same time. You would expect to hear the same
executed eight times and $i will take on the values 0 to 7 in order. But the values arpeggio played using the two keys as base notes, but actually you will only hear
0 to 7 represent the indices of the elements in our %notes array (remember that every other note played. The reason for this is that both of the two notes will
we start counting at zero instead of one), so by writing %notes[$i] we refer to the trigger the note callback and just like the tones being played simultaneously we
i'th element which also can be thought of as "the current note offset". will have two instances of the note callback running more or less in parallel. The
problem stems from both of these using and changing the $i variable. Since both
The first line in the while-loop body plays the original note transposed by the of them increments $i by 1, the net effect will be that $i is increased by 2 instead
current note offset for 0.1 seconds. On the next line we wait for 0.1 seconds to of 1 at each step of the inner loop.
let the note finish before moving on. Please try this script out yourself.
What we would need is a way of telling KSP that each note/release callback that
Ok, but let's say we wish to play this over and over again until the note that is triggered should keep its own value of the variable $i - to enable us to play
originally triggered the whole thing is released. Then we simply wrap the above polyphonically without the callback instances interfering with each other. This
solution inside an outer while-loop that continues while the note is still being can be done by using the word "polyphonic" in the declaration of $i like this
held. As you see while-loops can be nested: (please try changing the declaration of $i above accordingly):

on init declare polyphonic $i


declare %notes[8] := (0, 4, 7, 4, 12, 4, 7, 4)
declare $i
Whereas a normal (nonpolyphonic) variable holds one single value that is
end on
accessible from all callbacks a polyphonic variable is tied to a certain note event.
on note Just like some built-in variables such as $EVENT_ID and $EVENT_NOTE they can
ignore_event($EVENT_ID) therefor only be accessed in the note and release callback. If two instances of a
while ($NOTE_HELD = 1) callback are executed in parallel and one of them changes the value of a
$i := 0 { assign the value 0 to $i }
polyphonic variable this change will not be visible to the other - they both keep than that you will have to call the wait function which gives Kontakt a chance to
their own value. Apart from providing you with a way to avoid having parallel handle other callbacks. Calling wait will reset the control counter making it
callbacks interfer, polyphonic variables also provide a way to pass information possible to do a maximum of another 49 999 iterations before calling it again.
from the note callback to the release callback. If you assign a value to a
polyphonic variable in the note callback the value be retained in the release The problem with two triggerings of the same callback interfering with each as in
callback corresponding to that note. So to summarize you will need to make a the arpeggiator script above occurs just because we use the wait function. Say
variable polyphonic when: the notes are played at the same time, then KSP will start to execute one of the
note callbacks. When it reaches the wait line it will put the callback on hold and
 The variable is used and and its value changed in the note/release continue with the second triggering. That callback will also reach the wait line
callback, and the callback invokes the wait function, and your and since there are no other note events to handle KSP will simply wait doing
instrument is supposed to support polyphonic playing. nothin until the first callback has finished.
 You want to pass a value from the note callback to the release callback
without risking the value being changed by intermediate notes/releases. In short, callback execution is not multithreaded but calling the wait function will
interweave multiple callbacks. If you use and change a certain variable in a
Polyphony and the callback execution model callback but don't use the wait function, multiple triggerings of callbacks won't
cause you problems since the callbacks will then be executed sequentially and
This section is for advanced users who want to better understand the background not in parallel. However you may still benefit from using polyphonic variables
of the two recommendation of when to use polyphonic variables given above. since they provide a way to pass information from a note callback to the
Feel free to skip this if you like. corresponding release callback.

Exactly how are the callbacks executed you may wonder when two notes are Group functions
played (almost) simultaneously. Well, the execution model of KSP is not
documented by NI, but it likely works like this: KSP has a pair of functions which enable you to specify which groups to
The execution of callbacks is not multithreaded - two notes will not cause two enable/disable when playing a note. Here's an example:
instances of say the note/release callbacks to actually run "simultaneously".
Instead the callbacks run one after another more or less in the order events that on note
triggered them occured. However, the one exception to this is the function wait. disallow_group($ALL_GROUPS)
allow_group(0)
If KSP were to wait and do nothing until the waiting time has passed, other allow_group(2)
callback triggerings would suffer a very noticable latency, eg. you wouldn't be end on
able to play two arpeggios at the same time because the second callback would
have to wait for the first to complete. Instead when you invoke the wait function The allow_group and disallow_group functions both take an integer parameter
KSP will put the current callback on hold so to say, and continue with any other corresponding to the group you want to turn on or off. Group indices start at 0,
pending callbacks and after the waiting time has passed later resume executing so if your instrument has 10 groups the indices lie between 0 and 9. There's a
the current one. Apart from the wait function while loops can also cause builtin constant variable with name $ALL_GROUPS that you can pass to either of
significant delays if they would be allowed to run for a long time. Because of this these functions to indicate that all groups should be turned on/off (depending on
Kontakt impose an upper limit of 49 999 iterations. If you need more iterations which of the function you use). The above script will turn off all groups and then
turn the first and third group on, ensuring that those are the only enabled 10 cents the release sample would also be tuned that way), and by enabling you
groups. to use the allow_group/disallow_group functions to turn on or off release trigger
groups. Unfortunately this is not the case. As soon as you want to do any of the
Release triggers above you will need to turn off the built-in release triggering functionality
alltogether. This will essentially make the release trigger groups work just like
normal groups. You then have to use play release triggers yourself by first the
In some cases you want or are forced to handle release triggers in a customized
release trigger group and no other groups using the allog_group/disallow_group
way. This need arises when you:
functions, and then use play_note to play the release sample. Although this way
of handling releases gives you a lot of flexibility, it also also causes unneeded
 Change the volume, panning or tuning of a note. complexity in the two first and most common use cases above (I've written a
 Want to turn release triggers on or off or choose what release trigger summarizing the problems).
groups to use for a specific note
 Want to play custom release notes.

Changing the volume, panning or tuning of a note won't change its release
sample. Furthermore the allow_group and disallow_group functions have no
effect on release triggers unless you completely turn off Kontakt's builtin release
trigger handling and handle things manually. Both of these facts I consider bad
design (see my forum post about this) but we'll have to live with it until NI makes
it better.

Let's look at a script that changes the panning of a note. Try loading this script on
an instrument that has release triggers and you'll hear that the note is panned
but the release sample is unaffected:

on note
change_pan($EVENT_ID, -1000, 0) { pan left }
end on

Clearly since Kontakt doesn't do the job, we will have to do the release playing
ourselves. The first thing we must do is to turn off the built-in release triggering
functionality which is done by including
SET_CONDITION(NO_SYS_SCRIPT_RLS_TRIG) in the init callback.

If Kontakt had been better designed the two first of these would have been
solved by automatically mirroring any change made to a note to the
corresponding release trigger sample (eg. if you changed the tuning of a note by

Das könnte Ihnen auch gefallen