Jakieś oprogramowanie do wykrywania beatów dla systemu Linux? [Zamknięte]

29

Amarok 2 może przeszukiwać kolekcję muzyki za pomocą pola „bpm” znacznika ID3v2. Byłoby bardzo miło przeredagować całą kolekcję muzyki, dzięki czemu mogę znaleźć „nastrój” utworu, który lubię.

Jednak nie znalazłem żadnego oprogramowania do wykrywania uderzeń, które mogłoby mi pomóc. Czy kiedykolwiek go używałeś? Najlepiej CLI. Interesuje mnie również, czy istnieje coś podobnego do oznaczania plików FLAC tym samym polem „bpm”.

Dzięki! :)

PS Zdaję sobie sprawę, że jest fajna funkcja paska nastroju, jednak nie nadaje się ona do wyszukiwania.

kolypto
źródło
3
widziałeś tę stronę? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Wygląda dokładnie na to, czego szukasz.
DaveParillo,
@DaveParillo, że link „mood of a track” jest linkiem do twojego dysku twardego i jako taki jest bezużyteczny dla nikogo oprócz ciebie
Justin Smith
@Justin Smith, miał na myśli plik w dokumentach BpmDj :) Oto wersja online: bpmdj.yellowcouch.org/clustering.html
kolypto
@Justin - przepraszam - chyba drgający palec spustowy.
DaveParillo

Odpowiedzi:

17

Na stronie DaveParillo zasugerował, że znalazłem projekt BpmDj . Ma bpmcountplik wykonywalny, który bardzo ładnie oblicza bpm: obsługuje mp3, a także flac:

161.135 Metallica/2008 - Death Magnetic/01-That Was Just Your Life.flac
63.5645 Doom3.mp3

Jedyne, co pozostało, to przebudować kolekcję. Zaktualizuję tę odpowiedź, ilekroć mi się uda. Dzięki! :)


Krok 1

Uruchom bpmcountcałą kolekcję i zapisz wyniki w pliku tekstowym. Problem polega na tym, że bpmcountod czasu do czasu ulega awarii i próbuje zużyć do 2 GB pamięci, gdy przetwarza kilka plików, dlatego powinniśmy podawać je nazwami plików jeden po drugim. Lubię to:

musicdir='/home/ootync/music'
find "$musicdir" -iregex ".*\.\(mp3\|ogg\|flac\|ape\)" -exec bpmcount {} \; \
    | fgrep "$musicdir" > "$musicdir/BPMs.txt"

Krok 2

Będziemy potrzebowali kilka dodatkowych pakietów: apt-get install vorbis-tools flac python-mutagen. Zobacz, jak można dodać tag „bpm”:

mid3v2 --TBPM 100 doom3.mp3
vorbiscomment -a -t "BPM=100" mother.ogg
metaflac --set-tag="BPM=100" metallica.flac

Niestety, nie mam utworów * .ape

Teraz mamy BPM i cała kolekcja powinna zostać zmieniona. Oto skrypt:

cat "$musicdir/BPMs.txt" | while read bpm file ; do
    bpm=`printf "%.0f" "$bpm"` ;
    case "$file" in 
        *.mp3) mid3v2 --TBPM "$bpm" "$file" > /dev/null ;; 
        *.ogg) vorbiscomment -a -t "BPM=$bpm" "$file" ;; 
        *.flac) metaflac --set-tag="BPM=$bpm" "$file" ;; 
        esac
    done

Krok 2.1 Powrót do strony Oto skrypt, który doda tagi BPM do Twojej kolekcji.

Uruchamia jeden proces na rdzeń procesora, aby proces był szybszy. Ponadto nie używa plików tymczasowych i jest w stanie wykryć, czy plik jest już oznaczony.

Dodatkowo odkryłem, że FLAC czasami zawiera zarówno ID3, jak i VorbisComment. Ten skrypt aktualizuje oba.

#!/bin/bash

function display_help() {
    cat <<-HELP
            Recursive BPM-writer for multicore CPUs.
            It analyzes BPMs of every media file and writes a correct tag there.
            Usage: $(basename "$0") path [...]
            HELP
    exit 0
    }

