A track's normalization gain is never computed #102
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
AudioNormalizationTask(Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs, keyAudioNormalization, 24 h) runs ffmpeg's EBU R128 filter over tracks and albums and stores the gain, which clients apply so that a playlist does not jump in volume between albums.The column is read on our side already — #72 was "a track's normalization gain is not read" and is closed — so what is missing is the thing that computes it. Upstream normalizes per track and per album, skips what it has already done, and has a per-library switch.
Music is a first-class library kind here, so this is in scope rather than an edge; it is filed separately from the umbrella because it is real ffmpeg work rather than a sweep.
Where to start
The read side landed with #72 — find the column there rather than guessing at
it, and check both the item row and the stream row, because upstream stores a
track's and an album's separately.
Upstream:
Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs(key
AudioNormalization, 24 h), andMediaBrowser.Providers/MediaInfo/AudioFileProber.csfor how a value reaches a row on ingest. It skips what it has already measured
and has a per-library switch; the measurement is ffmpeg's EBU R128, which
crates/jellyfin-ffmpegis the crate to spawn from.How to know it is done
Measure a track both servers can see and
diff-dbthe column — it is a floatproduced by the same filter in the same ffmpeg build, so agreement is the check
rather than an approximation. Say what tolerance, if any, was needed and why.
House rules
CLAUDE.mdis the short version and these are the parts that bite: tests rununder
cargo nextest; upstream's source is the checkout inupstream/at thepin (
v12.0-rc5,6d501ba4) and not the clone beside the repository;generated files are regenerated rather than edited; a comment says what the code
does, not how it got there; and
Dnreferences stay out of code and comments.ARCHITECTURE.mddecides which crate a thing goes in and which dependency edgesare illegal,
DECISIONS.mdsays why a rule exists.The reference pair is
cargo xtask reference up(andreference provision,which is idempotent); a run is
cargo run -p jellyfin-harness -- replay --upstream URL --ours URL --scenario FILE --out DIR,with
diff-dbfor the two databases.crates/jellyfin-harness/README.mdsayswhat a diff means and what an ignore entry has to carry — every entry needs a
reason and an expiry milestone, and an adds-only history is the smell the risk
table names.
Done, on
main—968bf7d.Three rows each — the album and both tracks — identical to the bit, with no tolerance. The value is a C#
floatwidened into aREAL, so the column holds-22.200000762939453; parsing tof32and widening is load-bearing rather than incidental. Upstream's> 1batching is kept, which drops a lone measurement rather than writing it, andEnableLUFSScanis written intooptions.xmlso an upstream reading the same directory agrees.One response-level difference resisted and turned out to be upstream's: immediately after its task ran, upstream omitted
AlbumNormalizationGainwhere we reported it, because its in-memory item cache still held the pre-task album. Restarting upstream makes the two agree exactly. Cache staleness, not a contract.