Posted by & filed under KSP Scripting (NI Kontakt) - Tutorials, KSP: Custom UI (design).

Animations via image sprite sheet let your Kontakt instruments feel more organic and vivid. Sprite sheets can also be used for displaying dynamic info like note names or other information. This article is kept simple for a better understanding. The download contains a more advanced version of an oscilloscope, reacting on note height & velocity, easing out on note release, and displaying the note numbers (see video)


Content


Download all files to this tutorial premium

  • advanced script
    (displaying note names, reacting on note height & velocity and easing out)
  • NKI file: open script tab
  • open resource container (access all png files)
  • full ksp script for sublime 3 with helpful comments
DOWNLOAD


License:

KSP Scripts licensed under Creative Commons Attribution 4.0 International License.http://creativecommons.org/licenses/by/4.0/ You may use the scripts, modify them, build up on them, as you like (even commercially), as long as you keep all credits inside the scripts. If you would like to remove the credits or publish them somewhere else, please contact us

You may not  commercialize, share, distribute, whatsoever the included image files and other media (© all rights reserved).

 

Recommended Tools

  • 3D tool like blender to create an animated png sequence
    (you can also use Knobman for simple animations like VU-Meters or LED arrays)
  • Texture Packer for creating png sprites from image pool
  • Sublime Text [required]

 


 

animation sequence


First we create a simple animation with a duration of 2 seconds @ 30 fps and output it as png sequence. So in our case we get 60 images.
Here you can read how to export a png sequence in blender

If your animation software can export a full sprite sheet from scratch you can already output the sprite sheet of course and skip the next part.


sprite sheet

What’s a sprite sheet? Just watch this video

With Texture Packer you can easily create a sprite sheet from your single png files. Simply drag and drop all your single images (or the whole folder) into Texture Packer.
For Kontakt we need the sprite sheet to be either horizontally or vertically only! So we need to increase the max size and disable trim.

Just copy the settings from the screenshot and press ctrl + p to publish (export) the sprite sheet as png file with alpha channel. It should look like this then:

Unfortunately with the free version of Texture Packer every n th sprite will be rendered in red. If you did a simple one-colored animation like here, you can colorize the whole sprite sheet with your favorite graphics tool again.

 


KSP Script

First of copy your sprite sheet into the Resources/pictures folder and set the text file like this. You can use this sprite sheet for testing if you like.

Has Alpha Channel: yes
Number of Animations: 60
Horizontal Animation: yes
Vertical Resizable: no
Horizontal Resizable: no
Fixed Top: 0
Fixed Bottom: 0
Fixed Left: 0
Fixed Right: 0

The Full KSP Script (for Sublime Text 3)

The following script does an animation using our sprite sheet above, whenever a note is played. We can also set a frame rate (=> animation speed).

For your better understanding the script has been simplified compared to what you see int the video Also to give a basic starting point so that you can add your own code. With the full script the animation reacts on note heights and velocity. Furthermore the played note names are displayed.

on init
  // set constants
    declare const $UI_HEIGHT := 312
    declare const $ANIMATION_FRAMES := 60 //total frame number of our animation (number of sprites)
    declare const $FRAMERATE := 30 //fps => higher = faster animation

  // global vars
    declare $frame_idx //holds current frame index
    declare $playing //used to prevent animation from being stacked
    declare $wait //used to define animation speed according to given frame rate
    declare $run //runs animation when any note is held or stops it when all notes released

  // calcualate animation speed
    $wait := 1000*1000 / $FRAMERATE // => convert micro seconds to seconds and divide by frame rate

  // UI
    // animation container : label
      declare ui_label $animation(1,1)
      $animation -> text := ""
      $animation -> picture := "animation_noteon"
      $animation -> picture_state := 0
      $animation -> width := 180
      $animation -> height := 180
      $animation -> pos_x := 226
      $animation -> pos_y := 40

    // ui & script tab
      set_script_title("animation")
      set_control_par_str($INST_ICON_ID,$CONTROL_PAR_PICTURE,"yb_ico")
      set_ui_height_px($UI_HEIGHT)
      set_control_par_str($INST_WALLPAPER_ID,$CONTROL_PAR_PICTURE,"bg_init")
      make_perfview
  message("")
end on

on note 	
  //run animation
    $run := 1 //run when note played
    if($playing = 0) //but only when no animation is currently running 
      call animation	
    end if
end on


on release
  if (search(%KEY_DOWN,1) = -1) //checks if any note is still held
    $run := 0 //stop animation
  end if
end on


function animation
  // do endless loop, while allowed
    while($run = 1)
      $playing := 1 // set animation playing state (1 = currently playing)
      $animation -> picture_state := $frame_idx //increasing picture state and roll the animation
      
      inc($frame_idx)
      wait($wait) //simulates the framerate (30 fps) otherwise it's 1.000.000 fps (or one frame per micro second)
      
      // loop - restarts animation again
        if($frame_idx >= $ANIMATION_FRAMES)
          $frame_idx := 0
        end if
    end while

  //reset animation status when stopped so that it can be started again
    if ($run = 0)
      $playing := 0 
    end if
