【问题标题】:Embedding CSound in Common Lisp在 Common Lisp 中嵌入 CSound
【发布时间】:2017-10-18 07:03:02
【问题描述】:

我正在将 CSound 嵌入到 Lisp 中。 CSound is a music synthesis (and more) open source software.

它有一种相当简单的(脚本)语言。上面的链接提供了快速入门(10 分钟阅读)。目前我只在做作业部分(这是 csound 语言的很大一部分)。

这是我的代码:

(defparameter *assign-statements* nil)

(defmacro assign (_name value &optional (rate 'i))
  (let* ((name (if (typep _name 'symbol) _name (eval _name)))
         (var (symb (format nil "~(~a~)" rate) name)))
    `(progn 
       (defparameter ,name ',var)
       (defparameter ,var ,value)
       (setf *assign-statements* 
                 (cons (format nil "~A = ~A" ,name ,value) *assign-statements*)))))

(defmacro assign* (&rest args)
  `(progn ,@(mapcar (lambda (arg) (cons 'assign arg)) args)))

(defun opcode-call (opcode &rest args)
  (format nil "~a ~{~a~^, ~}" opcode (mapcar (lambda (arg) 
             (if (stringp arg) 
                 (let ((var (gensym)))
                   (eval (list 'assign (symb (symbol-name var)) arg 'a))
                   (symbol-value (symb (symbol-name var)))) 
                 arg)) 
          args)))

(defmacro op (opcode &rest args)
  `(opcode-call ',opcode ,@args))

演示代码的作用:

(progn  
  (defparameter *assign-statements* nil)
  (assign* 
    (freq 'p4)
    (amp 'p5)
    (att (+ 0.1 0.1))
    (dec 0.4)
    (sus 0.6)
    (rel 0.7)
    (cutoff 5000)
    (res 0.4 k)
    (env (op madsr (op moogladder freq amp) att dec sus rel) k))
  (format t "~{~A~^~%~}~%" 
      (nreverse *assign-statements*)))

输出:

iFREQ = P4
iAMP = P5
iATT = 0.2
iDEC = 0.4
iSUS = 0.6
iREL = 0.7
iCUTOFF = 5000
kRES = 0.4
aG8707 = MOOGLADDER iFREQ, iAMP
aG8708 = MOOGLADDER iFREQ, iAMP
kENV = MADSR aG8708, iATT, iDEC, iSUS, iREL
NIL  

这在所有方面都是正确的,除了“MOOGLADDER iFREQ,iAMP”出现两次。

这是为什么呢?我不知道它在哪里被评估了两次。 如何消除这种重复?


关于代码的注释:

  • Csound 具有 a、k 和 i 速率变量的概念。这是 奇怪地实现为变量符号的前缀。最近的 lisp 中的对应物是全局变量。所以我有 就这样实现了。但是为了适应这个价格,我有一个 符号与其值之间的间接级别。例如这 符号 'res 的值是 'kRes。现在符号 'kRes 有它的 值,原来的 0.4。

  • 宏 'op 和 'assign* 分别是对 'opcode-call 和 'assign 的简单包装。

  • 'opcode-call 是一个函数,因此自动允许正常 顺序评估,从而允许嵌套函数调用,这 csound 本身并不(完全)支持。为了解决这个问题,'opcode-call 通过检查它的类型在它的参数列表中查找任何评估的操作码调用 (细绳)。如果它找到一个字符串,它将用 gensym 替换它 变量。

  • 每次分配调用都会将分配添加到分配列表 语句,然后最终用于输出到 csound 语言。

【问题讨论】:

  • 操作码调用在运行时创建一个分配表单并对其进行评估?这是个好主意吗?

标签: common-lisp csound


【解决方案1】:

您的宏ASSIGN 允许计算两次值。请参阅下面的评论。

(defmacro assign (_name value &optional (rate 'i))
  (let* ((name (if (typep _name 'symbol) _name (eval _name)))
         (var (symb (format nil "~(~a~)" rate) name)))
    `(progn 
       (defparameter ,name ',var)
       (defparameter ,var ,value)
       (push (format nil "~A = ~A" ,name ,var)    ; <- use the var
             *assign-statements*))))

试试看:

CL-USER 52 > (progn  
               (defparameter *assign-statements* nil)
               (assign* 
                (freq 'p4)
                (amp 'p5)
                (att (+ 0.1 0.1))
                (dec 0.4)
                (sus 0.6)
                (rel 0.7)
                (cutoff 5000)
                (res 0.4 k)
                (env (op madsr (op moogladder freq amp) att dec sus rel) k))
               (format t "~{~A~^~%~}~%" 
                       (nreverse *assign-statements*)))
iFREQ = P4
iAMP = P5
iATT = 0.2
iDEC = 0.4
iSUS = 0.6
iREL = 0.7
iCUTOFF = 5000
kRES = 0.4
aG2719 = MOOGLADDER iFREQ, iAMP
kENV = MADSR aG2719, iATT, iDEC, iSUS, iREL
NIL

【讨论】:

  • 谢谢!关于在运行时评估分配表单:我不知道这是否是个好主意。你是说因为这会导致运行时和编译时以非标准方式进行交互,从而产生不必要的复杂性,而这也许应该避免?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
  • 2018-02-21
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多