[ $# -lt 1 ] && display_help

#=== Requirements
requires="bpmcount mid3v2 vorbiscomment metaflac"
which $requires > /dev/null || { echo "E: These binaries are required: $requires" >&2 ; exit 1; }

#=== Functions

function bpm_read(){
    local file="$1"
    local ext="${file##*.}"
    declare -l ext
    # Detect
    { case "$ext" in
        'mp3')  mid3v2 -l "$file" ;;
        'ogg')  vorbiscomment -l "$file" ;;
        'flac') metaflac --export-tags-to=- "$file" ;;
        esac ; } | fgrep 'BPM=' | cut -d'=' -f2
    }
function bpm_write(){
    local file="$1"
    local bpm="${2%%.*}"
    local ext="${file##*.}"
    declare -l ext
    echo "BPM=$bpm @$file"
    # Write
    case "$ext" in
        'mp3')  mid3v2 --TBPM "$bpm" "$file" ;;
        'ogg')  vorbiscomment -a -t "BPM=$bpm" "$file" ;;
        'flac') metaflac --set-tag="BPM=$bpm" "$file"
                mid3v2 --TBPM "$bpm" "$file" # Need to store to ID3 as well :(
                ;;
        esac
    }

#=== Process
function oneThread(){
    local file="$1"
    #=== Check whether there's an existing BPM
        local bpm=$(bpm_read "$file")
        [ "$bpm" != '' ] && return 0 # there's a nonempty BPM tag
    #=== Detect a new BPM
    # Detect a new bpm
    local bpm=$(bpmcount "$file" | grep '^[0-9]' | cut -f1)
    [ "$bpm" == '' ] && { echo "W: Invalid BPM '$bpm' detected @ $file" >&2 ; return 0 ; } # problems
    # Write it
    bpm_write "$file" "${bpm%%.*}" >/dev/null
    }

NUMCPU="$(grep ^processor /proc/cpuinfo | wc -l)"
find $@ -type f -regextype posix-awk -iregex '.*\.(mp3|ogg|flac)' \
    | while read file ; do
        [ `jobs -p | wc -l` -ge $NUMCPU ] && wait
        echo "$file"
        oneThread "$file" &
        done

Cieszyć się! :)

kolypto
źródło
Doskonały! Nie udało mi się spróbować tej ostatniej nocy. Jeśli chodzi o oznaczanie w wierszu poleceń, wypróbuj mid3v2: linux.die.net/man/1/mid3v2 , serwisowalny przynajmniej do momentu, aż Ex Falso będzie obsługiwać edycję wiersza poleceń. Id TBPM
id3v2
1
Dzięki, spróbuję za kilka dni i opublikuję wyniki :) Zastanawiam się, czy FLAC obsługuje takie rzeczy: muszę to sprawdzić.
kolypto
1
Dobra robota na kroku 2. Chciałbym móc dwukrotnie głosować!
DaveParillo
1
Dzięki :) Niestety, mój Amarok nie zauważył nowego tagu w plikach FLAC, które lubię najbardziej :)) zgłoszony błąd.
kolypto
Jak to zainstalowałeś? Oferowane przez nich obroty nie działają na moim komputerze i mam problemy z kompilacją.
pedrosaurio
8

To narzędzie wiersza polecenia do wykrywania BPM i umieszczania go w tagach plików FLAC:

http://www.pogo.org.uk/~mark/bpm-tools/

mmx
źródło
Najnowsza wersja obsługuje również pliki mp3 i ogg vorbis.
zakodowano
Ubuntu ma dostępne pakiety bpm-tools w pyskaty sposób.
naught101
6

Użyłem oryginalnego skryptu kolypto, używając go bpmcounti przepisałem go na bpm-tag(użyteczne bpm-tools), co miałem więcej szczęścia przy instalacji. Wprowadziłem też kilka ulepszeń.

Możesz go znaleźć na GitHub https://github.com/meridius/bpmwrap

