port.lsp Version 1.00 Date 31 October 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 port.lsp implements a complex control signal for use as a portamento envelope. A port signal is composed of an arbitrary number of break-points. Each break-point i has a level Li, duration Di and transition time Ti. The signal is held at level Li for Di-Ti seconds and transitions to level L(i+1) over Ti seconds. The transition time Ti is deducted from the duration Di and is undefined for Ti>Di. The final break point has no transition so is held for its entire duration.
class
An instance of port:break-points maintains a list of portamento break points. Each break-point is a list of the form (L D T) where L is the level, D the duration and T the transition time. The transition time is deducted from the duration and is undefined for T>D.
method
(send port:break-points :new) Construct new instance of port:break-points. The new break-point list is initially empty. Use the :point method to add points to the list. return - object. A new instance of port:break-points
method
(send /port:break-points/ :point lev dur [tran]) Add break point to break-point list. lev - flonum. The new point's level dur - flonum. The new point's duration in seconds. tran - flonum. The transition time, trans <= dur, The default transition time is in port:*default-transition*
method
(send /port:break-points/ :get-bp-list) Get list of breakpoints. The list is formated to serve as an argument to pwlvr-list return - list.
function
(port bplst [r][stepmode]) Portamento envelope with arbitrary number of break points. The envelope's contour is controlled by the break point list bplst. bplist - list. The list of break-points. bplist has the form ((L0 D0 [T0])(L1 D1 [T1]) ... (Ln Dn [Tn])) Where for each break-point i, Li is the level, Di is the duration and Ti is the optional transition time. If the transition time is not specified the default of port:*default-transition* is used. The envelope is undefined if Ti>Di for any i. :r - flonum. Modulation ratio for use with FM modulators, default 1. :stepmode - bool. There are two methods for interpreting the break-point levels. If stepmode is false the amplitude of each break-point i is equal to the level Li. This mode is suitable for the when the port signal is being used to modulate an FM modulator. If stepmode is true each break-point level Li is interpreted as a MIDI key number. A translation is made between the level and the tones frequency in Hertz. Default t. return - sound.
function
(port:get-bplist-duration bplst) Determine break-point list "duration". arg - list. A list of break-points of the form ((L0 D0 [T0])(L1 D1 [T1]) ... (Ln Dn [Tn])) return - flonum.. The total duration D0+D1+D2+...+Dn
function
(port:increase-bplist-terminal-duration bplst dur) Increase break-point list duration. The duration of the terminal break-point is increased as required so that the total list "duration" is equal to dur. If the initial list "duration" is greater than dur no changes are made. bplst - list. List.of break-points. dur - flonum. New list "duration" return - list.
function
(port:lfo frq penv dur [:mi][:tab][:phase][:delay][:attack]) An LFO specifically for use with portamento driven oscillators. --- frq >-->| L | ----- tab >-->| F |---->| X |----> out phase >-->| O | ----- -->| | ^ ^ | --- | | | | | dur >-- mi >------ ------------ | | | ---------- | -->| Delayed | ----- | delay >->| Ramp |-->| X |---- attack >->| | ----- ---------- ^ | penv >---------------------- penv should be the same control signal used to produce portamento, here it is used to dynamically scale the LFO's amplitude to maintain a consistent FM modulation index. port:lfo also features a built in envelope for creating delayed onset vibrato. frq - flonum. LFO frequency in Hertz. penv - sound. The portamento envelope signal. dur - flonum. The total duration in seconds. Typically the duration argument should match the duration of the portamento signal. If less then the port duration the LFO output will terminate early. Use port:get-bplist-duration to determine duration from break-point list :mi - flonum. Output amplitude. Default: 1 :tab - table. Wave table. Default: *sine-table* :phase - any. See phase function in utilities.lsp. Default 0 :delay - flonum. The onset delay time in seconds. Default: 0 :attack - flonum. The attack time in seconds. Default: 0 return - sound.
function
(port:fm-cop penv dur [:r][:fm][:am][:tab][:cfrq]) An fm "carrier operator" with portamento penv - sound. The portamento envelope dur - flonum. Duration in seconds :r - flonum. The carrier frequency ratio relative to modulation frequency. Default 1 :fm - sound. The fm modulator signal. Default silence :am - sound. The amplitude modulation signal, typically used to impart an envelope. Default (rgate dur) :tab - table. Carrier wave table. Default *sine-table* :cfrq - flonum. Carrier frequency. ISSUE: Need to document how cfrq argument interacts with r. return - sound
function
(port:fm-mop penv dur r i [:fm][:am][:tab][:phase][:cfrq] An fm "modulator operator" with portamento penv - sound. The portamento envelope dur - flonum. Duration in seconds r - flonum. The carrier frequency ratio relative to modulation frequency. Default 1 i - flonum. The modulation index. :fm - sound. The fm modulator signal. Default silence :am - sound. The amplitude modulation signal, typically used to impart an envelope. Default (rgate dur) :tab - table. Carrier wave table. Default *sine-table* :phase - any. See phase function in utilities.lsp :cfrq - flonum. Carrier frequency. Used with modulation index i to calculate modulator amplitude return - sound.
example
;; @doc example portamento ; *************************************************************************** ; Sample Code Use ; *************************************************************************** (defun test (bplst) (let (dur penv r1 i1 r2 i2) (setf dur (port:get-bplist-duration bplst)) (setf penv (port bplst)) (setf r1 1.01) (setf i1 15.00) (setf r2 3.03) (setf i2 6.00) (port:fm-cop penv dur :am NIL :fm (sum (port:fm-mop penv dur r1 i1 :am (percussion dur) :fm (port:lfo 7.00 penv dur :mi 0.01) ) (port:fm-mop penv dur r2 i2 :am (asd dur 0 0) :fm (port:lfo 6.00 penv dur :mi 0.11) ) )))) (setq qq (list (list a4 2)(list c4 3 2)(list a5 2))) (play (test qq))
;; port.lsp ;; Version 1.00 Date 31 October 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 ;; ;; port.lsp implements a complex control signal for use as a portamento ;; envelope. A port signal is composed of an arbitrary number of ;; break-points. Each break-point i has a level Li, duration Di and transition ;; time Ti. The signal is held at level Li for Di-Ti seconds and transitions ;; to level L(i+1) over Ti seconds. The transition time Ti is deducted from ;; the duration Di and is undefined for Ti>Di. The final break point has no ;; transition so is held for its entire duration. ;; (require 'math) (provide 'port) (current-file "port") (setf port:*default-level* 0 port:*default-duration* 1 port:*default-transition* (/ s 2) ;Default transition 1/32nd note ) ;; @doc class port:break-points ;; An instance of port:break-points maintains a list of portamento break ;; points. Each break-point is a list of the form (L D T) where L is the ;; level, D the duration and T the transition time. The transition time is ;; deducted from the duration and is undefined for T>D. (setf port:break-points (send class :new '(.lst))) ;; @doc method port:break-points :new ;; (send port:break-points :new) ;; Construct new instance of port:break-points. ;; The new break-point list is initially empty. Use the :point method to add ;; points to the list. ;; ;; return - object. A new instance of port:break-points ;; (send port:break-points :answer :isnew '() '((setf .lst '()))) ;; @doc method port:break-points :point ;; (send /port:break-points/ :point lev dur [tran]) ;; Add break point to break-point list. ;; ;; lev - flonum. The new point's level ;; ;; dur - flonum. The new point's duration in seconds. ;; ;; tran - flonum. The transition time, trans <= dur, ;; The default transition time is in port:*default-transition* ;; (send port:break-points :answer :point '(lev dur &optional tran) '((setf .lst (append .lst (list (list lev dur (or tran port:*default-transition*))) )))) ;; @doc method port:break-points :get-bp-list ;; (send /port:break-points/ :get-bp-list) ;; Get list of breakpoints. The list is formated to serve as an argument to ;; pwlvr-list ;; ;; return - list. ;; (send port:break-points :answer :get-bp-list '() '((let (acc last-bp) (setf acc '()) (setf last-bp (car (last .lst))) (dolist (bp (butlast .lst)) (let ((level (car bp)) (dur (second bp)) (tran (third bp))) (setf acc (append acc (list level (- dur tran) level tran))))) (setf acc (append acc (list (car last-bp)(second last-bp)(car last-bp)))) acc))) ; *************************************************************************** ; Primary Port Function ; *************************************************************************** ;; @doc function port ;; (port bplst [r][stepmode]) ;; Portamento envelope with arbitrary number of break points. The envelope's ;; contour is controlled by the break point list bplst. ;; ;; bplist - list. The list of break-points. bplist has the form ;; ((L0 D0 [T0])(L1 D1 [T1]) ... (Ln Dn [Tn])) Where for each ;; break-point i, Li is the level, Di is the duration and Ti is ;; the optional transition time. If the transition time is not ;; specified the default of port:*default-transition* is used. ;; The envelope is undefined if Ti>Di for any i. ;; ;; :r - flonum. Modulation ratio for use with FM modulators, default 1. ;; ;; :stepmode - bool. There are two methods for interpreting the break-point ;; levels. ;; ;; If stepmode is false the amplitude of each break-point i is ;; equal to the level Li. This mode is suitable for the when the ;; port signal is being used to modulate an FM modulator. ;; ;; If stepmode is true each break-point level Li is interpreted ;; as a MIDI key number. A translation is made between the level ;; and the tones frequency in Hertz. Default t. ;; ;; return - sound. ;; (defun port (bplst &key (r 1)(stepmode t)) (if (not (listp bplst)) (error "port expected first argument to be a list of break-points.")) (let (break-points) (setf break-points (send port:break-points :new)) (dolist (bp bplst) (let (lev dur tran) (if (or (not (listp bp))(< (length bp) 2)) (error "port expects each element of the break-point list to be a list of at least 2 elements.")) (setf lev (* r (if stepmode (step-to-hz (car bp)) (car bp)))) (setf dur (second bp)) (setf tran (or (third bp) port:*default-transition*)) (send break-points :point lev dur tran))) (pwlvr-list (send break-points :get-bp-list)))) ; *************************************************************************** ;; @doc function port:get-bplist-duration ;; (port:get-bplist-duration bplst) ;; Determine break-point list "duration". ;; ;; arg - list. A list of break-points of the form ;; ((L0 D0 [T0])(L1 D1 [T1]) ... (Ln Dn [Tn])) ;; ;; return - flonum.. The total duration D0+D1+D2+...+Dn ;; (defun port:get-bplist-duration (bplst) (if (null bplst) 0 (+ (second (car bplst))(port:get-bplist-duration (cdr bplst))))) ; *************************************************************************** ; Break-Point List Transformations ; *************************************************************************** ;; @doc function port:increase-bplist-terminal-duration ;; (port:increase-bplist-terminal-duration bplst dur) ;; Increase break-point list duration. ;; The duration of the terminal break-point is increased as required so that ;; the total list "duration" is equal to dur. If the initial list "duration" ;; is greater than dur no changes are made. ;; ;; bplst - list. List.of break-points. ;; ;; dur - flonum. New list "duration" ;; ;; return - list. ;; (defun port:increase-bplist-terminal-duration (bplst dur) (let (ldur delta) (setf ldur (port:get-bplist-duration bplst)) (setf delta (- dur ldur)) (if (plusp delta) (let (last-dur) (setf last-dur (car (cdr (car (last bplst))))) (setf (car (cdr (car (last bplst))))(+ last-dur delta))) ))) ; *************************************************************************** ; Specialized Portamento Oscillators. ; *************************************************************************** ;; @doc function port:lfo ;; (port:lfo frq penv dur [:mi][:tab][:phase][:delay][:attack]) ;; An LFO specifically for use with portamento driven oscillators. ;; ;; --- ;; frq >-->| L | ----- ;; tab >-->| F |---->| X |----> out ;; phase >-->| O | ----- ;; -->| | ^ ^ ;; | --- | | ;; | | | ;; dur >-- mi >------ ------------ ;; | | ;; | ---------- | ;; -->| Delayed | ----- | ;; delay >->| Ramp |-->| X |---- ;; attack >->| | ----- ;; ---------- ^ ;; | ;; penv >---------------------- ;; ;; ;; penv should be the same control signal used to produce portamento, here ;; it is used to dynamically scale the LFO's amplitude to maintain a ;; consistent FM modulation index. port:lfo also features a built in envelope ;; for creating delayed onset vibrato. ;; ;; frq - flonum. LFO frequency in Hertz. ;; ;; penv - sound. The portamento envelope signal. ;; ;; dur - flonum. The total duration in seconds. Typically the duration ;; argument should match the duration of the portamento signal. If ;; less then the port duration the LFO output will terminate early. ;; Use port:get-bplist-duration to determine duration from ;; break-point list ;; ;; :mi - flonum. Output amplitude. Default: 1 ;; ;; :tab - table. Wave table. Default: *sine-table* ;; ;; :phase - any. See phase function in utilities.lsp. Default 0 ;; ;; :delay - flonum. The onset delay time in seconds. Default: 0 ;; ;; :attack - flonum. The attack time in seconds. Default: 0 ;; ;; return - sound. ;; (defun port:lfo (frq penv dur &key mi tab phase delay attack) (let* ((mi (or mi 1.00)) (tab (or tab *sine-table*)) (delay (or delay 0.00)) (attack (or attack 0.00)) (hold (max 0 (- dur delay attack)))) (scale mi (mult (lfo frq dur tab (phase phase)) (mult (dramp delay attack hold) penv))))) ; *************************************************************************** ; FM "operators" with portamento ; *************************************************************************** ;; @doc function port:fm-cop ;; (port:fm-cop penv dur [:r][:fm][:am][:tab][:cfrq]) ;; An fm "carrier operator" with portamento ;; ;; penv - sound. The portamento envelope ;; ;; dur - flonum. Duration in seconds ;; ;; :r - flonum. The carrier frequency ratio relative to modulation ;; frequency. Default 1 ;; ;; :fm - sound. The fm modulator signal. Default silence ;; ;; :am - sound. The amplitude modulation signal, typically used to impart ;; an envelope. Default (rgate dur) ;; ;; :tab - table. Carrier wave table. Default *sine-table* ;; ;; :cfrq - flonum. Carrier frequency. ;; ISSUE: Need to document how cfrq argument interacts with r. ;; ;; return - sound ;; (defun port:fm-cop (penv dur &key r fm am tab phase cfrq) (let (pitch) (setf r (or r 1.00)) ;Carrier frq ratio (setf fm (or fm (rgate dur 0))) ;Frequency modulator (setf am (or am (rgate dur))) ;Amplitude modulator (setf tab ;Wave table (or tab *sine-table*)) (setf phase (or phase 0)) ;Phase angle (setf cfrq (or cfrq 0)) ;Carrier frequency (setf pitch (hz-to-step cfrq)) ;Carrier pitch (MIDI key) (mult (fmosc pitch (sum (scale r penv) fm) tab phase) am))) ;; @doc function port:fm-mop ;; (port:fm-mop penv dur r i [:fm][:am][:tab][:phase][:cfrq] ;; An fm "modulator operator" with portamento ;; ;; penv - sound. The portamento envelope ;; ;; dur - flonum. Duration in seconds ;; ;; r - flonum. The carrier frequency ratio relative to modulation ;; frequency. Default 1 ;; ;; i - flonum. The modulation index. ;; ;; :fm - sound. The fm modulator signal. Default silence ;; ;; :am - sound. The amplitude modulation signal, typically used to impart ;; an envelope. Default (rgate dur) ;; ;; :tab - table. Carrier wave table. Default *sine-table* ;; ;; :phase - any. See phase function in utilities.lsp ;; ;; :cfrq - flonum. Carrier frequency. ;; Used with modulation index i to calculate modulator amplitude ;; ;; return - sound. ;; (defun port:fm-mop (penv dur r i &key fm am tab phase cfrq) (let (pitch) (setf fm (or fm (rgate dur 0))) (setf am (or am (rgate dur))) (setf tab (or tab *sine-table*)) (setf phase (or phase 0)) (setf cfrq (or cfrq 0)) (setf pitch (hz-to-step cfrq)) (mult (fmosc pitch (sum (scale r penv) fm) tab phase) (mult am (scale i penv))))) #| ;; @doc example portamento ; *************************************************************************** ; Sample Code Use ; *************************************************************************** (defun test (bplst) (let (dur penv r1 i1 r2 i2) (setf dur (port:get-bplist-duration bplst)) (setf penv (port bplst)) (setf r1 1.01) (setf i1 15.00) (setf r2 3.03) (setf i2 6.00) (port:fm-cop penv dur :am NIL :fm (sum (port:fm-mop penv dur r1 i1 :am (percussion dur) :fm (port:lfo 7.00 penv dur :mi 0.01) ) (port:fm-mop penv dur r2 i2 :am (asd dur 0 0) :fm (port:lfo 6.00 penv dur :mi 0.11) ) )))) (setq qq (list (list a4 2)(list c4 3 2)(list a5 2))) (play (test qq))