OxygenCobalt / Auxio

A simple, rational music player for android
GNU General Public License v3.0
1.82k stars 120 forks source link

Add support for Replaygain #7

Closed h4xor666 closed 2 years ago

h4xor666 commented 3 years ago

Describe the feature you want to implement:

A toggle for enabling replaygain

Is your feature request related to a problem? Please describe:

Yes, replaygain solves the problem of different tracks sometimes being different levels of volume, and it normalizes the volume.

Sometimes tracks in an album can be different levels of volume, or tracks in a playlist.

Why do you think this will improve everyone's usage of Auxio?

For users who have their music with replaygain tags, definitely. Many users on Android who store their music locally use Replaygain.

Due Diligence:

OxygenCobalt commented 3 years ago

I'm not really sure its possible. I load my music through MediaStore, which does not index ReplayGain tags, and MediaMetadataRetriever does not support ReplayGain tags either. The only implementation that I found was in Vanilla Music, but it rolls its own music loader and tag parser, which is a non-starter for me.

Are there any other implementations that you know of?

h4xor666 commented 3 years ago

Not really, I don't know any other implementations using exoplayer. I am a dummy so I have no clue how things work lmao. I appreciate your response though.

OxygenCobalt commented 3 years ago

Yeah, I don't think that Android supports this natively without some kind of custom shim. In a future version it could be possible but for now I think your options are either Vanilla Music or (Maybe) PowerAMP.

breversa commented 2 years ago

I'd love to see ReplayGain support too (I've used Vanilla Music but the UI needs overhauling, and it lacks a working "play when headset is plugged in" feature for recent Adnroid versions).

Foobar2000 Mobile has such a feature too but as far as I know it's closed source.

OxygenCobalt commented 2 years ago

Sadly, nothing has still changed on this front. The only way I could feasibly support replaygain is either by:

A. Streaming music via SoundPod as we can let the music server handle it, but such an change is incredibly far away. B. Completely replacing the music backend with my own metadata parser instead of android's built-in media database, but that also impractical right now for reasons I described in the the media indexing issues thread.

breversa commented 2 years ago

