feat: level the mirror's volume with ReplayGain tags
Build and publish container / build (pull_request) Successful in 7m41s

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>
This commit is contained in:
Emma Thorpe
2026-08-28 16:49:37 +01:00
co-authored by Claude Opus 5
parent 8d6885c46a
commit c63115f246
7 changed files with 472 additions and 15 deletions
+4 -2
View File
@@ -7,8 +7,10 @@ FROM python:3.13-alpine AS runtime
ENV PYTHONUNBUFFERED=1
# ffmpeg does the encoding; the application itself has no Python dependencies.
RUN apk add --no-cache ffmpeg
# ffmpeg does the encoding and rsgain the volume levelling; the application
# itself has no Python dependencies. rsgain lives in the community repository,
# which the official Python images already have enabled.
RUN apk add --no-cache ffmpeg rsgain
WORKDIR /app
COPY pyproject.toml README.md ./