;;;
;;; "Logistics" function, a simple example that illustrates
;;; how a simple change in parameter space can alter the
;;; behavior from simple repetitive selections to chaotic
;;; behavior
;;;
;;; The function itself
(defun logistics (x p)
(* p x (- 1 x)))
;;; A function that runs logistics a given number of times
(defun run-logistics (x p &key (times 200))
(loop repeat times
collect (setf x (logistics x p))))
;;; NOTE: for this work you have to compile and load the
;;; plotter functions...
(plot-data (run-logistics (random 1.0) 3.857 :times 1000) :style "points")
(plot-data (run-logistics (random 1.0) 4 :times 1000) :style "points")
(plot-data (run-logistics (random 1.0) 2.6 :times 100) :style "points")
;;; Simple mapping of output to pitch
(with-sound()
(let* ((data (run-logistics (random 1.0) 3 :times 50)))
;; (plot-data data :style "points")
(loop
for d in data
for time from 0 by 0.1 do
(fm-violin time 0.1 (hertz (floor (* 100 d))) 0.05))))
;;; Hummm, let's package the behavior into a function
;;; first one maps numbers into pitches:
;;; first try gets back just very high notes... wrong parameter scaling!
(defun try (parameter &key (times 50)(rhythm 0.1))
(let* ((data (run-logistics (random 1.0) parameter :times times)))
;; (plot-data data :style "points")
(format t "repeated ~s times~%" (length data))
(loop
for d in data
for time from 0 by rhythm do
(format t " ~s" time)
(fm-violin time 0.4 (hertz (min 90 (+ 40 (floor (* 100 d))))) 0.05))))
;;; Let's try again and map to the min and max values found
;;; setup parameters so that we can control the mapping (scaler and offset)
;;; this one works fine...
(defun try (parameter
&key
(times 50)(rhythm 0.1)
(scaler 40)
(offset 20))
(let* ((data (run-logistics (random 1.0) parameter :times times))
(min (loop for d in data minimize d))
(max (loop for d in data maximize d)))
;; (plot-data data :style "points")
(format t "repeated ~s times, min=~f, max=~f~%" (length data) min max)
(loop
for d in data
for time from 0 by rhythm do
(format t " ~s" time)
(fm-violin time 0.4 (hertz (floor (+ offset (* scaler (/ (- d min)(- max min)))))) 0.05))))
;;
(defun try (parameter
&key
(times 50)(rhythm 0.1)(transfer-function '(0 40 1 80)))
(let* ((data (run-logistics (random 1.0) parameter :times times))
(min (loop for d in data minimize d))
(max (loop for d in data maximize d)))
(format t "max is ~f and min is ~f~%" max min)
;; (plot-data data :style "points")
(format t "repeated ~s times, min=~f, max=~f~%" (length data) min max)
(loop
for d in data
for time from 0 by rhythm do
(format t " ~s" time)
(fm-violin time 0.4 (hertz (floor (interpl d transfer-function))) 0.05))))
;;; (with-sound()(try 3.7 :times 20 :transfer-function '(0 50 0.5 53 0.51 80 1 83)))
(defun try (parameter
&key
(times 50)(rhythm 0.1)(transfer-function '(0 40 1 80)))
(let* ((data (run-logistics (random 1.0) parameter :times times))
(min (loop for d in data minimize d))
(max (loop for d in data maximize d)))
(format t "max is ~f and min is ~f~%" max min)
;; (plot-data data :style "points")
(format t "repeated ~s times, min=~f, max=~f~%" (length data) min max)
(loop
for d in data
for time from 0 by rhythm do
(format t " ~s" time)
(fm-violin time 0.4 (interpl d transfer-function) 0.05))))
;;; or: second function maps numbers directly into frequencies:
(defun try (parameter &key (times 50)(rhythm 0.1))
(let* ((data (run-logistics 0.1 parameter :times times)))
;; (plot-data data :style "points")
(format t "repeated ~s times~%" (length data))
(loop
for d in data
for time from 0 by rhythm do
(fm-violin time 0.4 (+ 60 (* 1000 d)) 0.05))))
;;; different calls for different value of the logistics parameter:
(with-sound()(try 3.5 :times 100))
(with-sound()(try 2.9))
(with-sound()(try 3))
(with-sound()(try 3.2))
(with-sound()(try 3.4))
(with-sound()(try 3.5))
(with-sound()(try 3.55))
(with-sound()(try 3.57))
(with-sound()(try 3.7))
(with-sound()(try 3.8))
(with-sound()(try 3.857))
;;; on this function we put an envelope in the parameter of the
;;; logistics funcion, that is, we change the behavior over
;;; time:
(defun try (parameter &key
(times 50)
(index 1.0))
(if (numberp parameter)
(setf parameter (list 0 parameter 1 parameter)))
(let* ((x (random 1.0))
(rhythm 0.1)
(duration (* rhythm times))
(norm (clm::x-norm parameter duration)))
(loop
repeat times
for time from 0 by rhythm
for x = (random 1.0) then (logistics x (envelope-interp time norm))
collect x into output
do
(fm-violin time 0.4 (hertz (floor (* 100 x))) 0.05 :fm-index index))))
;; finally
;; (plot-data output :style "points"))))
;; A simple example:
(with-sound()(try '(0 2 0.5 3 1 3.7)))
;;; three logistic functions control three different parameters
;;; of each generated note:
(defun try (parameter &key
(times 50)
(index 1.0)
(index-p 3.5)
(index-a 3.6)
(index-offset -0.2))
(if (numberp parameter)
(setf parameter (list 0 parameter 1 parameter)))
(let* ((x (random 1.0))
(rhythm 0.1)
(duration (* rhythm times))
(norm (clm::x-norm parameter duration)))
(loop
repeat times
for time from 0 by rhythm
for x = (random 1.0) then (logistics x (envelope-interp time norm))
for y = (random 1.0) then (logistics y index-p)
for z = (random 1.0) then (logistics z index-a)
collect z into output
do
(fm-violin time 0.4 (hertz (floor (* 100 x)))
(* 0.1 z)
:fm-index (max 0 (* 10 (+ y index-offset)))))))
;; obviously this function could use normalizing of the parameter space...
;; finally
;; (plot-data output :style "points"))))
(with-sound()(try '(0 3 1 3) :times 100 :index-p 3.7))
(with-sound()
(try '(0 2 0.3 3.857 0.5 3.856 0.6 3 1 3.2) :times 100)
(try '(0 3.2 0.3 3.857 0.5 3.856 0.6 3 1 3.4) :times 100)
(try '(0 3.5 0.3 3.857 0.5 3.856 0.6 3 1 3.45) :times 100))
(with-sound()
(try '(0 2 0.3 3.857 0.5 3.856 0.6 3 1 3.2) :times 100 :index 0.3)
(try '(0 3.2 0.3 3.857 0.5 3.856 0.6 3 1 3.4) :times 100 :index 2)
(try '(0 3.5 0.3 3.857 0.5 3.856 0.6 3 1 3.45) :times 100 :index 4))