;; code_12.sal -- examples using patterns to control sounds, ;; sounds to control patterns define function pat-ctrl(durpat, valpat) return seq(const(next(valpat), next(durpat)), pat-ctrl(durpat, valpat)) define function pat-fm(durpat, valpat, pitch, dur) begin with hz = step-to-hz( pitch + pat-ctrl(durpat, valpat)) return pwl(0.01, 1, dur - 0.1, 1, dur) * hzosc(hz + 4.0 * hz * hzosc(hz)) end define function pat-fm-scale() return pat-fm(0.2, make-cycle({0 2 4 5 7 9 11 12}), c4, 4.8) ; play pat-fm-scale() define function pat-fm-fast() return pat-fm(make-random({0.002 0.004 0.006}), make-cycle({0 2 4 5 7 9 11 12}), c4, 4.8) ; play pat-fm-fast() define function pat-fm(durpat, valpat, pitch, dur) begin with hz = step-to-hz( pitch + pat-ctrl(durpat, valpat)) return pwl(0.01, 1, dur - 0.1, 1, dur) * hzosc(hz + 4.0 * hz * hzosc(hz)) end ;; fast modulation creates a complex sound set durpat = make-product( make-copier( make-random({0.005 0.01 0.02 0.0025}, for: 1), repeat: 10), 1.0) set valpat = make-sum( make-copier( ; long-term changes make-accumulate( make-random( {-4 0 1 2 3 {-24 weight: 0.06}}), for: 1, min: -20, max: 20), repeat: make-product( make-random({1 2 3}), 20)), ; short-term changes for make-sum: make-heap({0 3 5 6 7 9})) ; play pat-fm(durpat, valpat, c4, 40) ;; pat-fm-note is a note that can be invoked from a score ;; This function is based on pat-fm-complex.sal ;; define function pat-fm-note(grain-dur: 1.0, spread: 20, pitch: c4, fixed-dur: nil, vel: 100) begin with durpat = make-product( make-copier( make-random( #?(fixed-dur, {0.01}, {0.005 0.01 0.02 0.0025}), for: 1), repeat: 10), grain-dur), valpat = make-sum( make-copier( ; long-term changes make-accumulate( make-random( {-4 0 1 2 3 {-24 :weight 0.06}}), for: 1, min: -20, max: 20), repeat: make-product( make-random({1 2 3}), 20)), ; short-term changes for make-sum make-heap({0 3 5 6 7 9})), duration = get-duration(1.0) return stretch-abs(1.0, vel * 0.01 * pat-fm(durpat, valpat, pitch, duration)) end ;; here is a simple test, a sequence of two pat-fm-notes ; play seq(pat-fm-note(), s-rest(), pat-fm-note(pitch: c5)) define function pat-fm-score() return timed-seq( {{ 0 30 {pat-fm-note grain-dur: 8 spread: 1 pitch: c3 fixed-dur: t vel: 50}} {10 20 {pat-fm-note grain-dur: 3 spread: 10 pitch: c4 vel: 75}} {15 18 {pat-fm-note grain-dur: 1 :spread: 20 pitch: c5}} {20 13 {pat-fm-note grain-dur: 1 spread: 10 grain-dur: 20 pitch: c1}}}) ; play pat-fm-score() ;; these pwl functions control the evolution of the pattern data define variable pitch-contour = pwl(10, 25, 15, 10, 20, 10, 22, 25, 22) define function get-pitch() return sref(pitch-contour, 0) define function pwl-pat-fm() begin with durpat = make-product( make-copier( make-random( {0.005 0.01 0.02 0.0025}, for: 1), repeat: 10), 1), valpat = make-sum( make-copier( ; long-term changes make-eval({get-pitch}), repeat: make-product( make-random({1 2 3}), 20)), ; short-term changes for make-sum make-heap({0 3 5 6 7})) return pat-fm(durpat, valpat, c4, 22) end ;; plot the contour so we can see it while we listen ; exec s-plot(pitch-contour, 1000, 22) ; play pwl-pat-fm() ; generating a score controlled by a SOUND (control function) ; as pitch contour begin with pitch-contour = pwl(10, 25, 15, 10, 20, 10, 22, 25, 22), ioi-pattern = make-heap({0.2 0.3 0.4}) exec score-gen(save: quote(pwl-score), score-dur: 22, pitch: truncate( c4 + sref(pitch-contour, sg:start) + #if(oddp(sg:count), 0, -5)), ioi: next(ioi-pattern), dur: sg:ioi - 0.1, vel: 100) end ; exec s-plot(pitch-contour, 1000, 22) ; exec score-play(pwl-score)