This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Forum Thread #14
Comments
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! |
note that STEPS is now a required argument, and the OFFSET, DUR, and REPEATS arguments are keyword arguments
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 |
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)) |
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 |
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 |
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! |
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 |
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 :) |
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 :) |
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) |
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 |
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*)))) |
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 |
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! |
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. |
My first impression from the new |
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. |
Since |
As of de8a449, |
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 |
Oh, sorry, I forgot to reply to this. Personally I think I prefer |
As of commit 889e737 the |
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. |
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 |
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)))) |
Ohh, that is confusing. Turns out it's because |
OK, I ended up just making |
What do you think about closing this thread and using Github Discussions instead? |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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!
The text was updated successfully, but these errors were encountered: