diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index 5ba6a114b..528b7848e 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -81,7 +81,6 @@ class PlaybackFragment : Fragment() { setOnMenuItemClickListener { item -> if (item.itemId == R.id.action_queue) { findNavController().navigate(MainFragmentDirections.actionShowQueue()) - true } else { false diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt index 2fbe7777c..dba0a3fa3 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt @@ -42,25 +42,25 @@ enum class LoopMode { */ fun toInt(): Int { return when (this) { - NONE -> CONST_NONE - ALL -> CONST_ALL - TRACK -> CONST_TRACK + NONE -> INT_NONE + ALL -> INT_ALL + TRACK -> INT_TRACK } } companion object { - private const val CONST_NONE = 0xA100 - private const val CONST_ALL = 0xA101 - private const val CONST_TRACK = 0xA102 + private const val INT_NONE = 0xA100 + private const val INT_ALL = 0xA101 + private const val INT_TRACK = 0xA102 /** * Convert an int [constant] into a LoopMode, or null if it isnt valid. */ fun fromInt(constant: Int): LoopMode? { return when (constant) { - CONST_NONE -> NONE - CONST_ALL -> ALL - CONST_TRACK -> TRACK + INT_NONE -> NONE + INT_ALL -> ALL + INT_TRACK -> TRACK else -> null } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt index 512fe598e..48924d241 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt @@ -38,30 +38,30 @@ enum class PlaybackMode { */ fun toInt(): Int { return when (this) { - ALL_SONGS -> CONST_ALL_SONGS - IN_ALBUM -> CONST_IN_ALBUM - IN_ARTIST -> CONST_IN_ARTIST - IN_GENRE -> CONST_IN_GENRE + ALL_SONGS -> INT_ALL_SONGS + IN_ALBUM -> INT_IN_ALBUM + IN_ARTIST -> INT_IN_ARTIST + IN_GENRE -> INT_IN_GENRE } } companion object { // Kept in reverse order because of backwards compat, do not re-order these - private const val CONST_ALL_SONGS = 0xA106 - private const val CONST_IN_ALBUM = 0xA105 - private const val CONST_IN_ARTIST = 0xA104 - private const val CONST_IN_GENRE = 0xA103 + private const val INT_ALL_SONGS = 0xA106 + private const val INT_IN_ALBUM = 0xA105 + private const val INT_IN_ARTIST = 0xA104 + private const val INT_IN_GENRE = 0xA103 /** * Get a [PlaybackMode] for an int [constant] - * @return The mode, null if there isnt one for this. + * @return The mode, null if there isn't one for this. */ fun fromInt(constant: Int): PlaybackMode? { return when (constant) { - CONST_ALL_SONGS -> ALL_SONGS - CONST_IN_ALBUM -> IN_ALBUM - CONST_IN_ARTIST -> IN_ARTIST - CONST_IN_GENRE -> IN_GENRE + INT_ALL_SONGS -> ALL_SONGS + INT_IN_ALBUM -> IN_ALBUM + INT_IN_ARTIST -> IN_ARTIST + INT_IN_GENRE -> IN_GENRE else -> null } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt index de8812a92..981ff54ba 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt @@ -37,7 +37,7 @@ import kotlin.math.pow * Manages the current volume and playback state across ReplayGain and AudioFocus events. * @author OxygenCobalt */ -class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener { +class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener, SettingsManager.Callback { private data class Gain(val track: Float, val album: Float) private val playbackManager = PlaybackStateManager.maybeGetInstance() @@ -62,13 +62,24 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener { get() = field * multiplier private set + init { + settingsManager.addCallback(this) + } + + /** + * Request the android system for audio focus + */ + fun requestFocus() { + AudioManagerCompat.requestAudioFocus(audioManager, request) + } + /** * Updates the rough volume adjustment for [Metadata] with ReplayGain tags. * This is based off Vanilla Music's implementation. */ fun applyReplayGain(metadata: Metadata?) { - if (metadata == null) { - logD("No parsable ReplayGain tags, returning volume to 1.") + if (settingsManager.replayGainMode == ReplayGainMode.OFF || metadata == null) { + logD("ReplayGain is disabled or cannot be determined for this track, resetting volume.") volume = 1f return } @@ -76,14 +87,14 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener { val gain = parseReplayGain(metadata) // Currently we consider both the album and the track gain. - // TODO: Add configuration here var adjust = 0f if (gain != null) { - adjust = if (gain.album != 0f) { - gain.album + // Allow the user to configure a preferred mode for ReplayGain. + adjust = if (settingsManager.replayGainMode == ReplayGainMode.TRACK) { + if (gain.track != 0f) gain.track else gain.album } else { - gain.track + if (gain.album != 0f) gain.album else gain.track } } @@ -155,20 +166,16 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener { } } - /** - * Request the android system for audio focus - */ - fun requestFocus() { - AudioManagerCompat.requestAudioFocus(audioManager, request) - } - /** * Abandon the current focus request, functionally "Destroying it". */ fun release() { AudioManagerCompat.abandonAudioFocusRequest(audioManager, request) + settingsManager.removeCallback(this) } + // --- INTERNAL AUDIO FOCUS --- + override fun onAudioFocusChange(focusChange: Int) { if (!settingsManager.doAudioFocus) { // Don't do audio focus if its not enabled @@ -219,6 +226,14 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener { logD("Unducked volume, now $volume") } + // --- SETTINGS MANAGEMENT --- + + override fun onAudioFocusUpdate(focus: Boolean) { + if (!focus) { + onGain() + } + } + companion object { private const val MULTIPLIER_DUCK = 0.2f 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 8656fd08e..3a18374a0 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 @@ -353,6 +353,10 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac } } + override fun onReplayGainUpdate(mode: ReplayGainMode) { + onTracksInfoChanged(player.currentTracksInfo) + } + // --- OTHER FUNCTIONS --- /** diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt new file mode 100644 index 000000000..9c085b080 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt @@ -0,0 +1,30 @@ +package org.oxycblt.auxio.playback.system + +enum class ReplayGainMode { + OFF, + TRACK, + ALBUM; + + fun toInt(): Int { + return when (this) { + OFF -> INT_OFF + TRACK -> INT_TRACK + ALBUM -> INT_ALBUM + } + } + + companion object { + private const val INT_OFF = 0xA110 + private const val INT_TRACK = 0xA111 + private const val INT_ALBUM = 0xA112 + + fun fromInt(value: Int): ReplayGainMode? { + return when (value) { + INT_OFF -> OFF + INT_TRACK -> TRACK + INT_ALBUM -> ALBUM + else -> null + } + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt index b24c9c204..51851e8bf 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt @@ -25,6 +25,7 @@ import androidx.preference.PreferenceManager import org.oxycblt.auxio.accent.Accent import org.oxycblt.auxio.home.tabs.Tab import org.oxycblt.auxio.playback.state.PlaybackMode +import org.oxycblt.auxio.playback.system.ReplayGainMode import org.oxycblt.auxio.ui.DisplayMode import org.oxycblt.auxio.ui.Sort @@ -102,6 +103,11 @@ class SettingsManager private constructor(context: Context) : val doPlugMgt: Boolean get() = sharedPrefs.getBoolean(KEY_PLUG_MANAGEMENT, true) + /** The current ReplayGain configuration */ + val replayGainMode: ReplayGainMode + get() = ReplayGainMode.fromInt(sharedPrefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE)) + ?: ReplayGainMode.OFF + /** What queue to create when a song is selected (ex. From All Songs or Search) */ val songPlaybackMode: PlaybackMode get() = handleSongPlayModeCompat(sharedPrefs) @@ -236,6 +242,14 @@ class SettingsManager private constructor(context: Context) : KEY_LIB_TABS -> callbacks.forEach { it.onLibTabsUpdate(libTabs) } + + KEY_AUDIO_FOCUS -> callbacks.forEach { + it.onAudioFocusUpdate(doAudioFocus) + } + + KEY_REPLAY_GAIN -> callbacks.forEach { + it.onReplayGainUpdate(replayGainMode) + } } } @@ -250,6 +264,8 @@ class SettingsManager private constructor(context: Context) : fun onNotifActionUpdate(useAltAction: Boolean) {} fun onShowCoverUpdate(showCovers: Boolean) {} fun onQualityCoverUpdate(doQualityCovers: Boolean) {} + fun onAudioFocusUpdate(focus: Boolean) {} + fun onReplayGainUpdate(mode: ReplayGainMode) {} } companion object { @@ -267,6 +283,7 @@ class SettingsManager private constructor(context: Context) : const val KEY_AUDIO_FOCUS = "KEY_AUDIO_FOCUS" const val KEY_PLUG_MANAGEMENT = "KEY_PLUG_MGT" + const val KEY_REPLAY_GAIN = "auxio_replay_gain" const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE2" const val KEY_KEEP_SHUFFLE = "KEY_KEEP_SHUFFLE" diff --git a/app/src/main/java/org/oxycblt/auxio/ui/DisplayMode.kt b/app/src/main/java/org/oxycblt/auxio/ui/DisplayMode.kt index 502a680b2..92ed829c3 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/DisplayMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/DisplayMode.kt @@ -47,11 +47,11 @@ enum class DisplayMode { } companion object { - private const val CONST_NULL = 0xA107 - private const val CONST_SHOW_GENRES = 0xA108 - private const val CONST_SHOW_ARTISTS = 0xA109 - private const val CONST_SHOW_ALBUMS = 0xA10A - private const val CONST_SHOW_SONGS = 0xA10B + private const val INT_NULL = 0xA107 + private const val INT_SHOW_GENRES = 0xA108 + private const val INT_SHOW_ARTISTS = 0xA109 + private const val INT_SHOW_ALBUMS = 0xA10A + private const val INT_SHOW_SONGS = 0xA10B /** * Convert this enum into an integer for filtering. @@ -60,11 +60,11 @@ enum class DisplayMode { */ fun toFilterInt(value: DisplayMode?): Int { return when (value) { - SHOW_SONGS -> CONST_SHOW_SONGS - SHOW_ALBUMS -> CONST_SHOW_ALBUMS - SHOW_ARTISTS -> CONST_SHOW_ARTISTS - SHOW_GENRES -> CONST_SHOW_GENRES - null -> CONST_NULL + SHOW_SONGS -> INT_SHOW_SONGS + SHOW_ALBUMS -> INT_SHOW_ALBUMS + SHOW_ARTISTS -> INT_SHOW_ARTISTS + SHOW_GENRES -> INT_SHOW_GENRES + null -> INT_NULL } } @@ -75,10 +75,10 @@ enum class DisplayMode { */ fun fromFilterInt(value: Int): DisplayMode? { return when (value) { - CONST_SHOW_SONGS -> SHOW_SONGS - CONST_SHOW_ALBUMS -> SHOW_ALBUMS - CONST_SHOW_ARTISTS -> SHOW_ARTISTS - CONST_SHOW_GENRES -> SHOW_GENRES + INT_SHOW_SONGS -> SHOW_SONGS + INT_SHOW_ALBUMS -> SHOW_ALBUMS + INT_SHOW_ARTISTS -> SHOW_ARTISTS + INT_SHOW_GENRES -> SHOW_GENRES else -> null } } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt b/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt index 5ec294452..a7c378ee9 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt @@ -163,10 +163,10 @@ sealed class Sort(open val isAscending: Boolean) { */ fun toInt(): Int { return when (this) { - is ByName -> CONST_NAME - is ByArtist -> CONST_ARTIST - is ByAlbum -> CONST_ALBUM - is ByYear -> CONST_YEAR + is ByName -> INT_NAME + is ByArtist -> INT_ARTIST + is ByAlbum -> INT_ALBUM + is ByYear -> INT_YEAR }.shl(1) or if (isAscending) 1 else 0 } @@ -202,10 +202,10 @@ sealed class Sort(open val isAscending: Boolean) { } companion object { - private const val CONST_NAME = 0xA10C - private const val CONST_ARTIST = 0xA10D - private const val CONST_ALBUM = 0xA10E - private const val CONST_YEAR = 0xA10F + private const val INT_NAME = 0xA10C + private const val INT_ARTIST = 0xA10D + private const val INT_ALBUM = 0xA10E + private const val INT_YEAR = 0xA10F /** * Convert a sort's integer representation into a [Sort] instance. @@ -216,10 +216,10 @@ sealed class Sort(open val isAscending: Boolean) { val ascending = (value and 1) == 1 return when (value.shr(1)) { - CONST_NAME -> ByName(ascending) - CONST_ARTIST -> ByArtist(ascending) - CONST_ALBUM -> ByAlbum(ascending) - CONST_YEAR -> ByYear(ascending) + INT_NAME -> ByName(ascending) + INT_ARTIST -> ByArtist(ascending) + INT_ALBUM -> ByAlbum(ascending) + INT_YEAR -> ByYear(ascending) else -> null } } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 97077ff73..335e9556e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -73,6 +73,10 @@ Pausieren wenn andere Töne abspielt wird [Bsp. Anrufe] Kopfhörerfokus Abspielen/Pausieren wenn sich die Kopfhörerverbindung ändert + ReplayGain (Nur MP3/FLAC) + Aus + Titel bevorzugen + Album bevorzugen Verhalten Wenn ein Lied ausgewählt wird diff --git a/app/src/main/res/values/settings.xml b/app/src/main/res/values/settings.xml index c00e52ab8..3da1a79ac 100644 --- a/app/src/main/res/values/settings.xml +++ b/app/src/main/res/values/settings.xml @@ -6,7 +6,7 @@ - + @string/set_theme_auto @string/set_theme_day @string/set_theme_night @@ -32,6 +32,18 @@ @integer/play_mode_genre + + @string/set_replay_gain_off + @string/set_replay_gain_track + @string/set_replay_gain_album + + + + @integer/replay_gain_off + @integer/replay_gain_track + @integer/replay_gain_album + + -1 1 2 @@ -40,4 +52,8 @@ 0xA104 0xA105 0xA106 + + 0xA110 + 0xA111 + 0xA112 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 64424bd60..22449240a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -85,6 +85,10 @@ Pause when other audio plays [ex. Calls] Headset focus Play/Pause when the headset connection changes + ReplayGain (MP3/FLAC Only) + Off + Prefer track + Prefer album Behavior When a song is selected diff --git a/app/src/main/res/xml/prefs_main.xml b/app/src/main/res/xml/prefs_main.xml index 0b2ee933c..cfab281d2 100644 --- a/app/src/main/res/xml/prefs_main.xml +++ b/app/src/main/res/xml/prefs_main.xml @@ -7,7 +7,7 @@ + +