summaryrefslogtreecommitdiff
path: root/lib/plugin-test.lsp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugin-test.lsp')
-rw-r--r--lib/plugin-test.lsp184
1 files changed, 184 insertions, 0 deletions
diff --git a/lib/plugin-test.lsp b/lib/plugin-test.lsp
new file mode 100644
index 0000000..5e78342
--- /dev/null
+++ b/lib/plugin-test.lsp
@@ -0,0 +1,184 @@
+;; plugin-test -- simulate Audacity interface to Nyquist plugins
+;;
+;; Roger B. Dannenberg, Dec 2005
+;;
+;; This program runs an Audacity plugin from within Nyquist, where
+;; more debugging tools are available.
+;; There are two functions:
+;;
+;; (PLUGIN-TEST "plugin-file-name") -- full emulation of Audacity,
+;; prompts for parameters and audio file. The ".ny" extension
+;; is optional.
+;;
+;; (PLUGIN-AUTO-TEST "plugin-file-name" bindings ["audio-file-name"]) -- load
+;; and run the plugin. Bindings is a list of bindings, e.g.
+;; ((amp 1.0) (n 3)), setting the controls of the plugin.
+;; This version does not prompt for values.
+;;
+
+
+;; ADD-EXTENSION -- if filename does not end in ext, append ext to
+;; filename ext should include the ".", e.g. ".ny"
+;;
+(defun add-extension (filename ext)
+ (cond ((equal (subseq filename (- (length filename) (length ext)))
+ ext)
+ filename)
+ (t
+ (strcat filename ext))))
+
+(defun string-to-number (str)
+ (read (make-string-input-stream str)))
+
+
+(defun parse-control-spec (line)
+ (let ((stream (make-string-input-stream (subseq line 8))))
+ (list (read stream)
+ (read stream)
+ (read stream)
+ (read stream)
+ (read stream)
+ (read stream)
+ (read stream))))
+
+
+(defun describe-sound (snd)
+ (let ((typ (type-of snd)) sr)
+ (cond ((eq typ 'sound)
+ (setf typ "single-channel sound")
+ (setf sr (snd-srate snd)))
+ ((and (eq typ 'VECTOR) (eq (type-of (aref snd 0)) 'SOUND))
+ (setf typ "multi-channel sound")
+ (setf sr (snd-srate (aref snd 0)))))
+ (cond ((stringp typ)
+ (format t "=== Plugin result is a ~A at sample rate ~A ===~%"
+ typ sr)
+ snd)
+ (t
+ (format t "=== Plugin result is of type ~A ===~%" typ)
+ (pprint snd) ;; print result of plugin if it's not a sound
+ (s-rest 0.1))))) ;; return silence to make play happy
+
+
+(defun read-file-expressions (filename)
+ (let (file expr exprs)
+ (setf file (open filename))
+ (while (setf expr (read file))
+ (push expr exprs))
+ (reverse exprs)))
+
+
+;; AUDIO-FILE-TO-BINDINGS -- convert audio filename to pair of bindings:
+;; ((s (s-read <filename>)) (len <length-of-audio-file>))
+;; return nil if filename is invalid
+;;
+(defun audio-file-to-bindings (audio-file)
+ (let (source)
+ (if (> (length audio-file) 0)
+ (setf source (s-read audio-file)))
+ (cond (source
+ (setf len (* (nth 5 *rslt*) (nth 6 *rslt*)))
+ (list `(len ,len)
+ `(s (s-read ,audio-file))))
+ (t nil))))
+
+
+(defun plugin-test (filename)
+ (let (file controls bindings description plug-type
+ value audio-file source exprs len)
+ ;; first, check for filename extension
+ (setf filename (add-extension filename ".ny"))
+ ;; see if we can open the file
+ (setf file (open filename))
+ (if (null file)
+ (error (strcat "Could not open " filename)))
+ ;; parse the file
+ ;sym init step
+ (do ((line (read-line file) (read-line file)))
+ ((null line))
+ ;(display "pass 1" line)
+ (cond ((eql 0 (string-search ";control" line))
+ (push (parse-control-spec line) controls))
+ ((or (eql 0 (string-search ";nyquist" line))
+ (eql 0 (string-search ";version" line))
+ (eql 0 (string-search ";name" line))
+ (eql 0 (string-search ";action" line))
+ (eql 0 (string-search ";info" line)))
+ (push line description))
+ ((eql 0 (string-search ";type" line))
+ (cond ((string-search "process" line)
+ (setf plug-type 'process))
+ ((string-search "generate")
+ (setf plug-type 'generate))
+ ((string-search "analyze")
+ (setf plug-type 'analyze))
+ (t
+ (error (strcat "unexpected specification: " line)))))))
+ (close file)
+ ;; print description
+ (dolist (line description)
+ (format t "~A~%" line))
+ ;; get control values and set them as global variables
+ (setf controls (reverse controls))
+ (read-line) ;; read the newline after the expression that called this fn
+ ;; (otherwise, we'll read in unintended new-line for first control)
+ (dolist (control controls)
+ ;; control is (symbol description type units default minimum maximum)
+ (let ((sym (car control))
+ (desc (cadr control))
+ (ctrl-type (caddr control))
+ (units (cadddr control))
+ (default (nth 4 control))
+ (minimum (nth 5 control))
+ (maximum (nth 6 control)))
+ (loop
+ (format t "~A (~A) [~A]: " desc units default)
+ (setf value (read-line))
+ (if (equal value "")
+ (setf value default)
+ (setf value (string-to-number value)))
+ (if (equal ctrl-type 'int)
+ (setf value (round value))
+ (setf value (float value)))
+ (if (and (<= minimum value) (<= value maximum))
+ (return)) ; break from loop
+ (format t "Try again, value must be between ~A and ~A.~%"
+ minimum maximum))
+ (push (list sym value) bindings)))
+ (setf bindings (reverse bindings))
+ ;; determine the sound file name to process, if any, and open it
+ (cond ((member plug-type '(process analyze))
+ (loop
+ (format t "Audio input file: ")
+ (setf audio-file (read-line))
+ (setf source (audio-file-to-bindings audio-file))
+ (if source (return))
+ (format t "Could not open ~A. Try again.~%" audio-file))
+ (setf bindings (append source bindings))))
+ ;; now we're ready to read the plug-in as expressions
+ (setf exprs (read-file-expressions filename))
+ ;; turn expression list into a let and evaluate
+ (run-plugin exprs bindings)))
+
+
+(defun plugin-auto-test (filename bindings &optional audio-file)
+ (setf filename (add-extension filename ".ny"))
+ (let ((exprs (read-file-expressions filename))
+ source)
+ (cond (audio-file
+ (setf source (audio-file-to-bindings audio-file))))
+ (cond (source
+ (setf bindings (append source bindings)))
+ (t
+ (error (strcat "audio file not valid: " audio-file))))
+ (run-plugin exprs bindings)))
+
+(defun run-plugin (exprs bindings)
+ (setf exprs `(let (,@bindings) ,@exprs))
+ (pprint exprs)
+ (play (describe-sound (eval exprs))))
+
+
+
+
+