południk
źródło
Wymagało to kilku modyfikacji do pracy na komputerze Mac, które zamieściłem we własnej odpowiedzi poniżej (ponieważ jest to zbyt długo na komentarz)
Adrian
2

Nie znam narzędzia, które robi dokładnie to, czego szukasz, ale bawiłem się z MusicIP .

Użyłem wersji linux / java - pełna analiza biblioteki muzycznej zajmuje dużo czasu, ale naprawdę działa. Możesz znaleźć utwory podobne do innych utworów. Możesz kliknąć prawym przyciskiem myszy wygenerowaną listę odtwarzania i wybrać opcję, aby wybrać więcej lub mniej utworów, takich jak ten wybrany. Możesz także wyeliminować określone gatunki. To trochę fajne, ale kiedy minął czynnik wow, przestałem go używać.

Darmowa wersja eksportuje listy odtwarzania do 75 utworów w (co najmniej) formacie m3u.

Obecnie nie jest obsługiwany, ale myślę, że starali się uznać go za komercyjny jako Predexis .

DaveParillo
źródło
1

Chociaż nie jest to tylko narzędzie, o którym mówisz, że szukasz, odtwarzacz multimedialny Banshee może wykryć bpm.

Używam Banshee do odtwarzania, organizacji i synchronizacji muzyki z przenośnymi odtwarzaczami. Nie jestem związany, ale podoba mi się ten program ze wszystkiego, co próbowałem. Może także generować „inteligentne listy odtwarzania” w oparciu o różne właściwości ścieżek, w tym bpm.

Istnieje rozszerzenie, które analizuje różne rzeczy związane z utworem i znajdzie podobne utwory do tego, w którym grasz. Nazywa się Mirage i używałem go przez jakiś czas, ale już go nie używam, ponieważ stworzyłem wiele list odtwarzania, które pasują do różnych nastrojów (niekoniecznie podobne według Mirage).

Nie wiem, czy Banshee zapisze wykryte bpm z powrotem w znaczniku ID3v2 „bpm” pliku. Jeśli ktoś wie, jak łatwo sprawdzić tag bpm spoza programu, sprawdzę.

Dom
źródło
0

Znalazłem inne narzędzie do oznaczania plików MP3 prawidłową wartością BPM.

To się nazywa BPMDetect . Otwarte źródło. Biblioteki QT działają więc dobrze pod Gnome. Pochodzi z GUI, ale można go skompilować jako wersję wyłącznie konsolową (uruchom „scons console = 1”, jak podano w pliku readme.txt).

