jellyfin / jellyfin

The Free Software Media System
https://jellyfin.org
GNU General Public License v2.0
35.04k stars 3.18k forks source link

Audio normalisation / volume levelling #537

Closed HavermansStef closed 1 year ago

HavermansStef commented 5 years ago

Would be nice to have, but not easy to implement. Low priority.

hawken93 commented 5 years ago

Just wanted to leave the keyword replaygain in here.

https://www.bobulous.org.uk/misc/Replay-Gain-in-Linux.html

You would usually write replaygain information to the file, then utilize it on playback. Different formats have different ways to store this data though.

JustAMan commented 5 years ago

Another approach is re-formatting your media beforehand. I found this to be very useful: https://github.com/slhck/ffmpeg-normalize

hawken93 commented 5 years ago

Mp3 is also often normalized by rewriting the files. I won't do this as the music collection already tries to take care to be a bit perfect copy of the cd (flac format). (I'm one of those people...) so would love to see a metadata based solution here.

But point taken thst you could normalize media today for use with jellyfin :)

onny commented 5 years ago

I just hacked this feature into my Jellyfin installation. It's a dirty workaround :) https://blog.project-insanity.org/2019/04/10/hacking-replay-gain-audio-normalization-into-jellyfin/

I enable audio transcoding with ffmpeg and enable the replay gain audio normalization with an additional parameter: "-af volume=replaygain=track". I hardocded this into the Jellyfin source. But it works pretty good!

joshuaboniface commented 5 years ago

That should be pretty easy to hide behind a checkmark! :tada:

anthonylavado commented 5 years ago

The question - does this only apply with transcoding though? I imagine a lot of people have that as direct play. Otherwise, spectacular work @onny!

I’ll have to look at getting things figured out for other formats later. I think FLAC does ReplayGain too?

dkanada commented 5 years ago

@anthonylavado you are correct, and it's a pretty large problem to solve. I think a lot of settings are dependent on the playback being a transcode. Another example of this is that all video streams transcode audio to MP3 last I checked.

hawken93 commented 5 years ago

I think the correct way here is to rely on file metadata or generate metadata from the file about its replaygain values. These can be served to the client to perform corrections on the client side. Usually the most important information is the rms values for the track and for the whole album

topas-rec commented 4 years ago

Why in my eyes this is important: The End Of The Loudness War?

Replaygain 2 uses the newer loudness measurement algorithms:

RG2 uses BS.1770-3 for loudness measurement.

See ReplayGain 2.0 specification Jump to: navigation, search In the end measuring is one thing and then using the tagged values is another, so I just wanted to mention RG2 here because it was mentioned above.

Sources (audio files) should in my opinion stay untouched and only be tagged for example with r128gain and then normalised when played back.

There was a study (Recommendation for loudness normalization by music streaming services) for a big and popular streaming service made by Eelco Grimm in which he suggested to use album gain. The study is linked at Music Loudness Alliance. But the choice of album or track gain can be left up to the user (settings).

sjiampojamarn commented 3 years ago

Anything can be done here?

sjiampojamarn commented 3 years ago

Thanks @onny for the blog post and idea in the meantime. A few things have changed. Based on the pull as of Nov 24th, here what I did:

in Jellyfin.Api/Helpers/AudioHelper.cs, comment out direct play and always transcode in the code without UI : ) around line 167

+            //if (streamingRequest.Static)
+            //{
+            //    var contentType = state.GetMimeType("." + state.OutputContainer, false) ?? state.GetMimeType(state.MediaPath);
+
+            //    if (state.MediaSource.IsInfiniteStream)
+            //        await new ProgressiveFileCopier(state.MediaPath, null, _transcodingJobHelper, CancellationToken.None)
+            //            {
+            //                AllowEndOfFile = false
+            //            }.WriteToAsync(_httpContextAccessor.HttpContext.Response.Body, CancellationToken.None)
+            //            .ConfigureAwait(false);
+
+            //        return new FileStreamResult(_httpContextAccessor.HttpContext.Response.Body, contentType);
+            //    }
+
+            //    return FileStreamResponseHelpers.GetStaticFileResult(
+            //        state.MediaPath,
+            //        contentType,
+            //        isHeadRequest,
+            //        _httpContextAccessor.HttpContext);
+            //}

in MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs, add ffmpeg args for replaygain; round line 3521

-                "{0} {1}{7}{8} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"",
+                "{0} {1}{7}{8} -threads {2}{3} {4} -af volume=replaygain=track -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"",
georgek commented 3 years ago

Doing the correction in the playback client is preferable to transcoding. When using a playback device that supports doing Replaygain (like Kodi, for example), then conceptually it seems quite simple as we would simply need to pass the metadata to the client. When I've experimented with this, Kodi doesn't apply any gain, so presumably it's not getting any metadata.

Is it as simple as I think? Is there a whitelist for tags that get passed over DLNA?

