cl-patterns-study/2022-09-08-more-on-chord-me...

534 lines
20 KiB
Common Lisp
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(in-package :cl-patterns)
(pdef :maybe-arpegio
(parp (pbind :chord (pseq (list :minor-triad :major) 2))
(pbind :note (pseq (pnary #'nchord (pk :chord)) 2))))
;; (play :maybe-arpegio)
;; (end :maybe-arpegio)
;; (stop :maybe-arpegio)
(next-upto-n (pdef :maybe-arpegio) 10)
;; now about including patterns one into another, can I reference this pattern
;; in another which would specify an instrument?
;; judging by https://github.com/defaultxr/cl-patterns/blob/master/doc/patterns.org
;; maybe #'PSYM or #'PPAR would help?
(pb :legato-keys
:legato 1
:instrument :fmrhodes1)
(pdef :maybe-joined-patterns (psym (ppar (list :maybe-arpegio :legato-keys))))
;; (play :maybe-joined-patterns)
;; (end :maybe-joined-patterns)
;; (stop :maybe-joined-patterns)
;; nope, they just play in parallel, :legato-keys plays single note,
;; how would I merge them into a single pattern \ stream?
;;; maybe #'IPSTREAM ?
(pdef :attempt-2-at-joining (ipstream (list (pdef :maybe-arpegio) (pdef :legato-keys))))
;; (play :attempt-2-at-joining)
;; (stop :attempt-2-at-joining)
;; nope.
;; (and I actually can use #NEXT-UPTO-N to introspect
(next-upto-n (pdef :maybe-joined-patterns) 10)
(next-upto-n (pdef :attempt-2-at-joining) 10) ; also has EVENT :TYPE :REST inserted
;;; what about #'PMETA ?
;; nope
;;; let's check how #'PBIND works, it might just be able to extend existing patterns
;; looking at #'PBIND code I saw ":embed" key and went searching through the file
;; and found this:
;;
;;; pchain
;; :documentation Combine multiple patterns into one event stream.
;; (next-n (pchain (pbind :foo (pseq '(1 2 3))) (pbind :bar (pseq '(7 8 9) 1))) 4)
;;
;; ;=> ((EVENT :FOO 1 :BAR 7) (EVENT :FOO 2 :BAR 8) (EVENT :FOO 3 :BAR 9) NIL)
;; "see also pbind :embed key"
;;
;; So I don't know what :embed does, but pchain seems to be what I need, right?
;;
;; from the "patterns.org" : pchain - Chain patterns together by using the first source patterns output as the input to the second, etc.
;; didn't seem like the thing
;;; let's try it
(pdef :attempt-3
(pchain (pdef :legato-keys) (pdef :maybe-arpegio)))
(next-upto-n (pdef :attempt-3) 10)
;; (play :attempt-3)
;; (end :attempt-3)
;; (stop :attempt-3)
;; that's it!
;; now let's try :embed ?
(pb :attempt-4
:instrument :fmrhodes1
:legato 1
:embed :maybe-arpegio)
;; (play :attempt-4)
;; (end :attempt-4)
;; this also works, yay!
;; now, can I have several :embed arguments?
(pbind :dur 2) ; so #'PBIND "binds keys to patterns"
(pdef :with-lengths
(pbind :dur 2)) ; #'PDEF gives this name
; and takes in patterns, so can take in "pattern-constructors"
(pb :with-lengths-2
:dur 2) ; is a shorthand
; where we can use "pattern-combiners" for each key anyway
(pb :attempt-5
:instrument :fmrhodes1
:legato 1
:embed :with-lengths-2
:embed :maybe-arpegio)
;; (play :attempt-5)
;; (end :attempt-5)
;; (stop :attempt-5)
;; this also works, yay!
;; now, can I have several :embed arguments?
(next-upto-n (pdef :attempt-5) 10)
;; for some reason :dur shows up in a stream printed form, but same thing plays
;; maybe I need :embed in the beginning, so that I would overwrite it?
;; nope. I just had (play :attempt-4), so whops, more than a reason to get emacs support package
;;; It works!
;; yay.
;;; so, next? is trying to set attributes like :legato or :octave
;; per tact \ phrase regardless of how many events are there
;; maybe #'PMETA , maybe with :stepinject
;; nah?
;;
;; or maybe still arpeggio?
;; it would play first pattern until the end, but would also want the pattern of the repeating attribute to end
;; right?
;; I guess maybe as there are limits for "amount of events from stream"
;; there could be limits for "amount of clock ticks / beats"?
;; yup, pfindur
(pdef :with-with-beat-limiting (pfindur (pbind :eitght-beat-limiting t) 8))
(pb :test-limiting
:embed :with-with-beat-limiting
:dur 1/4
:degree (pwhite 0 7))
(next-upto-n (pdef :test-limiting) 30)
;; nah, what do I really want?
;; let's take the arpeggio as it is,
;; but then to 2 tacts of 1 beat per event,
;; and then 2 tacks of 1/4
(pb :try-arpeggio-with-durs
:embed :maybe-arpegio
)
;; (play :try-arpeggio-with-durs)
;; (end :try-arpeggio-with-durs)
;; (stop :try-arpeggio-with-durs)
(pbind
:dur (pseq (list 1 1/4)))
;; now that maybe done with arpeggio?
;; (next-upto-n (pdef :try-per-tact-with-parp) 20)
(next-upto-n (parp
(pbind :dur (pseq (list 1 1/2)))
(pfindur (pr (pk :dur)) 4)) 30)
;; oh, wow, yea
(pdef :durations-per-tact
(parp
(pbind :value (pseq (list 1 1/2)))
(pfindur (pr (pk :value)) 4)))
(next-upto-n (pdef :durations-per-tact) 30)
(pb :try-arpeggio-with-durations-per-tact
:instrument :fmrhodes1
:embed :maybe-arpegio
:dur (pdef :durations-per-tact)
;; :embed :durations-per-tact
)
(next-upto-n (pdef :try-arpeggio-with-durations-per-tact) 40)
;; (play :try-arpeggio-with-durations-per-tact)
;; (stop :try-arpeggio-with-durations-per-tact)
;; (end :try-arpeggio-with-durations-per-tact)
;; so, this is almost what I want?
;; with durations I'd likely want either something different, or make sure
;; that phrases can be sped up to the multiple
;;
;; but that's exactly what I want for legato?
(pb :try-arpeggio-with-legato-per-tact
:instrument :fmrhodes1
:embed :maybe-arpegio
:legato (pdef :durations-per-tact)
;; :embed :legato-per-tact
)
(next-upto-n (pdef :try-arpeggio-with-legato-per-tact) 40)
;; (play :try-arpeggio-with-legato-per-tact)
;; (stop :try-arpeggio-with-legato-per-tact)
;; (end :try-arpeggio-with-legato-per-tact)
;; oh no that's not it!
;; :durations-per-tact don't actually look at :dur of the final stream
;; but produce static
;; (1 1 1 1 1/2 1/2 1/2 1/2 1/2 1/2 1/2 1/2 1 1 1 1 1/2 1/2 1/2 1/2 1/2 1/2 1/2
;; 1/2 1 1 1 1 1/2 1/2)
;; stream. whlp.
;; so, what if that would be for any other type of attribute, not for duration
;; then I could use pbind :dur (pk :dur) I guess,
;; and then pfindur would use duration of the embedding stream?
(pdef :try-keep-attribute-per-tact
(parp
(pbind :value (pseq (list 1 1/2)))
(pbind :dur (pfindur (pr (pk :dur)) 4))))
(next-upto-n (pdef :try-keep-attribute-per-tact) 30)
;; i think I just need to (pfindur (pr ...) 4)
;; but that's for single value
;; so if I'm keeping it too simple for stupid first step:
(pdef :abc-per-tact
(pseq (list
(pfindur (pbind :attr "a" :dur (pk :dur)) 4)
(pfindur (pbind :attr "b" :dur (pk :dur)) 4)
(pfindur (pbind :attr "c" :dur (pk :dur)) 4))
))
(next-upto-n (pdef :abc-per-tact) 30)
;; so right now this has default :dur of 1,
;; but if I :embed this into a stream with variable
(pb :trying-abc-with-different-durations
:dur 1/2
:embed :abc-per-tact)
(next-upto-n (pdef :trying-abc-with-different-durations) 30)
;; yep, that seems to work
;; now I want to not hardcode :attr, and possibly not hardcode 4
;; I don't know whether I can use actual :dur
(pb :trying-abc-with-different-durations-2
:dur (pseq (list (pfindur 1 4) (pfindur 1/2 4)))
:embed :abc-per-tact
)
(next-upto-n (pdef :trying-abc-with-different-durations-2) 30)
;; yep, that seems to work too
;; so I could probably define that :attr-per-duration with a marco
;; but is there a way to defite it with a fucntion?
;;; also, let's make our arpeggio aligned by 2 tacts in 4 measure, so 8 beats?
;; there was something that aligns with padding pauses in the end?
(pdef :maybe-arpegio-padded
(parp (pbind :chord (pseq (list :minor-triad :major) 2))
;; (pbind :note (psync (pseq (pnary #'nchord (pk :chord)) 2) 8))
(psync (pbind :note (pseq (pnary #'nchord (pk :chord)) 2)) 4)))
;; (play :maybe-arpegio-padded)
;; (end :maybe-arpegio-padded)
;; (stop :maybe-arpegio-padded)
(next-upto-n (pdef :maybe-arpegio-padded) 30)
(next-upto-n (psync (pseq (list 1 2 3) 6) 8) 30)
;; maybe that doesn't work because #'PSYNC need to be in context of bound stream, with :dur present?
;; that's possible
(next-upto-n (psync (pbind
:attr (pseq (list 1 2 3) 6)
:dur 1) 8) 30)
;; yup, thats it
;; so. ugh. if I have arpeggio.
;; then how do I add padding? possibly around the whole thing?
;; but then I'm loosing information about each particular chord
;;; well, now I have arpegios psync'ed to 8 beats \ 2 tacts
;; so I could try to do per tact legato
(pdef :legato-per-tact
(pseq (list
(pfindur (pbind :legato 1 :dur (pk :dur)) 8)
(pfindur (pbind :legato 0.7 :dur (pk :dur)) 8)
(pfindur (pbind :legato 0.2 :dur (pk :dur)) 8))
))
(next-upto-n (pdef :legato-per-tact) 30)
(pb :arpegio-synced-and-per-tact-legato
:embed :maybe-arpegio-padded
:embed :legato-per-tact)
;; (play :arpegio-synced-and-per-tact-legato)
;; (stop :arpegio-synced-and-per-tact-legato)
(next-upto-n (pdef :arpegio-synced-and-per-tact-legato) 30)
;;; that is kind of a solution
;; and likely mapping patterns is a thing, so I could have
;; now, let's try overwriting duration?
;;; well, current padding is still "outside" of pseq that is limited to 2 repetitions.
;; so, I'd want psync somehow inside of pseq?
;; arpegiator is like this
(next-upto-n (pseq (list 0 1 2 3) 2) 30)
;; but I want it to be 0 1 2 3 .... repeated as many times as needed to be inside of 8 beats, and padded with pauses
(next-upto-n (psync (pbind :degree (pseq (list 0 1 2 3))) 8 8 ) 30)
;; nope.
;; well, i guess maybe it's here where I'd need pmeta?
(pdef :maybe-arpegio-padded-2
(parp (pbind :chord (pseq (list :minor-triad :major) 2))
;; (pbind :note (psync (pseq (pnary #'nchord (pk :chord)) 2) 8))
(psync (pbind :note (pseq (pnary #'nchord (pk :chord)))) 8 8)))
;; (play :maybe-arpegio-padded-2)
;; (end :maybe-arpegio-padded-2)
;; (stop :maybe-arpegio-padded-2)
(next-upto-n (pdef :maybe-arpegio-padded-2) 30)
;; or maybe I need to make pseq repeat 1 time
;; and then wrap that in repeat, and then do psync over that?
(next-upto-n (psync (pn (pbind :degree (pseq (list 0 1 2) 1))) 8 8 ) 30)
;; pr - repeats each event - so 0 1 2 repeated once
;; pn - loops source pattern - so 0 1 2 0 1 2 0 1 | psynced here
;; because psync still considers per single event
;;; so the question is:
;; how can I write a pseq and have it repeating for duration,
;; taking into account durations of notes that can be set in the outer stream
;; so that repeat would end on a final note and padded rest with pause, not breaking last loop?
;;; well, while I don't know the answer, let's work with "per phrase legato"
;; just have 'breaking last loop iteration' repeat
;; and play around with durations
(pb :lets-vary-durations-in-arpegio-with-phrase-legato
:instrument :fmrhodes1
:octave 3
:embed :maybe-arpegio-padded-2
:embed :legato-per-tact
:dur 1/3
)
;; (play :lets-vary-durations-in-arpegio-with-phrase-legato)
;; (end :lets-vary-durations-in-arpegio-with-phrase-legato)
;; (stop :lets-vary-durations-in-arpegio-with-phrase-legato)
;; during the playthrough setting :dur to different 1 1/2 1/3 1/6, that's nice
;;; now let's copy things over so that all components are close together:
;; exhibit A
(defun not-perfect-but-arpeggio (chords &optional (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)))) phrase-dur phrase-dur)))
(pdef :maybe-arpegio-padded-3 (not-perfect-but-arpeggio (list :major :minor)))
;; (play :maybe-arpegio-padded-3)
;; (end :maybe-arpegio-padded-3)
;; (stop :maybe-arpegio-padded-3)
;; (next-upto-n (pdef :maybe-arpegio-padded-3))
;; (next-upto-n (pdef (not-perfect-but-arpeggio (list :major :minor))) 30)
;; (next-upto-n (not-perfect-but-arpeggio (list :major :minor)) 30)
;; (next-upto-n (not-perfect-but-arpeggio (list :major :minor :minor-6th)) 30)
;; so to have patterns with arguments
;; I just need to return the pattern - pbind or another constructor from the function
;; and then bind it to the name with pdef, or just pass in to #NEXT-UPTO-N or to key in #'PB (likely, right?)
;; can I do same thing with legato per phrase?
(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)))
(attr-per-phrase :legato (list 1 0.7 0.2))
;; #<PSEQ (#<PFINDUR (PBIND :LEGATO 1 :DUR #<PK :DUR 1>) 8 0>
;; #<PFINDUR (PBIND :LEGATO 0.7 :DUR #<PK :DUR 1>) 8 0>
;; #<PFINDUR (PBIND :LEGATO 0.2 :DUR #<PK :DUR 1>) 8 0>) :INF 0>
;; wowy, is that what I think it is?
(pb :lets-have-arpeggio-with-phrase-settings-and-vary-other-things-manually
:embed (not-perfect-but-arpeggio (list :major :minor :minor-6th :minor-7th :major-7th :major)) ; each chord plays arpeggio for whole phrase
:embed (attr-per-phrase :legato (list 1 0.7 0.2 0.8)) ; each phrase takes single value for legato
; even if amount of events in phrase change with change of :dur
:dur 1/3
)
;; (play :lets-have-arpeggio-with-phrase-settings-and-vary-other-things-manually)
;; (end :lets-have-arpeggio-with-phrase-settings-and-vary-other-things-manually)
;; (stop :lets-have-arpeggio-with-phrase-settings-and-vary-other-things-manually)
;; and now I'm limited by my understanding of chords and how to select progressions
;; also - the base for the chord! currently we only select root
;; it's maybe not as clean as have root coupled with the chord, but could add attr-per-phrase with root seleciton, that could work?
;; but! the root is in "note" mode or "degree" mode?
(pb :is-root-degree-or-note
:degree 0
:root (pseq (list 0 1 2 3 4 5 6 7) 1))
;; (play :is-root-degree-or-note)
;; (end :is-root-degree-or-note)
;; definitely degree, right?
;; so let's do arpegios for some of the chord progressions I had as whole chords?
;; i - III - iv - VI
(pb :lets-arpegio-i-III-iv-VI
:embed (not-perfect-but-arpeggio (list :minor :major :minor :major)) ; each chord plays arpeggio for whole phrase
:embed (attr-per-phrase :legato (list 1 0.7 0.2 0.8)) ; 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 0 2 3 5))
:dur 1/4
)
;; (play :lets-arpegio-i-III-iv-VI)
;; (end :lets-arpegio-i-III-iv-VI)
;; (stop :lets-arpegio-i-III-iv-VI)
;; IV V iii vi
(pb :lets-arpegio-IV-V-iii-vi
:embed (not-perfect-but-arpeggio (list :major :major :minor :minor)) ; each chord plays arpeggio for whole phrase
:embed (attr-per-phrase :legato (list 1 0.7 0.2 0.8)) ; 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 2 5))
:dur 1
)
;; (play :lets-arpegio-IV-V-iii-vi)
;; (end :lets-arpegio-IV-V-iii-vi)
;; (stop :lets-arpegio-IV-V-iii-vi)
;;; now, if I want a simple pase line?
;;; like taking a chord and holding it, or playing whole with simle pattern?
(next-upto-n (pdef :lets-arpegio-iv-v-iii-vi) 30)
;; well, #'not-perfect-but-arpeggio does inject :chord as an attribute
;; but if I don't merge this with base line, and only #'PPAR, then base pattern \ stream wouldn't have it
;; so, I guess I'd want to have "generic base line" that takes in a list of chords
;; and pass in same list (or maybe even a different list sometimes)
;; and well, I'd also need to pass in roots for those chords, since right now they are separate, oh well
(setq *my-chords* (list :major :major :minor :minor))
;; (setq *my-chords* (list :major :minor))
;; (setq *my-bases* (list 3 4))
(setq *my-bases* (list 3 4 2 5))
;; let's have a phrase that plays chord over base for 4 beats - 2 tacts
;; still would need parp, right?
(pb :simple-base-line
(parp
(pbind
:chord (pseq *my-chords* 2)
:root (pseq *my-bases* 2)
)
(pbind
:note (pnary #'chord-notes (pk :chord))
:dur (pseq (list 4 1/2 1/2 (prest 3)) 1)) ; that's 2 tacts
))
(pb :simple-base
:instrument :fmbass
:embed :simple-base-line
:embed (attr-per-phrase :octave (list 3 2) (* 4 2 4))) ; 4 beats per tact, 2 tacts per chord, 4 hard coded chords
;; (play :simple-base)
;; (end :simple-base)
;; (stop :simple-base)
;; other simpler way would be to make parp - with simple-base-line playing for each :octave
(next-upto-n (pdef :simple-base) 60)
;;; and yes this could also be more configurable, let's just try to combine these first
;; IV V iii vi - same 3 4 2 5 from :simple-base
(pdef :try-one-arpeggio-and-bass
(ppar (list (pdef :simple-base) (pdef :lets-arpegio-IV-V-iii-vi))))
;; (play :try-one-arpeggio-and-bass)
;; (stop :try-one-arpeggio-and-bass)
;; (end :try-one-arpeggio-and-bass)
;;; wait, so my arpeggio doesn't keep whole 2 tacts of same chord, oh, sad
;; IV V iii vi
(pb :lets-arpegio-IV-V-iii-vi
;; :instrument :strings
:embed (not-perfect-but-arpeggio (list :major :major :minor :minor) 1/4) ; each chord plays arpeggio for whole phrase
:embed (attr-per-phrase :legato (list 1 0.7 0.2 0.8)) ; 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 2 5))
:dur 1/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)
;; yup, :dur 1 and :dur 1/2 still produce 8 notes for the :chord
;; exhibit B
(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) ; looks like parent :dur here inaccessible
:dur dur
)
phrase-dur phrase-dur)))
;; and now it's worse, since need to specify durations twice
;; but should align with simple bass
;; same as before
;; IV V iii vi - same 3 4 2 5 from :simple-base
(pdef :try-one-arpeggio-and-bass
(ppar (list (pdef :simple-base) (pdef :lets-arpegio-IV-V-iii-vi))))
;; (play :try-one-arpeggio-and-bass)
;; (stop :try-one-arpeggio-and-bass)
;; (end :try-one-arpeggio-and-bass)
;;; let's try updating and rendering again?
;; (render (pdef :try-one-arpeggio-and-bass) "/tmp/arpeggio-and-bass-1.wav" :dur 16)
;; 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/arpeggio-and-bass.aiff" :leave-open-p t)
;; ;; now play whatever sounds you like
;; ;; e.g.
;; (in-package :cl-patterns)
;; (play :try-one-arpeggio-and-bass)
;; (stop :try-one-arpeggio-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