W przeciwnym razie ostatecznie użyłem „bpmcount” z BpmDJ, ponieważ miałem trudności z kompilacją BPMDetect na 64-bitowym hoście Ubuntu (z powodu zależności fmodex). Więc wziąłem (bardzo fajny i dobrze napisany) skrypt powłoki powyżej (patrz poniżej), plik binarny „bpmcount” wyodrębniony z [x64 .rpm] [3] dostępny na stronie internetowej BpmDJ (właśnie wyodrębniłem .rpm z

pm2cpio bpmdj-4.2.pl2-0.x86_64.rpm|cpio -idv

i działało jak urok. Musiałem tylko zmodyfikować powyższy skrypt, ponieważ po wyjęciu z pudełka nie działał on po mojej stronie (problem ze stdout / stderr binarnego bpmcount). Moja modyfikacja dotyczy przekierowania pliku:

local bpm=$(bpmcount "$file" 3>&1 1>/dev/null 2>&3 | grep '^[0-9]' | cut -f1)
Sergio
źródło
0

Istnieje inne narzędzie zalecane w tym pytaniu dotyczące stackoverflow: aubio , które jest dostarczane wraz z modułami python.

Nie próbowałem tego, ponieważ byłem trochę zajęty kompilacją BpmDj . Na wypadek, gdyby ktoś inny zmagał się z podobnymi problemami podczas próby, chciałbym zdecydowanie upewnić się absolutnie:

  1. po pobraniu najnowszej wersji źródeł BpmDj
  2. mając zainstalowane odpowiednie biblioteki doładowania

Przy najnowszych aktualizacjach kompilatora g ++ wydaje się, że pojawiły się pewne problemy, szczególnie dotyczące ostatnich wydań debian i ubuntu. Gdy tylko zdał sobie sprawę z tych problemów, autor miał życzliwość naprawić pojawiające się niezgodności i skompletować nowe wydanie, które teraz kompiluje się jak urok. Więc każdy, kto był bliski popadnięcia w rozpacz z powodu nieustępliwych błędów kompilacji: jesteś teraz uratowany.

@ mmx , twoje narzędzia również wyglądają dobrze, ale na nich polegają SoX, które domyślnie nie mają funkcji mp3. Wymagają więc najpierw skompilowania SoX z obsługą Lame / MAD, co jest niestety zbyt dużym wysiłkiem dla ludzi tak leniwych jak ja.

J. Katzwinkel
źródło
0

Aby rozwiązanie @meridius działało na moim Macu, musiałem trochę dopracować i zmodyfikować skrypt:

# Let's install bpm-tools
git clone http://www.pogo.org.uk/~mark/bpm-tools.git
cd bpm-tools
make && make install
# There will be errors, but they did not affect the result

# The following three lines could be replaced by including this directory in your $PATH
ln -s <absolute path to bpm-tools>/bpm /usr/local/bin/bpm
ln -s <absolute path to bpm-tools>/bpm-tag /usr/local/bin/bpm-tag
ln -s <absolute path to bpm-tools>/bpm-graph /usr/local/bin/bpm-graph
cd ..

# Time to install a bunch of GNU tools
# Not all of these packages are strictly necessary for this script, but I decided I wanted the whole GNU toolchain in order to avoid this song-and-dance in the future
brew install coreutils findutils gnu-tar gnu-sed gawk gnutls gnu-indent gnu-getopt bash flac vorbis-tools
brew tap homebrew/dupes; brew install grep

# Now for Mutagen (contains mid3v2)
git clone https://github.com/nex3/mutagen.git
cd mutagen
./setup.py build
sudo ./setup.py install
# There will be errors, but they did not affect the result
cd ..

Następnie musiałem zmodyfikować skrypt, aby wskazywał na wersje GNU wszystkiego i kilka innych poprawek:

#!/usr/local/bin/bash

# ================================= FUNCTIONS =================================

function help() {
    less <<< 'BPMWRAP

Description:
    This BASH script is a wrapper for bpm-tag utility of bpm-tools and several
    audio tagging utilities. The purpose is to make BPM (beats per minute)
    tagging as easy as possible.
    Default behaviour is to look through working directory for *.mp3 files
    and compute and print their BPM in the following manner:
        [current (if any)] [computed] [filename]

Usage:
    bpmwrap [options] [directory or filenames]

Options:
    You can specify files to process by one of these ways:
        1) state files and/or directories containing them after options
        2) specify --import file
        3) specify --input file
    With either way you still can filter the resulting list using --type option(s).
    Remember that the script will process only mp3 files by default, unless
    specified otherwise!

    -i, --import file
        Use this option to set BPM tag for all files in given file instead of
        computing it. Expected format of every row is BPM number and absolute path
        to filename separated by semicolon like so:
            145;/home/trinity/music/Apocalyptica/07 beyond time.mp3
        Remember to use --write option too.
    -n, --input file
        Use this option to give the script list of FILES to process INSTEAD of paths
        where to look for them. Each row whould have one absolute path.
        This will bypass the searching part and is that way useful when you want
        to process large number of files several times. Like when you are not yet
        sure what BPM limits to set. Extension filtering will still work.
    -o, --output file
        Save output also to a file.
    -l, --list-save file
        Save list of files about to get processed. You can use this list later
        as a file for --input option.
    -t, --type filetype
        Extension of file type to work with. Defaults to mp3. Can be specified
        multiple times for more filetypes. Currently supported are mp3 ogg flac.
    -e, --existing-only
        Only show BPM for files that have it. Do NOT compute new one.
    -w, --write
        Write computed BPM to audio file but do NOT overwrite existing value.
    -f, --force
        Write computed BPM to audio file even if it already has one. Aplicable only
        with --write option.
    -m, --min minbpm
        Set minimal BPM to look for when computing. Defaults to bpm-tag minimum 84.
    -x, --max maxbpm
        Set maximal BPM to look for when computing. Defaults to bpm-tag maximum 146.
    -v, --verbose
        Show "progress" messages.
    -c, --csv-friendly
        Use semicolon (;) instead of space to separate output columns.
    -h, --help
        Show this help.

