; ******************************************************************** ; 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) ;;; variables ======================= (defparameter *gnuplot-path* "/usr/local/bin/gnuplot") ; should probably be in the preferences file. For now no specific option for this "/usr/local/bin/gnuplot" (defvar *gnuplot-terminal-menu* '( ("png" "png") ("aqua" "aqua") ;("ascii" "dumb") ("jpeg" "jpeg") ("gif" "gif") ("pdf" "pdf") ("eps" "eps") ("eps-landscape" "eps-landscape") )) (defparameter *gnuplot-terminal-commands* '( ("aqua" "set term aqua") ("pdf" "set term pdf") ("eps" "set term postscript eps color enhanced rounded font \"Times\" 12") ("png" "set term pngcairo") ("jpeg" "set term jpeg") ("gif" "set term gif") )) (defun gnuplot-not-found () ;(om-message-abort (format nil "SoX exectuable not found. Path set in preferences?") (om-beep-msg (format nil "gnuplot not found. Is it in /usr/local/bin ?")) ) #| (if (probe-file *gnuplot-path*) () (gnuplot-not-found)) (environment-variable "PATH") (setf (environment-variable "PATH") (concatenate 'string (environment-variable "PATH") ":/usr/local/bin")) |# ;;; sox-plot =============================== ; Perhaps I should be able to connect other things to my sox-plot function. ; For example, to draw a waveform or to draw the dftlist ; So that when it receives a list of values it can plot them (in 2D or 3D) ; this function needs to be able to distinguish list of numbers from biquad effects, from fir effects ; so 3 methods? ;perhaps introduce a 3rd dimension (z) (defmethod! sox-plot ((commands string) &key output (terminal "png") (samplerate "48k") (numsamples 250) title x-label y-label x-range y-range (x-scale "logarithmic") (y-scale "deciBel")) :icon 07 :initvals '(nil nil "aqua" nil nil nil "" "" "" nil nil nil nil) ;(nil "" "aqua" nil nil nil "" "" "" nil nil nil nil) :menuins (list (list 2 *gnuplot-terminal-menu*) (list 3 *sox-samplerates*) (list 10 '(("linear" "linear") ("logarithmic" "logarithmic"))) (list 11 '(("linear" "linear") ("deciBel" "deciBel")))) (if (probe-file *sox-path*) (if (probe-file *gnuplot-path*) (let ((outfile (create-path () output "plt"))) (sox-print "terminal:" terminal) (sox-print "outfile:" outfile) (setf str (format nil "~s -r~a --plot gnuplot -n -n " (namestring *sox-path*) samplerate )) (setf str (string+ str commands " >")) (setf str (sox-concat outfile str)) (om-cmd-line str *sys-console*) (let ((mypltfile (probe-file outfile))) ;modif of textfile (let* ((loaded-textfile (objfromobjs outfile (make-instance 'textfile :eval-mode "text"))) (xscale (if (equal x-scale "logarithmic") "set logscale x" "#set logscale x")) (samples (format nil "set samples ~D" numsamples)) (plot-title (format nil "set title '~a'" title)) (x-range (or x-range (list 10 "Fs/2"))) (y-range (or y-range (if (equal y-scale "deciBel") (list -40 12) (list 0 2)))) (xlabel (if x-label ; maybe this if-statement is not needed, as X is always Frequency and Y always Amplitude (if (equal x-label "") "#set xlabel " (format nil "set xlabel '~a'" x-label)) (if (equal x-scale "logarithmic") "set xlabel 'Frequency (Hz) (log.)'" "set xlabel 'Frequency (Hz)'"))) (ylabel (if y-label (if (equal y-label "") "#set ylabel " (format nil "set ylabel '~a'" y-label)) (if (equal y-scale "deciBel") "set ylabel 'Amplitude (dBFS)'" "set ylabel 'Amplitude (linear)'"))) (plot-text (if (equal y-scale "deciBel") (format nil "plot [f=~D:~a] [~D:~D] 20*log10(H(f))" (first x-range) (second x-range) (first y-range) (second y-range)) (format nil "plot [f=~D:~a] [~D:~D] (H(f))" (first x-range) (second x-range) (first y-range) (second y-range)))) (plot-fir-text (if numsamples (format nil "plot '-' with lines") (format nil "plot '-'"))) (manipulated-text (if (find (read-from-string commands) '(fir hilbert sinc)) (if title (insert-in-list (subs-posn (butlast (butlast (cdr (exp-list loaded-textfile)))) '(0 1 2 5) (list plot-title xlabel ylabel plot-fir-text)) xscale 3) (insert-in-list (subs-posn (butlast (butlast (cdr (exp-list loaded-textfile)))) '(1 2 5) (list xlabel ylabel plot-fir-text)) xscale 3)) (if title (subs-posn (butlast (butlast (cdr (exp-list loaded-textfile)))) '(0 1 2 7 8 11) (list plot-title xlabel ylabel xscale samples plot-text)) (subs-posn (butlast (butlast (cdr (exp-list loaded-textfile)))) '(1 2 7 8 11) (list xlabel ylabel xscale samples plot-text))))) (mypicfile (create-path () output terminal)) (new-text (x-append *sox-gnuplot-header* (second (assoc terminal *gnuplot-terminal-commands* :test #'equalp)) (format nil "set output ~s" (namestring mypicfile)) manipulated-text)) (new-textfile (make-instance 'textfile :eval-mode "text" ))) (setf (exp-list new-textfile) new-text) (save-data new-textfile mypltfile) (probe-file mypltfile) (om-cmd-line (format nil "~s ~s " *gnuplot-path* (namestring mypltfile)) *sys-console*) (let ((myoutfile (probe-file mypicfile)) (mypict (make-instance 'picture))) (setf (background mypict) myoutfile) ;optional removal of temporary files (add-tmp-file mypltfile) (add-tmp-file myoutfile) (when *delete-inter-file* (clean-tmp-files)) mypict) );) ) ) (gnuplot-not-found)) (sox-not-found) ) ) (defmethod! sox-plot ((data list) &key output (terminal "png") (samplerate "48k") (numsamples 250) title x-label y-label x-range y-range x-scale y-scale) (if (probe-file *sox-path*) (if (probe-file *gnuplot-path*) (let* ((mypltfile (print (create-path () output "plt"))) (default-text *default-gnuplot-text*) (dimensions (length (car data))) (datalist (if (eq dimensions 3) (loop for item in data collect (format nil "~d ~d ~d" (first item) (if (equal y-scale "deciBel") (lin->dB (abs (second item))) (second item)) (third item) #\newline) ) (loop for item in data collect (format nil "~d ~d" (first item) (if (equal y-scale "deciBel") (lin->dB (abs (second item))) (second item)) #\newline) ))) ;isn't that (datalist) like this? (reduce #'(lambda (s1 s2) (format nil "~d ~d " s1 s2)) coefficients) -> and like 'concat-strings'? (xscale (if (equal x-scale "logarithmic") "set logscale x" "#set logscale x")) (samples (format nil "set samples ~D" numsamples)) (plot-title (format nil "set title '~a'" title)) ;ranges have no effect at the moment. Don't know how to set it for gnuplot. Would need a band-filter on the data list. (xlabel (if x-label (format nil "set xlabel '~a'" x-label) "#set xlabel ")) (ylabel (if y-label (format nil "set ylabel '~a'" y-label) "#set ylabel ")) (plot-list-text (if (eq dimensions 3) (if numsamples (format nil "splot '-' with lines") (format nil "splot '-'")) (if numsamples (format nil "plot '-' with lines") (format nil "plot '-'")))) (manipulated-text (if title (subs-posn default-text '(0 1 2 3 6) (list plot-title xlabel ylabel xscale plot-list-text)) (subs-posn default-text '(1 2 3 6) (list xlabel ylabel xscale plot-list-text)))) (mypicfile (create-path () output terminal)) (new-text (x-append *sox-gnuplot-header* (second (assoc terminal *gnuplot-terminal-commands* :test #'equalp)) (format nil "set output ~s" (namestring mypicfile)) manipulated-text datalist)) (new-textfile (make-instance 'textfile :eval-mode "text" ))) (setf (exp-list new-textfile) new-text) (save-data new-textfile mypltfile) (probe-file mypltfile) (om-cmd-line (format nil "~s ~s " *gnuplot-path* (namestring mypltfile)) *sys-console*) (let ((myoutfile (probe-file mypicfile)) (mypict (make-instance 'picture))) (setf (background mypict) myoutfile) ;optional removal of temporary files (add-tmp-file mypltfile) (add-tmp-file myoutfile) (when *delete-inter-file* (clean-tmp-files)) mypict) ) (gnuplot-not-found)) (sox-not-found) ) ) #| (defmethod sox-coeffs-to-string ((coeffs list)) (if (eq (length (car coeffs)) 2) (reduce #'(lambda (s1 s2) (format nil "~d ~d " s1 s2 #\newline)) coeffs) (reduce #'(lambda (s1 s2 s3) (format nil "~d ~d ~d " s1 s2 s3 #\newline)) coeffs) )) ;doesn't seem to work -- WHY? (setf test-list '(("Aqua" "Bingo") ("Delta" "nothing"))) (assoc "Aqua" test-list :test #'equalp) (second (find "Aqua" test-list :test (lambda (item arg) (string-equal item (car arg))))) |# (defmethod! sox-plot ((data bpf) &key output (terminal "png") (samplerate "48k") (numsamples 250) biquad-cascade title x-label y-label x-range y-range x-scale y-scale) (sox-plot (mat-trans (list (x-points data) (y-points data))) :output output :terminal terminal :samplerate samplerate :numsamples numsamples :biquad-cascade biquad-cascade :title title :x-label x-label :y-label y-label :x-range x-range :y-range y-range :x-scale x-scale :y-scale y-scale)) (defmethod! sox-plot ((data 3dc) &key output (terminal "png") (samplerate "48k") (numsamples 250) biquad-cascade title x-label y-label x-range y-range x-scale y-scale) (sox-plot (mat-trans (list (x-points data) (y-points data) (z-points data))) :output output :terminal terminal :samplerate samplerate :numsamples numsamples :biquad-cascade biquad-cascade :title title :x-label x-label :y-label y-label :x-range x-range :y-range y-range :x-scale x-scale :y-scale y-scale)) (defparameter *exp-list* '("# gnuplot file" "set title 'SoX effect: bass gain=12 frequency=100 Q=10 (rate=48000)'" "set xlabel 'Frequency (Hz)'" "set ylabel 'Amplitude Response (dB)'" "Fs=48000" "b0=1.000524909583901e+00; b1=-1.998859954575080e+00; b2=9.986767718845346e-01; a1=-1.998987899064422e+00; a2=9.990737369790924e-01" "o=2*pi/Fs" "H(f)=sqrt((b0*b0+b1*b1+b2*b2+2.*(b0*b1+b1*b2)*cos(f*o)+2.*(b0*b2)*cos(2.*f*o))/(1.+a1*a1+a2*a2+2.*(a1+a1*a2)*cos(f*o)+2.*a2*cos(2.*f*o)))" "#set logscale x" "set samples 250" "#set grid xtics ytics" "#set key off" "plot [f=10:Fs/2] [-35:55] H(f) #20*log10(H(f))" "pause -1 'Hit return to continue'" "")) ;-> perhaps add this to the sox-plot function: Meaning, that you can make a list of the individual fx, then supply them to "biquad" cascade!! #| (defmethod! sox-multiplot (&rest biquad-fx &key output (terminal "png") (samplerate "48k") (numsamples 250) biquad-cascade title x-label y-label x-range y-range x-scale y-scale) ; this function is like sox-plot but allows to connect multiple effects which are then combined in the visualization (new inlet per new biquad effect)) :icon 07 ; :initvals (list (repeat-n nil (length biquad-effects)) nil "" "aqua" nil nil nil "" "" "" nil nil nil nil) ;hmmm, not sure what to do with the initvals ; :menuins (list ; (list 2 *gnuplot-terminal-menu*) ; (list 3 *sox-samplerates*) ; (list 10 '(("linear" "linear") ("logarithmic" "logarithmic"))) ; (list 11 '(("linear" "linear") ("deciBel" "deciBel")))) ; :doc '("sox-multiplot allows connecting multiple effects which are then combined and plotted (new inlet per sox-effect) ;Plots the combined amplitude response of biquad-based effects. Biquad-based effects are: ;highpass, lowpass, bandpass, bandreject, allpass, bass, treble, equalizer, band, deemph, riaa, biquad.") (if (probe-file *sox-path*) (if (probe-file *gnuplot-path*) (let* ((default-text *def-pearl-txt*) (datalist (sox-reduce-fx biquad-fx)) (xscale (if (equal x-scale "logarithmic") "set logscale x" "#set logscale x")) (samples (format nil "set samples ~D" numsamples)) (plot-title (or title (format nil "set title 'SoX effects: $desc (rate=$rate)'"))) (xlabel (if x-label ; maybe this if-statement is not needed, as X is always Frequency and Y always Amplitude (if (equal x-label "") "#set xlabel " (format nil "set xlabel '~a'" x-label)) (if (equal x-scale "logarithmic") "set xlabel 'Frequency (Hz) (log.)'" "set xlabel 'Frequency (Hz)'"))) (ylabel (if y-label (if (equal y-label "") "#set ylabel " (format nil "set ylabel '~a'" y-label)) (if (equal y-scale "deciBel") "set ylabel 'Amplitude (dBFS)'" "set ylabel 'Amplitude (linear)'"))) (sox-gnuplot-call "$_ = `~a --plot gnuplot --rate $rate -n -n $_ | sed -n -e '6 p'`" *sox-path*) (plot-text (if (equal y-scale "deciBel") (format nil "plot [f=~D:~a] [~D:~D] 20*log10(H(f))" (first x-range) (second x-range) (first y-range) (second y-range)) (format nil "plot [f=~D:~a] [~D:~D] (H(f))" (first x-range) (second x-range) (first y-range) (second y-range)))) (manipulated-text (if title (subs-posn default-text '(0 1 2 3 6) (list plot-title xlabel ylabel xscale plot-list-text)) (subs-posn default-text '(1 2 3 6) (list xlabel ylabel xscale plot-list-text)))) (mypicfile (create-path () output terminal)) (new-text (x-append *sox-gnuplot-header* (second (assoc terminal *gnuplot-terminal-commands* :test #'equalp)) (format nil "set output ~s" (namestring mypicfile)) manipulated-text datalist)) (new-textfile (make-instance 'textfile :eval-mode "text" ))) (setf (exp-list new-textfile) new-text) (save-data new-textfile mypearlfile) (probe-file mypearlfile) (om-cmd-line (format nil "~s ~s " mypearlfile (namestring mypltfile)) *sys-console*) datalist (let ((myoutfile (probe-file mypicfile)) (mypict (make-instance 'picture))) (setf (background mypict) myoutfile) ;optional removal of temporary files (add-tmp-file mypltfile) (add-tmp-file myoutfile) (when *delete-inter-file* (clean-tmp-files)) mypict) ) (gnuplot-not-found)) (sox-not-found) ) |# (defparameter *sox-gnuplot-header* '("#This file was produced for gnuplot by OM-SoX v1.0.")) (defparameter *default-gnuplot-text* (list "#set title " "set xlabel " "set ylabel " "set scale " "set grid xtics ytics" "set key off" "plot '-' with lines")) (defparameter *def-pearl-txt* '((*sox-gnuplot-header* "# Perl Script by Ulrich Klauer" "" "my $rate = 48000;" "$rate = shift if $ARGV[0] =~ /^\d+$/ && $ARGV[0] != 0;" "#usage() if $#ARGV == -1;" "my $desc = join(" ", @ARGV);" "print <" "#set xlabel " "#set ylabel " "Fs=$rate" "o=2*pi/Fs" "#set logscale x" "set grid xtics ytics" "set key off" "END" "my $l = 0;" "foreach (@ARGV) {" " $l++;" " $_ = `sox --plot gnuplot --rate $rate -n -n $_ | sed -n -e '6 p'`;" " s/([ab][012])/$1_$l/g;" " print;" " print \"H_$l(f)=sqrt((b0_$l*b0_$l+b1_$l*b1_$l+b2_$l*b2_$l+2.*(b0_$l*b1_$l+b1_$l*b2_$l)*cos(f*o)+2.*(b0_$l*b2_$l)*cos(2.*f*o))/(1.+a1_$l*a1_$l+a2_$l*a2_$l+2.*(a1_$l+a1_$l*a2_$l)*cos(f*o)+2.*a2_$l*cos(2.*f*o)))\n\";" "}" "my $prod = join(\"*\", map { \"H_$_(f)\" } (1..$l));" "print \"H(f)=$prod\n\";" "print < args ... (which are the commands!) #| "#!/usr/bin/perl -w sub usage() { print STDERR "Usage: $0 [sampling_rate] \"effect1 effargs1\" " . "\"effect2 effargs2\" ...\n"; print STDERR "Plots the combined amplitude response of biquad-based " . "effects.\n"; print STDERR "Biquad-based effects are: highpass, lowpass, " . "bandpass, bandreject, allpass,\n" . "bass, treble, " . "equalizer, band, deemph, riaa, biquad.\n"; exit(1); } usage() if $#ARGV == -1; ;--------this above is not needed------- ;maybe don't change this title (or title (format nil "set title 'SoX effects: $desc (rate=$rate)'"))" "set title 'SoX effects: $desc (rate=$rate)' "#set xlabel " "#set ylabel " "Fs=$rate" "o=2*pi/Fs" "#set logscale x" "set grid xtics ytics" "set key off" "END" "my $l = 0;" "foreach (@ARGV) {" " $l++;" "$_ = `sox --plot gnuplot --rate $rate -n -n $_ | sed -n -e '6 p'`;" " s/([ab][012])/$1_$l/g;" " print;" " print \"H_$l(f)=sqrt((b0_$l*b0_$l+b1_$l*b1_$l+b2_$l*b2_$l+2.*(b0_$l*b1_$l+b1_$l*b2_$l)*cos(f*o)+2.*(b0_$l*b2_$l)*cos(2.*f*o))/(1.+a1_$l*a1_$l+a2_$l*a2_$l+2.*(a1_$l+a1_$l*a2_$l)*cos(f*o)+2.*a2_$l*cos(2.*f*o)))\n\";" "}" "my $prod = join(\"*\", map { \"H_$_(f)\" } (1..$l));" "print \"H(f)=$prod\n\";" "print <