I have to deal with some lossless music folders, to "export" them in lossy formats to use on smartphones or music players or just to share with some "less geeky" people. You can compress with software like soundcounverter on MX. I find it clumsy when you have to convert a whole folder : you have to write the whole path of a new destination folder, or to convert "in-place" and then create manually a folder. I have written a script to meet my needs, and called it muscomp (music compresser).
muscomp features :
- takes files and/or folders as arguments. For .flac, .wav and .aiff files (lossless formats).
- if folder is given, it strips any [FLAC*] tag from the folder name, and optionnally adds a new [format] tag in the destination folder name and creates the latter in the same parent folder. Pictures and pdf from original folders are copied as well in the new folder. Should be nice to quicly convert lossless album.
- you can convert to opus (default), ogg or mp3.
- there are presets for quality : low, medium, high (default, for good music files) and excellent.
- you can manually set quality
- it handles most of metadata (and for mp3, more than ffmpeg, in id3v2 format). But no embedded pictures.
- and (my pleasure) : you can throw arguments in any order, with or without trailing "-". That’s great not to have to remember anything about right formatting.
- it is a wrapper for following commands : opusenc (from opus-tool package), oggenc (from vorbis-tool package) and lame (mp3). You can use your self-compiled version of these reference commands. If not installed or if you force it, the script relies on ffmpeg.
- (new feature - edited 05/05/24) a sync mode that let you duplicate music folders recursively, including lossy formats and converting lossless formats.
- it is fast thanks to parallelization (xarg commands with -P0 option, that runs as many conversion as it can run simultaneously).
- it is a light bash script. Perfect to embed in your file manager and convert files and folders on-the-fly.
Something like that will work : muscomp vorb folder1 folder2 medium
Here is the help text (edited 05/05/24 - v2.0 version) :
And here is the whole script if you care to have a look, and/or test it for your own use. Ideas are welcome.MUSCOMP – CONVERTER FROM LOSSLESS TO LOSSY FORMATS
##################################################
This script was written by Girafenaine
first version v0.1 13/04/2024, this version v2.0 04/05/2024
Released under GNU General Public License V3
USAGE
#####
muscomp "foldername" "filename"
--> to get ~180 kb/s opus. If there is a [FLAC] tag in foldername, a new folder without
this tag will be created, and pictures and pdf in "foldername" will be copied in it as well.
muscomp 128 opus "folderA" "folderB"
--> to get ~128 kb/s opus. New folders "folderX [opus]" will be created if not existing,
and pictures and pdf from original folders will be copied in them as well.
muscomp "fileA" "fileB" -vorb 5,5
--> to get ~140 kb/s ogg vorbis. Ouput files in same folder as input files.
muscomp sync mp3 med "foldername"
--> to get ~128 kb/s mp3. Special sync mode, all files are searched for recursively in
"foldername" and compressed files are created in the new "foldername [mp3]" folder. Lossy
files in original folder are added to the synced folders by default.
muscomp "folderA" folderB" ogg max
--> to get ~256 kb/s vorbis. No [ogg] added in target folder names since "ogg" is given after
foldername, but any [FLAC] tag in initial folder name will still be removed.
DESCRIPTION
###########
This script takes all flac, wav or aiff files in the given folders, or some isolated files,
and convert them into a lossy format : opus (default), vorbis or mp3. It is quite fast thanks to
parallels tasks. Original lossless files are never overwritten. Output Opus, vorbis or mp3 files
ARE overwritten. This script is an advanced wrapper for opusenc, oggenc, lame or ffmpeg commands.
Files given as arguments are converted in their own folder.
When folders are given as arguments :
- a new folder may be created : the script strips any [*(FLAC|WAV|AIFF)*] tag (or lower case) from
the folder name. It then adds a [format] tag in the target folder name, but only if the format
argument is given BEFORE the folder to convert. It lets you chose whether you want a [format] tag.
- the files are not searched for recursively, only first-level files are converted.
- jpg and pdf files in the original folder are copied in the destination folder, if any.
A special mode (-a or -all) convert all files recursively from the folders given as arguments. The
files are converted in-place, no folders are created.
This script use "vbr" (variable bit rate) mode for all three formats, as it is known as the best
quality/size ratio, much better than similar fixed rate. The default behaviour is to convert in opus
with a 175 kb/s average bitrate. Opus gives the best quality/size format and is a fully open
specification.
SPECIAL SYNC MODE
#################
Sync mode let you have a "lossy version" of part of, or your whole music library. Could be useful to
prepare music for a portable device. By contrast with the default mode, sync mode :
- explores subfolders recursively
- converts files only if a similar file does not already exist in destination
- will sync lossy format files as well (can be avoid with -l(ossless-only) option)
- you can still force conversion even if files already exist with -f(orce) option
(eg. you want to change quality)
ARGUMENTS
#########
You can give arguments, with or without any leading "-" or "--" and in any order :
- mp3, vor(b(is)) or ogg, op(us)
to switch to desired format. Opus is the defautlt. If given before folder name,
a new folder with [format] tag will be created if non-existing.
- any number
will be interpreted as quality target. However, the three formats have different
ways to specify quality target :
- for opus : you have to give the target bitrate in kb/s.
- for vorbis and mp3 : the quality is given on a specific scale.
- lo(w(er)), med(ium), hi(gh) or ex(cel(lent))
presets to use instead of quality settings.
"high" preset is the default setting. "lower" is for voice usage.
- s(ync)
special mode to convert all files recursively from given folders.
To be given BEFORE folder name. See above.
- d((est)ination) FOLDERNAME
let you give a new or existing destination folder, instead of the
automatic folder naming. The destination folder will be use for all
arguments given AFTER this option on the command line. Not well suited
if you have several folders to convert.
- ff(mpeg)
to force use of ffmpeg instead of oggenc, opusenc or lame
- v(erbose)
to get the full output of conversion command
- another argument
will be intepreted as folders or files to convert
The default qualities for three formats are set for high quality music tracks. Some presets
can be used. They can be guidelines for you to set quality according to your needs :
- opus : 128 kb/s and above should be transparent for music. Lower are great for speech.
Presets : low=80 kb/s. medium=120 kb/s. high(default)=175 kb/s. excellent=215 kb/s.
- vorbis : scale from 1 (lowest) to 10 (best). 5+ should be transparent. Presets :
low=2 (96 kb/s). medium=4 (128 kb/s). high(default)=6,5 (220 kb/s). excellent=7,5 (250 kb/s).
- mp3 : scale from 0 (best) to 10 (lowest). Beware of inverted scale. Presets :
low=6 (115 kb/s). medium=4,5 (147 kb/s). high(default)=1 (225 kb/s). excellent=0 (245 kb/s).
(edited 05/05/24 - v2.0)
Code: Select all
#!/usr/bin/env bash
#MUSCOMP – CONVERTER FROM LOSSLESS TO LOSSY FORMATS
#This script is written by Girafenaine
#first version v1.0 13/04/2024
#V2.0 05/05/24
#Released under GNU General Public License V3
set -euo pipefail
[[ "${TRACE-}" == "1" ]] && set -x
###VARIABLES AND DEFAULTS SETTINGS
#Default format and quality
format_default="opus"
format=""
opus_presets=(80 120 175 215)
vorbis_presets=(2 4 6,5 7,5)
mp3_presets=(6 4,5 1 0)
quality=""
preset=2
tag_folder=""
global_dest=""
#To deal with "verbosity"
inverted_verbose="--quiet"
verbose=""
#parameters to default to "no recursive search" (normal/sync mode)
mode="normal"
depth="-maxdepth 1"
exclude_existing=""
lossytracks="yes"
convert_force_switch=0
#xargs paramaters
proc_nb=0
#switch to force use of ffmpeg over oggenc and opusenc
ffmpeg_switch=0
#Internal variables
tempfile="/tmp/tempcomp"
trap 'rm -f "$tempfile" ' EXIT
touch "$tempfile"
conversion_command=""
id3v2tags_command=""
###ARGUMENTS - to deal with given arguments in a smart way and write down the list of files to convert
while [ $# -gt 0 ] ; do
if [[ "$1" =~ ^-*h(elp)?$ ]] ; then
echo '
MUSCOMP – CONVERTER FROM LOSSLESS TO LOSSY FORMATS
##################################################
This script was written by Girafenaine
first version v0.1 13/04/2024, this version v2.0 05/05/2024
Released under GNU General Public License V3
USAGE
#####
muscomp "foldername" "filename"
--> to get ~180 kb/s opus. If there is a [FLAC] tag in foldername, a new folder without
this tag will be created, and pictures and pdf in "foldername" will be copied in it as well.
muscomp 128 opus "folderA" "folderB"
--> to get ~128 kb/s opus. New folders "folderX [opus]" will be created if not existing,
and pictures and pdf from original folders will be copied in them as well.
muscomp "fileA" "fileB" -vorb 5,5
--> to get ~140 kb/s ogg vorbis. Ouput files in same folder as input files.
muscomp sync mp3 med "foldername"
--> to get ~128 kb/s mp3. Special sync mode, all files are searched for recursively in
"foldername" and compressed files are created in the new "foldername [mp3]" folder. Lossy
files in original folder are added to the synced folders by default.
muscomp "folderA" folderB" ogg max
--> to get ~256 kb/s vorbis. No [ogg] added in target folder names since "ogg" is given after
foldername, but any [FLAC] tag in initial folder name will still be removed.
DESCRIPTION
###########
This script takes all flac, wav or aiff files in the given folders, or some isolated files,
and convert them into a lossy format : opus (default), vorbis or mp3. It is quite fast thanks to
parallels tasks. Original lossless files are never overwritten. Output Opus, vorbis or mp3 files
ARE overwritten. This script is an advanced wrapper for opusenc, oggenc, lame or ffmpeg commands.
Files given as arguments are converted in their own folder.
When folders are given as arguments :
- a new folder may be created : the script strips any [*(FLAC|WAV|AIFF)*] tag (or lower case) from
the folder name. It then adds a [format] tag in the target folder name, but only if the format
argument is given BEFORE the folder to convert. It lets you chose whether you want a [format] tag.
- the files are not searched for recursively, only first-level files are converted.
- jpg and pdf files in the original folder are copied in the destination folder, if any.
A special mode (-a or -all) convert all files recursively from the folders given as arguments. The
files are converted in-place, no folders are created.
This script use "vbr" (variable bit rate) mode for all three formats, as it is known as the best
quality/size ratio, much better than similar fixed rate. The default behaviour is to convert in opus
with a 175 kb/s average bitrate. Opus gives the best quality/size format and is a fully open
specification.
SPECIAL SYNC MODE
#################
Sync mode let you have a "lossy version" of part of, or your whole music library. Could be useful to
prepare music for a portable device. By contrast with the default mode, sync mode :
- explores subfolders recursively
- converts files only if a similar file does not already exist in destination
- will sync lossy format files as well (can be avoid with -l(ossless-only) option)
- you can still force conversion even if files already exist with -f(orce) option
(eg. you want to change quality)
ARGUMENTS
#########
You can give arguments, with or without any leading "-" or "--" and in any order :
- mp3, vor(b(is)) or ogg, op(us)
to switch to desired format. Opus is the defautlt. If given before folder name,
a new folder with [format] tag will be created if non-existing.
- any number
will be interpreted as quality target. However, the three formats have different
ways to specify quality target :
- for opus : you have to give the target bitrate in kb/s.
- for vorbis and mp3 : the quality is given on a specific scale.
- lo(w(er)), med(ium), hi(gh) or ex(cel(lent))
presets to use instead of quality settings.
"high" preset is the default setting. "lower" is for voice usage.
- s(ync)
special mode to convert all files recursively from given folders.
To be given BEFORE folder name. See above.
- d((est)ination) FOLDERNAME
let you give a new or existing destination folder, instead of the
automatic folder naming. The destination folder will be use for all
arguments given AFTER this option on the command line. Not well suited
if you have several folders to convert.
- ff(mpeg)
to force use of ffmpeg instead of oggenc, opusenc or lame
- v(erbose)
to get the full output of conversion command
- another argument
will be intepreted as folders or files to convert
The default qualities for three formats are set for high quality music tracks. Some presets
can be used. They can be guidelines for you to set quality according to your needs :
- opus : 128 kb/s and above should be transparent for music. Lower are great for speech.
Presets : low=80 kb/s. medium=120 kb/s. high(default)=175 kb/s. excellent=215 kb/s.
- vorbis : scale from 1 (lowest) to 10 (best). 5+ should be transparent. Presets :
low=2 (96 kb/s). medium=4 (128 kb/s). high(default)=6,5 (220 kb/s). excellent=7,5 (250 kb/s).
- mp3 : scale from 0 (best) to 10 (lowest). Beware of inverted scale. Presets :
low=6 (115 kb/s). medium=4,5 (147 kb/s). high(default)=1 (225 kb/s). excellent=0 (245 kb/s).
'
exit
elif [[ "$1" =~ ^-*mp3$ ]] ; then format="mp3" ; shift
elif [[ "$1" =~ ^-*op(us)?$ ]] ; then format="opus" ; shift
elif [[ "$1" =~ ^-*ogg$|^\-*vor(b(is)?)?$ ]] ; then format="ogg" ; shift
elif [[ "$1" =~ ^-*lo(w(er)?)?$ ]] ; then preset=0 ; shift
elif [[ "$1" =~ ^-*(med(ium)?|min)$ ]] ; then preset=1 ; shift
elif [[ "$1" =~ ^-*hi(gh)?$ ]] ; then preset=2 ; shift
elif [[ "$1" =~ ^-*ex(cel(lent)?)?$ ]] ; then preset=3 ; shift
elif [[ "$1" =~ ^-*[\,\.0-9]+$ ]] ; then quality="$1" ; shift
elif [[ "$1" =~ ^-*s(ync)?$ ]] ; then mode="sync" ; depth="" ; exclude_existing='[[ ! -s "$4${5%.*}."$0"" ]] &&' ; echo "Script in \"sync\" mode, will convert files recursively to a duplicate folders tree if they don't exist. It will sync mp3, opus, ogg and wma files in addition." ; shift
elif [[ "$1" =~ ^-*l(ossless-only)?$ ]] ; then lossytracks="no" ; echo "\"lossless-only\" option : special sync mode without any lossy files sync." ; shift
elif [[ "$1" =~ ^-*f(orce)?$ ]] ; then convert_force_switch=1 ; shift
elif [[ "$1" =~ ^-*v(erbose)?$ ]] ; then inverted_verbose="" ; verbose="--verbose" ; shift
elif [[ "$1" =~ ^-*ff(mpeg)?$ ]] ; then ffmpeg_switch=1 ; shift
elif [[ "$1" =~ ^-*d(est(ination)?)?$ ]] ; then global_dest="$2" ; shift 2
##If the argument is a file, takes it apart in the files list to process it further
elif [[ -f "$1" ]] && [[ "$1" =~ .(flac|wav|aiff)$ ]] ; then echo "Will convert : file \"$1\"" ; printf '\n\n%b\n' "$1">> "$tempfile" ; shift
##If the argument was not a flac or wav file, and is not any valid folder neither, then ignore it
elif [[ ! -d "$1" ]] ; then echo "This target is not a valid folder neither a .flac, .wav or .aiff file, ignoring it : \"$1\"" ; shift
##If the argument is a valid folder, takes all of its files apart in a files list to process them further
else folder="${1%%\/}"
[[ "$mode" = "normal" ]] && [[ ! $(ls "$folder"/*.{flac,wav,aiff} 2>/dev/null) ]] && echo "no .flac, .wav or .aiff files in the folder \"$folder\", the script skips this folder." && shift && continue
( [[ -z $format ]] && [[ tag_folder="" ]] ) || tag_folder=" [$format]"
echo -e "Will convert : folder \"$folder\""
[[ $global_dest = "" ]] && destfolder="$(echo "$folder" | sed -re 's/\ ?\[\ ?(FLAC|flac|AIFF|aiff|AIF|aif|WAV|wav){1}.*\]//')$tag_folder" || destfolder="$global_dest"
echo -e "Creating destination folder if not already existing : $destfolder" ; mkdir -p "$destfolder"
##If normal mode, copy non music files in destination folder. If sync mode, make a sync for folders tree and non music files, and lossy files if asked for ("all" mode)
if [[ "$mode" = "sync" ]] ; then
echo -e "Creating or syncing destination folders in : "$destfolder""
if [[ "$lossytracks" = "yes" ]] ; then rsync -a $inverted_verbose --size-only -f"+ *.jpg" -f"+ *.jpeg" -f"+ *.png" -f"+ *.pdf" -f"+ *.txt" -f"+ *.mp3" -f"+ *.opus" -f"+ *.ogg" -f"+ *.m4a" -f"+ *.wma" -f"-! */" "$folder"/ "$destfolder"
else rsync -a $inverted_verbose --size-only --ignore-errors -f"+ *.jpg" -f"+ *.jpeg" -f"+ *.png" -f"+ *.pdf" -f"+ *.txt" -f"-! */" "$folder"/ "$destfolder"
fi
else
find "$folder" \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.pdf" -o -iname "*.txt" \) -exec cp -R -t "$destfolder" {} +
fi
##append the to be converted files in temporary list
find "$folder" $depth \( -iname "*.flac" -o -iname "*.wav" -o -iname "*.aiff" \) -printf "$folder"/'\n'"$destfolder"/'\n%P\n' >> "$tempfile"
shift
fi
done
[[ -z $format ]] && format="$format_default"
[[ -z "$(cat $tempfile)" ]] && echo -e "No file to convert, script exiting." && exit 1
[[ $convert_force_switch -eq 1 ]] && exclude_existing=""
###FUNCTION - to create conversion function according to required format and parameters
case $format in
opus)
[[ $quality ]] || quality="${opus_presets[$preset]}"
if [[ $(which opusenc) ]] && [[ $ffmpeg_switch -eq 0 ]] ; then
conversion_command='echo "converting "$5"" && opusenc $1 --bitrate "$2" "$3$5" "$4${5%.*}.opus"'
elif [[ $(which ffmpeg) ]] ; then echo "Using ffmpeg instead of opusenc" ; conversion_command='echo "converting "$5"" && ffmpeg -y -v "${1##--}" -i "$3$5" -vn -codec:a libopus -b:a "$2"k "$4${5%.*}.opus"'
else echo "You need to install opus-tool or ffmpeg to convert in opus. Exiting." && exit 1
fi
;;
ogg)
[[ $quality ]] || quality="${vorbis_presets[$preset]}"
if [[ $(which oggenc) ]] && [[ $ffmpeg_switch -eq 0 ]] ; then
conversion_command='echo "converting "$5"" && oggenc $1 -q "$2" -o "$4${5%.*}.ogg" "$3$5"'
elif [[ $(which ffmpeg) ]] ; then echo "Using ffmpeg instead of oggenc" ; conversion_command='echo "converting "$5"" && ffmpeg -y -v "${1##--}" -i "$3$5" -vn -codec:a libvorbis -q:a "$2" "$4${5%.*}.ogg"'
else echo "You need to install vorbis-tool or ffmpeg to convert in ogg vorbis. Exiting." && exit 1
fi
;;
mp3)
[[ $quality ]] || quality="${mp3_presets[$preset]}"
if [[ $(which lame) ]] && [[ $ffmpeg_switch -eq 0 ]] ; then
# storing metadata given by metaflac command in an array. Modifying IFS to newline in a way that works within single quotes. Stripping newlines away and then reintroducing newlines to get 1 line 1 tag. Modifying names of tag to be the id3v2 way.
id3v2tags_command='IFS=$(echo -en "\n\b") tags=($(metaflac --export-tags-to=- "$3$5" | tr "\r\n" ";" | sed -re "s/([A-Za-z_\ ]*=)/\n\1/g" -e "$ s/$/\n/" | sort | sed -re "/NUMBER/ {N;s/\;\n(TRACK|DISC)TOTAL=/\//g;}" | sed -re "s/;+$//" -e "s/([a-z]);+([A-Za-z])/\1\ ;\ \2/g" | sed -re "/^REPLAY/ d" -e "s/^(TITLE|Title|title)/TIT2/" -e "s/^(TRACKNUMBER|Tracknumber|tracknumber)/TRCK/" -e "s/^GROUPING/TIT1/" -e "s/^(ARTIST|Artist|artist)/TPE1/" -e "s/^(ALBUM|Album|album)=/TALB=/" -e "s/^(ALBUMARTIST|Albumartist|albumartist)/TPE2/" -e "s/^(RELEASE)?(DATE|Date|date)=/TYER=/" -e "s/^(GENRE|Genre|genre)/TCON/" -e "s/^(COMPOSER|Composer|composer)/TCOM/" -e "s/^(DESCRIPTION|Description|description|COMMENT|Comment|comment)/TIT3/" -e "s/^COPYRIGHT/TCOP/" -e "s/^DISCNUMBER/TPOS/" -e "$ ! a --tv" )) ; [[ "$1" == "" ]] && echo "Detected tags :" && echo "${tags[*]}" | grep -Eve "--tv" ;'
conversion_command='echo "converting : "$5"" && lame $1 --nohist --add-id3v2 --ignore-tag-errors ${tags[*]-} -V "$2" "$3$5" "$4${5%.*}.mp3"'
elif [[ $(which ffmpeg) ]] ; then echo "Using ffmpeg instead of lame" ; conversion_command='echo "converting "$5"" && ffmpeg -y -v "${1##--}" -i "$3$5" -qscale:a "$2" "$4${5%.*}.mp3"'
else echo "You need to install lame or ffmpeg to convert in ogg vorbis. Exiting." && exit 1
fi
;;
esac
###MAIN – Xargs command to deal with all lossless files to convert from the tempfile list in a parallelized way
echo -e "\nHere are the settings for the conversion :\n - format : $format\n - quality : ${quality}\n"
echo -e "The script is now going to convert the given targets.\n…"
<"$tempfile" xargs $verbose -d '\n' -P"$proc_nb" -n3 bash -c "$id3v2tags_command $exclude_existing $conversion_command" "$format" "$inverted_verbose" "$quality"
echo -e "…\nThe conversion of the targets should be done, exiting."
exit 0