¿Algún software de detección de latidos para Linux? [cerrado]

29

Amarok 2 puede buscar en la colección de música usando el campo 'bpm' de la etiqueta ID3v2. Sería muy agradable volver a etiquetar toda la colección de música para poder encontrar el 'estado de ánimo' de la canción que me gusta.

Sin embargo, no he encontrado ningún software de detección de latidos que pueda haberme ayudado. ¿Alguna vez has usado uno? CLI, preferiblemente. También estoy interesado si hay algo similar para etiquetar FLAC con el mismo campo 'bpm'.

¡Gracias! :)

PD: Soy consciente de que hay una buena función de barra de humor, sin embargo, es inútil para buscar.

Kolypto
fuente
3
¿Has visto esta página? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Parece exactamente lo que está buscando.
DaveParillo
@DaveParillo ese enlace de "estado de ánimo de una pista" es un enlace a su disco duro, y como tal es inútil para cualquiera que no sea usted
Justin Smith
@Justin Smith, se refería a un archivo en BpmDj docs :) Aquí está la versión en línea: bpmdj.yellowcouch.org/clustering.html
kolypto
@Justin - lo siento - dedo de gatillo nervioso, supongo.
DaveParillo

Respuestas:

17

En el sitio DaveParillo sugirió que encontré el proyecto BpmDj . Tiene un bpmcountejecutable que calcula muy bien los bpm: maneja mp3 y flac:

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

Lo único que queda es volver a etiquetar la colección. Actualizaré esta respuesta cada vez que tenga éxito. ¡Gracias! :)


Paso 1

Ejecute bpmcountcontra toda la colección y almacene los resultados en un archivo de texto. El problema es que se bpmcountbloquea de vez en cuando e intenta consumir hasta 2 GB de memoria cuando procesa varios archivos, por lo que debemos alimentarlo con nombres de archivo uno por uno. Me gusta esto:

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

Paso 2

Vamos a necesitar algunos paquetes adicionales: apt-get install vorbis-tools flac python-mutagen. Ahora eche un vistazo a cómo se puede agregar la etiqueta 'bpm':

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

Por desgracia, no tengo pistas * .ape

Ahora tenemos los BPM y toda la colección debería volver a etiquetarse. Aquí está el guión:

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

Paso 2.1 Revisited Aquí hay un script que agregará etiquetas BPM a su colección.

Ejecuta un proceso por CPU Core para acelerar el proceso. Además, no utiliza archivos temporales y es capaz de detectar si un archivo ya está etiquetado.

Además, descubrí que FLAC a veces tiene ID3 y VorbisComment dentro. Este script actualiza ambos.

#!/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

¡Disfrutar! :)

Kolypto
fuente
¡Excelente! No había llegado a intentar esto anoche. En cuanto al etiquetado de la línea de comandos, intente mid3v2: linux.die.net/man/1/mid3v2 , al menos hasta que Ex Falso admita la edición de la línea de comandos. El id3v2 tad id esTBPM
DaveParillo
1
Gracias, lo intentaré en un par de días y publicaré los resultados :) Me pregunto si FLAC admite tal cosa: tendré que comprobar esto.
kolypto
1
Buen trabajo en el paso 2. ¡Ojalá pudiera votar dos veces!
DaveParillo
1
Gracias :) Por desgracia, mi Amarok no notó la nueva etiqueta en FLAC que más me gusta :)) error enviado.
kolypto
¿Cómo lo instalaste? las rpm que proporcionan no parecen funcionar en mi computadora y estoy luchando con la compilación.
pedrosaurio
6

Utilicé el script original de kolypto bpmcounty lo reescribí para bpm-tag(utilidad de bpm-tools) que tuve mejor suerte con la instalación. También hice algunas mejoras por mi cuenta.

Puede encontrarlo en GitHub https://github.com/meridius/bpmwrap

meridius
fuente
Esto requirió algunas modificaciones para trabajar en una Mac, que he incluido en mi propia respuesta a continuación (porque es demasiado largo para un comentario)
Adrian
2

No conozco una herramienta que haga exactamente lo que estás buscando, pero he jugado con MusicIP .

