From 788c27c54bfabf3b28100db2326d784b647405bb Mon Sep 17 00:00:00 2001 From: efim Date: Thu, 8 Sep 2022 15:28:08 +0000 Subject: [PATCH] more arpeggio, per-phrase-attr --- 2022-09-08-more-on-chord-melodies.lisp | 533 +++++++++++++++++++++++++ programming-music-journal.org | 76 +++- 2 files changed, 608 insertions(+), 1 deletion(-) create mode 100644 2022-09-08-more-on-chord-melodies.lisp diff --git a/2022-09-08-more-on-chord-melodies.lisp b/2022-09-08-more-on-chord-melodies.lisp new file mode 100644 index 0000000..df2a853 --- /dev/null +++ b/2022-09-08-more-on-chord-melodies.lisp @@ -0,0 +1,533 @@ + +(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 diff --git a/programming-music-journal.org b/programming-music-journal.org index 1275c04..bb5781a 100644 --- a/programming-music-journal.org +++ b/programming-music-journal.org @@ -322,7 +322,7 @@ or something what I want is "pitch model" http://doc.sccode.org/Tutorials/Streams-Patterns-Events5.html :degree - is from an octave, and :octave 5 default and quite high, :root is which octave to take -** NEXT - how to keep same note pattern repeating with different settings of legato \ duration +** DONE - how to keep same note pattern repeating with different settings of legato \ duration I'll try to define notes separately with pdef and insert it? would then single length would be for all? ** so, I tried to figure out how to change parameters for whole "phrases" "automatic jazz" does that with getting new random value, that gets repeated "forever" until the end of the pattern @@ -331,3 +331,77 @@ if I try to use not random, but "from sequence", then on pattern restart I'd get so then "kind of working" solution, is keep melogy repeating, then - know what is the length of the melody and repeat for "amount of events" in the melody, +* [2022-09-08 Thu] +** one day I'll move all instruments into easy to evaluate \ import \ use package +but not today? because I don't yet too comfortable with the concept of Common-Lisp packages +** and today? +*** TODO get more code on creating arpeggios by specifying chords +not quite, but yeah +*** DONE and maybe how to combine \ merge \ include patterns +so that chord pattern is defined separately and merged into separate patterns for base \ melogy + +one convenient way to do it is to use :embed key in the pdef +#+begin_src common-lisp +(pb :with-lengths-2 + :dur 2) +(pb :attempt-5 + :instrument :fmrhodes1 + :legato 1 + :embed :with-lengths-2) +(next-upto-n (pdef :attempt-5) 10) +#+end_src + +and there can be several :embed's yay + +another pay is #'PCHAIN +judging by documentation it's very similar, not sure when it would be more convenient +#+begin_src common-lisp +(pdef :attempt-3 + (pchain (pdef :legato-keys) (pdef :maybe-arpegio))) +(next-upto-n (pdef :attempt-3) 10) +#+end_src + +*** DONE also - maybe I'd learn about setting parameters "per tact" and not "per event" +- I already guess that it would be infinitely repeating pattern that somehow aligns by beat number, maybe? + then we'd get emitting for example :legato for each event in a tact regardless of it being 1 or 1/16s + +I think something similar I saw at #'PMETA, like connecting patterns with some kind of measuring + +kind of have some kind of solution here +** so what was going on today: +*** :embed - in pb +if I have separate stream \ patter with some of the values +I can "include" it into currently defined stream. +so for example notes and durations are saved under :melody-pattern + +and other patterns embed it and specify own :instrument and :octave +[[file:2022-09-08-more-on-chord-melodies.lisp::(pb :attempt-5]] +*** trying "attribute per tact" - with PARP - arpegio +**** starting with durations per tact: +[[file:2022-09-08-more-on-chord-melodies.lisp::(pdef :durations-per-tact]] + +not quite + +**** then attribute per tact: +[[file:2022-09-08-more-on-chord-melodies.lisp::(pdef :abc-per-tact]] +**** the simplest hardcoded version +and with :legato +[[file:2022-09-08-more-on-chord-melodies.lisp::(pdef :legato-per-tact]] + +**** to probably what I want? +[[file:2022-09-08-more-on-chord-melodies.lisp::defun attr-per-phrase (attr-name values &optional (phrase-dur 8)]] +*** also wanted to "pad" arpeggio, so that it would fill full two tacts, but only have complete loop iterations +in that I didn't succeed +having PSYNC around PSEQ - inner pattern started last incomplete iteration instead of producing rest +**** initial implementation also didn't fill two tacts +[[file:2022-09-08-more-on-chord-melodies.lisp::defun not-perfect-but-arpeggio (chords &optional (phrase-dur 8)]] +in it the #'PSYNC didn't really work because it was around pattern that didn't have :dur +**** next iteration that starts unfinished last loops +[[file:2022-09-08-more-on-chord-melodies.lisp::defun not-perfect-but-arpeggio (chords &optional (dur 1) (phrase-dur 8)]] + +but I have to pass in :dur, +so in pattern I currently have to change it in two places. +seems that "parent" pattern is inaccessible with (pk :dur) + +- note: I somewhere read phrase "parent pattern", so possibly in some #'PFUNC or #'PNARY + maybe I could access attributes of a pattern that :embed this one