Building on last month's introduction to the basics of scripting in Native Instruments' Kontakt soft sampler, we explain how to make your scripted functions adjustable via a simple user interface.
Last month, we looked at creating the simplest harmoniser, and finished with a script that would play a harmony note a minor third below the original note. It should have looked something like this:
play_note($EVENT_NOTE — 3, $EVENT_VELOCITY, 0, ‑1)
Because the interval of the harmony note is written into the script with the constant ‑3, the script is, in essence, a fixed harmoniser. We refer to a value such as ‑3 as a constant because its value will never change: ‑3 will always be ‑3. So if we wanted this particular script to produce a different harmony note instead, we would need to edit this constant value.
In order to make our harmoniser adjustable without needing to edit the script, we need to define the interval of the harmony note as a variable — a value that can change — instead of defining it as a constant. To do this, we'll substitute the ‑3 constant for a variable that's called $interval.
play_note($EVENT_NOTE + $interval, $EVENT_VELOCITY, 0, ‑1)
In the above instruction, we now get the pitch of the harmony note by adding $interval to the built‑in $EVENT_NOTE variable. The reason for this — and now for a flashback to school maths lessons — is that adding a negative value results in that value being subtracted from the sum. So if $interval stores the value ‑3 and $EVENT_NOTE is 60, the pitch of the note to play would be 60 plus ‑3, which is the same as 60 – 3 = 57.
If you click Apply right now, you'll notion the Change Indicator doesn't turn black. The line containing our play_note command will be highlighted in red, and this means that KSP found an error in our script. It's worth noting that there are cases where the highlighted line isn't always the one that contains the error (see the 'Debugging' box for more on script troubleshooting), so it's often worth checking the line before or after as well when tracking down a problem. But in this case, the highlighted line is indeed to blame. So what went wrong?
The reason for the error is that when we use a variable of our own — as opposed to a built‑in variable like $EVENT_NOTE — we have to first tell KSP of its existence before we can actually start using it. And if you look at the Script Editor's Status Line, KSP reports "ERROR (line 2): variable $interval was not declared”. It's bit like bringing a really expensive bottle of Scotch through customs: you need to declare it before you can start drinking it. So to declare our $interval variable, we need to write the following instruction in our script.
However, to make matters a little more complicated, KSP doesn't allow you to declare variables in the on note callback. The reason for this is that if variables could be declared in the on note callback, they would end up being declared every time a note was played, which could create a number of issues. Instead, variables should be declared once when KSP first initialises the script. And to do this, we need to define another callback, called on init.
As you might already have guessed, the on init callback is triggered when KSP initialises a script, just as the on note callback is triggered when a note is played. And, in the same way we defined the behaviour of the on note callback by adding instructions between the lines on note and end on, we'll define the on init callback by adding instructions between on init and end on. So if you define the on init callback in the script with the declare $interval instruction, you should end up with a script that looks like the one shown in the screen above right.
Now that we've declared our $interval variable, we need to assign a value to it. By default, newly declared variables are assigned a value of 0, so at the moment our harmoniser will simply trigger a duplicate note of the same pitch. You can assign a value to a variable when it's declared by changing the declare instruction to:
declare $interval := ‑3
Alternatively, you can assign a value to a variable at any other point in the script — including the on note callback — by using the following instruction.
$interval := ‑3
The := notation can be read as "set equal to”. So the way to read the above instruction would be that the variable $interval is set equal to ‑3. In our script, set the value of $interval to ‑3 in the declaration instruction and click Apply. Our harmoniser now works as it did before, adding a minor‑third harmony to notes that we play.
At this point, you might be wondering why we just went through all that effort to achieve exactly the same result. After all, even though we're now defining the interval of the harmony note by way of a variable, we still have to edit the script in order to change the interval. It's just that now we change the value of the variable rather than changing the play_note command. However, that's exactly the point: we can now change the value of the interval without changing the play_note command. And this lays the foundation for being able to adjust the value of $interval with a graphical control in the Instrument's Performance View.
The best thing about the way KSP handles user‑interface controls is that they can be used in a script in exactly the same way as variables. So instead of creating a knob and writing instructions to update the value of $interval when the value of the knob changes, all we have to do is declare a knob and use it the same way we would use a variable. To see what I mean, let's change the instruction that declares the $interval variable to:
declare ui_knob $interval(‑12, 12, 1)
If you play some notes on the keyboard, you'll hear that the harmony note is now an octave lower than the notes being played. This is because the 'interval' knob is set to ‑12 by default. But if you change the value of this knob, the interval of the harmony note changes accordingly. Pretty neat, right? And pretty simple. By changing our $interval variable to be a knob, we can now change the value of $interval simply by adjusting that knob. Let's now go back to the script and investigate the modified declaration instruction more closely.
To begin with, note that we declare user‑interface objects in exactly the same way we declare variables: by using the declare command in the on init callback. Remember, declarations can only happen in the on init callback. However, this time our declaration is little more complex. By placing the ui_knob keyword between declare and $interval, we tell KSP that we're declaring $interval as a knob. And with the additional three parameters in brackets, we're specifying a number of properties for the knob, just as we specified properties for the note to be played by the play_note command.
The three parameters for the knob are the minimum and maximum values the knob can be set to, followed by the 'display ratio'. The display ratio is used to calculate the number that gets displayed on the knob's interface, where KSP takes the value of the knob and divides it by the display ratio. For example, with a display ratio of 1, the user can adjust the knob between ‑12 and +12 in one‑unit steps: ‑12, ‑11, ‑10, and so on. However, if you wanted to display these values as ‑6 to +6 with 0.5‑unit steps, you could set the display ratio to 2. In this case, the $interval values in KSP would still be between ‑12 and +12, but the user would see values of ‑6 to +6 in the Performance View.
If you leave Instrument Edit Mode and return to the Multi Rack view (by clicking the Instrument Edit Mode's Exit button or toggling the Edit button on the Instrument), you'll notice that the script doesn't have a Performance View. There are still the same two tabs we had to start with: Options and Instrument. Even though we created a knob, and even though our knob shows up in the preview area of the script Editor, KSP doesn't automatically create Performance Views for scripts. To create a Performance View, we need to add the following instruction to the on init callback.
Now, when you go back to the Rack view for the Instrument, a third tab will be visible, with the title of the script. However, if you click this tab, you'll see that the Performance View looks rather messy. This is because the Instrument has a background image set; and although this makes the existing Performance Views look rather slick, it doesn't really work for the one we just created. We can remove the background image for an Instrument (or choose a different one) by returning to Instrument Edit Mode and clicking the Instrument Options button below the Instrument Header. In the Instrument Options window, select the Instrument tab and note the Skin Bitmap option at the bottom of the window. Clicking Reset will remove the image, and a replacement can be chosen by clicking the Browse button. For now, click Reset to remove the image.
Just for fun, let's try creating a second knob that allows the velocity of the harmony note to be adjusted. In the same way we created a knob that adjusts the pitch of the harmony note based on the pitch of the input note, this knob should adjust the velocity of the harmony note based on the velocity of the input note. Name the knob $velocity and give it a range of ‑64 to +64, and you should end up with a script that looks like the one shown at the bottom of the page.
One slightly annoying consequence of the velocity now being based on a variable is that its default value is always ‑64. It would be much better to make the knob default to 0, and it turns out that this is really easy to fix. Because knobs behave exactly like variables, we can set the knob to equal 0 in exactly the same way we would set a variable to equal 0.
$velocity := 0
If you add this instruction to the on init callback, the 'velocity' knob will now be set to 0 when the script initialises. However, even though we've assigned the value of 0 to the velocity knob, its real default value will still be ‑64. This means if you Ctrl‑click the knob, just like many other graphical controls in Kontakt, it will be set to its default value, which is still ‑64. So to change the knob's Ctrl‑click default value, we add another instruction to the on init callback.
The set_knob_defval command has two parameters, and you can probably guess what they do. The first specifies the knob we want to configure, and the second provides the default value we want to use. Now, after clicking Apply, the 'velocity' knob will be set to 0 when it's Ctrl‑clicked.
Before we finish learning about knobs, there's another issue related to default values that we have to consider when creating user interface objects in our scripts. Currently, every time our script is loaded, $velocity will be set to 0 because we added the instruction $velocity := 0. But what if we save our Instrument as part of a Multi, having changed the 'velocity' knob to 10? Because our script initialises $velocity to 0, the knob will be reset to 0 every time the Instrument loads, discarding any user settings that were made when the Multi was saved. In order for the state of a knob to be saved, we need to tell KSP that the variable the knob represents should be made 'persistent'. To do this for the 'velocity' knob, we would add the following instruction to the on init callback:
Once again, that's all we have space for this month. Next time, we'll be adding a transpose mode to our script, and also taking a look at Kontakt 4's new Multi Script feature.
When writing a script, it's inevitable that mistakes will be made and a script might not behave as expected. Often, the mistake will be identified by KSP when you click the Apply button: maybe you forgot to declare a variable, as with the example in the main text, or you mistyped a command or keyword. But sometimes the mistake will be in your script's actual logic. Programmers usually refer to these types of mistakes as bugs, so the process of finding and fixing them is known as debugging.
For example, say we had written our simple minor‑third harmoniser like this:
declare $harmony := ‑3
$harmony := $harmony + $EVENT_NOTE
play_note($harmony, $EVENT_VELOCITY, 0, ‑1)
The idea is that the $harmony variable is set to the interval we want for the harmony note, ‑3. Every time a note is played, the pitch of the note gets added to $harmony in order to determine the pitch of the harmony note to play. However, when we click Apply, although KSP doesn't report any errors, we only hear a correct harmony note the first time we play a note. For every subsequent note played, there is no harmony note. How do we figure out what's going on?
One clue is to look at Kontakt's Status Line at the bottom of the main window. If you play a couple of notes, you'll see the following text appear: "SCRIPT WARNING: play_note: 'note' parameter out of range!” This is a warning from KSP and it gives us a clue as to why our script isn't working correctly, namely because our harmony note's pitch is out of range. This means that the pitch is a number outside the allowed range between 0 and 127 for note pitches.
In order to solve this problem, it would help to know the value of $harmony at the time it's used in the play_note command. To do this, we can use the handy KSP command message, which prints a custom message to the Status Line. For example, try adding the following instruction to the script on a new line after the play_note instruction:
Now, when you play a note, you'll see the text "Hello, Kontakt!” appear in the Status Line. The quote symbols denote that the text is a string and will not actually get printed in the Status Line. A string is similar to a variable, except that where a variable is a container for numbers, a string is a container for text.
That's great, except we really want to see the value of $harmony rather than a charming greeting. So to see the value of $harmony, change the message command so it reads:
When you play the first couple of notes, you'll see in the Status Line that $harmony is within the 0 to 127 range. However, as you play more notes, you'll notice the number keeps increasing. If we look back at the script, the problem is that we assumed that $harmony would always start at ‑3 every time a note was played. But it doesn't. Every time a new note is played, it gets added to the current value of $harmony, which quickly becomes out of range. The solution is to add the following line to be the first instruction of the on note callback, resetting the value of $harmony to ‑3 every time a note is played.
$harmony := ‑3
Of course, the better solution is just to write the harmoniser as we originally did in the main text. However, hopefully this brief introduction to solving problems with KSP gives you a feel for what to do in your own scripts when things don't work out as planned.