Replies: 29 comments 1 reply
-
Hi! Yes, for
Here's an example of how to use (pb :inner
:instrument :default
:degree (pseries)
:dur 1
:pfindur 4)
(pdef :outer (pmeta :pattern :inner
:stretch (pseq (list 2 1 1/2 1/4) 1)))
(play :outer) In this example, So basically this is similar to doing something like (pb :foo
:embed (pn (pdef :inner))
:dur (p* (pk :dur)
(pr (pseq (list 2 1 1/2 1/4) 1) 4)))
(play :foo) The only problem with the above is that the Hope that makes sense! Also, another source of confusion I'm seeing in the pmeta docstring is that it's still using the previous design of the pattern where you do something like Anyway just let me know if you have any other questions or anything! |
Beta Was this translation helpful? Give feedback.
-
New (next-n (pb :kick :embed (pbjorklund 1 4 :dur 4)) 4)
((EVENT :PDEF :KICK :TYPE :NOTE :DUR 1) (EVENT :PDEF :KICK :TYPE :REST :DUR 1)
(EVENT :PDEF :KICK :TYPE :REST :DUR 1) (EVENT :PDEF :KICK :TYPE :REST :DUR 1)) Now if I want to send it via MIDI:
Notes are played every beat, as |
Beta Was this translation helpful? Give feedback.
-
Thanks for pointing that out! I've fixed that so it should work as expected now, though you will need to specify the note type before embedding the (next-n (pbind :type :midi :embed (pbjorklund 1 4 :dur 4)) 4)
((EVENT :TYPE :MIDI :DUR 1) (EVENT :TYPE :REST :DUR 1)
(EVENT :TYPE :REST :DUR 1) (EVENT :TYPE :REST :DUR 1)) |
Beta Was this translation helpful? Give feedback.
-
As I'm still learning I often find that I want to stop everything, so I'm using: (defun stop-all ()
"Stop all."
(mapc #'stop (all-pdefs))) Of course at first I tried to do what was working in |
Beta Was this translation helpful? Give feedback.
-
I would love to do that, but unfortunately it's not really possible to define a generic function with an optional argument in Common Lisp (at least, not cleanly, as far as I'm aware)... After defining the following: (defmethod foo (&optional (bar t))
(print t)
(print bar)
(defmethod foo (&optional bar)
(print 'none))
(defmethod foo (&optional (bar symbol))
(print 'sym)
(print bar)) You'll get the following behavior: > (foo 'foo)
SYM
FOO
FOO
> (foo 1)
SYM
1
1 (1 bit, #x1, #o1, #b1)
> (foo)
; Debugger entered on #<UNBOUND-VARIABLE SYMBOL {10042EB603}> The last definition of One workaround that was suggested to me when I tried to implement this behavior for (defun beat (&optional object)
(if object
(%beat object)
(slot-value *clock* 'beat)))
(defmethod %beat ((this pstream))
...) ...And then define the methods on I definitely agree with you in principle, though; it would be nice to have |
Beta Was this translation helpful? Give feedback.
-
One more question. I'm trying to use (pb :pattern
:degree (pseq (list (list 0 5 10)
(list 3 10 14)
(list 3 10 14)))
:parp (pbind :dur (pseq (list 1 1/4 1/2 1/4) 1))) This will play every chord four times with provided durations. Can I somehow play individual notes with the provided duration? In general having a musical example in the docscrting would be really nice! BTW, is there anything like Rest? So one could write Thank you for all the explanations! |
Beta Was this translation helpful? Give feedback.
-
Actually, I just remembered that For your parp question, I'm not 100% sure what you mean; do you mean that you want something like "make degree 0 last 1 beat, degree 5 last 1/4 beat, and degree 10 last 1/2 beat" or similar? If that's what you're going for, I think you can do this: (pb :pattern
:degree (pseq (list (list 0 5 10)
(list 3 10 14)
(list 3 10 14))
1)
:sustain (list 1 1/4 1/2)
:dur 2) No If you want the note lengths to "cycle", i.e. if the durations of the notes in the second list in your
then I think you'd want to use (pb :pattern
:degree (pseq (list (list 0 5 10)
(list 3 10 14)
(list 3 10 14))
1)
:sustain (pclump (pseq (list 1 1/4 1/2 1/4)) 3)
:dur 2) There's also (pb :pattern
:degree (pseq (list (list 0 5 10)
(list 3 10 14)
(list 3 10 14 18)) ;; not the same length? no problem!
1)
:sustain (paclump (pseq (list 1 1/4 1/2 1/4)))
:dur 2) Note that (right now at least) you can't have (pb :pattern
:degree (pseq (list (list 0 5 10)
(list 3 10 14)
(list 3 10 14))
1)
:sustain (paclump (pseq (list 1 1/4 1/2 1/4 1/5)))
:dur (pf (reduce #'max (e :sustain)))) Hope that helps! If I'm misunderstanding you just let me know :) Right now rest is semi-implemented, just as a note type (as you've already seen). I was planning on implementing the ability to put (pbind
:midinote (pseq (list 50 52 54 :rest))
:midinote (p+ (pk :midinote) 2))) ;; will cause an error ...But just having a function like your example makes more sense since you can still provide an actual numeric value too (which could probably just default to |
Beta Was this translation helpful? Give feedback.
-
Absolutely!
Nice to hear! I was thinking about arpeggiator as in regular synthesizer: you hold 3 notes let's say
I was trying to do this with And as always, thank you so much for explanations and ideas! More things to try now :) |
Beta Was this translation helpful? Give feedback.
-
Yeah, "arp" is maybe not the best name for that pattern since it doesn't do exactly the same thing as a typical arpeggiator on a synth does. Obviously if you just want to play the notes of a chord in order, you can just use (next-n (pindex (list 0 1 2 3) (pseries -1 -1) t) 10)
;; ;=> (3 2 1 0 3 2 1 0 3 2) ...Though, thinking about it now, For doing the "up-down" behavior, (next-n (pwalk (list 0 1 2 3) 1 (pseq (list 1 -1))) 10)
;; ;=> (0 1 2 3 2 1 0 1 2 3) You might also find There isn't a generalized "classic arpeggiator"-style pattern (yet, at least) since most of those kinds of behaviors are covered by other, more general, patterns in the library already. If you want to switch "arpeggiator modes" at runtime without redefining the pattern, I'm not sure how easy that would be. You could do this to switch between up and down on-the-fly: (defparameter *dir* 1)
(pb :test
:dur 1
:degree (pindex (list 0 3 7) (pseries 0 (pf *dir*)) t)) Then you can just redefine And no problem! Glad you're finding the library useful so far :) |
Beta Was this translation helpful? Give feedback.
-
Is there a way to reevaluate pattern right now (or better on the nearest quant) without waiting for it to finish? UPD: using plain list for arpeggiating is indeed a very simple and effective idea, thanks for pointing in the right direction! :amp (pwrand [0.7 0.2] [4 1])
:degree (let ((chords (flatten-1 (list [[0 7 12] 5 10 15]
[-2 5 10 12]
[0 5 10 15]
[7 10 12 5]))))
(ptrace (pwalk chords (pr (pseq [1 3 5]) (length chords)))))
:dur (p* (pseq (normalized-sum [5 1 1 1])) 2) |
Beta Was this translation helpful? Give feedback.
-
Technically, patterns are supposed to swap out (or (defun reset (pattern)
(mapc #'cl-patterns::clock-remove (pattern-tasks pattern))
(play pattern)) Then you can just wrap your patterns in (reset
(pb :test
:dur 1
:degree (pn (pseries 0 1 8)))) That will automatically stop and restart the pattern whenever it's redefined. Not as musical as if it would do that on the You'll also need to be on the latest master for that Glad that arpeggiating with that method does what you were looking for! :) Also, I tried writing |
Beta Was this translation helpful? Give feedback.
-
No stress! (defparameter *midi-input-channel* (1- 1))
(defparameter *midi-chord* nil)
(defparameter *midi-on-notes* nil)
(defun midi-map (messages)
(dolist (message messages)
(let* ((event-type (getf message :event-type))
(event-data (getf message :event-data))
(source (car (getf message :source)))
(destination (car (getf message :dest))))
(declare (ignorable source destination))
;; replace `nil' with `t' to see debug output
(format nil "~a: ~s~%"
(case event-type
(:snd_seq_event_noteon (let ((midinote (getf event-data 'cl-alsaseq:note))
(channel (getf event-data 'cl-alsaseq:channel)))
(when (= channel *midi-input-channel*)
(when (emptyp *midi-on-notes*)
(setf *midi-chord* nil))
(pushnew midinote *midi-chord*)
(pushnew midinote *midi-on-notes*))
"Note on"))
(:snd_seq_event_noteoff (let ((midinote (getf event-data 'cl-alsaseq:note))
(channel (getf event-data 'cl-alsaseq:channel)))
(when (= channel *midi-input-channel*)
(setf *midi-on-notes* (delete midinote *midi-on-notes*)))
"Note off"))
(:snd_seq_event_controller "CC")
(t event-type))
event-data))))
(midihelper:stop-midihelper)
(midihelper:start-midihelper :master 96 'midi-map) It works similar to chord learn functionality on my hardware sequencer: Finally you can use the following small function in (defun my-insert-chord ()
"Insert `*midi-chord*' at point"
(interactive)
(insert (prin1-to-string (sly-eval 'cl-patterns::*midi-chord*)))) |
Beta Was this translation helpful? Give feedback.
-
Oh, I see! I don't have a Pyramid myself but it sounds like it's basically like the "latch" functionality that some synthesizers have which keeps track of which keys you've pressed together and holds them until you release them all and start pressing another "chord". I'd definitely like to have some kind of functionality like that in the library, but I'd prefer to keep backend-specific functionality to a minimum if possible. Ideally in the future "latching" like that could be implemented as a pattern type and MIDI (or other data stream types) could just be converted to a cl-patterns Unfortunately right now cl-patterns only supports Your elisp function also reminds me that I should cleanup/package my own cl-collider/cl-patterns elisp convenience functions and put them in this repo or something, since I have a few that are handy when working with these libraries. For example: (defun get-current-paragraph (&optional between-newlines) ;; helper function
"Gets the buffer positions of the start & end of the current paragraph or defun, or returns the region if it is active.
With between-newlines true, always return buffer positions of the previous and next double-newlines."
(save-excursion
(if (region-active-p)
(cons (region-beginning) (region-end))
(or (and (not between-newlines) (bounds-of-thing-at-point 'defun))
(let* ((start-loc (save-excursion (search-backward (string ?\n ?\n) nil t)))
(end-loc (search-forward (string ?\n ?\n) nil t))
(start (if start-loc
(+ start-loc 2)
(point-min)))
(end (if end-loc
(- end-loc 2)
(point-max))))
(cons start end))))))
(defun sly-cl-collider-context ()
"Get the type (i.e., pdef, ndef, defsynth, proxy, etc) and name of the current context."
(let* ((bounds (or (bounds-of-thing-at-point 'defun)
(get-current-paragraph)))
(def-string (buffer-substring-no-properties (car bounds) (cdr bounds)))
(type))
(save-excursion
(save-restriction
(narrow-to-region (car bounds) (cdr bounds))
(goto-char (point-min))
(search-forward-regexp "\(\\(\\b\\w+\\b\\)" nil t)
(setf type (match-string-no-properties 1))
(goto-char (point-min))
(search-forward-regexp "\[\(:\]\\(pdef\\|pb\\|defsynth\\|ds\\|proxy\\|ndef\\|dn\\|bdef\\)\[ \t\n\]+:\\(\[^ \t\n\]+\\)" nil t)
(list (ignore-errors (intern type)) (match-string-no-properties 2))))))
(defun sly-cl-collider-get-name-of-previous-item (items)
(save-excursion
(let ((context (sly-cl-collider-context)))
(beginning-of-defun)
(while (and (not (member (car context) items))
(not (= (point) (point-min))))
(backward-sexp)
(setf context (sly-cl-collider-context)))
(cadr context))))
(defun cl-collider-guess-synth ()
(sly-cl-collider-get-name-of-previous-item (list 'defsynth 'ds 'proxy 'ndef 'dn)))
(defun sly-cl-collider-select-synth ()
"Select a synth from the list of currently-defined synthdefs."
(interactive)
(let ((synths (sly-eval `(cl:mapcar (cl:lambda (x) (cl:string-downcase (cl:symbol-name x)))
(cl-patterns::keys cl-collider::*synthdef-metadata*))))
(guess (sly-cl-collider-guess-synth)))
(completing-read "Synth? " synths nil nil (when (member guess synths) guess))))
(defun sly-play-context (&optional stop)
"Play or end the pdef, synthdef, bdef, etc, that the point resides within."
(interactive)
(cl-destructuring-bind (type name) (sly-cl-collider-context)
(save-excursion
(save-restriction
(narrow-to-region (save-excursion (beginning-of-defun) (point)) (save-excursion (end-of-defun) (point)))
(cond
((member type (list 'pdef 'pb 'pbind))
(let ((string (concat "(cl-patterns::" (if stop "play-or-stop" "play-or-end") " :" name ")")))
(sly-interactive-eval string)))
((member type (list 'ds 'defsynth 'defsynth*))
(sly-interactive-eval
(concat "(play (event :instrument :" name " :dur 2 :amp 1 :latency 0 :quant 0))")))
((member type (list 'bdef))
(let ((string (concat "(play (bdef :" name "))")))
(sly-interactive-eval string)))))))) With the above code in the sly section of my init.el, I bind I haven't tested that that code will work though; they might depend on some other helper functions I forgot to include or something. If you decide to try them out, let me know if you get any errors or anything. I've been going through cl-patterns a bit, working on updating the |
Beta Was this translation helpful? Give feedback.
-
Hi, I just pushed a few updates to the library including the changes to the clock/quant handling that I mentioned in my last reply. It ended up being a big restructuring of the clock so there might be some bugs yet (though I've of course tested the changes a bit, and added a few tests as well). See commit ed10b6f for the clock/quant changes. If you try it out I'd be happy to know if you find any bugs! |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
I'm not sure if there is an established convention for what column to wrap docstrings/code at in the CL community, but for me I thought this was because of In any case I prefer not to manually split up the paragraphs in docstrings into separate lines since I feel like that's something that should be handled by the program displaying them. Also because I'm working on a cl-patterns-based DAW which will display the docstrings and depending on the size of the window/pane they might end up looking weird, i.e. if the pane is smaller than the length of a line, or if the docstrings are displayed in a non-monospaced font. |
Beta Was this translation helpful? Give feedback.
-
My first impression from the new |
Beta Was this translation helpful? Give feedback.
-
It seems there is something weird happening with the combination of (progn
(pb :foo
:instrument :default
:loop-p nil
:quant 1
:dur 1
:midinote (pseq [60] 4))
(play :foo)) This pattern plays only 1 event, but I was expecting 4. |
Beta Was this translation helpful? Give feedback.
-
Since |
Beta Was this translation helpful? Give feedback.
-
As of de8a449, |
Beta Was this translation helpful? Give feedback.
-
Two naming questions: (pb :automatic-jazz
:type :midi
:chan (1- 3)
:note (pshuf (scale-notes :minor) 4)
:octave (pr (pwhite 2 7))
:root (pr (pwhite 0 12))
:dur (pshuf (list 1/3 1/4))) And then really not important, but still what do you think about |
Beta Was this translation helpful? Give feedback.
-
Oh, sorry, I forgot to reply to this. Personally I think I prefer |
Beta Was this translation helpful? Give feedback.
-
As of commit 889e737 the |
Beta Was this translation helpful? Give feedback.
-
I find myself doing this all the time: (progn
(pb :progression
:backend :alsa-midi
:quant 1
:chan (1- 9)
:scale :chromatic
:dur (pseq (cl-patterns::normalized-sum [4 2 1]))
:db (pseq [-3 -5 -8 -10])
:degree (pwrand [(pseq [(prand [0 -12]) (prand [7 (prest)] 1) (prand [9 10] 1)] 1) (prest)] [3 1])
:degree (p+ (pk :degree) (pseq [0 -12 -5 0 7])))
(play :progression)) Perhaps another special key can be added for this specific forkflow: (pb :progression
:backend :alsa-midi
:quant 1
:state :play
;; :state :stop
;; :state :pause ; Maybe?
:chan (1- 9)
:scale :chromatic
:dur (pseq (cl-patterns::normalized-sum [4 2 1]))
:db (pseq [-3 -5 -8 -10])
:degree (pwrand [(pseq [(prand [0 -12]) (prand [7 (prest)] 1) (prand [9 10] 1)] 1) (prest)] [3 1])
:degree (p+ (pk :degree) (pseq [0 -12 -5 0 7]))) Key itself and possible values are for illustrative purposes. |
Beta Was this translation helpful? Give feedback.
-
One problem with the idea of such a key is that not all patterns are named; i.e. if I do It's easier for I'll think about this more though; maybe there is some way to make a key along those lines work for all patterns somehow. I think if I did implement it, I'd probably call the key On a similar note, I just pushed a few commits, the most recent of which includes an Emacs "helper library" of functions I wrote to make playing/stopping patterns easier, among other things. See this section that I just added to the readme. If you put that setup code in your |
Beta Was this translation helpful? Give feedback.
-
Nice! Will give it a try. It seems this is doing what I need: (play (pdef :foo
(pbind
:quant 1
:degree (ptrace (pseries 0 1)))))
;; replace play with stop
(stop (pdef :foo
(pbind
:quant 1
:degree (ptrace (pseries 0 1))))) But this doesn't work: ;; Plays only one one event
(play (pb :foo
:quant 1
:degree (ptrace (pseries 0 1)))) |
Beta Was this translation helpful? Give feedback.
-
Ohh, that is confusing. Turns out it's because |
Beta Was this translation helpful? Give feedback.
-
OK, I ended up just making |
Beta Was this translation helpful? Give feedback.
-
What do you think about closing this thread and using Github Discussions instead? |
Beta Was this translation helpful? Give feedback.
-
Hello!
I have several questions which aren't big enough for their own issues, so I decided to put them here together.
How to stretch
pbjorklund
?Docs says
I came up with this:
Is there a simpler way do that?
How to use
pmeta
For example there is a
:stretch
pmeta key, but I can't wrap my head around it.Could you please give another example of
pmeta
usage?Thank you!
Beta Was this translation helpful? Give feedback.
All reactions