qball.lsp Version 2.01 15 February 05 Author Steven Jones Contact jones57@swbell.net include the word "nyquist" in subject line The contents of this file are released under the terms of the GNU General Public License. See the file LICENSE.txt QBALL is a recombinant(?) event sequencer. At its simplest you specify cue and pitch lists. Elements from each list are combined in sequence to generate the result. An event is generated for each element in the cue list. If elements from the pitch list are exhausted before the end of the cue list is reached, they are reused in a cyclical manner. This re-combination is generalized, any number of keyword arguments may be passed, in cyclical sequence, to the sound generation function. Indeed the sound generating function itself may change on each iteration. There are two loops; inner and outer. Parameters may be set to change in either loop. The inner loop generates an event for each element in the cue list. The outer loop repeats inner loop. By default the outer loop contains transpose and amplitude scale parameters but other parameters may be added. See also rball.lsp
class
Combines a symbol with list of sequential values. The symbol is intended to serve as a keyword argument to functions.
method
(send qball:keyarg :new key values) Construct new qball:keyarg instance key - symbol. values - vector | list | symbol | number. The possible value which may be applied to key. return - object. A new instance of qball:keyarg
method
(send /qball:keyarg/ :reset) Reset to initial value. See :next method
method
(send /qball:keyarg :length) return - integer. Number of elements in values list.
method
(send /qball:keyarg/ :key) return - symbol. The keyword symbol.
method
(send /qball:keyarg/ :next) return - any. The next element in the values list. If the final element has been reached start over from the beginning of the list.
method
(send /qball:keyarg/ :rep [echo]) String representation of this object. echo - bool. If true output results to terminal, default t return - String | NIL. If echo is nil return the string representation of this object.
class
Instances of qball:cuelist represent rhythmic patterns.
method
(send qball:cuelist :new points [count [unit]]) Object constructor. points - array | list | number. The rhythmic pattern as a list of numbers. count - integer. The number of basic time units. Default 16 unit - symbol | number. The basic time unit may be specified symbolically or as a number (in seconds). If specified symbolically tempo changes may be utilized. See tempo.lsp, default 's return - object. A new instance of qball:cuelist. Each instance consist of a basic time unit, a count of basic time units and a list defining a rhythmic pattern. The combination of unit and count essentially set a time signature. The default is 1 bar of 16 16th notes. The cue list is a list of numbers specifying when within the bar event occurs. For example to specify each quarter note the list would be (0 4 8 12).
method
(send /qball:cuelist/ :reset) Initialize, see :next method.
method
(send /qball:cuelist/ :length) return - integer. the number of elements in the cue list.
method
(send /qball:cuelist/ :next) return - flonum. The time for the next event. If the previous event was the final event, this event will be the initial event with a time offset placing it in the next bar. The bar offset is determined by the unit and count values.
method
(send /qball:cuelist/ :rep [echo]) echo - bool. Flag if true dump results to standard out, default t return - string | nil. The string representation for this object.
class
A recombinant(?) event sequencer.
method
(send qball:class :new [:u][:c][:n][:cue][:fn][:p][:dur][:okeys][:ikeys][:xpose][:db][:reset]) Construct new qball:class instance. Each instance has a time signature (:u and :c), number of phrase repetitions (:n), phrase cue list (:cue), function list (:fn), pitch list (:p), event duration list (:dur), list of key word parameters updated on phrase repetition (:okeys), list of parameters updated every note (:ikeys), and transposition and amplitude on phrase repetition (:xpose and :db) :u - symbol | flonum. The basic time unit. If time is specified symbolically tempo changes could be introduced. See tempo.lsp Default is 's (16th note) :c - integer. Count of basic time units per phrase, default 16 :n - integer. Number of phrase repetitions, default 1 :cue - list | flonum. An event is generated for each element in the cue list and thereby the cue list sets the phrase rhythm. For example assuming the default of 1 bar of 1/16 notes (unit = s, count= 16) a cue list of (0 4 8 12) would produce events at quarter note intervals. Typically the highest value in the cue list is less then count. This is not an enforced restriction if a cue list contains values greater or equal to count the phrases will overlap. The default is a single event at time 0 (0). :fn - list | closure | string. The list of sound generating functions. All sound functions should have the form (lambda (pitch dur &key [...]...)) or (lambda (pitch dur &rest args)...) and return sound. The functions are used sequentially and cyclically for each cue list event. There is a bit of UGLINESS here. There are two distinct ways to use an instance of qball:class. One way is to generate sound objects using the :render method. The other use is to generate an equivalent text score using the :->score method. These two modes are not compatible. To use the :->score method you need to specify functions by their string name. To use the :render method you must specify functions directly usually by the #' read macro. If you try to use :render with functions specified as strings the procedure will fail. The default is a single "pluck" function :p - list | flonum. List of pitch values as MIDI key numbers. Pitch values are used sequentially and cyclically for each cue list event. Default (a4) :dur - list | flonum. List of event durations in seconds. Duration values are used sequentially and cyclically for each cue list event. Default (unit) :okeys - list. A list of keyword arguments passed to the sound functions and updated in the outer loop (on each phrase repetition). The form of the list should be ((':key1 v1 v2 v3..)(':key2 v1 v2 v3...)...(':keyn v1 v2 v3...)) On each phrase repetition the keywords are aggregated with the values v1, v2 ... and passed to the sound generating function. The values are used in sequence and in a cyclical manner. Default '() :ikeys - list. A list of keyword arguments passed to the sound generation functions updated in the inner loop (on each event in the cue list). The form of the list is the same as for :okeys. Default '() :xpose - list. A list of transpositions updated in the outer loop. Default (0) :db - list. A list of amplitude scale (in db) updated in the outer loop. Default (0) :reset - boolean. If true all argument list, :p :dur :okeys :ikeys :xpose and :db are reset on each phrase repetition. Default nil. return - object. A new instance of qball:class
method
(send /qball:class/ :reset) Reset all internal argument list to their initial state.
method
(send /qball:class/ :rep [echo]) echo - boolean. If true dump results to standard out. Default t return - string | nil. String representation of this object.
method
(send /qball:class/ :->score [:echo][:n][:reset]) Produce text score. See ugliness note in constructor. :echo - boolean. If true dump results to standard out. Default t :n - integer. Number of phrase repetitions, default 1 :reset - boolean. If true reset object state prior to generating score. return - string.
method
(send /qball:class/ [:n][:reset]) Generate sound object. See ugliness note in constructor method. :n - integer. Number of phrase repetitions, default 1 :reset - boolean. If true reset all argument list prior to generating sound. Default nil return - sound.
;; qball.lsp ;; Version 2.01 15 February 05 ;; Author Steven Jones ;; ;; Contact jones57@swbell.net include the word "nyquist" in subject line ;; ;; The contents of this file are released under the terms of the GNU General ;; Public License. See the file LICENSE.txt ;; ;; QBALL is a recombinant(?) event sequencer. At its simplest you specify cue ;; and pitch lists. Elements from each list are combined in sequence to ;; generate the result. An event is generated for each element in the cue ;; list. If elements from the pitch list are exhausted before the end of the ;; cue list is reached, they are reused in a cyclical manner. This ;; re-combination is generalized, any number of keyword arguments may be ;; passed, in cyclical sequence, to the sound generation function. Indeed the ;; sound generating function itself may change on each iteration. There are ;; two loops; inner and outer. Parameters may be set to change in either ;; loop. The inner loop generates an event for each element in the cue ;; list. The outer loop repeats inner loop. By default the outer loop contains ;; transpose and amplitude scale parameters but other parameters may be added. ;; ;; See also rball.lsp (require 'tempo) (provide 'qball) (current-file "qball") ; *************************************************************************** ; QBALL:KEYARG CLASS ; *************************************************************************** ;; @doc class qball:keyarg ;; Combines a symbol with list of sequential values. The symbol is intended to ;; serve as a keyword argument to functions. ;; (setf qball:keyarg (send class :new '(.key .vector .ptr .len))) ;; @doc method qball:keyarg :new ;; (send qball:keyarg :new key values) ;; Construct new qball:keyarg instance ;; ;; key - symbol. ;; values - vector | list | symbol | number. The possible value which may be ;; applied to key. ;; return - object. A new instance of qball:keyarg ;; (send qball:keyarg :answer :isnew '(key values) '((setf .key key .vector (cond ((listp values)(list->vector values)) ((arrayp values) values) (t (vector values))) .ptr 0 .len (length .vector) ))) ;; @doc method qball:keyarg :reset ;; (send /qball:keyarg/ :reset) ;; Reset to initial value. See :next method ;; (send qball:keyarg :answer :reset '() '((setf .ptr 0))) ;; @doc method qball:keyarg :length ;; (send /qball:keyarg :length) ;; return - integer. Number of elements in values list. ;; (send qball:keyarg :answer :length '() '((length .vector))) ;; @doc method qball:keyarg :key ;; (send /qball:keyarg/ :key) ;; return - symbol. The keyword symbol. ;; (send qball:keyarg :answer :key '() '(.key)) ;; @doc method qball:keyarg :next ;; (send /qball:keyarg/ :next) ;; return - any. The next element in the values list. If the final element has ;; been reached start over from the beginning of the list. ;; (send qball:keyarg :answer :next '() '((if (= .ptr .len) (setf .ptr 0)) (prog1 (aref .vector .ptr) (setf .ptr (1+ .ptr)) ))) ;; @doc method qball:keyarg :rep ;; (send /qball:keyarg/ :rep [echo]) ;; String representation of this object. ;; echo - bool. If true output results to terminal, default t ;; return - String | NIL. If echo is nil return the string representation of ;; this object. ;; (send qball:keyarg :answer :rep '(&optional (echo t)) '((format echo "QBALL:KEYARG ~a ~a" .key .vector))) ; *************************************************************************** ; QBALL:CUELIST CLASS ; *************************************************************************** ;; @doc class qball:cuelist ;; Instances of qball:cuelist represent rhythmic patterns. ;; (setf qball:cuelist (send class :new '(.points .unit .count .ptr .offset))) ;; @doc method qball:cuelist ;; (send qball:cuelist :new points [count [unit]]) ;; Object constructor. ;; ;; points - array | list | number. The rhythmic pattern as a list of numbers. ;; ;; count - integer. The number of basic time units. Default 16 ;; ;; unit - symbol | number. The basic time unit may be specified symbolically ;; or as a number (in seconds). If specified symbolically tempo ;; changes may be utilized. See tempo.lsp, default 's ;; ;; ;; return - object. A new instance of qball:cuelist. Each instance consist of ;; a basic time unit, a count of basic time units and a list ;; defining a rhythmic pattern. The combination of unit and count ;; essentially set a time signature. The default is 1 bar of 16 ;; 16th notes. The cue list is a list of numbers specifying when ;; within the bar event occurs. For example to specify each quarter ;; note the list would be (0 4 8 12). ;; (send qball:cuelist :answer :isnew '(points &optional (count 16)(unit 's)) '((setf .points (cond ((arrayp points) points) ((listp points)(list->vector points)) (t (list->vector (->list points))))) (setf .unit unit .count count .ptr 0 .offset 0))) ;; @doc method qball:cuelist :reset ;; (send /qball:cuelist/ :reset) ;; Initialize, see :next method. ;; (send qball:cuelist :answer :reset '() '((setf .ptr 0 .offset 0))) ;; @doc method qball:cuelist :length ;; (send /qball:cuelist/ :length) ;; return - integer. the number of elements in the cue list. ;; (send qball:cuelist :answer :length '() '((length .points))) ;; @doc method qball:cuelist :next ;; (send /qball:cuelist/ :next) ;; ;; return - flonum. The time for the next event. If the previous event was the ;; final event, this event will be the initial event with a time ;; offset placing it in the next bar. The bar offset is determined by ;; the unit and count values. ;; (send qball:cuelist :answer :next '() '((if (= .ptr (length .points)) (setf .ptr 0 .offset (+ .offset (* .count (get-time-symbol-value .unit))))) (prog1 (+ .offset (* (get-time-symbol-value .unit)(aref .points .ptr))) (setf .ptr (1+ .ptr)) ))) ;; @doc method qball:cuelist :rep ;; (send /qball:cuelist/ :rep [echo]) ;; ;; echo - bool. Flag if true dump results to standard out, default t ;; return - string | nil. The string representation for this object. ;; (send qball:cuelist :answer :rep '(&optional (echo t)) '((format echo "QBALL:CUELIST unit ~a count ~a ~a" .unit .count .points))) ; *************************************************************************** ; QBALL:CLASS CLASS ; *************************************************************************** ;; @doc class qball:class ;; A recombinant(?) event sequencer. ;; (setf qball:class (send class :new '(.unit .count .reps .reset .cue-list .transpose-list .amp-scale-list .function-list .pitch-list .duration-list .outer-key-arguments .inner-key-arguments ))) ;; @doc method qball:class :new ;; (send qball:class :new [:u][:c][:n][:cue][:fn][:p][:dur][:okeys][:ikeys][:xpose][:db][:reset]) ;; Construct new qball:class instance. Each instance has a time signature ;; (:u and :c), number of phrase repetitions (:n), phrase cue list (:cue), ;; function list (:fn), pitch list (:p), event duration list (:dur), list of ;; key word parameters updated on phrase repetition (:okeys), list of ;; parameters updated every note (:ikeys), and transposition and amplitude on ;; phrase repetition (:xpose and :db) ;; ;; :u - symbol | flonum. The basic time unit. If time is specified ;; symbolically tempo changes could be introduced. See tempo.lsp ;; Default is 's (16th note) ;; ;; :c - integer. Count of basic time units per phrase, default 16 ;; ;; :n - integer. Number of phrase repetitions, default 1 ;; ;; :cue - list | flonum. An event is generated for each element in the cue ;; list and thereby the cue list sets the phrase rhythm. For example ;; assuming the default of 1 bar of 1/16 notes (unit = s, count= 16) ;; a cue list of (0 4 8 12) would produce events at quarter note ;; intervals. Typically the highest value in the cue list is less ;; then count. This is not an enforced restriction if a cue list ;; contains values greater or equal to count the phrases will overlap. ;; The default is a single event at time 0 (0). ;; ;; :fn - list | closure | string. The list of sound generating functions. ;; All sound functions should have the form ;; (lambda (pitch dur &key [...]...)) or ;; (lambda (pitch dur &rest args)...) ;; and return sound. The functions are used sequentially and ;; cyclically for each cue list event. ;; ;; There is a bit of UGLINESS here. There are two distinct ways to use ;; an instance of qball:class. One way is to generate sound objects ;; using the :render method. The other use is to generate an ;; equivalent text score using the :->score method. These two modes ;; are not compatible. To use the :->score method you need to specify ;; functions by their string name. To use the :render method you must ;; specify functions directly usually by the #' read macro. If you ;; try to use :render with functions specified as strings the ;; procedure will fail. ;; ;; The default is a single "pluck" function ;; ;; :p - list | flonum. List of pitch values as MIDI key numbers. Pitch ;; values are used sequentially and cyclically for each cue list ;; event. Default (a4) ;; ;; :dur - list | flonum. List of event durations in seconds. Duration values ;; are used sequentially and cyclically for each cue list event. ;; Default (unit) ;; ;; :okeys - list. A list of keyword arguments passed to the sound functions ;; and updated in the outer loop (on each phrase repetition). The ;; form of the list should be ;; ((':key1 v1 v2 v3..)(':key2 v1 v2 v3...)...(':keyn v1 v2 v3...)) ;; On each phrase repetition the keywords are aggregated with the ;; values v1, v2 ... and passed to the sound generating function. ;; The values are used in sequence and in a cyclical manner. ;; Default '() ;; ;; :ikeys - list. A list of keyword arguments passed to the sound generation ;; functions updated in the inner loop (on each event in the cue list). ;; The form of the list is the same as for :okeys. Default '() ;; ;; :xpose - list. A list of transpositions updated in the outer loop. ;; Default (0) ;; ;; :db - list. A list of amplitude scale (in db) updated in the outer loop. ;; Default (0) ;; ;; :reset - boolean. If true all argument list, :p :dur :okeys :ikeys :xpose ;; and :db are reset on each phrase repetition. Default nil. ;; ;; return - object. A new instance of qball:class ;; (send qball:class :answer :isnew '(&rest args) '((setf .unit (get-keyword-value ':u args 's)) (setf .count (get-keyword-value ':c args 16)) (setf .reps (get-keyword-value ':n args 1)) (setf .reset (get-keyword-value ':reset args nil)) (setf .cue-list (send qball:cuelist :new (get-keyword-value ':cue args 0) .count .unit)) (setf .transpose-list (send qball:keyarg :new ':xpose (get-keyword-value ':xpose args 0))) (setf .amp-scale-list (send qball:keyarg :new ':db (get-keyword-value ':db args 0))) (setf .function-list (send qball:keyarg :new ':fn (get-keyword-value ':fn args #'(lambda (p d &rest dummy) (pluck p d))))) (setf .pitch-list (send qball:keyarg :new ':pitch (get-keyword-value ':p args a4))) (setf .duration-list (send qball:keyarg :new ':dur (get-keyword-value ':dur args (get-time-symbol-value .unit)))) (setf .outer-key-arguments '()) (dolist (kf (get-keyword-value ':okeys args '())) (setf .outer-key-arguments (cons (send qball:keyarg :new (car kf)(cdr kf)) .outer-key-arguments))) (setf .inner-key-arguments '()) (dolist (kf (get-keyword-value ':ikeys args '())) (setf .inner-key-arguments (cons (send qball:keyarg :new (car kf)(cdr kf)) .inner-key-arguments))) )) ;; @doc method qball:class :reset ;; (send /qball:class/ :reset) ;; Reset all internal argument list to their initial state. ;; (send qball:class :answer :reset '() '((send .cue-list :reset) (send .transpose-list :reset) (send .amp-scale-list :reset) (send .function-list :reset) (send .pitch-list :reset) (send .duration-list :reset) (dolist (oka .outer-key-arguments) (send oka :reset)) (dolist (ika .inner-key-arguments) (send ika :reset)))) ;; @doc method qball:class :rep ;; (send /qball:class/ :rep [echo]) ;; ;; echo - boolean. If true dump results to standard out. Default t ;; return - string | nil. String representation of this object. ;; (send qball:class :answer :rep '(&optional (echo t)) '((let ((acc (format nil "~%QBALL:CLASS :u ~a :c ~a :n ~a :reset ~a" .unit .count .reps .reset))) (setf acc (strcat acc (format nil "~% :cue ~a" (send .cue-list :rep nil)))) (setf acc (strcat acc (format nil "~% :fn ~a" (send .function-list :rep nil)))) (setf acc (strcat acc (format nil "~% :dur ~a" (send .pitch-list :rep nil)))) (setf acc (strcat acc (format nil "~% :dur ~a" (send .duration-list :rep nil)))) (setf acc (strcat acc (format nil "~% Outer loop"))) (setf acc (strcat acc (format nil "~% :xpose ~a" (send .transpose-list :rep nil)))) (setf acc (strcat acc (format nil "~% :db ~a" (send .amp-scale-list :rep nil)))) (dolist (kf .outer-key-arguments) (setf acc (strcat acc (format nil "~% ~a" (send kf :rep nil))))) (setf acc (strcat acc (format nil "~% Inner loop"))) (dolist (kf .inner-key-arguments) (setf acc (strcat acc (format nil "~% ~a" (send kf :rep nil))))) (format echo "~%~a~%" acc)))) ;; @doc method qball:class :->score ;; (send /qball:class/ :->score [:echo][:n][:reset]) ;; Produce text score. See ugliness note in constructor. ;; ;; :echo - boolean. If true dump results to standard out. Default t ;; ;; :n - integer. Number of phrase repetitions, default 1 ;; ;; :reset - boolean. If true reset object state prior to generating score. ;; ;; return - string. ;; (send qball:class :answer :->score '(&key (echo t)(n nil)(reset nil)) '((let ((acc "") (oargs "") (iargs "") ) (if (or reset .reset) (send self :reset)) (setf acc (format nil "(sim ")) (dotimes (rep (or n .reps 1)) ;LOOP 1 (setf oargs "") (setf acc (format nil "~a~% (transpose ~a (scale-db ~a (sim " acc (su:zfill (->string (send .transpose-list :next)) 2) (su:rjust (->string (send .amp-scale-list :next)) 3))) (dolist (oai .outer-key-arguments) ;LOOP 2 (setf oargs (format nil "~a ~a ~a " oargs (send oai :key) (send oai :next))) ) ;END LOOP 2 (dotimes (cp (send .cue-list :length)) ;LOOP 3 (setf acc (format nil "~a~% (at ~a (cue " acc (su:ljust (->string (send .cue-list :next)) 6))) (setf acc (format nil "~a (~a ~a ~a ~a" acc (send .function-list :next) (su:zfill (->string (send .pitch-list :next)) 3) (su:ljust (->string (send .duration-list :next)) 4) oargs)) (setf iargs "") (dolist (iai .inner-key-arguments) ;LOOP 4 (setf iargs (format nil "~a ~a ~a " iargs (send iai :key) (send iai :next))) ) ;END LOOP 4 (setf acc (format nil "~a ~a ))) " acc iargs)) ) ;END LOOP 3 (setf acc (format nil "~a~% )))" acc)) ) ;END LOOP 1 (setf acc (format nil "~a~%)" acc)) (format echo "~%~a~%" acc)))) ;; @doc method qball:class :render ;; (send /qball:class/ [:n][:reset]) ;; Generate sound object. ;; See ugliness note in constructor method. ;; ;; :n - integer. Number of phrase repetitions, default 1 ;; :reset - boolean. If true reset all argument list prior to generating ;; sound. Default nil ;; return - sound. (send qball:class :answer :render '(&key (n nil)(reset nil)) '((if (or reset .reset) (send self :reset)) (let (oargs iargs arglist) (simrep (i (truncate (or n .reps 1))) (progn (setf oargs '()) (dolist (oai .outer-key-arguments) (setf oargs (append oargs (list (send oai :key)(send oai :next))))) (transpose (send .transpose-list :next) (scale-db (send .amp-scale-list :next) (simrep (cp (send .cue-list :length)) (at (send .cue-list :next) (cue (apply (send .function-list :next) (progn (setf iargs '()) (dolist (iai .inner-key-arguments) (setf iai (append iargs (list (send iai :key)(send iai :next))))) (setf arglist (append (list (send .pitch-list :next) (send .duration-list :next)) oargs iargs)) ) ;; progn ) ;; apply ) ;; cue ) ;; at ) ;; simrep ) ;; scale-db ) ;; transpose ) ;; progn ) ;; simrep )))