(defun triangle () (let ((first-random (random 1.0)) (second-random (random 1.0))) (/ (+ first-random second-random) 2))) (defun linear () (let ((first-random (random 1.0)) (second-random (random 1.0))) (if (< first-random second-random) first-random second-random))) (defun triangular-mean (mean) (* (triangle) mean 2)) (defun triangular-scaled (mean scale) (+ (- (* (triangle) scale) (/ scale 2)) mean)) (defun make-section (density duration start-time) (if (<= density 0) nil (let* ((onset-in-section (* duration (random 1.0))) (onset-global (+ onset-in-section start-time)) (sustain (* (- duration onset-in-section) (random 1.0)))) (cons (new midi :time onset-global :keynum (+ 21 (* 88 (random 1.0))) :duration sustain :amplitude (triangle)) (make-section (- density 1) duration start-time))))) ; simplest version - sections don't overlap ; section contents are entirely stochastic (defun make-piece (number-sections mean-density mean-duration start-time) (if (<= number-sections 0) nil (let* ((next-duration (triangular-mean mean-duration)) (next-start (+ start-time next-duration)) (next-density (triangular-mean mean-density))) (append (make-section next-density next-duration start-time) (make-piece (- number-sections 1) mean-density mean-duration next-start))))) ; section definition with a little more intra-section consistency (defun make-profiled-section (density duration start-time mean-duration mean-amplitude amplitude-scale mean-pitch pitch-scale) (if (<= density 0) nil (let* ((onset-in-section (* duration (random 1.0))) (onset-global (+ onset-in-section start-time)) (sustain (* (- duration onset-in-section) (triangular-mean mean-duration)))) (cons (new midi :time onset-global :keynum (triangular-scaled mean-pitch pitch-scale) :duration sustain :amplitude (abs (triangular-scaled mean-amplitude amplitude-scale))) (make-profiled-section (- density 1) duration start-time mean-duration mean-amplitude amplitude-scale mean-pitch pitch-scale))))) ; (events (make-profiled-section 25 8 0 0.05 0.8 0.2 76 10) "test.mid") ; piece definition which makes overlap a possibility, utilizes make-profiled-section (defun make-overlapping-piece (number-sections mean-density mean-section-duration start-time mean-note-duration mean-note-amplitude mean-amplitude-scale mean-note-pitch mean-pitch-scale) (if (<= number-sections 0) nil (let* ((next-duration (triangular-mean mean-section-duration)) (next-start (+ start-time (triangular-mean mean-section-duration))) (next-density (triangular-mean mean-density))) (append (make-profiled-section next-density next-duration start-time (triangular-mean mean-note-duration) (triangular-mean mean-note-amplitude) (triangular-mean mean-amplitude-scale) (triangular-mean mean-note-pitch) (triangular-mean mean-pitch-scale)) (make-overlapping-piece (- number-sections 1) mean-density mean-section-duration next-start mean-note-duration mean-note-amplitude mean-amplitude-scale mean-note-pitch mean-pitch-scale))))) ; (events (make-overlapping-piece 8 35 15 0 0.5 0.5 0.2 72 24) "test.mid") ; (events (make-overlapping-piece 8 45 7.5 0 0.1 0.1 0.05 96 3) "test.mid") ; (events (make-overlapping-piece 8 45 7.5 0 0.05 0.05 0.02 96 2) "test.mid")