Table of Contents

Adding ReplayGain to FLACs in Bulk

1.0 Introduction

Semplice 2.12 introduced the concept of a “metadata-only” volume boost for freshly ripped and tagged FLAC files. This means it will scan all the FLACs in a folder, do some psychoacoustic analysis of the music it finds and compute values for per-track ReplayGain and per-album (or, really, per-folder) ReplayGain and then writes that data into a set of five new metadata tags. Your music player can then read that metadata and use it to adjust the playback volume of the music as it is played. The aim is to get a sustained volume boost to a standardised 'perceived loudness' level without actually altering the audio data in the FLAC.

As such, ReplayGain is rather a nifty idea that (in my experience) works very well for classical music… and, therefore, it's a bit unfortunate that I only just gave Semplice this ability because there's about 20 years'-worth of CD rips which lack any ReplayGain information!

Accordingly, I needed a way to 'catch up': to compute (and apply) ReplayGain in bulk, to the nearly 20,000 FLAC files in my music collection. Below is the result.

I dislike offering bulk update scripts to anyone, really, because doing substantial data changes to a large amount of collected music has the potential to be extremely damaging… and I do not want to damage a music collection that has taken roughly 26 years to amass! On this occasion, however, the addition of new ReplayGain metadata tags isn't doing much harm. It's not likely to obliterate the music in the FLACs, and metadata tags can be removed and re-placed as often as you like without doing damage. So, just this once, a bulk update script seemed plausible.

If you use it, though: you do so entirely at your own risk. And for Heaven's sake: take a backup before you start!!

===== 2.0 The Script Without elaboration at this stage, here's the script I used against my own music collection:

#!/usr/bin/env bash
#
###############################################################################
# A routine to go through every folder mentioned in the local
# music database and apply ReplayGain to the FLACs found within them.
# If ReplayGain is determined to already have been computed, the folder is
# skipped without further work being performed.
#
###############################################################################
clear
sqlite3 "$HOME/.local/share/giocoso3/db/test.db" "select dirname from recordings order by composer" > "directories.txt"
tput civis

# File containing the list of directories
DIRFILE="directories.txt"

# Initialize counters
GAINCOUNTER=0
SKIPCOUNTER=0

# Loop through each line of the file
while IFS= read -r dirname || [[ -n "$dirname" ]]; do

    # Change into the directory
    cd "$dirname" || { echo "Failed to cd into $dirname"; continue; }
    
    FLACFILE=$(find "$(pwd)" -name "*.flac" | sort | head -n 1)
    DISPLAYFLAC="${FLACFILE#/*/*/*/*/}"; DISPLAYFLACFILE="$(basename "$DISPLAYFLAC")"; DISPLAYFLACFILE=${DISPLAYFLACFILE:0:98}
    DISPLAYPATH="$(dirname "$DISPLAYFLAC")"; DISPLAYPATH=${DISPLAYPATH:0:98}
    GAIN=$(metaflac --show-tag=REPLAYGAIN_ALBUM_GAIN "$FLACFILE" 2>/dev/null | cut -d= -f2)
    tput cup 2 54; echo -n "ReplayGain Computed: $GAINCOUNTER - Skipped: $SKIPCOUNTER"
    if [[ -z "$GAIN" ]]; then
        tput cup 6 2; printf '%*s' 98 ''; tput cup 6 2; echo -n "$DISPLAYPATH"
        tput cup 7 2; printf '%*s' 98 ''; tput cup 7 2; echo -n "$DISPLAYFLACFILE"
        echo "$FLACFILE" >> "$HOME/Desktop/adjusted.txt"
        metaflac --add-replay-gain *.flac 2>/dev/null 
        ((GAINCOUNTER++))    
    else
        tput cup 6 2; printf '%*s' 98 ''; tput cup 6 2; echo -n "$DISPLAYPATH"
        tput cup 7 2; printf '%*s' 98 ''; tput cup 7 2; echo -n "$DISPLAYFLACFILE"
        ((SKIPCOUNTER++)) 
    fi
done < "$DIRFILE"
tput civvis

3.0 The Script Explained

The script assumes you are running Giocoso and have accordingly created a local, non-Pro, database of what music files exist in which hard disk folders.

It therefore opens by issuing a 'select dirname from recordings' query against that local database: my script above has hard-coded the name “test.db” as the database to query, but you need to replace that name with the actual name of your database .db file. The query returns the full path to folders that contain FLACs.

The results of the query are written out to a local text file, called directories.txt.

We then read from that text file, one line at a time. For each line, we 'cd' to the folder listed and use the metaflac utility to check if there is already a REPLAYGAIN_ALBUM_GAIN tag present in the first FLAC found within that folder. If such a tag already exists, then ReplayGain has already been computed for that folder and there's no point re-computing ReplayGain in that case. We therefore skip past that folder and read the next line of the text file to get to the next folder.

Should we encounter a folder for which no previous ReplayGain has been computed, we issue the metaflac –add-replay-gain command to compute ReplayGain from scratch for all the FLACs in that folder.

As we loop through each folder mentioned in the text file, we keep count of the folders for which we've had to compute ReplayGain and of those we were able to skip because ReplayGain was already computed. As the script runs, you'll see these counters increment steadily. A lot of the other code in the loop is there to provide some textual feedback as to whereabouts in the list of folders we've reached at any given point.

4.0 Running the Script

Save the script in a text file with a .sh extension anywhere on your file system. Let's say you store it in /home/fred/Desktop, as a file called “addreplaygain.sh”. You then need to make the script executable:

chmod +x /home/fred/Desktop/addreplaygain.sh

Once it's executable, you can run it by typing:

cd /home/fred/Desktop
./addreplaygain.sh

Sit back and be patient as it works its way through your music collection. Computing ReplayGain can take a few tens of seconds. Skipping because it's already been computed takes mere fractions of a second.

The thing was not written with beauty in mind: it will flash annoyingly for the duration of its operation!

Note that if you are a 'dip your toes in the water before plunging into the pool' kind of person, you can restrict the script to working on just a tiny part of your collection by way of a test by altering the text of the initial query.

As written, the query simply selects all recordings in the database's RECORDINGS table. If you add a WHERE clause to the query, however, you can restrict the script to modifying only those folders returned by the modified query. For example:

select dirname from recordings where composer like 'Q% order by composer'

…means that only composers whose first names begins with a 'Q' will get processed. If you have music by Qigang Chen, you'll be in business but you're unlikely to modify many FLACs as his recorded legacy is not huge! Obviously you could modify it as you wish to limit the number of folders the query returns in other ways:

select dirname from recordings order by composer limit 10

…would ensure only the first ten folders of music would get processed. You could check those ten to satisfy yourself that no damage has been done, come back and remove the limit clause and then run the script without any limits or filters.