music: add texttags tests
Add tests for the TextTags processing wrapper.
This commit is contained in:
parent
502dd8ccc4
commit
782b570b38
5 changed files with 113 additions and 14 deletions
2
.github/workflows/android.yml
vendored
2
.github/workflows/android.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
- name: Test app with Gradle
|
- name: Test app with Gradle
|
||||||
run: ./gradlew app:test
|
run: ./gradlew app:testDebug
|
||||||
- name: Build debug APK with Gradle
|
- name: Build debug APK with Gradle
|
||||||
run: ./gradlew app:packageDebug
|
run: ./gradlew app:packageDebug
|
||||||
- name: Upload debug APK artifact
|
- name: Upload debug APK artifact
|
||||||
|
|
|
@ -160,9 +160,9 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
|
|
||||||
val metadata = format.metadata
|
val metadata = format.metadata
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
val tags = Tags(metadata)
|
val textTags = TextTags(metadata)
|
||||||
populateWithId3v2(tags.id3v2)
|
populateWithId3v2(textTags.id3v2)
|
||||||
populateWithVorbis(tags.vorbis)
|
populateWithVorbis(textTags.vorbis)
|
||||||
} else {
|
} else {
|
||||||
logD("No metadata could be extracted for ${raw.name}")
|
logD("No metadata could be extracted for ${raw.name}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,11 @@ import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
||||||
import org.oxycblt.auxio.music.parsing.correctWhitespace
|
import org.oxycblt.auxio.music.parsing.correctWhitespace
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processing wrapper for [Metadata] that allows access to more organized music tags.
|
* Processing wrapper for [Metadata] that allows organized access to text-based audio tags.
|
||||||
* @param metadata The [Metadata] to wrap.
|
* @param metadata The [Metadata] to wrap.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class Tags(metadata: Metadata) {
|
class TextTags(metadata: Metadata) {
|
||||||
private val _id3v2 = mutableMapOf<String, List<String>>()
|
private val _id3v2 = mutableMapOf<String, List<String>>()
|
||||||
/** The ID3v2 text identification frames found in the file. Can have more than one value. */
|
/** The ID3v2 text identification frames found in the file. Can have more than one value. */
|
||||||
val id3v2: Map<String, List<String>>
|
val id3v2: Map<String, List<String>>
|
||||||
|
@ -65,6 +65,10 @@ class Tags(metadata: Metadata) {
|
||||||
is VorbisComment -> {
|
is VorbisComment -> {
|
||||||
// Vorbis comment keys can be in any case, make them uppercase for simplicity.
|
// Vorbis comment keys can be in any case, make them uppercase for simplicity.
|
||||||
val id = tag.key.sanitize().lowercase()
|
val id = tag.key.sanitize().lowercase()
|
||||||
|
if (id == "metadata_block_picture") {
|
||||||
|
// Picture, we don't care about these
|
||||||
|
continue
|
||||||
|
}
|
||||||
val value = tag.value.sanitize().correctWhitespace()
|
val value = tag.value.sanitize().correctWhitespace()
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
_vorbis.getOrPut(id) { mutableListOf() }.add(value)
|
_vorbis.getOrPut(id) { mutableListOf() }.add(value)
|
|
@ -30,7 +30,7 @@ import java.nio.ByteBuffer
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.extractor.Tags
|
import org.oxycblt.auxio.music.extractor.TextTags
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
@ -166,23 +166,23 @@ class ReplayGainAudioProcessor(private val context: Context) :
|
||||||
* @return A [Adjustment] adjustment, or null if there were no valid adjustments.
|
* @return A [Adjustment] adjustment, or null if there were no valid adjustments.
|
||||||
*/
|
*/
|
||||||
private fun parseReplayGain(format: Format): Adjustment? {
|
private fun parseReplayGain(format: Format): Adjustment? {
|
||||||
val tags = Tags(format.metadata ?: return null)
|
val textTags = TextTags(format.metadata ?: return null)
|
||||||
var trackGain = 0f
|
var trackGain = 0f
|
||||||
var albumGain = 0f
|
var albumGain = 0f
|
||||||
|
|
||||||
// Most ReplayGain tags are formatted as a simple decibel adjustment in a custom
|
// Most ReplayGain tags are formatted as a simple decibel adjustment in a custom
|
||||||
// replaygain_*_gain tag.
|
// replaygain_*_gain tag.
|
||||||
if (format.sampleMimeType != MimeTypes.AUDIO_OPUS) {
|
if (format.sampleMimeType != MimeTypes.AUDIO_OPUS) {
|
||||||
tags.id3v2["TXXX:$TAG_RG_TRACK_GAIN"]
|
textTags.id3v2["TXXX:$TAG_RG_TRACK_GAIN"]
|
||||||
?.run { first().parseReplayGainAdjustment() }
|
?.run { first().parseReplayGainAdjustment() }
|
||||||
?.let { trackGain = it }
|
?.let { trackGain = it }
|
||||||
tags.id3v2["TXXX:$TAG_RG_ALBUM_GAIN"]
|
textTags.id3v2["TXXX:$TAG_RG_ALBUM_GAIN"]
|
||||||
?.run { first().parseReplayGainAdjustment() }
|
?.run { first().parseReplayGainAdjustment() }
|
||||||
?.let { albumGain = it }
|
?.let { albumGain = it }
|
||||||
tags.vorbis[TAG_RG_ALBUM_GAIN]
|
textTags.vorbis[TAG_RG_ALBUM_GAIN]
|
||||||
?.run { first().parseReplayGainAdjustment() }
|
?.run { first().parseReplayGainAdjustment() }
|
||||||
?.let { trackGain = it }
|
?.let { trackGain = it }
|
||||||
tags.vorbis[TAG_RG_TRACK_GAIN]
|
textTags.vorbis[TAG_RG_TRACK_GAIN]
|
||||||
?.run { first().parseReplayGainAdjustment() }
|
?.run { first().parseReplayGainAdjustment() }
|
||||||
?.let { albumGain = it }
|
?.let { albumGain = it }
|
||||||
} else {
|
} else {
|
||||||
|
@ -191,10 +191,10 @@ class ReplayGainAudioProcessor(private val context: Context) :
|
||||||
// intrinsic to the format to create the normalized adjustment. That base adjustment
|
// intrinsic to the format to create the normalized adjustment. That base adjustment
|
||||||
// is already handled by the media framework, so we just need to apply the more
|
// is already handled by the media framework, so we just need to apply the more
|
||||||
// specific adjustments.
|
// specific adjustments.
|
||||||
tags.vorbis[TAG_R128_TRACK_GAIN]
|
textTags.vorbis[TAG_R128_TRACK_GAIN]
|
||||||
?.run { first().parseReplayGainAdjustment() }
|
?.run { first().parseReplayGainAdjustment() }
|
||||||
?.let { trackGain = it / 256f }
|
?.let { trackGain = it / 256f }
|
||||||
tags.vorbis[TAG_R128_ALBUM_GAIN]
|
textTags.vorbis[TAG_R128_ALBUM_GAIN]
|
||||||
?.run { first().parseReplayGainAdjustment() }
|
?.run { first().parseReplayGainAdjustment() }
|
||||||
?.let { albumGain = it / 256f }
|
?.let { albumGain = it / 256f }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.auxio.music.extractor
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.metadata.Metadata
|
||||||
|
import com.google.android.exoplayer2.metadata.flac.PictureFrame
|
||||||
|
import com.google.android.exoplayer2.metadata.id3.ApicFrame
|
||||||
|
import com.google.android.exoplayer2.metadata.id3.InternalFrame
|
||||||
|
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
|
||||||
|
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class TextTagsTest {
|
||||||
|
@Test
|
||||||
|
fun textTags_vorbis() {
|
||||||
|
val textTags = TextTags(VORBIS_METADATA)
|
||||||
|
assertTrue(textTags.id3v2.isEmpty())
|
||||||
|
assertEquals(listOf("Wheel"), textTags.vorbis["title"])
|
||||||
|
assertEquals(listOf("Paraglow"), textTags.vorbis["album"])
|
||||||
|
assertEquals(listOf("Parannoul", "Asian Glow"), textTags.vorbis["artist"])
|
||||||
|
assertEquals(listOf("2022"), textTags.vorbis["date"])
|
||||||
|
assertEquals(listOf("ep"), textTags.vorbis["releasetype"])
|
||||||
|
assertEquals(listOf("+2 dB"), textTags.vorbis["replaygain_track_gain"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun textTags_id3v2() {
|
||||||
|
val textTags = TextTags(ID3V2_METADATA)
|
||||||
|
assertTrue(textTags.vorbis.isEmpty())
|
||||||
|
assertEquals(listOf("Wheel"), textTags.id3v2["TIT2"])
|
||||||
|
assertEquals(listOf("Paraglow"), textTags.id3v2["TALB"])
|
||||||
|
assertEquals(listOf("Parannoul", "Asian Glow"), textTags.id3v2["TPE1"])
|
||||||
|
assertEquals(listOf("2022"), textTags.id3v2["TDRC"])
|
||||||
|
assertEquals(listOf("ep"), textTags.id3v2["TXXX:musicbrainz album type"])
|
||||||
|
assertEquals(listOf("+2 dB"), textTags.id3v2["TXXX:replaygain_track_gain"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun textTags_combined() {
|
||||||
|
val textTags = TextTags(VORBIS_METADATA.copyWithAppendedEntriesFrom(ID3V2_METADATA))
|
||||||
|
assertEquals(listOf("Wheel"), textTags.vorbis["title"])
|
||||||
|
assertEquals(listOf("Paraglow"), textTags.vorbis["album"])
|
||||||
|
assertEquals(listOf("Parannoul", "Asian Glow"), textTags.vorbis["artist"])
|
||||||
|
assertEquals(listOf("2022"), textTags.vorbis["date"])
|
||||||
|
assertEquals(listOf("ep"), textTags.vorbis["releasetype"])
|
||||||
|
assertEquals(listOf("+2 dB"), textTags.vorbis["replaygain_track_gain"])
|
||||||
|
assertEquals(listOf("Wheel"), textTags.id3v2["TIT2"])
|
||||||
|
assertEquals(listOf("Paraglow"), textTags.id3v2["TALB"])
|
||||||
|
assertEquals(listOf("Parannoul", "Asian Glow"), textTags.id3v2["TPE1"])
|
||||||
|
assertEquals(listOf("2022"), textTags.id3v2["TDRC"])
|
||||||
|
assertEquals(listOf("ep"), textTags.id3v2["TXXX:musicbrainz album type"])
|
||||||
|
assertEquals(listOf("+2 dB"), textTags.id3v2["TXXX:replaygain_track_gain"])
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val VORBIS_METADATA =
|
||||||
|
Metadata(
|
||||||
|
VorbisComment("TITLE", "Wheel"),
|
||||||
|
VorbisComment("ALBUM", "Paraglow"),
|
||||||
|
VorbisComment("ARTIST", "Parannoul"),
|
||||||
|
VorbisComment("ARTIST", "Asian Glow"),
|
||||||
|
VorbisComment("DATE", "2022"),
|
||||||
|
VorbisComment("RELEASETYPE", "ep"),
|
||||||
|
VorbisComment("METADATA_BLOCK_PICTURE", ""),
|
||||||
|
VorbisComment("REPLAYGAIN_TRACK_GAIN", "+2 dB"),
|
||||||
|
PictureFrame(0, "", "", 0, 0, 0, 0, byteArrayOf()))
|
||||||
|
|
||||||
|
private val ID3V2_METADATA =
|
||||||
|
Metadata(
|
||||||
|
TextInformationFrame("TIT2", null, listOf("Wheel")),
|
||||||
|
TextInformationFrame("TALB", null, listOf("Paraglow")),
|
||||||
|
TextInformationFrame("TPE1", null, listOf("Parannoul", "Asian Glow")),
|
||||||
|
TextInformationFrame("TDRC", null, listOf("2022")),
|
||||||
|
TextInformationFrame("TXXX", "MusicBrainz Album Type", listOf("ep")),
|
||||||
|
InternalFrame("com.apple.iTunes", "replaygain_track_gain", "+2 dB"),
|
||||||
|
ApicFrame("", "", 0, byteArrayOf()))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue