audacia/nyquist/sliders.lsp

197 lines
8.3 KiB
Common Lisp

;; sliders.lsp -- communicate with NyquistIDE to implement control panels
;; Roger B. Dannenberg
;; April 2015
;; (stop-on-zero s) -- a sound that returns 1 until s goes to zero, then
;; the sound terminates. If s comes from a slider and you multiply
;; a sound by (stop-on-zero s), you can interactively stop it
;; (make-slider-panel "name" color) -- sets panel name for the following
;; sliders
;; (make-slider "param" [initial [low high]]) -- create slider named
;; "param" with optional range and initial value. Also returns
;; a sound.
;; (make-button "param" normal) -- create a button named "param" with
;; a starting value of normal (either 0 or 1). While the button
;; in the panel is pressed, the value changes to 1 or 0.
;; (get-slider-value "param") -- when called with a string, this looks up
;; the slider value by name
;; (slider-panel-close "name") -- close the panel window. Values of any
;; existing sliders become undefined.
;; (slider "panel" "name" [dur]) -- make a signal from slider value
;; (slider "name" [dur]) -- make a signal from slider in current panel
;; (get-slider-value "panel" "name") -- get a float value
;; (get-slider-value "name") -- get a float in current panel
;; *active-slider-panel* is the current panel to which sliders are added
;;
(if (not (boundp '*active-slider-panel*))
(setf *active-slider-panel* nil))
;; *panels-in-use* is an assoc list of panels, where each panel
;; is a list of allocated sliders stored as (name number)
;;
(if (not (boundp '*panels-in-use*))
(setf *panels-in-use* nil))
;; allocate-slider-num -- find an unused slider number
;; linear search is used to avoid maintaining a parallel structure
;; for faster searching. We search starting at slider #10, leaving
;; sliders 0-9 unused; for example, you might want to control them
;; via open sound control, so this gives you 10 sliders that are
;; off limits to allocation by the SLIDER function.
;;
;; This code takes advantage of the fact that dotimes and dolist
;; return nil when they end normally, so we signal that we found
;; or did not find i by explicitly returning. Note that RETURN
;; returns from the innermost dotimes or dolist -- they do not
;; return from allocate-slider-num.
;;
(defun allocate-slider-num ()
(dotimes (n 990)
(let ((i (+ n 10)))
(cond ((not (dolist (panel *panels-in-use*)
(cond ((dolist (pair (cdr panel))
(cond ((eql (second pair) i) (return t))))
(return t)))))
(return i))))))
;; remove panel from list of panels
(defun slider-panel-free (panel)
(setf *panels-in-use* (remove panel *panels-in-use* :test #'equal)))
(setfn stop-on-zero snd-stoponzero)
(defun make-slider-panel (name &optional (color 0))
(let ((panel (assoc name *panels-in-use* :test #'equal)))
;; first find if panel already exists. If so, free the resources
(cond (panel
(slider-panel-free panel)))
(setf *active-slider-panel* (list name))
(setf *panels-in-use* (cons *active-slider-panel* *panels-in-use*))
(format t "slider-panel-create: \"~A\" ~A~%" name color)))
(defun make-slider (name &optional (init 0) (low 0) (high 1))
(let ((num (allocate-slider-num)))
(cond ((null num)
(format t "WARNING: MAKE-SLIDER is out of slider numbers. ~A~%"
"No slider created."))
((not (and (stringp name) (numberp init)
(numberp low) (numberp high)))
(display
"WARNING: MAKE-SLIDER called with bad arguments. No slider created"
name init low high)))
;; make sure we have an active panel
(cond ((null *active-slider-panel*)
(make-slider-panel "Controls")))
;; insert new slider into list of sliders in active panel. This
;; is aliased with an element in the assoc list *panels-in-use*.
(rplacd *active-slider-panel* (cons (list name num)
(cdr *active-slider-panel*)))
(format t "slider-create: \"~A\" ~A ~A ~A ~A~%" name num init low high)
num))
(defun make-button (name &optional (normal 0))
(let ((num (allocate-slider-num)))
(cond ((null num)
(format t "WARNING: MAKE-BUTTON is out of slider numbers. ~A~%"
"No button created."))
((not (and (stringp name) (numberp normal)))
(display
"WARNING: MAKE-BUTTON called with bad arguments. No button created"
name normal)))
;; make sure we have an active panel
(cond ((null *active-slider-panel*)
(slider-panel "Controls")))
;; insert new button into list of controls in active panel. This
;; is aliased with an element in the assoc list *panels-in-use*.
(rplacd *active-slider-panel* (cons (list name num)
(cdr *active-slider-panel*)))
(format t "button-create: \"~A\" ~A ~A~%" name num normal)
num))
(defun close-slider-panel (name)
(let ((panel (assoc name *panels-in-use* :test #'equal)))
(cond ((not (stringp name))
(display "WARNING: SLIDER-PANEL-CLOSED called with bad argument."
name)))
(cond (panel
(slider-panel-free panel)
(format t "slider-panel-close: \"~A\"~%" name))
(t
(format t "WARNING: slider panel ~A not found.~%" name)))))
;; SLIDER-LOOKUP - find the slider by name
;;
(defun slider-lookup (name slider)
(let ((panel (assoc name *panels-in-use* :test #'equal)) s)
(cond ((null panel)
(error "Could not find slider panel named" name)))
(setf s (assoc slider (cdr panel) :test #'equal))
(cond ((null s)
(error "Could not find slider named" s)))
(second s)))
;; SLIDER - creates a signal from real-time slider input
;;
;; options are:
;; (SLIDER number [dur])
;; (SLIDER "name" [dur]) -- look up slider in current slider panel
;; (SLIDER "panel" "name" [dur]) -- look up panel, then look up slider
;;
(defun slider (id &optional slider-name dur)
(cond ((and (numberp id) (null slider-name))
(setf dur 1.0))
((and (numberp id) (numberp slider-name) (null dur))
(setf dur slider-name))
((and (stringp id) (null slider-name))
(setf dur 1.0)
(setf id (slider-lookup (car *active-slider-panel*) id)))
((and (stringp id) (numberp slider-name) (null dur))
(setf dur slider-name)
(setf id (slider-lookup (car *active-slider-panel*) id)))
((and (stringp id) (stringp slider-name) (null dur))
(setf dur 1.0)
(setf id (slider-lookup id slider-name)))
((and (stringp id) (stringp slider-name) (numberp dur))
(setf id (slider-lookup id slider-name)))
(t
(error "SLIDER called with invalid arguments")))
(setf dur (get-duration dur))
(setf id (round id)) ;; just to make sure it's an integer
(cond ((or (< id 0) (>= id 1000))
(error "SLIDER index out of bounds" id)))
(display "slider" id slider-name dur)
(snd-slider id *rslt* *sound-srate* dur))
(if (not (boundp '*lpslider-cutoff*))
(setf *lpslider-cutoff* 20.0))
(defun lpslider (id &optional slider-name dur)
(lp (slider id slider-name dur) 20.0))
;; save built-in get-slider-value so we can redefine it
(if (not (fboundp 'prim-get-slider-value))
(setfn prim-get-slider-value get-slider-value))
(defun get-slider-value (id &optional slider-name)
(cond ((and (numberp id) (null slider-name)) nil)
((and (stringp id) (null slider-name))
(setf id (slider-lookup (car *active-slider-pael*) id)))
((and (stringp id) (stringp slider-name))
(setf id (slider-lookup id slider-name)))
(t
(error "GET-SLIDER-VALUE called with invalid arguments")))
;; further parameter checking is done in get-slider-value:
(prim-get-slider-value id))
(autonorm-off)
(snd-set-latency 0.02)
(print "**********sliders.lsp************************")
(print "WARNING: AUTONORM IS NOW TURNED OFF")
(print "WARNING: AUDIO LATENCY SET TO 20MS")
(print "To restore settings, execute (autonorm-on) and")
(print " (set-audio-latency 0.3)")
(print "*********************************************")