(in-package :cl-patterns) (defun attr-per-phrase (attr-name values &optional (phrase-dur 8)) (pseq (mapcar (lambda (val) (pfindur (pbind attr-name val :dur (pk :dur)) phrase-dur phrase-dur)) values))) ;;; change with previous - another attempt after advice to move :dur higher in outer pattern, that this /should/ pick up (pk :dur) ;; and function wouldn't need dur argument (defun not-perfect-but-arpeggio (chords &optional (dur 1) (phrase-dur 8)) (parp (pbind :chord (pseq chords 2)) ; base for arpegio - for each chord next pattern will play out (psync (pbind :note (pseq (pnary #'nchord (pk :chord))) :dur (pk :dur) ; yes, that is picked up, if :dur is defined above :embed in the outer pattern, yay ) phrase-dur phrase-dur))) ;; IV V iii vi (pb :lets-arpegio-IV-V-iii-vi :dur 1/2 :embed (not-perfect-but-arpeggio (list :major :minor)) ; each chord plays arpeggio for whole phrase (two bars) :embed (attr-per-phrase :legato (list 1 0.7)) ; each phrase takes single value for legato ; even if amount of events in phrase change with change of :dur :embed (attr-per-phrase :root (list 3 4)) ) ;; (next-upto-n (pdef :lets-arpegio-iv-v-iii-vi) 30) ;; (play :lets-arpegio-IV-V-iii-vi) ;; (end :lets-arpegio-IV-V-iii-vi) ;; (stop :lets-arpegio-IV-V-iii-vi) ;; Now, if I have totally repeating phrases (like here) ;; I suppose it would be much better to just use PARP to set per phrase attributes? (pb :lets-arpegio-in-arpegio (parp (pbind :legato (pseq (list 1 0.7)) :root (pseq (list 3 2))) (pbind :dur 1/3 :embed (not-perfect-but-arpeggio (list :major :minor))))) ;; I already see how this wouldn't be the same! ;; PARP would play both chords :major and :minor for setting of (:legato 1 :root 3) and then again both for setting (:legato 0.7 :root 2) ;; so ATTR-PER-PHRASE does something different ;; yup. generally it does (pseq (list (pfindur (pbind .. value ..)) ..) ;; ok! ;;; now let's clean up bass thingy? ;; pattern (list 4 1/2 1/2 (prest 3)) (defun simple-bass-line (chords strum-pattern) (parp (pbind :chord (pseq chords 2)) (pbind :note (pnary #'chord-notes (pk :chord)) ; repeated list of chord notes - played together :dur (pseq strum-pattern 1)))) (pb :simple-base :instrument :fmbass :embed (simple-bass-line (list :major :minor) (list 4 1/2 1/2 (prest 3))) :embed (attr-per-phrase :root (list 3 2) 8) :embed (attr-per-phrase :octave (list 3 2) (* 4 2 2))) ; 4 beats per tact, 2 tacts per chord, 2 hard coded chords (next-upto-n (pdef :simple-base) 40) ;; (play :simple-base) ;; (end :simple-base) ;; (stop :simple-base) (pdef :new-arpegio-and-bass-1 (ppar (list :lets-arpegio-iv-v-iii-vi :simple-base))) ;; (play :new-arpegio-and-bass-1) ;; (end :new-arpegio-and-bass-1) ;; (stop :new-arpegio-and-bass-1) ;;; notes: ;; arpegio hardcodes 2 repetitions ;; base hardcodes now 2 repetiitons as well, to match with :octave change thing ;; also #'SIMPLE-BASE-LINE could just take in pattern for strum-pattern, ;; not list to be used in #'PSEQ ;;; ok! now let's do 4 bars of same, then 4 bars with 1/3 duration in arpeggios ;; IV V iii vi (setq *my-chords* (list :major :major :minor :minor) *my-roots* (list 3 4 2 5)) (pb :lets-arpegio-before-duration :embed (not-perfect-but-arpeggio *my-chords*) ; each chord plays arpeggio for whole phrase (two bars) :embed (attr-per-phrase :root *my-roots*) :embed (attr-per-phrase :legato (list 1 0.7 0.5 0.8)) ; each phrase takes single value for legato ; even if amount of events in phrase change with change of :dur ) ;; (next-upto-n (pdef :lets-arpegio-iv-v-iii-vi) 30) ;; (play :lets-arpegio-IV-V-iii-vi) ;; (end :lets-arpegio-IV-V-iii-vi) ;; (stop :lets-arpegio-IV-V-iii-vi) (pb :arpegio-in-halves :dur 1/2 :embed :lets-arpegio-before-duration) (pb :arpegio-in-thirds :dur 1/4 :embed :lets-arpegio-before-duration) ;; (play :arpegio-in-halves) ;; (stop :arpegio-in-halves) ;; (play :arpegio-in-thirds) ;; (stop :arpegio-in-thirds) (pb :simple-base-2 :instrument :fmbass :embed (simple-bass-line *my-chords* (list 4 1/2 1/2 (prest 3))) :embed (attr-per-phrase :root *my-roots* 8) :embed (attr-per-phrase :octave (list 3 2) (* 4 2 4))) ; 4 beats per tact, 2 tacts per chord, 4 hard coded chords (next-upto-n (pdef :simple-base-2) 40) ;; (play :simple-base-2) ;; (end :simple-base-2) ;; (stop :simple-base-2) ;; now. I want 8 bars of base, and 8 bars of arp-in-halves ;; then another 8 bars of base and 8 bars of arp-in-thirds (pb :arpegios-one-after-another (pseq (list (pdef :arpegio-in-halves) (pdef :arpegio-in-thirds)) 1)) ;; (play :arpegios-one-after-another) ;; (end :arpegios-one-after-another) ;; (stop :arpegios-one-after-another) (pb :arpegios-and-bass (ppar (list :arpegios-one-after-another (pn (pdef :simple-base-2) 2)))) ;; (play :arpegios-and-bass) ;; (end :arpegios-and-bass) ;; (stop :arpegios-and-bass) ;;; note - and through all of this I'm forgetting about :quant setting ;; that would help align patterns if I start \ end them manually ;; now let's record this and share and get on with the day ;; well, let' copy long recording thingy and just record already ;; https://github.com/byulparan/cl-collider#record-audio-output ;;; write a single channel to disk ;; we can write to buffer number out_buf_num by reading in from the 0 bus ;; (in-package cl-collider) ;; (defsynth disk_writer ((out_buf_num 99)) ;; (disk-out.ar out_buf_num (in.ar 0))) ;; (setf mybuffer (buffer-alloc (expt 2 17))) ;; mybuffer ;; ;; start a disk_writer synth ;; (setf writer_0 (synth 'disk_writer)) ;; ;; make it output to buffer you allocated ;; (ctrl writer_0 :out_buf_num (bufnum mybuffer)) ;; ;; continuously write the buffer contents to a file ;; (buffer-write mybuffer "/tmp/two-arpeggios-and-bass.aiff" :leave-open-p t) ;; ;; now play whatever sounds you like ;; ;; e.g. ;; (in-package :cl-patterns) ;; (play :arpegios-and-bass) ;; (end :arpegios-and-bass) ;; ;; then when you are done ;; (in-package cl-collider) ;; ;; stop the disk_writer synth ;; (free writer_0) ;; ;; close and free the buffer ;; (buffer-close mybuffer) ;; (buffer-free mybuffer) ;; ;; then you can play what you recorded with a utility like mpv: ;; ;; mpv /tmp/foo.aiff