Note:
    Program bpm-tag (on whis is this script based) is looking only for lowercase
    file extensions. If you get 0 (zero) BPM, this should be the case. So just
    rename the file.

License:
    GPL V2

Links:
    bpm-tools (http://www.pogo.org.uk/~mark/bpm-tools/)

Dependencies:
    bpm-tag mid3v2 vorbiscomment metaflac

Author:
    Martin Lukeš ([email protected])
    Based on work of kolypto (http://superuser.com/a/129157/137326)
    '
}

# Usage: result=$(inArray $needle haystack[@])
# @param string needle
# @param array haystack
# @returns int (1 = NOT / 0 = IS) in array
function inArray() {
    needle="$1"
    haystack=("${!2}")
    out=1
    for e in "${haystack[@]}" ; do
        if [[ "$e" = "$needle" ]] ; then
            out=0
            break
        fi
    done
    echo $out
}

# Usage: result=$(implode $separator array[@])
# @param char separator
# @param array array to implode
# @returns string separated array elements
function implode() {
    separator="$1"
    array=("${!2}")
    IFSORIG=$IFS
    IFS="$separator"
    echo "${array[*]}"
    IFS=$IFSORIG
}

# @param string file
# @returns int BPM value
function getBpm() {
    local file="$1"
    local ext="${file##*.}"
    declare -l ext # convert to lowercase
    { case "$ext" in
        'mp3')  mid3v2 -l "$file" ;;
        'ogg')  vorbiscomment -l "$file" ;;
        'flac') metaflac --export-tags-to=- "$file" ;;
    esac ; } | fgrep 'BPM=' -a | cut -d'=' -f2
}

# @param string file
# @param int BPM value
function setBpm() {
    local file="$1"
    local bpm="${2%%.*}"
    local ext="${file##*.}"
    declare -l ext # convert to lowercase
    case "$ext" in
        'mp3')  mid3v2 --TBPM "$bpm" "$file" ;;
        'ogg')  vorbiscomment -a -t "BPM=$bpm" "$file" ;;
        'flac') metaflac --set-tag="BPM=$bpm" "$file"
            mid3v2 --TBPM "$bpm" "$file" # Need to store to ID3 as well :(
        ;;
    esac
}

# # @param string file
# # @returns int BPM value
function computeBpm() {
    local file="$1"
    local m_opt=""
    [ ! -z "$m" ] && m_opt="-m $m"
    local x_opt=""
    [ ! -z "$x" ] && x_opt="-x $x"
    local row=$(bpm-tag -fn $m_opt $x_opt "$file" 2>&1 | fgrep "$file")
    echo $(echo "$row" \
        | gsed -r 's/.+ ([0-9]+\.[0-9]{3}) BPM/\1/' \
        | gawk '{printf("%.0f\n", $1)}')
}

# @param string file
# @param int file number
# @param int BPM from file list given by --import option
function oneThread() {
    local file="$1"
    local filenumber="$2"
    local bpm_hard="$3"
    local bpm_old=$(getBpm "$file")
    [ -z "$bpm_old" ] && bpm_old="NONE"
    if [ "$e" ] ; then # only show existing
        myEcho "$filenumber/$NUMFILES${SEP}$bpm_old${SEP}$file"
    else # compute new one
        if [ "$bpm_hard" ] ; then
            local bpm_new="$bpm_hard"
        else
            local bpm_new=$(computeBpm "$file")
        fi
        [ "$w" ] && { # write new one
            if [[ ! ( ("$bpm_old" != "NONE") && ( -z "$f" ) ) ]] ; then
                setBpm "$file" "$bpm_new"
            else
                [ "$v" ] && myEcho "Non-empty old BPM value, skipping ..."
            fi
        }
        myEcho "$filenumber/$NUMFILES${SEP}$bpm_old${SEP}$bpm_new${SEP}$file"
    fi
}

