;*********************************************************************
|
; OM-SoX, (c) 2011-2014 Marlon Schumacher (CIRMMT/McGill University) *
|
; http://sourceforge.net/projects/omsox/ *
|
; *
|
; Multichannel Audio Manipulation and Functional Batch Processing. *
|
; DSP based on SoX - (c) C.Bagwell and Contributors *
|
; http://sox.sourceforge.net/ *
|
;*********************************************************************
|
;
|
;This program is free software; you can redistribute it and/or
|
;modify it under the terms of the GNU General Public License
|
;as published by the Free Software Foundation; either version 2
|
;of the License, or (at your option) any later version.
|
;
|
;See file LICENSE for further informations on licensing terms.
|
;
|
;This program is distributed in the hope that it will be useful,
|
;but WITHOUT ANY WARRANTY; without even the implied warranty of
|
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
;GNU General Public License for more details.
|
;
|
;You should have received a copy of the GNU General Public License
|
;along with this program; if not, write to the Free Software
|
;Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,10 USA.
|
;
|
;Authors: M. Schumacher
|
|
(in-package :om)
|
|
|
; %%%%%%%%%%%%%%% SOX-PROCESS %%%%%%%%%%%%%%%%%%%%
|
; Main Processing Function
|
|
;== method for pathname + string =======
|
(defmethod! sox-process ((sox-input pathname) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
:icon 08
|
:initvals '(nil "" "realtime" nil nil nil Off "break")
|
:menuins (list (list 2 *sox-output-options*)
|
(list 3 *sox-file-formats*)
|
(list 4 *sox-samplerates*)
|
(list 5 *sox-bitdephts*)
|
(list 6 '(("On" On) ("Off" Off)))
|
(list 7 '(("break" break) ("repeat" repeat) ("cycle" cycle))))
|
:indoc '("Audio input to be processed [sound, path, string/pipe, sox-input]"
|
"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]"
|
"Filetype of produced audio [string]" "Samplerate of produced audio [string]" "Bitdepth of produced audio [number]"
|
"recursive (when 'on' applies processing recursively to audio) [symbol]" "Mode for batch-processing (break, repeat, cycle) [symbol]")
|
:doc "Main audio processing function for OM-SoX. Takes audio provided in <sox-input> and processes it with sox-effects provided in <sox-effect>.
|
|
<output> determines whether output is to be rendered into
|
a) new file,
|
b) replace the input file,
|
c) create a sox-command as a pipe (for further processing),
|
d) play back in realtime through audio device.
|
|
Alternatively, a filename, directory, or path can be provided which will write an audio file to the given destination.
|
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.
|
This can be used to algorithmically name output files (e.g. in batch processes).
|
|
Optionally, the audio output <filetype> can be specified, as well as the <samplerate> and <bitdepth> (default: same as input).
|
<recursive> is an experimental option allowing to apply a sox-effect recursively to audio input (e.g. trimming).
|
<batch-mode> determines the behaviour when processing lists of sox-inputs and sox-effects that differ in length.
|
"
|
|
(if (probe-file *sox-path*)
|
|
(let ((outfile (create-path sox-input output filetype)))
|
|
(setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* (namestring sox-input)))
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits str bitdepth samplerate '-p))
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
|
(setf str (string+ str sox-effect))
|
|
; optional removal of temp-files
|
(if *delete-inter-file*
|
(let ((outstring (sox-out str sox-input output outfile recursive)))
|
(if (>= *om-version* 6.07)
|
(om-run-process "cleantempfiles" #'sleep&clean 5)
|
(om-run-process "cleantempfiles" sleep&clean 5))
|
outstring)
|
(sox-out str sox-input output outfile recursive)
|
))
|
(sox-not-found))
|
)
|
|
|
;== method for sound + string =======
|
(defmethod! sox-process ((sox-input sound) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(let ((gainval (sox-vol-convert (vol sox-input)))
|
(panval (+ (tracknum sox-input) (* 0.01 (pan sox-input)))))
|
(if (equal (tracknum sox-input) 0)
|
(sox-process (sound-path sox-input) (string+ sox-effect " gain " (makestring gainval))
|
:output output :filetype filetype :samplerate samplerate :bitdepth bitdepth
|
:recursive recursive :batch-mode batch-mode)
|
(sox-process (make-instance 'sox-pan
|
:sound sox-input
|
:gains gainval
|
:panning panval
|
:numchannels nil)
|
sox-effect
|
:output output :filetype filetype :samplerate samplerate :bitdepth bitdepth
|
:recursive recursive :batch-mode batch-mode)))
|
)
|
|
|
;== method for string + string =======
|
;-> allows for arbitrary 'manual' sox-effect or options or pipe inputs...
|
(defmethod! sox-process ((sox-input string) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
;catch exception ---------
|
(if (and (pipe-p sox-input) (equal output "pipe") )
|
(om-message-abort "Pipe output not possible with this type of input.")
|
; ------------------------
|
|
(let ((outfile (create-path nil output filetype)))
|
|
(setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* sox-input))
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits str bitdepth samplerate '-p))
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
|
(setf str (string+ str sox-effect))
|
|
(if *delete-inter-file*
|
(let ((outstring (sox-out str sox-input output outfile recursive)))
|
(if (>= *om-version* 6.07)
|
(om-run-process "cleantempfiles" #'sleep&clean 5)
|
(om-run-process "cleantempfiles" sleep&clean 5))
|
outstring)
|
(sox-out str sox-input output outfile recursive)
|
)))
|
|
(sox-not-found))
|
)
|
|
; %%%%%%%% METHODS FOR SOX-INPUT CLASSES %%%%%%%%%%%%
|
|
; === sox-merge =============
|
|
(defmethod! sox-process ((sox-input sox-merge) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(progn
|
;(print (sound sox-input))
|
(let* ((sound (if (> (length (gains sox-input)) (length (list! (sound sox-input))))
|
(repeat-n (first (list! (sound sox-input))) (length (gains sox-input)))
|
(sound sox-input))))
|
|
(when (and (find-if 'stringp sound) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
|
|
(let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
|
|
(let* ((filenames (loop for soundfile in sound collect
|
(namestring soundfile))))
|
|
(setf str (format nil "~s ~a -M " (namestring *sox-path*) *sox-options* ))
|
|
(if (gains sox-input)
|
(loop for filename in filenames do
|
for gain in (db->lin (list! (gains sox-input))) do
|
(setf str (string+ str (format nil " -v~d ~s " gain filename))))
|
(loop for filename in filenames do
|
(setf str (string+ str (format nil " ~s" filename)))))
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
|
(setf str (string+ str sox-effect))
|
|
(sox-out str sox-input output outfile recursive)))))
|
|
(sox-not-found))
|
)
|
|
; === sox-hoaencode =============
|
|
(defmethod! sox-process ((sox-input sox-hoaencode) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
|
; would be more consistent with OO programming to have a function sox-hoaencode->sox-merge
|
; (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.
|
; in that case a 3DC would have to be used by using points (point-pairs) in a loop
|
|
(if (probe-file *sox-path*)
|
(progn
|
;(unless (listp (sound sox-input)) (setf (positions sox-input) (list (positions sox-input)))) ; quick hack to prevent having to provide position arg as list of lists
|
#|
|
(when
|
(not
|
(or
|
(subtypep (type-of (positions sox-input)) '3dc)
|
(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))
|
)
|
)
|
(om-message-abort "positions must be of type 3dc, or a list of lists.")
|
)
|
|#
|
(let* ((sound (list! (sound sox-input)))
|
(positions-ae (list (azimuth sox-input) (elevation sox-input))))
|
|
(when (and (find-if 'stringp sound) (equal output "pipe"))
|
(om-message-abort "Pipe output not possible with this type of input."))
|
|
(let* ((filenames (loop for soundfile in sound collect (namestring soundfile)))
|
(outfile (create-path nil output filetype)))
|
|
(setf str (format nil " ~s ~a" (namestring *sox-path*) *sox-options*))
|
(if (= (length filenames) 1)
|
; case: one file
|
(let* ((filename (first filenames))
|
(position positions-ae))
|
(if (= (order sox-input) 0)
|
; case: order = 0
|
(setf str (string+ str (format nil " ~s" filename)))
|
; case: order > 0
|
(progn ; here it would be more redundant to call sox-merge with the given params
|
(setf str (string+ str " -M"))
|
(loop for gain in (sox-hoaencode-gains-up-to-order (order sox-input) (first position) (second position))
|
do (setf str (string+ str (format nil " -v~d ~s" gain filename))))
|
)
|
)
|
)
|
; case: multiple files
|
#|
|
(if (= (order sox-input) 0)
|
; case: order = 0
|
(progn
|
(setf str (string+ str " -m")) ; this is the mixer. e.g. sox-mix
|
(loop for filename in filenames
|
do (setf str (string+ str (format nil " ~s" filename))))
|
)
|
; case: order > 0
|
(progn
|
(setf str (string+ str " -m"))
|
|
; Create components
|
(setf component_idx 0)
|
(loop
|
for filename in filenames
|
for position in positions-ae
|
do
|
|
; in Object-Oriented Programming we would call-next-method i.e. set a sox-merge input class with the corresponding params.
|
; e.g. (make-instance 'sox-merge ... (more redundancy and compactness, less error-prone) ... possible?
|
; for multiple sound inputs if possible we would then call a sox-mix to mix the sox merge pipes. everything
|
|
(progn
|
(setf str_component (format nil " ~s ~a -M" (namestring *sox-path*) *sox-options*))
|
(loop for gain in (sox-hoaencode-gains-up-to-order (order sox-input) (first position) (second position))
|
do (setf str_component (string+ str_component (format nil " -v~d ~s" gain filename))))
|
|
(setf outfile_tmp (format nil "~a~a-~d.~a" (directory-namestring outfile) (pathname-name outfile) component_idx (pathname-type outfile)))
|
(setf str_component (sox-samplebits str_component bitdepth samplerate outfile_tmp))
|
(sox-out str_component filename "new file" outfile_tmp nil)
|
|
(setf component_idx (1+ component_idx))
|
|
; Add component path to <str>
|
(setf str (string+ str (format nil " ~s" outfile_tmp)))
|
)
|
)
|
)
|
)
|
|#
|
)
|
(setf str
|
(cond
|
((equal output "realtime")
|
(sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe")
|
(sox-samplebits str bitdepth samplerate "-p"))
|
(t
|
(sox-samplebits str bitdepth samplerate outfile))
|
)
|
)
|
(setf str (string+ str sox-effect))
|
(print str)
|
(sox-out str sox-input output outfile recursive)
|
|
;optional removal of temp file
|
; (add-tmp-file outfile_tmp)
|
; (when *delete-inter-file* (clean-tmp-files))
|
|
)
|
)
|
)
|
(sox-not-found)
|
)
|
)
|
|
; === sox-mix =============
|
|
(defmethod! sox-process ((sox-input sox-mix) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(progn
|
(when (and (find-if 'stringp (sound sox-input)) (equal output "pipe"))
|
(om-message-abort "Pipe output not possible with this type of input."))
|
|
(let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
|
|
(let* ((filenames
|
(loop for soundfile in (sound sox-input) collect ;I guess this throws the error when using pipes as inputs
|
(namestring soundfile))))
|
|
(setf str (format nil "~s ~a -m " (namestring *sox-path*) *sox-options*))
|
|
;potentially not needed...
|
;(if (1/n sox-input)
|
|
; (loop for filename in filenames do
|
; 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?
|
; (setf str (string+ str (format nil " -v~d ~s " gain filename )))) ; is it not scaling when volumes are given? try out!
|
|
(loop for filename in filenames do
|
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?
|
(setf str (string+ str (format nil " -v~d ~s " gain filename ))))
|
;)
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((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...
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
|
(setf str (string+ str sox-effect))
|
|
(sox-out str sox-input output outfile recursive))))
|
|
(sox-not-found))
|
)
|
|
; === sox-multiply =============
|
|
(defmethod! sox-process ((sox-input sox-multiply) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(progn
|
(when (and (find-if 'stringp (sound sox-input)) (equal output "pipe"))
|
(om-message-abort "Pipe output not possible with this type of input."))
|
|
(let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
|
|
(let* ((filenames
|
(loop for soundfile in (sound sox-input) collect ;I guess this throws the error when using pipes as inputs
|
(namestring soundfile))))
|
|
(setf str (format nil "~s ~a -T " (namestring *sox-path*) *sox-options*))
|
|
(loop for filename in filenames do
|
for gain in (db->lin (gains sox-input)) do
|
(setf str (string+ str (format nil " -v~d ~s " gain filename )))) ; note that "-v" suppresses sox's in-built clipping protection
|
;)
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
|
(setf str (string+ str sox-effect))
|
|
(sox-out str sox-input output outfile recursive))))
|
|
(sox-not-found))
|
)
|
|
|
; === sox-concatenate =============
|
|
(defmethod! sox-process ((sox-input sox-concatenate) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(progn
|
(when (and (find-if 'stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
|
|
(let ((outfile (create-path nil output filetype))) ;(create-path (first (soundfiles sox-input)) output filetype)
|
|
(let* ((filenames (loop for soundfile in (sound sox-input) collect
|
(namestring soundfile))))
|
|
(setf str (format nil "~s ~a " (namestring *sox-path*) *sox-options*))
|
|
(if (gains sox-input)
|
(loop for filename in filenames do
|
for gain in (db->lin (list! (gains sox-input))) do
|
(setf str (string+ str (format nil " -v~d ~s " gain filename ))))
|
;I think this case never happens
|
(loop for filename in filenames do
|
(setf str (string+ str (format nil " ~s" filename)))))
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
|
(setf str (string+ str sox-effect))
|
(sox-out str sox-input output outfile recursive))))
|
|
(sox-not-found))
|
)
|
|
; === sox-splice ============
|
|
(defmethod! sox-process ((sox-input sox-splice) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(progn
|
(when (and (find-if 'stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
|
|
(let ((outfile (create-path nil output filetype)))
|
|
(let* ((mysoundfiles (sound sox-input))
|
(filenames (loop for soundfile in mysoundfiles collect (namestring soundfile)))
|
(first-dur (sox-sound-duration (car (sound sox-input))))
|
(begins (list! (splice-begin sox-input)))
|
(ends (list! (splice-end sox-input)))
|
(tols (list! (tolerance sox-input))))
|
|
(sox-print (format nil "begins: ~a" begins))
|
(sox-print (format nil "ends: ~a" ends))
|
(sox-print (format nil "tols: ~a" tols))
|
|
(setf str (format nil "~s ~a " (namestring *sox-path*) *sox-options*))
|
|
(loop for filename in filenames do
|
for gain in (db->lin (list! (gains sox-input))) do
|
(setf str (string+ str (format nil " -v~d ~s " gain filename ))))
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits str bitdepth samplerate "-p"))
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
|
(setf str
|
(cond ((eql (fade-type sox-input) 'linear) (string+ str (format nil " splice -t " )))
|
((eql (fade-type sox-input) 'half-cosine) (string+ str (format nil " splice -h " )))
|
((eql (fade-type sox-input) 'quarter-cosine) (string+ str (format nil " splice -q " )))
|
(t (string+ str (format nil " splice " )))))
|
|
;dbl-check the sum-calculation. Something's fishy!
|
|
(loop for mysound in (sound sox-input)
|
for end in ends
|
counting end into ordinal
|
summing (or (sox-sound-duration mysound) 0) into dur-sum
|
do
|
(progn (sox-print ordinal)
|
(let ((i (- ordinal 1))
|
(current-sum (- dur-sum (or first-dur 0))))
|
|
(sox-print (format nil "begins: ~d" (nth i begins)))
|
(sox-print (format nil "ends: ~d" (nth i ends)))
|
(sox-print (format nil "current-sum: ~d" current-sum))
|
|
(cond ((and (nth i begins) (nth i tols))
|
(progn
|
(sox-print "begin+tol")
|
(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))))))
|
((nth i begins)
|
(progn
|
(print "begin")
|
(setf str (string+ str (format nil "~d,~d " (+ current-sum (nth i ends)) (* 0.5 (- (nth i ends) (nth i begins))))))))
|
(t (progn
|
(print "end")
|
(setf str (string+ str (format nil "~d " (+ current-sum (nth i ends))))))))
|
)))
|
|
(setf str (sox-commands sox-effect str))
|
(sox-out str sox-input output outfile recursive)
|
)))
|
(sox-not-found))
|
)
|
|
|
; === sox-remix =============
|
|
(defmethod! sox-process ((sox-input sox-remix) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
;(print (first (channels sox-input)))
|
(if (probe-file *sox-path*)
|
|
(progn
|
(when (and (stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
|
|
(let* ((inpath (sound sox-input))
|
(outfile (create-path inpath output filetype)) ;(create-path nil output filetype)
|
(channel-matrix (list! (channel-matrix sox-input)))) ; is this still needed?
|
|
(setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* (namestring inpath)))
|
; (print (channels sox-input))
|
; (print (gains sox-input))
|
|
(when bitdepth (setf str (string+ str (format nil " -b ~d" bitdepth)))) ;could I not use sox-samplebits here?
|
(setf str (string+ str (format nil " ~s remix ~a"
|
(cond ((equal output "realtime") *sox-audio-device*) ;should add a quiet mode here...
|
((equal output "pipe") "-p")
|
(t (namestring outfile)))
|
(sox-remixconc-no-norm (gain-matrix sox-input) (channel-matrix sox-input))
|
)))
|
|
(when samplerate (setf str (string+ str (format nil " rate ~d " samplerate))))
|
(setf str (string+ str sox-effect))
|
(sox-out str inpath output outfile recursive)))
|
(sox-not-found))
|
)
|
|
; === sox-pan ====================
|
|
(defmethod! sox-process ((sox-input sox-pan) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
;(print (first (channels sox-input)))
|
(sox-process (sox-pan->sox-remix (sound sox-input) (car (list! (gains sox-input))) (car (list! (panning sox-input))) (numchannels sox-input))
|
sox-effect
|
:output output
|
:filetype filetype
|
:samplerate samplerate
|
:bitdepth bitdepth
|
:recursive recursive
|
:batch-mode batch-mode))
|
|
|
; === sox-mix-console =============
|
|
|
; Notes on polymorphism
|
; 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
|
; OR: keep this as an extra function for the player
|
; 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?
|
|
|
(defmethod! sox-process ((sox-input sox-mix-console) (sox-effect t) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(progn
|
;(print "sox-process: sox-mix-console")
|
(when (and (stringp (sound sox-input)) (equal output "pipe")) (om-message-abort "Pipe output not possible with this type of input."))
|
|
(let* ((inpath (sound sox-input))
|
(outfile (create-path inpath output filetype)) ;(create-path nil output filetype) ; Filetype MUST NOT be pipe (it can be only a sound)
|
|
(list-of-pans (loop for path in (list! inpath)
|
for gain in (gains sox-input)
|
for pan in (panning sox-input) collect
|
(make-instance 'sox-pan
|
:sound path
|
:gains gain
|
:panning pan)))
|
|
(list-of-pipes (loop for sox-pan in list-of-pans
|
for i from 0 to (length list-of-pans) collect
|
(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
|
:output "pipe"
|
:filetype filetype
|
:samplerate samplerate
|
:bitdepth bitdepth
|
:recursive recursive
|
:batch-mode batch-mode)))
|
)
|
(if (> (length list-of-pipes) 1)
|
(sox-process (make-instance 'sox-mix :sound list-of-pipes) ""
|
:output output
|
:filetype filetype
|
:samplerate samplerate
|
:bitdepth bitdepth
|
:recursive recursive
|
:batch-mode batch-mode
|
)
|
(sox-process list-of-pipes ""
|
:output output
|
:filetype filetype
|
:samplerate samplerate
|
:bitdepth bitdepth
|
:recursive recursive
|
:batch-mode batch-mode
|
))
|
))
|
(sox-not-found)
|
))
|
|
|
|
|
; === sox-split =============
|
; special method with (sox-effect t) only for sox-split (returns a sequence of sox-remixes)
|
|
(defmethod! sox-process ((sox-input sox-split) (sox-effect t) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(progn
|
(cond ((equal output "replace file") (om-message-abort "Replace output not possible with sox-split."))
|
((and (equal output "pipe") (stringp (sound sox-input))) (om-message-abort "Pipe output not possible with this type of input")))
|
|
(let*
|
((thesoundfile (sound sox-input))
|
(list-of-remixes (loop for channel in (channels sox-input)
|
for gain in (gains sox-input) collect
|
(make-instance 'sox-remix
|
:sound thesoundfile
|
:gain-matrix (list gain)
|
:channel-matrix (list (list channel)))))
|
;here the 'realtime' and 'pipe' ??
|
(list-of-outpaths (loop for channel in (channels sox-input) collect
|
(format nil "~a_omsox-ch~d" (pathname-name thesoundfile) channel))
|
))
|
|
(sox-process list-of-remixes sox-effect
|
:output (or output list-of-outpaths)
|
:filetype filetype
|
:samplerate samplerate
|
:bitdepth bitdepth
|
:recursive recursive
|
:batch-mode batch-mode)))
|
|
(sox-not-found))
|
)
|
|
|
|
|
|
; === sox-record =============
|
|
(defmethod! sox-process ((sox-input sox-record) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(let ((outfile (create-path nil output filetype)))
|
|
(setf str (format nil "~s ~a ~s" (namestring *sox-path*) *sox-options* *sox-audio-device*)) ;perhaps sox-recording vs playback-device?
|
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits (string+ str " -q ") bitdepth samplerate "-p"))
|
(t (sox-samplebits (string+ str " -q ") bitdepth samplerate outfile))))
|
|
(setf str (string+ str (format nil " trim 0 ~d " (duration sox-input))))
|
|
(cond ((and (channels sox-input) (gains sox-input))
|
(let ((newstr (format nil " remix" )))
|
(loop for channel in (channels sox-input)
|
for gain in (gains sox-input) do
|
(setf newstr (string+ newstr (format nil " ~av~d " channel (db->lin gain)))))
|
(setf str (string+ str newstr))))
|
|
((gains sox-input)
|
(let ((newstr (format nil " remix" )))
|
(loop for item from 1 to (length (gains sox-input))
|
for gain in (gains sox-input) do
|
(setf newstr (string+ newstr (format nil " ~av~d " item (db->lin gain)))))
|
(setf str (string+ str newstr))))
|
|
((channels sox-input)
|
(let ((newstr (format nil " remix" )))
|
(loop for channel in (channels sox-input) do
|
(setf newstr (string+ newstr (format nil " ~a" channel))))
|
(setf str (string+ str newstr))))
|
)
|
|
(setf str (string+ str sox-effect))
|
|
(sox-out str sox-input output outfile recursive))
|
|
(sox-not-found))
|
)
|
|
|
; === sox synth =========
|
#|
|
(defmethod! sox-process ((sox-input sox-synth) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (probe-file *sox-path*)
|
|
(let ((outfile (create-path nil output filetype)))
|
(setf str (format nil "~s ~a -n" (namestring *sox-path*) *sox-options*))
|
(setf str (cond ((equal output "realtime") (sox-samplebits (string+ str " -q ") bitdepth samplerate *sox-audio-device*))
|
((equal output "pipe") (sox-samplebits str bitdepth samplerate '-p))
|
(t (sox-samplebits str bitdepth samplerate outfile))))
|
(setf str (string+ str " synth" (sox-synth-format sox-input)))
|
(setf str (string+ str sox-effect))
|
(sox-out str sox-input output outfile recursive))
|
|
(sox-not-found))
|
)
|
|#
|
; %%%%%%%%%%%% LIST METHODS %%%%%%%%%%%%%%%%%%%%%%
|
|
; for string + list + list/string
|
(defmethod! sox-process ((sox-input t) (sox-effect list) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (and (listp output) (first output))
|
(mapcar (lambda (thesox-effect thepath)
|
(sox-process sox-input thesox-effect :output thepath :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-effect output)
|
(mapcar (lambda (thesox-effect)
|
(sox-process sox-input thesox-effect :output output :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-effect)))
|
|
; these functions below can be grouped together into multiple if/when statements for the different types of 'output'
|
;--------------------------------------------------------------------------------------------------------------------
|
; for list + string + string
|
(defmethod! sox-process ((sox-input list) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(mapcar (lambda (file)
|
(sox-process file sox-effect :output output :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input))
|
|
; #### 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)
|
; for list + string + function
|
(defmethod! sox-process ((sox-input list) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (functionp output)
|
; these 2 cases seem to be identical
|
(mapcar (lambda (file)
|
(sox-process file sox-effect :output output :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input)
|
(mapcar (lambda (file)
|
(sox-process file sox-effect :output output :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input)))
|
|
; for list + string + list
|
(defmethod! sox-process ((sox-input list) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
|
(if (and (listp output) (first output))
|
(mapcar (lambda (file thepath)
|
(sox-process file sox-effect :output thepath :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input output)
|
(mapcar (lambda (file)
|
(sox-process file sox-effect :output output :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input)))
|
|
; ---------------------------------------------------------------------------------------------------------------------
|
|
; for list + list + string/list
|
(defmethod! sox-process ((sox-input list) (sox-effect list) &key output filetype samplerate bitdepth recursive batch-mode)
|
(let (
|
(numsounds (length sox-input))
|
(numsox-effect (length sox-effect)))
|
|
(when (> numsounds numsox-effect)
|
(progn
|
(when (equal batch-mode 'cycle)
|
(setf sox-effect
|
(flat (group-list sox-effect (list numsounds) 'circular))))
|
(when (equal batch-mode 'repeat)
|
(setf sox-effect
|
(flat (x-append sox-effect (repeat-n (last sox-effect) (- numsounds numsox-effect))))))))
|
|
(if (consp output)
|
(mapcar (lambda (file thesox-effect thepaths)
|
(sox-process file thesox-effect :output thepaths :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input sox-effect output)
|
(mapcar (lambda (file thesox-effect)
|
(sox-process file thesox-effect :output output :filetype filetype :samplerate samplerate
|
:bitdepth bitdepth :recursive recursive :batch-mode batch-mode)) sox-input sox-effect))))
|
|
|
; for anything else beep ------------------
|
(defmethod! sox-process ((sox-input t) (sox-effect t) &key output filetype samplerate bitdepth recursive batch-mode)
|
(om-beep-msg (format nil "!!! Wrong input type or command for sox-process: ~A with ~A" sox-input sox-effect)))
|