在 Common Lisp 中嵌入 CSound
Embedding CSound in Common Lisp
我正在努力将 CSound 嵌入到 Lisp 中。 CSound is a music synthesis (and more) open source software.
它有一种相当简单的(脚本)语言。上面的 link 提供了快速入门(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 替换它
变量。
每次调用 assign 都会将赋值添加到 assign 列表中
statements,最后用于输出到csound语言。
您的宏 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
我正在努力将 CSound 嵌入到 Lisp 中。 CSound is a music synthesis (and more) open source software.
它有一种相当简单的(脚本)语言。上面的 link 提供了快速入门(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 替换它 变量。
每次调用 assign 都会将赋值添加到 assign 列表中 statements,最后用于输出到csound语言。
您的宏 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