function myEcho() {
    [ "$o" ] && echo -e "$1" >> "$o"
    echo -e "$1"
}


# ================================== OPTIONS ==================================

eval set -- $(/usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt -n $0 -o "-i:n:o:l:t:ewfm:x:vch" \
    -l "import:,input:,output:,list-save:,type:,existing-only,write,force,min:,max:,verbose,csv-friendly,help" -- "$@")

declare i n o l t e w f m x v c h
declare -a INPUTFILES
declare -a INPUTTYPES
while [ $# -gt 0 ] ; do
    case "$1" in
        -i|--import)                shift ; i="$1" ; shift ;;
        -n|--input)                 shift ; n="$1" ; shift ;;
        -o|--output)                shift ; o="$1" ; shift ;;
        -l|--list-save)         shift ; l="$1" ; shift ;;
        -t|--type)                  shift ; INPUTTYPES=("${INPUTTYPES[@]}" "$1") ; shift ;;
        -e|--existing-only) e=1 ; shift ;;
        -w|--write)                 w=1 ; shift ;;
        -f|--force)                 f=1 ; shift ;;
        -m|--min)                       shift ; m="$1" ; shift ;;
        -x|--max)                       shift ; x="$1" ; shift ;;
        -v|--verbose)               v=1 ; shift ;;
        -c|--csv-friendly)  c=1 ; shift ;;
        -h|--help)                  h=1 ; shift ;;
        --)                                 shift ;;
        -*)                                 echo "bad option '$1'" ; exit 1 ;; #FIXME why this exit isn't fired?
        *)                                  INPUTFILES=("${INPUTFILES[@]}" "$1") ; shift ;;
    esac
done


# ================================= DEFAULTS ==================================

#NOTE Remove what requisities you don't need but don't try to use them after!
#         always  mp3/flac     ogg       flac
REQUIRES="bpm-tag mid3v2 vorbiscomment metaflac"
which $REQUIRES > /dev/null || { myEcho "These binaries are required: $REQUIRES" >&2 ; exit 1; }

[ "$h" ] && {
    help
    exit 0
}

[[ $m && $x && ( $m -ge $x ) ]] && {
    myEcho "Minimal BPM can't be bigger than NOR same as maximal BPM!"
    exit 1
}
[[ "$i" && "$n" ]] && {
    echo "You cannot specify both -i and -n options!"
    exit 1
}
[[ "$i" && ( "$m" || "$x" ) ]] && {
    echo "You cannot use -m nor -x option with -i option!"
    exit 1
}
[ "$e" ] && {
    [[ "$w" || "$f" ]] && {
        echo "With -e option you don't have any value to write!"
        exit 1
    }
    [[ "$m" || "$x" ]] && {
        echo "With -e option you don't have any value to count!"
        exit 1
    }
}

for file in "$o" "$l" ; do
    if [ -f "$file" ] ; then
        while true ; do
            read -n1 -p "Do you want to overwrite existing file ${file}? (Y/n): " key
            case "$key" in
                y|Y|"") echo "" > "$file" ; break ;;
                n|N)        exit 0 ;;
            esac
            echo ""
        done
        echo ""
    fi
done

