Any beat detection software for Linux? [closed]
Amarok 2 can search through music collection using ID3v2 tag's 'bpm' field. That would be very nice to retag the entire music collection so I can find the 'mood' of the track I like.
However I've not found any beat-detection software that could have helped me. Have you ever used one? CLI, preferably. Also I'm interested if there's anything alike for tagging FLACs with the same 'bpm' field.
Thanks! :)
P.S. I'm aware there's a nice moodbar feature, however it's useless for searching.
linux mp3 music beat-detection bpm
closed as off-topic by Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb Dec 19 '18 at 19:39
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions seeking product, service, or learning material recommendations are off-topic because they become outdated quickly and attract opinion-based answers. Instead, describe your situation and the specific problem you're trying to solve. Share your research. Here are a few suggestions on how to properly ask this type of question." – Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb
If this question can be reworded to fit the rules in the help center, please edit the question.
add a comment |
Amarok 2 can search through music collection using ID3v2 tag's 'bpm' field. That would be very nice to retag the entire music collection so I can find the 'mood' of the track I like.
However I've not found any beat-detection software that could have helped me. Have you ever used one? CLI, preferably. Also I'm interested if there's anything alike for tagging FLACs with the same 'bpm' field.
Thanks! :)
P.S. I'm aware there's a nice moodbar feature, however it's useless for searching.
linux mp3 music beat-detection bpm
closed as off-topic by Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb Dec 19 '18 at 19:39
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions seeking product, service, or learning material recommendations are off-topic because they become outdated quickly and attract opinion-based answers. Instead, describe your situation and the specific problem you're trying to solve. Share your research. Here are a few suggestions on how to properly ask this type of question." – Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb
If this question can be reworded to fit the rules in the help center, please edit the question.
3
have you seen this page? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Seems exactly what you are looking for.
– DaveParillo
Apr 9 '10 at 4:24
@DaveParillo that "mood of a track" link is a link to your hard disk, and as such useless to anyone but you
– Justin Smith
Apr 11 '10 at 7:48
@Justin Smith, he meant a file in BpmDj docs :) Here's the online version: bpmdj.yellowcouch.org/clustering.html
– kolypto
Apr 11 '10 at 11:50
@Justin - sorry - twitchy trigger finger, I guess.
– DaveParillo
Apr 12 '10 at 4:28
add a comment |
Amarok 2 can search through music collection using ID3v2 tag's 'bpm' field. That would be very nice to retag the entire music collection so I can find the 'mood' of the track I like.
However I've not found any beat-detection software that could have helped me. Have you ever used one? CLI, preferably. Also I'm interested if there's anything alike for tagging FLACs with the same 'bpm' field.
Thanks! :)
P.S. I'm aware there's a nice moodbar feature, however it's useless for searching.
linux mp3 music beat-detection bpm
Amarok 2 can search through music collection using ID3v2 tag's 'bpm' field. That would be very nice to retag the entire music collection so I can find the 'mood' of the track I like.
However I've not found any beat-detection software that could have helped me. Have you ever used one? CLI, preferably. Also I'm interested if there's anything alike for tagging FLACs with the same 'bpm' field.
Thanks! :)
P.S. I'm aware there's a nice moodbar feature, however it's useless for searching.
linux mp3 music beat-detection bpm
linux mp3 music beat-detection bpm
asked Apr 9 '10 at 3:01
kolyptokolypto
1,98852234
1,98852234
closed as off-topic by Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb Dec 19 '18 at 19:39
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions seeking product, service, or learning material recommendations are off-topic because they become outdated quickly and attract opinion-based answers. Instead, describe your situation and the specific problem you're trying to solve. Share your research. Here are a few suggestions on how to properly ask this type of question." – Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb
If this question can be reworded to fit the rules in the help center, please edit the question.
closed as off-topic by Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb Dec 19 '18 at 19:39
This question appears to be off-topic. The users who voted to close gave this specific reason:
- "Questions seeking product, service, or learning material recommendations are off-topic because they become outdated quickly and attract opinion-based answers. Instead, describe your situation and the specific problem you're trying to solve. Share your research. Here are a few suggestions on how to properly ask this type of question." – Kamil Maciorowski, fixer1234, Twisty Impersonator, VL-80, bertieb
If this question can be reworded to fit the rules in the help center, please edit the question.
3
have you seen this page? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Seems exactly what you are looking for.
– DaveParillo
Apr 9 '10 at 4:24
@DaveParillo that "mood of a track" link is a link to your hard disk, and as such useless to anyone but you
– Justin Smith
Apr 11 '10 at 7:48
@Justin Smith, he meant a file in BpmDj docs :) Here's the online version: bpmdj.yellowcouch.org/clustering.html
– kolypto
Apr 11 '10 at 11:50
@Justin - sorry - twitchy trigger finger, I guess.
– DaveParillo
Apr 12 '10 at 4:28
add a comment |
3
have you seen this page? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Seems exactly what you are looking for.
– DaveParillo
Apr 9 '10 at 4:24
@DaveParillo that "mood of a track" link is a link to your hard disk, and as such useless to anyone but you
– Justin Smith
Apr 11 '10 at 7:48
@Justin Smith, he meant a file in BpmDj docs :) Here's the online version: bpmdj.yellowcouch.org/clustering.html
– kolypto
Apr 11 '10 at 11:50
@Justin - sorry - twitchy trigger finger, I guess.
– DaveParillo
Apr 12 '10 at 4:28
3
3
have you seen this page? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Seems exactly what you are looking for.
– DaveParillo
Apr 9 '10 at 4:24
have you seen this page? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Seems exactly what you are looking for.
– DaveParillo
Apr 9 '10 at 4:24
@DaveParillo that "mood of a track" link is a link to your hard disk, and as such useless to anyone but you
– Justin Smith
Apr 11 '10 at 7:48
@DaveParillo that "mood of a track" link is a link to your hard disk, and as such useless to anyone but you
– Justin Smith
Apr 11 '10 at 7:48
@Justin Smith, he meant a file in BpmDj docs :) Here's the online version: bpmdj.yellowcouch.org/clustering.html
– kolypto
Apr 11 '10 at 11:50
@Justin Smith, he meant a file in BpmDj docs :) Here's the online version: bpmdj.yellowcouch.org/clustering.html
– kolypto
Apr 11 '10 at 11:50
@Justin - sorry - twitchy trigger finger, I guess.
– DaveParillo
Apr 12 '10 at 4:28
@Justin - sorry - twitchy trigger finger, I guess.
– DaveParillo
Apr 12 '10 at 4:28
add a comment |
9 Answers
9
active
oldest
votes
At the site DaveParillo suggested I've found BpmDj project. It has a bpmcount executable that calculates the bpm very nice: it handles mp3 as well as flac:
161.135 Metallica/2008 - Death Magnetic/01-That Was Just Your Life.flac
63.5645 Doom3.mp3
The only thing that's left is to retag the collection. I'll update this answer whenever I succeed.
Thanks! :)
Step 1
Run bpmcount against the entire collection and store the results into a textfile.
The problem is that bpmcount crashes from time to time and tries to eat up to 2GB of memory when it processes several files so we should feed it with filenames one by one. Like this:
musicdir='/home/ootync/music'
find "$musicdir" -iregex ".*.(mp3|ogg|flac|ape)" -exec bpmcount {} ;
| fgrep "$musicdir" > "$musicdir/BPMs.txt"
Step 2
We'll need some additional packages: apt-get install vorbis-tools flac python-mutagen.
Now have a look at how the 'bpm' tag can be added:
mid3v2 --TBPM 100 doom3.mp3
vorbiscomment -a -t "BPM=100" mother.ogg
metaflac --set-tag="BPM=100" metallica.flac
Alas, I have no *.ape tracks
Now we have the BPMs and the entire collection should be retagged. Here's the script:
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
Step 2.1 Revisited
Here's a script that will add BPM tags to your collection.
It runs one process per CPU Core to make the process faster. Additionally, it uses no temporary files and it capable of detecting whether a file is already tagged.
Additionally, I've discovered that FLAC sometimes has both ID3 and VorbisComment inside. This script updates both.
#!/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
Enjoy! :)
Excellent! I hadn't gotten around to trying this last night. As far as command line tagging, try mid3v2: linux.die.net/man/1/mid3v2, serviceable at least until Ex Falso supports command line editing. The id3v2 tad id isTBPM
– DaveParillo
Apr 9 '10 at 16:24
1
Thanks, I'll try in a couple of days and post the results :) I wonder whether FLAC supports such thing: I'll have to check this out.
– kolypto
Apr 9 '10 at 19:51
1
Nice work on step #2. Wish I could upvote twice!
– DaveParillo
Apr 12 '10 at 4:31
1
Thanks :) Alas, my Amarok didn't notice the new tag in FLACs which I like the most :)) bug submitted.
– kolypto
Apr 12 '10 at 15:36
How did you install it? the rpm they provide doesn't seem to work in my computer and I am struggling with the compilation.
– pedrosaurio
Jul 25 '12 at 18:56
|
show 3 more comments
This is a command-line tool to detect the BPM and put it in the FLAC file tags:
http://www.pogo.org.uk/~mark/bpm-tools/
The latest version also handles mp3s and ogg vorbis.
– encoded
Jan 10 '13 at 20:51
Ubuntu has bpm-tools packages available in saucy.
– naught101
Apr 5 '14 at 6:44
add a comment |
I used kolypto's original script using bpmcount and rewrote it for bpm-tag (utility of bpm-tools) which I had better luck with installing. I also made some improvements of my own.
You can find it on GitHub https://github.com/meridius/bpmwrap
This required a few modifications to work on a Mac, which I have included in my own answer below (because it is too long for a comment)
– Adrian
Nov 17 '16 at 23:14
add a comment |
I don't know of a tool that does exactly what you are looking for, but I have played around with MusicIP.
Used the linux / java version - it takes a long time to completely analyze a music library, but it really does work. You can find songs that are similar to other songs. You can right click on the playlist generated and select option to select more or fewer songs like the one selected. You can also choose to eliminate certain genre's. It's kind of cool, but after the wow factor wore off, I stopped using it.
The free version exports playlists up to 75 songs in (at least) m3u format.
It's currently unsupported, but I think they have tried to take it commercial as Predexis.
add a comment |
While it is not just a tool like you say you are looking for, Banshee media player can detect bpm.
I use Banshee for all my music playing, organisation and synchronizing to portable players.
I'm not affiliated, but I like the program the best of all that I've tried.
It can also generate "smart playlists" based on all sorts of properties of the tracks, including bpm.
There is an extension which analyses all sorts of things about the song, and will find similar songs to the one you're playing. It's called Mirage, and I used it for a while, but I don't any more, as I've created a number of playlists of ones that suit various moods (not necessarily similar according to Mirage).
I don't know if Banshee will save the bpm it detected back into the ID3v2 "bpm" tag of the file. If anyone knows how to easily check the bpm tag from outside the program I'll check.
add a comment |
It's not Linux but may well work in Wine - I use MixMeister BPM Analyzer
add a comment |
I found another tool for tagging MP3 files with the correct BPM value.
It's called BPMDetect. Open-source. QT libs so works fine under Gnome. Comes with a GUI but can be compiled as a console only version (run "scons console=1" as stated in the readme.txt).
Otherwise, in the end, i've too used the "bpmcount" from BpmDJ as i had difficulties to compile BPMDetect on a 64 bits Ubuntu host (due to the fmodex dependency). So i took the (very cool and well-written) shell script above (see below), the "bpmcount" binary extracted from the [x64 .rpm][3] available on the BpmDJ website (i've just extract the .rpm with
pm2cpio bpmdj-4.2.pl2-0.x86_64.rpm|cpio -idv
and it worked like a charm. I just had to modify the above script as, out of the box, it weren't working on my side (problem with stdout / stderr of the bpmcount binary). My modification is about file redirection :
local bpm=$(bpmcount "$file" 3>&1 1>/dev/null 2>&3 | grep '^[0-9]' | cut -f1)
add a comment |
There is another tool recommended in this question on stackoverflow: aubio, which comes along with python modules.
I haven't tried it because I was kinda busy taking care of compiling BpmDj. Just in case anybody else finds themselves struggling similar troubles while trying, I'd like to strongly recommend to make absolutely sure:
- having downloaded the latest release of the BpmDj sources
- having the appropriate boost libraries installed
With the latest g++ compiler upgrades, some issues seem to have arisen especially concerning recent debian and ubuntu releases. As soon as he became aware of these problems, the author had the kindness to fix the emerged incompatibilities and put together a new release which now compiles like a charm. So anybody who have been close to falling into despair over relentless compile errors lately: you are save now.
@mmx, your tools look good too, but they rely on SoX, which by default has no mp3 features. So they require compiling SoX with Lame/MAD support first, which unfortunately is too much effort for people as lazy as me.
add a comment |
To get @meridius' solution working on my Mac I had to do a bit of extra legwork and modify the script a bit:
# 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 ..
Then I had to modify the script to point to the GNU versions of everything, and a few other tweaks:
#!/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š (martin.meridius@gmail.com)
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("%.0fn", $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}33[0K"\r
FILES=("${FILES[@]}" "$file")
done < <(gfind "${INPUTFILES[@]}" -type f -regextype posix-awk -iregex ".*.($impl_types)")
echo -e "Counted ${#FILES[@]} files33[0K"\r
fi
[ "$l" ] && printf '%sn' "${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"
Thank you for your hard work @kolypto and @meridius.
...the pain I go through to maintain a CLI workflow and pay no money for music tools...
add a comment |
9 Answers
9
active
oldest
votes
9 Answers
9
active
oldest
votes
active
oldest
votes
active
oldest
votes
At the site DaveParillo suggested I've found BpmDj project. It has a bpmcount executable that calculates the bpm very nice: it handles mp3 as well as flac:
161.135 Metallica/2008 - Death Magnetic/01-That Was Just Your Life.flac
63.5645 Doom3.mp3
The only thing that's left is to retag the collection. I'll update this answer whenever I succeed.
Thanks! :)
Step 1
Run bpmcount against the entire collection and store the results into a textfile.
The problem is that bpmcount crashes from time to time and tries to eat up to 2GB of memory when it processes several files so we should feed it with filenames one by one. Like this:
musicdir='/home/ootync/music'
find "$musicdir" -iregex ".*.(mp3|ogg|flac|ape)" -exec bpmcount {} ;
| fgrep "$musicdir" > "$musicdir/BPMs.txt"
Step 2
We'll need some additional packages: apt-get install vorbis-tools flac python-mutagen.
Now have a look at how the 'bpm' tag can be added:
mid3v2 --TBPM 100 doom3.mp3
vorbiscomment -a -t "BPM=100" mother.ogg
metaflac --set-tag="BPM=100" metallica.flac
Alas, I have no *.ape tracks
Now we have the BPMs and the entire collection should be retagged. Here's the script:
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
Step 2.1 Revisited
Here's a script that will add BPM tags to your collection.
It runs one process per CPU Core to make the process faster. Additionally, it uses no temporary files and it capable of detecting whether a file is already tagged.
Additionally, I've discovered that FLAC sometimes has both ID3 and VorbisComment inside. This script updates both.
#!/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
Enjoy! :)
Excellent! I hadn't gotten around to trying this last night. As far as command line tagging, try mid3v2: linux.die.net/man/1/mid3v2, serviceable at least until Ex Falso supports command line editing. The id3v2 tad id isTBPM
– DaveParillo
Apr 9 '10 at 16:24
1
Thanks, I'll try in a couple of days and post the results :) I wonder whether FLAC supports such thing: I'll have to check this out.
– kolypto
Apr 9 '10 at 19:51
1
Nice work on step #2. Wish I could upvote twice!
– DaveParillo
Apr 12 '10 at 4:31
1
Thanks :) Alas, my Amarok didn't notice the new tag in FLACs which I like the most :)) bug submitted.
– kolypto
Apr 12 '10 at 15:36
How did you install it? the rpm they provide doesn't seem to work in my computer and I am struggling with the compilation.
– pedrosaurio
Jul 25 '12 at 18:56
|
show 3 more comments
At the site DaveParillo suggested I've found BpmDj project. It has a bpmcount executable that calculates the bpm very nice: it handles mp3 as well as flac:
161.135 Metallica/2008 - Death Magnetic/01-That Was Just Your Life.flac
63.5645 Doom3.mp3
The only thing that's left is to retag the collection. I'll update this answer whenever I succeed.
Thanks! :)
Step 1
Run bpmcount against the entire collection and store the results into a textfile.
The problem is that bpmcount crashes from time to time and tries to eat up to 2GB of memory when it processes several files so we should feed it with filenames one by one. Like this:
musicdir='/home/ootync/music'
find "$musicdir" -iregex ".*.(mp3|ogg|flac|ape)" -exec bpmcount {} ;
| fgrep "$musicdir" > "$musicdir/BPMs.txt"
Step 2
We'll need some additional packages: apt-get install vorbis-tools flac python-mutagen.
Now have a look at how the 'bpm' tag can be added:
mid3v2 --TBPM 100 doom3.mp3
vorbiscomment -a -t "BPM=100" mother.ogg
metaflac --set-tag="BPM=100" metallica.flac
Alas, I have no *.ape tracks
Now we have the BPMs and the entire collection should be retagged. Here's the script:
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
Step 2.1 Revisited
Here's a script that will add BPM tags to your collection.
It runs one process per CPU Core to make the process faster. Additionally, it uses no temporary files and it capable of detecting whether a file is already tagged.
Additionally, I've discovered that FLAC sometimes has both ID3 and VorbisComment inside. This script updates both.
#!/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
Enjoy! :)
Excellent! I hadn't gotten around to trying this last night. As far as command line tagging, try mid3v2: linux.die.net/man/1/mid3v2, serviceable at least until Ex Falso supports command line editing. The id3v2 tad id isTBPM
– DaveParillo
Apr 9 '10 at 16:24
1
Thanks, I'll try in a couple of days and post the results :) I wonder whether FLAC supports such thing: I'll have to check this out.
– kolypto
Apr 9 '10 at 19:51
1
Nice work on step #2. Wish I could upvote twice!
– DaveParillo
Apr 12 '10 at 4:31
1
Thanks :) Alas, my Amarok didn't notice the new tag in FLACs which I like the most :)) bug submitted.
– kolypto
Apr 12 '10 at 15:36
How did you install it? the rpm they provide doesn't seem to work in my computer and I am struggling with the compilation.
– pedrosaurio
Jul 25 '12 at 18:56
|
show 3 more comments
At the site DaveParillo suggested I've found BpmDj project. It has a bpmcount executable that calculates the bpm very nice: it handles mp3 as well as flac:
161.135 Metallica/2008 - Death Magnetic/01-That Was Just Your Life.flac
63.5645 Doom3.mp3
The only thing that's left is to retag the collection. I'll update this answer whenever I succeed.
Thanks! :)
Step 1
Run bpmcount against the entire collection and store the results into a textfile.
The problem is that bpmcount crashes from time to time and tries to eat up to 2GB of memory when it processes several files so we should feed it with filenames one by one. Like this:
musicdir='/home/ootync/music'
find "$musicdir" -iregex ".*.(mp3|ogg|flac|ape)" -exec bpmcount {} ;
| fgrep "$musicdir" > "$musicdir/BPMs.txt"
Step 2
We'll need some additional packages: apt-get install vorbis-tools flac python-mutagen.
Now have a look at how the 'bpm' tag can be added:
mid3v2 --TBPM 100 doom3.mp3
vorbiscomment -a -t "BPM=100" mother.ogg
metaflac --set-tag="BPM=100" metallica.flac
Alas, I have no *.ape tracks
Now we have the BPMs and the entire collection should be retagged. Here's the script:
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
Step 2.1 Revisited
Here's a script that will add BPM tags to your collection.
It runs one process per CPU Core to make the process faster. Additionally, it uses no temporary files and it capable of detecting whether a file is already tagged.
Additionally, I've discovered that FLAC sometimes has both ID3 and VorbisComment inside. This script updates both.
#!/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
Enjoy! :)
At the site DaveParillo suggested I've found BpmDj project. It has a bpmcount executable that calculates the bpm very nice: it handles mp3 as well as flac:
161.135 Metallica/2008 - Death Magnetic/01-That Was Just Your Life.flac
63.5645 Doom3.mp3
The only thing that's left is to retag the collection. I'll update this answer whenever I succeed.
Thanks! :)
Step 1
Run bpmcount against the entire collection and store the results into a textfile.
The problem is that bpmcount crashes from time to time and tries to eat up to 2GB of memory when it processes several files so we should feed it with filenames one by one. Like this:
musicdir='/home/ootync/music'
find "$musicdir" -iregex ".*.(mp3|ogg|flac|ape)" -exec bpmcount {} ;
| fgrep "$musicdir" > "$musicdir/BPMs.txt"
Step 2
We'll need some additional packages: apt-get install vorbis-tools flac python-mutagen.
Now have a look at how the 'bpm' tag can be added:
mid3v2 --TBPM 100 doom3.mp3
vorbiscomment -a -t "BPM=100" mother.ogg
metaflac --set-tag="BPM=100" metallica.flac
Alas, I have no *.ape tracks
Now we have the BPMs and the entire collection should be retagged. Here's the script:
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
Step 2.1 Revisited
Here's a script that will add BPM tags to your collection.
It runs one process per CPU Core to make the process faster. Additionally, it uses no temporary files and it capable of detecting whether a file is already tagged.
Additionally, I've discovered that FLAC sometimes has both ID3 and VorbisComment inside. This script updates both.
#!/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
Enjoy! :)
edited Jun 16 '14 at 5:24
user277533
answered Apr 9 '10 at 11:16
kolyptokolypto
1,98852234
1,98852234
Excellent! I hadn't gotten around to trying this last night. As far as command line tagging, try mid3v2: linux.die.net/man/1/mid3v2, serviceable at least until Ex Falso supports command line editing. The id3v2 tad id isTBPM
– DaveParillo
Apr 9 '10 at 16:24
1
Thanks, I'll try in a couple of days and post the results :) I wonder whether FLAC supports such thing: I'll have to check this out.
– kolypto
Apr 9 '10 at 19:51
1
Nice work on step #2. Wish I could upvote twice!
– DaveParillo
Apr 12 '10 at 4:31
1
Thanks :) Alas, my Amarok didn't notice the new tag in FLACs which I like the most :)) bug submitted.
– kolypto
Apr 12 '10 at 15:36
How did you install it? the rpm they provide doesn't seem to work in my computer and I am struggling with the compilation.
– pedrosaurio
Jul 25 '12 at 18:56
|
show 3 more comments
Excellent! I hadn't gotten around to trying this last night. As far as command line tagging, try mid3v2: linux.die.net/man/1/mid3v2, serviceable at least until Ex Falso supports command line editing. The id3v2 tad id isTBPM
– DaveParillo
Apr 9 '10 at 16:24
1
Thanks, I'll try in a couple of days and post the results :) I wonder whether FLAC supports such thing: I'll have to check this out.
– kolypto
Apr 9 '10 at 19:51
1
Nice work on step #2. Wish I could upvote twice!
– DaveParillo
Apr 12 '10 at 4:31
1
Thanks :) Alas, my Amarok didn't notice the new tag in FLACs which I like the most :)) bug submitted.
– kolypto
Apr 12 '10 at 15:36
How did you install it? the rpm they provide doesn't seem to work in my computer and I am struggling with the compilation.
– pedrosaurio
Jul 25 '12 at 18:56
Excellent! I hadn't gotten around to trying this last night. As far as command line tagging, try mid3v2: linux.die.net/man/1/mid3v2, serviceable at least until Ex Falso supports command line editing. The id3v2 tad id is
TBPM– DaveParillo
Apr 9 '10 at 16:24
Excellent! I hadn't gotten around to trying this last night. As far as command line tagging, try mid3v2: linux.die.net/man/1/mid3v2, serviceable at least until Ex Falso supports command line editing. The id3v2 tad id is
TBPM– DaveParillo
Apr 9 '10 at 16:24
1
1
Thanks, I'll try in a couple of days and post the results :) I wonder whether FLAC supports such thing: I'll have to check this out.
– kolypto
Apr 9 '10 at 19:51
Thanks, I'll try in a couple of days and post the results :) I wonder whether FLAC supports such thing: I'll have to check this out.
– kolypto
Apr 9 '10 at 19:51
1
1
Nice work on step #2. Wish I could upvote twice!
– DaveParillo
Apr 12 '10 at 4:31
Nice work on step #2. Wish I could upvote twice!
– DaveParillo
Apr 12 '10 at 4:31
1
1
Thanks :) Alas, my Amarok didn't notice the new tag in FLACs which I like the most :)) bug submitted.
– kolypto
Apr 12 '10 at 15:36
Thanks :) Alas, my Amarok didn't notice the new tag in FLACs which I like the most :)) bug submitted.
– kolypto
Apr 12 '10 at 15:36
How did you install it? the rpm they provide doesn't seem to work in my computer and I am struggling with the compilation.
– pedrosaurio
Jul 25 '12 at 18:56
How did you install it? the rpm they provide doesn't seem to work in my computer and I am struggling with the compilation.
– pedrosaurio
Jul 25 '12 at 18:56
|
show 3 more comments
This is a command-line tool to detect the BPM and put it in the FLAC file tags:
http://www.pogo.org.uk/~mark/bpm-tools/
The latest version also handles mp3s and ogg vorbis.
– encoded
Jan 10 '13 at 20:51
Ubuntu has bpm-tools packages available in saucy.
– naught101
Apr 5 '14 at 6:44
add a comment |
This is a command-line tool to detect the BPM and put it in the FLAC file tags:
http://www.pogo.org.uk/~mark/bpm-tools/
The latest version also handles mp3s and ogg vorbis.
– encoded
Jan 10 '13 at 20:51
Ubuntu has bpm-tools packages available in saucy.
– naught101
Apr 5 '14 at 6:44
add a comment |
This is a command-line tool to detect the BPM and put it in the FLAC file tags:
http://www.pogo.org.uk/~mark/bpm-tools/
This is a command-line tool to detect the BPM and put it in the FLAC file tags:
http://www.pogo.org.uk/~mark/bpm-tools/
answered Oct 21 '12 at 21:42
mmxmmx
8111
8111
The latest version also handles mp3s and ogg vorbis.
– encoded
Jan 10 '13 at 20:51
Ubuntu has bpm-tools packages available in saucy.
– naught101
Apr 5 '14 at 6:44
add a comment |
The latest version also handles mp3s and ogg vorbis.
– encoded
Jan 10 '13 at 20:51
Ubuntu has bpm-tools packages available in saucy.
– naught101
Apr 5 '14 at 6:44
The latest version also handles mp3s and ogg vorbis.
– encoded
Jan 10 '13 at 20:51
The latest version also handles mp3s and ogg vorbis.
– encoded
Jan 10 '13 at 20:51
Ubuntu has bpm-tools packages available in saucy.
– naught101
Apr 5 '14 at 6:44
Ubuntu has bpm-tools packages available in saucy.
– naught101
Apr 5 '14 at 6:44
add a comment |
I used kolypto's original script using bpmcount and rewrote it for bpm-tag (utility of bpm-tools) which I had better luck with installing. I also made some improvements of my own.
You can find it on GitHub https://github.com/meridius/bpmwrap
This required a few modifications to work on a Mac, which I have included in my own answer below (because it is too long for a comment)
– Adrian
Nov 17 '16 at 23:14
add a comment |
I used kolypto's original script using bpmcount and rewrote it for bpm-tag (utility of bpm-tools) which I had better luck with installing. I also made some improvements of my own.
You can find it on GitHub https://github.com/meridius/bpmwrap
This required a few modifications to work on a Mac, which I have included in my own answer below (because it is too long for a comment)
– Adrian
Nov 17 '16 at 23:14
add a comment |
I used kolypto's original script using bpmcount and rewrote it for bpm-tag (utility of bpm-tools) which I had better luck with installing. I also made some improvements of my own.
You can find it on GitHub https://github.com/meridius/bpmwrap
I used kolypto's original script using bpmcount and rewrote it for bpm-tag (utility of bpm-tools) which I had better luck with installing. I also made some improvements of my own.
You can find it on GitHub https://github.com/meridius/bpmwrap
answered May 8 '14 at 17:38
meridiusmeridius
16316
16316
This required a few modifications to work on a Mac, which I have included in my own answer below (because it is too long for a comment)
– Adrian
Nov 17 '16 at 23:14
add a comment |
This required a few modifications to work on a Mac, which I have included in my own answer below (because it is too long for a comment)
– Adrian
Nov 17 '16 at 23:14
This required a few modifications to work on a Mac, which I have included in my own answer below (because it is too long for a comment)
– Adrian
Nov 17 '16 at 23:14
This required a few modifications to work on a Mac, which I have included in my own answer below (because it is too long for a comment)
– Adrian
Nov 17 '16 at 23:14
add a comment |
I don't know of a tool that does exactly what you are looking for, but I have played around with MusicIP.
Used the linux / java version - it takes a long time to completely analyze a music library, but it really does work. You can find songs that are similar to other songs. You can right click on the playlist generated and select option to select more or fewer songs like the one selected. You can also choose to eliminate certain genre's. It's kind of cool, but after the wow factor wore off, I stopped using it.
The free version exports playlists up to 75 songs in (at least) m3u format.
It's currently unsupported, but I think they have tried to take it commercial as Predexis.
add a comment |
I don't know of a tool that does exactly what you are looking for, but I have played around with MusicIP.
Used the linux / java version - it takes a long time to completely analyze a music library, but it really does work. You can find songs that are similar to other songs. You can right click on the playlist generated and select option to select more or fewer songs like the one selected. You can also choose to eliminate certain genre's. It's kind of cool, but after the wow factor wore off, I stopped using it.
The free version exports playlists up to 75 songs in (at least) m3u format.
It's currently unsupported, but I think they have tried to take it commercial as Predexis.
add a comment |
I don't know of a tool that does exactly what you are looking for, but I have played around with MusicIP.
Used the linux / java version - it takes a long time to completely analyze a music library, but it really does work. You can find songs that are similar to other songs. You can right click on the playlist generated and select option to select more or fewer songs like the one selected. You can also choose to eliminate certain genre's. It's kind of cool, but after the wow factor wore off, I stopped using it.
The free version exports playlists up to 75 songs in (at least) m3u format.
It's currently unsupported, but I think they have tried to take it commercial as Predexis.
I don't know of a tool that does exactly what you are looking for, but I have played around with MusicIP.
Used the linux / java version - it takes a long time to completely analyze a music library, but it really does work. You can find songs that are similar to other songs. You can right click on the playlist generated and select option to select more or fewer songs like the one selected. You can also choose to eliminate certain genre's. It's kind of cool, but after the wow factor wore off, I stopped using it.
The free version exports playlists up to 75 songs in (at least) m3u format.
It's currently unsupported, but I think they have tried to take it commercial as Predexis.
answered Apr 9 '10 at 4:21
DaveParilloDaveParillo
13k3444
13k3444
add a comment |
add a comment |
While it is not just a tool like you say you are looking for, Banshee media player can detect bpm.
I use Banshee for all my music playing, organisation and synchronizing to portable players.
I'm not affiliated, but I like the program the best of all that I've tried.
It can also generate "smart playlists" based on all sorts of properties of the tracks, including bpm.
There is an extension which analyses all sorts of things about the song, and will find similar songs to the one you're playing. It's called Mirage, and I used it for a while, but I don't any more, as I've created a number of playlists of ones that suit various moods (not necessarily similar according to Mirage).
I don't know if Banshee will save the bpm it detected back into the ID3v2 "bpm" tag of the file. If anyone knows how to easily check the bpm tag from outside the program I'll check.
add a comment |
While it is not just a tool like you say you are looking for, Banshee media player can detect bpm.
I use Banshee for all my music playing, organisation and synchronizing to portable players.
I'm not affiliated, but I like the program the best of all that I've tried.
It can also generate "smart playlists" based on all sorts of properties of the tracks, including bpm.
There is an extension which analyses all sorts of things about the song, and will find similar songs to the one you're playing. It's called Mirage, and I used it for a while, but I don't any more, as I've created a number of playlists of ones that suit various moods (not necessarily similar according to Mirage).
I don't know if Banshee will save the bpm it detected back into the ID3v2 "bpm" tag of the file. If anyone knows how to easily check the bpm tag from outside the program I'll check.
add a comment |
While it is not just a tool like you say you are looking for, Banshee media player can detect bpm.
I use Banshee for all my music playing, organisation and synchronizing to portable players.
I'm not affiliated, but I like the program the best of all that I've tried.
It can also generate "smart playlists" based on all sorts of properties of the tracks, including bpm.
There is an extension which analyses all sorts of things about the song, and will find similar songs to the one you're playing. It's called Mirage, and I used it for a while, but I don't any more, as I've created a number of playlists of ones that suit various moods (not necessarily similar according to Mirage).
I don't know if Banshee will save the bpm it detected back into the ID3v2 "bpm" tag of the file. If anyone knows how to easily check the bpm tag from outside the program I'll check.
While it is not just a tool like you say you are looking for, Banshee media player can detect bpm.
I use Banshee for all my music playing, organisation and synchronizing to portable players.
I'm not affiliated, but I like the program the best of all that I've tried.
It can also generate "smart playlists" based on all sorts of properties of the tracks, including bpm.
There is an extension which analyses all sorts of things about the song, and will find similar songs to the one you're playing. It's called Mirage, and I used it for a while, but I don't any more, as I've created a number of playlists of ones that suit various moods (not necessarily similar according to Mirage).
I don't know if Banshee will save the bpm it detected back into the ID3v2 "bpm" tag of the file. If anyone knows how to easily check the bpm tag from outside the program I'll check.
answered Apr 9 '10 at 10:35
DomDom
546312
546312
add a comment |
add a comment |
It's not Linux but may well work in Wine - I use MixMeister BPM Analyzer
add a comment |
It's not Linux but may well work in Wine - I use MixMeister BPM Analyzer
add a comment |
It's not Linux but may well work in Wine - I use MixMeister BPM Analyzer
It's not Linux but may well work in Wine - I use MixMeister BPM Analyzer
answered Apr 9 '10 at 16:42
ShevekShevek
14.1k54075
14.1k54075
add a comment |
add a comment |
I found another tool for tagging MP3 files with the correct BPM value.
It's called BPMDetect. Open-source. QT libs so works fine under Gnome. Comes with a GUI but can be compiled as a console only version (run "scons console=1" as stated in the readme.txt).
Otherwise, in the end, i've too used the "bpmcount" from BpmDJ as i had difficulties to compile BPMDetect on a 64 bits Ubuntu host (due to the fmodex dependency). So i took the (very cool and well-written) shell script above (see below), the "bpmcount" binary extracted from the [x64 .rpm][3] available on the BpmDJ website (i've just extract the .rpm with
pm2cpio bpmdj-4.2.pl2-0.x86_64.rpm|cpio -idv
and it worked like a charm. I just had to modify the above script as, out of the box, it weren't working on my side (problem with stdout / stderr of the bpmcount binary). My modification is about file redirection :
local bpm=$(bpmcount "$file" 3>&1 1>/dev/null 2>&3 | grep '^[0-9]' | cut -f1)
add a comment |
I found another tool for tagging MP3 files with the correct BPM value.
It's called BPMDetect. Open-source. QT libs so works fine under Gnome. Comes with a GUI but can be compiled as a console only version (run "scons console=1" as stated in the readme.txt).
Otherwise, in the end, i've too used the "bpmcount" from BpmDJ as i had difficulties to compile BPMDetect on a 64 bits Ubuntu host (due to the fmodex dependency). So i took the (very cool and well-written) shell script above (see below), the "bpmcount" binary extracted from the [x64 .rpm][3] available on the BpmDJ website (i've just extract the .rpm with
pm2cpio bpmdj-4.2.pl2-0.x86_64.rpm|cpio -idv
and it worked like a charm. I just had to modify the above script as, out of the box, it weren't working on my side (problem with stdout / stderr of the bpmcount binary). My modification is about file redirection :
local bpm=$(bpmcount "$file" 3>&1 1>/dev/null 2>&3 | grep '^[0-9]' | cut -f1)
add a comment |
I found another tool for tagging MP3 files with the correct BPM value.
It's called BPMDetect. Open-source. QT libs so works fine under Gnome. Comes with a GUI but can be compiled as a console only version (run "scons console=1" as stated in the readme.txt).
Otherwise, in the end, i've too used the "bpmcount" from BpmDJ as i had difficulties to compile BPMDetect on a 64 bits Ubuntu host (due to the fmodex dependency). So i took the (very cool and well-written) shell script above (see below), the "bpmcount" binary extracted from the [x64 .rpm][3] available on the BpmDJ website (i've just extract the .rpm with
pm2cpio bpmdj-4.2.pl2-0.x86_64.rpm|cpio -idv
and it worked like a charm. I just had to modify the above script as, out of the box, it weren't working on my side (problem with stdout / stderr of the bpmcount binary). My modification is about file redirection :
local bpm=$(bpmcount "$file" 3>&1 1>/dev/null 2>&3 | grep '^[0-9]' | cut -f1)
I found another tool for tagging MP3 files with the correct BPM value.
It's called BPMDetect. Open-source. QT libs so works fine under Gnome. Comes with a GUI but can be compiled as a console only version (run "scons console=1" as stated in the readme.txt).
Otherwise, in the end, i've too used the "bpmcount" from BpmDJ as i had difficulties to compile BPMDetect on a 64 bits Ubuntu host (due to the fmodex dependency). So i took the (very cool and well-written) shell script above (see below), the "bpmcount" binary extracted from the [x64 .rpm][3] available on the BpmDJ website (i've just extract the .rpm with
pm2cpio bpmdj-4.2.pl2-0.x86_64.rpm|cpio -idv
and it worked like a charm. I just had to modify the above script as, out of the box, it weren't working on my side (problem with stdout / stderr of the bpmcount binary). My modification is about file redirection :
local bpm=$(bpmcount "$file" 3>&1 1>/dev/null 2>&3 | grep '^[0-9]' | cut -f1)
answered Jan 14 '12 at 14:05
SergioSergio
25515
25515
add a comment |
add a comment |
There is another tool recommended in this question on stackoverflow: aubio, which comes along with python modules.
I haven't tried it because I was kinda busy taking care of compiling BpmDj. Just in case anybody else finds themselves struggling similar troubles while trying, I'd like to strongly recommend to make absolutely sure:
- having downloaded the latest release of the BpmDj sources
- having the appropriate boost libraries installed
With the latest g++ compiler upgrades, some issues seem to have arisen especially concerning recent debian and ubuntu releases. As soon as he became aware of these problems, the author had the kindness to fix the emerged incompatibilities and put together a new release which now compiles like a charm. So anybody who have been close to falling into despair over relentless compile errors lately: you are save now.
@mmx, your tools look good too, but they rely on SoX, which by default has no mp3 features. So they require compiling SoX with Lame/MAD support first, which unfortunately is too much effort for people as lazy as me.
add a comment |
There is another tool recommended in this question on stackoverflow: aubio, which comes along with python modules.
I haven't tried it because I was kinda busy taking care of compiling BpmDj. Just in case anybody else finds themselves struggling similar troubles while trying, I'd like to strongly recommend to make absolutely sure:
- having downloaded the latest release of the BpmDj sources
- having the appropriate boost libraries installed
With the latest g++ compiler upgrades, some issues seem to have arisen especially concerning recent debian and ubuntu releases. As soon as he became aware of these problems, the author had the kindness to fix the emerged incompatibilities and put together a new release which now compiles like a charm. So anybody who have been close to falling into despair over relentless compile errors lately: you are save now.
@mmx, your tools look good too, but they rely on SoX, which by default has no mp3 features. So they require compiling SoX with Lame/MAD support first, which unfortunately is too much effort for people as lazy as me.
add a comment |
There is another tool recommended in this question on stackoverflow: aubio, which comes along with python modules.
I haven't tried it because I was kinda busy taking care of compiling BpmDj. Just in case anybody else finds themselves struggling similar troubles while trying, I'd like to strongly recommend to make absolutely sure:
- having downloaded the latest release of the BpmDj sources
- having the appropriate boost libraries installed
With the latest g++ compiler upgrades, some issues seem to have arisen especially concerning recent debian and ubuntu releases. As soon as he became aware of these problems, the author had the kindness to fix the emerged incompatibilities and put together a new release which now compiles like a charm. So anybody who have been close to falling into despair over relentless compile errors lately: you are save now.
@mmx, your tools look good too, but they rely on SoX, which by default has no mp3 features. So they require compiling SoX with Lame/MAD support first, which unfortunately is too much effort for people as lazy as me.
There is another tool recommended in this question on stackoverflow: aubio, which comes along with python modules.
I haven't tried it because I was kinda busy taking care of compiling BpmDj. Just in case anybody else finds themselves struggling similar troubles while trying, I'd like to strongly recommend to make absolutely sure:
- having downloaded the latest release of the BpmDj sources
- having the appropriate boost libraries installed
With the latest g++ compiler upgrades, some issues seem to have arisen especially concerning recent debian and ubuntu releases. As soon as he became aware of these problems, the author had the kindness to fix the emerged incompatibilities and put together a new release which now compiles like a charm. So anybody who have been close to falling into despair over relentless compile errors lately: you are save now.
@mmx, your tools look good too, but they rely on SoX, which by default has no mp3 features. So they require compiling SoX with Lame/MAD support first, which unfortunately is too much effort for people as lazy as me.
edited May 23 '17 at 12:41
Community♦
1
1
answered Dec 28 '12 at 17:23
J. KatzwinkelJ. Katzwinkel
1011
1011
add a comment |
add a comment |
To get @meridius' solution working on my Mac I had to do a bit of extra legwork and modify the script a bit:
# 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 ..
Then I had to modify the script to point to the GNU versions of everything, and a few other tweaks:
#!/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š (martin.meridius@gmail.com)
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("%.0fn", $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}33[0K"\r
FILES=("${FILES[@]}" "$file")
done < <(gfind "${INPUTFILES[@]}" -type f -regextype posix-awk -iregex ".*.($impl_types)")
echo -e "Counted ${#FILES[@]} files33[0K"\r
fi
[ "$l" ] && printf '%sn' "${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"
Thank you for your hard work @kolypto and @meridius.
...the pain I go through to maintain a CLI workflow and pay no money for music tools...
add a comment |
To get @meridius' solution working on my Mac I had to do a bit of extra legwork and modify the script a bit:
# 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 ..
Then I had to modify the script to point to the GNU versions of everything, and a few other tweaks:
#!/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š (martin.meridius@gmail.com)
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("%.0fn", $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}33[0K"\r
FILES=("${FILES[@]}" "$file")
done < <(gfind "${INPUTFILES[@]}" -type f -regextype posix-awk -iregex ".*.($impl_types)")
echo -e "Counted ${#FILES[@]} files33[0K"\r
fi
[ "$l" ] && printf '%sn' "${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"
Thank you for your hard work @kolypto and @meridius.
...the pain I go through to maintain a CLI workflow and pay no money for music tools...
add a comment |
To get @meridius' solution working on my Mac I had to do a bit of extra legwork and modify the script a bit:
# 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 ..
Then I had to modify the script to point to the GNU versions of everything, and a few other tweaks:
#!/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š (martin.meridius@gmail.com)
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("%.0fn", $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}33[0K"\r
FILES=("${FILES[@]}" "$file")
done < <(gfind "${INPUTFILES[@]}" -type f -regextype posix-awk -iregex ".*.($impl_types)")
echo -e "Counted ${#FILES[@]} files33[0K"\r
fi
[ "$l" ] && printf '%sn' "${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"
Thank you for your hard work @kolypto and @meridius.
...the pain I go through to maintain a CLI workflow and pay no money for music tools...
To get @meridius' solution working on my Mac I had to do a bit of extra legwork and modify the script a bit:
# 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 ..
Then I had to modify the script to point to the GNU versions of everything, and a few other tweaks:
#!/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š (martin.meridius@gmail.com)
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("%.0fn", $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}33[0K"\r
FILES=("${FILES[@]}" "$file")
done < <(gfind "${INPUTFILES[@]}" -type f -regextype posix-awk -iregex ".*.($impl_types)")
echo -e "Counted ${#FILES[@]} files33[0K"\r
fi
[ "$l" ] && printf '%sn' "${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"
Thank you for your hard work @kolypto and @meridius.
...the pain I go through to maintain a CLI workflow and pay no money for music tools...
answered Nov 17 '16 at 23:23
AdrianAdrian
1663
1663
add a comment |
add a comment |
3
have you seen this page? mmartins.com/mmartins/bpmdetection/bpmdetection.asp Seems exactly what you are looking for.
– DaveParillo
Apr 9 '10 at 4:24
@DaveParillo that "mood of a track" link is a link to your hard disk, and as such useless to anyone but you
– Justin Smith
Apr 11 '10 at 7:48
@Justin Smith, he meant a file in BpmDj docs :) Here's the online version: bpmdj.yellowcouch.org/clustering.html
– kolypto
Apr 11 '10 at 11:50
@Justin - sorry - twitchy trigger finger, I guess.
– DaveParillo
Apr 12 '10 at 4:28