Встраивание CSound в Common Lisp

Я работаю над внедрением CSound в Lisp. CSound - это программное обеспечение с открытым исходным кодом для синтеза музыки (и не только).

У него довольно простой (скриптовый) язык. Быстрый старт (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 ищет все оцененные вызовы opcode в своем списке параметров, проверяя его тип (строку). Если он находит строку, он заменяет ее переменной gensym.

  • Каждый вызов присваивания добавляет присваивание в список операторов присваивания, который затем, наконец, используется для вывода на язык звукового сопровождения.

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
Другие вопросы по тегам