Utilicé la versión de Linux / Java: lleva mucho tiempo analizar completamente una biblioteca de música, pero realmente funciona. Puede encontrar canciones similares a otras canciones. Puede hacer clic derecho en la lista de reproducción generada y seleccionar la opción para seleccionar más o menos canciones como la seleccionada. También puedes elegir eliminar ciertos géneros. Es genial, pero después de que el factor sorpresa desapareció, dejé de usarlo.

La versión gratuita exporta listas de reproducción de hasta 75 canciones en (al menos) formato m3u.

Actualmente no es compatible, pero creo que han intentado tomarlo comercialmente como Predexis .

DaveParillo
fuente
1

Si bien no es solo una herramienta como usted dice que está buscando, el reproductor multimedia Banshee puede detectar bpm.

Uso Banshee para toda mi reproducción de música, organización y sincronización con reproductores portátiles. No estoy afiliado, pero me gusta el programa lo mejor que he probado. También puede generar "listas de reproducción inteligentes" basadas en todo tipo de propiedades de las pistas, incluidas las bpm.

Hay una extensión que analiza todo tipo de cosas sobre la canción y encontrará canciones similares a la que estás tocando. Se llama Mirage , y lo usé por un tiempo, pero ya no lo hago, ya que he creado una serie de listas de reproducción que se adaptan a varios estados de ánimo (no necesariamente similares según Mirage).

No sé si Banshee guardará los bpm que detectó en la etiqueta ID3v2 "bpm" del archivo. Si alguien sabe cómo verificar fácilmente la etiqueta bpm desde fuera del programa, lo comprobaré.

Dom
fuente
0

Encontré otra herramienta para etiquetar archivos MP3 con el valor BPM correcto.

Se llama BPMDetect . Fuente abierta. QT libs funciona muy bien en Gnome. Viene con una GUI pero se puede compilar como una versión de consola solamente (ejecute "scons console = 1" como se indica en el archivo readme.txt).

De lo contrario, al final, también usé el "bpmcount" de BpmDJ ya que tuve dificultades para compilar BPMDetect en un host Ubuntu de 64 bits (debido a la dependencia de fmodex). Así que tomé el script de shell (muy bueno y bien escrito) anterior (ver más abajo), el binario "bpmcount" extraído del [x64 .rpm] [3] disponible en el sitio web de BpmDJ (acabo de extraer el .rpm con

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

Y funcionó como un encanto. Solo tuve que modificar el script anterior ya que, de fábrica, no funcionaba de mi lado (problema con stdout / stderr del bpmcount binary). Mi modificación es sobre la redirección de archivos:

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

Hay otra herramienta recomendada en esta pregunta sobre stackoverflow: aubio , que viene junto con los módulos de Python.

No lo he intentado porque estaba un poco ocupado ocupándome de compilar BpmDj . En caso de que alguien más tenga problemas similares mientras lo intenta, me gustaría recomendar encarecidamente que se asegure absolutamente:

  1. habiendo descargado la última versión de las fuentes BpmDj
  2. tener instaladas las bibliotecas de impulso adecuadas

Con las últimas actualizaciones del compilador de g ++, algunos problemas parecen haber surgido especialmente con respecto a las recientes versiones de Debian y Ubuntu. Tan pronto como se dio cuenta de estos problemas, el autor tuvo la amabilidad de solucionar las incompatibilidades emergentes y armar un nuevo lanzamiento que ahora se compila como un encanto. Entonces, cualquiera que haya estado cerca de caer en la desesperación por los errores de compilación implacables últimamente: ahora está a salvo.

@ mmx , sus herramientas también se ven bien, pero dependen de ellas SoX, que por defecto no tienen funciones de mp3. Por lo tanto, primero requieren compilar SoX con soporte Lame / MAD, lo que desafortunadamente es demasiado esfuerzo para personas tan flojas como yo.

J. Katzwinkel
fuente
0

Para que la solución de @meridius funcione en mi Mac, tuve que hacer un poco de trabajo extra y modificar un poco el script:

# 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 ..

Luego tuve que modificar el script para señalar las versiones de GNU de todo, y algunos otros ajustes:

#!/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"

Gracias por su arduo trabajo @kolypto y @meridius.

... el dolor que sufro por mantener un flujo de trabajo de CLI y no pagar dinero por las herramientas de música ...

Adrian
fuente