From 2bdbe212df57ca670d09a0653775b3b7f5c64bc9 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Fri, 29 Apr 2022 12:07:14 -0600 Subject: [PATCH] playback: decouple fields and callback Decouple the callback notifying code and the field code. This makes code more reasonable for future changes. --- CHANGELOG.md | 2 +- .../home/fastscroll/FastScrollPopupView.kt | 3 +- .../home/fastscroll/FastScrollRecyclerView.kt | 6 +- .../auxio/home/list/HomeListFragment.kt | 4 +- .../auxio/playback/PlaybackViewModel.kt | 28 +-- .../playback/state/PlaybackStateManager.kt | 196 ++++++++++-------- .../auxio/playback/system/PlaybackService.kt | 28 +-- .../system/PlaybackSessionConnector.kt | 8 +- .../system/ReplayGainAudioProcessor.kt | 61 +++--- .../oxycblt/auxio/widgets/WidgetController.kt | 8 +- 10 files changed, 186 insertions(+), 158 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e2dbd8b..327468ae0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ - Made the layout of album songs more similar to other songs #### Dev/Meta -- Updated translations [Konstantin Tutsch -> German, cccClyde -> Chinese] +- Updated translations [Konstantin Tutsch -> German, cccClyde -> Chinese, Gsset -> Russian] - Switched to spotless and ktfmt instead of ktlint - Migrated constants to centralized table - Introduced new RecyclerView framework diff --git a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupView.kt b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupView.kt index aec9460d7..f7b86b381 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupView.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupView.kt @@ -115,9 +115,9 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleRes: Int = 0) } override fun isAutoMirrored(): Boolean = true - override fun getOpacity(): Int = PixelFormat.TRANSLUCENT override fun setAlpha(alpha: Int) {} override fun setColorFilter(colorFilter: ColorFilter?) {} + override fun getOpacity(): Int = PixelFormat.TRANSLUCENT private fun updatePath() { val r = bounds.height().toFloat() / 2 @@ -168,6 +168,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleRes: Int = 0) } companion object { + // Cache sqrt(2) for faster calculations private const val SQRT2 = 1.4142135623730950488f } } diff --git a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt index 0d18b4389..ecc00085b 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt @@ -135,11 +135,11 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr removeCallbacks(hideThumbRunnable) showScrollbar() showPopup() - onDragListener?.onFastScrollStart() + listener?.onFastScrollStart() } else { postAutoHideScrollbar() hidePopup() - onDragListener?.onFastScrollStop() + listener?.onFastScrollStop() } } @@ -161,7 +161,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr * A listener for when a drag event occurs. The value will be true if a drag has begun, and * false if a drag ended. */ - var onDragListener: OnFastScrollListener? = null + var listener: OnFastScrollListener? = null init { overlay.add(thumbView) diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt index a4f1c0da1..e444b6af2 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt @@ -52,7 +52,7 @@ abstract class HomeListFragment : override fun onBindingCreated(binding: FragmentHomeListBinding, savedInstanceState: Bundle?) { setupRecycler(binding.homeRecycler) binding.homeRecycler.popupProvider = this - binding.homeRecycler.onDragListener = this + binding.homeRecycler.listener = this } override fun onDestroyBinding(binding: FragmentHomeListBinding) { @@ -60,7 +60,7 @@ abstract class HomeListFragment : binding.homeRecycler.apply { adapter = null popupProvider = null - onDragListener = null + listener = null } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index 768b2b7ee..5dc1c3967 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -314,13 +314,13 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { private fun restorePlaybackState() { logD("Attempting to restore playback state") - onSongUpdate(playbackManager.song) - onPositionUpdate(playbackManager.position) - onParentUpdate(playbackManager.parent) - onQueueUpdate(playbackManager.queue, playbackManager.index) - onPlayingUpdate(playbackManager.isPlaying) - onShuffleUpdate(playbackManager.isShuffling) - onLoopUpdate(playbackManager.loopMode) + onSongChanged(playbackManager.song) + onPositionChanged(playbackManager.position) + onParentChanged(playbackManager.parent) + onQueueChanged(playbackManager.queue, playbackManager.index) + onPlayingChanged(playbackManager.isPlaying) + onShuffleChanged(playbackManager.isShuffling) + onLoopModeChanged(playbackManager.loopMode) } // --- OVERRIDES --- @@ -329,31 +329,31 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { playbackManager.removeCallback(this) } - override fun onSongUpdate(song: Song?) { + override fun onSongChanged(song: Song?) { mSong.value = song } - override fun onParentUpdate(parent: MusicParent?) { + override fun onParentChanged(parent: MusicParent?) { mParent.value = parent } - override fun onPositionUpdate(position: Long) { + override fun onPositionChanged(position: Long) { mPositionSeconds.value = position / 1000 } - override fun onQueueUpdate(queue: List, index: Int) { + override fun onQueueChanged(queue: List, index: Int) { mNextUp.value = queue.slice(index.inc() until queue.size) } - override fun onPlayingUpdate(isPlaying: Boolean) { + override fun onPlayingChanged(isPlaying: Boolean) { mIsPlaying.value = isPlaying } - override fun onShuffleUpdate(isShuffling: Boolean) { + override fun onShuffleChanged(isShuffling: Boolean) { mIsShuffling.value = isShuffling } - override fun onLoopUpdate(loopMode: LoopMode) { + override fun onLoopModeChanged(loopMode: LoopMode) { mLoopMode.value = loopMode } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index dc5fbcd6a..cc5d7face 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -48,42 +48,17 @@ class PlaybackStateManager private constructor() { // Playback private var mSong: Song? = null - set(value) { - field = value - callbacks.forEach { it.onSongUpdate(value) } - } - private var mPosition: Long = 0 - set(value) { - field = value - callbacks.forEach { it.onPositionUpdate(value) } - } private var mParent: MusicParent? = null - set(value) { - field = value - callbacks.forEach { it.onParentUpdate(value) } - } // Queue private var mQueue = mutableListOf() private var mIndex = 0 - // Status + // State private var mIsPlaying = false - set(value) { - field = value - callbacks.forEach { it.onPlayingUpdate(value) } - } - + private var mPosition: Long = 0 private var mIsShuffling = false - set(value) { - field = value - callbacks.forEach { it.onShuffleUpdate(value) } - } private var mLoopMode = LoopMode.NONE - set(value) { - field = value - callbacks.forEach { it.onLoopUpdate(value) } - } private var mIsRestored = false private var mHasPlayed = false @@ -94,24 +69,26 @@ class PlaybackStateManager private constructor() { /** The parent the queue is based on, null if all_songs */ val parent: MusicParent? get() = mParent - /** The current playback progress */ - val position: Long - get() = mPosition - /** The current queue determined by [parent] and [playbackMode] */ + /** The current queue determined by [parent] */ val queue: List get() = mQueue /** The current position in the queue */ val index: Int get() = mIndex + /** Whether playback is paused or not */ val isPlaying: Boolean get() = mIsPlaying - /** Whether the queue is shuffled */ - val isShuffling: Boolean - get() = mIsShuffling + /** The current playback progress */ + val position: Long + get() = mPosition /** The current [LoopMode] */ val loopMode: LoopMode get() = mLoopMode + /** Whether the queue is shuffled */ + val isShuffling: Boolean + get() = mIsShuffling + /** Whether this instance has already been restored */ val isRestored: Boolean get() = mIsRestored @@ -165,9 +142,11 @@ class PlaybackStateManager private constructor() { } } + notifyParentChanged() + updatePlayback(song) // Keep shuffle on, if enabled - setShuffling(settingsManager.keepShuffle && mIsShuffling, keepSong = true) + setShuffling(settingsManager.keepShuffle && isShuffling, keepSong = true) } /** @@ -178,19 +157,21 @@ class PlaybackStateManager private constructor() { logD("Playing ${parent.rawName}") mParent = parent + notifyParentChanged() mIndex = 0 - when (parent) { - is Album -> { - mQueue = parent.songs.toMutableList() + mQueue = + when (parent) { + is Album -> { + parent.songs.toMutableList() + } + is Artist -> { + parent.songs.toMutableList() + } + is Genre -> { + parent.songs.toMutableList() + } } - is Artist -> { - mQueue = parent.songs.toMutableList() - } - is Genre -> { - mQueue = parent.songs.toMutableList() - } - } setShuffling(shuffled, keepSong = false) updatePlayback(mQueue[0]) @@ -211,6 +192,8 @@ class PlaybackStateManager private constructor() { private fun updatePlayback(song: Song, shouldPlay: Boolean = true) { mSong = song mPosition = 0 + notifySongChanged() + notifyPositionChanged() setPlaying(shouldPlay) } @@ -225,10 +208,10 @@ class PlaybackStateManager private constructor() { updatePlayback(mQueue[mIndex]) } else { mIndex = 0 - updatePlayback(mQueue[mIndex], shouldPlay = mLoopMode == LoopMode.ALL) + updatePlayback(mQueue[mIndex], shouldPlay = loopMode == LoopMode.ALL) } - pushQueueUpdate() + notifyQueueChanged() } /** Go to the previous song, doing any checks that are needed. */ @@ -243,7 +226,7 @@ class PlaybackStateManager private constructor() { } updatePlayback(mQueue[mIndex]) - pushQueueUpdate() + notifyQueueChanged() } } @@ -258,7 +241,7 @@ class PlaybackStateManager private constructor() { logD("Removing item ${mQueue[index].rawName}") mQueue.removeAt(index) - pushQueueUpdate() + notifyQueueChanged() return true } @@ -271,7 +254,7 @@ class PlaybackStateManager private constructor() { logD("Moving item $from to position $to") mQueue.add(to, mQueue.removeAt(from)) - pushQueueUpdate() + notifyQueueChanged() return true } @@ -282,7 +265,7 @@ class PlaybackStateManager private constructor() { } mQueue.add(mIndex + 1, song) - pushQueueUpdate() + notifyQueueChanged() } /** Add a list of [songs] to the top of the queue. */ @@ -292,24 +275,19 @@ class PlaybackStateManager private constructor() { } mQueue.addAll(mIndex + 1, songs) - pushQueueUpdate() + notifyQueueChanged() } /** Add a [song] to the end of the queue. */ fun addToQueue(song: Song) { mQueue.add(song) - pushQueueUpdate() + notifyQueueChanged() } /** Add a list of [songs] to the end of the queue. */ fun addToQueue(songs: List) { mQueue.addAll(songs) - pushQueueUpdate() - } - - /** Force any callbacks to receive a queue update. */ - private fun pushQueueUpdate() { - callbacks.forEach { it.onQueueUpdate(mQueue, mIndex) } + notifyQueueChanged() } // --- SHUFFLE FUNCTIONS --- @@ -320,6 +298,7 @@ class PlaybackStateManager private constructor() { */ fun setShuffling(shuffled: Boolean, keepSong: Boolean) { mIsShuffling = shuffled + notifyShufflingChanged() if (mIsShuffling) { genShuffle(keepSong) @@ -348,7 +327,7 @@ class PlaybackStateManager private constructor() { mSong = mQueue[0] } - pushQueueUpdate() + notifyQueueChanged() } /** @@ -359,20 +338,20 @@ class PlaybackStateManager private constructor() { val library = musicStore.library ?: return val lastSong = mSong + val parent = parent mQueue = when (parent) { null -> settingsManager.libSongSort.songs(library.songs).toMutableList() - is Album -> settingsManager.detailAlbumSort.album(mParent as Album).toMutableList() - is Artist -> - settingsManager.detailArtistSort.artist(mParent as Artist).toMutableList() - is Genre -> settingsManager.detailGenreSort.genre(mParent as Genre).toMutableList() + is Album -> settingsManager.detailAlbumSort.album(parent).toMutableList() + is Artist -> settingsManager.detailArtistSort.artist(parent).toMutableList() + is Genre -> settingsManager.detailGenreSort.genre(parent).toMutableList() } if (keepSong) { mIndex = mQueue.indexOf(lastSong) } - pushQueueUpdate() + notifyQueueChanged() } // --- STATE FUNCTIONS --- @@ -385,6 +364,10 @@ class PlaybackStateManager private constructor() { } mIsPlaying = playing + + for (callback in callbacks) { + callback.onPlayingChanged(playing) + } } } @@ -393,11 +376,12 @@ class PlaybackStateManager private constructor() { * @param position The new position in millis. * @see seekTo */ - fun setPosition(position: Long) { + fun synchronizePosition(position: Long) { mSong?.let { song -> // Don't accept any bugged positions that are over the duration of the song. if (position <= song.duration) { mPosition = position + notifyPositionChanged() } } } @@ -409,6 +393,7 @@ class PlaybackStateManager private constructor() { */ fun seekTo(position: Long) { mPosition = position + notifyPositionChanged() callbacks.forEach { it.onSeek(position) } } @@ -427,6 +412,7 @@ class PlaybackStateManager private constructor() { /** Set the [LoopMode] to [mode]. */ fun setLoopMode(mode: LoopMode) { mLoopMode = mode + notifyLoopModeChanged() } /** Mark whether this instance has played or not */ @@ -463,13 +449,13 @@ class PlaybackStateManager private constructor() { database.writeState( PlaybackStateDatabase.SavedState( - mSong, - mPosition, - mParent, - mIndex, + song, + position, + parent, + index, playbackMode, - mIsShuffling, - mLoopMode, + isShuffling, + loopMode, )) database.writeQueue(mQueue) @@ -503,7 +489,7 @@ class PlaybackStateManager private constructor() { if (playbackState != null) { unpackFromPlaybackState(playbackState) mQueue = queue - pushQueueUpdate() + notifyQueueChanged() doParentSanityCheck(playbackState.playbackMode) doIndexSanityCheck() } @@ -515,8 +501,6 @@ class PlaybackStateManager private constructor() { /** Unpack a [playbackState] into this instance. */ private fun unpackFromPlaybackState(playbackState: PlaybackStateDatabase.SavedState) { - // Turn the simplified information from PlaybackState into usable data. - // Do queue setup first mParent = playbackState.parent mIndex = playbackState.queueIndex @@ -526,7 +510,11 @@ class PlaybackStateManager private constructor() { mLoopMode = playbackState.loopMode mIsShuffling = playbackState.isShuffling + notifySongChanged() + notifyParentChanged() seekTo(playbackState.position) + notifyShufflingChanged() + notifyLoopModeChanged() } /** Do a sanity check to make sure the parent was not lost in the restore process. */ @@ -542,6 +530,8 @@ class PlaybackStateManager private constructor() { PlaybackMode.IN_ARTIST -> mQueue.firstOrNull()?.album?.artist PlaybackMode.IN_GENRE -> mQueue.firstOrNull()?.genre } + + notifyParentChanged() } } @@ -554,7 +544,7 @@ class PlaybackStateManager private constructor() { if (correctedIndex > -1) { logD("Correcting malformed index to $correctedIndex") mIndex = correctedIndex - pushQueueUpdate() + notifyQueueChanged() } } } @@ -591,19 +581,57 @@ class PlaybackStateManager private constructor() { } } + // --- CALLBACKS --- + + private fun notifySongChanged() { + for (callback in callbacks) { + callback.onSongChanged(song) + } + } + + private fun notifyParentChanged() { + for (callback in callbacks) { + callback.onParentChanged(parent) + } + } + + private fun notifyPositionChanged() { + for (callback in callbacks) { + callback.onPositionChanged(position) + } + } + + private fun notifyLoopModeChanged() { + for (callback in callbacks) { + callback.onLoopModeChanged(loopMode) + } + } + + private fun notifyShufflingChanged() { + for (callback in callbacks) { + callback.onShuffleChanged(isShuffling) + } + } + + /** Force any callbacks to receive a queue update. */ + private fun notifyQueueChanged() { + for (callback in callbacks) { + callback.onQueueChanged(mQueue, mIndex) + } + } + /** * The interface for receiving updates from [PlaybackStateManager]. Add the callback to * [PlaybackStateManager] using [addCallback], remove them on destruction with [removeCallback]. */ interface Callback { - fun onSongUpdate(song: Song?) {} - fun onParentUpdate(parent: MusicParent?) {} - fun onPositionUpdate(position: Long) {} - fun onQueueUpdate(queue: List, index: Int) {} - fun onModeUpdate(mode: PlaybackMode) {} - fun onPlayingUpdate(isPlaying: Boolean) {} - fun onShuffleUpdate(isShuffling: Boolean) {} - fun onLoopUpdate(loopMode: LoopMode) {} + fun onSongChanged(song: Song?) {} + fun onParentChanged(parent: MusicParent?) {} + fun onPositionChanged(position: Long) {} + fun onQueueChanged(queue: List, index: Int) {} + fun onPlayingChanged(isPlaying: Boolean) {} + fun onShuffleChanged(isShuffling: Boolean) {} + fun onLoopModeChanged(loopMode: LoopMode) {} fun onSeek(position: Long) {} } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index 26241b973..d40825258 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -127,7 +127,7 @@ class PlaybackService : positionScope.launch { while (true) { - playbackManager.setPosition(player.currentPosition) + playbackManager.synchronizePosition(player.currentPosition) delay(POS_POLL_INTERVAL) } } @@ -235,7 +235,7 @@ class PlaybackService : reason: Int ) { if (reason == Player.DISCONTINUITY_REASON_SEEK) { - playbackManager.setPosition(player.currentPosition) + playbackManager.synchronizePosition(player.currentPosition) } } @@ -258,7 +258,7 @@ class PlaybackService : // --- PLAYBACK STATE CALLBACK OVERRIDES --- - override fun onSongUpdate(song: Song?) { + override fun onSongChanged(song: Song?) { if (song != null) { logD("Setting player to ${song.rawName}") player.setMediaItem(MediaItem.fromUri(song.uri)) @@ -273,25 +273,25 @@ class PlaybackService : stopForegroundAndNotification() } - override fun onParentUpdate(parent: MusicParent?) { + override fun onParentChanged(parent: MusicParent?) { notification.setParent(parent) startForegroundOrNotify() } - override fun onPlayingUpdate(isPlaying: Boolean) { + override fun onPlayingChanged(isPlaying: Boolean) { player.playWhenReady = isPlaying notification.setPlaying(isPlaying) startForegroundOrNotify() } - override fun onLoopUpdate(loopMode: LoopMode) { + override fun onLoopModeChanged(loopMode: LoopMode) { if (!settingsManager.useAltNotifAction) { notification.setLoop(loopMode) startForegroundOrNotify() } } - override fun onShuffleUpdate(isShuffling: Boolean) { + override fun onShuffleChanged(isShuffling: Boolean) { if (settingsManager.useAltNotifAction) { notification.setShuffle(isShuffling) startForegroundOrNotify() @@ -306,7 +306,7 @@ class PlaybackService : override fun onColorizeNotifUpdate(doColorize: Boolean) { playbackManager.song?.let { song -> - connector.onSongUpdate(song) + connector.onSongChanged(song) notification.setMetadata(song, ::startForegroundOrNotify) } } @@ -323,7 +323,7 @@ class PlaybackService : override fun onShowCoverUpdate(showCovers: Boolean) { playbackManager.song?.let { song -> - connector.onSongUpdate(song) + connector.onSongChanged(song) notification.setMetadata(song, ::startForegroundOrNotify) } } @@ -376,11 +376,11 @@ class PlaybackService : logD("Restoring the service state") // Re-call existing callbacks with the current values to restore everything - onParentUpdate(playbackManager.parent) - onPlayingUpdate(playbackManager.isPlaying) - onShuffleUpdate(playbackManager.isShuffling) - onLoopUpdate(playbackManager.loopMode) - onSongUpdate(playbackManager.song) + onParentChanged(playbackManager.parent) + onPlayingChanged(playbackManager.isPlaying) + onShuffleChanged(playbackManager.isShuffling) + onLoopModeChanged(playbackManager.loopMode) + onSongChanged(playbackManager.song) onSeek(playbackManager.position) // Notify other classes that rely on this service to also update. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt index a08cad080..cd63183c0 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt @@ -47,8 +47,8 @@ class PlaybackSessionConnector( playbackManager.addCallback(this) player.addListener(this) - onSongUpdate(playbackManager.song) - onPlayingUpdate(playbackManager.isPlaying) + onSongChanged(playbackManager.song) + onPlayingChanged(playbackManager.isPlaying) } fun release() { @@ -108,7 +108,7 @@ class PlaybackSessionConnector( // --- PLAYBACKSTATEMANAGER CALLBACKS --- - override fun onSongUpdate(song: Song?) { + override fun onSongChanged(song: Song?) { if (song == null) { mediaSession.setMetadata(emptyMetadata) return @@ -137,7 +137,7 @@ class PlaybackSessionConnector( } } - override fun onPlayingUpdate(isPlaying: Boolean) { + override fun onPlayingChanged(isPlaying: Boolean) { invalidateSessionState() } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainAudioProcessor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainAudioProcessor.kt index eeea74af2..aaaa34b2c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainAudioProcessor.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainAudioProcessor.kt @@ -64,41 +64,39 @@ class ReplayGainAudioProcessor : BaseAudioProcessor() { * Vanilla Music's implementation. */ fun applyReplayGain(metadata: Metadata?) { - if (metadata == null) { - logW("No metadata could be extracted from this track") + if (metadata == null || settingsManager.replayGainMode == ReplayGainMode.OFF) { + logW( + "Not applying replaygain [" + + "metadata: ${metadata != null}, " + + "enabled: ${settingsManager.replayGainMode == ReplayGainMode.OFF}]") volume = 1f return } - // ReplayGain is configurable, so determine what to do based off of the mode. - val useAlbumGain: (Gain) -> Boolean = - when (settingsManager.replayGainMode) { - ReplayGainMode.OFF -> { - logD("ReplayGain is off") - volume = 1f - return - } - - // User wants track gain to be preferred. Default to album gain only if there - // is no track gain. - ReplayGainMode.TRACK -> { gain -> gain.track == 0f } - - // User wants album gain to be preferred. Default to track gain only if there - // is no album gain. - ReplayGainMode.ALBUM -> { gain -> gain.album != 0f } - - // User wants album gain to be used when in an album, track gain otherwise. - ReplayGainMode.DYNAMIC -> { _ -> - playbackManager.parent is Album && - playbackManager.song?.album == playbackManager.parent - } - } - val gain = parseReplayGain(metadata) val adjust = if (gain != null) { - if (useAlbumGain(gain)) { + // ReplayGain is configurable, so determine what to do based off of the mode. + val useAlbumGain = + when (settingsManager.replayGainMode) { + ReplayGainMode.OFF -> error("Unreachable") + + // User wants track gain to be preferred. Default to album gain only if + // there is no track gain. + ReplayGainMode.TRACK -> gain.track == 0f + + // User wants album gain to be preferred. Default to track gain only if + // here is no album gain. + ReplayGainMode.ALBUM -> gain.album != 0f + + // User wants album gain to be used when in an album, track gain otherwise. + ReplayGainMode.DYNAMIC -> + playbackManager.parent is Album && + playbackManager.song?.album?.id == playbackManager.parent?.id + } + + if (useAlbumGain) { logD("Using album gain") gain.album } else { @@ -177,6 +175,7 @@ class ReplayGainAudioProcessor : BaseAudioProcessor() { } private fun parseReplayGainFloat(raw: String): Float { + // Grok a float from a ReplayGain tag by removing everything that is not 0-9, , or -. return try { raw.replace(Regex("[^0-9.-]"), "").toFloat() } catch (e: Exception) { @@ -211,11 +210,11 @@ class ReplayGainAudioProcessor : BaseAudioProcessor() { } } else { for (i in position until limit step 2) { - var sample = inputBuffer.getLeShort(i) - // Clamp the values to the minimum and maximum values possible for the - // encoding. This prevents issues where samples amplified beyond 1 << 16 - // will end up becoming truncated during the conversion to a short, + // Ensure we clamp the values to the minimum and maximum values possible + // for the encoding. This prevents issues where samples amplified beyond + // 1 << 16 will end up becoming truncated during the conversion to a short, // resulting in popping. + var sample = inputBuffer.getLeShort(i) sample = (sample * volume) .toInt() diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt index 7f48a1daf..0c3eab6a1 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt @@ -61,19 +61,19 @@ class WidgetController(private val context: Context) : // --- PLAYBACKSTATEMANAGER CALLBACKS --- - override fun onSongUpdate(song: Song?) { + override fun onSongChanged(song: Song?) { widget.update(context, playbackManager) } - override fun onPlayingUpdate(isPlaying: Boolean) { + override fun onPlayingChanged(isPlaying: Boolean) { widget.update(context, playbackManager) } - override fun onShuffleUpdate(isShuffling: Boolean) { + override fun onShuffleChanged(isShuffling: Boolean) { widget.update(context, playbackManager) } - override fun onLoopUpdate(loopMode: LoopMode) { + override fun onLoopModeChanged(loopMode: LoopMode) { widget.update(context, playbackManager) }