;;; ;;; "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)))) ;;; (with-sound()(try 3.7)) ;;; (with-sound()(try 2 :rhythm 0.4 :times 10)) ;; (defun try (parameter &key (start-time 0) (times 50) (rhythm 0.1) (duration 0.4) (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 start-time by rhythm do (format t " ~s" time) (fm-violin time duration (hertz (floor (interpl d transfer-function))) 0.05)))) ;;; (with-sound()(try 2 :rhythm 0.2 :duration 0.1 :times 20)) ;;; (with-sound()(try 2.8 :rhythm 0.2 :duration 0.1 :times 20 :transfer-function '(0 60 1 120))) ;;; (with-sound()(try 3.8 :rhythm 0.2 :duration 0.1 :times 20 :transfer-function '(0 60 1 120))) ;;; this function creates a transfer function that maps the space 0...1 into ;;; the particular notes specified in the list (defun map-notes (note-list) (let* ((n (/ (length note-list))) (prev 0)) `(,@(loop for note in note-list for x from 0 by n collect x collect prev collect x collect note do (setf prev note)) 1 ,(first (last note-list))))) (defun map-notes (note-list &key (transpose 0)) (let* ((tnotes (mapcar (lambda(x)(+ x transpose)) note-list)) (n (/ (length note-list))) (prev 0)) `(,@(loop for note in tnotes for x from 0 by n collect x collect prev collect x collect note do (setf prev note)) 1 ,(first (last note-list))))) (with-sound() (try 3.7 :start-time 0 :rhythm 0.2 :duration 0.1 :times 20 :transfer-function (map-notes '(60 61 66 68 71 20) :transpose 0)) (try 3.7 :start-time 2 :rhythm 0.2 :duration 0.1 :times 20 :transfer-function (map-notes '(60 61 66 68 71 20) :transpose 4))) (with-sound() (loop for transp in '(0 2 6 -12) for start in '(0 1 3 4) for p in '(1 3 3.5 3.9) do (try p :start-time start :rhythm 0.2 :duration 0.1 :times 20 :transfer-function (map-notes '(60 61 66 68 71 20) :transpose transp)))) ;;; (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) (duration 0.4) (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 duration (interpl d transfer-function) 0.05)))) ;;; or: second function maps numbers directly into frequencies: (defun try (parameter &key (times 50) (rhythm 0.1) (duration 0.4)) (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 duration (+ 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) (rhythm 0.1) (duration 0.4)) (if (numberp parameter) (setf parameter (list 0 parameter 1 parameter))) (let* ((x (random 1.0)) (rhythm rhythm) (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 duration (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) (rhythm 0.1) (dur 0.4)) (if (numberp parameter) (setf parameter (list 0 parameter 1 parameter))) (let* ((x (random 1.0)) (rhythm rhythm) (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 dur ;; frequency (hertz (floor (* 100 x))) ;; amplitude (amplitude (* 0.5 z)) :fm-index (max 0 (* 5 (+ y index-offset))))))) ;;; change *power* to get an exponential mapping of input to amplitude... ;; 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)) (with-sound() (try '(0 2 0.3 3.857 0.5 3.856 0.6 3 1 3.2) :times 20 :index 0.3 :rhythm 0.5) (try '(0 3.2 0.3 3.857 0.5 3.856 0.6 3 1 3.4) :times 50 :index 2 :rhythm 0.2) (try '(0 3.5 0.3 3.857 0.5 3.856 0.6 3 1 3.45) :times 100 :index 4 :dur 0.06)) (defun try (parameter &key (total-duration 10) (times 50) (index 1.0) (index-p 3.5) (index-a 3.6) (index-offset -0.2) (rhythm 0.1) (dur 0.4)) (if (numberp parameter) (setf parameter (list 0 parameter 1 parameter))) (let* ((x (random 1.0)) (rhythm rhythm) (norm (clm::x-norm parameter total-duration))) (loop while (< time total-duration) 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 dur ;; frequency (hertz (floor (* 100 x))) ;; amplitude (amplitude (* 0.5 z)) :fm-index (max 0 (* 5 (+ y index-offset)))))))