(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 pattern’s 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)) ;; #) 8 0> ;; #) 8 0> ;; #) 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