#|
From: Marco Trevisani <marco@ccrma.Stanford.EDU>

This should work like a Graphic Equalizer....
Very easy to use. Just some note:

"amp" & "amp-env" apply an enveloppe to the final result of the
filtering.  

"dur" as ""standard"" in my instruments, when dur = 0 it will take the length of the
sndfile input, otherwise the duration in seconds.

"gain-freq-list" is a list of gains and frequencies to
filter --in this order gain and frequencies--. There is no limit to
the size of the list. Gain can be a number or an
envelope. Unfortunatelly in this version they cant alternate, one
should chose, all envelopes or all numbers i.e.: 
case 1 -> '( .1 440.0 .3 1500.0 .2 330.0 ...etc) or 
case 2 -> '((0 .1 1 .5) 440.0 (0 1 1 .01) 1500 (0 .3 1 .5) 330.0 ...etc) 
'( .1 440.0 (0 1 1 .01) 1500 ..etc) <<< again, this is not allowed ..

"offset-gain" This apply to all the gains if case 1. It adds or
subtracts an offset to all the gains in the list. This number can be positive or
negative. In case the result is a negative number --let's say offset =
-.4 and, like in case 1, the first gain is .1, the result would be
-.3 -- the instrument will pass a gain equal to 0.  

"filt-gain-scale" & "filt-gain-base" will apply to the elements of the
envelopes if we are in case 2, gains are envelopes.

"stats" if t --default-- prints the number of seconds processed, if
nil doesnt print anything, which will speed up a bit the process.
|#

(definstrument graphEq (file &key (beg 0) (dur 0) (or-beg 0) (amp 1) (amp-env '(0 1 .8 1 1 0)) (amp-base 1) 
	(offset-gain 0)  
	(gain-freq-list '((0 1 1 0) 440 (0 0 1 1) 660))      
	(filt-gain-scale 1)                   
	(filt-gain-base 1)                    
	(a1 .99)
	(stats t))                             
  (let* ((fil (open-input* file))
	 (st (floor (* beg *srate*)))
	 (durata (if (= 0 dur) (sound-duration file) dur))
	 (nd (+ st (floor (* *srate* durata))))
	 (or-start (round (* or-beg (sound-srate file))))
	 (RdA (make-readin :file fil :start or-start))
	 (half-list (/ (length gain-freq-list) 2))
	 (ampenv (make-env :envelope amp-env :scaler amp :duration durata :base amp-base))
	 (gain-list (loop for i from 0 below (length gain-freq-list)
			  when (evenp i) collect (nth i gain-freq-list)))
	 (freq-list (loop for i from 0 below (length gain-freq-list)
			  when (oddp i) collect (nth i gain-freq-list)))
	 (env-size (when (listp (car gain-list))
		     (make-array (length freq-list))))
	 (if-list-in-gain (when (listp (car gain-list)) t))
	 (frm-size (make-array (length freq-list)))
	 (gains (make-array (length freq-list))))
    
    (loop for k from 0 below half-list do
      (when (listp (nth k gain-list))
	(setf (aref env-size k) (make-env :envelope (nth k gain-list)
					  :scaler filt-gain-scale
					  :duration durata :base filt-gain-base))))
    (loop for k from 0 below half-list do
      (if (listp (nth k gain-list))
	  (progn
	    (setf (aref frm-size k) (make-formant (nth k freq-list) a1))
	    (setf (aref gains k) 1.0))
	(progn
	  (setf (aref frm-size k) (make-formant (nth k freq-list) a1))
	  (setf (aref gains k) (if (< (+ offset-gain (nth k gain-list)) 0) 
				   0
				 (+ offset-gain (nth k gain-list)))))))
    (run
      (loop for i from st to nd do
	(declare (type :mus-any* frm-size))
        (when  (and stats (= 44100 (mod i 44200)))
	  (clm-print "~& ~F" (/ i 44100.0)))
	(let ((outval 0.0)
	      (inval (readin RdA)))
	  (loop for k from 0 below half-list do
	    (when if-list-in-gain
	      (setf (mus-xcoeff (aref frm-size k) 0) (* (env (aref env-size k)) (- 1.0 a1))))
	    (incf outval (* (aref gains k)
			    (formant (aref frm-size k) inval))))
	  (outa i (* (env ampenv) outval)))))
    (close-input fil)))



