From f413b5d498d554b7cd91460ce152239292050b44 Mon Sep 17 00:00:00 2001 From: antonio Date: Sun, 2 Jul 2023 23:38:33 +0200 Subject: [PATCH] feat: added initial version ReplayGain functionality, still in development --- .../tempo/model/ReplayGain.kt | 9 ++ .../tempo/util/ReplayGainUtil.java | 107 ++++++++++++++++++ .../tempo/service/MediaService.kt | 7 ++ 3 files changed, 123 insertions(+) create mode 100644 app/src/main/java/com/cappielloantonio/tempo/model/ReplayGain.kt create mode 100644 app/src/main/java/com/cappielloantonio/tempo/util/ReplayGainUtil.java diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/ReplayGain.kt b/app/src/main/java/com/cappielloantonio/tempo/model/ReplayGain.kt new file mode 100644 index 00000000..0d469616 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/model/ReplayGain.kt @@ -0,0 +1,9 @@ +package com.cappielloantonio.tempo.model + +import androidx.annotation.Keep + +@Keep +data class ReplayGain( + var trackGain: Float = 0f, + var albumGain: Float = 0f, +) \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/ReplayGainUtil.java b/app/src/main/java/com/cappielloantonio/tempo/util/ReplayGainUtil.java new file mode 100644 index 00000000..a56897f8 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/util/ReplayGainUtil.java @@ -0,0 +1,107 @@ +package com.cappielloantonio.tempo.util; + +import androidx.media3.common.Metadata; +import androidx.media3.common.Tracks; +import androidx.media3.exoplayer.ExoPlayer; + +import com.cappielloantonio.tempo.model.ReplayGain; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ReplayGainUtil { + private static final String[] tags = {"REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN", "R128_TRACK_GAIN", "R128_ALBUM_GAIN"}; + + public static void setReplayGain(ExoPlayer player, Tracks tracks) { + List metadata = getMetadata(tracks); + List gains = getReplayGains(metadata); + + applyReplayGain(player, gains); + } + + private static List getMetadata(Tracks tracks) { + List metadata = new ArrayList<>(); + + for (int i = 0; i < tracks.getGroups().size(); i++) { + Tracks.Group group = tracks.getGroups().get(i); + + for (int j = 0; j < group.getMediaTrackGroup().length; j++) { + metadata.add(group.getTrackFormat(j).metadata); + } + } + + return metadata; + } + + private static List getReplayGains(List metadata) { + List gains = new ArrayList<>(); + + for (int i = 0; i < metadata.size(); i++) { + for (int j = 0; j < metadata.get(i).length(); j++) { + Metadata.Entry entry = metadata.get(i).get(j); + + if (checkReplayGain(entry)) { + ReplayGain replayGain = setReplayGains(entry); + gains.add(replayGain); + } + } + } + + return gains; + } + + private static boolean checkReplayGain(Metadata.Entry entry) { + for (String tag : tags) { + if (entry.toString().contains(tag)) { + return true; + } + } + + return false; + } + + private static ReplayGain setReplayGains(Metadata.Entry entry) { + ReplayGain replayGain = new ReplayGain(); + + if (entry.toString().contains(tags[0])) { + replayGain.setTrackGain(parseReplayGainTag(entry)); + } + + if (entry.toString().contains(tags[1])) { + replayGain.setAlbumGain(parseReplayGainTag(entry)); + } + + if (entry.toString().contains(tags[2])) { + replayGain.setTrackGain(parseReplayGainTag(entry) / 256f); + } + + if (entry.toString().contains(tags[3])) { + replayGain.setAlbumGain(parseReplayGainTag(entry) / 256f); + } + + return replayGain; + } + + private static Float parseReplayGainTag(Metadata.Entry entry) { + try { + return Float.parseFloat(entry.toString().replaceAll("[^\\d.]", "")); + } catch (NumberFormatException exception) { + return 0f; + } + } + + private static void applyReplayGain(ExoPlayer player, List gains) { + if (Objects.equals(Preferences.getReplayGainMode(), "disabled") || gains.size() == 0) { + setReplayGain(player, 0f); + } else if (Objects.equals(Preferences.getReplayGainMode(), "track")) { + setReplayGain(player, gains.get(0).getTrackGain() != 0f ? gains.get(0).getTrackGain() : gains.get(0).getAlbumGain()); + } else if (Objects.equals(Preferences.getReplayGainMode(), "album")) { + setReplayGain(player, gains.get(0).getAlbumGain() != 0f ? gains.get(0).getAlbumGain() : gains.get(0).getTrackGain()); + } + } + + private static void setReplayGain(ExoPlayer player, float gain) { + player.setVolume((float) Math.pow(10f, -gain / 20f)); + } +} diff --git a/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt index 1a445eb9..02f9e448 100644 --- a/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt +++ b/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt @@ -10,12 +10,15 @@ import androidx.media3.common.* import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import androidx.media3.exoplayer.source.TrackGroupArray +import androidx.media3.exoplayer.trackselection.TrackSelectionArray import androidx.media3.session.* import androidx.media3.session.MediaSession.ControllerInfo import com.cappielloantonio.tempo.R import com.cappielloantonio.tempo.ui.activity.MainActivity import com.cappielloantonio.tempo.util.Constants import com.cappielloantonio.tempo.util.DownloadUtil +import com.cappielloantonio.tempo.util.ReplayGainUtil import com.google.common.collect.ImmutableList import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture @@ -170,6 +173,10 @@ class MediaService : MediaLibraryService() { } } + override fun onTracksChanged(tracks: Tracks) { + ReplayGainUtil.setReplayGain(player, tracks) + } + override fun onIsPlayingChanged(isPlaying: Boolean) { if (!isPlaying) { MediaManager.setPlayingPausedTimestamp(