From 4ee20bc112606161c7a86534a4d445dcf7ef6bab Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 9 Jun 2023 10:07:27 -0600 Subject: [PATCH] playback: fix regression regarding partial rgadj Fix a regression where partial ReplayGain adjustments missing an album or track component would not be indexed. This was an oversight made when moving adjustments to the cache, as the nullability model of ReplayGain adjustments changed. --- CHANGELOG.md | 2 + .../java/org/oxycblt/auxio/music/Music.kt | 2 +- .../auxio/music/device/DeviceLibrary.kt | 5 +- .../auxio/music/device/DeviceMusicImpl.kt | 11 +-- .../oxycblt/auxio/music/device/RawMusic.kt | 1 - .../oxycblt/auxio/music/metadata/TextTags.kt | 2 + .../playback/picker/PlayFromArtistDialog.kt | 2 +- .../playback/picker/PlayFromGenreDialog.kt | 2 +- .../auxio/playback/replaygain/ReplayGain.kt | 7 +- .../replaygain/ReplayGainAudioProcessor.kt | 82 +++++++++---------- 10 files changed, 57 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c61131ac..1f121f950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,13 @@ #### What's Improved - Tags formatted as `artistssort` or `albumartistssort` are now recognized by Auxio +- Non-english digit strings are now sorted better - Reduced visual loading time #### What's Fixed - Disc number is no longer mis-aligned when no subtitle is present - Fixed selection not updating when playlists are changed +- Fixed duplicate albums appearing in certain cases ## 3.1.1 diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index edb5a69e1..395b17d93 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -257,7 +257,7 @@ interface Song : Music { /** The duration of the audio file, in milliseconds. */ val durationMs: Long /** The ReplayGain adjustment to apply during playback. */ - val replayGainAdjustment: ReplayGainAdjustment? + val replayGainAdjustment: ReplayGainAdjustment /** The date the audio file was added to the device, as a unix epoch timestamp. */ val dateAdded: Long /** diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt index 08e312ed5..9ec43ee9b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceLibrary.kt @@ -215,9 +215,8 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu is SongImpl -> body.raw = PrioritizedRaw(rawArtist, album) is AlbumImpl -> { // Album information from later dates is prioritized, as it is more - // likely to - // contain the "modern" name of the artist if the information really is - // in-consistent. Fall back to the name otherwise. + // likely to contain the "modern" name of the artist if the information + // really is in-consistent. Fall back to the name otherwise. val dateEarlier = album.dates != null && (prioritized.dates == null || album.dates < prioritized.dates) diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt index aaad318b3..82827b59b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt @@ -90,14 +90,9 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings) : Son override val size = requireNotNull(rawSong.size) { "Invalid raw: No size" } override val durationMs = requireNotNull(rawSong.durationMs) { "Invalid raw: No duration" } override val replayGainAdjustment = - if (rawSong.replayGainTrackAdjustment != null && - rawSong.replayGainAlbumAdjustment != null) { - ReplayGainAdjustment( - track = unlikelyToBeNull(rawSong.replayGainTrackAdjustment), - album = unlikelyToBeNull(rawSong.replayGainAlbumAdjustment)) - } else { - null - } + ReplayGainAdjustment( + track = rawSong.replayGainTrackAdjustment, album = rawSong.replayGainAlbumAdjustment) + override val dateAdded = requireNotNull(rawSong.dateAdded) { "Invalid raw: No date added" } private var _album: AlbumImpl? = null override val album: Album diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt b/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt index 5358f0244..5828ac75a 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt @@ -128,7 +128,6 @@ data class RawAlbum( // - If we do not have a MusicBrainz ID, compare by the lowercase album name and lowercase // artist name. This allows for case-insensitive artist/album grouping, which can be common // for albums/artists that have different naming (ex. "RAMMSTEIN" vs. "Rammstein"). - private val artistKeys = inner.rawArtists.map { it.key } // Cache the hash-code for HashMap efficiency. diff --git a/app/src/main/java/org/oxycblt/auxio/music/metadata/TextTags.kt b/app/src/main/java/org/oxycblt/auxio/music/metadata/TextTags.kt index cecd572ba..a3d916b69 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/metadata/TextTags.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/metadata/TextTags.kt @@ -28,6 +28,8 @@ import androidx.media3.extractor.metadata.vorbis.VorbisComment * * @param metadata The [Metadata] to wrap. * @author Alexander Capehart (OxygenCobalt) + * + * TODO: Merge with TagWorker */ class TextTags(metadata: Metadata) { private val _id3v2 = mutableMapOf>() diff --git a/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromArtistDialog.kt b/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromArtistDialog.kt index c8fc134e7..e42e6ff61 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromArtistDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromArtistDialog.kt @@ -81,7 +81,7 @@ class PlayFromArtistDialog : override fun onDestroyBinding(binding: DialogMusicChoicesBinding) { super.onDestroyBinding(binding) - choiceAdapter + binding.choiceRecycler.adapter = null } override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromGenreDialog.kt b/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromGenreDialog.kt index 1f2693a10..6811e3510 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromGenreDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/picker/PlayFromGenreDialog.kt @@ -81,7 +81,7 @@ class PlayFromGenreDialog : override fun onDestroyBinding(binding: DialogMusicChoicesBinding) { super.onDestroyBinding(binding) - choiceAdapter + binding.choiceRecycler.adapter = null } override fun onClick(item: Genre, viewHolder: RecyclerView.ViewHolder) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGain.kt b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGain.kt index 8a85b73e2..1495fd6fb 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGain.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGain.kt @@ -53,10 +53,11 @@ enum class ReplayGainMode { /** * Represents a ReplayGain adjustment to apply during song playback. * - * @param track The track-specific adjustment that should be applied. - * @param album A more general album-specific adjustment that should be applied. + * @param track The track-specific adjustment that should be applied. Null if not available. + * @param album A more general album-specific adjustment that should be applied. Null if not + * available. */ -data class ReplayGainAdjustment(val track: Float, val album: Float) +data class ReplayGainAdjustment(val track: Float?, val album: Float?) /** * The current ReplayGain pre-amp configuration. 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 d74e45543..5c039ab8c 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 @@ -99,58 +99,58 @@ constructor( * @param song The [Format] of the currently playing track, or null if nothing is playing. */ private fun applyReplayGain(song: Song?) { + if (song == null) { + logD("Nothing playing, disabling adjustment") + volume = 1f + return + } + logD("Applying ReplayGain adjustment for $song") - val gain = song?.replayGainAdjustment + + val gain = song.replayGainAdjustment val preAmp = playbackSettings.replayGainPreAmp - val adjust = - if (gain != null) { - logD("Found ReplayGain adjustment $gain") - // ReplayGain is configurable, so determine what to do based off of the mode. - val useAlbumGain = - when (playbackSettings.replayGainMode) { - // User wants track gain to be preferred. Default to album gain only if - // there is no track gain. - ReplayGainMode.TRACK -> { - logD("Using track strategy") - gain.track == 0f - } - // User wants album gain to be preferred. Default to track gain only if - // here is no album gain. - ReplayGainMode.ALBUM -> { - logD("Using album strategy") - gain.album != 0f - } - // User wants album gain to be used when in an album, track gain otherwise. - ReplayGainMode.DYNAMIC -> { - logD("Using dynamic strategy") - playbackManager.parent is Album && - playbackManager.queue.currentSong?.album == playbackManager.parent - } + // ReplayGain is configurable, so determine what to do based off of the mode. + val resolvedAdjustment = + when (playbackSettings.replayGainMode) { + // User wants track gain to be preferred. Default to album gain only if + // there is no track gain. + ReplayGainMode.TRACK -> { + logD("Using track strategy") + gain.track ?: gain.album + } + // User wants album gain to be preferred. Default to track gain only if + // here is no album gain. + ReplayGainMode.ALBUM -> { + logD("Using album strategy") + gain.album ?: gain.track + } + // User wants album gain to be used when in an album, track gain otherwise. + ReplayGainMode.DYNAMIC -> { + logD("Using dynamic strategy") + gain.album?.takeIf { + playbackManager.parent is Album && + playbackManager.queue.currentSong?.album == playbackManager.parent } + ?: gain.track + } + } - val resolvedGain = - if (useAlbumGain) { - logD("Using album gain") - gain.album - } else { - logD("Using track gain") - gain.track - } - - // Apply the adjustment specified when there is ReplayGain tags. - resolvedGain + preAmp.with + val amplifiedAdjustment = + if (resolvedAdjustment != null) { + // Successfully resolved an adjustment, apply the corresponding pre-amp + logD("Applying with pre-amp") + resolvedAdjustment + preAmp.with } else { - // No ReplayGain tags existed, or no tags were parsable, or there was no metadata - // in the first place. Return the gain to use when there is no ReplayGain value. - logD("No ReplayGain tags present") + // No adjustment found, use the corresponding user-defined pre-amp + logD("Applying without pre-amp") preAmp.without } - logD("Applying ReplayGain adjustment ${adjust}db") + logD("Applying ReplayGain adjustment ${amplifiedAdjustment}db") // Final adjustment along the volume curve. - volume = 10f.pow(adjust / 20f) + volume = 10f.pow(amplifiedAdjustment / 20f) } // --- AUDIO PROCESSOR IMPLEMENTATION ---