zone.lsp Version 2.00 10 November 2004 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 Provides framework for creating sampler like instruments with multiple keyboard zones and velocity cross-switch.
function
(zone:sampler pitch dur file [:loop-start][:loop-dur][:loop-index][:refkey][:sr][:env][:fm][:vib][:vfrq][:vdelay][:vtab][:vphase]) A sample file play back instrument. pitch - flonum. MIDI key number. dur - flonum. Duration in seconds. file - string. Fully qualified sample file name :loop-start - flonum. Loop start time in seconds. The sound is produced from the beginning up to loop-start seconds. Thereafter the sample is looped. Default 0 :loop-dur - flonum. Loop duration in seconds. Default 0.1 :loop-index - integer. The loop start may be moved forward or backward in loop-dur increments. Default 0 :refkey - integer. The reference MIDI key number. :sr - integer. Sample rate. Normally the sample rate is determined automatically from the sound file header. You may use sr to set an explicit rate. :env - sound. The amplitude envelope. Default is a rectangular "gate" of dur seconds. :fm - sound. An auxiliary fm modulator. Default silence :vib - flonum. Vibrato depth (scaled to MIDI controller value), default 0. :vfrq - flonum. Vibrato frequency, default 7 Hz. :vdelay - flonum. Vibrato delay, default 0 second. :vtab - table. Vibrato wave table, default *tri-table* :vphase - any. Vibrato phase, see phase function in utilities.lsp, default 0 return - sound or sound vector.
class
Instances of zone respond to a fixed MIDI key range. Additionally zones select one of three sample files in response to MIDI velocity. The three velocity ranges are pp, mf and ff.
method
(send zone :new floor ceiling [vswitch]) Construct new zone for MIDI key range [floor, ceiling] inclusive. floor - integer. The minimum MIDI key to which the zone responds. ceiling - integer. The maximum MIDI key to which the zone responds. 0 <= floor < ceiling <= 127 vswitch - cons. The velocity cross switch points. vswitch has the form (pp ff). MIDI velocity values less then pp use the pp file, velocities greater then ff use ff. Default (48 . 80) return - object. A new instance of zone
method
(send /zone/ :range [floor [ceiling]]) Get and optionally set key range. floor - integer. 0 <= floor < ceiling <= 127 ceiling - integer. 0 <= floor < ceiling <= 127 return - cons (floor . ceiling)
method
(send /zone/ :vswitch [pp [ff]]) Get and optionally set velocity switch points pp - integer. MIDI velocity 0 <= pp < ff <= 127 ff - integer. MIDI velocity 0 <= pp < ff <= 127 return - cons (pp . ff)
method
(send /zone/ :respondp key) Test if this zone responds to given key. key - flonum. MIDI key number return - bool. True iff floor <= key <= ceiling
method
(send /zone/ :set vsel file loop-start loop-dur [:loop-index][:sr][:env][:fm][:vib][:vfrq][:vdelay][:vtab][:vphase][:transpose][:tune][:db]) Set zones response to given velocity range. vsel - symbol. Velocity range select. The three valid symbols are 'PP 'MF and 'FF any other symbol causes an error. file - string. Fully qualified sample file name. loop-start - flonum. Loop start time in seconds. loop-dur - flonum. Loop duration in seconds. :loop-index - integer. Moves loop-start in increments of loop duration seconds. For negative indexes the loop start is moved backwards towards the sample's beginning. :sr - integer. Sample rate. Normally the sample rate is determined automatically from the sound file header. The sr keyword maybe used to set an explicit rate if desired. :env - sound. The amplitude envelope. Default rectangular "gate" of dur seconds. :fm - sound. Axillary fm source. Default silence. :vib - flonum. Vibrato depth (scaled to MIDI controller range), default 0. :vfrq - flonum. Vibrato frequency, default 7 Hz. :vdelay - flonum. Vibrato delay, default 0 second. :vtab - table. Vibrato wave table, default *tri-table* :vphase - any. Vibrato phase. See phase function in utilities.lsp default 0. :transpose - integer. Transposition in half-step, default 0 :tune - flonum. (de)tune ratio. The tuning ration is the reciprocal of a stretch factor. Default 1 :db - flonum. Amplitude scale in db, default 0
method
(send /zone/ :render pitch dur arglist) Render sound in response to specific key. If this zone does not respond to the given key, return silence. pitch - flonum. MIDI key number. dur - flonum. Duration in seconds. arglist - list. A list of keyword/value pairs. The recognized keywords are :vel - flonum. midi velocity :loop-index - integer. :env - sound. Amplitude envelope :fm - sound. FM modulator. :vib - flonum. Vibrato depth. :vfrq - flonum. Vibrato frequency. :vdelay - flonum. Vibrato delay. :vtab - table. Vibrato wave table. :vphase - any. Vibrato phase. return - sound or sound vector.
class
zbi ~ "Zone Based Instrument" An instance of zone:zbi is essentially a list of zones and forms the basis for sampler like instruments.
method
(send zone:zbi :new path [vmap]) Construct new zone:zbi instance. path - string. The sample file directory. vmap - object. An instance of map, default map:*vel6* return - object. An instance of zone:zbi
method
(send /zone:zbi/ :add z) Add zone to zone list. iff it is not already a member of the list. Make z the current working zone z - object. An object which implements the same interface as zone. ISSUE: Clarify what methods are required.
method
(send /zone:zbi/ :new-zone floor ceiling [vswitch]) Add new zone to zone list. The zone is made the "current" zone. See the :set method floor - flonum. The least MIDI key the new zone responds to. ceiling - flonum. The greatest MIDI key the zone responds to. 0 <= floor < ceiling <= 127 vswitch - cons. Velocity cross switch points, default (48 . 80) return - object. The new zone.
method
(send /zone:zbi/ :render pitch dur arglist) Render sound object by iterating over zone list. Each zone returns a sound but if the zone does not respond to the given key its output is silence. The zone outputs are summed and then amplitude scaled in response to MIDI velocity. pitch - flonum. MIDI key number. dur - flonum. Duration in seconds
;;zone.lsp ;; Version 2.00 10 November 2004 ;; 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 ;; ;; Provides framework for creating sampler like instruments with multiple ;; keyboard zones and velocity cross-switch. (require 'dlfo) (require 'map) (require 'map-vel6) (require 'map-vibrato) (require 'sam) (provide 'zone) (current-file "zone") ;; @doc function zone:sampler ;; (zone:sampler pitch dur file [:loop-start][:loop-dur][:loop-index][:refkey][:sr][:env][:fm][:vib][:vfrq][:vdelay][:vtab][:vphase]) ;; A sample file play back instrument. ;; ;; pitch - flonum. MIDI key number. ;; dur - flonum. Duration in seconds. ;; file - string. Fully qualified sample file name ;; :loop-start - flonum. Loop start time in seconds. The sound is produced ;; from the beginning up to loop-start seconds. Thereafter the ;; sample is looped. Default 0 ;; :loop-dur - flonum. Loop duration in seconds. Default 0.1 ;; :loop-index - integer. The loop start may be moved forward or backward in ;; loop-dur increments. Default 0 ;; :refkey - integer. The reference MIDI key number. ;; :sr - integer. Sample rate. Normally the sample rate is determined ;; automatically from the sound file header. You may use sr to ;; set an explicit rate. ;; :env - sound. The amplitude envelope. Default is a rectangular ;; "gate" of dur seconds. ;; :fm - sound. An auxiliary fm modulator. Default silence ;; :vib - flonum. Vibrato depth (scaled to MIDI controller value), ;; default 0. ;; :vfrq - flonum. Vibrato frequency, default 7 Hz. ;; :vdelay - flonum. Vibrato delay, default 0 second. ;; :vtab - table. Vibrato wave table, default *tri-table* ;; :vphase - any. Vibrato phase, see phase function in utilities.lsp, ;; default 0 ;; ;; return - sound or sound vector. ;; (defun zone:sampler (pitch dur file &key loop-start loop-dur loop-index refkey sr env fm vib vfrq vdelay vtab vphase) (let (hz loop-end vamp vattack sndobj vibsig) (setq hz (* (step-to-hz pitch))) (setq refkey (or refkey pitch)) (setq loop-start (or loop-start 0)) (setq loop-dur (or loop-dur 0.1)) (setq loop-index (or loop-index 0)) (setq loop-start (+ loop-start (* loop-dur loop-index))) (setq loop-end (+ loop-start loop-dur)) (setq sndobj (sam:read file :dur loop-end)) (setq sr (or sr (snd-srate sndobj))) (setq vamp (map:vibrato hz (or vib 0))) (setq vfrq (or vfrq 7.00)) (setq vattack (* (or vdelay 0) 0.25)) (setq vdelay (* (or vdelay 0) 0.75)) (setq vtab (or vtab *tri-table*)) (setq vibsig (scale vamp (dlfo vfrq dur :delay vdelay :attack vattack :phase (phase vphase) :tab vtab))) (setq fm (or fm 0)) (mult (sampler pitch (sum vibsig fm)(list sndobj refkey loop-start)) (uenv (or env (asd 0 dur 0)))) )) ; *************************************************************************** ; zone:vrange class ; ; ; Each instance of zone may call different sample files as a response to MIDI ; velocity. The zone:vrange class is a thin wrapper around zone:sampler and ; holds information for each velocity range in a zone. ; ; *************************************************************************** (setq zone:vrange (send class :new '(.file .loop-start .loop-dur .loop-index .refkey .sr .env .fm .vib .vfrq .vdelay .vtab .vphase .transpose .tune .db))) ;; Construct new instance of zone:vrange. With the exception of transpose, ;; tune and db, the arguments are the same as zone:sampler. ;; (send zone:vrange :answer :isnew '(file refkey loop-start loop-dur &key loop-index sr env fm vib vfrq vdelay vtab vphase transpose tune db) '((setq .file file .refkey refkey .loop-start loop-start .loop-dur loop-dur .loop-index (or loop-index 1) .sr (or sr *default-sound-srate*) .env (or env nil) .fm (or fm (s-rest)) .vib (or vib 0.00) .vfrq (or vfrq 5.00) .vdelay (or vdelay 0.00) .vtab (or vtab *tri-table*) .vphase (or vphase 0) .transpose (or transpose 0) .tune (or tune 1.00) .db (or db 0)))) ;; Render sound object by calling zone:sampler. With exception of pitch and ;; duration the arguments to zone:sampler are set at construction time as ;; defaults. The third argument, args, is an optional list of key/value ;; pairs. Keywords in args shadow the default value. ;; ;; pitch - flonum. ;; dur - flonum ;; args - list ;; return - sound or sound vector ;; (send zone:vrange :answer :render '(pitch dur args) '( (let ((arglist (append (list dur .file) args))) (setq arglist (append arglist (list ':loop-start .loop-start ':loop-dur .loop-dur ':loop-index .loop-index ':refkey .refkey ':sr .sr ':env .env ':fm .fm ':vib .vib ':vfrq .vfrq ':vdelay .vdelay ':vtab .vtab ':vphase .vphase))) (setq arglist (cons (+ pitch .transpose (get-transpose)) arglist)) (stretch (/ 1.0 .tune) (scale-db .db (apply #'zone:sampler arglist)))))) (send zone:vrange :answer :dump '() '((let ((acc "zone:vrange:\n")) (setq acc (strcat acc " .file " (->string .file) "\n")) (setq acc (strcat acc " .loop-start " (->string .loop-start) "\n")) (setq acc (strcat acc " .loop-dur " (->string .loop-dur) "\n")) (setq acc (strcat acc " .loop-index " (->string .loop-index) "\n")) (setq acc (strcat acc " .refkey " (->string .refkey) "\n")) (setq acc (strcat acc " .sr " (->string .sr) "\n")) (setq acc (strcat acc " .env " (->string .env) "\n")) (setq acc (strcat acc " .fm " (->string .fm) "\n")) (setq acc (strcat acc " .vib " (->string .vib) "\n")) (setq acc (strcat acc " .vfrq " (->string .vfrq) "\n")) (setq acc (strcat acc " .vdelay " (->string .vdelay) "\n")) (setq acc (strcat acc " .vtab " (->string .vtab) "\n")) (setq acc (strcat acc " .vphase " (->string .vphase) "\n")) (setq acc (strcat acc " .transpose " (->string .transpose) "\n")) (setq acc (strcat acc " .tune " (->string .tune) "\n")) (setq acc (strcat acc " .db " (->string .db) "\n")) acc))) ; *************************************************************************** ; Zone Class ; *************************************************************************** ;; @doc class zone ;; Instances of zone respond to a fixed MIDI key range. Additionally zones ;; select one of three sample files in response to MIDI velocity. The ;; three velocity ranges are pp, mf and ff. ;; (setq zone (send class :new '(.floor .ceiling .vswitch .pp .mf .ff))) ;; @doc method zone :new ;; (send zone :new floor ceiling [vswitch]) ;; Construct new zone for MIDI key range [floor, ceiling] inclusive. ;; ;; floor - integer. The minimum MIDI key to which the zone responds. ;; ceiling - integer. The maximum MIDI key to which the zone responds. ;; 0 <= floor < ceiling <= 127 ;; vswitch - cons. The velocity cross switch points. vswitch has the form ;; (pp ff). MIDI velocity values less then pp use the pp file, ;; velocities greater then ff use ff. Default (48 . 80) ;; return - object. A new instance of zone ;; (send zone :answer :isnew '(floor ceiling &optional vswitch) '((setq .floor (min floor ceiling) .ceiling (max floor ceiling) .vswitch (or vswitch (cons 48 80))) )) ;; @doc method zone :range ;; (send /zone/ :range [floor [ceiling]]) ;; Get and optionally set key range. ;; ;; floor - integer. 0 <= floor < ceiling <= 127 ;; ceiling - integer. 0 <= floor < ceiling <= 127 ;; return - cons (floor . ceiling) ;; (send zone :answer :range '(&optional floor ceiling) '((if floor (setq .floor floor)) (if ceiling (setq .ceiling ceiling)) (cons .floor .ceiling))) ;; @doc method zone :vswitch ;; (send /zone/ :vswitch [pp [ff]]) ;; Get and optionally set velocity switch points ;; ;; pp - integer. MIDI velocity 0 <= pp < ff <= 127 ;; ff - integer. MIDI velocity 0 <= pp < ff <= 127 ;; return - cons (pp . ff) ;; (send zone :answer :vswitch '(&optional pp ff) '((if pp (setf (car .vswitch) pp)) (if ff (setf (cdr .vswitch) ff)) .vswitch)) ;; @doc method zone :respondp ;; (send /zone/ :respondp key) ;; Test if this zone responds to given key. ;; ;; key - flonum. MIDI key number ;; return - bool. True iff floor <= key <= ceiling ;; (send zone :answer :respondp '(key) '((and (>= key .floor)(<= key .ceiling)))) ;; @doc method zone :set ;; (send /zone/ :set vsel file loop-start loop-dur [:loop-index][:sr][:env][:fm][:vib][:vfrq][:vdelay][:vtab][:vphase][:transpose][:tune][:db]) ;; Set zones response to given velocity range. ;; ;; vsel - symbol. Velocity range select. The three valid symbols are ;; 'PP 'MF and 'FF any other symbol causes an error. ;; file - string. Fully qualified sample file name. ;; loop-start - flonum. Loop start time in seconds. ;; loop-dur - flonum. Loop duration in seconds. ;; :loop-index - integer. Moves loop-start in increments of loop duration ;; seconds. For negative indexes the loop start is moved ;; backwards towards the sample's beginning. ;; :sr - integer. Sample rate. Normally the sample rate is determined ;; automatically from the sound file header. The sr keyword ;; maybe used to set an explicit rate if desired. ;; :env - sound. The amplitude envelope. Default rectangular "gate" of ;; dur seconds. ;; :fm - sound. Axillary fm source. Default silence. ;; :vib - flonum. Vibrato depth (scaled to MIDI controller range), ;; default 0. ;; :vfrq - flonum. Vibrato frequency, default 7 Hz. ;; :vdelay - flonum. Vibrato delay, default 0 second. ;; :vtab - table. Vibrato wave table, default *tri-table* ;; :vphase - any. Vibrato phase. See phase function in utilities.lsp ;; default 0. ;; :transpose - integer. Transposition in half-step, default 0 ;; :tune - flonum. (de)tune ratio. The tuning ration is the reciprocal ;; of a stretch factor. Default 1 ;; :db - flonum. Amplitude scale in db, default 0 ;; (send zone :answer :set '(vsel file refkey loop-start loop-dur &key loop-index sr env fm vib vfrq vdelay vtab vphase transpose tune db) '((let (vr) (setq vr (send zone:vrange :new file refkey loop-start loop-dur :loop-index loop-index :sr sr :env env :fm fm :vib vib :vfrq vfrq :vdelay vdelay :vtab vtab :vphase vphase :transpose transpose :tune tune :db db)) (cond ((eq vsel 'PP) (setq .pp vr)) ((eq vsel 'MF) (setq .mf vr)) ((eq vsel 'FF) (setq .ff vr)) (t (error "Invalid zone velocity selector" sym)))))) ;; @doc method zone :render ;; (send /zone/ :render pitch dur arglist) ;; Render sound in response to specific key. If this zone does not respond to ;; the given key, return silence. ;; ;; pitch - flonum. MIDI key number. ;; dur - flonum. Duration in seconds. ;; arglist - list. A list of keyword/value pairs. The recognized keywords are ;; :vel - flonum. midi velocity ;; :loop-index - integer. ;; :env - sound. Amplitude envelope ;; :fm - sound. FM modulator. ;; :vib - flonum. Vibrato depth. ;; :vfrq - flonum. Vibrato frequency. ;; :vdelay - flonum. Vibrato delay. ;; :vtab - table. Vibrato wave table. ;; :vphase - any. Vibrato phase. ;; return - sound or sound vector. ;; (send zone :answer :render '(pitch dur arglist) '((if (send self :respondp pitch) (let (vel vr) (setq vel (get-keyword-value ':VEL arglist 64)) (cond ((and .pp (<= vel (car .vswitch)))(setf vr .pp)) ((and .ff (>= vel (cdr .vswitch)))(setf vr .ff)) (t (setf vr .mf))) (send vr :render pitch dur arglist)) (s-rest)))) (send zone :answer :dump '() '((let ((acc (format nil "zone [~a ~a]~%" .floor .ceiling))) (setq acc (strcat acc ".pp " (send .pp :dump))) (setq acc (strcat acc ".mf " (send .mf :dump))) (setq acc (strcat acc ".ff " (send .ff :dump))) acc))) ; *************************************************************************** ; Zone:ZBI class ; ZBI ~ Zone Based Instrument ; *************************************************************************** ;; cwz ~ "Current Working Zone" ;; (setq zone:zbi:*cwz* 'NIL) ;; The default path, appended to filenames when a zbi zone is modified. ;; (setq zone:zbi:*path* "") (setq zone:zbi:*default-vswitch* (cons 48 80)) ;; @doc class zone:zbi ;; zbi ~ "Zone Based Instrument" ;; An instance of zone:zbi is essentially a list of zones and forms the basis ;; for sampler like instruments. ;; (setq zone:zbi (send class :new '(.zlist .vmap))) ;; @doc method zone:zbi :new ;; (send zone:zbi :new path [vmap]) ;; Construct new zone:zbi instance. ;; ;; path - string. The sample file directory. ;; vmap - object. An instance of map, default map:*vel6* ;; return - object. An instance of zone:zbi ;; (send zone:zbi :answer :isnew '(path &optional (vmap map:*vel6*)) '((setq zone:zbi:*path* path .zlist '() .vmap vmap))) ;; @doc method zone:zbi :add ;; (send /zone:zbi/ :add z) ;; Add zone to zone list. iff it is not already a member of the list. ;; Make z the current working zone ;; ;; z - object. An object which implements the same interface as zone. ;; ISSUE: Clarify what methods are required. ;; (send zone:zbi :answer :add '(z) '((if (not (member z .zlist)) (setq .zlist (cons z .zlist))) (setq zone:zbi:*cwz* z) z)) ;; @doc method zone:zbi :new-zone ;; (send /zone:zbi/ :new-zone floor ceiling [vswitch]) ;; Add new zone to zone list. The zone is made the "current" zone. See the ;; :set method ;; ;; floor - flonum. The least MIDI key the new zone responds to. ;; ceiling - flonum. The greatest MIDI key the zone responds to. ;; 0 <= floor < ceiling <= 127 ;; vswitch - cons. Velocity cross switch points, default (48 . 80) ;; return - object. The new zone. ;; (send zone:zbi :answer :new-zone '(floor ceiling &optional vswitch) '((setq zone:zbi:*cwz* (send zone :new floor ceiling (or vswitch zone:zbi:*default-vswitch*))) (setq .zlist (cons zone:zbi:*cwz* .zlist)) zone:zbi:*cwz*)) (send zone:zbi :answer :set '(vsel file refkey loop-start loop-end &key loop-index sr env fm vib vfrq vdelay vtab vphase transpose tune db) '((send zone:zbi:*cwz* :set vsel (os:join zone:zbi:*path* file) refkey loop-start loop-end :loop-index loop-index :sr sr :env env :fm fm :vib vib :vfrq vfrq :vdelay vdelay :vtab vtab :vphase vphase :transpose transpose :tune tune :db db))) ;; @doc method zone:zbi :render ;; (send /zone:zbi/ :render pitch dur arglist) ;; Render sound object by iterating over zone list. Each zone returns a sound ;; but if the zone does not respond to the given key its output is ;; silence. The zone outputs are summed and then amplitude scaled in response ;; to MIDI velocity. ;; ;; pitch - flonum. MIDI key number. ;; dur - flonum. Duration in seconds ;; arglist - list. Optional Argument list in the form of keyword/value pairs. ;; Recognized keywords are: ;; vel - flonum. MIDI velocity, default 64 ;; env - sound ;; fm - sound ;; vib - flonum. Vibrato depth ;; vfrq - flonum. Vibrato frequency ;; vdelay - flonum. Vibrato delay ;; vtab - table. Vibrato table ;; vphase - any. Vibrato phase ;; loop-index - integer. ;; ;; return - sound or sound vector. ;; (send zone:zbi :answer :render '(pitch dur arglist) '((let ((vel (get-keyword-value ':vel arglist 64))) (scale-db (send .vmap :get vel) (simrep (n (length .zlist)) (send (nth n .zlist) :render pitch dur arglist)))))) (send zone:zbi :answer :dump '() '((let ((acc "zone:zbi\n")) (dolist (z .zlist) (setq acc (strcat acc (send z :dump))) (setq acc (strcat acc "\n**********************************************\n"))) acc)))