From 900a64bc02221d762570c4fbde22f23ddec30e28 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Thu, 15 Dec 2022 21:01:41 -0700 Subject: [PATCH] playback: add support for mp4 replaygain Add support for MP4 ReplayGain tags. These are usually under a `----` atom with an iTunes domain and ReplayGain description. These are mapped to an ID3v2 internal frame within ExoPlayer, which is why Auxio did not support them, as it only expected Vorbis comments and ID3v2 TXXX frames. Resolves #292. --- CHANGELOG.md | 1 + README.md | 2 +- .../replaygain/ReplayGainAudioProcessor.kt | 25 ++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb8ad2740..48b226fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Music loader now caches parsed metadata for faster load times - Redesigned icon - Added animated splash screen on Android 12+ +- Added support for MP4 ReplayGain (`----`) atoms #### What's Improved - Sorting now takes accented characters into account diff --git a/README.md b/README.md index 38a5a1e00..ddd58df0d 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ I primarily built Auxio for myself, but you can use it too, I guess. precise/original dates, sort tags, and more - SD Card-aware folder management - Reliable playback state persistence -- Full ReplayGain support (On MP3, FLAC, OGG, OPUS, and (some) MP4 files) +- Full ReplayGain support (On MP3, FLAC, OGG, OPUS, and MP4 files) - External equalizer support (ex. Wavelet) - Edge-to-edge - Embedded covers support diff --git a/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainAudioProcessor.kt b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainAudioProcessor.kt index cef7527ff..1b030a194 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainAudioProcessor.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainAudioProcessor.kt @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.audio.AudioProcessor import com.google.android.exoplayer2.audio.BaseAudioProcessor import com.google.android.exoplayer2.metadata.Metadata import com.google.android.exoplayer2.metadata.id3.TextInformationFrame +import com.google.android.exoplayer2.metadata.id3.InternalFrame import com.google.android.exoplayer2.metadata.vorbis.VorbisComment import java.nio.ByteBuffer import kotlin.math.pow @@ -129,9 +130,15 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() { // (like "replaygain_track_gain"), but can also be uppercase. Make sure that // capitalization is consistent before continuing. is TextInformationFrame -> { - key = entry.description?.uppercase() + key = entry.description value = entry.values[0] } + // Internal Frame. This is actually MP4's "----" atom, but mapped to an ID3v2 + // frame by ExoPlayer (presumably to reduce duplication). + is InternalFrame -> { + key = entry.description + value = entry.text + } // Vorbis comment. These are nearly always uppercase, so a check for such is // skipped. is VorbisComment -> { @@ -148,14 +155,14 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() { // Case 1: Normal ReplayGain, most commonly found on MPEG files. tags - .findLast { tag -> tag.key == RG_TRACK } + .findLast { tag -> tag.key.equals(RG_TRACK, ignoreCase = true) } ?.let { tag -> trackGain = tag.value found = true } tags - .findLast { tag -> tag.key == RG_ALBUM } + .findLast { tag -> tag.key.equals(RG_ALBUM, ignoreCase = true) } ?.let { tag -> albumGain = tag.value found = true @@ -168,14 +175,14 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() { // want to read it is to zero previous ReplayGain values for being invalid, however there // is no demand to fix that edge case right now. tags - .findLast { tag -> tag.key == R128_TRACK } + .findLast { tag -> tag.key.equals(R128_TRACK, ignoreCase = true) } ?.let { tag -> trackGain += tag.value / 256f found = true } tags - .findLast { tag -> tag.key == R128_ALBUM } + .findLast { tag -> tag.key.equals(R128_ALBUM, ignoreCase = true) } ?.let { tag -> albumGain += tag.value / 256f found = true @@ -253,10 +260,10 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() { } companion object { - private const val RG_TRACK = "REPLAYGAIN_TRACK_GAIN" - private const val RG_ALBUM = "REPLAYGAIN_ALBUM_GAIN" - private const val R128_TRACK = "R128_TRACK_GAIN" - private const val R128_ALBUM = "R128_ALBUM_GAIN" + private const val RG_TRACK = "replaygain_track_gain" + private const val RG_ALBUM = "replaygain_album_gain" + private const val R128_TRACK = "r128_track_gain" + private const val R128_ALBUM = "r128_album_gain" private val REPLAY_GAIN_TAGS = arrayOf(RG_TRACK, RG_ALBUM, R128_ALBUM, R128_TRACK) }