|
34 | 34 |
|
35 | 35 | ;; s-save -- saves a file
|
36 | 36 | (setf *in-s-save* nil)
|
37 |
| -(setf NY:ALL 1000000000) ; 1GIG constant for maxlen |
38 |
| -(defmacro s-save (expression &optional (maxlen NY:ALL) filename |
39 |
| - &key (format '*default-sf-format*) |
40 |
| - (mode '*default-sf-mode*) (bits '*default-sf-bits*) |
41 |
| - (endian NIL) ; nil, :big, or :little -- specifies file format |
42 |
| - (play nil)) |
43 |
| - `(let ((ny:fname ,filename) |
44 |
| - (ny:maxlen ,maxlen) |
45 |
| - (ny:endian ,endian) |
46 |
| - (ny:swap 0) |
47 |
| - max-sample) ; return value |
48 |
| - (cond (*in-s-save* |
49 |
| - (error "Recursive call to s-save (maybe play?) detected!"))) |
50 |
| - (progv '(*in-s-save*) '(t) |
51 |
| - ; allow caller to omit maxlen, in which case the filename will |
52 |
| - ; be a string in the maxlen parameter position and filename will be null |
53 |
| - (cond ((null ny:fname) |
54 |
| - (cond ((stringp ny:maxlen) |
55 |
| - (setf ny:fname ny:maxlen) |
56 |
| - (setf ny:maxlen NY:ALL)) |
57 |
| - (t |
58 |
| - (setf ny:fname *default-sound-file*))))) |
59 |
| - |
60 |
| - (cond ((equal ny:fname "") |
61 |
| - (cond ((not ,play) |
62 |
| - (format t "s-save: no file to write! play option is off!\n")))) |
63 |
| - (t |
64 |
| - (setf ny:fname (soundfilename ny:fname)) |
65 |
| - (format t "Saving sound file to ~A~%" ny:fname))) |
66 |
| - (cond ((eq ny:endian :big) |
67 |
| - (setf ny:swap (if (bigendianp) 0 1))) |
68 |
| - ((eq ny:endian :little) |
69 |
| - (setf ny:swap (if (bigendianp) 1 0)))) |
70 |
| - ; print device info the first time sound is played |
71 |
| - (cond (,play |
72 |
| - (cond ((not (boundp '*snd-list-devices*)) |
73 |
| - (setf *snd-list-devices* t))))) ; one-time show |
74 |
| - (setf max-sample |
75 |
| - (snd-save ',expression ny:maxlen ny:fname ,format |
76 |
| - ,mode ,bits ny:swap ,play)) |
77 |
| - ; more information if *snd-list-devices* was unbound: |
78 |
| - (cond (,play |
79 |
| - (cond (*snd-list-devices* |
80 |
| - (format t "\nSet *snd-list-devices* = t\n~A\n~A\n~A\n~A\n\n" |
81 |
| - " and call play to see device list again." |
82 |
| - "Set *snd-device* to a fixnum to select an output device" |
83 |
| - " or set *snd-device* to a substring of a device name" |
84 |
| - " to select the first device containing the substring."))) |
85 |
| - (setf *snd-list-devices* nil))) ; normally nil |
86 |
| - max-sample))) |
| 37 | +(setf NY:ALL 576460752303423488) ; constant for maxlen == 1 << 59 |
| 38 | +;; note that at 16-bytes-per-frame, this could generate a file byte offset |
| 39 | +;; that overflows an int64_t. Is this big enough? Time will tell. |
| 40 | +;; What if Nyquist is compiled for 32-bit machines and FIXNUM is 32-bits? |
| 41 | +;; if we don't have 64-bit ints, use 0x7f000000, which is about 10M less |
| 42 | +;; than the maximum signed 32-bit int, giving a lot of "headroom" but still |
| 43 | +;; over 2 billion, or about 13.4 hours at 44.1KHz |
| 44 | +(if (/= 10000000000 (* 100000 100000)) |
| 45 | + (setf NY:ALL 2130706432)) |
| 46 | + |
| 47 | + |
| 48 | +;; S-SAVE combines optional and keyword parameters, but this is a really bad |
| 49 | +;; idea because keywords and values are used as optional parameters until |
| 50 | +;; all the optional parameters are used up. Thus if you leave out filename |
| 51 | +;; and progress, but you provide :endian T, then filename becomes :endian and |
| 52 | +;; progress becomes T. AARRGG!! |
| 53 | +;; I should have required filename and made everything else keyword, but |
| 54 | +;; rather than breaking compatibility, I'm using &rest to grab everything, |
| 55 | +;; parse the parameters for keywords (giving them priority over optional |
| 56 | +;; parameters, and filling in optional parameters as they are encountered. |
| 57 | +;; |
| 58 | +(defmacro s-save (expression &rest parameters) |
| 59 | + (prog (parm (format *default-sf-format*) |
| 60 | + (mode *default-sf-mode*) |
| 61 | + (bits *default-sf-bits*) |
| 62 | + ;; endian can be nil, :big, or :little |
| 63 | + endian play optionals maxlen filename progress swap) |
| 64 | + loop ;; until all parameters are used |
| 65 | + (cond ((setf parm (car parameters)) |
| 66 | + (setf parameters (cdr parameters)) |
| 67 | + (case parm |
| 68 | + (:format (setf format (car parameters) |
| 69 | + parameters (cdr parameters))) |
| 70 | + (:mode (setf mode (car parameters) |
| 71 | + parameters (cdr parameters))) |
| 72 | + (:bits (setf bits (car parameters) |
| 73 | + parameters (cdr parameters))) |
| 74 | + (:endian (setf endian (car parameters) |
| 75 | + parameters (cdr parameters))) |
| 76 | + (:play (setf play (car parameters) |
| 77 | + parameters (cdr parameters))) |
| 78 | + (t (setf optionals (cons parm optionals)))) |
| 79 | + (go loop))) |
| 80 | + (cond ((> (length optionals) 3) |
| 81 | + (error "S-SAVE got extra parameter(s)"))) |
| 82 | + (cond ((< (length optionals) 1) ;; need maxlen |
| 83 | + (setf optionals (list ny:all)))) |
| 84 | + (cond ((< (length optionals) 2) ;; need filename |
| 85 | + (setf optionals (cons nil optionals)))) |
| 86 | + (cond ((< (length optionals) 3) ;; need progress |
| 87 | + (setf optionals (cons 0 optionals)))) |
| 88 | + (setf progress (first optionals) ;; note that optionals are in reverse order |
| 89 | + filename (second optionals) |
| 90 | + maxlen (third optionals)) |
| 91 | + (cond (*in-s-save* |
| 92 | + (error "Recursive call to S-SAVE (or maybe PLAY) detected!"))) |
| 93 | + |
| 94 | + ;; finally, we have all the parameters and we can call snd-save |
| 95 | + (return |
| 96 | + `(let ((ny:fname ,filename) (ny:swap 0) (ny:endian ,endian) |
| 97 | + (ny:play ,play) |
| 98 | + ny:max-sample) ; return value |
| 99 | + (progv '(*in-s-save*) '(t) |
| 100 | + (if (null ny:fname) |
| 101 | + (setf ny:fname *default-sound-file*)) |
| 102 | + |
| 103 | + (cond ((equal ny:fname "") |
| 104 | + (cond ((not ,play) |
| 105 | + (format t "S-SAVE: no file to write! ~ |
| 106 | + play option is off!\n")))) |
| 107 | + (t |
| 108 | + (setf ny:fname (soundfilename ny:fname)) |
| 109 | + (format t "Saving sound file to ~A~%" ny:fname))) |
| 110 | + |
| 111 | + (cond ((eq ny:endian :big) |
| 112 | + (setf ny:swap (if (bigendianp) 0 1))) |
| 113 | + ((eq ny:endian :little) |
| 114 | + (setf ny:swap (if (bigendianp) 1 0)))) |
| 115 | + |
| 116 | + ; print device info the first time sound is played |
| 117 | + (cond (ny:play |
| 118 | + (cond ((not (boundp '*snd-list-devices*)) |
| 119 | + (setf *snd-list-devices* t))))) ; one-time show |
| 120 | + (setf max-sample |
| 121 | + (snd-save ',expression ,maxlen ny:fname ,format |
| 122 | + ,mode ,bits ny:swap ny:play ,progress)) |
| 123 | + ; more information if *snd-list-devices* was unbound: |
| 124 | + (cond (ny:play |
| 125 | + (cond (*snd-list-devices* |
| 126 | + (format t "\nSet *snd-lfist-devices* = t \n ~ |
| 127 | + and call play to see device list again.\n~ |
| 128 | + Set *snd-device* to a fixnum to select an output device\n ~ |
| 129 | + or set *snd-device* to a substring of a device name\n ~ |
| 130 | + to select the first device containing the substring.\n"))) |
| 131 | + (setf *snd-list-devices* nil))) ; normally nil |
| 132 | + max-sample))))) |
| 133 | + |
87 | 134 |
|
88 | 135 | ;; MULTICHANNEL-MAX -- find peak over all channels
|
89 | 136 | ;;
|
|
226 | 273 |
|
227 | 274 | ;; s-read -- reads a file
|
228 | 275 | (defun s-read (filename &key (time-offset 0) (srate *sound-srate*)
|
229 |
| - (dur 10000.0) (nchans 1) (format *default-sf-format*) |
| 276 | + (dur 10e20) (nchans 1) (format *default-sf-format*) |
230 | 277 | (mode *default-sf-mode*) (bits *default-sf-bits*) (endian NIL))
|
231 | 278 | (let ((swap 0))
|
232 | 279 | (cond ((eq endian :big)
|
|
319 | 366 | (defun snd-read-srate (rslt) (cadr (cddddr rslt)))
|
320 | 367 | (defun snd-read-dur (rslt) (caddr (cddddr rslt)))
|
321 | 368 | (defun snd-read-flags (rslt) (cadddr (cddddr rslt)))
|
322 |
| -(defun snd-read-byte-offset (rslt) (cadr (cddddr (cddddr rslt)))) |
323 | 369 |
|
324 | 370 | ;; round is tricky because truncate rounds toward zero as does C
|
325 | 371 | ;; in other words, rounding is down for positive numbers and up
|
|
339 | 385 | (defun coterm (snd1 snd2)
|
340 | 386 | (multichan-expand #'snd-coterm snd1 snd2))
|
341 | 387 |
|
342 |
| -(defmacro s-add-to (expr maxlen filename &optional (time-offset 0.0)) |
| 388 | +(defmacro s-add-to (expr maxlen filename |
| 389 | + &optional (time-offset 0.0) (progress 0)) |
343 | 390 | `(let ((ny:fname (soundfilename ,filename))
|
344 | 391 | ny:peak ny:input (ny:offset ,time-offset))
|
345 | 392 | (format t "Adding sound to ~A at offset ~A~%"
|
|
350 | 397 | :time-offset ny:offset)
|
351 | 398 | ny:addend)
|
352 | 399 | ny:addend))
|
353 |
| - ,maxlen ny:fname ny:offset SND-HEAD-NONE 0 0 0)) |
| 400 | + ,maxlen ny:fname ny:offset ,progress)) |
354 | 401 | (format t "Duration written: ~A~%" (car *rslt*))
|
355 | 402 | ny:peak))
|
356 | 403 |
|
357 | 404 |
|
358 |
| -(defmacro s-overwrite (expr maxlen filename &optional (time-offset 0.0)) |
| 405 | +(defmacro s-overwrite (expr maxlen filename |
| 406 | + &optional (time-offset 0.0) (progress 0)) |
359 | 407 | `(let ((ny:fname (soundfilename ,filename))
|
360 | 408 | (ny:peak 0.0)
|
361 | 409 | ny:input ny:rslt (ny:offset ,time-offset))
|
362 | 410 | (format t "Overwriting ~A at offset ~A~%" ny:fname ny:offset)
|
363 |
| - (setf ny:offset (snd-read-byte-offset ny:rslt)) |
364 |
| - (setf ny:peak (snd-overwrite `,expr ,maxlen ny:fname ny:offset |
365 |
| - SND-HEAD-NONE 0 0 0)) |
| 411 | + (setf ny:peak (snd-overwrite `,expr ,maxlen ny:fname ny:offset ,progress)) |
366 | 412 | (format t "Duration written: ~A~%" (car *rslt*))
|
367 | 413 | ny:peak))
|
368 | 414 |
|
|
0 commit comments