diff --git a/README.md b/README.md
index e4457cb6a..4835cd537 100644
--- a/README.md
+++ b/README.md
@@ -63,9 +63,9 @@ I primarily built Auxio for myself, but you can use it too, I guess.
## Building
-Auxio relies on a local version of ExoPlayer that enables some extra features. So, the build process is as follows:
+Auxio relies on a custom version of ExoPlayer that enables some extra features. So, the build process is as follows:
-1. Change into the project directory
+1. Enter into the project directory
2. Run `python3 prebuild.py`, which installs ExoPlayer and it's extensions.
3. Build the project normally in Android Studio.
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
index 99fabac36..ee84720c1 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
@@ -27,6 +27,7 @@ import androidx.media.AudioManagerCompat
import com.google.android.exoplayer2.metadata.Metadata
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
+import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.getSystemServiceSafe
@@ -88,30 +89,54 @@ class AudioReactor(
* This is based off Vanilla Music's implementation.
*/
fun applyReplayGain(metadata: Metadata?) {
- if (settingsManager.replayGainMode == ReplayGainMode.OFF || metadata == null) {
- logD("ReplayGain is disabled or cannot be determined for this track, resetting volume.")
+ if (metadata == null) {
+ logD("No metadata.")
volume = 1f
return
}
+ // ReplayGain is configurable, so determine what to do based off of the mode.
+ val useAlbumGain: (Gain) -> Boolean = when (settingsManager.replayGainMode) {
+ ReplayGainMode.OFF -> {
+ logD("ReplayGain is off.")
+ volume = 1f
+ return
+ }
+
+ // User wants track gain to be preferred
+ ReplayGainMode.TRACK ->
+ { gain ->
+ gain.track == 0f
+ }
+
+ ReplayGainMode.ALBUM ->
+ { gain ->
+ gain.album != 0f
+ }
+
+ ReplayGainMode.DYNAMIC ->
+ { _ ->
+ playbackManager.parent is Album &&
+ playbackManager.song?.album == playbackManager.parent
+ }
+ }
val gain = parseReplayGain(metadata)
- // Currently we consider both the album and the track gain.
- var adjust = 0f
-
- if (gain != null) {
- // Allow the user to configure a preferred mode for ReplayGain.
- adjust = if (settingsManager.replayGainMode == ReplayGainMode.TRACK) {
- if (gain.track != 0f) gain.track else gain.album
+ val adjust = if (gain != null) {
+ if (useAlbumGain(gain)) {
+ logD("Using album gain.")
+ gain.album
} else {
- if (gain.album != 0f) gain.album else gain.track
+ logD("Using track gain.")
+ gain.track
}
+ } else {
+ 0f
}
// Final adjustment along the volume curve.
// Ensure this is clamped to 0 or 1 so that it can be used as a volume.
volume = MathUtils.clamp((10f.pow((adjust / 20f))), 0f, 1f)
- logD("Applied ReplayGain adjustment: $volume")
}
private fun parseReplayGain(metadata: Metadata): Gain? {
@@ -126,14 +151,25 @@ class AudioReactor(
for (i in 0 until metadata.length()) {
val entry = metadata.get(i)
- // Sometimes the ReplayGain keys will be lowercase, so make them uppercase.
- if (entry is TextInformationFrame && entry.description?.uppercase() in REPLAY_GAIN_TAGS) {
- tags.add(GainTag(entry.description!!.uppercase(), parseReplayGainFloat(entry.value)))
- continue
+ val key: String?
+ val value: String
+
+ when (entry) {
+ is TextInformationFrame -> {
+ key = entry.description?.uppercase()
+ value = entry.value
+ }
+
+ is VorbisComment -> {
+ key = entry.key
+ value = entry.value
+ }
+
+ else -> continue
}
- if (entry is VorbisComment && entry.key.uppercase() in REPLAY_GAIN_TAGS) {
- tags.add(GainTag(entry.key.uppercase(), parseReplayGainFloat(entry.value)))
+ if (key in REPLAY_GAIN_TAGS) {
+ tags.add(GainTag(key!!, parseReplayGainFloat(value)))
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt
index 9c085b080..b28d2ef4a 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt
@@ -3,13 +3,15 @@ package org.oxycblt.auxio.playback.system
enum class ReplayGainMode {
OFF,
TRACK,
- ALBUM;
+ ALBUM,
+ DYNAMIC;
fun toInt(): Int {
return when (this) {
OFF -> INT_OFF
TRACK -> INT_TRACK
ALBUM -> INT_ALBUM
+ DYNAMIC -> INT_DYNAMIC
}
}
@@ -17,12 +19,14 @@ enum class ReplayGainMode {
private const val INT_OFF = 0xA110
private const val INT_TRACK = 0xA111
private const val INT_ALBUM = 0xA112
+ private const val INT_DYNAMIC = 0xA113
fun fromInt(value: Int): ReplayGainMode? {
return when (value) {
INT_OFF -> OFF
INT_TRACK -> TRACK
INT_ALBUM -> ALBUM
+ INT_DYNAMIC -> DYNAMIC
else -> null
}
}
diff --git a/app/src/main/res/values/settings.xml b/app/src/main/res/values/settings.xml
index 3da1a79ac..e027cb94f 100644
--- a/app/src/main/res/values/settings.xml
+++ b/app/src/main/res/values/settings.xml
@@ -36,12 +36,14 @@
- @string/set_replay_gain_off
- @string/set_replay_gain_track
- @string/set_replay_gain_album
+ - @string/set_replay_gain_dynamic
- @integer/replay_gain_off
- @integer/replay_gain_track
- @integer/replay_gain_album
+ - @integer/replay_gain_dynamic
-1
@@ -56,4 +58,5 @@
0xA110
0xA111
0xA112
+ 0xA113
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d4b83a1ea..4b1250834 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -85,10 +85,11 @@
Pause when other audio plays (ex. Calls)
Headset focus
Play/Pause when the headset connection changes
- ReplayGain (MP3/FLAC Only)
+ ReplayGain
Off
Prefer track
Prefer album
+ Dynamic
Behavior
When a song is selected
diff --git a/info/FAQ.md b/info/FAQ.md
index 28d95654a..b918951d3 100644
--- a/info/FAQ.md
+++ b/info/FAQ.md
@@ -30,6 +30,19 @@ As per the [Supported ExoPlayer Formats](https://exoplayer.dev/supported-formats
MP4, MP3, MKA, OGG, WAV, MPEG, AAC on all versions of Android. Auxio also supports FLAC on all versions
of Android through the use of the ExoPlayer FLAC extension.
+#### ReplayGain isn't working on my music!
+
+This is for a couple reason:
+- Auxio doesn't extract ReplayGain tags for your format. This is the case with MP4 files since there's no
+defined ReplayGain standard for those.
+- Auxio doesn't recognize your ReplayGain tags. This is usually because of a non-standard tag like ID3v2's `RVAD` or
+an unrecognized name.
+
+#### What is dynamic ReplayGain?
+
+Dynamic ReplayGain is a quirk based off the FooBar2000 plugin that dynamically switches from track gain to album
+gain depending on if the current playback is from an album or not.
+
#### Why are accents lighter/less saturated in dark mode?
As per the [Material Design Guidelines](https://material.io/design/color/dark-theme.html), accents should be less
diff --git a/prebuild.py b/prebuild.py
index 2e0ef6401..48fb76b11 100755
--- a/prebuild.py
+++ b/prebuild.py
@@ -33,7 +33,7 @@ def sh(cmd):
exoplayer_path = os.path.join(os.path.abspath(os.curdir), "deps", "exoplayer")
if os.path.exists(exoplayer_path):
- reinstall = input(INFO + "info:" + NC + " ExoPlayer is already installed. Would you like to reinstall it? [y/n] ")
+ reinstall = input(INFO + "info:" + NC + " exoplayer is already installed. would you like to reinstall it? [y/n] ")
if not re.match("[yY][eE][sS]|[yY]", reinstall):
sys.exit(0)
@@ -55,31 +55,31 @@ if ndk_path is None or not os.path.isfile(os.path.join(ndk_path, "ndk_build")):
candidates.append(entry.path)
if len(candidates) > 0:
- print(WARN + "warn:" + NC + " NDK_PATH was not set or invalid. Multiple candidates were found however:")
+ print(WARN + "warn:" + NC + " NDK_PATH was not set or invalid. multiple candidates were found however:")
for i, candidate in enumerate(candidates):
print("[" + str(i) + "] " + candidate)
try:
- ndk_path = candidates[int(input("Enter the NDK to use [Default 0]: "))]
+ ndk_path = candidates[int(input("Enter the ndk to use [Default 0]: "))]
except:
ndk_path = candidates[0]
else:
- print(FATAL + "fatal:" + NC + " NDK_PATH is either invalid, or the Android NDK was not installed at a recognized location.")
+ print(FATAL + "fatal:" + NC + " NDK_PATH is either invalid, or the android ndk was not installed at a recognized location.")
system.exit(1)
# Now try to install ExoPlayer.
sh("rm -rf deps")
-print(INFO + "info:" + NC + " Cloning ExoPlayer...")
+print(INFO + "info:" + NC + " cloning ExoPlayer...")
sh("git clone https://github.com/oxygencobalt/ExoPlayer.git " + exoplayer_path)
os.chdir(exoplayer_path)
sh("git checkout release-v2")
flac_ext_jni_path = os.path.join("extensions", "flac", "src", "main", "jni")
-print(INFO + "info:" + NC + " Installing FLAC extension...")
+print(INFO + "info:" + NC + " installing FLAC extension...")
os.chdir(flac_ext_jni_path)
sh('curl "https://ftp.osuosl.org/pub/xiph/releases/flac/flac-' + FLAC_VERSION + '.tar.xz" | tar xJ && mv "flac-' + FLAC_VERSION + '" flac')
sh(ndk_path + "/ndk-build APP_ABI=all -j4")
-print(OK + "success:" + NC + " Completed pre-build.")
+print(OK + "success:" + NC + " completed pre-build.")