1$nyquist plug-in 2$version 4 3$type tool generate 4$name (_ "Sample Data Import") 5$manpage "Sample_Data_Import" 6$action (_ "Reading and rendering samples...") 7$author (_ "Steve Daulton") 8$release 3.0.4 9$copyright (_ "GNU General Public License v2.0 or later") 10 11$control filename (_ "Select file") file "" "*default*/sample-data.txt" (((_ "Text file") (txt TXT)) 12 ((_ "All files") (""))) "open,exists" 13$control bad-data (_ "Invalid data handling") choice (("ThrowError" (_ "Throw Error")) 14 ("ReadAsZero" (_ "Read as Zero"))) 0 15 16 17;; Released under terms of the GNU General Public License v2.0 or later: 18;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 19;; 20;; For information about writing and modifying Nyquist plug-ins: 21;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference 22 23 24;; Check file can be opened 25(defun fileopensp (fname) 26 (cond 27 ((not (setf fstream (open fname))) 28 (throw 'err (format nil (_ "Error~%~ 29 '~a' could not be opened.~%~ 30 Check that file exists.") 31 fname))) 32 ; File opened OK, so check for normal ASCII, then close it and return 'true' 33 (t (do ((j 0 (1+ j))(b (read-byte fstream)(read-byte fstream))) 34 ((or (> j 100000)(not b))) 35 (when (> b 127) 36 (throw 'err (format nil (_ "Error:~%~ 37 The file must contain only plain ASCII text.~%~ 38 (Invalid byte '~a' at byte number: ~a)") b (1+ j) )))) 39 (close fstream) 40 t))) 41 42;; ':new' creates a new class 'streamreader' 43;; 'filestream' and 'chanel' are its instance variables. 44;; (every object of class 'streamreader' has its own 45;; copy of these variables) 46(setq streamreader 47 (send class :new '(filestream chanel))) 48 49;; Initialize class 'streamreader' 50(send streamreader :answer :isnew '(stream ch) '( 51 (setq filestream stream) 52 (setq channel ch))) 53 54;; Create ':next' method. 55;; Snd-fromobject calls this method to obtain the 56;; next sound sample until it receives 'nil' 57(send streamreader :answer :next '() '( 58 (case channel 59 (0 ;mono 60 (read-and-verify filestream)) 61 (1 ;left channel 62 ;Note, we still need to verify data even if skipping it. 63 (let ((val (read-and-verify filestream))) 64 (read-and-verify filestream) ;skip right channel sample 65 val)) 66 (t ;right channel 67 (read-and-verify filestream) ;skip left channel sample 68 (read-and-verify filestream))))) 69 70(defun read-and-verify (stream) 71"snd-fromobject requires float values, nil to terminate" 72 (let ((val (read stream))) 73 (cond 74 ((not val) nil) ;end of file 75 ((numberp val) (float val)) ;valid. 76 ((= bad-data 0) ;invalid. Throw error and quit 77 (throw 'err (format nil (_ "Error~%~ 78 Data must be numbers in plain ASCII text.~%~ 79 '~a' is not a numeric value.") val))) 80 (t 0.0)))) ;invalid. Replace with zero. 81 82;; Instantiate a new sound object 83(defun make-sound-object (stream chan) 84 (send streamreader :new stream chan)) 85 86(defun sound-from-file (filename) 87 ;; Set path. fileopenp should return 'true' 88 (if (not (fileopensp filename)) 89 (throw 'err (format nil (_ "Error.~%Unable to open file")))) 90 ; Note: we can't use (arrayp *track*) because 91 ; *track* is nil in generate type plug-ins. 92 (cond 93 ((= (get '*track* 'channels) 2) 94 (let ((left-snd (get-sound filename 1)) 95 (right-snd (get-sound filename 2))) 96 (vector left-snd right-snd))) 97 (t ;; Mono track 98 (get-sound filename 0)))) 99 100(defun get-sound (fname chan) 101 (let* ((stream (open fname :direction :input)) 102 (left (make-sound-object stream chan))) 103 (setf audio-out (snd-fromobject 0 *sound-srate* left)) 104 (snd-play audio-out) ;force samples to be calculated now. 105 (close stream) 106 audio-out)) 107 108(catch 'err (sound-from-file filename)) 109