;; Freeverb - Free, studio-quality reverb SOURCE CODE in the public domain ;; ;; Written by Jezar at Dreampoint, June 2000 ;; http://www.dreampoint.co.uk ;; ;; Translated into clm-2 by Fernando Lopez-Lezcano ;; Version 1.0 for clm-2 released in January 2001 ;; http://www-ccrma.stanford.edu/~nando/clm/freeverb/ ;; ;; Changes to the original code by Jezar (by Fernando Lopez-Lezcano): ;; - the clm version can now work with a mono input or an n-channel input ;; stream (in the latter case the number of channels of the input and output ;; streams must match. ;; - the "wet" parameter has been eliminated as it does not apply to the model ;; that clm uses to generate reverberation ;; - the "width" parameter name has been changed to :global. It now controls the ;; coefficients of an NxN matrix that specifies how the output of the reverbs ;; is mixed into the output stream. ;; - predelays for the input channels have been added. ;; - damping can be controlled individually for each channel. ;; To do: ;; - change stereospread parameter for a generalized n-channel solution to ;; channel decorrelation. ;; Global parameters ;; length of delay lines for comb and allpass filters: ;; these values assume 44.1KHz sample rate, they are scaled for other ;; sampling rates, the values were obtained by listening tests. (defparameter freeverb-stereo-spread 23) (defparameter freeverb-combtuning '(1116 1188 1277 1356 1422 1491 1557 1617)) (defparameter freeverb-allpasstuning '(556 441 341 225)) ;; decay time of room (defparameter freeverb-room-decay 0.5) ;; damping of the comb feedback filters (defparameter freeverb-damping 0.5) ;; predelay for input signal (defparameter freeverb-predelay 0.03) ;; output gain scaler (defparameter freeverb-output-gain 1.0) ;; be verbose with warnings (defparameter freeverb-verbose t) ;; balance between global and local reverb for multichannel input ;; 1 -> all reverberated channels are mixed on all output channels ;; 0 -> each reverberated channel is sent to its correnponding output only (defparameter freeverb-global 0.3) ;; custom output mixing matrix (defparameter freeverb-output-mixer nil) ;; scale and offsets for parameters (defparameter freeverb-scale-damping 0.4) (defparameter freeverb-scale-room-decay 0.28) (defparameter freeverb-offset-room-decay 0.7) ;; ignore sane limits to input parameters (defparameter freeverb-ignore-param-limits nil) ;; CLM unit generator: comb filter with one-zero filtered feedback (def-clm-struct fcomb delay filter (feedback 0.0)) (defmacro fcomb (comb input) `(delay (fcomb-delay ,comb) (+ ,input (* (one-zero (fcomb-filter ,comb) (tap (fcomb-delay ,comb))) (fcomb-feedback ,comb))))) (definstrument freeverb (start-time duration &key (room-decay freeverb-room-decay) (damping freeverb-damping) (global freeverb-global) (predelay freeverb-predelay) (output-gain freeverb-output-gain) (output-mixer freeverb-output-mixer) (verbose freeverb-verbose)) (let* ((beg (floor (* start-time *srate*))) (end (+ beg (floor (* duration *srate*)))) (out-chans (mus-channels *output*)) (out-mix (if (mixer? output-mixer) output-mixer (make-empty-mixer out-chans))) (out-buf (make-empty-frame out-chans)) (out-gain output-gain) (out (make-empty-frame out-chans)) (in-chans (mus-channels *reverb*)) (in (make-empty-frame in-chans)) (predelays (make-array in-chans)) (local-gain (+ (/ (- 1 global)(- 1 (/ out-chans))) (/ out-chans))) (global-gain (/ (- out-chans (* local-gain out-chans)) (- (* out-chans out-chans) out-chans))) (srate-scale (/ *srate* 44100)) (room-decay-val (+ (* room-decay freeverb-scale-room-decay) freeverb-offset-room-decay)) (numcombs (length freeverb-combtuning)) (numallpasses (length freeverb-allpasstuning)) (combs (make-array (list out-chans numcombs))) (allpasses (make-array (list out-chans numallpasses)))) (format t ";;; freeverb: ~d input channels, ~d output channels~%" in-chans out-chans) (if (and (> in-chans 1) (/= in-chans out-chans)) (error "input must be mono or input channels must equal output channels")) ;; create the output mixer if it was not supplied as an argument (if (not (mixer? output-mixer)) (if (arrayp output-mixer) (loop for in from 0 below out-chans do (loop for out from 0 below out-chans do (setf (mixer-ref out-mix in out)(aref output-mixer in out)))) (loop for in from 0 below out-chans do (loop for out from 0 below out-chans do (setf (mixer-ref out-mix in out) (double-float (/ (* out-gain (if (= in out) local-gain global-gain)) out-chans))))))) ;; create the input predelays (loop for c from 0 below in-chans do (setf (aref predelays c) (make-delay :size (* *srate* (if (arrayp predelay) (aref predelay c) (if (listp predelay) (nth c predelay) predelay)))))) ;; create the comb filters (loop for c from 0 below out-chans do (loop for i from 0 for tuning in freeverb-combtuning for len = (floor (* srate-scale tuning)) for dmp = (* freeverb-scale-damping (if (arrayp damping)(aref damping i) (if (listp damping)(nth i damping) damping))) do (if (oddp c)(incf len (floor (* srate-scale freeverb-stereo-spread)))) (setf (aref combs c i) (make-fcomb :delay (make-delay len) :feedback room-decay-val :filter (make-one-zero :a0 (- 1.0 dmp) :a1 dmp))))) ;; create the allpass filters (loop for c from 0 below out-chans do (loop for i from 0 for tuning in freeverb-allpasstuning for len = (floor (* srate-scale tuning)) do (if (oddp c)(incf len (floor (* srate-scale freeverb-stereo-spread)))) (setf (aref allpasses c i) (make-all-pass :size len :feedforward -1 :feedback 0.5)))) (run (loop for i from beg to end do (file->frame *reverb* i in) (if (> in-chans 1) ;; multichannel input: all combs in parallel for all channels (loop for c from 0 below out-chans do (setf (frame-ref in c)(delay (aref predelays c)(frame-ref in c)) (frame-ref out c) 0.0) (loop for j from 0 below numcombs do (incf (frame-ref out c)(fcomb (aref combs c j)(frame-ref in c))))) ;; mono input: all combs in parallel (progn (setf (frame-ref in 0)(delay (aref predelays 0)(frame-ref in 0))) (loop for c from 0 below out-chans do (setf (frame-ref out c) 0.0) (loop for j from 0 below numcombs do (incf (frame-ref out c)(fcomb (aref combs c j)(frame-ref in 0))))))) ;; all allpass filters in series for all channels (loop for c from 0 below out-chans do (loop for j from 0 below numallpasses do (setf (frame-ref out c)(all-pass (aref allpasses c j)(frame-ref out c))))) ;; mix down and output samples (frame->file *output* i (frame->frame out-mix out out-buf))))))