make a repo
This commit is contained in:
parent
1032ebee06
commit
ac8860be5f
|
@ -0,0 +1,133 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
|
||||
;preview linear
|
||||
;name "Additive Tone..."
|
||||
;action "Generating Tones..."
|
||||
;author "dm"
|
||||
;copyright "Released under terms of the GNU General Public License version 2"
|
||||
;release 1.1
|
||||
|
||||
;control coefs-string "Decay Coefficients" string "" "(2.25 3 2.0 5 3) (3.3 2.7) (5.4 2 7.5 (3.2 5) (4 2)) (7.8 9.2) (13.3)"
|
||||
;control pch "Pitch (Steps)" float-text "" 60 1 96
|
||||
;control vib-rate "Vibrato Speed (Hz)" float-text "" 36 0 1102.4
|
||||
;control vib-depth "Vibrato Depth (Hz)" float-text "" 3 0 nil
|
||||
;control dur "Duration" time "" 3 nil nil
|
||||
|
||||
;;;WHAT IT DO
|
||||
|
||||
;Generates a tone with pitch pch and duration dur, with vibrato.
|
||||
;coefs-string determines the timbre, by specifying how quickly harmonics should ;attenuate according to their frequency.
|
||||
;coefs-string consists of terms delimited by parentheses (), whose elements may be ;either positive numbers or further sub-terms delimited by parentheses.
|
||||
;If there are n terms, the first term gives the timbre at the start time, the second at ;dur/n, the third at 2*dur/n, and so on. Timbre is smoothly interpolated between these ;points.
|
||||
;In a term with k elements, the rth element determines the amplitude of
|
||||
;the r-1 mod k harmonics.
|
||||
;If the rth element is a number x, then the ith harmonic (where i is r-1 modulo k) has ;amplitude i^(-x). That is, higher numbers make the harmonics attenuate faster.
|
||||
;If the rth element is a subterm with l elements, then the sth element of that subterm
|
||||
;determines the amplitude of the (r+k*(s-1)) mod k*l harmonics.
|
||||
|
||||
;As an example, here are the harmonics and relative amplitudes when
|
||||
;coefs-string is "((5 (4 2)) 3)" and pch is 69:
|
||||
;Harmonic Frequency Amplitude Amplitude
|
||||
;1 440 1^-5 1.000000
|
||||
;2 880 2^-3 0.125000
|
||||
;3 1320 3^-4 0.012345
|
||||
;4 1760 4^-3 0.015625
|
||||
;5 2200 5^-5 0.000320
|
||||
;6 2640 6^-3 0.004630
|
||||
;7 3080 7^-2 0.020408
|
||||
;8 3520 8^-3 0.001953
|
||||
|
||||
;;;ACKNOWLEDGEMENT
|
||||
|
||||
;Shout out to steve for a very thorough code review and loads of coding tips, and the ;rewritten gen-tables which I nicked verbatim from a post of his.
|
||||
;Thank you David R. Sky for the handy string-to-list function.
|
||||
|
||||
;;;ADDITIONAL GLOBAL VARIABLES
|
||||
|
||||
;;Don't compute harmonics if their frequencies are too high.
|
||||
(setq hi-freq (min (/ *sound-srate* 2) 17000.0))
|
||||
(setq top-overtone (min
|
||||
1000 ;magic number to avoid argument stack overflow when calling simrep.
|
||||
(+ 1 (truncate (/ hi-freq (step-to-hz pch))))))
|
||||
|
||||
(defun get-coefficient (overtone lst divis)
|
||||
;;;recursively search coefficient list for exponent
|
||||
(let* ((len (length lst))
|
||||
(branches (mult divis len))
|
||||
(term (nth (rem (/ overtone divis) len) lst)))
|
||||
(if (numberp term)
|
||||
term
|
||||
(get-coefficient overtone term branches))))
|
||||
|
||||
(defun get-amplitude (overtone coef-list)
|
||||
(expt (+ 1.0 overtone)
|
||||
(- (get-coefficient overtone coef-list 1))))
|
||||
|
||||
;2022-07-06, I fucked up my backup and started from this old version
|
||||
;which still had stack overflow errors from simrep.
|
||||
;(defun gen-table (coef-list)
|
||||
; (simrep (current-overtone top-overtone)
|
||||
; (scale (get-amplitude current-overtone coef-list)
|
||||
; (build-harmonic (+ 1 current-overtone) 2048))))
|
||||
;2022-07-06, this one should work better.
|
||||
(defun gen-table (coef-list)
|
||||
(do ((table 0)
|
||||
(current-overtone 0))
|
||||
((> current-overtone top-overtone) table)
|
||||
(setq current-overtone (+ 1 current-overtone))
|
||||
(setq table
|
||||
(sum table (scale (get-amplitude current-overtone coef-list)
|
||||
(build-harmonic (+ 1 current-overtone) 2048))))))
|
||||
|
||||
(defun normalize (sig)
|
||||
;;;assume signal has 2048 samples
|
||||
(scale (/ 0.75 (peak sig 2048)) sig))
|
||||
|
||||
(defun gen-tables (coef-lists pitch interval)
|
||||
;;; Push wavetable and breakpoint onto a list 'tables'.
|
||||
;;; Discard the final breakpoint, then return 'tables' for siosc.
|
||||
(let ((count 1) tables)
|
||||
(dolist (coef-list coef-lists (reverse (cdr tables)))
|
||||
(push (normalize (gen-table coef-list)) tables)
|
||||
(push (* interval count) tables)
|
||||
(incf count))))
|
||||
|
||||
(defun modulation (vib-rate vib-depth duration)
|
||||
(if (= vib-rate 0)
|
||||
(s-rest duration)
|
||||
(scale vib-depth (lfo vib-rate duration))))
|
||||
|
||||
(defun gen-tone (pitch duration coef-lists)
|
||||
;;;get wavetables and mod. env., pass to built-in Nyquist oscillators.
|
||||
(validate-coefs coef-lists)
|
||||
(let* ((mod (modulation vib-rate vib-depth duration))
|
||||
(interval (/ duration (length coef-lists)))
|
||||
(tables (gen-tables coef-lists pitch interval)))
|
||||
(if (= (length coef-lists) 1)
|
||||
(fmosc pitch mod (maketable (car tables)) 0)
|
||||
(siosc pitch mod tables))))
|
||||
|
||||
(defun validate-coefs (lst)
|
||||
;;; All items in top level list must be lists.
|
||||
(dolist (coef lst)
|
||||
(when (not (listp coef))
|
||||
(throw 'err (format nil "Coefficient ~s is invalid (not a list)." coef)))
|
||||
(validate-items lst)))
|
||||
|
||||
(defun validate-items (item)
|
||||
;;; Items must be lists or numbers. Test recursively.
|
||||
(cond
|
||||
((and (numberp item) (>= item 0))) ;valid
|
||||
((listp item) (mapc 'validate-items item)) ;recurse
|
||||
(t (throw 'err (format nil "Coefficient ~s is invalid." item)))))
|
||||
|
||||
;;;Took this one-liner from David R. Sky's Sequencer 2, released under GPLv2.
|
||||
;;;Replace this with eval-string when 2.3.1 releases.
|
||||
(defun string-to-list (string)
|
||||
(read (make-string-input-stream (format nil "(~a)" string))))
|
||||
|
||||
;;read string, then generate tone (or throw error)
|
||||
(setf coefs (string-to-list coefs-string))
|
||||
(catch 'err (gen-tone pch dur coefs))
|
|
@ -0,0 +1,31 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type process
|
||||
;preview linear
|
||||
;name "Convolve Tracks"
|
||||
;action "Convolving..."
|
||||
;author "dm"
|
||||
|
||||
(defun get-storage ()
|
||||
(get '*SCRATCH* 'DM-CONVOLVE-STORAGE))
|
||||
|
||||
(defun clear-storage ()
|
||||
(remprop '*SCRATCH* 'DM-CONVOLVE-STORAGE))
|
||||
|
||||
(defun store (signal)
|
||||
(putprop '*SCRATCH* signal 'DM-CONVOLVE-STORAGE)
|
||||
(sum (s-rest 0) signal))
|
||||
|
||||
(defun conv-stored (signal)
|
||||
(convolve signal (get-storage)))
|
||||
|
||||
(setq index (get '*TRACK* 'INDEX))
|
||||
(setq tracks (length (get '*SELECTION* 'TRACKS)))
|
||||
;(print (symbol-plist '*SCRATCH*))
|
||||
(if (= index 1)
|
||||
(if (< tracks 2)
|
||||
(print "Select two or more tracks")
|
||||
(store *TRACK*))
|
||||
(if (= index tracks)
|
||||
(prog1 (conv-stored *TRACK*) (clear-storage))
|
||||
(store (conv-stored *TRACK*))))
|
|
@ -0,0 +1,34 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type process
|
||||
;preview linear
|
||||
;name "Filter Sweep..."
|
||||
;action "Filtering..."
|
||||
;author "dm"
|
||||
|
||||
;control type "Filter Type" choice "Low-Pass,High-Pass,Band-Pass" 0
|
||||
;control start-freq "Start Frequency (Hz)" float-text "" 7000 0 20000
|
||||
;control end-freq "End Frequency (Hz)" float-text "" 1000 0 20000
|
||||
;control shape-num "Curve Parameter" float-text "" 5 nil nil
|
||||
;control intensity "Intensity" int-text "" 1 1 50
|
||||
|
||||
(defun curve (inif finf crv)
|
||||
(if (zerop crv)
|
||||
(pwlv inif 1 finf)
|
||||
(let* ((crv (expt 0.5 crv))
|
||||
(norm (/ (- inif finf) (- 1.0 crv)))
|
||||
(arc (scale norm (diff (pwev 1 1 crv) crv))))
|
||||
(sim finf arc))))
|
||||
|
||||
(defun iter-lp (signal cutoff iter)
|
||||
(dotimes (n iter signal) (setf signal (lp signal cutoff))))
|
||||
|
||||
(defun iter-hp (signal cutoff iter)
|
||||
(dotimes (n iter signal) (setf signal (hp signal cutoff))))
|
||||
|
||||
(let* ((cutoff (curve start-freq end-freq shape-num))
|
||||
(bandwidth (/ *sound-srate* (float intensity) 5.0)))
|
||||
(case type
|
||||
(0 (iter-lp *TRACK* cutoff intensity))
|
||||
(1 (iter-hp *TRACK* cutoff intensity))
|
||||
(2 (reson *TRACK* cutoff bandwidth 1))))
|
|
@ -0,0 +1,52 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type process
|
||||
;preview linear
|
||||
;name "Filter Sweep (Track Input)..."
|
||||
;action "Filtering..."
|
||||
;author "dm"
|
||||
|
||||
;control type "Filter Type" choice "Low-Pass,High-Pass,Band-Pass" 0
|
||||
;control intensity "Intensity" int-text "" 1 1 50
|
||||
;control max-cu-freq "Maximum Cutoff Frequency" float "" 15000 1 22030
|
||||
|
||||
;clip to halfwave, scale to max cutoff freq, downsample to control rate, store,
|
||||
;compute
|
||||
(defun store-as-cutoff (signal)
|
||||
(setf to-store
|
||||
(sim (s-rest 0)
|
||||
(force-srate *control-srate*
|
||||
(scale max-cu-freq
|
||||
(s-min (const 1)
|
||||
(s-max (s-rest 1) signal))))))
|
||||
(putprop '*SCRATCH* (sim (s-rest 0) to-store) 'DM-FSWEEP-CUTOFF)
|
||||
*TRACK*)
|
||||
|
||||
(defun clear-storage ()
|
||||
(remprop '*SCRATCH* 'DM-SWEEP-CUTOFF))
|
||||
|
||||
(defun iter-lp (signal cutoff iter)
|
||||
(print (snd-length cutoff ny:all))
|
||||
(dotimes (n iter signal)
|
||||
(setf signal (lp signal cutoff))))
|
||||
|
||||
(defun iter-hp (signal cutoff iter)
|
||||
(dotimes (n iter signal)
|
||||
(setf signal (hp signal cutoff))))
|
||||
|
||||
(defun apply-filter (cutoff)
|
||||
(let* ((bandwidth (/ *sound-srate* (float intensity) 5.0)))
|
||||
(case type
|
||||
(0 (iter-lp *TRACK* cutoff intensity))
|
||||
(1 (iter-hp *TRACK* cutoff intensity))
|
||||
(2 (reson *TRACK* cutoff bandwidth 1)))))
|
||||
|
||||
(setq index (get '*TRACK* 'INDEX))
|
||||
(setq tracks (length (get '*SELECTION* 'TRACKS)))
|
||||
(if (< tracks 2)
|
||||
(print "Select 2 tracks")
|
||||
(if (< index tracks)
|
||||
(store-as-cutoff *TRACK*)
|
||||
(let ((cutoff (get '*SCRATCH* 'DM-FSWEEP-CUTOFF)))
|
||||
(if (= index tracks) (clear-storage))
|
||||
(multichan-expand #'apply-filter cutoff))))
|
|
@ -0,0 +1,44 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
|
||||
;preview linear
|
||||
;name "FM Chirp..."
|
||||
;action "Generating Chirp..."
|
||||
;author "dm"
|
||||
;copyright "Released under terms of the GNU General Public License version 2"
|
||||
|
||||
;control carri-params "Carrier (Start End Curve)" string "" "800 200 -1.5"
|
||||
;control modul-params "Modulator (Start End Curve)" string "" "450 -1.0 5.7"
|
||||
;control modul-scale "Modulation Index" float-text "" 50 0 nil
|
||||
;control dur "Duration" float "" 1 0.01 5
|
||||
|
||||
;;;curve going from inf to finf with curve parameter crv.
|
||||
(defun piece (inif finf crv)
|
||||
(if (zerop crv)
|
||||
(pwlv inif 1 finf)
|
||||
(let* ((epsilon (expt 0.5 crv))
|
||||
(norm (/ (- inif finf) (- 1.0 epsilon)))
|
||||
(arc (scale norm (diff (pwev 1 1 epsilon) epsilon))))
|
||||
(sum finf arc))))
|
||||
|
||||
;;;stole this one from David R. Sky's Sequencer 2.
|
||||
(defun string-to-list (string)
|
||||
(read (make-string-input-stream (format nil "(~a)" string))))
|
||||
|
||||
(defun list-to-floats (input-list)
|
||||
(mapcar #'float input-list))
|
||||
|
||||
(defun string-to-curve (string)
|
||||
(apply #'piece (list-to-floats (string-to-list string))))
|
||||
|
||||
(defun three-numbersp (input-list)
|
||||
(eq (mapcar #'numberp input-list) (list (= 0 0) (= 0 0) (= 0 0))))
|
||||
|
||||
(stretch-abs dur
|
||||
(let* ((modul-freq (string-to-curve modul-params))
|
||||
(carri-freq (string-to-curve carri-params)))
|
||||
(hzosc
|
||||
(sum carri-freq
|
||||
(scale modul-scale
|
||||
(hzosc modul-freq))))))
|
|
@ -0,0 +1,192 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type process
|
||||
;name "Grainimogrifier..."
|
||||
;action "Milling..."
|
||||
;author "dm"
|
||||
;preview disabled
|
||||
;release 1.3.1
|
||||
;copyright "Released under terms of the GNU General Public License version 2"
|
||||
|
||||
;control grain-number "Number of Grains" int "" 50 2 1750
|
||||
;control min-grain-gap "Minimum Grain Separation (ms)" float "" 1 0 50
|
||||
;control grain-length "Grain Length (ms)" float "" 500 0 1000
|
||||
;control grain-type "Grain Envelope" choice "Triangle,Sine,Exponential,Rectangular" 0
|
||||
;control normal-places "Normalize Sample Start Control Envelope" choice "No,Yes" 0
|
||||
|
||||
; 'input' raw sample to granulate
|
||||
; 'gaps-env' t: grain number x: absolute time in seconds until next grain
|
||||
; 'starts' processed gaps - list of absolute start times of grains
|
||||
; 'place-env' t: relative time in output x: relative input time where grain starts
|
||||
; 'places' processed places - list of relative input times where grains start
|
||||
; where gaps-env or place-env is negative, grains are reversed.
|
||||
|
||||
(defun scratch-cleanup ()
|
||||
(remprop '*SCRATCH* '*DM-GRAN-STARTS*)
|
||||
(remprop '*SCRATCH* '*DM-GRAN-PLACES*)
|
||||
(remprop '*SCRATCH* '*DM-GRAN-SIGN*))
|
||||
|
||||
(defun get-r-grain-length ()
|
||||
(/ grain-length (get-duration 1) 1000.0))
|
||||
|
||||
;;;FUNCTIONS CALLED BY FIRST RUN
|
||||
(defun array-to-list (arr)
|
||||
(do (lst
|
||||
(ind 0 (1+ ind))
|
||||
(end (- (length arr) 1)))
|
||||
((> ind end) (reverse lst))
|
||||
(push (aref arr ind) lst)))
|
||||
|
||||
(defun downsample-and-list (sig)
|
||||
(array-to-list (snd-fetch-array
|
||||
(force-srate (/ grain-number (get-duration 1)) sig)
|
||||
grain-number 1)))
|
||||
|
||||
(defun gaps-to-starts (lst)
|
||||
(do ((rslt (list 0)))
|
||||
((null lst) (cdr (reverse rslt)))
|
||||
(push
|
||||
(+ (car rslt)
|
||||
(max min-grain-gap (abs (pop lst))))
|
||||
rslt)))
|
||||
|
||||
(defun first-invocation ()
|
||||
;;;resample first track so that we have one sample per grain
|
||||
;;;cast sound to list, take cumulative sum and store for future invocations
|
||||
;;;also record when list is negative so we know when to reverse grains
|
||||
;;mono error
|
||||
(if (arrayp *TRACK*)
|
||||
(throw 'err (format nil "First two tracks should be mono.")))
|
||||
(let ((gaps-list (downsample-and-list *TRACK*)))
|
||||
(putprop '*SCRATCH*
|
||||
(gaps-to-starts gaps-list)
|
||||
'*DM-GRAN-STARTS*)
|
||||
(putprop '*SCRATCH*
|
||||
(mapcar #'minusp gaps-list)
|
||||
'*DM-GRAN-SIGN*)))
|
||||
|
||||
;;;FUNCTIONS CALLED BY SECOND RUN
|
||||
(defun rev-time (len tim)
|
||||
;;;grain starts at local time tim, local duration is len.
|
||||
;;;find local start time such that reversing track plays same grain up to reversal.
|
||||
(- 1.0 tim len))
|
||||
|
||||
(defun normalize-places (lst)
|
||||
;;;ensure elements span from 0 to 1 (or if it's constant, 0)
|
||||
(if (and lst normal-places)
|
||||
(let* ((hi (apply #'max lst))
|
||||
(lo (apply #'min lst))
|
||||
(spread (- hi lo)))
|
||||
(if (= spread 0)
|
||||
(mapc (lambda (y) 0) lst)
|
||||
(mapc (lambda (y) (/ (- y lo) spread)) lst)))
|
||||
lst))
|
||||
|
||||
(defun get-final-duration (starts)
|
||||
;;;duration of processed track
|
||||
(+ (/ grain-length 1000.0) (car (last starts))))
|
||||
|
||||
(defun process-place-env (starts reverses)
|
||||
;;;convert times to relative start times, look up values of track
|
||||
;;;reverse grains whenever first or second track is negative
|
||||
(do ((dur-out (get-final-duration starts))
|
||||
rev reverses-out
|
||||
place places)
|
||||
((null starts)
|
||||
(cons
|
||||
(reverse (normalize-places places))
|
||||
(reverse reverses-out)))
|
||||
(setf place (sref *TRACK* (/ (pop starts) dur-out)))
|
||||
(setf rev (or (pop reverses) (minusp place)))
|
||||
;;Reversing grains is accomplished by graining a reversed copy of the whole track.
|
||||
;;Thus we need to adjust the start times.
|
||||
(push
|
||||
(if rev
|
||||
(rev-time (get-r-grain-length) (abs place))
|
||||
place) places)
|
||||
(push rev reverses-out)))
|
||||
|
||||
(defun second-invocation ()
|
||||
;;;get list of absolute start times of grains from previous invocation
|
||||
;;;convert to relative start times
|
||||
;;;look up values of current (i.e. second) track at rel. times, store for later
|
||||
(if (arrayp *TRACK*)
|
||||
(throw 'err (format nil "First two tracks should be mono."
|
||||
(scratch-cleanup))))
|
||||
(let ((results (process-place-env
|
||||
(get '*SCRATCH* '*DM-GRAN-STARTS*)
|
||||
(get '*SCRATCH* '*DM-GRAN-SIGN*))))
|
||||
(putprop '*SCRATCH* (car results) '*DM-GRAN-PLACES*)
|
||||
(putprop '*SCRATCH* (cdr results) '*DM-GRAN-SIGN*)))
|
||||
|
||||
;;;FUNCTIONS CALLED BY THIRD RUN
|
||||
(defun array-reverse (a length)
|
||||
(do ((left 0 (1+ left))
|
||||
(right (- length 1) (1- right))
|
||||
(middle (/ length 2))
|
||||
temp)
|
||||
((= left middle) a)
|
||||
(setf temp (aref a left))
|
||||
(setf (aref a left) (aref a right))
|
||||
(setf (aref a right) temp)))
|
||||
|
||||
(defun snd-reverse (sig)
|
||||
(let* ((len (snd-length sig NY:ALL))
|
||||
(arr (snd-samples sig len)))
|
||||
(snd-from-array
|
||||
(snd-t0 sig)
|
||||
*sound-srate*
|
||||
(array-reverse arr len))))
|
||||
|
||||
(defun grain-envelope (len)
|
||||
(case grain-type
|
||||
(0 (pwl (/ len 2.0) 1 len 0)) ;triangle
|
||||
(1 (lfo (/ 0.5 (get-duration 1) len) len)) ;sine
|
||||
(2 (scale (/ (- 1 len)) (diff (pwev 1 len len) len))) ;exp
|
||||
(3 1))) ;rect
|
||||
|
||||
(defun any (lst)
|
||||
(do ((rslt nil (pop lst)))
|
||||
((or rslt (null lst)) rslt)))
|
||||
|
||||
(defun granulate (sample)
|
||||
;;;extract grains from sample at relative times specified by places
|
||||
;;;apply envelope to each grain, cue grains at absolute times specified by starts
|
||||
(do* ((starts (get '*SCRATCH* '*DM-GRAN-STARTS*))
|
||||
(places (get '*SCRATCH* '*DM-GRAN-PLACES*))
|
||||
(revers (get '*SCRATCH* '*DM-GRAN-SIGN*))
|
||||
;;optimization - only reverse the sample if we need to.
|
||||
(rev-sample (if (any revers) (snd-reverse sample) nil))
|
||||
(throw 'err (format nil "AAA"))
|
||||
place
|
||||
r-grain-length
|
||||
(grain (s-rest 1))
|
||||
(out (s-rest 0)))
|
||||
((null starts) out)
|
||||
(setq place (pop places))
|
||||
(setq r-grain-length (get-r-grain-length))
|
||||
(setf grain (mult (grain-envelope r-grain-length)
|
||||
(extract place (+ place r-grain-length)
|
||||
(if (pop revers) rev-sample sample))))
|
||||
(setf out (sim (at 0 (cue out))
|
||||
(at-abs (pop starts) (cue grain))))))
|
||||
|
||||
(defun process-sample-track ()
|
||||
(prog1
|
||||
(multichan-expand #'granulate *TRACK*)
|
||||
(if (= index tracks) (scratch-cleanup))))
|
||||
|
||||
;;;GLOBALS
|
||||
(setq index (get '*TRACK* 'INDEX))
|
||||
(setq tracks (length (get '*SELECTION* 'TRACKS)))
|
||||
(setq min-grain-gap (/ min-grain-gap 1000.0))
|
||||
|
||||
;;;MAIN
|
||||
(catch 'err
|
||||
(if (< tracks 3)
|
||||
(throw 'err (format nil
|
||||
"Select three or more audio tracks. The top two should be mono."))
|
||||
(cond
|
||||
((= index 1) (first-invocation) *TRACK*)
|
||||
((= index 2) (second-invocation) *TRACK*)
|
||||
(T (process-sample-track)))))
|
|
@ -0,0 +1,34 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type process
|
||||
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
|
||||
;preview linear
|
||||
;name "Harmonize..."
|
||||
;action "Harmonizing..."
|
||||
;author "dm"
|
||||
|
||||
;control decay "Decay" float "" 0.5 0 1
|
||||
;control harmonics "Harmonics" int "" 2 1 10
|
||||
|
||||
(setq minor-third (/ 6.0 5.0))
|
||||
(setq major-third (/ 5.0 4.0))
|
||||
|
||||
(defun get-ratio (index)
|
||||
(if (evenp index)
|
||||
(/
|
||||
(* (expt minor-third (float (/ index 4)))
|
||||
(expt major-third (float (/ (+ 2 index) 4)))))
|
||||
(*
|
||||
(expt minor-third (float (+ 1 (/ index 4))))
|
||||
(expt major-third (float (/ (+ 1 index) 4))))))
|
||||
|
||||
(defun normalize (signal)
|
||||
(let ((x (* (peak signal ny:all) 0.95)))
|
||||
(setq signal (scale (/ x) signal))))
|
||||
|
||||
(do* ((index 0 (incf index))
|
||||
(ratio nil (get-ratio index))
|
||||
(vol 1.0 (* vol decay))
|
||||
(result *TRACK*
|
||||
(sum result (scale vol (pitshift *TRACK* ratio 1.0)))))
|
||||
((>= index harmonics) (normalize result)))
|
|
@ -0,0 +1,25 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
|
||||
;preview linear
|
||||
;name "Inharmonic..."
|
||||
;action "Generating Tone..."
|
||||
;author "dm"
|
||||
|
||||
;control decay "Decay" float "" 0.5 0 1
|
||||
;control ratio "Ratio" float "" 1.005 1 1.05
|
||||
;control fund-freq "Frequency (Hz)" float "" 420 1 600
|
||||
;control duration "Length (Seconds)" float "" 3 0 6
|
||||
|
||||
(setq loop-number 500)
|
||||
|
||||
(do* ((cur-ind 1 (incf cur-ind))
|
||||
(cur-pch (hz-to-step fund-freq) (hz-to-step (* (/ (+ cur-ind 1) 1.0 cur-ind) ratio (step-to-hz cur-pch))))
|
||||
(cur-vol decay (* decay cur-vol))
|
||||
(result (scale cur-vol (sine cur-pch duration))
|
||||
(sum result (scale cur-vol (sine cur-pch duration)))))
|
||||
(
|
||||
(or (> cur-pch 120) (>= cur-ind loop-number)) result)
|
||||
(print cur-pch)
|
||||
(print cur-vol))
|
|
@ -0,0 +1,66 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type process
|
||||
;preview linear
|
||||
;name "Noise Modulation..."
|
||||
;action "Modulating..."
|
||||
;author "dm"
|
||||
|
||||
;control mixes-str "Wet-Dry Mix List" string "" "0.5 0 1"
|
||||
;control width-str "Passband Width" string "" "100 300 0"
|
||||
;control lp-iterations "Stopband Attenuation" int "" 5 0 15
|
||||
|
||||
;--- GLOBAL VARIABLES
|
||||
|
||||
;--- PREPARE INPUT
|
||||
(defun cast-nonnegative-float (element)
|
||||
(cond
|
||||
((floatp element) (abs element))
|
||||
((integerp element) (float (abs element)))
|
||||
(T 0.0)))
|
||||
|
||||
(defun list-to-floats (input-list)
|
||||
(mapcar #'cast-nonnegative-float input-list))
|
||||
|
||||
;;;stole this one from David R. Sky's Sequencer 2.
|
||||
(defun string-to-list (string)
|
||||
(read (make-string-input-stream (format nil "(~a)" string))))
|
||||
|
||||
;;;add some validation to these functions at some point
|
||||
(defun process-string (string)
|
||||
(list-to-floats (string-to-list string)))
|
||||
|
||||
;;; COMPUTE SOUND
|
||||
|
||||
;;;this should be written better
|
||||
(defun gen-envelope (inlist &optional (cutoff (/ *control-srate* 2.1)))
|
||||
(if (= 1 (length inlist))
|
||||
(const (car inlist) 1.0)
|
||||
(lowpass6 (pwlvr-list (reverse (cdr
|
||||
(do* ((ylist inlist (cdr ylist))
|
||||
(height (car ylist) (car ylist))
|
||||
(interval (/ (- (length ylist) 1.0)))
|
||||
(bp-list (list interval height)
|
||||
(cons interval (cons height bp-list))))
|
||||
((null (cdr ylist)) bp-list)))))
|
||||
cutoff)))
|
||||
|
||||
(defun iterate-lp (signal cutoff iterations)
|
||||
(if (zerop iterations) signal
|
||||
(iterate-lp (lp signal cutoff) cutoff (- iterations 1))))
|
||||
|
||||
(defun gen-noise (widths)
|
||||
(let ((cutoff (gen-envelope widths)))
|
||||
(iterate-lp (noise 1) cutoff lp-iterations)))
|
||||
|
||||
(defun normalize (signal)
|
||||
(let ((x (/ 0.95 (peak signal ny:all))))
|
||||
(setq signal (scale x signal))))
|
||||
|
||||
(let* ((modulator (gen-noise (process-string width-str)))
|
||||
(wet-env (gen-envelope (process-string mixes-str)))
|
||||
(dry-env (diff 1.0 wet-env))
|
||||
(raw (normalize *TRACK*))
|
||||
(dry (mult dry-env raw))
|
||||
(wet (mult wet-env modulator raw)))
|
||||
(normalize (sum dry wet)))
|
|
@ -0,0 +1,39 @@
|
|||
;nyquist plug-in
|
||||
;name "Random Tones..."
|
||||
;type generate
|
||||
;version 4
|
||||
;author "dm"
|
||||
;action "Generating Random Tones..."
|
||||
|
||||
;control seed "Random Seed" int "" 12532 1 134455
|
||||
;control duration "Length (Seconds)" float "" 3 0 10
|
||||
;control num-tones "Number of tones" int "" 15 1 100
|
||||
;control middle "Center Frequency (Hz)" float-text "" 1000 0 20000
|
||||
;control width "Width" float-text "" 1 0 20000
|
||||
;control table-type "Table" choice "Sine,Saw,Square,Triangle" 0
|
||||
|
||||
;;;generate pseudorandom int between 1 and 134456
|
||||
(defun rnd ()
|
||||
(setq seed (rem (sum (mult 8121 seed) 28411) 134456)))
|
||||
|
||||
;;;generate pseudrandom float between 0 and 1
|
||||
(defun rnd-scale ()
|
||||
(/ (float (rnd)) 134456.0))
|
||||
|
||||
;;;generate pseudorandom (uniformly distributed) frequency within width of middle
|
||||
(defun rnd-freq ()
|
||||
(print (min (/ *sound-srate* 2.1)
|
||||
(+ middle (* (- (rnd-scale) 0.5) width 2.0)))))
|
||||
|
||||
(defun gen-tone (hz-freq tbl)
|
||||
(case tbl
|
||||
(0 (sine (hz-to-step hz-freq)))
|
||||
(1 (osc-saw hz-freq))
|
||||
(2 (osc-pulse hz-freq 0.0))
|
||||
(3 (osc-tri hz-freq))))
|
||||
|
||||
(stretch-abs duration
|
||||
(do* ((tone 0 (incf tone))
|
||||
(pch 1.0 (rnd-freq))
|
||||
(result 0 (sum result (scale (rnd-scale) (gen-tone pch table-type)))))
|
||||
((= tone num-tones) (scale (/ (float num-tones)) result))))
|
|
@ -0,0 +1,117 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;name "Random Piecewise Linear..."
|
||||
;action "Generating..."
|
||||
;author "dm"
|
||||
;copyright "Released under terms of the GNU General Public License version 2"
|
||||
;release 1.1
|
||||
|
||||
;control duration "Duration" time "" 3 0 nil
|
||||
;control fund-freq "Frequency (Hz)" float "" 50 1 300
|
||||
;control locks "Parameter Lock (Table Duration)" string "" "(5 3.5) (7 1.1)"
|
||||
;control text "Timbral Parameters:"
|
||||
;control seed "Random Seed" int "" 12532 1 134455
|
||||
;control num-tables "Number of Key Tables" int "" 5 1 50
|
||||
;control num-breakpoints "Max. Segments Per Key Table" int "" 5 1 15
|
||||
;control vary-breakpoints "Vary Segments Per Key Table" choice "No,Yes" 0
|
||||
;control table-type "Table In" choice "Sine,Saw,Fifth,Triangle,Random" 0
|
||||
|
||||
(defun rnd ()
|
||||
;;;pseudorandom number between -1 and 1
|
||||
(setq seed (rem (sum (mult 8121 seed) 28411) 134456))
|
||||
(- (/ (float seed) 134456 0.5) 1))
|
||||
|
||||
;;;GENERATE LIST OF WAVETABLES AND BREAKPOINTS FOR SIOSC
|
||||
|
||||
(defun build-shaper (num-breaks)
|
||||
;;;pseudorandom piecewise linear function on [0,2] with num-breaks pieces
|
||||
(if (zerop num-breaks)
|
||||
(pwlv -1 2.01 1)
|
||||
(do* ((i 1 (+ i 1))
|
||||
(new-time 0 (sum (rnd) 1 new-time))
|
||||
(point-list (list (rnd)) (cons (rnd) (cons new-time point-list))))
|
||||
((> i num-breaks)
|
||||
(stretch-abs (/ 2.01 new-time) (pwlv-list (reverse point-list)))))))
|
||||
|
||||
(defun get-num-breaks (max-breaks)
|
||||
;;;decide how many breakpoints build-shaper should create
|
||||
(case vary-breakpoints
|
||||
(0 max-breaks)
|
||||
(1 (truncate (* max-breaks (+ 1 (rnd)))))))
|
||||
|
||||
(defun gen-table (raw-table max-breaks)
|
||||
(shape raw-table (build-shaper (get-num-breaks max-breaks)) 1))
|
||||
|
||||
(defun get-raw-table ()
|
||||
;;;decide which raw table to use
|
||||
(case table-type
|
||||
(0 (build-harmonic 1 2048))
|
||||
(1 (pwlv -1 1 1))
|
||||
(2 (scale 0.49 (sum (build-harmonic 2 2048) (build-harmonic 3 2048))))
|
||||
(3 (pwlv -1 1 1 2 -1))
|
||||
(4 (scale 0.49 (build-shaper num-breakpoints)))))
|
||||
|
||||
(defun wav-list (duration num-tables breaks-per-table slock-list)
|
||||
(if (= num-tables 1)
|
||||
(let ((cur-table (gen-table (get-raw-table) breaks-per-table)))
|
||||
(list cur-table duration cur-table))
|
||||
(let ((interval (/ duration num-tables))
|
||||
(raw-table (get-raw-table)))
|
||||
(do* ((ind 1 (+ 1 ind))
|
||||
(cur-time interval (sum interval cur-time))
|
||||
(cur-table (gen-table raw-table breaks-per-table)
|
||||
(gen-table raw-table breaks-per-table))
|
||||
(breakpoints-list (list cur-table)
|
||||
(cons cur-table (cons cur-time breakpoints-list))))
|
||||
((= ind num-tables) (reverse breakpoints-list))
|
||||
;;locks: continue to use current wavetable for specified time
|
||||
(if (and slock-list (= ind (caar slock-list)))
|
||||
(progn
|
||||
(setf cur-time (sum (cadar slock-list) cur-time))
|
||||
(setf breakpoints-list (cons cur-table (cons cur-time breakpoints-list)))
|
||||
(setf slock-list (cdr slock-list))))))))
|
||||
|
||||
;;;VALIDATE AND PROCESS USER INPUT
|
||||
|
||||
(defun sort-by-car (lst)
|
||||
(sort lst
|
||||
(lambda (x y) (< (car x) (car y)))))
|
||||
|
||||
(defun validate-pairs (pair)
|
||||
(if (and (listp pair) (= 2 (length pair)))
|
||||
(if (and (integerp (car pair))
|
||||
(> (car pair) 0)
|
||||
(<= (car pair) num-tables))
|
||||
(if (and (numberp (cadr pair)) (plusp (cadr pair)))
|
||||
pair
|
||||
(throw 'err
|
||||
(format nil "Lock duration ~s not a positive number."
|
||||
(cadr pair))))
|
||||
(throw 'err (format nil
|
||||
"Lock table ~s not between 1 and ~s inclusive." (car pair) num-tables)))
|
||||
(throw 'err (format nil
|
||||
"Lock ~s not a pair of the form (Table Duration)." pair))))
|
||||
|
||||
;;;When 2.3.1 is out, replace with eval-string
|
||||
(defun string-to-list (string)
|
||||
(read (make-string-input-stream (format nil "(~a)" string))))
|
||||
|
||||
(defun process-locks (str)
|
||||
(sort-by-car (mapcar #'validate-pairs (string-to-list str))))
|
||||
|
||||
;;;PROCESS OUTPUT
|
||||
|
||||
(defun pch-mod (dur)
|
||||
;;placeholder
|
||||
(s-rest dur))
|
||||
|
||||
(defun normalize (sig)
|
||||
(let ((ac (highpass8 sig 25)))
|
||||
(scale (/ 0.95 (peak ac ny:all)) ac)))
|
||||
|
||||
;;;main
|
||||
(catch 'err (normalize (siosc
|
||||
(hz-to-step fund-freq)
|
||||
(pch-mod duration)
|
||||
(wav-list duration num-tables num-breakpoints (process-locks locks)))))
|
|
@ -0,0 +1,85 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;preview linear
|
||||
;name "Random Chirps..."
|
||||
;action "Chirping..."
|
||||
;author "dm"
|
||||
|
||||
;control seed "Seed" int "" 4123 1 134456
|
||||
;control duration "Duration" float "" 1 0 5
|
||||
;control num-chirps "Number of Chirps" int-text "" 8 1 100
|
||||
;control rand-vols "Randomize Volumes" choice "No,Yes" 0
|
||||
;control rand-phase "Randomize Phases" choice "No,Yes" 0
|
||||
;control freq-intervals "Frequency intervals (low high time)" string "" "(100 1000)"
|
||||
|
||||
;;;Globals: min-rate, max-rate and above
|
||||
(setq max-rate (/ *sound-srate* 2.1))
|
||||
(setq min-rate 0.1)
|
||||
|
||||
;;;generate pseudorandom int between 1 and 134456
|
||||
(defun rnd ()
|
||||
(setq seed (rem (sum (mult 8121 seed) 28411) 134456)))
|
||||
|
||||
;;;pseudorandom (uniform) float between lo and hi (assume hi > lo)
|
||||
(defun unirand (lo hi)
|
||||
(+ lo (* (- hi lo) (/ (rnd) 134456.0))))
|
||||
|
||||
(defun piece (inif finf crv)
|
||||
(if (zerop crv)
|
||||
(pwlv inif 1 finf)
|
||||
(let* ((epsilon (expt 0.5 crv))
|
||||
(norm (/ (- inif finf) (- 1.0 epsilon)))
|
||||
(arc (scale norm (diff (pwev 1 1 epsilon) epsilon))))
|
||||
(sum finf arc))))
|
||||
|
||||
(defun bottom (pair)
|
||||
(min max-rate (max min-rate (car pair))))
|
||||
|
||||
(defun top (pair)
|
||||
(min max-rate (max min-rate (cadr pair))))
|
||||
|
||||
(defun freq-curve (band-list)
|
||||
(do* ((time -1 (incf time))
|
||||
(cur-band (car band-list) (car band-list))
|
||||
(lo (bottom cur-band) (bottom cur-band))
|
||||
(hi (top cur-band) (top cur-band))
|
||||
(prev-freq nil cur-freq)
|
||||
(cur-freq (unirand lo hi) (unirand lo hi))
|
||||
(crv-param (unirand -10.0 10.0))
|
||||
(segment nil (piece prev-freq cur-freq crv-param))
|
||||
(curve 0 (sim curve (at time (cue segment)))))
|
||||
((= (length band-list) 1) curve)
|
||||
(setf band-list (cdr band-list))))
|
||||
|
||||
(defun chirp (band-list phase)
|
||||
(hzosc (freq-curve band-list) *table* phase))
|
||||
|
||||
;Stole this one from David R. Sky's Sequencer2
|
||||
(defun string-to-list (string)
|
||||
(read (make-string-input-stream (format nil "(~a)" string))))
|
||||
|
||||
(defun process-freq-intervals (string)
|
||||
(let ((raw-list (string-to-list string)))
|
||||
(if (= (length raw-list) 1)
|
||||
(list (car raw-list) (car raw-list))
|
||||
raw-list)))
|
||||
|
||||
(defun get-phase ()
|
||||
(if (zerop rand-phase)
|
||||
0
|
||||
(unirand -180.0 180.0)))
|
||||
|
||||
(defun get-scale ()
|
||||
(if (zerop rand-vols)
|
||||
1.0
|
||||
(unirand 0.0 1.0)))
|
||||
|
||||
(stretch-abs duration
|
||||
(do* ((count 0 (incf count))
|
||||
(bands (process-freq-intervals freq-intervals))
|
||||
(phase nil (get-phase))
|
||||
(vol nil (get-scale))
|
||||
(cur-chirp nil (scale vol (chirp bands phase)))
|
||||
(result 0 (sum result cur-chirp)))
|
||||
((= count num-chirps) (scale (/ (float num-chirps)) result))))
|
|
@ -0,0 +1,28 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
|
||||
;preview linear
|
||||
;name "Reverse Chirp..."
|
||||
;action "Generating Chirp..."
|
||||
;author "dm"
|
||||
|
||||
;control crv-param "Curve" float "" 5 0 10
|
||||
;control top-freq "Top Frequency (Hz)" float "" 6000 1 22050
|
||||
;control duration "Length (Seconds)" float-text "" 0.05 0 5
|
||||
;control tbl-choice "Waveform" choice "Sine,Sawtooth,Triangle,Square" 0
|
||||
|
||||
;tone which lingers at high frequencies before chirping downward to zero
|
||||
;bandpass filter is applied to emphasize frequency halfway down
|
||||
(let* ((passq 5.0)
|
||||
(epsilon (expt 0.5 crv-param))
|
||||
(tbl-form (case tbl-choice
|
||||
(0 *sine-table*)
|
||||
(1 *saw-table*)
|
||||
(2 (maketable (pwlv -1 0.5 -1 0.501 1 1.0 1)))
|
||||
(3 *tri-table*))))
|
||||
(stretch-abs duration
|
||||
(bandpass2
|
||||
(hzosc (scale (- top-freq) (diff (pwev epsilon 1 1) 1))
|
||||
tbl-form 0.0)
|
||||
(/ top-freq 2.0) passq)))
|
|
@ -0,0 +1,70 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
|
||||
;author "dm"
|
||||
;name "Reverb Impulse Drum..."
|
||||
;action "Generating..."
|
||||
;info ""
|
||||
|
||||
;control band-list "List: (center amplitude &optional bandwith)" string "" "(420 0.5 250) (1500 0.3 150) (4300 0.01) (7500 0.02 2000) (12345 0.01 3000) (3500 0.05 300)"
|
||||
;control impulse-type "Kind of Impulse" choice "chirp, noise, spike" 0
|
||||
;control impulse-volume "Volume of Impulse" float "" 0.5 0 1
|
||||
;control impulse-length "Length of Impulse (ms)" float "" 10 1 50
|
||||
;control decay "Decay" float "" 0.5 0.01 1
|
||||
|
||||
(setq impulse-length (mult 0.001 impulse-length))
|
||||
|
||||
;Stolen from David R. Sky's Sequencer2, I don't know how this works.
|
||||
(defun string-to-list (string)
|
||||
(read (make-string-input-stream (format nil "(~a)" string))))
|
||||
|
||||
(defun normalize (signal &optional (amplitude 1))
|
||||
(setf factor (/ amplitude (peak signal ny:all)))
|
||||
(scale factor signal)
|
||||
);end normalize
|
||||
|
||||
(defun gen-noiseband (center &optional (amplitude 1) (bandwidth 1000))
|
||||
(partial (hz-to-step center) (lowpass8 (normalize (noise 1) amplitude) bandwidth))
|
||||
);end gen-noiseband
|
||||
|
||||
(defun gen-response (band-list dur)
|
||||
(setf bands ())
|
||||
(dolist (band-params (string-to-list band-list))
|
||||
(setf bands (cons (apply #'gen-noiseband band-params) bands))
|
||||
);end dolist
|
||||
(mult (normalize (apply #'sim bands) impulse-volume)
|
||||
(pwlv 1 (* dur 0.25) 0.1 dur 0))
|
||||
);end response
|
||||
|
||||
(defun chirp-impulse (dur)
|
||||
(mult (hzosc (sum
|
||||
(pwlv (/ *sound-srate* 2.1) (* dur 0.3) 100 dur 1) ;frequency of hzosc
|
||||
(scale 100 (noise dur)) ;noise to vary frequency of chirp randomly
|
||||
));end of hzosc
|
||||
(pwlv 1 dur 0)) ;envelope to get rid of click at and of impulse
|
||||
);end gen-impulse
|
||||
|
||||
(defun noise-impulse (dur)
|
||||
(noise dur)
|
||||
)
|
||||
|
||||
(defun spike-impulse (dur)
|
||||
(pwl (/ dur 5.0) 1 dur 0)
|
||||
)
|
||||
|
||||
(defun gen-impulse (dur)
|
||||
(cond ;chooses which kind of impulse
|
||||
((= impulse-type 0) (chirp-impulse dur))
|
||||
((= impulse-type 1) (noise-impulse dur))
|
||||
((= impulse-type 2) (spike-impulse dur))
|
||||
);end cond
|
||||
)
|
||||
|
||||
|
||||
(setf impulse (normalize (gen-impulse impulse-length) impulse-volume))
|
||||
(setf response (gen-response band-list decay))
|
||||
(lowpass8 (sum
|
||||
impulse
|
||||
(convolve impulse response))
|
||||
15000)
|
|
@ -0,0 +1,25 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type generate
|
||||
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
|
||||
;preview linear
|
||||
;name "Common Ratio Tone..."
|
||||
;action "Generating Tone..."
|
||||
;author "dm"
|
||||
|
||||
;control decay "Decay" float "" 0.5 0 1
|
||||
;control ratio "Ratio" float "" 2 1 5
|
||||
;control fund-freq "Frequency (Hz)" float "" 420 1 600
|
||||
;control duration "Length (Seconds)" float "" 3 0 6
|
||||
|
||||
(setq loop-number (truncate
|
||||
(/ (log (/ *sound-srate* fund-freq)) (log ratio))))
|
||||
|
||||
(do* ((cur-ind 1 (incf cur-ind))
|
||||
(cur-pch (hz-to-step fund-freq) (hz-to-step (* ratio (step-to-hz cur-pch))))
|
||||
(cur-vol decay (* decay cur-vol))
|
||||
(result (scale cur-vol (sine cur-pch duration))
|
||||
(sum result (scale cur-vol (sine cur-pch duration)))))
|
||||
((>= cur-ind loop-number) result)
|
||||
(print cur-pch)
|
||||
(print cur-vol))
|
|
@ -0,0 +1,16 @@
|
|||
;nyquist plug-in
|
||||
;version 4
|
||||
;type process
|
||||
;name "Apply Window"
|
||||
;action "Windowing..."
|
||||
;author "dm"
|
||||
|
||||
(defun lump (width)
|
||||
(lfo (/ 0.5 (get-duration 1) width) width))
|
||||
|
||||
(setq index (float (get '*TRACK* 'INDEX)))
|
||||
(setq tracks (float (length (get '*SELECTION* 'TRACKS))))
|
||||
(setq start-time (/ (- index 1.0) tracks))
|
||||
(mult *TRACK*
|
||||
(seq (s-rest start-time)
|
||||
(cue (lump (/ 2.0 tracks)))))
|
Loading…
Reference in New Issue