[ ${#INPUTTYPES} -eq 0 ] && INPUTTYPES=("mp3")

# NUMCPU="$(ggrep ^processor /proc/cpuinfo | wc -l)"
NUMCPU="$(sysctl -a | ggrep machdep.cpu.core_count | gsed -r 's/(.*)([0-9]+)(.*)/\2/')"
LASTPID=0
TYPESALLOWED=("mp3" "ogg" "flac")
# declare -A BPMIMPORT # array of BPMs from --import file, keys are file names
declare -A BPMIMPORT # array of BPMs from --import file, keys are file names

for type in "${INPUTTYPES[@]}" ; do
    [[ $(inArray $type TYPESALLOWED[@]) -eq 1 ]] && {
        myEcho "Filetype $type is not one of allowed types (${TYPESALLOWED[@]})!"
        exit 1
    }
done

### here are three ways how to pass files to the script...
if [ "$i" ] ; then # just parse given file list and set BPM to listed files
    if [ -f "$i" ] ; then
        # myEcho "Setting BPM tags from given file ..."
        while read row ; do
            bpm="${row%%;*}"
            file="${row#*;}"
            ext="${file##*.}"
            ext="${ext,,}" # convert to lowercase
            if [ -f "$file" ] ; then
                if [ $(inArray $ext INPUTTYPES[@]) -eq 0 ] ; then
                    FILES=("${FILES[@]}" "$file")
                    BPMIMPORT["$file"]="$bpm"
                else
                    myEcho "Skipping file on row $rownumber (unwanted filetype $ext) ... $file"
                fi
            else
                myEcho "Skipping non-existing file $file"
            fi
        done < "$i"
    else
        myEcho "Given import file does not exists!"
        exit 1
    fi
elif [ "$n" ] ; then # get files from file list
    if [ -f "$n" ] ; then
        rownumber=1
        while read file ; do
            if [ -f "$file" ] ; then
                ext="${file##*.}"
                ext="${ext,,}" # convert to lowercase
                if [ $(inArray $ext INPUTTYPES[@]) -eq 0 ] ; then
                    FILES=("${FILES[@]}" "$file")
                else
                    myEcho "Skipping file on row $rownumber (unwanted filetype $ext) ... $file"
                fi
            else
                myEcho "Skipping file on row $rownumber (non-existing) ... $file"
            fi
            let rownumber++
        done < "$n"
        unset rownumber
    else
        myEcho "Given input file $n does not exists!"
        exit 1
    fi
else # get files from given parameters
    [ ${#INPUTFILES[@]} -eq 0 ] && INPUTFILES=`pwd`
    for file in "${INPUTFILES[@]}" ; do
        [ ! -e "$file" ] && {
            myEcho "File or directory $file does not exist!"
            exit 1
        }
    done
    impl_types=`implode "|" INPUTTYPES[@]`
    while read file ; do
        echo -ne "Creating list of files ... (${#FILES[@]}) ${file}\033[0K"\\r
        FILES=("${FILES[@]}" "$file")
    done < <(gfind "${INPUTFILES[@]}" -type f -regextype posix-awk -iregex ".*\.($impl_types)")
    echo -e "Counted ${#FILES[@]} files\033[0K"\\r
fi

[ "$l" ] && printf '%s\n' "${FILES[@]}" > "$l"

NUMFILES=${#FILES[@]}
FILENUMBER=1

[ $NUMFILES -eq 0 ] && {
    myEcho "There are no ${INPUTTYPES[@]} files in given files/paths."
    exit 1
}

declare SEP=" "
[ "$c" ] && SEP=";"


# =============================== MAIN SECTION ================================

if [ "$e" ] ; then # what heading to show
    myEcho "num${SEP}old${SEP}filename"
else
    myEcho "num${SEP}old${SEP}new${SEP}filename"
fi

for file in "${FILES[@]}" ; do
    [ `jobs -p | wc -l` -ge $NUMCPU ] && wait
    [ "$v" ] && myEcho "Parsing (${FILENUMBER}/${NUMFILES})\t$file ..."
    oneThread "$file" "$FILENUMBER" "${BPMIMPORT[$file]}" &
    LASTPID="$!"
    let FILENUMBER++
done

[ "$v" ] && myEcho "Waiting for last process ..."
wait $LASTPID
[ "$v" ] && myEcho \\n"DONE"

Dziękujemy za twoją ciężką pracę @kolypto i @meridius.

... ból, przez który przechodzę, aby utrzymać przepływ pracy CLI i nie płacić za narzędzia muzyczne ...

Adrian
źródło