Clamps Packages
Using a Korg NanoKONTROL2 Controller
- Preparation
Restore the NanoKontrol2 to the factory defaults and set the LED Mode in the Common section of the Korg Kontrol Editor application to "External".
- Initialization
To initialize the controller, issue the add-midi-controller method with the nanoktl2-midi class and a custom ID and optionally its channel as arguments:
;; create an instance of a NanoKONTROL2 midi-controller with ID :nk2 ;; using MIDI channel 1 and add it to the registry: (add-midi-controller 'nanoktl2-midi :nk2 :chan 1)
If the NanoKONTROL2 hardware device is connected to the incudine in/output ports, it now can be used. For details about the slots and their layout, refer to nanoktl2-midi in the Clamps Dictionary.
- Adding Behaviour
We can add behaviour to the instance by attaching watch functions to the faders and knobs or trigger functions to the buttons:
- Faders and Knobs
(with-slots (unwatch nk2-faders) (find-controller :nk2) (dotimes (i 8) (let ((n i)) (push (watch (lambda () (msg :warn (format nil "Knob ~a turned: ~a" (1+ n) (get-val (aref nk2-faders n)))))) unwatch) (push (watch (lambda () (msg :warn (format nil "Fader ~a moved: ~a" (1+ n) (get-val (aref nk2-faders (+ n 8))))))) unwatch)))) ;; => nil ;; ;; output in the REPL: ;; warn: Knob 1 turned: 0 ;; warn: Fader 1 moved: 0 ;; warn: Knob 2 turned: 0 ;; warn: Fader 2 moved: 0 ;; warn: Knob 3 turned: 0 ;; warn: Fader 3 moved: 0 ;; warn: Knob 4 turned: 0 ;; warn: Fader 4 moved: 0 ;; warn: Knob 5 turned: 0 ;; warn: Fader 5 moved: 0 ;; warn: Knob 6 turned: 0 ;; warn: Fader 6 moved: 0 ;; warn: Knob 7 turned: 0 ;; warn: Fader 7 moved: 0 ;; warn: Knob 8 turned: 0 ;; warn: Fader 8 moved: 0
The output in the REPL signals that the watch function has been established for all knobs and faders.
Moving a fader works like expected:
clamps> warn: Fader 1 moved: 1.0 warn: Fader 1 moved: 2.0 warn: Fader 1 moved: 3.0 warn: Fader 1 moved: 4.0 warn: Fader 1 moved: 5.0 warn: Fader 1 moved: 6.0 warn: Fader 1 moved: 7.0 warn: Fader 1 moved: 8.0 warn: Fader 1 moved: 9.0 <...> clamps>
Important Note
In the above Fader/Knob example, it might not be obvious that the binding of n to i using the let in the body of the dotimes is crucial for this to work. If it isn't clear, why it wouldn't work to use i directly in the lambda forms of the watch expressions, refer to the section Excursion: Closures. The section tries to shed some light on binding and the difference of compile-time vs. run-time. Knowing how to deal with closures is a recurring necessity when working with Clamps and a thorough understanding indispensible.
- Buttons
A button is inspired by a Gui element of a button, adding click events on the button. It is implemented in cl-refs as a bang-object, a ref-cell with an additional trigger function, which ist invoked on a button press/click.
To add an action to the button, use the function add-trigger-fn.
Here is an example:
(add-trigger-fn (tr-play (find-controller :nk2)) (lambda () (msg :warn "tr-play Button pressed."))) ;; => nil
(trigger (tr-play (find-object :nk2))) ;; output in the REPL: ;; warn: tr-play Button pressed.
This behaviour is automatically implemented in the handle-midi-in method of a nanoktl2-midi instance: When pressing the tr-play button on the NanoKONTROL2 Hardware Controller, it will call the trigger function on the tr-play slot of the instance and you should see the same message in the REPL as before:
clamps> warn: tr-play Button pressed. clamps>
The same result is achievd by calling trigger with the bang-object as argument:
Removing the trigger functions from the button can be done using the function remove-all-triggers:
(remove-all-triggers (tr-play (find-controller :nk2)))
Like watch, add-trigger-fn returns a function to remove the trigger function(s) from the ref-cell. Therefore you can also implement adding the trigger function to the nanoktl2-midi instance like this to be able to have a finer control over selectively adding and removing trigger functions for ref-cells:
(defvar *unwatch-triggers* nil) (push (add-trigger-fn (tr-play (find-controller :nk2)) (lambda () (msg :warn "tr-play Button pressed."))) *unwatch-triggers*) ;; => (#<function (lambda () :in add-trigger-fn) {100A94123B}> ;; remove the trigger function: (progn (map nil #'funcall *unwatch-triggers*) (setf *unwatch-triggers* nil)) ; => nil ;; abbreviation of the above expression: (setf *unwatch-triggers* (map nil #'funcall *unwatch-triggers*)) ; => nil
Note
As buttons are extended ref-cells, they also have a value slot which can be accessed and manipulated like any normal ref-cell. An application can be use this to capture the state of the button (on, off, flashing, etc.) in relation to, but independent from the button-press (trigger) action.
- Faders and Knobs