Ich arbeite an der Einbettung von CSound in Lisp. CSound is a music synthesis (and more) open source software.Einbetten von CSound in Common Lisp
Es hat eine ziemlich einfache (Scripting) Sprache. Schnellstart (10 Minuten gelesen) ist unter dem obigen Link verfügbar. Momentan arbeite ich nur an dem Zuweisungsteil (was ein großer Teil der csound-Sprache ist).
Hier ist mein Code:
(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))
Um zu zeigen, was der Code tut:
(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*)))
Ausgänge:
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
Diese in jeder Hinsicht korrekt ist, außer, „MOOGLADDER ifreq, iAMP "erscheint zweimal.
Warum ist das? Ich kann nicht herausfinden, wo es zweimal ausgewertet wird. Wie beseitige ich diese Wiederholung?
Hinweise über den Code:
Csound hat das Konzept von a, k und i Variablen bewerten. Dies ist seltsam als Präfix für das variable Symbol implementiert. Das am nächsten liegende Gegenstück in Lisp würde das einer globalen Variable sein. Also habe ich als solches implementiert. Um jedoch die Rate anzupassen, habe ich eine Indirektions-Ebene zwischen dem Symbol und seinem Wert. z.B. das Symbol 'res hat für seinen Wert' kRes. Nun hat das Symbol 'kRes für seinen Wert das Original 0.4.
Die Makros 'op' und 'assign *' sind einfache Wrapper um 'opcode-call' bzw. 'assign'. nativ
‚opcode-call ist eine Funktion, und ermöglicht somit automatisch für normale um Auswertung und damit für verschachtelte Funktionsaufrufe ermöglichen, die csound nicht unterstützt (vollständig). Um dies zu umgehen, sucht opcode-call in seiner Parameterliste nach allen ausgewerteten Opcode-Aufrufen, indem es den Typ (string) überprüft. Wenn es eine Zeichenkette findet, wird diese durch eine gensym Variable ersetzt.
Jeder Assign-Aufruf fügt die Zuweisung zur Liste der Assign-Anweisungen hinzu, die dann schließlich zur Ausgabe in die csound-Sprache verwendet wird.
Opcode-Aufruf erzeugt zur Laufzeit ein Assign-Form und wertet sie aus? Ist das eine gute Idee? –