代码之家  ›  专栏  ›  技术社区  ›  kflak

如何在公共lisp中将函数传递给宏

  •  2
  • kflak  · 技术社区  · 1 年前

    我正试图用Common Lisp中的宏来理解,我有这段代码可以用cl collider客户端为超级对撞机声音合成语言创建合成器定义:

    (defmacro make-defsynth (name &key args sig (num-channels 2) (env :asr) (pan t))
      "Macro for creating defsynths. Defines some common args: out,
    pan, gate, as well as setting up the envelope according to the env
    argument. Valid envelopes are :asr and :perc. If pan is nil, then
    the signal will not be panned, but passed directly through."
      (let ((pan-func nil))
        (case num-channels
          (2 (setf pan-func '(pan2.ar sig pan)))
          (4 (setf pan-func '(pan4.ar sig pan)))
          (6 (setf pan-func '(pan-az.ar sig pan))))
        `(defsynth ,name (,@args (attack 0) (release 0)
                                 (out 0) (pan 0) (amp 1) (gate 1))
           (let* ((env (env-gen.kr (asr attack 1 release)
                                   :gate gate :act :free))
                  ,@sig
                  (sig (* sig env amp))
                  (sig (eval pan-func)))
             (out.ar out sig)))))
    

    我想使用这个宏来创建这样的synthdef:

    (make-defsynth playbuf-mono
                   :args ((buf 0)
                          (rate 1)
                          (start-pos 0)
                          (loop 0))
                   :sig ((sig (play-buf.ar 1 buf rate
                                           :start-pos start-pos
                                           :loop loop))))
    

    我得到错误:

    The variable PAN-FUNC is unbound.
       [Condition of type UNBOUND-VARIABLE]
    

    那么问题是:如何使pan函数正确展开,取决于 num-channels ?

    2 回复  |  直到 1 年前
        1
  •  5
  •   Rainer Joswig mmmmmm    1 年前

    形式 (eval pan-func) 在宏观扩张时不会进行评估,而且无论如何都不会起作用,因为 EVAL 不能 看见 本地词汇绑定。

    您希望插入代码

    (defmacro make-defsynth (name &key args sig
                                  (num-channels 2) (env :asr) (pan t))
      (let ((pan-func nil))
        (case num-channels
          (2 (setf pan-func '(pan2.ar sig pan)))
          (4 (setf pan-func '(pan4.ar sig pan)))
          (6 (setf pan-func '(pan-az.ar sig pan))))
        `(defsynth ,name (,@args
                         (attack 0) (release 0) (out 0) (pan 0) (amp 1) (gate 1))
           (let* ((env (env-gen.kr (asr attack 1 release)
                                   :gate gate :act :free))
                  ,@sig
                  (sig (* sig env amp))
                  (sig ,pan-func))
             (out.ar out sig)))))
    

    你现在需要考虑在哪里 env pan 应该使用,绑定应该来自哪里。现在在上面的代码中 env 平底锅 论点 make-defsynth 未使用。

    风格:我不会称之为 制作defsynth . make-something 意味着某些东西是在运行时制作的。但这是一个定义宏,在宏扩展/编译时展开。

    使用 macroexpand , macroexpand-1 pprint 要调试代码,请检查使用宏创建的代码:

    CL-USER 35 > (defmacro make-defsynth (name &key args sig
                                               (num-channels 2) (env :asr) (pan t))
                   (let ((pan-func nil))
                     (case num-channels
                       (2 (setf pan-func '(pan2.ar sig pan)))
                       (4 (setf pan-func '(pan4.ar sig pan)))
                       (6 (setf pan-func '(pan-az.ar sig pan))))
                     `(defsynth ,name (,@args
                                       (attack 0) (release 0) (out 0) (pan 0) (amp 1) (gate 1))
                                (let* ((env (env-gen.kr (asr attack 1 release)
                                                        :gate gate :act :free))
                                       ,@sig
                                       (sig (* sig env amp))
                                       (sig ,pan-func))
                                  (out.ar out sig)))))
    MAKE-DEFSYNTH
    

    然后调试它:

    CL-USER 36 > (pprint
                  (macroexpand-1
                   '(make-defsynth playbuf-mono
                                   :args ((buf 0)
                                          (rate 1)
                                          (start-pos 0)
                                          (loop 0))
                                   :sig ((sig (play-buf.ar 1 buf rate
                                                           :start-pos start-pos
                                                           :loop loop))))))
    
    (DEFSYNTH
     PLAYBUF-MONO
     ((BUF 0)
      (RATE 1)
      (START-POS 0)
      (LOOP 0)
      (ATTACK 0)
      (RELEASE 0)
      (OUT 0)
      (PAN 0)
      (AMP 1)
      (GATE 1))
     (LET* ((ENV (ENV-GEN.KR (ASR ATTACK 1 RELEASE) :GATE GATE :ACT :FREE))
            (SIG (PLAY-BUF.AR 1 BUF RATE :START-POS START-POS :LOOP LOOP))
            (SIG (* SIG ENV AMP))
            (SIG (PAN2.AR SIG PAN)))
       (OUT.AR OUT SIG)))
    
        2
  •  2
  •   kflak    1 年前

    回答我自己。。。

    如果我更换线路

    (sig (eval pan-func))
    

    具有

    (sig (,@pan-func))
    

    它似乎奏效了!虽然我仍然不确定这是“正确”的方法,或者我的方法是否适合我试图处理的问题。

    推荐文章