;;; bounded brownian noise ("green noise" was Michael McNabb's name for it)
;;; has a low frequency component from the bounce off the bounds
;;; a sort of weak 1/f noise
;;; on each step, the bounds are checked, and if exceeded, the step is sent the other direction.

(def-clm-struct grn (output 0.0) (amp .1) (lo -1.0) (hi 1.0) (phase 0.0) (freq 0.0) (incr 0.0))

(defmacro green-noise-1 (gr)
  `(let ((val (centered-random (grn-amp ,gr))))
     (incf (grn-output ,gr) val)
     (if (not (<= (grn-lo ,gr) (grn-output ,gr) (grn-hi ,gr)))
	 (decf (grn-output ,gr) (* 2 val)))
     (grn-output ,gr)))

;;; to get brownian noise (which is unbounded, so not usually very useful)
;;; you could simply set the lo and high bounds to some enormous numbers, or:

(defmacro brownian-noise (gr)
  `(incf (grn-output ,gr) (centered-random (grn-amp ,gr))))

(defun make-green-noise (&key (frequency 440.0) (amplitude 1.0) (high 1.0) (low -1.0))
  (make-grn :freq (hz->radians frequency)
	    :amp amplitude
	    :hi high
	    :lo low))

(defmacro green-noise (r &optional (sweep 0.0))
  `(progn
     (if (>= (grn-phase ,r) two-pi)
	 (progn
	   ;(loop while (>= (grn-phase ,r) two-pi) do (decf (grn-phase ,r) two-pi))
	   ;; clisp + run can't deal with this loop: "(LOOP-FINISH) is possible only from within LOOP"
	   ;; so use mod
	   (setf (grn-phase ,r) (mod (grn-phase ,r) two-pi))
	   (green-noise-1 ,r)))
     (incf (grn-phase ,r) (+ (grn-freq ,r) ,sweep))
     ;(loop while (minusp (grn-phase ,r)) do (incf (grn-phase ,r) two-pi))
     (if (< (grn-phase ,r) 0.0)
	 (setf (grn-phase ,r) (mod (grn-phase ,r) two-pi)))
     (grn-output ,r)))

(defun make-green-noise-interp (&key (frequency 440.0) (amplitude 1.0) (high 1.0) (low -1.0))
  (make-grn :freq (hz->radians frequency)
	    :hi high
	    :lo low
	    :amp amplitude
	    :incr (* (centered-random amplitude) (/ frequency *srate*))))

(defmacro green-noise-interp (r &optional (sweep 0.0))
  `(progn
     (incf (grn-output ,r) (grn-incr ,r))
     (when (>= (grn-phase ,r) two-pi)
       (let ((cur (grn-output ,r))
	     (new (green-noise-1 ,r)))
	 (setf (grn-output ,r) cur)
	 ;(loop while (>= (grn-phase ,r) two-pi) do (decf (grn-phase ,r) two-pi))
	 (setf (grn-phase ,r) (mod (grn-phase ,r) two-pi))
	 (setf (grn-incr ,r) (* (- new cur) (/ (+ (grn-freq ,r) ,sweep) two-pi)))))
     ;; (both grn-freq and sweep are in terms of radians/sample, so by dividing by two-pi, we get
     ;; the distance we go to the next new number in terms of 0..1 (i.e. inverse of number of
     ;; samples per period) -- this is equivalent to the multiply in the make function.
     (incf (grn-phase ,r) (+ (grn-freq ,r) ,sweep))
     ;(loop while (minusp (grn-phase ,r)) do (incf (grn-phase ,r) two-pi))
     (if (< (grn-phase ,r) 0.0)
	 (setf (grn-phase ,r) (mod (grn-phase ,r) two-pi)))
     (grn-output ,r)))

#|
(definstrument green (beg end)
  (let* ((grn (make-green-noise :frequency 4000 :amplitude .01 :high .2 :low -.5)))
    (run
     (loop for i from beg below end do
       (outa i (+ .5 (green-noise grn)))))))

(definstrument green (beg end)
  (let* ((grn (make-green-noise-interp :frequency 1000 :amplitude .01 :high .2 :low -.5)))
    (run
     (loop for i from beg below end do
       (outa i (+ .5 (green-noise-interp grn)))))))

|#
