Skip to content

Instantly share code, notes, and snippets.

@rriemann
Created April 20, 2016 16:49
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rriemann/286dc826b0457fc071fa84faf6a8f6ef to your computer and use it in GitHub Desktop.
Save rriemann/286dc826b0457fc071fa84faf6a8f6ef to your computer and use it in GitHub Desktop.
BPM Tags
#!/bin/sh
#
# Copyright (C) 2013 Mark Hills <mark@xwax.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License version 2 for more details.
#
# You should have received a copy of the GNU General Public License
# version 2 along with this program; if not, write to the Free
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
#
# Analyse an audio file and add BPM metadata
#
set -e
usage()
{
cat <<END
bpm-tag (C) Copyright 2013 Mark Hills <mark@xwax.org>
Usage: bpm-tag [options] <file>
Tag an audio file with tempo (in beats-per-minute, BPM)
-f Ignore existing BPM value
-n Display BPM only, don't tag
-m Minimum detected BPM
-x Maximum detected BPM
-h Display this help message and exit
See the bpm-tag(1) man page for more information and examples.
END
}
# Parse command line arguments
FORCE=false
WRITE=true
ARGS=""
while getopts "fhnm:x:" OPT; do
case "$OPT" in
f)
FORCE=true
;;
n)
WRITE=false
;;
m)
ARGS="$ARGS -m $OPTARG"
;;
x)
ARGS="$ARGS -x $OPTARG"
;;
h)
usage
exit 0
;;
?)
exit 1
esac
done
shift $((OPTIND - 1))
if [ -z "$1" ]; then
usage >&2
exit 1
fi
set -u
FILE="$1"
shift
# Don't overwrite an existing BPM tag
case "$FILE" in
*.flac)
BPM=`metaflac --show-tag=BPM "$FILE" | sed -e 's/BPM=//'`
;;
*.mp3)
BPM=`mid3v2 --list "$FILE" | sed -n 's/^TBPM.*: \([0-9\.]\+\)/\1/p'`
;;
*.ogg)
BPM=`vorbiscomment "$FILE" | sed -n 's/^BPM=//p'`
;;
*)
echo "$FILE: file extension not known" >&2
exit 1
;;
esac
if [ -n "$BPM" ] && ! $FORCE; then
echo "$FILE: already tagged, $BPM BPM" >&2
exit 1
fi
# Analyse the BPM
BPM=`sox -V1 "$FILE" -r 44100 -e float -c 1 -t raw - | bpm $ARGS`
if [ -z "$BPM" ]; then
exit 1
fi
echo "$FILE: $BPM BPM" >&2
if ! $WRITE; then
exit 0
fi
# Write a BPM tag
case "$FILE" in
*.flac)
metaflac --remove-tag=BPM --set-tag="BPM=$BPM" "$FILE"
;;
*.mp3)
mid3v2 --TBPM "$BPM" "$FILE"
;;
*.ogg)
vorbiscomment -at "BPM=$BPM" "$FILE"
;;
*)
echo "$FILE: don't know how to tag this type of file" >&2
exit 1
esac
@rriemann
Copy link
Author

I changed:

  • id2v3 to mid2v3 due to encoding issues
  • I changed '-R' to '--list'

@kennyboy1978
Copy link

kennyboy1978 commented Jan 26, 2020

Hi, I had the exact same trouble as you in using id3v2 (it deleted all other tags!!!), I also followed your train of thought and did a straight swap with mid3v2. I didn't notice until yesterday that mid3v2 has a different output format to id3v2. Because of this the ignore function doesn't initialise and scans all files regardless.

My PC is in bits just now but off the top of my head:

id3v2 output is TBPM (SomeText):
mid3v2 output is TBPM=

I can send the edited line to you if you wish , when my PC is back up and running.

@rriemann
Copy link
Author

@kennyboy1978 Would be nice to have it here as a reference. Thx!

@kennyboy1978
Copy link

kennyboy1978 commented Jan 26, 2020

Edited for being an idiot, I was using the -f command in my script!

Anyway things back together again, the line is.....

BPM=mid3v2 -l "$FILE" | sed -En 's/^TBPM=([[:digit:].]+).*/\1/p'

@thewitchmaker
Copy link

Once this is all said and done, how do I have a program reference the BPM tag? if, for example, I wanted to change how something functions based on the BPM, how do I write the code to check that?

@rriemann
Copy link
Author

rriemann commented Apr 2, 2020

I used some playback apps that would read this value from files and display it in a bpm column that I can use to filter and sort after bpm.
Of course, you can also use another shell app to retrieve the tag value.

Some of those are listed in the various answers here: https://askubuntu.com/questions/226773/how-to-read-mp3-tags-in-shell

@kennyboy1978
Copy link

kennyboy1978 commented Apr 2, 2020

I use the below script (based on bpm-tag itself) to check whether the BPM tag is either 0 or empty and then run bpm-tag to generate the tag. If there is a tag found greater than 0 it prints the value of the tag. I know I've gone the long way around (I could have just edited/branched the bpm-tag script) but I'm attempting to learn basic bash and wanted to create my own using bpm-tag as the main program, if that makes sense.

I presume you could use the variables "$FOUNDBPM" (The BPM in a numercial value) and "${FILE%/*}" (The filename of the file being scanned) in your script ?

#!/bin/bash

find "$1" -type f ( -name ".mp3" -o -name ".flac" ) | while read -r FILE; do

case "$FILE" in
*.flac)
FOUNDBPM=$(metaflac --show-tag=BPM "$FILE" | sed -e 's/BPM=//')
;;
.mp3)
FOUNDBPM=$(mid3v2 -l "$FILE" | sed -En 's/^TBPM=([[:digit:].]+).
/\1/p')
;;
esac

if [ 0 == "$FOUNDBPM" ] || [ -z "$FOUNDBPM" ]
then
echo "${FILE%/}" contains a track that is has no BPM set, now running bpm-tag.
bpm-tag -f -m 120 -x 250 "$FILE"
else
echo "${FILE%/
}" has already been given the value of "$FOUNDBPM" BPM
fi
done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment