;;; -*- Mode: LISP; Syntax: Common-lisp; Base: 10 -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Copyright (c) 92, 93, 94, 98, 99, 2000, 2001 Fernando Lopez Lezcano. 
;;; All rights reserved.
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted and may be copied as long as 
;;; no fees or compensation are charged for use, copying, or accessing
;;; this software and all copies of this software include this copyright
;;; notice. Suggestions, comments and bug reports are welcome. Please 
;;; address email to: nando@ccrma.stanford.edu
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Moving a source using dlocsig

(definstrument move (start-time file
                     &key 
		     (amp 1.0)
		     (paths '()))
  (let* ((in-file (open-input file))
	 (in-file-dur (sound-duration file))
	 (in-file-sr (sound-srate file))
	 (in-file-chans (sound-chans file))
	 (npaths (if (> in-file-chans (length paths))
		     (progn
		       (warn "less paths [~s] than input channels [~s], extra channels ignored" 
			     (length paths) in-file-chans)
		       (length paths))
		   (if (< in-file-chans (length paths))
		       (progn
			 (warn "less input channels [~s] than paths [~s], extra paths ignored" 
			       in-file-chans (length paths))
			 in-file-chans)
		     (length paths))))
	 ;; file readers bank, one for each channel spatialized
	 (in (make-array npaths))
	 ;; dlocsig bank, one for each channel spatialized
	 (loc (make-array npaths))
	 (beg (make-array npaths))
	 (end (make-array npaths))
	 ;; variables used inside the run loop
	 (min-beg)
	 (max-end))
    ;; warn user if sample rates don't match
    (if (/= in-file-sr *srate*)
	(warn "input file sampling rate ~s does not match output sampling rate ~s"
	      in-file-sr *srate*))
    ;; create the input readers for each channel
    (loop 
      for f from 0 below npaths do
      (setf (aref in f)(make-readin :file in-file :channel f)))
    ;; create the dlocsig unit generators
    (loop 
      for d from 0 below npaths do
      (multiple-value-bind (dloc dbeg dend)
	  (make-dlocsig :path (pop paths)
			:start-time start-time
			:duration in-file-dur
			:scaler amp)
	(setf (aref loc d) dloc
	      (aref beg d) dbeg
	      (aref end d) dend)
	(if (or (null min-beg)
		(< dbeg min-beg))
	    (setf min-beg dbeg))
	(if (or (null max-end)
		(> dend max-end))
	    (setf max-end dend))))
    ;; localize the input soundfile
    (run
     (loop for i from min-beg to max-end do
       (declare (type (array dlocs) loc))
       (loop for c from 0 below npaths do
	 (if (and (>= i (aref beg c))
		  (<= i (aref end c)))
	     (dlocsig (aref loc c) i (readin (aref in c)))))))
    (close-input in-file)))


;;; Wrap a moving sound instrument around arbitrary clm code

(defmacro move-sound ((&key 
		       (path nil)
		       (paths nil)
		       (srate *srate*)
		       (channels nil)) &body body)
  (if (or path paths)
      ;; only spatialize if paths were supplied
      `(sound-let ((to-move (:srate ,srate 
			     :channels (if ,channels ,channels (if ,path 1 (length ,paths))))
			    ,.body))
         (format t ";;; moving sound on ~d channels...~%" 
		 (if ,channels ,channels (if ,path 1 (length ,paths))))
	 (move 0 to-move 
	       :paths (if ,path (list ,path) ,paths)))
    ;; otherwise just evaluate the body
    body))


#|
(with-sound(:channels 4 :play nil)
  (move-sound (:path (make-path '((-10 10)(0.1 0.1)(10 -10))))
    (fm-violin 0 1 440 0.1)
    (fm-violin 0.3 2 1020 0.05)))

(with-sound(:channels 4 :play nil)
  (move-sound (:path (make-spiral-path :turns 1.8))
    (fm-violin 0 4 440 0.1)
    (fm-violin 0.3 6 1020 0.05)))

(with-sound(:channels 4 :play nil)
  (setf dlocsig-render-using decoded-ambisonics)
  (move-sound (:path (make-spiral-path :turns 1.8))
    (fm-violin 0 4 440 0.1)
    (fm-violin 0.3 6 1020 0.05)))

(with-sound(:channels 4 :play nil)
  (setf dlocsig-render-using decoded-ambisonics)
  (move-sound (:path (make-spiral-path :turns 1.8))
    (fm-violin 0 4 440 0.1)
    (fm-violin 0.3 6 1020 0.05)))

(with-sound(:channels 4 :play nil)
  (let* ((dlocsig-render-using decoded-ambisonics))
    (move-sound (:path (make-spiral-path :turns 1.8))
		(fm-violin 0 4 440 0.1)
		(fm-violin 0.3 6 1020 0.05)))
  (let* ((dlocsig-render-using amplitude-panning))
    (move-sound (:path (make-spiral-path :turns 1.8))
		(fm-violin 0 4 440 0.1)
		(fm-violin 0.3 6 1020 0.05))))

(with-sound(:channels 4 :reverb freeverb :reverb-channels 4 :play nil)
  (let* ((dlocsig-render-using decoded-ambisonics))
    (move-sound (:path (make-spiral-path :turns 1.8))
		(fm-violin 0 4 440 0.1)
		(fm-violin 0.3 6 1020 0.05)))
  (let* ((dlocsig-render-using amplitude-panning))
    (move-sound (:path (make-spiral-path :turns 1.8))
		(fm-violin 0 4 440 0.1)
		(fm-violin 0.3 6 1020 0.05))))


(with-sound(:channels 4 :reverb freeverb :reverb-channels 4 :play nil)
  (move 0 "/usr/ccrma/sndb/nando/sounds/kitchen/bowl/small-medium-large-1.snd"
	:paths (list (make-spiral-path :turns 1.8))))

(with-sound(:srate 44100 :channels 4 :reverb freeverb :reverb-channels 4 :play nil)
  (move 0 "/usr/ccrma/sndb/nando/sounds/kitchen/bowl/small-medium-large-1.snd"
	:paths (list (make-spiral-path :turns 1.8))))

(with-sound(:srate 44100 :channels 4 :reverb freeverb :reverb-channels 4 :play nil)
  (move 0 "/usr/ccrma/sndb/nando/sounds/kitchen/bowl/small-medium-large-1.snd"
	:paths (list (make-spiral-path :turns 1.8)
		     (make-spiral-path :turns 4 :step-angle -10))))

(with-sound(:srate 44100 :channels 4 :reverb freeverb :reverb-channels 4 :play nil)
  (move 0 "/usr/ccrma/sndb/nando/sounds/kitchen/bowl/small-medium-large-2.snd"
	:paths (list (make-spiral-path :turns 4 :step-angle -10
				       :distance '(0 25 0.5 5 1 30)
				       :velocity '(0 2 0.5 0 1 4)))))

|#
