2017-05-18 5 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 "が2回表示されます。

これはなぜですか?私はそれがどこで2回評価されているのか分かりません。 この繰り返しを削除するにはどうすればよいですか?コードに関する


注:

  • Csoundファイルは、kの概念を持っていると私は変数を評価。これは、可変記号の接頭辞として奇妙に実装された です。 lispの最も近い は、グローバル変数のものになります。だから私は そのように実装しました。しかし、レートに対応するために、私はシンボルとその値の間に間接参照のレベルを1つ持っています。例えば シンボル 'resには値' kResがあります。今度はシンボル 'kRes'の値は で、元の0.4です。

  • マクロ 'opと' assign *はそれぞれ 'opcode-call'と 'assign'を囲む単純なラッパーです。

  • 「はオペコードコール機能であるため、自動的にこのよう Csoundファイルがネイティブ(完全)をサポートしていないネストされた関数呼び出しを可能にする、通常 順序評価が可能になります。これを回避するために、 'opcode-callは、それがタイプ (文字列)をチェックすることによって、paramリスト内の評価されたopcode呼び出しを探します。文字列が見つかった場合は、それをgensym 変数に置き換えます。

  • 各割り当て呼び出しは、 割り当てのリストに割り当てを追加し、最後にcsound言語に出力するために使用されます。

+0

オペコード・コールは、実行時に割り当て、フォームを作成し、それを評価しますか?それは良いアイデアですか? –

答えて

4

マクロASSIGNは、値が2回計算されるようにします。以下のコメントを参照してください。

(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

ありがとう!実行時にassign-formを評価することについて:良いアイデアかどうかは分かりません。これは、これが非標準的な方法で実行時とコンパイル時の相互作用につながるため、おそらく避けるべき不要な複雑さを作り出していると言いますか? –