Volume levelling between tracks and albums, written into the mirror as ReplayGain tags.
Rockbox has the setting — Settings → Playback Settings → Replaygain — but it only applies an offset a tag already carries. It has no loudness analysis of its own, so an untagged mirror plays every album at whatever level it was mastered to.
What it does
Measures each album with rsgain once its tracks are in place, writing album gain and track gain both. Which one is applied is the device's decision.
Re-measures an album as a whole whenever it gains, loses or replaces a track — album gain is a property of all of its tracks.
Backfills a mirror that predates this, by checking each file for the tag rather than relying on the pass having touched it.
--no-replaygain / MUSIC_MIRROR_REPLAYGAIN=0 turns it off.
Two details worth reviewing
rsgain runs with --preserve-mtimes. Staleness here is an mtime comparison and tagging rewrites the file, so without it every levelled track would look newer than its source and the next pass would re-encode the whole library. There is a test for exactly that.
Whether a file has been levelled is decided by walking its ID3v2 frame headers and seeking over the bodies. Cover art is embedded in every mirror file, so reading the tag whole would turn an idle pass into a full read of the library.
Cost
The first pass after this lands levels the entire library, which costs a decode of every track — roughly as long as the original encode took. After that only changed albums are measured.
A missing rsgain is a warning, not a failure.
Test changes
Two existing tests move with the change: ffprobe's csv writer renders the ReplayGain side data as a trailing empty field, and a copied MP3 now differs from its source in the container while carrying identical audio frames.
Volume levelling between tracks and albums, written into the mirror as ReplayGain tags.
Rockbox has the setting — **Settings → Playback Settings → Replaygain** — but it only applies an offset a tag already carries. It has no loudness analysis of its own, so an untagged mirror plays every album at whatever level it was mastered to.
## What it does
- Measures each album with `rsgain` once its tracks are in place, writing album gain and track gain both. Which one is applied is the device's decision.
- Re-measures an album as a whole whenever it gains, loses or replaces a track — album gain is a property of all of its tracks.
- Backfills a mirror that predates this, by checking each file for the tag rather than relying on the pass having touched it.
- `--no-replaygain` / `MUSIC_MIRROR_REPLAYGAIN=0` turns it off.
## Two details worth reviewing
`rsgain` runs with `--preserve-mtimes`. Staleness here is an mtime comparison and tagging rewrites the file, so without it every levelled track would look newer than its source and the next pass would re-encode the whole library. There is a test for exactly that.
Whether a file has been levelled is decided by walking its ID3v2 frame headers and seeking over the bodies. Cover art is embedded in every mirror file, so reading the tag whole would turn an idle pass into a full read of the library.
## Cost
The first pass after this lands levels the entire library, which costs a decode of every track — roughly as long as the original encode took. After that only changed albums are measured.
A missing `rsgain` is a warning, not a failure.
## Test changes
Two existing tests move with the change: ffprobe's csv writer renders the ReplayGain side data as a trailing empty field, and a copied MP3 now differs from its source in the container while carrying identical audio frames.
Rockbox applies the offset a ReplayGain tag carries but has no loudness
analysis of its own, so an untagged mirror plays every album at whatever
level it was mastered to.
Albums are measured with rsgain once their tracks are in place, album gain
and track gain both, leaving the device to choose between them. An album is
re-measured as a whole whenever it gains, loses or replaces a track, because
album gain is a property of all of its tracks and one new track makes the
value stored on every sibling wrong.
rsgain runs with --preserve-mtimes. Staleness here is an mtime comparison
and tagging rewrites the file, so without it every levelled track would look
newer than its source and the next pass would re-encode the whole library.
Whether a file has already been levelled is decided by walking its ID3v2
frame headers and seeking over the bodies. Cover art is embedded in every
mirror file, so reading the tag whole would turn an idle pass into a full
read of the library.
A missing rsgain is reported and then left alone rather than failing the
pass: the mirror is still correct audio in the right place.
Two existing tests move with the change. ffprobe's csv writer renders the
ReplayGain side data as a trailing empty field, and a copied MP3 now differs
from its source in the container while carrying identical audio.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A ReplayGain album gain belongs to the whole record, so a track arriving or
leaving rewrites the tags on every one of its siblings. rsgain fits the new
values into the padding its previous write left behind, which changes neither
the file's size nor its mtime:
before: 277757 bytes, mtime 1577880000, album gain 3.75 dB
after: 277757 bytes, mtime 1577880000, album gain 6.25 dB
Those are the two things rsync's quick check compares, so the siblings are
invisible to it and the device keeps the old gains indefinitely.
The track that arrived or left is always visible. So sync-to-ipod.sh now runs a
second pass over the albums the first one touched, with --ignore-times to
defeat the same quick check. touched_albums.py derives the list from rsync's
own report of what it moved, which costs no extra traversal of either tree, and
leaves out the tracks the first pass has already copied so a whole-album
quality upgrade is not sent twice. Albums whose file set has not changed are
left alone.
A dry run reports how many further tracks are involved.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Volume levelling between tracks and albums, written into the mirror as ReplayGain tags.
Rockbox has the setting — Settings → Playback Settings → Replaygain — but it only applies an offset a tag already carries. It has no loudness analysis of its own, so an untagged mirror plays every album at whatever level it was mastered to.
What it does
rsgainonce its tracks are in place, writing album gain and track gain both. Which one is applied is the device's decision.--no-replaygain/MUSIC_MIRROR_REPLAYGAIN=0turns it off.Two details worth reviewing
rsgainruns with--preserve-mtimes. Staleness here is an mtime comparison and tagging rewrites the file, so without it every levelled track would look newer than its source and the next pass would re-encode the whole library. There is a test for exactly that.Whether a file has been levelled is decided by walking its ID3v2 frame headers and seeking over the bodies. Cover art is embedded in every mirror file, so reading the tag whole would turn an idle pass into a full read of the library.
Cost
The first pass after this lands levels the entire library, which costs a decode of every track — roughly as long as the original encode took. After that only changed albums are measured.
A missing
rsgainis a warning, not a failure.Test changes
Two existing tests move with the change: ffprobe's csv writer renders the ReplayGain side data as a trailing empty field, and a copied MP3 now differs from its source in the container while carrying identical audio frames.