i'm trying execute this tcl script, should batch normalise folder of mp3's. i'm on osx yosemite (ffmpeg installed). line is:
./normalise.tcl mp3folder which (inc. sudo) returns:
./normalise.tcl: line 37: proc: command not found ./normalise.tcl: line 38: global: command not found ./normalise.tcl: line 40: switch: command not found ./normalise.tcl: line 42: puts: command not found ./normalise.tcl: line 43: syntax error near unexpected token `}' ./normalise.tcl: line 43: ` }' .. , directs shell inbuilt documentation. have no experience of language reading up, haven't come across far explain it. grateful idea what's going wrong.
edit
the -d option suggested in script comments seems have no effect.
the full script:
#!/bin/sh #\ exec tclsh "$0" ${1+"$@"} # copyright 2015 tholis biroi (tholis dot biroi @ yahoo dot it) # # file part of 'normalize.tcl'. # 'normalize.tcl' free software: can redistribute and/or modify # under terms of gnu general public license published # free software foundation, either version 3 of license, or # (at option) later version. # 'normalize.tcl' distributed in hope useful, # without warranty; without implied warranty of # merchantability or fitness particular purpose. # # see gnu general public license more details. # should have received copy of gnu general public license # along 'normalize.tcl'. if not, see http://www.gnu.org/licenses/. # # # # 'normalize.tcl' simple tcl script drives 'ffmpeg' normalise # audio levels group of mp3 files. # each mp3 file found directory, reads mean volume level, # calculates average level among various files , adjusts volume # level of each of them. # # global debugging variable # override option set '-d' on command line set debugon false # log -- # # puts "" wrapper # proc log {label args} { global debugon switch $label { "info" { puts {*}$args } "error" { puts "error: [join {*}$args]" } "warning" { puts "warning: [join {*}$args]" } "debug" { if {$debugon == "true"} { puts "debug: [join {*}$args]" } } default { # nothing in case } } } # get_volumes -- # # exec 'ffmpeg' in order volume mean level # # proc get_volume {mp3} { if {$mp3 == {}} { log error "empty file name." return {} } # set volume variable set volume {} # set 'ffmpeg' command set cmd "ffmpeg -i \"$mp3\" -af \"volumedetect\" -f null /dev/null" log debug "'ffmpeg' cmd= $cmd" # exec 'ffmpeg' if {[catch {eval exec -ignorestderr $cmd 2>@1} out]} { log error "'ffmpeg' execution command failed." log debug "reason= $out" return {} } # in order avoid 'case sensitive' parsing, output of # command converted uppercase set out [string toupper $out] log debug "'ffmpeg' out= $out" # scan out line @ time searching 'mean_volume:' # output string label set lines [split $out "\n"] foreach line $lines { log debug "$line" # first of search 'volumedetect' string , if foud # search 'mean_volume:' string. if {[string first volumedetect $line] == -1} { # not found, skip line parsing continue } # 'volumedetect' string found, search 'mean_volume' string set pos [string first mean_volume $line] if { $pos != -1} { set start [expr {$pos + 11}] set volstr [string range $line $start end] log debug "volstr= $volstr" # extract , trim first word volume set words [split $volstr] log debug "words= $words" set volume [string trim [lindex $words 1]] log debug "volume= $volume" } } return $volume } ;# end get_volume # set_volume -- # # exec 'ffmpeg' re-encode mp3 # proc set_volume {mp3 actualvol targetvol} { if {($mp3 == {}) || ($actualvol == {}) || ($actualvol == {})} { log error "one or more parameter empty" return {} } # create filename output set mp3root [file rootname $mp3] set mp3outfile "${mp3root}.norm.mp3" # if normalized file exists, deleted if {[file exists $mp3outfile]} { catch {file delete -force -- $mp3outfile} } # calculate delta volume set deltavol [expr {$targetvol - $actualvol}] # set 'ffmpeg' command set cmd "ffmpeg -y -i \"$mp3\" -af \"volume=${deltavol}db\" \"$mp3outfile\"" # exec 'ffmpeg' if {[catch {eval exec -ignorestderr $cmd 2>@1} out]} { log error "'ffmpeg' execution command failed." log debug "reason= $out" return {} } # debug purposes set out [string toupper $out] log debug "'ffmpeg' out= $out" return $deltavol } ;# end set_volume # byebye -- # proc byebye {} { log info "" log info "bye!" log info "" } ;# end byebye # print_help -- # # prints little # proc print_help {} { global argv0 log info "" log info "usage: $argv0 \[-h|--help\]|<mp3 dir>" log info "" log info "-h|--help print thi help" log info "<mp3 dir> directory path containing mp3 normalize" log info "" byebye return } ;# end print_help # main ----------------------------------------------------------------- log info "" log info "mp3 normalizer v0.9 21 mar 2015" log info "" log info "" # save current dir set currdir [pwd] log debug "working dir= $currdir" # control input parameters setup working dir # if no parameter passed little printed on screen if {$argc == 0} { print_help exit 0 } # if more 1 parameter passed if {$argc != 1} { log error "wrong number of arguments." log error "use '-h' or '--help' option print usage info." byebye exit 1 } # if 1 paramter passed, option or # desired working path if {([lindex $argv 0] == "-h") || ([lindex $argv 0] == "--help")} { print_help exit 0 } # save passed workdir in order make controls set workdir [lindex $argv 0] # path passed must directory path if { ![file isdirectory $workdir] } { log error "the argument passed not valid directory path" byebye exit 1 } # argument passed must existing directory if { ![file exists $workdir] } { log error "directory '$workdir' not exists" byebye exit 1 } # move on working dir cd $workdir # list of files in current directory set mp3files [glob -nocomplain *.mp3] if {$mp3files == {}} { log info "no .mp3 files found on working dir: '$workdir'" byebye exit 1 } # exclude list files exetension *.norm.mp3" set mp3filelist {} foreach mp3 $mp3files { set rootfname [file rootname $mp3] set ext [file extension $rootfname] if {$ext == ".norm"} { # skip normalized files mp3 list continue } lappend mp3filelist $mp3 } # init mp3 array #set mp3ar {} log info "list of file mp3 normalized:" # foreach *.mp3 file foreach mp3 $mp3filelist { log info " '$mp3'" # extract volumes set vol [get_volume $mp3] if {$vol == {}} { log warning "no volume information found file: $mp3" } else { # fill array of volumes set mp3ar($mp3) $vol } } log info "" # parray debugging #parray mp3ar # calculating average volume set avgvol 0 set mp3list [array names mp3ar] set numfiles [llength $mp3list] foreach mp3 $mp3list { set avgvol [expr {$mp3ar($mp3) + $avgvol}] } set avgvolume [expr {$avgvol/double($numfiles)}] set avgvol [format "%0.1f" $avgvolume] log info "avg volume= $avgvol" log info "" # foreach file calculate delta volume normalize log info "file normalization @ $avgvol db" foreach mp3 $mp3list { log info " '$mp3' $mp3ar($mp3) $avgvol" if {[set_volume $mp3 $mp3ar($mp3) $avgvol] == {}} { log info "warning: set volume failed file '$mp3'" } } log info "" log info "done." # before exit return run dir cd $currdir byebye exit 0
for reason, script being executed bourne shell (/bin/sh) in entirety, , not tcl. since 2 languages have different syntaxes, gets error messages.
but why happening?
well, key lines these:
#!/bin/sh #\ exec tclsh "$0" ${1+"$@"} that's supposed run script bourne shell, , transfer execution tcl (since standard shell exec replaces current process executable). it's based on fact shell doesn't think backslash @ end of line in comment special, , yet tcl treats meaning following line part of comment. different rules.
yet that's failing. i'm guessing problem tclsh isn't on path (really? it's standard part of osx system.) , exec failing , remainder of script therefore being interpreted. more bit strange. you're going via sudo might problem, there ought tclsh in 1 of directories sudo puts on path default (/usr/bin), might not what's going on.
the recommended approach @ point change 3 lines of script use more modern idiom that's single line:
#!/usr/bin/env tclsh you can use fully-specified name tclsh in there well. or can try launching code with:
tclsh ./normalise.tcl mp3folder that last step way detect if there other problems; overrides lookup of script interpreter , lets focus on happens after that.
Comments
Post a Comment