mrx23dot commented 2 years ago

ffmpeg has already built in modules:

loudnorm: loudness normalization according to EBU R128. You can set an integrated loudness target, a loudness range target, or maximum true peak. This is recommended for publishing audio and video and it is used by broadcasters all over the world. single pass for livestreams

looks great ffmpeg -i file_example_MP3_5MG.mp3 -filter:a loudnorm=i=-24:tp=-2:lra=7,volume=5dB file_example_MP3_5MG_proc0.mp3 comp

coming from: https://github.com/jellyfin/jellyfin-androidtv/issues/1434

We could add a user config for it. What about transcoding only the audio?

Would be great for night-movie-mode (eliminating the need of buying AV with this feature)

georgek commented 2 years ago

There are two different types of normalisation being discussed here and it's important to be clear about them:

  1. Inter-track/album/programme normalisation like ReplayGain, Dialogue normalisation (dialnorm), linear loudnorm. These preserve the dynamic range of the original while normalising perceived volume between tracks/albums/programmes. This means you don't have to touch your volume knob when changing track/album/programme but dynamics are still maintained. This requires an initial pass of each track/album/programme to determine the perceived loudness and record this as metadata (some media comes with this pre-computed, notably Dolby Digital/AC3).
  2. Intra-track/album/programme normalisation like "midnight mode", dynamic loudnorm (suggested above). These affect the dynamic range of tracks/albums/programmes to force them to maintain a consistent loudness throughout. It is commonly used for TV and radio stations to maintain loudness throughout the day. This effectively results in dynamic range compression and is highly undesirable in many cases.

These could both be useful in their own right. 1 is very much the only option if audio fidelity is the main concern. Anyone even remotely an audiophile would never consider 2. However, 2 might be a good option in noisier environments when you want your playlist to sound more like a radio station.

Coloradohusky commented 2 years ago

Yeah, I'd like to toggle between applying per-album gain and per-track gain, for when I either want to listen to a whole album in sequence, or as a shuffled playlist. I know ReplayGain has both track gain and album gain (as seen in Foobar2000), so as long as the tags are there, and we have a method to apply decibel gain, it shouldn't be too difficult to switch between track and album gain.

ElectricAlgorhythm commented 2 years ago

I would really like to see this implemented, preferably without having to change my music files or source code.

I noticed that my client, Symphonium, has a beta option for "local renderer replay gain", which makes me think passing the metadata to the client could be a good option. It also seems like the simplest approach.

I also would also like to see an option for adding the normalization flags to ffmpeg (when transcoding), since I think that would be a decent way to add support without the user having to alter the source code, and hopefully wouldn't require much more work.

melvyn2 commented 2 years ago

It would probably be easiest to start with passing through the tags from files that have them, and then figure out afterwards how to deal with files that jellyfin has to normalize itself. For example, my music library's files are normalized per-album with r128gain (replaygain tool), and so clients that support it just use those tags. Using those would already be much better than nothing.

KucharczykL commented 2 years ago

Agreed, my library is also tagged with ReplayGain metadata so having Jellyfin use that would be a great first step (that I daresay would satisfy most users but I don't have anything to back up that statement).

mishaturnbull commented 1 year ago

I hate to be the guy that has suggestions but no PR's... but it seems to me that this has been doable with seemingly minor code changes since 2019 (i.e., three years): https://github.com/jellyfin/jellyfin/issues/537#issuecomment-481465290 and it hasn't been brought into the codebase at all.

May I suggest instead of thinking up all the possible uses that everyone might want, to start small -- add one switch to toggle the feature as shown earlier in this chain? Having some proof of ability will probably make a bunch of people happy and kick off more work in that direction towards the eventual meet-everyone's-needs goal. Release early, release often :)

For real though, though I am far from a C# expert (or even amateur, I treat it like the plague), if someone could point me in the direction of adding a switch to the user settings and using it, I'd be happy to combine that with the patch above and open a PR for it. Would love to have some loudness normalization!

TelepathicWalrus commented 1 year ago

After lots of trial and error and many hours, I have created an implementation of audio normalization. I would be very appreciative if people would take a look and perhaps download the branch and try it on your own system. I haven't encountered bugs but different systems should show these.

https://github.com/jellyfin/jellyfin/pull/9222

There is a front end linked on the pull page which enables the normalization behind the scenes on the webpage and adds a checkbox to enable/disable this in settings/playback/audio normalization. The front end is required for audio normalization to work.

Thanks! :)

Y0ngg4n commented 1 year ago

@TelepathicWalrus how can i use this? It seems like it is merged into main?

TelepathicWalrus commented 1 year ago

@Y0ngg4n, the easiest way ATM is to use the docker unstable build, that is built off of the current master branch I believe. Then the options should be there 👍 at least until the next version of jellyfin is officially released

sevenrats commented 1 year ago

moved https://features.jellyfin.org/posts/2363/replay-gain-normalization