Work-in-progress repo for ambisonics extensions for OM-SoX
Marlon Schumacher
24.02.25 197ce0f724aa567c7401f63dc889e7861ed8c0aa
commit | author | age
92c40d 1 ;*********************************************************************
AN 2 ; OM-SoX, (c) 2011-2014 Marlon Schumacher (CIRMMT/McGill University) *
3 ;             http://sourceforge.net/projects/omsox/                 *
4 ;                                                                    *
5 ;  Multichannel Audio Manipulation and Functional Batch Processing.  *
6 ;        DSP based on SoX - (c) C.Bagwell and Contributors           *
7 ;                  http://sox.sourceforge.net/                       *
8 ;*********************************************************************
9 ;
10 ;This program is free software; you can redistribute it and/or
11 ;modify it under the terms of the GNU General Public License
12 ;as published by the Free Software Foundation; either version 2
13 ;of the License, or (at your option) any later version.
14 ;
15 ;See file LICENSE for further informations on licensing terms.
16 ;
17 ;This program is distributed in the hope that it will be useful,
18 ;but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;GNU General Public License for more details.
21 ;
22 ;You should have received a copy of the GNU General Public License
23 ;along with this program; if not, write to the Free Software
24 ;Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,10 USA.
25 ;
26 ;Authors: M. Schumacher
27
28 (in-package :om)
29
30
31 ; %%%%%%%%%%%%%%% SOX-PROCESS %%%%%%%%%%%%%%%%%%%%
32 ;           Main Processing Function 
33
34 ;== method for pathname + string =======
35 (defmethod! sox-process ((sox-input pathname) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
36             :icon 08
37             :initvals '(nil "" "realtime" nil nil nil Off "break")
38             :menuins (list (list 2 *sox-output-options*)
39                            (list 3 *sox-file-formats*)
40                            (list 4 *sox-samplerates*)
41                            (list 5 *sox-bitdephts*)
42                            (list 6 '(("On" On) ("Off" Off)))
43                            (list 7 '(("break" break) ("repeat" repeat) ("cycle" cycle))))                       
44             :indoc '("Audio input to be processed [sound, path, string/pipe, sox-input]" 
45                      "Sox-effect to be applied to audio input [string]" "Output type (new file, replace file, pipe, or realtime) [string]. Also accepts directory, filename, filepath [path]"
46                      "Filetype of produced audio [string]" "Samplerate of produced audio [string]" "Bitdepth of produced audio [number]"
47                      "recursive (when 'on' applies processing recursively to audio) [symbol]" "Mode for batch-processing (break, repeat, cycle) [symbol]") 
48             :doc "Main audio processing function for OM-SoX. Takes audio provided in <sox-input> and processes it with sox-effects provided in <sox-effect>. 
49
50 <output> determines whether output is to be rendered into 
51 a) new file, 
52 b) replace the input file, 
53 c) create a sox-command as a pipe (for further processing), 
54 d) play back in realtime through audio device.
55
56 Alternatively, a filename, directory, or path can be provided which will write an audio file to the given destination. 
57 It is also posisble to connect a function/patch in lambda mode, which (if available) will receive the path of <sox-input> as first argument. 
58 This can be used to algorithmically name output files (e.g. in batch processes).
59
60 Optionally, the audio output <filetype> can be specified, as well as the <samplerate> and <bitdepth> (default: same as input). 
61 <recursive> is an experimental option allowing to apply a sox-effect recursively to audio input (e.g. trimming).
62 <batch-mode> determines the behaviour when processing lists of sox-inputs and sox-effects that differ in length.
63 "
64
65             (if (probe-file *sox-path*)
66
67                 (let ((outfile (create-path sox-input output filetype)))
68
69                   (setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* (namestring sox-input)))
70
71                   (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*)) 
72                                   ((equal output "pipe") (sox-samplebits str bitdepth samplerate '-p))
73                                   (t (sox-samplebits str bitdepth samplerate outfile))))
74
75                   (setf str (string+ str sox-effect))
76                  
77                   ; optional removal of temp-files
78                   (if *delete-inter-file* 
79                       (let ((outstring (sox-out str sox-input output outfile recursive))) 
80                         (if (>= *om-version* 6.07)
81                             (om-run-process "cleantempfiles" #'sleep&clean 5)
82                           (om-run-process "cleantempfiles" sleep&clean 5))
83                         outstring)                   
84                     (sox-out str sox-input output outfile recursive)
85                     ))
86               (sox-not-found))
87             )
88
89
90 ;== method for sound + string =======
91 (defmethod! sox-process ((sox-input sound) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
92             (let ((gainval (sox-vol-convert (vol sox-input)))
93                   (panval (+ (tracknum sox-input) (* 0.01 (pan sox-input)))))
94               (if (equal (tracknum sox-input) 0)
95                   (sox-process (sound-path sox-input) (string+ sox-effect " gain " (makestring gainval)) 
96                                :output output :filetype filetype :samplerate samplerate :bitdepth bitdepth 
97                                :recursive recursive :batch-mode batch-mode)            
98                 (sox-process (make-instance 'sox-pan
99                                             :sound sox-input
100                                             :gains gainval
101                                             :panning panval
102                                             :numchannels nil)
103                              sox-effect 
104                              :output output :filetype filetype :samplerate samplerate :bitdepth bitdepth 
105                              :recursive recursive :batch-mode batch-mode)))
106             )
107
108
109 ;== method for string + string =======
110 ;-> allows for arbitrary 'manual' sox-effect or options or pipe inputs...
111 (defmethod! sox-process ((sox-input string) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
112             (if (probe-file *sox-path*)
113
114             ;catch exception ---------
115                 (if (and (pipe-p sox-input) (equal output "pipe") )
116                    (om-message-abort "Pipe output not possible with this type of input.") 
117             ; ------------------------
118
119                 (let ((outfile (create-path nil output filetype)))
120
121                   (setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* sox-input))
122                   
123                   (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*)) 
124                                   ((equal output "pipe") (sox-samplebits str bitdepth samplerate '-p))
125                                   (t (sox-samplebits str bitdepth samplerate outfile))))
126
127                   (setf str (string+ str sox-effect))
128
129                   (if *delete-inter-file* 
130                       (let ((outstring (sox-out str sox-input output outfile recursive))) 
131                         (if (>= *om-version* 6.07)
132                             (om-run-process "cleantempfiles" #'sleep&clean 5)
133                           (om-run-process "cleantempfiles" sleep&clean 5))
134                         outstring)                   
135                     (sox-out str sox-input output outfile recursive)
136                     )))
137
138               (sox-not-found))
139               )
140
141 ; %%%%%%%% METHODS FOR SOX-INPUT CLASSES %%%%%%%%%%%%
142
143 ; === sox-merge =============
144
145 (defmethod! sox-process ((sox-input sox-merge) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
146             (if (probe-file *sox-path*)
147
148                 (progn
149                   ;(print (sound sox-input))
150                   (let* ((sound (if (> (length (gains sox-input)) (length (list! (sound sox-input)))) 
151                                         (repeat-n (first (list! (sound sox-input))) (length (gains sox-input)))
152                                       (sound sox-input))))                                     
153
154                   (when (and (find-if 'stringp sound) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
155
156                 (let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
157
158                   (let* ((filenames (loop for soundfile in sound collect                              
159                                           (namestring soundfile))))          
160
161                     (setf str (format nil "~s ~a -M " (namestring *sox-path*) *sox-options* )) 
162                     
163                     (if (gains sox-input)
164                         (loop for filename in filenames do
165                               for gain in (db->lin (list! (gains sox-input))) do
166                               (setf str (string+ str (format nil " -v~d ~s  " gain filename))))
167                       (loop for filename in filenames do
168                             (setf str (string+ str (format nil " ~s" filename)))))
169  
170                     (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
171                                     ((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
172                                     (t (sox-samplebits str bitdepth samplerate outfile))))
173
174                     (setf str (string+ str sox-effect))
175
176                     (sox-out str sox-input output outfile recursive)))))
177
178               (sox-not-found))
179             )
180
791513 181 ; === sox-hoaencode =============
AN 182
183 (defmethod! sox-process ((sox-input sox-hoaencode) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
197ce0 184
MS 185 ; would be more consistent with OO programming to have a function sox-hoaencode->sox-merge 
186 ; (consider how to deal with multiple sounds to be hoaencoded and mixed later on... sox-mix might be better done in an explicit way rather than via the class directly. 
187 ; in that case a 3DC would have to be used by using points (point-pairs) in a loop 
188
791513 189     (if (probe-file *sox-path*)
AN 190         (progn
a590ad 191             (when 
AN 192                 (not (listp (sound sox-input)))
197ce0 193                 (om-message-abort "sound must be a list.") ; use list! instead
a590ad 194             )
AN 195             (when 
196                 (not 
197                     (or 
198                         (subtypep (type-of (positions sox-input)) '3dc)
199                         (and (subtypep (type-of (positions sox-input)) 'list) (> (length (positions sox-input)) 0) (subtypep (type-of (first (positions sox-input))) 'list) (> (length (first (positions sox-input))) 1)))
200                 )
201                 (om-message-abort "positions must be of type 3dc, or a list of lists.")
202             )
791513 203             (let* 
AN 204                 (
205                     (sound 
a590ad 206                         (sound sox-input)
791513 207                     )
AN 208                     (positions-ae 
209                         (sox-hoaencode-auto-convert-positions (positions sox-input))
210                     )
211                 )
212                 (when 
213                     (and (find-if 'stringp sound) (equal output "pipe")) 
214                     (om-message-abort "Pipe output not possible with this type of input.")
215                 )
216                 (let* 
217                     (
218                         (filenames (loop for soundfile in sound collect (namestring soundfile)))
a590ad 219                         (outfile (create-path nil output filetype))
791513 220                     )
AN 221                     (setf str (format nil " ~s ~a" (namestring *sox-path*) *sox-options*))
222                     (if (= (length filenames) 1)
223                         ; case: one file 
224                         (let* 
225                             (
226                                 (filename (first filenames))
227                                 (position (first positions-ae))
228                             )
229                             (if (= (order sox-input) 0) 
230                                 ; case: order = 0
231                                 (setf str (string+ str (format nil " ~s" filename)))
232                                 ; case: order > 0
233                                 (progn
234                                     (setf str (string+ str " -M"))
235                                     (loop for gain in (sox-hoaencode-gains-up-to-order (order sox-input) (first position) (second position)) do
236                                         (setf str (string+ str (format nil " -v~d ~s" gain filename))))
237                                 )
238                             )
239                         )
240                         ; case: multiple files
241                         (if (= (order sox-input) 0)
242                             ; case: order = 0
243                             (progn 
197ce0 244                                 (setf str (string+ str " -m")) ; this is the mixer. e.g. sox-mix
791513 245                                 (loop for filename in filenames do
AN 246                                     (setf str (string+ str (format nil " ~s" filename)))
247                                 )
248                             )
249                             ; case: order > 0
250                             (progn
251                                 (setf str (string+ str " -m"))
252                                 
253                                 ; Create components 
254                                 (setf component_idx 0)
255                                 (loop 
256                                     for filename in filenames
257                                     for position in positions-ae do
197ce0 258
MS 259                                                                    ; in Object-Oriented Programming we would call-next-method i.e. set a sox-merge input class with the corresponding params. 
260                                                                    ; e.g. (make-instance 'sox-merge ... (more redundancy and compactness, less error-prone) ... possible?
261                                                                    ; for multiple sound inputs if possible we would then call a sox-mix to mix the sox merge pipes. everything
262
791513 263                                     (progn  
AN 264                                         (setf str_component (format nil " ~s ~a -M" (namestring *sox-path*) *sox-options*))
265                                         (loop 
266                                             for gain in (sox-hoaencode-gains-up-to-order (order sox-input) (first position) (second position)) do
267                                             (setf str_component (string+ str_component (format nil " -v~d ~s" gain filename)))
268                                         )
269                                         (setf outfile_tmp (format nil "~a~a-~d.~a" (directory-namestring outfile) (pathname-name outfile) component_idx (pathname-type outfile)))
270                                         (setf str_component (sox-samplebits str_component bitdepth samplerate outfile_tmp))
271                                         (sox-out str_component filename "new file" outfile_tmp nil)
272
273                                         (setf component_idx (1+ component_idx))
274
275                                         ; Add component path to <str>
276                                         (setf str (string+ str (format nil " ~s" outfile_tmp)))
277                                     )
278                                 )
279                             )
280                         )
281                     )
282                     (setf str 
283                         (cond 
a590ad 284                             ((equal output "realtime") 
AN 285                                 (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
286                             ((equal output "pipe") 
287                                 (sox-samplebits str bitdepth samplerate "-p"))
288                             (t 
289                                 (sox-samplebits str bitdepth samplerate outfile))
791513 290                         )
AN 291                     )
292                     (setf str (string+ str sox-effect))
293                     (print str)
294                     (sox-out str sox-input output outfile recursive)
197ce0 295
MS 296                        ;optional removal of temp file
297                   ;    (add-tmp-file outfile_tmp)
298                   ;    (when *delete-inter-file* (clean-tmp-files))
299
791513 300                 )
AN 301             )
302         )
303         (sox-not-found)
304     )
305 )
306
92c40d 307 ; === sox-mix =============
AN 308
309 (defmethod! sox-process ((sox-input sox-mix) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
310             (if (probe-file *sox-path*)
311
312                  (progn
313                   (when (and (find-if 'stringp (sound sox-input)) (equal output "pipe"))
314                     (om-message-abort "Pipe output not possible with this type of input."))
315
316                   (let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
317
318                     (let* ((filenames 
319                             (loop for soundfile in (sound sox-input) collect ;I guess this throws the error when using pipes as inputs
320                                   (namestring soundfile))))
321
322                       (setf str (format nil "~s ~a -m " (namestring *sox-path*) *sox-options*))
323                                                           
324                       ;potentially not needed...
325                    ;(if (1/n sox-input) 
326                         
327                    ;     (loop for filename in filenames do
328                    ;           for gain in (om/ (db->lin (gains sox-input)) (length (gains sox-input))) do ;(db->lin (list! (gains sox-input))) -> but it's always going to be a list, no?
329                    ;           (setf str (string+ str (format nil " -v~d ~s " gain filename )))) ; is it not scaling when volumes are given? try out!
330
331                       (loop for filename in filenames do
332                               for gain in (db->lin (gains sox-input)) do ;(db->lin (list! (gains sox-input))) -> but it's always going to be a list, no?
333                               (setf str (string+ str (format nil " -v~d ~s " gain filename ))))
334                       ;)
335
336                     (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
337                                     ((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p")) ; "-p is an alias for '-t sox -' " and it's better but I can't use as I need to provide the sox path again...
338                                     (t (sox-samplebits str bitdepth samplerate outfile))))
339
340                     (setf str (string+ str sox-effect))
341
342                     (sox-out str sox-input output outfile recursive))))
343
344               (sox-not-found))
345             )
346
347 ; === sox-multiply =============
348
349 (defmethod! sox-process ((sox-input sox-multiply) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
350             (if (probe-file *sox-path*)
351
352                  (progn
353                   (when (and (find-if 'stringp (sound sox-input)) (equal output "pipe"))
354                     (om-message-abort "Pipe output not possible with this type of input."))
355
356                   (let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
357
358                     (let* ((filenames 
359                             (loop for soundfile in (sound sox-input) collect ;I guess this throws the error when using pipes as inputs
360                                   (namestring soundfile))))
361
362                       (setf str (format nil "~s ~a -T " (namestring *sox-path*) *sox-options*))
363                                                           
364                       (loop for filename in filenames do
365                               for gain in (db->lin (gains sox-input)) do 
366                               (setf str (string+ str (format nil " -v~d ~s " gain filename )))) ; note that "-v" suppresses sox's in-built clipping protection
367                       ;)
368
369                     (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
370                                     ((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
371                                     (t (sox-samplebits str bitdepth samplerate outfile))))
372
373                     (setf str (string+ str sox-effect))
374
375                     (sox-out str sox-input output outfile recursive))))
376
377               (sox-not-found))
378             )
379
380
381 ; === sox-concatenate =============
382
383 (defmethod! sox-process ((sox-input sox-concatenate) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
384             (if (probe-file *sox-path*)
385
386                 (progn
387                   (when (and (find-if 'stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
388
389                   (let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
390
391                     (let* ((filenames (loop for soundfile in (sound sox-input) collect
392                                             (namestring soundfile))))    
393                                
394                       (setf str (format nil "~s ~a " (namestring *sox-path*) *sox-options*))
395                                   
396                       (if (gains sox-input)
397                           (loop for filename in filenames do
398                                 for gain in (db->lin (list! (gains sox-input))) do
399                                 (setf str (string+ str (format nil " -v~d ~s  " gain filename ))))
400                         ;I think this case never happens
401                         (loop for filename in filenames do 
402                               (setf str (string+ str (format nil " ~s" filename)))))
403                       
404                       (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
405                                       ((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
406                                       (t (sox-samplebits str bitdepth samplerate outfile))))
407
408                       (setf str (string+ str sox-effect))
409                       (sox-out str sox-input output outfile recursive))))
410
411               (sox-not-found))
412             )
413
414 ; === sox-splice ============
415
416 (defmethod! sox-process ((sox-input sox-splice) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
417             (if (probe-file *sox-path*)
418              
419               (progn
420                   (when (and (find-if 'stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
421
422                   (let ((outfile (create-path nil output filetype)))
423
424                     (let* ((mysoundfiles (sound sox-input))
425                            (filenames (loop for soundfile in mysoundfiles collect (namestring soundfile)))
426                            (first-dur (sox-sound-duration (car (sound sox-input))))
427                            (begins (list! (splice-begin sox-input)))
428                            (ends (list! (splice-end sox-input)))
429                            (tols (list! (tolerance sox-input))))
430
431                       (sox-print (format nil "begins: ~a" begins))
432                       (sox-print (format nil "ends: ~a" ends))
433                       (sox-print (format nil "tols: ~a" tols))
434
435                       (setf str (format nil "~s ~a " (namestring *sox-path*) *sox-options*))
436                       
437                       (loop for filename in filenames do
438                             for gain in (db->lin (list! (gains sox-input))) do
439                             (setf str (string+ str (format nil " -v~d ~s " gain filename ))))
440
441                     (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
442                                     ((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
443                                     (t (sox-samplebits str bitdepth samplerate outfile))))
444
445                     (setf str 
446                           (cond ((eql (fade-type sox-input) 'linear)  (string+ str (format nil " splice -t " )))
447                                 ((eql (fade-type sox-input) 'half-cosine)  (string+ str (format nil " splice -h " )))
448                                 ((eql (fade-type sox-input) 'quarter-cosine)  (string+ str  (format nil " splice -q " )))
449                                 (t  (string+ str  (format nil " splice " )))))
450
451                     ;dbl-check the sum-calculation. Something's fishy!
452
453                     (loop for mysound in (sound sox-input) 
454                           for end in ends 
455                           counting end into ordinal
456                           summing (or (sox-sound-duration mysound) 0) into dur-sum
457                           do
458                           (progn (sox-print ordinal)
459                           (let ((i (- ordinal 1))
460                                 (current-sum (- dur-sum (or first-dur 0))))
461                             
462                             (sox-print (format nil "begins: ~d" (nth i begins)))
463                             (sox-print (format nil "ends: ~d" (nth i ends)))
464                             (sox-print (format nil "current-sum: ~d" current-sum))
465                             
466                           (cond ((and (nth i begins) (nth i tols))
467                                  (progn 
468                                    (sox-print "begin+tol") 
469                                    (setf str (string+ str (format nil "~d,~d,~d " (+ current-sum (nth i ends))  (* 0.5 (- (nth i ends) (nth i begins))) (nth i tols))))))
470                                 ((nth i begins)
471                                  (progn 
472                                    (print "begin") 
473                                    (setf str (string+ str (format nil "~d,~d " (+ current-sum (nth i ends)) (* 0.5 (- (nth i ends) (nth i begins))))))))
474                                 (t (progn 
475                                      (print "end")
476                                      (setf str (string+ str (format nil "~d " (+ current-sum (nth i ends))))))))
477                           )))
478
479                     (setf str (sox-commands sox-effect str))
480                     (sox-out str sox-input output outfile recursive)
481                     )))
482             (sox-not-found))
483             )
484
485
486 ; === sox-remix =============
487
488 (defmethod! sox-process ((sox-input sox-remix) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
489            ;(print (first (channels sox-input)))
490             (if (probe-file *sox-path*)
491
492                 (progn
493                   (when (and (stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
494
495                   (let* ((inpath (sound sox-input))
496                          (outfile (create-path inpath output filetype)) ;(create-path nil output filetype)
497                          (channel-matrix (list! (channel-matrix sox-input)))) ; is this still needed?
498                     
499                     (setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* (namestring inpath)))
500                    ; (print (channels sox-input))
501                    ; (print (gains sox-input))
502
503                     (when bitdepth (setf str (string+ str (format nil " -b ~d" bitdepth)))) ;could I not use sox-samplebits here?
504                     (setf str (string+ str (format nil " ~s remix ~a" 
505                                                    (cond ((equal output "realtime")  *sox-audio-device*) ;should add a quiet mode here...
506                                                          ((equal output "pipe") "-p")
507                                                          (t (namestring outfile)))
508                                                    (sox-remixconc-no-norm (gain-matrix sox-input) (channel-matrix sox-input))
509                                                    )))
510
511                     (when samplerate (setf str (string+ str (format nil " rate ~d " samplerate))))
512                     (setf str (string+ str sox-effect))
513                     (sox-out str inpath output outfile recursive)))
514             (sox-not-found))
515             )
516
517 ; === sox-pan ====================
518
519 (defmethod! sox-process ((sox-input sox-pan) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
520            ;(print (first (channels sox-input)))
521             (sox-process (sox-pan->sox-remix (sound sox-input) (car (list! (gains sox-input))) (car (list! (panning sox-input))) (numchannels sox-input))
522                          sox-effect
523                          :output output
524                          :filetype filetype
525                          :samplerate samplerate
526                          :bitdepth bitdepth
527                          :recursive recursive
528                          :batch-mode batch-mode))
529
530
531 ; === sox-mix-console =============
532
533
197ce0 534 ; Notes on polymorphism
92c40d 535 ; I could make this sox-mix-console (non-consistent) a method where a list of effects will be applied to each sound in the mix-console
AN 536 ;   OR: keep this as an extra function for the player
537 ; I can't use pipe-input to the mix-console as it makes sox-remix instances with pipe-outputs ->is a pipe in a pipe not possible?
538
539
540 (defmethod! sox-process ((sox-input sox-mix-console) (sox-effect t) &key output filetype samplerate bitdepth recursive batch-mode)
541             (if (probe-file *sox-path*)
542
543                 (progn
544                   ;(print "sox-process: sox-mix-console")
545                   (when (and (stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
546
547                   (let* ((inpath (sound sox-input))
548                          (outfile (create-path inpath output filetype)) ;(create-path nil output filetype)  ; Filetype MUST NOT be pipe (it can be only a sound)
549
550                          (list-of-pans (loop for path in (list! inpath)
551                                for gain in (gains sox-input)
552                                for pan in (panning sox-input) collect
553                                (make-instance 'sox-pan 
554                                               :sound path
555                                               :gains gain
556                                               :panning pan)))
557                          
558                          (list-of-pipes (loop for sox-pan in list-of-pans 
559                                               for i from 0 to (length list-of-pans) collect 
560                                               (sox-process sox-pan (or (nth i (list! sox-effect)) "") ;note that in this special case a list of effects is applied to each in-sound
561                                                            :output "pipe"
562                                                            :filetype filetype 
563                                                            :samplerate samplerate 
564                                                            :bitdepth bitdepth
565                                                            :recursive recursive 
566                                                            :batch-mode batch-mode)))
567                          )
568                     (if (> (length list-of-pipes) 1)
569                         (sox-process (make-instance 'sox-mix :sound list-of-pipes) ""
570                                      :output output
571                                      :filetype filetype 
572                                      :samplerate samplerate 
573                                      :bitdepth bitdepth
574                                      :recursive recursive 
575                                      :batch-mode batch-mode
576                                      )
577                       (sox-process list-of-pipes ""
578                                      :output output
579                                      :filetype filetype 
580                                      :samplerate samplerate 
581                                      :bitdepth bitdepth
582                                      :recursive recursive 
583                                      :batch-mode batch-mode
584                                      ))
585                       ))
586               (sox-not-found)
587               ))
588
589
590
591
592 ; === sox-split =============
593 ; special method with (sox-effect t) only for sox-split (returns a sequence of sox-remixes)
594
595 (defmethod! sox-process ((sox-input sox-split) (sox-effect t) &key output filetype samplerate bitdepth recursive batch-mode)
596             (if (probe-file *sox-path*)
597
598                 (progn
599                   (cond ((equal output "replace file") (om-message-abort "Replace output not possible with sox-split."))
600                         ((and (equal output "pipe") (stringp (sound sox-input))) (om-message-abort "Pipe output not possible with this type of input")))
601                   
602                   (let*
603                       ((thesoundfile (sound sox-input))
604                        (list-of-remixes (loop for channel in (channels sox-input)
605                                               for gain in (gains sox-input) collect
606                                               (make-instance 'sox-remix
607                                                              :sound thesoundfile
608                                                              :gain-matrix (list gain)
609                                                              :channel-matrix (list (list channel)))))
610                      ;here the 'realtime' and 'pipe' ??
611                        (list-of-outpaths  (loop for channel in (channels sox-input) collect
612                                                 (format nil "~a_omsox-ch~d" (pathname-name thesoundfile) channel))
613                                           ))
614                     
615                     (sox-process list-of-remixes sox-effect 
616                                  :output (or output list-of-outpaths)
617                                  :filetype filetype 
618                                  :samplerate samplerate 
619                                  :bitdepth bitdepth 
620                                  :recursive recursive 
621                                  :batch-mode batch-mode)))
622
623               (sox-not-found))
624             )
625
626
627
628
629
630 ; === sox-record =============
631
632 (defmethod! sox-process ((sox-input sox-record) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
633             (if (probe-file *sox-path*)
634
635                 (let ((outfile (create-path nil output filetype)))                         
636                   
637                   (setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* *sox-audio-device*)) ;perhaps sox-recording vs playback-device?
638                   
639                   (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
640                                   ((equal output "pipe") (sox-samplebits (string+ str " -q ") bitdepth samplerate "-p"))
641                                   (t (sox-samplebits (string+ str " -q ") bitdepth samplerate outfile))))
642                   
643                   (setf str (string+ str (format nil " trim 0 ~d " (duration sox-input))))
644                   
645                   (cond ((and (channels sox-input) (gains sox-input))
646                          (let ((newstr (format nil " remix" )))
647                            (loop for channel in (channels sox-input)
648                                  for gain in (gains sox-input) do
649                                  (setf newstr (string+ newstr (format nil " ~av~d " channel (db->lin gain)))))                                                   
650                            (setf str (string+ str newstr))))
651                         
652                         ((gains sox-input)
653                          (let ((newstr (format nil " remix" )))
654                            (loop for item from 1 to (length (gains sox-input))
655                                  for gain in (gains sox-input) do
656                                  (setf newstr (string+ newstr (format nil " ~av~d " item (db->lin gain)))))
657                            (setf str (string+ str newstr))))
658                         
659                         ((channels sox-input)
660                          (let ((newstr (format nil " remix" )))
661                            (loop for channel in (channels sox-input) do
662                                  (setf newstr (string+ newstr (format nil " ~a" channel))))                                                   
663                            (setf str (string+ str newstr))))
664                         )
665
666                   (setf str (string+ str sox-effect))       
667
668                   (sox-out str sox-input output outfile recursive))
669                     
670               (sox-not-found))
671             )
672
673
674 ; === sox synth =========
675 #|
676 (defmethod! sox-process ((sox-input sox-synth) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
677             (if (probe-file *sox-path*)
678
679                 (let ((outfile (create-path nil output filetype)))
680                   (setf str (format nil "~s ~a -n" (namestring *sox-path*) *sox-options*))
681                   (setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
682                                   ((equal output "pipe") (sox-samplebits str bitdepth samplerate '-p))
683                                   (t (sox-samplebits str bitdepth samplerate outfile))))
684                   (setf str (string+ str " synth" (sox-synth-format sox-input)))
685                   (setf str (string+ str sox-effect))
686                   (sox-out str sox-input output outfile recursive))
687               
688               (sox-not-found))
689             )          
690 |#
691 ; %%%%%%%%%%%% LIST METHODS %%%%%%%%%%%%%%%%%%%%%%
692
693 ; for string + list + list/string
694 (defmethod! sox-process ((sox-input t) (sox-effect list) &key output filetype samplerate bitdepth recursive batch-mode)
695             (if (and (listp output) (first output))
696                 (mapcar (lambda (thesox-effect thepath)
697                           (sox-process sox-input thesox-effect :output thepath :filetype filetype :samplerate samplerate 
698                                        :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-effect output)
699               (mapcar (lambda (thesox-effect)
700                         (sox-process sox-input thesox-effect :output output :filetype filetype :samplerate samplerate 
701                                      :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-effect)))
702
703 ; these functions below can be grouped together into multiple if/when statements for the different types of 'output'
704 ;--------------------------------------------------------------------------------------------------------------------
705 ; for list + string + string
706 (defmethod! sox-process ((sox-input list) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
707             (mapcar (lambda (file) 
708                       (sox-process file sox-effect :output output :filetype filetype :samplerate samplerate 
709                                    :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input))
710
711 ; #### NEED TO MAKE AN EXAMPLE FOR THIS CASE // ALSO, THE LIST LIST FUNCTION method seems to be missing.. PROBABLY FOR AUTO_NAMING FILES (batch processing)
712 ; for list + string + function
713 (defmethod! sox-process ((sox-input list) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
714             (if (functionp output)
715                 ; these 2 cases seem to be identical 
716                 (mapcar (lambda (file) 
717                           (sox-process file sox-effect :output output :filetype filetype :samplerate samplerate 
718                                        :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input)
719               (mapcar (lambda (file) 
720                         (sox-process file sox-effect :output output :filetype filetype :samplerate samplerate 
721                                      :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input)))
722
723 ; for list + string + list 
724 (defmethod! sox-process ((sox-input list) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
725             (if (and (listp output) (first output))
726                 (mapcar (lambda (file thepath) 
727                           (sox-process file sox-effect :output thepath :filetype filetype :samplerate samplerate 
728                                        :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input output)
729               (mapcar (lambda (file) 
730                         (sox-process file sox-effect :output output :filetype filetype :samplerate samplerate 
731                                      :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input)))
732
733 ; ---------------------------------------------------------------------------------------------------------------------
734
735 ; for list + list + string/list
736 (defmethod! sox-process ((sox-input list) (sox-effect list) &key output filetype samplerate bitdepth recursive batch-mode)
737             (let (
738                   (numsounds (length sox-input))
739                   (numsox-effect (length sox-effect)))
740              
741               (when (> numsounds numsox-effect)
742                 (progn
743                   (when (equal batch-mode 'cycle)
744                     (setf sox-effect
745                           (flat (group-list sox-effect (list numsounds) 'circular))))
746                  (when (equal batch-mode 'repeat)
747                     (setf sox-effect
748                           (flat (x-append sox-effect (repeat-n (last sox-effect) (- numsounds numsox-effect))))))))
749
750               (if (consp output)
751                   (mapcar (lambda (file thesox-effect thepaths)
752                             (sox-process file thesox-effect :output thepaths :filetype filetype :samplerate samplerate 
753                                          :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input sox-effect output)
754                 (mapcar (lambda (file thesox-effect)
755                             (sox-process file thesox-effect :output output :filetype filetype :samplerate samplerate 
756                                          :bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input sox-effect))))
757
758
759 ; for anything else beep ------------------
760 (defmethod! sox-process ((sox-input t) (sox-effect t) &key output filetype samplerate bitdepth recursive batch-mode)
761     (om-beep-msg (format nil "!!! Wrong input type or command for sox-process: ~A with ~A" sox-input sox-effect)))
762
763