(For some reason, this current issue isn't searchable from https://github.com/OxygenCobalt/Auxio/issues?q=is%3Aissue+is%3Aclosed :thinking: )

OxygenCobalt commented 2 years ago

Actually, I could possibly support replaygain. I believe ExoPlayer exposes some APIs for music metadata that are loaded with the stream itself. If the API exposes raw music tags I could probably be able to get the replaygain information and adjust the player volume based on that. I'll be re-opening this issue and investigating this further.

breversa commented 2 years ago

That would be awesome ! :heart_eyes: I'm no developer but I volunteer to test your app and provide feedback as needed. :D

OxygenCobalt commented 2 years ago

Okay, I've determined this is possible and I'll try to implement it. A couple caveats though:

  1. Depending on how fast the tags are loaded, the volume might change a bit into playback. Nothing I can do about this.
  2. This will only likely work for MP3/OGG/FLAC files.
  3. This will be gated behind a settings option since I have no idea how stable this will be.
  4. Auxio will only read replaygain_track_gain [or similar] tags and then treat the adjustment in decibels. No funky ID3-specific junk.
OxygenCobalt commented 2 years ago

@breversa Hey, do you have a MP3 file with ReplayGain tags? I'm not sure how to parse them outright.

breversa commented 2 years ago

Of course !

There you go : https://ecloud.global/s/zaZW4eH55wxz93P

I use rgbpm to add ReplayGain tags to my files.

breversa commented 2 years ago

And here's for a FLAC file : https://ecloud.global/s/NjR38sk3TggaJ2z

OxygenCobalt commented 2 years ago

Cool! Thanks for the tool as well.

OxygenCobalt commented 2 years ago

@breversa Can you try this APK? I've made an implementation that supports the basic REPLAYGAIN_TRACK_GAIN, REPLAYGAIN_ALBUM_GAIN, R128_TRACK_GAIN, and R128_ALBUM_GAIN tags. I stopped there since I have zero idea what to do with the more exotic tags. Let me know if it sounds right, and then I'll commit the change.

As soon as this implementation is pushed, I'll add a configuration option for the feature, so you can switch the priority [track/album] and whether the feature is enabled at all.

Note: You have to rename the extension to to .apk to install it.

Auxio_ReplayGain.zip

breversa commented 2 years ago

At once !!

breversa commented 2 years ago

Damn that's awesome ! It so great to NOT have to change the volume on each (random) track change ! :D THANK YOU !!

(I still miss ReplayGain support for Ogg Vorbis files stored in VORBISCOMMENT IIRC, but I'm being picky !)

OxygenCobalt commented 2 years ago

Fantastic! I'll commit the change.

(I still miss ReplayGain support for Ogg Vorbis files stored in VORBISCOMMENT IIRC, but I'm being picky !)

I think I implemented this? Not sure. Can you provide more information?

OxygenCobalt commented 2 years ago

@breversa Can you provide an OGG with a VORBISCOMMENT ReplayGain tag? I can't imagine it would be too hard to add to my ReplayGain implementation.

breversa commented 2 years ago

I just noticed something weird : if I get a notification (e.g. Signal message) while music is playing, once the notification sound is over, music playback volume seems reset (= no gain applied) and sounds louder.

Skipping to the next track corrects it, but pause/play doesn't.

Yes, I'll provide you with an Ogg Vorbis file.

OxygenCobalt commented 2 years ago

Oh shoot. I forgot about notification ducking. The music will duck whenever a notification pops up and then re-appears once the sound is gone. I hard-coded a return volume of 1 because I assumed it would never change. Let me fix that.

breversa commented 2 years ago

Here's an Ogg Vorbis file : https://ecloud.global/s/JME58Nnk6GSfznt (It's THE track that got me into the goth/futurepop scene ! ^^)

OxygenCobalt commented 2 years ago

@breversa After doing some testing, it seems like ExoPlayer can't parse OGG for some reason, despite it being nearly identical to FLAC's format. This is sadly the cost of using ExoPlayer here, very little non-streamed metadata formats are actively read by the player. However, it's either that or parsing metadata myself, so I may as well pick the former.

Heres an updated APK with the notification issue fixed: Auxio_ReplayGain_Fixed.zip

breversa commented 2 years ago

It works ! Thanks !! :D

OxygenCobalt commented 2 years ago

Quick note: I identified another issue. If a notification shows up as one song transitions into another, the volume adjustment will not work correctly. I'm working on fixing this. Expect more issues as I try to debug this system.

Edit: Fixed it. In fact, there should be no more issues with ducking [at least on Android Oreo+] as I refactored my audio focus to make ducking be handled by the system.

OxygenCobalt commented 2 years ago

This APK contains the mostly finalized ReplayGain implementation, with customization enabled, the ducking issues fixed, and an improved audio focus mechanism that I added trying to fix the former.

Some notes about this:

This feature will arrive in 2.1.0. Some other changes might be made from here to the release of, but this contains an implementation that I think is suitable for normal use. Have fun.

Auxio_ReplayGain_Final.zip

breversa commented 2 years ago

I'm so grateful of your work, @OxygenCobalt !!! :heart:

I now declare Auxio my #1 Android audio player, thanks to these features that no other player combines:

  1. Simple/ergonomic UI
  2. Autoplay when plugging headphone
  3. ReplayGain support
  4. Open-source
OxygenCobalt commented 2 years ago

@breversa good news: I found a way to get files with VORBISCOMMENT ReplayGain tags working. Turns out ExoPlayer can already parse those tags from OGG files and just...doesn't expose them. So, I created a fork of ExoPlayer that exposes the metadata, and after locally depending on that, OGG Files seem to work now. I attached an APK below.

Auxio_OGG_Replaygain.zip

breversa commented 2 years ago

Great ! I'll give it a try (maybe not today though) and report back ASAP. :)

CorePoint commented 2 years ago

Great to hear that you implemented Replaygain, I think your project is the only one that uses exoplayer and supports replaygain!

I have another feature request which I don't really know how hard it would be to implement. It would be great if Auxio would recognize if you are listening to an album, or just a playlist with mixed tracks and switch the replaygain mode accordingly. I got the idea from this foobar2k plugin.

Have a good day and thanks for your work!

breversa commented 2 years ago

@breversa good news: I found a way to get files with VORBISCOMMENT ReplayGain tags working. Turns out ExoPlayer can already parse those tags from OGG files and just...doesn't expose them. So, I created a fork of ExoPlayer that exposes the metadata, and after locally depending on that, OGG Files seem to work now. I attached an APK below.

Auxio_OGG_Replaygain.zip

Hi @OxygenCobalt ! Forgot to say that I tried your Vorbis-compatible version and, as expected, it works fine. So once again, a huge THANK YOU ! I'm eager to see this feature in the production build so that I can recommend it to my friends !

breversa commented 2 years ago

I have another feature request which I don't really know how hard it would be to implement. It would be great if Auxio would recognize if you are listening to an album, or just a playlist with mixed tracks and switch the replaygain mode accordingly. I got the idea from this foobar2k plugin.

Could it be as simple as:

replaygain = track
if (previoustrack.album == currenttrack.album) then
  replaygain = album
endif

?

CorePoint commented 2 years ago

@breversa

Wouldn't catch every case, but if it would consider maybe the previous and last two tracks it probably would be a "good enough" solution.

OxygenCobalt commented 2 years ago

I have another feature request which I don't really know how hard it would be to implement. It would be great if Auxio would recognize if you are listening to an album, or just a playlist with mixed tracks and switch the replaygain mode accordingly. I got the idea from this foobar2k plugin.

Could it be as simple as:

if (previoustrack.album == currenttrack.album) then
  replaygain = album
else
  replaygain = track

?

Personally, I don't want to use the track method as that means that the album gain would only kick in in the second song. I would rather do something like this:

if playback.parent is Album && track.album == playback.parent: // Is this song from the album we are currently playing?
    replaygain = album
else:
    replaygain = track

This should eliminate any issues from queueing or other playback modes.

@CorePoint Thanks for this suggestion, it seems like a pretty solid "quirk" to add, so I'll implement it under a "Dynamic" option in the ReplayGain setting.

OxygenCobalt commented 2 years ago

As for anyone who might be reading this issue and figuring out how to implement ReplayGain, I want to point out a few things:

CorePoint commented 2 years ago

@OxygenCobalt

I'm very impressed by your openness to suggestions and your quick responses. As far as I know this dynamic feature is non existent on mobile devices as of now. Thank you again! :)

edit:

Also thanks for your pointers for other developers. I hope this will be a good start for others to implement the feature.

OxygenCobalt commented 2 years ago

I just realized: ReplayGain tags that increase volume won't work, as they will need the volume to be increased above 1.0. Theoretically here I can just add my own AudioProcessor that allows a volume higher than 1.0. This can wait for now though, as files that need to gain volume are far less common.

breversa commented 2 years ago

Indeed, I have only seldom encountered tracks that had a below-reference volume level. I don't think it'll be a common issue but it's good to know about this limitation. Maybe add this comment in the ReplayGain option menu UI ?

OxygenCobalt commented 2 years ago

Yeah, I've tried my hand at implementing an AudioProcessor based on this issue and it seems to be far too risky given that if I forget a format or fiddle with the bytes incorrectly, the audio stream would at best not apply the gain, and at worst be completely corrupted. I would need test files that emit every possible output encoding before I could add it. I've already marked the limitation in the FAQ, so I may as well leave this implementation as-is until someone actually requests the feature.

As for other formats I've found are actually supported, I can also add MP4 (with an ID32 atom) to the list of supported formats as well. Turns out ExoPlayer actually parses those tags.

OxygenCobalt commented 2 years ago

Okay, I decided to add support for files with positive ReplayGain values. This mostly stemmed from me having to migrate the ReplayGain engine to an AudioProcessor due to some other changes that would cause conflicts if I continued to manipulate the normal volume field, so at that point I may as well add it anyway now that I can.

Donkey-Doug commented 1 year ago

@OxygenCobalt Maybe rename "ReplayGain" to "Normalize volume"

OxygenCobalt commented 1 year ago

@Donkey-Doug Why? I think ReplayGain is clearer since it actually describes where Auxio is getting volume normalization data. Without it, the setting is unclear and could mislead users into thinking that the normalization process is automatic.

breversa commented 1 year ago

Agreed with @OxygenCobalt. Also, as an "amateur" audiophile and non-native english speaker, "Normalize volume" doesn't tell me as much information as "Replaygain".

lord-ne commented 7 months ago

I agree that "Replaygain" gives.more information than "Normalize Volume", but I think something like "Replaygain (Normalize Volume)" would give the best of both worlds. That way if someone happens to see the setting, they'll know vaguely whether or not it's something they're interested in, and if they are then they know to google ReplayGain for more info

snwflake commented 3 months ago

@Donkey-Doug Why? I think ReplayGain is clearer since it actually describes where Auxio is getting volume normalization data. Without it, the setting is unclear and could mislead users into thinking that the normalization process is automatic.

To be fair, the current naming lead me to believe the same, which is the reason I'm here right now.... x) though gonna add tags to my entire library now and enjoy it working.