177 lines
6.3 KiB
Common Lisp
177 lines
6.3 KiB
Common Lisp
(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
|