end function

 

 

On init – Callback

we skip the explanation for declaring all the basic variables & stuff (just read the nearby comments or read the other articles).

  // calcualte animation speed 
        $wait := 1000*1000 / $FRAMERATE // => convert micro seconds to seconds and divide by frame rate

For the animation we need to set a wait() command inside the animation function to simulate a certain frame rate. Without any wait() the animation would run with 1.000.000 FPS. So we probably wouldn’t notice any animation at all or we would have to create 1.000.000 pictures .. for each second :) Don’t worry the animation function will be explained in detail below.

$wait := 1000*1000 / $FRAMERATE In Kontakt the basic time unit for the wait() command is micro seconds. So we need to convert everything to seconds (1000 * 1000) and divide it by our given frame rate (which is 30 fps). That gives us a waiting period of 33.333 micro seconds.


Container (ui label) for the sprite sheet

// animation container : label
    declare ui_label $animation(1,1)
    $animation -> text := ""
    $animation -> picture := "animation_noteon"
    $animation -> picture_state := 0
    $animation -> width := 180
    $animation -> height := 180
    $animation -> pos_x := 226
    $animation -> pos_y := 40

This creates the label which will be the “container” for our animation. We can only use control_par_picture_state with labels, value edits or tables.

Note: you can also use a slider instead of a label and change the slider’s value instead of using picture_state. There are nice tutorials about that. But labels are more practical since you can use them as background and overlay them again with (transparent) buttons, sliders, etc. Furthermore labels can not be controlled by mouse events.

 

$animation -> picture := "animation_noteon" assigns our sprite image to the label

$animation -> picture_state := 0 sets the initial sprite. One sprite represents one frame.
in full ksp language it’s: set_control_par($CONTROL_PAR_PICTURE_STATE,0)

 

$animation -> width := 180 we only set the sizes of one sprite not of the whole sprite sheet. So the width is 180 px not 10.800 px. But actually this isn’t necessarily required anyway!

 

animation function

function animation
  // do endless loop, as long as allowed
    while($run = 1)
      $playing := 1 // set animation playing state (1 = currently playing)
      $animation -> picture_state := $frame_idx //increasing picture state and roll the animation
      
      inc($frame_idx) //increases the frame index for the next loop pass
      wait($wait) //simulates the frame rate (30 fps) otherwise it's 1.000.000 fps (one frame per micro second)
      
      // loop - restarts animation again
        if($frame_idx >= $ANIMATION_FRAMES)
          $frame_idx := 0
        end if
    end while

  //reset animation status when stopped so that it can be started again
    if ($run = 0)
      $playing := 0 
    end if
end function

 

while($run = 1)...end while endlessly running unless $run is set to 0 somewhere else.

$playing := 1 animation state (1 = animation currently running). Prevents the animations from being stacked when new notes are played while the animation is still running. Explained later

$animation -> picture_state := $frame_idx this actually rolls the animation and “renders” the frame by showing the corresponding sprite. So it just swaps the visible sprite each loop pass.

inc($frame_idx) once the frame is “rendered” we can move to the next frame index

wait($wait) $wait = 33.333 micro seconds to “slow down” the animation speed from 1.000.000 fps to in our case  30fps.

if($frame_idx >= $ANIMATION_FRAMES) ... end if when we have reached the last picture state or the last sprite of our sprite sheet we need to reset the frame index so that the animation starts from frame 0 again. This loops the animation otherwise it would stick on the last frame forever. Furthermore $frame_idx would get bigger and bigger.

if ($run = 0) ... end if when $run= 0 no note is held anymore and the animation has stopped. We lastly need to set the animation status to 0 ($playing = 0) which allows to start the animation again when a new note is played.

 

on note – callback

on note 	
  //run animation
    $run := 1 //run when note played
    if($playing = 0) //but only when no animation is currently running 
      call animation	
    end if
end on

 

$run := 1 on every note played (no matter what) we allow the animation to get started

if($playing = 0) ... end if but only if no other animation is already running. (otherwise the animations would kind of stack on each other which looks glitchy)

call animation simply calls the animation and loops it as long as $run stays 1

on release – callback

on release
  if (search(%KEY_DOWN,1) = -1) //checks if any note is still held
    $run := 0 //stop animation
  end if
end on

%KEY_DOWN[<note_number>]a Kontakt internal array. It contains the state of all keys. For example if we hit C3: %KEY_DOWN[60] = 0 otherwise it would be -1

if (search(%KEY_DOWN,1) = -1)... end if check if really all notes are released. If so we set $run to 0 which immediately stops the animation loop

 

Leave a Reply

Your email address will not be published. Required fields are marked *