#!/bin/bash set -e verwendung() { >&2 echo 'Verwendung: '"$(basename "${me}")"' [OPTIONEN]' >&2 echo '' >&2 echo ' -1, --noGain Lautstärke nicht normalisieren' >&2 echo ' -a, --ausfuehrlich ausführliche Zwischenausgaben machen' >&2 echo ' -c, --cd Dateien passend für CD-Tracks erzeugen' >&2 echo ' -f, --force output.flac ggf. überschreiben' >&2 echo ' -i, --input=inputDatei Quelle' >&2 echo ' -n, --dummy nur auszuführenden Befehl anzeigen und nichts tun' >&2 echo ' -o, --output=outputDatei Zieldatei' >&2 echo ' -p, --points=pointsDatei Punktedatei' >&2 echo ' -s, --sampleRate=Rate alternative Samplerate zum Abspeichern' >&2 echo ' -t, --temp=tempDir alternatives temporäres Verzeichnis für sox' >&2 echo \ '#HELPTEXT# #' [ -z "$1" ] && exit 1 || exit $1 } timetosample() { local zeit case "$2" in *:*:*) zeit="$2" ;; *:*) zeit="0:$2" ;; *) zeit="0:0:0$2" ;; esac bc <<< "( 0.5 + $1 * $(date "+%s.%N" -ud "1970-01-01 ${zeit}") ) / 1" } me="$(readlink -f "$0")" eval set -- "$( getopt -o 1acfi:no:p:s:t: \ --long noGain \ --long ausfuehrlich \ --long cd \ --long force \ --long help \ --long input: \ --long dummy \ --long output: \ --long points: \ --long sampleRate: \ --long tempDir: \ --long version \ -n "$(basename "$0")" -- "$@" || \ echo verwendung )" noGain=false ausfuehrlich=false force=false dummy=false cdAusrichten=false while true; do case "$1" in -1|--noGain) noGain=true ;; -a|--ausfuehrlich) ausfuehrlich=true ;; -c|--cd) [ -n "${outRate}" ] && echo 'Ich kann nur eine Samplerate-Option verstehen.' && verwendung outRate="44100" cdAusrichten=true ;; -f|--force) force=true ;; --help) verwendung 0 ;; -i|--input) shift inputs[${#inputs[@]}]="$1" ;; -n|--dummy) dummy=true ;; -o|--output) shift [ -n "${output}" ] && echo 'Ich kann nur eine Output-Datei-Option verstehen.' && verwendung output="$1" ;; -p|--points) shift [ -n "${points}" ] && echo 'Ich kann nur eine Point-Datei-Option verstehen.' && verwendung points="$1" ;; -s|--sampleRate) shift [ -n "${outRate}" ] && echo 'Ich kann nur eine Samplerate-Option verstehen.' && verwendung outRate="$1" ;; -t|--tempDir) shift [ -n "${tempDir}" ] && echo 'Ich kann nur ein temporäres Verzeichnis verstehen.' && verwendung tempDir="$1" ;; --version) >&2 echo '#VERSION#' exit 0 ;; --) shift [ $# -gt 0 ] && echo 'Unbekannte Parameter: '"$#" && verwendung break ;; *) >&2 echo 'Hups, das sollte nicht passieren könne, '"$1"' kenne ich doch nicht ...' exit -1 ;; esac shift done [ -z "${output}" ] && >&2 echo 'Output-Datei fehlt!' && verwendung [ -z "${points}" ] && >&2 echo 'Points-Datei fehlt!' && verwendung [ "${#inputs[@]}" -eq 0 ] && >&2 echo 'Input-Datei fehlt!' && verwendung if ${cdAusrichten} && [[ ! "${output}" == *".wav" ]] then >&2 echo 'CD-Ausgabe sollte nur nach wav geschehen.' exit 1 fi [ ! -e "${points}" ] && >&2 echo "Die Schnittpunktedatei '${points}' existiert nicht!" && exit 1 for inp in "${inputs[@]}" do [ ! -e "${inp}" ] && >&2 echo "Die Inputdatei '${inp}' existiert nicht!" && verwendung echo "${inp}" | grep -q "\s" && \ >&2 echo "Bitte keine Leerzeichen im input-Dateinamen!" && \ exit 1 done echo "${output}" | grep -q "\s" && \ >&2 echo "Bitte keine Leerzeichen im output-Dateinamen!" && \ exit 1 echo "${points}" | grep -q "\s" && \ >&2 echo "Bitte keine Leerzeichen im points-Dateinamen!" && \ exit 1 [ -n "${tempDir}" ] && tempDir="--temp ${tempDir}" odn="$(dirname "$(readlink -f "${output}")")" obn="$(basename "${output}")" if [ "${obn%.*}" == "%s" ] then names=( $( sed -n '/^[^#].*noNewFile/{N;d};/^#/{s|^.*#||;s|^ \+||;p}' "${points}" | \ tr ' --,.:/' '_' ) ) obn="temp_%n.${output##*.}" output="${odn}/${obn}" for name in "${names[@]}" do if [ -e "${odn}/${name}.${obn#*.}" ] then if ${force} then ${dummy} || rm "${odn}/${name}.${obn#*.}" else >&2 echo "Die Ausgabedatei '${odn}/${name}.${obn#*.}' existiert bereits!" && exit 1 fi fi done fi if [ -e "${output}" ] then if ${force} then ${dummy} || rm "${output}" else >&2 echo "Die Ausgabedatei '${output}' existiert bereits!" && exit 1 fi fi if [ $(ls -1 "${odn}" | grep "$(echo "${obn}" | sed "s/%[0-9]\?n/.*/g")" | wc -l) -gt 0 ] then if ${force} then ${dummy} || rm ${odn}/$(echo "${obn}" | sed "s/%[0-9]\?n/*/g") else >&2 echo "Die Ausgabedatei '${output}' existiert bereits!" && exit 1 fi fi inRate="$(soxi -r "${inputs[0]}")" [ -z "${outRate}" ] && outRate="${inRate}" i=-1 absInTime=0 absOutTime=0 while read -r in do line="$( echo "${in}" | \ sed 's|\s\+| |g' | \ sed 's/^ *//' | \ sed 's/ *\(\#\( .*\)\?\)\?$//' )" [ -z "${line}" ] && continue if [ ${i} -eq -1 ] && echo "${line}" | grep -q '^reject\( \+[0-9]\+\)\{4\}$' then kanal="$(echo "${line}" | awk '{print $2}')" freq="$(echo "${line}" | awk '{print $3}')" breite="$(echo "${line}" | awk '{print $4}')" maxn="$(echo "${line}" | awk '{print $5}')" for ((j=1; j<=$maxn; j++)) do rejects[${kanal}]="${rejects[${kanal}]} bandreject $[${j}*${freq}] ${breite}" done continue fi teilEins="$(echo "${line}" | sed 's| *\(#.*\)\?$||')" teilZwei="$(echo "${line}" | sed 's|^[^#]*\(# *\)\?||')" if [ ${i} -eq -1 ] then firstline="${teilEins}" outEndTime=$[ \ $(timetosample "${outRate}" $(echo "${firstline}" | awk '{print $2}' | tr '=' ' ')) \ - $(timetosample "${outRate}" $(echo "${firstline}" | awk '{print $1}' )) \ ] remixcmd="${teilZwei}" if echo "${remixcmd}" | \ grep -q "rate " then >&2 echo 'FEHLER: "rate" explizit gesetzt, das bringt die Zeitmessung durcheinander - verwende statt dessen die Option "-s" bzw. "--sampleRate".' verwendung fi i=0 continue fi if [ -z "${teilEins}" ] then if [ -n "${trackmods[${i}]}" ] then echo "Syntaxfehler in Track $[${i}+1]: habe bereits Modifikatoren gelesen ('${trackmods[${i}]}' -> '${teilZwei}')" exit 1 fi trackmods[${i}]=" ${teilZwei}" inTimecorrections[${i}]=0 outTimecorrections[${i}]=0 while echo "${trackmods[${i}]}" | grep -q " trimsplice" do rest="${trackmods[${i}]#* trimsplice}" end=$(timetosample ${inRate} $(echo "${rest}" | awk '{print $1}')) if [ ! "${doverlaps[${i}]}" == "" ] then end=$[${end}+${doverlaps[${i}]}] fi inDiscard=$(timetosample ${inRate} $(echo "${rest}" | awk '{print $2}')) outDiscard=$(timetosample ${outRate} $(echo "${rest}" | awk '{print $2}')) inExcess=$(timetosample ${inRate} $(echo "${rest}" | awk '{print $3}')) outExcess=$(timetosample ${outRate} $(echo "${rest}" | awk '{print $3}')) leeway=$(timetosample ${inRate} $(echo "${rest}" | awk '{print $4}')) trackmods[${i}]="$( echo "${trackmods[${i}]}" | \ sed "s/ \+trimsplice\( \+[^ ]\+\)\{4\}/ trim 0s ${end}s ${inDiscard}s splice -t ${end}s,${inExcess}s,${leeway}s/" )" inTimecorrections[${i}]=$[${inTimecorrections[${i}]}+${inDiscard}+2*${inExcess}] outTimecorrections[${i}]=$[${outTimecorrections[${i}]}+${outDiscard}+2*${outExcess}] done continue fi i=$[${i}+1] tmp=$(timetosample ${inRate} "${teilEins}") inTimes[${i}]=$[${tmp}-${absInTime}] absInTime=${tmp} tmp=$(timetosample ${outRate} "${teilEins}") outTimes[${i}]=$[${tmp}-${absOutTime}] absOutTime=${tmp} newFile[${i}]=true teilZwei="${teilZwei} " while [ -n "${teilZwei}" ] do case "${teilZwei}" in "overlap "*) teilZwei="${teilZwei#* }" overlaps[${i}]=$[$(timetosample ${inRate} ${teilZwei%% *})/2] doverlaps[${i}]=$[2*${overlaps[${i}]}] teilZwei="${teilZwei#* }" ;; "noNewFile"*) teilZwei="${teilZwei#* }" newFile[${i}]=false ;; " ") break ;; *) >&2 echo "'${teilZwei}' ist unverständlich zwischen zwei Stücken ('${line}')" exit 1 ;; esac done done < "${points}" if ${ausfuehrlich} then echo "trackmods:" for (( i=0; i<=${#inTimes[@]}; i++ )) do echo "${i}: ${trackmods[${i}]} ; ${inTimes[${i}]} ; ${overlaps[${i}]} ; ${doverlaps[${i}]} ; ${inTimecorrections[${i}]}" done fi repcmd="" abstime=0 for (( i=1; i<=${#inTimes[@]}; i++ )) do abstime=$[${abstime}+${inTimes[${i}]}] [ -z "${doverlaps[${i}]}" ] && continue repcmd="${repcmd} trim 0s $[${abstime}-${doverlaps[${i}]}]s : trim 0s ${doverlaps[${i}]}s repeat :" abstime=0 done trackmodcmd="" abstime=0 for (( i=0; i<=${#inTimes[@]}; i++ )) do if [ ${i} -gt 0 ] then [ -n "${inTimecorrections[$[${i}-1]]}" ] && \ inTimes[${i}]=$[${inTimes[${i}]}-${inTimecorrections[$[${i}-1]]}] [ -n "${outTimecorrections[$[${i}-1]]}" ] && \ outTimes[${i}]=$[${outTimes[${i}]}-${outTimecorrections[$[${i}-1]]}] fi [ -n "${inTimes[$[${i}+1]]}" ] && abstime=$[${abstime}+${inTimes[$[${i}+1]]}] [ -n "${doverlaps[${i}]}" ] && abstime=$[${abstime}+${doverlaps[${i}]}] [ "${trackmods[${i}]}" == "${trackmods[$[${i}+1]]}" ] && continue if [ ${i} -eq ${#inTimes[@]} ] then trackmodcmd="${trackmodcmd}${trackmods[${i}]} " else trackmodcmd="${trackmodcmd}trim 0s ${abstime}s${trackmods[${i}]} : " fi abstime=0 done for (( i=0; i<=${#inTimes[@]}; i++ )) do [ -n "${outTimecorrections[${i}]}" ] && \ outEndTime=$[${outEndTime}-${outTimecorrections[${i}]}] done splicecmd="" abstime=0 for (( i=0; i<=${#inTimes[@]}; i++ )) do [ -n "${inTimes[${i}]}" ] && abstime=$[${abstime}+${inTimes[${i}]}] [ -z "${overlaps[${i}]}" ] && continue splicecmd="${splicecmd} ${abstime}s,${overlaps[${i}]}s,0s" abstime=$[${abstime}+${doverlaps[${i}]}] done [ -n "${splicecmd}" ] && splicecmd="splice -t${splicecmd}" if ${cdAusrichten} then diff=0 align2=294 for (( i=1; i<=${#outTimes[@]}; i++ )) do nDiff=$(bc <<< "(${outTimes[${i}]} + ${diff} + ${align2}) % (2 * ${align2}) - ${align2}") outTimes[${i}]=$(bc <<< "${outTimes[${i}]} + ${diff} - ${nDiff}") diff="${nDiff}" done diff=$(bc <<< "(${outEndTime} + ${align2}) % (2 * ${align2}) - ${align2}") outEndTime=$(bc <<< "${outEndTime} - ${diff}") fi trimcmd="" for (( i=1; i<=${#outTimes[@]}; i++ )) do trimcmd="${trimcmd}trim 0s =${outTimes[${i}]}s : " ${newFile[${i}]} && trimcmd="${trimcmd}newfile : " done [ ${outRate} -ne ${inRate} ] && remixcmd="${remixcmd} rate -v ${outRate}" if ${cdAusrichten} then output="-b 16 ${output}" remixcmd="${remixcmd} trim 0 =${outEndTime}s" extraIn=" -r ${inRate} -c ${#inputs[@]} -n" else extraIn="" fi if ! ${noGain} then firstline="${firstline} gain -b" remixcmd="${remixcmd} gain -n" fi for ((i=0; i<=${#inputs[@]}; i++)) do [ -z "${rejects[${i}]}" ] && continue inputs[${i}]="|sox ${inputs[${i}]} -p${rejects[${i}]}" done trackCount=$[1+$( echo "${trimcmd}" | \ tr ' ' '\n' | \ grep -c '^newfile$' || true )] if [ -n "${names}" ] && [ ${trackCount} -ne ${#names[@]} ] then >&2 echo "Die Anzahl der Namen (${#names[@]}) ist nicht gleich der Anzahl der Tracks (${trackCount})." >&2 echo "Das sollte eigentlich nicht passieren koennen." exit 1 fi echo sox ${tempDir} -M "${inputs[@]}" -p trim ${firstline} echo sox ${tempDir} -t sox - -p ${repcmd} echo sox ${tempDir} -t sox - -p ${trackmodcmd} echo sox ${tempDir} -t sox - ${extraIn} -p ${splicecmd} ${remixcmd} echo sox ${tempDir} -t sox - ${output} ${trimcmd} if [ -n "${names}" ] then for ((i=0; i<${#names[@]}; i++)) do echo mv "$(printf "%s/temp_%02d.%s" "${odn}" "$[${i}+1]" "${obn#*.}")" \ "$(printf "%s/%02d_%s.%s" "${odn}" "$[${i}+1]" "${names[${i}]}" "${obn#*.}")" done fi if ! ${dummy} then sox ${tempDir} -M "${inputs[@]}" -p trim ${firstline} | \ sox ${tempDir} -t sox - -p ${repcmd} | \ sox ${tempDir} -t sox - -p ${trackmodcmd} | \ sox ${tempDir} -t sox - ${extraIn} -p ${splicecmd} ${remixcmd} | \ sox ${tempDir} -t sox - ${output} ${trimcmd} if [ -n "${names}" ] then for ((i=0; i<${#names[@]}; i++)) do mv "$(printf "%s/temp_%02d.%s" "${odn}" "$[${i}+1]" "${obn#*.}")" \ "$(printf "%s/%02d_%s.%s" "${odn}" "$[${i}+1]" "${names[${i}]}" "${obn#*.}")" done fi fi