2017-05-18 37 views
1

我正在將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語言。

+0

操作碼呼叫在運行時創建的分配形式和評價呢?這是一個好主意嗎? –

回答

4

您的宏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 
+0

謝謝!關於在運行時評估分配表單:我不知道這是一個好主意。你是否在說因爲這會導致運行時間和編譯時間之間以非標準方式進行交互,會產生不必要的複雜性,這應該可以避免? –