byulparan / cl-collider

A SuperCollider client for CommonLisp
Other
218 stars 23 forks source link

NamedControl #94

Closed TatriX closed 3 years ago

TatriX commented 3 years ago

Hi! Is there something similar to NamedControl in cl-collider?

ntrocado commented 3 years ago

I think you can get the same basic functionality with the with-controls macro:

SC-USER> (proxy :sine-synth (with-controls ((freq 440 :lag 2)) (sin-osc.ar freq 0 0.2)))
=> #<CL-COLLIDER::NODE :server #<CL-COLLIDER::EXTERNAL-SERVER localhost-127.0.0.1:4444> :id 1003 :name "sine-synth">

SC-USER> (ctrl :sine-synth :freq 880)
=> :SINE-SYNTH
ntrocado commented 3 years ago

A limitation is that we can't have duplicate control names on the same node, while NamedControl allows this.

For example, if you try to replicate the last example in the SC help page, you get something like this:

(defun make-env (env &optional (done 0))
  (with-controls ((gt 1))
    (env-gen.kr env :gate gt :act done)))

(defmacro choose-noise ()
  (alexandria:random-elt '((pink-noise.ar)
               (white-noise.ar)
               (lf-noise2.ar (+ 100 (random 900))))))

(defmacro filter-input (in)
  (alexandria:random-elt `((with-controls ((freq 800))
                 (bpf.ar (sc::*~ ,in 15) freq 0.2))
               (with-controls ((freq 800 :lag 0.2))
                 (rhpf.ar ,in freq 0.2)))))

(proxy :a (* (sin-osc.ar 440)
         (make-env (asr) :free)))
(ctrl :a :gate -3)

(proxy :a (* (choose-noise)
         (make-env (asr) :free)))
(ctrl :a :gate -3)

(proxy :a (* (filter-input (choose-noise))
         (make-env (asr) :free)))
(ctrl :a :freq 1000)
(ctrl :a :gate -3)

(proxy :a (splay.ar (loop :repeat 8
              :collect (* (filter-input (choose-noise))
                      (make-env (asr) :free)))))
;;; => ERROR: duplicate control names

Which is kind of unfortunate because it seems like a cool and flexible way of combining functions to build synthdefs...

TatriX commented 3 years ago

Yep, with-controls is nice, as it doesn't have most of the downsides of arg in sclang. What is nice about NamedControl is that you can inline it in the synth, so it's easier to rename things and also make controls more visible. Maybe something like this:

(proxy :named-control
       (pan2.ar (sin-osc.ar (+ (kr :freq 440)
                               (in.ar (ar :fm (sin-osc.ar 20))))
                            0
                            (kr :amp 0.1))))
byulparan commented 3 years ago

with-controls seems need improvement and inline control looks cool. currentl cl-collider's control process is not clean, I'll fixed that in near future.

TatriX commented 3 years ago

Thank you so much @byulparan ! This project is extremely cool! I finally learned enough about synthesis and SuperCollider, so I've started using it more and more. And with-controls does its job perfectly fine. Named controls could make make it even better.

byulparan commented 3 years ago

@TatriX Thank you :-)
I have question. Is it possible use UGen for NamedControl's default value?

(proxy :named-control
       (pan2.ar (sin-osc.ar (+ (kr :freq 440)
                               (in.ar (ar :fm (sin-osc.ar 20))))   ;; <--- is it ok?
                            0
                            (kr :amp 0.1))))

my test code not run in SC.

(
x = SynthDef(\test, {
    var sig = Pan2.ar(SinOsc.ar(\freq.kr(SinOsc.kr(1).range(440, 880)), 0, 0.1));
    Out.ar(0,sig);
});

x.add; // ERROR: SynthDef: could not write def: DoesNotUnderstandError
)
TatriX commented 3 years ago

Oh, sorry for the confusion! No, I guess I just made it up to show as much possibilities as I could imagine.

byulparan commented 3 years ago

I commit named-control. please test :-) I'll add kr, ar for short code.

(defun kr (name &optional value lag) (named-control name :kr value lag))
(defun ar (name &optional value lag) (named-control name :ar value lag))

(defun make-env (env &optional (done 0))
  (env-gen.kr env :gate (kr :gt 1) :act done))

(defun choose-noise ()
  (funcall
   (alexandria:random-elt (list (lambda () (pink-noise.ar))
                (lambda () (white-noise.ar))
                (lambda () (lf-noise2.ar (+ 100 (random 900))))))))

(defun filter-input (in)
  (funcall 
   (alexandria:random-elt
    (list (lambda () (bpf.ar (sc::*~ in 15) (kr :freq 800) 0.2))
      (lambda () (rhpf.ar in (kr :freq 800) 0.2))))))

(proxy :a (* (sin-osc.ar 440)
         (make-env (asr) :free)))

(ctrl :a :gate -3)

(proxy :a (* (choose-noise)
         (make-env (asr) :free)))

(ctrl :a :gate -3)

(proxy :a (* (filter-input (choose-noise))
         (make-env (asr) :free)))

(ctrl :a :freq 1000)
(ctrl :a :gate -3)

(proxy :a (splay.ar (loop :repeat 8
              :collect (* (filter-input (choose-noise))
                      (make-env (asr) :free)))))

(ctrl :a :gate -3)
TatriX commented 3 years ago

That was fast! Works perfectly fine when evaluating proxy and with ctrl.

Example below doesn't seem to work:

(defsynth beep ()
  (out.ar 0 (pan2.ar (* (sin-osc.ar (dup (kr :freq 1000)))
                        (env-gen.kr (perc) :act :free))
                     0
                     (kr :amp 0.1))))

(synth :beep)
(synth :beep :freq 200) ; passed :freq is ignored
byulparan commented 3 years ago

@TatriX
It solved!


(defun kr (name &optional value lag) (named-control name :kr value lag))

(defsynth beep ()
  (out.ar 0 (pan2.ar (* (sin-osc.ar (dup (kr :freq 1000)))
                        (env-gen.kr (perc) :act :free))
                     0
                     (kr :amp 0.1))))

(synth :beep)
(synth :beep :freq (+ 500 (random 500))) 
TatriX commented 3 years ago

Works like a charm! Thank you!

TatriX commented 3 years ago

It looks so sweet now:

(proxy :saw
       (->> (-> (saw.ar (kr :freq 220))
                (lpf.ar (in.kr *fader-1*))
                (* (make-env (perc) :free))
                (pan2.ar (kr :pan 0)))
            (out.ar (kr :out 0))))

Will you add ar and friends to the lib?

byulparan commented 3 years ago

I'm considering... In most cases, control rate are "kr" so kr seems need.kr will add. but ar and tr are....I don't know if that would be good. and I already using tr symbol for other uses.

TatriX commented 3 years ago

Sounds reasonable. For ar one most likely will use (in.ar (kr :in-bus))

A bit off-topic, but what do you think about adding:

(defun midi-lin (value from to)
  (sc:lin-lin value 0 127 from to))

(defun midi-exp (value from to)
  (sc:lin-exp value 0 127 from to))

It's quite useful if you have external midi signal, like in:

(lpf.ar sig (midi-exp (in.kr *fader-bus*) 20 20000))
justin2004 commented 3 years ago

It looks so sweet now:

(proxy :saw
       (->> (-> (saw.ar (kr :freq 220))
                (lpf.ar (in.kr *fader-1*))
                (* (make-env (perc) :free))
                (pan2.ar (kr :pan 0)))
            (out.ar (kr :out 0))))

is that https://github.com/nightfly19/cl-arrows ?

TatriX commented 3 years ago

is that https://github.com/nightfly19/cl-arrows ?

Yes. It messes up eldoc a bit, but makes signal flow a bit more readable for me.

byulparan commented 3 years ago

How about make your own extension library for cl-collider. and add your custom musical / utility function to there.

I just want to keep the core on cl-collider. If I add all the functions that are only useful in a specific situation or sugar function then cl-collider will be too huge.

ofcourse I also have my own contrib like sc-extensions.

byulparan commented 3 years ago

I added kr. If really need ar too then I will add it. Today I will close this issue. Thanks for request and report issue! :-)