Clamps Packages
Example using *midi-cc-state*
The exact same behaviour can be achieved using *midi-cc-state*. As mentioned before, the values received for all 128 CC Numbers on all 16 MIDI Channels are registered in ref-objects in the *midi-cc-state* array. By attaching a No description for this link function to any of them the same behaviour as in the previous example can be achieved:
;; define a storage for removal of the watch relations (defparameter *unwatch* nil) (push (watch (lambda () (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 1" (get-val (aref (aref *midi-cc-state* 0) 0))))) *unwatch*) ;; => (#<function (lambda () :in watch) {1003126D9B}>)
Note that the function supplied to the watch function has no arguments: The CC value is obtained by using the #'get-val function in the function body.
To remove the connection between the MIDI controller and the watch function, the function returned by the call to watch has to be called. This can be done like this:
;; remove "connections" (mapc #'funcall *unwatch*) ;; => (#<function (lambda () :in watch) {1003126D9B}>) ;; clear the *unwatch* list (setf *unwatch* nil) ; => nil
Again there is a convenience function for accessing the *midi-cc-state* of Clamps called ccin. The function gets the CC Number and the MIDI channel as argument (both counting from 1) and returns the current value of that controller. The MIDI channel is optional and defaults to the special variable *global-midi-channel*.
Using ccin, the example above can be rewritten like this:
(push (watch (lambda () (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 1" (ccin 1 1)))) *unwatch*) ;; => (#<function (lambda () :in watch) {100981F52B}>)
As it can be quite confusing, when establishing connections between MIDI CC input and actions triggered by it dynamically over the course of a session, it is advisable to establish a bookkeeping infrastructure, which makes it easy to restart a session at any time. Here are examples for both cases:
;; using *midi-cc-fns* (defun init-connections () (remove-all-midi-cc-fns) ;; clear all connections (add-midi-cc-fn (lambda (ccval) (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 1" ccval)) 1 1) (add-midi-cc-fn (lambda (ccval) (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 2" ccval)) 1 2) (add-midi-cc-fn (lambda (ccval) (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 3" ccval)) 1 3)) ;; => init-connections ;; using *midi-cc-state* (defun init-connections () (mapc #'funcall *unwatch*) ;; clear all connections (setf *unwatch* nil) (watch (lambda (ccval) (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 1" ccval))) (add-midi-cc-fn (lambda (ccval) (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 2" ccval)) 1 2) (add-midi-cc-fn (lambda (ccval) (msg :warn "Received CC Value ~a on Midi Channel 1 and CC Number 3" ccval)) 1 3)) ;; => init-connections
In both cases, calling #'init-connections will first remove all existing connections and then set them up. Whenever a new connection is made, it can be added to the init-connections function. After compiling the function and calling it, all connections are set up.