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