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:
parent
873f15ff40
commit
900a64bc02
3 changed files with 18 additions and 10 deletions
|
@ -16,6 +16,7 @@
|
||||||
- Music loader now caches parsed metadata for faster load times
|
- Music loader now caches parsed metadata for faster load times
|
||||||
- Redesigned icon
|
- Redesigned icon
|
||||||
- Added animated splash screen on Android 12+
|
- Added animated splash screen on Android 12+
|
||||||
|
- Added support for MP4 ReplayGain (`----`) atoms
|
||||||
|
|
||||||
#### What's Improved
|
#### What's Improved
|
||||||
- Sorting now takes accented characters into account
|
- Sorting now takes accented characters into account
|
||||||
|
|
|
@ -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
|
precise/original dates, sort tags, and more
|
||||||
- SD Card-aware folder management
|
- SD Card-aware folder management
|
||||||
- Reliable playback state persistence
|
- 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)
|
- External equalizer support (ex. Wavelet)
|
||||||
- Edge-to-edge
|
- Edge-to-edge
|
||||||
- Embedded covers support
|
- Embedded covers support
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.google.android.exoplayer2.audio.AudioProcessor
|
||||||
import com.google.android.exoplayer2.audio.BaseAudioProcessor
|
import com.google.android.exoplayer2.audio.BaseAudioProcessor
|
||||||
import com.google.android.exoplayer2.metadata.Metadata
|
import com.google.android.exoplayer2.metadata.Metadata
|
||||||
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
|
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 com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import kotlin.math.pow
|
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
|
// (like "replaygain_track_gain"), but can also be uppercase. Make sure that
|
||||||
// capitalization is consistent before continuing.
|
// capitalization is consistent before continuing.
|
||||||
is TextInformationFrame -> {
|
is TextInformationFrame -> {
|
||||||
key = entry.description?.uppercase()
|
key = entry.description
|
||||||
value = entry.values[0]
|
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
|
// Vorbis comment. These are nearly always uppercase, so a check for such is
|
||||||
// skipped.
|
// skipped.
|
||||||
is VorbisComment -> {
|
is VorbisComment -> {
|
||||||
|
@ -148,14 +155,14 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() {
|
||||||
|
|
||||||
// Case 1: Normal ReplayGain, most commonly found on MPEG files.
|
// Case 1: Normal ReplayGain, most commonly found on MPEG files.
|
||||||
tags
|
tags
|
||||||
.findLast { tag -> tag.key == RG_TRACK }
|
.findLast { tag -> tag.key.equals(RG_TRACK, ignoreCase = true) }
|
||||||
?.let { tag ->
|
?.let { tag ->
|
||||||
trackGain = tag.value
|
trackGain = tag.value
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tags
|
tags
|
||||||
.findLast { tag -> tag.key == RG_ALBUM }
|
.findLast { tag -> tag.key.equals(RG_ALBUM, ignoreCase = true) }
|
||||||
?.let { tag ->
|
?.let { tag ->
|
||||||
albumGain = tag.value
|
albumGain = tag.value
|
||||||
found = true
|
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
|
// 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.
|
// is no demand to fix that edge case right now.
|
||||||
tags
|
tags
|
||||||
.findLast { tag -> tag.key == R128_TRACK }
|
.findLast { tag -> tag.key.equals(R128_TRACK, ignoreCase = true) }
|
||||||
?.let { tag ->
|
?.let { tag ->
|
||||||
trackGain += tag.value / 256f
|
trackGain += tag.value / 256f
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tags
|
tags
|
||||||
.findLast { tag -> tag.key == R128_ALBUM }
|
.findLast { tag -> tag.key.equals(R128_ALBUM, ignoreCase = true) }
|
||||||
?.let { tag ->
|
?.let { tag ->
|
||||||
albumGain += tag.value / 256f
|
albumGain += tag.value / 256f
|
||||||
found = true
|
found = true
|
||||||
|
@ -253,10 +260,10 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val RG_TRACK = "REPLAYGAIN_TRACK_GAIN"
|
private const val RG_TRACK = "replaygain_track_gain"
|
||||||
private const val RG_ALBUM = "REPLAYGAIN_ALBUM_GAIN"
|
private const val RG_ALBUM = "replaygain_album_gain"
|
||||||
private const val R128_TRACK = "R128_TRACK_GAIN"
|
private const val R128_TRACK = "r128_track_gain"
|
||||||
private const val R128_ALBUM = "R128_ALBUM_GAIN"
|
private const val R128_ALBUM = "r128_album_gain"
|
||||||
|
|
||||||
private val REPLAY_GAIN_TAGS = arrayOf(RG_TRACK, RG_ALBUM, R128_ALBUM, R128_TRACK)
|
private val REPLAY_GAIN_TAGS = arrayOf(RG_TRACK, RG_ALBUM, R128_ALBUM, R128_TRACK)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue