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.
This commit is contained in:
Alexander Capehart 2022-12-15 21:01:41 -07:00
parent 873f15ff40
commit 900a64bc02
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 18 additions and 10 deletions

View file

@ -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

View file

@ -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

View file

@ -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)
}