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.
This commit is contained in:
parent
672c256b1e
commit
4ee20bc112
10 changed files with 57 additions and 59 deletions
|
@ -4,11 +4,13 @@
|
||||||
|
|
||||||
#### What's Improved
|
#### What's Improved
|
||||||
- Tags formatted as `artistssort` or `albumartistssort` are now recognized by Auxio
|
- Tags formatted as `artistssort` or `albumartistssort` are now recognized by Auxio
|
||||||
|
- Non-english digit strings are now sorted better
|
||||||
- Reduced visual loading time
|
- Reduced visual loading time
|
||||||
|
|
||||||
#### What's Fixed
|
#### What's Fixed
|
||||||
- Disc number is no longer mis-aligned when no subtitle is present
|
- Disc number is no longer mis-aligned when no subtitle is present
|
||||||
- Fixed selection not updating when playlists are changed
|
- Fixed selection not updating when playlists are changed
|
||||||
|
- Fixed duplicate albums appearing in certain cases
|
||||||
|
|
||||||
## 3.1.1
|
## 3.1.1
|
||||||
|
|
||||||
|
|
|
@ -257,7 +257,7 @@ interface Song : Music {
|
||||||
/** The duration of the audio file, in milliseconds. */
|
/** The duration of the audio file, in milliseconds. */
|
||||||
val durationMs: Long
|
val durationMs: Long
|
||||||
/** The ReplayGain adjustment to apply during playback. */
|
/** 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. */
|
/** The date the audio file was added to the device, as a unix epoch timestamp. */
|
||||||
val dateAdded: Long
|
val dateAdded: Long
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -215,9 +215,8 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
|
||||||
is SongImpl -> body.raw = PrioritizedRaw(rawArtist, album)
|
is SongImpl -> body.raw = PrioritizedRaw(rawArtist, album)
|
||||||
is AlbumImpl -> {
|
is AlbumImpl -> {
|
||||||
// Album information from later dates is prioritized, as it is more
|
// Album information from later dates is prioritized, as it is more
|
||||||
// likely to
|
// likely to contain the "modern" name of the artist if the information
|
||||||
// contain the "modern" name of the artist if the information really is
|
// really is in-consistent. Fall back to the name otherwise.
|
||||||
// in-consistent. Fall back to the name otherwise.
|
|
||||||
val dateEarlier =
|
val dateEarlier =
|
||||||
album.dates != null &&
|
album.dates != null &&
|
||||||
(prioritized.dates == null || album.dates < prioritized.dates)
|
(prioritized.dates == null || album.dates < prioritized.dates)
|
||||||
|
|
|
@ -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 size = requireNotNull(rawSong.size) { "Invalid raw: No size" }
|
||||||
override val durationMs = requireNotNull(rawSong.durationMs) { "Invalid raw: No duration" }
|
override val durationMs = requireNotNull(rawSong.durationMs) { "Invalid raw: No duration" }
|
||||||
override val replayGainAdjustment =
|
override val replayGainAdjustment =
|
||||||
if (rawSong.replayGainTrackAdjustment != null &&
|
ReplayGainAdjustment(
|
||||||
rawSong.replayGainAlbumAdjustment != null) {
|
track = rawSong.replayGainTrackAdjustment, album = rawSong.replayGainAlbumAdjustment)
|
||||||
ReplayGainAdjustment(
|
|
||||||
track = unlikelyToBeNull(rawSong.replayGainTrackAdjustment),
|
|
||||||
album = unlikelyToBeNull(rawSong.replayGainAlbumAdjustment))
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
override val dateAdded = requireNotNull(rawSong.dateAdded) { "Invalid raw: No date added" }
|
override val dateAdded = requireNotNull(rawSong.dateAdded) { "Invalid raw: No date added" }
|
||||||
private var _album: AlbumImpl? = null
|
private var _album: AlbumImpl? = null
|
||||||
override val album: Album
|
override val album: Album
|
||||||
|
|
|
@ -128,7 +128,6 @@ data class RawAlbum(
|
||||||
// - If we do not have a MusicBrainz ID, compare by the lowercase album name and lowercase
|
// - 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
|
// 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").
|
// for albums/artists that have different naming (ex. "RAMMSTEIN" vs. "Rammstein").
|
||||||
|
|
||||||
private val artistKeys = inner.rawArtists.map { it.key }
|
private val artistKeys = inner.rawArtists.map { it.key }
|
||||||
|
|
||||||
// Cache the hash-code for HashMap efficiency.
|
// Cache the hash-code for HashMap efficiency.
|
||||||
|
|
|
@ -28,6 +28,8 @@ import androidx.media3.extractor.metadata.vorbis.VorbisComment
|
||||||
*
|
*
|
||||||
* @param metadata The [Metadata] to wrap.
|
* @param metadata The [Metadata] to wrap.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*
|
||||||
|
* TODO: Merge with TagWorker
|
||||||
*/
|
*/
|
||||||
class TextTags(metadata: Metadata) {
|
class TextTags(metadata: Metadata) {
|
||||||
private val _id3v2 = mutableMapOf<String, List<String>>()
|
private val _id3v2 = mutableMapOf<String, List<String>>()
|
||||||
|
|
|
@ -81,7 +81,7 @@ class PlayFromArtistDialog :
|
||||||
|
|
||||||
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
|
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
|
||||||
super.onDestroyBinding(binding)
|
super.onDestroyBinding(binding)
|
||||||
choiceAdapter
|
binding.choiceRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ class PlayFromGenreDialog :
|
||||||
|
|
||||||
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
|
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
|
||||||
super.onDestroyBinding(binding)
|
super.onDestroyBinding(binding)
|
||||||
choiceAdapter
|
binding.choiceRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Genre, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: Genre, viewHolder: RecyclerView.ViewHolder) {
|
||||||
|
|
|
@ -53,10 +53,11 @@ enum class ReplayGainMode {
|
||||||
/**
|
/**
|
||||||
* Represents a ReplayGain adjustment to apply during song playback.
|
* Represents a ReplayGain adjustment to apply during song playback.
|
||||||
*
|
*
|
||||||
* @param track The track-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.
|
* @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.
|
* The current ReplayGain pre-amp configuration.
|
||||||
|
|
|
@ -99,58 +99,58 @@ constructor(
|
||||||
* @param song The [Format] of the currently playing track, or null if nothing is playing.
|
* @param song The [Format] of the currently playing track, or null if nothing is playing.
|
||||||
*/
|
*/
|
||||||
private fun applyReplayGain(song: Song?) {
|
private fun applyReplayGain(song: Song?) {
|
||||||
|
if (song == null) {
|
||||||
|
logD("Nothing playing, disabling adjustment")
|
||||||
|
volume = 1f
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logD("Applying ReplayGain adjustment for $song")
|
logD("Applying ReplayGain adjustment for $song")
|
||||||
val gain = song?.replayGainAdjustment
|
|
||||||
|
val gain = song.replayGainAdjustment
|
||||||
val preAmp = playbackSettings.replayGainPreAmp
|
val preAmp = playbackSettings.replayGainPreAmp
|
||||||
|
|
||||||
val adjust =
|
// ReplayGain is configurable, so determine what to do based off of the mode.
|
||||||
if (gain != null) {
|
val resolvedAdjustment =
|
||||||
logD("Found ReplayGain adjustment $gain")
|
when (playbackSettings.replayGainMode) {
|
||||||
// ReplayGain is configurable, so determine what to do based off of the mode.
|
// User wants track gain to be preferred. Default to album gain only if
|
||||||
val useAlbumGain =
|
// there is no track gain.
|
||||||
when (playbackSettings.replayGainMode) {
|
ReplayGainMode.TRACK -> {
|
||||||
// User wants track gain to be preferred. Default to album gain only if
|
logD("Using track strategy")
|
||||||
// there is no track gain.
|
gain.track ?: gain.album
|
||||||
ReplayGainMode.TRACK -> {
|
}
|
||||||
logD("Using track strategy")
|
// User wants album gain to be preferred. Default to track gain only if
|
||||||
gain.track == 0f
|
// here is no album gain.
|
||||||
}
|
ReplayGainMode.ALBUM -> {
|
||||||
// User wants album gain to be preferred. Default to track gain only if
|
logD("Using album strategy")
|
||||||
// here is no album gain.
|
gain.album ?: gain.track
|
||||||
ReplayGainMode.ALBUM -> {
|
}
|
||||||
logD("Using album strategy")
|
// User wants album gain to be used when in an album, track gain otherwise.
|
||||||
gain.album != 0f
|
ReplayGainMode.DYNAMIC -> {
|
||||||
}
|
logD("Using dynamic strategy")
|
||||||
// User wants album gain to be used when in an album, track gain otherwise.
|
gain.album?.takeIf {
|
||||||
ReplayGainMode.DYNAMIC -> {
|
playbackManager.parent is Album &&
|
||||||
logD("Using dynamic strategy")
|
playbackManager.queue.currentSong?.album == playbackManager.parent
|
||||||
playbackManager.parent is Album &&
|
|
||||||
playbackManager.queue.currentSong?.album == playbackManager.parent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
?: gain.track
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val resolvedGain =
|
val amplifiedAdjustment =
|
||||||
if (useAlbumGain) {
|
if (resolvedAdjustment != null) {
|
||||||
logD("Using album gain")
|
// Successfully resolved an adjustment, apply the corresponding pre-amp
|
||||||
gain.album
|
logD("Applying with pre-amp")
|
||||||
} else {
|
resolvedAdjustment + preAmp.with
|
||||||
logD("Using track gain")
|
|
||||||
gain.track
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the adjustment specified when there is ReplayGain tags.
|
|
||||||
resolvedGain + preAmp.with
|
|
||||||
} else {
|
} else {
|
||||||
// No ReplayGain tags existed, or no tags were parsable, or there was no metadata
|
// No adjustment found, use the corresponding user-defined pre-amp
|
||||||
// in the first place. Return the gain to use when there is no ReplayGain value.
|
logD("Applying without pre-amp")
|
||||||
logD("No ReplayGain tags present")
|
|
||||||
preAmp.without
|
preAmp.without
|
||||||
}
|
}
|
||||||
|
|
||||||
logD("Applying ReplayGain adjustment ${adjust}db")
|
logD("Applying ReplayGain adjustment ${amplifiedAdjustment}db")
|
||||||
|
|
||||||
// Final adjustment along the volume curve.
|
// Final adjustment along the volume curve.
|
||||||
volume = 10f.pow(adjust / 20f)
|
volume = 10f.pow(amplifiedAdjustment / 20f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- AUDIO PROCESSOR IMPLEMENTATION ---
|
// --- AUDIO PROCESSOR IMPLEMENTATION ---
|
||||||
|
|
Loading…
Reference in a new issue