(provide 'snd-fullmix.scm)

(if (and (not (provided? 'snd-ws.scm)) 
	 (not (provided? 'sndlib-ws.scm)))
    (load "ws.scm"))


(definstrument (fullmix in-file beg outdur inbeg matrix srate reverb-amount)
  ;; "matrix" can be a simple amplitude or a list of lists
  ;;     each inner list represents one input channel's amps into one output channel
  ;;     each element of the list can be a number, a list (turned into an env) or an env
  ;;
  ;; "srate" can be a negative number (read in reverse), or an envelope.
  (let* ((st (seconds->samples (or beg 0.0)))
	 (dur (or outdur
		  (/ (- (mus-sound-duration in-file) (or inbeg 0.0))
		     (or (and srate (number? srate) (abs srate)) 1.0))))
	 (samps (seconds->samples dur))
	 (nd (+ st samps))
	 (in-chans (channels in-file))
	 (inloc (floor (* (or inbeg 0.0) (mus-sound-srate in-file))))
	 (out-chans (channels *output*))
	 (mx (if matrix
		 (make-mixer (max in-chans out-chans))
		 (make-scalar-mixer (max in-chans out-chans) 1.0)))
	 (rev-mx (if (and *reverb* reverb-amount (> reverb-amount 0.0))
		     (let ((rmx (make-mixer in-chans)))
		       (do ((i 0 (+ i 1)))
			   ((= i in-chans))
			 (mixer-set! rmx i 0 reverb-amount)) ; 0->assume 1 chan reverb stream, I think
		       rmx)
		     #f))
	 (revframe (if rev-mx (make-frame 1) #f))
	 (reversed (or (and (number? srate) (negative? srate))
		       (and (list? srate) (negative? (cadr srate)))))
	 (file (if (or (not srate) 
		       (and (number? srate) 
			    (= srate 1.0)))
		   (make-file->frame in-file)
		   (let ((vect (make-vector in-chans #f)))
		     (do ((i 0 (+ i 1)))
			 ((= i in-chans))
		       (vector-set! vect i (make-readin in-file i inloc :direction (if reversed -1 1))))
		     vect)))
	 (envs #f)
	 (srcenv (if (list? srate)
		     (make-env srate :duration dur :scaler (if reversed -1.0 1.0))
		     #f)))

    (if matrix
	(begin
	  (if (list? matrix) ; matrix is list of scalers, envelopes (lists), or env gens
	      (do ((inp 0 (+ 1 inp))
		   (off 0 (+ off out-chans)))
		  ((= inp in-chans))
		(let ((inlist (list-ref matrix inp)))
		  (do ((outp 0 (+ 1 outp)))
		      ((= outp out-chans))
		    (let ((outn (list-ref inlist outp)))
		      (if outn
			  (if (number? outn)
			      (mixer-set! mx inp outp outn)
			      (if (or (env? outn)
				      (list? outn))
				  (begin
				    (if (not envs)
					(set! envs (make-vector (* in-chans out-chans) #f)))
				    (if (env? outn)
					(vector-set! envs (+ off outp) outn)
					(vector-set! envs (+ off outp) (make-env outn :duration dur))))
				  (format #t "unknown element in matrix: ~A" outn))))))))
	      (do ((inp 0 (+ 1 inp))) ; matrix is a number in this case (a global scaler)
		  ((= inp in-chans))
		(if (< inp out-chans)
		    (mixer-set! mx inp inp matrix))))))

    (if (or (not srate)
	    (and (number? srate)
		 (= srate 1.0)))
	(begin
	  ;; -------- no src
	  (mus-mix *output* file st samps inloc mx (if envs
						       (let ((v (make-vector in-chans)))
							 (do ((i 0 (+ i 1))
							      (off 0 (+ off out-chans)))
							     ((= i in-chans))
							   (let ((vo (make-vector out-chans #f)))
							     (vector-set! v i vo)
							     (do ((j 0 (+ j 1)))
								 ((= j out-chans))
							       (vector-set! vo j (vector-ref envs (+ off j))))))
							 v)
						       envs))
	  (if rev-mx
	      (mus-mix *reverb* file st samps inloc rev-mx #f)))

	;; -------- with src
	;; unroll the loops if 1 chan input
	(if (= in-chans 1)
	    (let ((sr (make-src :input (vector-ref file 0) :srate (if (number? srate) (abs srate) 0.0)))
		  (outframe (make-frame out-chans)))
	      (if envs
		  (run 
		   (declare (envs clm-vector))
		   (do ((i st (+ i 1)))
		       ((= i nd))
		     (do ((outp 0 (+ 1 outp)))
			 ((= outp out-chans))
		       (if (env? (vector-ref envs outp))
			   (mixer-set! mx 0 outp (env (vector-ref envs outp)))))
		     (let ((inframe (src sr (if srcenv (env srcenv) 0.0))))
		       (frame->file *output* i (sample->frame mx inframe outframe))
		       (if rev-mx (frame->file *reverb* i (sample->frame rev-mx inframe revframe))))))
		  
		  ;; no envs
		  (run 
		   (do ((i st (+ i 1)))
		       ((= i nd))
		     (let ((inframe (src sr (if srcenv (env srcenv) 0.0))))
		       (frame->file *output* i (sample->frame mx inframe outframe))
		       (if rev-mx (frame->file *reverb* i (sample->frame rev-mx inframe revframe))))))))
	    
	    ;; more than 1 chan input
	    (let ((inframe (make-frame in-chans))
		  (outframe (make-frame out-chans))
		  (srcs (make-vector in-chans #f)))
	      (do ((inp 0 (+ 1 inp)))
		  ((= inp in-chans))
		(vector-set! srcs inp (make-src :input (vector-ref file inp) :srate (if (number? srate) (abs srate) 0.0))))
	      
	      (if envs 
		  (run
		   (declare (envs clm-vector))
		   (do ((i st (+ i 1)))
		       ((= i nd))
		     (do ((inp 0 (+ 1 inp))
			  (off 0 (+ off out-chans)))
			 ((= inp in-chans))
		       (do ((outp 0 (+ 1 outp)))
			   ((= outp out-chans))
			 (if (env? (vector-ref envs (+ off outp)))
			     (mixer-set! mx inp outp (env (vector-ref envs (+ off outp)))))))
		     (let ((sr-val (if srcenv (env srcenv) 0.0)))
		       (do ((inp 0 (+ 1 inp)))
			   ((= inp in-chans))
			 (frame-set! inframe inp (src (vector-ref srcs inp) sr-val)))
		       (frame->file *output* i (frame->frame inframe mx outframe))
		       (if rev-mx (frame->file *reverb* i (frame->frame inframe rev-mx revframe))))))
		  
		  ;; no envs
		  (run 
		   (do ((i st (+ i 1)))
		       ((= i nd))
		     (let ((sr-val (if srcenv (env srcenv) 0.0)))
		       (do ((inp 0 (+ 1 inp)))
			   ((= inp in-chans))
			 (frame-set! inframe inp (src (vector-ref srcs inp) sr-val)))
		       (frame->file *output* i (frame->frame inframe mx outframe))
		       (if rev-mx (frame->file *reverb* i (frame->frame inframe rev-mx revframe))))))))))))

#|
(with-sound (:channels 2 :statistics #t)
  (fullmix "pistol.snd")
  (fullmix "2.snd" .5 1)
  (fullmix "2.snd" 1.5 1 0 #f 2.0)
  (fullmix "oboe.snd" 1 2 0 (list (list .1 (make-env '(0 0 1 1) :duration 2 :scaler .5))))
  (fullmix "pistol.snd" 2 1 0 #f .5)
  (fullmix "2.snd" 0 2 0 (list (list .1 .2) (list .3 .4)) 2.0)
  (fullmix "oboe.snd" 3 2 0 (list (list .1 (make-env '(0 0 1 1) :duration 2 :scaler .5))) .25)
  (let ((e0->0 (make-env '(0 0 1 1) :duration 2))
	(e0->1 (make-env '(0 1 1 0) :duration 2))
	(e1->0 (make-env '(0 1 1 0) :duration 2))
	(e1->1 (make-env '(0 0 1 1) :duration 2)))
    (fullmix "2.snd" 4 2 0 (list (list e0->0 e0->1) (list e1->0 e1->1))))
  (let ((e0->0 (make-env '(0 0 1 1) :duration 2))
	(e0->1 (make-env '(0 1 1 0) :duration 2))
	(e1->0 (make-env '(0 1 1 0) :duration 2))
	(e1->1 (make-env '(0 0 1 1) :duration 2)))
    (fullmix "2.snd" 6 2 0 (list (list e0->0 e0->1) (list e1->0 e1->1)) 2.0)))

(with-sound (:channels 2 :statistics #t)
  (fullmix "2.snd" 0 2 0 (list (list .1 .2) (list .3 .4)) 2.0))

(with-sound () (fullmix "pistol.snd" 0 2 2 #f -1.0))

(with-sound (:channels 2)
  (let ((e0->0 (make-env '(0 0 1 1) :duration 2))
	(e0->1 (make-env '(0 1 1 0) :duration 2))
	(e1->0 (make-env '(0 1 1 0) :duration 2))
	(e1->1 (make-env '(0 0 1 1) :duration 2)))
    (fullmix "2.snd" 6 2 0 (list (list e0->0 e0->1) (list e1->0 e1->1))) 2.0))

  
(with-sound () (fullmix "pistol.snd"))
(with-sound () (fullmix "pistol.snd" 1))
(with-sound () (fullmix "pistol.snd" 1 1))
(with-sound () (fullmix "pistol.snd" 0 1 1))
(with-sound (:statistics #t) (fullmix "pistol.snd" 0 1 0 2.0))
(with-sound (:statistics #t :channels 2) (fullmix "pistol.snd" 0 1 0 2.0))
(with-sound (:statistics #t :channels 2) (fullmix "pistol.snd" 0 1 0 (list (list 0.1 0.7))))
(with-sound (:statistics #t :channels 2) (fullmix "pistol.snd" 0 1 0 (list (list 0.1 (list 0 0 1 1)))))

(with-sound (:channels 2 :output "one-2.snd") (do ((i 0 (+ i 1))) ((= i 10000)) (outa i 0.5) (outb i -0.5)))
(with-sound (:channels 4 :output "one-4.snd") (do ((i 0 (+ i 1))) ((= i 10000)) (outa i 0.5) (outb i -0.5) (outc i 0.1) (outd i -0.1)))

(with-sound (:statistics #t :channels 2) (fullmix "one-2.snd" 0 .2 0 '((1.0 0.5) (0.5 1.0))))
(with-sound (:statistics #t :channels 2) (fullmix "one-2.snd" 0 .2 0 (list (list 0.1 (list 0 0 1 1)) (list (list 0 1 1  0) .5))))
(with-sound (:statistics #t :channels 2) 
  (let ((e0->0 (make-env '(0 0 1 1) :end 10000))
	(e0->1 (make-env '(0 1 1 0) :end 10000))
	(e1->0 (make-env '(0 1 1 0) :end 10000))
	(e1->1 (make-env '(0 0 1 1) :end 10000)))
    (fullmix "one-2.snd" 0 .2 0 (list (list e0->0 e0->1) (list e1->0 e1->1)))))


(with-sound (:statistics #t :channels 2 :reverb jc-reverb) 
  (let ((e0->0 (make-env '(0 0 1 1) :end 10000))
	(e0->1 (make-env '(0 1 1 0) :end 10000))
	(e1->0 (make-env '(0 1 1 0) :end 10000))
	(e1->1 (make-env '(0 0 1 1) :end 10000)))
    (fullmix "one-2.snd" 0 .2 0 (list (list e0->0 e0->1) (list e1->0 e1->1)) #f .1)))

(with-sound () (fullmix "oboe.snd" 0 2 0 #f '(0 0.5 1 1 2 .1)))
|#
