我正在將CSound嵌入到Lisp中。 CSound is a music synthesis (and more) open source software.將CSound嵌入到Common Lisp中
它有一個相當簡單的(腳本)語言。快速入門(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的具有的概念,k和我率變量。這是奇怪地實現爲 作爲變量符號的前綴。 lisp中對應的最接近的 應該是全局變量。所以我 這樣實現它。然而,爲了適應這個速度,我在符號和它的值之間有一個間接等級 。例如 符號'res的值爲'kRes。現在符號'kRes的值爲 ,原始值爲0.4。
宏的op和'assign *'分別是'opcode-call和'assign分別的簡單包裝。
「操作碼呼叫是一個函數,並因此自動地允許正常 順序評估,由此允許嵌套的函數調用,這 CSound的不(充分)支持本身。爲了解決這個問題,'opcode-call通過檢查它的類型 (字符串)在其參數列表中查找任何經過評估的操作碼調用。如果它找到一個字符串,它會用一個gensym 變量代替它。
每個分配調用都會將分配添加到分配 語句的列表中,然後最終用於輸出到csound語言。
操作碼呼叫在運行時創建的分配形式和評價呢?這是一個好主意嗎? –